Skip to content

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:

[ruby]
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
[/ruby]

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:

[ruby]Fu.has_moo_and_foo.all[/ruby]

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.

Flattr this!

3 Responses
  1. February 7, 2011

    I wanted to just make some simple aliases like alias_method so I whipped this up:

    module ActiveRecord
    class Base
    def self.alias_named_scope(alias_scope, original_scope)
    named_scope alias_scope, lambda { self.send(original_scope).scope(:find) }
    end
    end
    end

  2. David Ongaro permalink
    June 4, 2010

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

  3. Michael Thompson permalink
    September 18, 2009

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

Comments are closed.