Não confunda ActiveRecord.find com Enumerable.find no Rails!
Eu primeiro aprendi Ruby para depois começar a brincar com o Rails. Esse deveria ser o processo natural e correto, mas sei que nem todo mundo tem começado a usar Rails dessa forma. De qualquer maneira, antes de escrever meus primeiros pet-projects com o Rails eu escrevi muito código Ruby, basicamente scripts de propósito geral.
Uma coisa com a qual me habituei bastante foi a utilizar o método find do módulo Enumerable. Esse método é uma mensagem que pode ser enviada a qualquer objeto de uma classe que inclua o módulo Enumerable em sua definição e serve, óbviamente, para procurar por algo dentro deste objeto. O caso mais comum é a classe Array, ou seja, uma lista de objetos quaisquer.
array = [1, 2, 3, 4, 5]
resultado = array.find { |i| i%2 == 0 }
No exemplo acima o método find vai retornar o primeiro elemento da lista que seja par. Retornaria nil caso nenhum valor atendesse à condição valor%2 == 0.
O problema é que eu fui tentar usar algo parecido dentro de um model em um dos projetos Rails no qual venho trabalhando.
Esse model segue um esquema mais ou menos assim:
class User < ActiveRecord::Base has_many :roles, :through => :permissions end class Role < AtiveRecord::Base has_many :users, :through => :permissions end class Permission < ActiveRecord::Base belongs_to :user belongs_to :role end
Em um determinado momento eu preciso verificar se um usuário possui um determinado role, entao tentei, ingenuamente, fazer algo como
class User < ActiveRecord::Base
def has_role?(role)
roles.find{ |r| r.description == role }
end
end
Simples, deveria funcionar perfeitamente. Só que quando eu rodava esse código, recebia uma mensagem do tipo “can’t find Role without an id”. Hummmmmm… Bingo! O problema é que quando acesso uma lista que representa um associação com outro objetos de outro model, o active record faz um pesquisa no banco e usa joins em cima dos ids dos objetos, ou seja, o código acima espera receber algo do tipo
def has_role?(role) roles.find :conditions => "blablabla..." end
Dessa forma o active record usa o valor recebido em :conditions para criar a cláusula “where” do sql enviado ao banco… E não é isso que eu quero, não quero que ele busque filtrando pelo banco, quero que ele busque todos os roles do usuário de uma vez só e pesquise na aplicação.
Mas se eu não posso usar o método find, porque o dito cujo existe na classe ActiveRecord::Base e no módulo Enumerable com o mesmo nome mas funcionalidade completamente diferente, o que fazer?
Simples: O método Enumerable.find na verdade é um alias (um sinônimo) do método Enumerable.detect. Ou seja: se eu enviar para minha lista a mensagem minha_lista.detect { …} eu tenho a funcioalidade que eu quero. E o código fica assim:
def has_role?(role)
roles.detect { |r| r.description == role }
end
Problema resolvido e mais uma lição aprendida.


