Scopes Are Obsolete
I admit, I’ve never been a big fan of named_scope or just scope as it’s been renamed in Rails 3. When it was first introduced I remember not being particularly impressed, as a Merb acolyte we’d had this chaining functionality in Datamapper for ages, only it was much better. In fact in Datamapper, every query you could construct was chainable. Thankfully in Rails 3 and ActiveRecord 3, queries have finally grown up so that everything is now chainable in ActiveRecord too:
class Person < ActiveRecord::Base
def self.alphabetically
order(:first_name, :last_name)
end
def self.active
where(:archived_at => nil)
end
end
Person.active # => [...]
Person.alphabetically.active # => [...]
In this case it seems like the scope method will give us much nicer more concise syntax:
class Person < ActiveRecord::Base
scope :alphabetically, order(:first_name, :last_name)
scope :active, where(:archived_at => nil)
end
But imagine if we want to add a new kind of scope to find user’s with a given last name:
scope :by_last_name, lambda { |name| where(:last_name => name) }
It’s getting a bit less nice, aside from the gratuitous lambda, it’s still pretty okay though.
What I believe is wrong with this code though is that it is essentially recreating Ruby functionality. We’re defining a method called by_last_name which will execute some code when called, only we’re doing it through meta-programming for essentially no reason at all. The above could have been written as:
def self.by_last_name(name); where(:last_name => name); end
And it would have worked exactly the same. The only difference that I can tell is that scope allows you to define extension methods by passing a block, which I’m sure no one has ever used, since it’s so completely useless.
The problem becomes even more striking when the code is even the slightest bit complicated.
scope :for_user, lambda { |user|
if user.admin?
where(:active => true)
else
where(:active => true, :user_id => user.id)
end
}
That’s just horrible.
def self.for_user(user)
if user.admin?
where(:active => true)
else
where(:active => true, :user_id => user.id)
end
end
This looks much more like Ruby code and less like some kind of weird JavaScript concoction.
Stop replicating functionality that already exists, stop using scope. It is obsolete.
