de forma geral, a convenção é que em Ruby o ==
compara se dois objetos tem o mesmo valor, não que são a mesma instância, como acontece em Java. Esse valor pode ser depender do domínio da sua aplicação, mas eu costumo pensar "esses 2 objetos tem a mesma representação em memória?". De novo, em geral. Existem exceções quando isso faz sentido.
'1' == 1 # é falso, pq o valor de '1' em memória seria o encoding desse caractere em unicode/ascii (49) e não 1
false == 0 # é falso, pq o tipo "boolean" é _como se fosse_ um enum que só aceita os valores true e false
# um exemplo que foge a regra, mas "faz sentido" (de acordo com o Rails team), assumindo que User é um model do Rails
u1 = User.new(id: 1, username: 'john.doe')
u2 = User.new(id: 1, username: 'joao.silva')
# pq do ponto de vista do AR dois objetos com o mesmo id representam o mesmo registro, já que se eu salvar os 2 no banco um vai sobrescrever o outro
o ===
checa se o objeto "aceita" o outro como um similar ou membro. a definição de aceitar é que muda de uma classe pra outra
Integer === 1 # a classe Integer aceita o valor 1 como um de seus items? é o mesmo que 1.is_a?(Integer)
String === '1' # 1.is_a?(String)
/\d+/ === '123' # /\d+/.match?('123')
(1..10) === 5 # (1..10).include?(5)
Percebam que os objetos da esquerda representam sempre o que eu gosto de chamar "categorias" de coisas, uma classe, uma regex ou um range.
O único caso que me lembro onde isso pode causar uma certa confusão é para arrays. [1, 2, 3] === 2
vai retornar false
. Isso é uma decisão de implementação por causa de como a linguagem usa o case
statement (análogo do switch
de outras linguagens), mas não vou entrar em detalhes (se quiserem saber, me pinguem).
Quando o objeto da esquerda não é uma "categoria" de coisas, o ===
se comporta como o ==
.
'1' === 1 # ainda é falso
O que eu quis dizer com "eles podem fazer o que raios o programador quiser" é que em Ruby os operadores são métodos, como quaiquer outros,:
class User
def ==(other)
self.id == other.id
end
def ===(other)
self == other
end
end
# inclusive podem ser chamados como qualquer outro método
u1 = User.new(id: 1)
u2 = User.new(id: 3)
u1.==(u2) # é a mesma coisa que u1 == u2
u2.===(u1) # é a mesma coisa que u2 === u1
É por isso que Integer === 1
é true, mas 1 === Integer
é false. O objeto da esquerda no primeiro caso é uma instância da classe Class
e a implementação de ==
lá usa a ideia de "categorias" e no segundo caso, o objeto da esquerda é uma instância de Integer
.