I suppose, you know the great scope_out plugin for rails. If not, go check it out, since it’s really great to define DRY scoped associations. Basically, you can use scope_out to define the scoped associations within the model the scope is applied to. However, what I’m missing is a feature like a default scope that can be applied to the model and scopes the default finder.
Consider the following example where a user belongs to a group and a group has many users:
1 class Group < ActiveRecord::Base 2 has_many :users 3 end 4 class User < ActiveRecord::Base 5 belongs_to :group 6 end
I want to distinguish between active and inactive users, so I added a “status” attribute that either contains ‘active’ or ‘inactive’. Using scope_out, you can easily access all active and all inactive users via all othe models that have a has_many :users association:
1 class User < ActiveRecord::Base 2 scope_out :active, :conditions => { :status => 'active' } 3 scope_out :inactive, :conditions => { :status => 'inactive' } 4 end
So far, that’s great… You get group.users.active and group.users.inactive. However, next I’d like to track an additional status of a user: ‘deleted’. If a user is deleted, his database record should not really be destroyed, but marked as ‘deleted’ in the status attribute. This would make it possible to keep a working reference to an existing record of a deleted user, e.g. in a post that this user wrote before he was deleted.
To get scoped finders for deleted and non-deleted users, you however need to add the following scope_out statements to the user model:
1 class User < ActiveRecord::Base 2 scope_out :deleted, :conditions => { :status => 'deleted' } 3 scope_out :non_deleted, :conditions => "status != 'deleted'" 4 end
Now, this works nice, however the drawback is, that you now have to watch out to use group.users.non_deleted at any place you used group.users before. E.g. the controller that takes care of showing a page with all users of a group probably uses group.users to fetch the users of a group. But actually, deleted users should not be displayed by default.
Idea: default_scope
It would be great, to be able to define a default scope that applies to all finder methods that are not otherwise scoped out. E.g. like this:
1 class User < ActiveRecord::Base 2 scope_out :deleted, :conditions => { :status => 'deleted' } 3 default_scope :conditions => "status != 'deleted'" 4 end
This should result in:
1 group.users # get a list of users where status is not 'deleted' (default scope applied) 2 group.users.active # get a list of users where status is 'active' (default scope NOT applied) 3 group.users.find(5) # get user with id 5 (default scope NOT applied) 4 group.users.find(:all, :conditions => '...') # get users which match the condition (default scope NOT applied)
I’m not sure yet, if it would make sense to apply the default scope to automatic finders like find_by_name.
I don’t know how others think about this idea (comments welcome), however I’d probably like it, since it would make things easier in an application I am currently developing (which has a lot of models that have those status attribute).
Unfortunately this idea is just plain theory yet and I probably won’t find time to test it until next week.
Update (2007-08-21): Actually there’s a rails plugin from dvisionfactory that seems to do exactly what I was proposing here.