How to alias and create a new named scope out of existing ones

2009 March 26
by tobi

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.

2 Responses leave one →
  1. 2009 September 18
    Michael Thompson permalink

    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 “.

  2. 2010 June 4
    David Ongaro permalink

    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) }

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS