zargony.com

#![desc = "Random thoughts of a software engineer"]

Scope_out feature: default_scope

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:

class Group < ActiveRecord::Base
  has_many :users
end
class User < ActiveRecord::Base
  belongs_to :group
end

I want to distinguish between active and inactive users, so I added a "status" attribute that either contains 'active' or 'inactive'. Using scopeout, you can easily access all active and all inactive users via all othe models that have a hasmany :users association:

class User < ActiveRecord::Base
  scope_out :active, :conditions => { :status => 'active' }
  scope_out :inactive, :conditions => { :status => 'inactive' }
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:

class User < ActiveRecord::Base
  scope_out :deleted, :conditions => { :status => 'deleted' }
  scope_out :non_deleted, :conditions => "status != 'deleted'"
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:

class User < ActiveRecord::Base
  scope_out :deleted, :conditions => { :status => 'deleted' }
  default_scope :conditions => "status != 'deleted'"
end

This should result in:

group.users            # get a list of users where status is not 'deleted' (default scope applied)
group.users.active     # get a list of users where status is 'active' (default scope NOT applied)
group.users.find(5)    # get user with id 5 (default scope NOT applied)
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.