How to alias and create a new named scope out of existing ones
Named scopes in Rails are just awesome. They so much DRY and beautify your code! But did you ever had the trouble to create a new named_scope out of others? E.g. aliasing multiple ones to a new one. Luckily I now figured out how this works in a nice way.
The magic key is the method scope( :find )
This method called on a Scope class returns the Hash used for the ActiveRecord .find() call. First it’s a nice way to debug your scopes, second it’s perfect for our need to chain multiple scoped into one. Check this out:
class Fu < ActiveRecord::Base
named_scope :has_moo, :conditions => { :moo => true }
named_scope :has_foo, :conditions => { :foo => true }
named_scope :has_moo_and_foo, lambda { has_moo.has_foo.scope(:find) }
end
We chain the two scoped has_moo and has_foo together into a new one called has_moo_and_foo. As the return value can only be a Hash we use the above mentioned scope(:find) to transform it into one.
Now this works:
Fu.has_moo_and_foo.all
Sweet! Imagine how great this would be if we can return a scope object instead of a hash as well??
See my pastie here too.
Thanks, this was useful. I had a bunch of anonymous named scopes I needed to chain together, but with OR, rather than AND. It was simple to create a new scope using the collection of #scope(:find)[:conditions] joined with ” OR “.
Nice. Even though just defining a method like
def self.has_moo_and_foo
has_moo. has_foo
end
also works, I find the named_scope syntax more appealing, because one can scan the named scopes in one sight. (And they also show up in Fu.scopes).
When you define the scopes in the right order, the lambda gets superflous though. So
named_scope :has_moo_and_foo, has_moo.has_foo.scope(:find)
is enough. But default_scope conditions are duplicated in this way (with or without lambda). Even though this doesn’t might hurt it seems to be saver to just exclude them:
named_scope :has_moo_and_foo, with_exclusive_scope { has_moo.has_foo.scope(:find) }