Model inhertiance

I’m working on an application with some CMS features. One requirement is that new content is moderated before being published. To enable this, some users are allocated as moderators. To achieve this I have a moderator status column which can be either ‘some’ or ‘all’ (at the moment a ‘some’ moderator can only moderate content in some languages). Up until now it hasn’t be too cumbersome to have user methods associated with moderaters defined within the User class, but now I’m getting to the point where it would be good to split some of this functionality out into a separate class.

My problem was how to connect a new Moderator class to the User class.

A starting point is that I want Moderator class to inherit from User. That’s simple:

Moderator < User
end

The problem with this is if you do Moderator.find(:all) you get all users and not just those who are moderators.

In my last project, I overrode find, but that was a Rails 2 project and the same technique doesn’t work so well for this Rails 3 project. For example, Moderator.all still returns all users.

Another option would be to use ‘Single Table Inheritance’ (see ActiveRecord::Base api), but that would mean adding a new field and then limiting users to just one sub-class. For example, I think I will use the same technique with admins, and what if a moderator is both a moderator and an admin. Single Table Inheritance won’t let you do that.

In the end the solution was ridiculous simple. It is ‘default_scope’.

This works

class Moderator < User
  default_scope where("moderation_scope IN (?)", ['all', 'some'])
end

And this is what I ended up with:

class User < ActiveRecord::Base

  MODERATE_ALL_SCOPE = 'all'
  MODERATE_SOME_SCOPE = 'some'
  MODERATION_SCOPES = [MODERATE_ALL_SCOPE, MODERATE_SOME_SCOPE]

  def self.find_moderators
    where("moderation_scope IN (?)", MODERATION_SCOPES)
  end
end

class Moderator < User
  default_scope find_moderators
end

If I have special data entries that alter behaviour, I prefer to put them into constants and use the constants within the code. That way I can change the specific data entries at a later date without having to change every instance where it is used. Hence using MODERATION_SCOPES.

The final version also allows me to share the same code between User.find_moderators and Moderator.default_scope.

This entry was posted in Ruby. Bookmark the permalink.