Alcance de ActiveRecord Rails 3 frente al método de clase
-
29-10-2019 - |
Pregunta
Soy nuevo en la nueva interfaz de consulta de ActiveRecord, así que todavía estoy averiguando cosas.
Esperaba que alguien pudiera explicar la diferencia entre usar un scope
en un modelo ActiveRecord y simplemente usando un método de clase (es decir self.some_method
)
Por lo que puedo deducir, siempre se espera que un alcance devuelva una relación, mientras que un método de clase no necesariamente tiene que hacerlo.¿Es esto cierto?
Por ejemplo, pensé que tendría sentido hacer algo como:
class Person
scope :grouped_counts, group(:name).count
end
Pero esto no funciona.Recibo este error:
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
Sin embargo, funciona como método de clase.
def self.grouped_counts
group(:name).count
end
Me interesa conocer la opinión de la gente sobre cuándo usar ámbitos y cuándo usar métodos de clase.¿Estoy en lo cierto al suponer que un ámbito siempre debe devolver una relación, pero un método de clase puede devolver lo que quiera?
Solución
Había más diferencia en Rails 2.x, ya que los name_scopes no ejecutaban tus consultas (por lo que podías encadenarlas), mientras que los métodos de clase generalmente ejecutaban las consultas (por lo que no podías encadenarlas), a menos que envolvieras tu consulta manualmente. en un scoped(...)
llamar.
En Rails 3, todo devuelve un ActiveRecord::Relation
hasta que necesite los resultados reales, por lo que los ámbitos se pueden encadenar contra métodos de clase y viceversa (siempre que los métodos de clase devuelvan ActiveRecord::Relation
objetos, no algún otro tipo de objeto (como un recuento)).
Generalmente uso scope
entradas de frases ingeniosas simples para filtrar mi conjunto de resultados.Sin embargo, si estoy haciendo algo complicado en un "alcance" que puede requerir lógica detallada, lambdas, líneas múltiples, etc., prefiero usar un método de clase.Y como habrás comprendido, si necesito devolver recuentos o algo así, uso un método de clase.
Otros consejos
Como Dylan Aludido en su respuesta, una diferencia entre el alcance y el método de clase es que los alcances se evalúan cuando se carga la clase.Esto puede llevar a resultados inesperados.
Por ejemplo,
class Post < ActiveRecord::Base
scope :published_earlier, where('published_at < ?', Date.today)
end
es propenso a errores.La forma correcta es usar una lambda.
class Post < ActiveRecord::Base
scope :published_earlier, -> { where('published_at < ?', Date.today) }
end
El bloque lambda se evalúa de forma perezosa.Así Date.today se ejecuta cuando se llama el alcance, no cuando se evalúa la clase.
Si usa un método de clase, no necesita usar lambda.
class Post < ActiveRecord::Base
def self.published_earlier
where('published_at < ?', Date.today)
end
end
Porque con el método de clase, el código se ejecuta en el momento de la llamada al método.