Ruby on Rails ActiveRecord scopes vs class methods
From the fine guide:
14 Scopes
[...]
To define a simple scope, we use thescope
method inside the class, passing the query that we'd like to run when this scope is called:class Article < ActiveRecord::Base
scope :published, -> { where(published: true) }
end
This is exactly the same as defining a class method, and which you use is a matter of personal preference:
class Article < ActiveRecord::Base
def self.published
where(published: true)
end
end
Note in particular:
This is exactly the same as defining a class method, and which you use is a matter of personal preference
And a little further (the Rails3 guide says the same thing here BTW):
14.1 Passing in arguments
[...]
Using a class method is the preferred way to accept arguments for scopes.
So which you use is a matter of preference and it is even recommended that you use class methods for scopes that take arguments.
Using scope
is mostly a notational issue. If you say scope :whatever
then you're explicitly saying that whatever
is meant to be a query builder; if you say def self.whatever
then you're not implying anything about the intent of the whatever
method, you're just defining some class method that may or may not behave like a scope.
Of course, 14.1 makes a mess of this notational distinction by recommending that you not use scope
when your scope takes arguments. Also keep in mind that in Rails3 you could say:
scope :published, where(published: true)
so an argumentless scope was visually "clean" and terse but adding a lambda to handle arguments would make it look messier:
scope :pancakes, ->(x) { where(things: x) }
But Rails4 wants lambdas even for argumentless scopes the distinction makes even less sense now.
I suspect that the difference is historical at this point. Scopes were probably something special back in the before times but became plain old class methods in the Rails3 era to cut down on duplication and to better mesh with the new query interface that came with Rails3.
So you can skip scope
and go straight to class methods if you wish. You're even encouraged to do so when your scope takes arguments.
ActiveRecord Rails 3 scope vs class method
There was more of a difference in Rails 2.x, since named_scopes did not execute your queries (so you could chain them), whereas class methods generally did execute the queries (so you could not chain them), unless you manually wrapped your query in a scoped(...)
call.
In Rails 3, everything returns an ActiveRecord::Relation
until you need the actual results, so scopes can be chained against class methods and vice versa (as long as the class methods return ActiveRecord::Relation
objects, not some other object type (like a count)).
Generally, I use scope
entries for simple one-liners to filter down my result set. However, if I'm doing anything complicated in a "scope" which may require detailed logic, lambdas, multiple lines, etc., I prefer to use a class method. And as you caught, if I need to return counts or anything like that, I use a class method.
Are scopes in rails class or instance methods?
Class methods on classes which inherit ActiveRecord::Base can be used also as scopes (on ActiveRecord Relation objects).
Since the module Visible was meant to be mixed into a model which inherits ActiveRecord::Base, its class method visible_to can be used also as a scope.
If this did not clear the issue, you can implement a scope which gets all adult users (age > 20) in the following ways:
class User < ActiveRecord::Base
scope :adult, lambda { where("age > ?", 20) } # with a scope
class << self
def adult # with class method
where("age > ?", 20)
end
end
end
And use it exactly the same with User.adult
Rails: What's the difference between lambda, scope and class method? What's best practice?
It's best to avoid using option 2. That code gets run immediately when your Rails app loads which is bad since it will always return the same value for any Time argument you use in it. That's because it isn't reevaluated every time it's called.
Option 1, as pointed out by musicnerd47, are lazy loaded and it is advisable that you pass lambdas to scopes in Rails 4 rather than doing option 2 since they are reevaluated every time called so they will return updated values.
So the only options would be 1 and 3. This is usually a matter of style that your team adheres to. In our company, we use option 1 when the code we pass to it is going to be an ActiveRecord query and we want it to output a query that can be chained. This is to ensure that an ActiveRecord::Relation object is returned every time we do our queries for multiple records. That would mean that they are always going to be chainable with other ActiveRecord::Relation methods and our other defined scopes.
We use option 3 if it's for behavior that doesn't need to be chained with other scopes.
Here's a good read on the matter of scopes and class_methods where he goes into detail and provides examples on the difference between scopes and class methods.
http://blog.plataformatec.com.br/2013/02/active-record-scopes-vs-class-methods/
Scopes vs class methods in Rails 3
For simple use cases, one can see it as just being syntax sugar. There are, however, some differences which go beyond that.
One, for instance, is the ability to define extensions in scopes:
class Flower < ActiveRecord::Base
named_scope :red, :conditions => {:color => "red"} do
def turn_blue
each { |f| f.update_attribute(:color, "blue") }
end
end
end
In this case,turn_blue
is only available to red flowers (because it's not defined in the Flower class but in the scope itself).
Scope vs Class Method in Rails 3
I think your invocation is incorrect. You should add so-called query method to execute the scope, such as all
, first
, last
, i.e.:
order.line_items.unshipped.all
I've observed some inconsistencies, especially in rspec, that are avoided by adding the query method.
You didn't post your test code, so it's hard to say precisely, but my exeprience has been that after you modify associated records, you have to force a reload, as the query cache isn't always smart enough to detect a change. By passing true
to the association, you can force the association to reload and the query to re-run:
order.line_items(true).unshipped.all
Accessing a class method from within an Active Record scope?
Scopes are same as class methods, so within the scope you are implicitly call another class methods. And your distinct_mechanics_sql
is an instance method, to use it inside a scope declare it as:
def self.distinct_mechanics_sql
or
def Mechanics.distinct_mechanics_sql
or
class << self
def distinct_mechanics_sql
...
end
end
Related Topics
Ruby Google_Drive Gem Oauth2 Saving
Scraping/Parsing Google Search Results in Ruby
Phonegap Mobile Rails Authentication (Devise? Authentication from Scratch)
Ruby Create Tar Ball in Chunks to Avoid Out of Memory Error
Rails Active Record: Find in Conjunction with :Order and :Group
Rspec 'Eq' VS 'Eql' in 'Expect' Tests
Pg::Invalidparametervalue: Error: Invalid Value for Parameter "Client_Min_Messages": "Panic"
How to Add New View to Ruby on Rails Spree Commerce App
Ruby on Rails Country/State Select Enigma
Authlogic Perishable_Token Resets on Every Request
Ruby Variable as Same Object (Pointers)
How to Create a Charge and a Customer in Stripe (Rails)
Ruby on Rails: Search Form - Multiple Search Fields
Ruby Daemons and Jruby - Alternative Options
Rails - How to Check for Online Users