Escopo ActiveRecord Rails 3 vs método de classe
-
29-10-2019 - |
Pergunta
Eu sou novo na nova interface de consulta do ActiveRecord, então ainda estou descobrindo algumas coisas.
Eu esperava que alguém pudesse explicar a diferença entre usar um scope
em um modelo ActiveRecord e apenas usar um método de classe (ou seja, self.some_method
)
Pelo que pude perceber, sempre se espera que um escopo retorne uma relação, enquanto um método de classe não necessariamente precisa.Isso é verdade?
Por exemplo, achei que faria sentido fazer algo como:
class Person
scope :grouped_counts, group(:name).count
end
Mas isso não funciona.Eu recebo este erro:
ArgumentError: Unknown key(s): communicating, failed, matched, unmatched
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/activesupport-3.0.5/lib/active_support/core_ext/hash/keys.rb:43:in `assert_valid_keys'
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/activerecord-3.0.5/lib/active_record/relation/spawn_methods.rb:110:in `apply_finder_options'
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/activerecord-3.0.5/lib/active_record/named_scope.rb:110:in `block in scope'
from (irb):48
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/railties-3.0.5/lib/rails/commands/console.rb:44:in `start'
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/railties-3.0.5/lib/rails/commands/console.rb:8:in `start'
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/railties-3.0.5/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
r
No entanto, funciona como um método de classe
def self.grouped_counts
group(:name).count
end
Estou interessado em saber a opinião das pessoas sobre quando usar escopos e quando usar métodos de classe.Estou correto ao presumir que um escopo deve sempre retornar uma relação, mas um método de classe pode retornar o que quiser?
Solução
Havia mais uma diferença no Rails 2.x, já que named_scopes não executava suas consultas (para que você pudesse encadear), enquanto os métodos de classe geralmente executavam as consultas (para que você não pudesse encadear), a menos que você manualmenteenvolveu sua consulta em uma chamada scoped(...)
.
No Rails 3, tudo retorna um ActiveRecord::Relation
até que você precise dos resultados reais, então escopos podem ser encadeados contra métodos de classe e vice-versa (contanto que os métodos de classe retornem objetos ActiveRecord::Relation
, e não algum outro tipo de objeto (como uma contagem)).
Geralmente, eu uso entradas scope
para linhas simples simples para filtrar meu conjunto de resultados.No entanto, se estou fazendo algo complicado em um "escopo" que pode exigir lógica detalhada, lambdas, várias linhas, etc., prefiro usar um método de classe.E como você percebeu, se eu precisar retornar contagens ou algo parecido, eu uso um método de classe.
Outras dicas
Como Dylan aludiu em sua resposta, uma diferença entre escopo e método de classe é que os escopos são avaliados quandoa classe está carregada.Isso pode levar a resultados inesperados.
Por exemplo,
class Post < ActiveRecord::Base
scope :published_earlier, where('published_at < ?', Date.today)
end
está sujeito a erros.A maneira correta é usar um lambda
class Post < ActiveRecord::Base
scope :published_earlier, -> { where('published_at < ?', Date.today) }
end
O bloco lambda é avaliado lentamente.Então Date.today é executado quando você chama o escopo, não quando a classe é avaliada.
Se você usar um método de classe, não precisará usar lambda.
class Post < ActiveRecord::Base
def self.published_earlier
where('published_at < ?', Date.today)
end
end
Porque com o método de classe, o código é executado no momento da chamada do método.