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:
class 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:
class 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:
class 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):
class 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.