22 October 2009 · About 2 minutes read

Rails: Multiple default scopes for ActiveRecord

Default scopes were introduced in Rails 2.3 to allow a default set of options to be applied to any find methods. The common example is to always order a set of results by a given column, e.g:

```rubyclass Post < ActiveRecord::Base

# Any calls to Post.find will automatically have the default :order option merged into them

# Post.find(:all)

# => SELECT * FROM “posts” ORDER BY “created_at DESC”;

default_scope :order => “created_at DESC”

end```

Unlike named_scopes (which I am finding more and more useful every day), I found that default scopes cannot be combined when I tried to use the acts_as_revisable and is_paranoid plugins together:

```rubyclass Post < ActiveRecord::Base

acts_as_revisable

is_paranoid

end```

It seems the default scope declared in is_paranoid overrides that of acts_as_revisable. Post.find(:all) will therefore return every revision of Post rather than just the current revision. You can check this out by reversing the plugin order:

```rubyclass Post < ActiveRecord::Base

is_paranoid

acts_as_revisable

end```

Now, Post.find(:all) will return only current revisions, but will include any destroyed posts as the acts_as_revisable default scope overrides is_paranoid!

A Solution

This post and code snippet shows a method for declaring multiple default scopes on a model. I’ve not yet tried out the code, though, as one of the commenter’s was kind enough to forkis_paranoid and modify it to merge any existing default scopes. With this forked plugin, Post can be scoped correctly by both plugins.

The fork is available at http://github.com/grioja/is_paranoid/tree/master.

But… is_paranoid is depracated

I noticed that the original is_paranoid plugin has ceased development, so I’m not sure if I’ll continue to use it, although It’s a neat little plugin, and has several forks.

The underlying problem, though, of ActiveRecord allowing only one default_scope to be declared, is something that I’m bound to come up against in the future, so it’s handy to know there is a workaround at least until Rails includes the functionality.

As an aside, Rich Cavanaugh, the developer of acts_as_revisable, has pointed out that the plugin includes some basic is_paranoid functionality already (see Rich’s reply to my original ramblings):

```rubyclass Post < ActiveRecord::Base

acts_as_revisable :on_delete => :revise

end```

Do you use default_scope? Do you find the single scope a limitation, or do you rely on named scopes? Feel free to discuss in the comments.

Chris Blunt
Chris Blunt @cblunt
Chris is the founder of Plymouth Software. As well as code and business, he enjoys being a Dad, swimming, and the fine art of drinking tea.