The policy object pattern

Ruby on Rails / Refactoring

Policy objects are primitive Ruby objects, used for checking operations isolation. I personally love this pattern, however, there are some rules that we should stick to in order to name given object as a policy object.

Policy object rules

  1. Method name always ends with a quote mark
  2. The method returns false or true
  3. We don’t modify passed attributes
  4. Code cover only simple read logic, no database calls etc.

Demonstration

Let’s build now a sample class which will be used later to implement policy object:

class UserService
  def initialize(user)
    @user = user
  end

  def name
    if user.full_name.blank? && user.email.present?
      user.email
    else
      user.full_name
    end
  end

  def account_name
    if user.sign_in_count > 0 && user.role == "admin"
      "Administrator"
    else
      "User"
    end
  end

  private
  attr_reader :user
end

We can easily divide our class into two types of actions: reading and checking. Checking part is perfect code for policy objects. Since we are operating on a User object we can create one policy object for both methods:

class UserPolicy
  def initialize(user)
    @user = user
  end

  def administrator_account_name?
    user.sign_in_count > 0 && user.role == "admin"
  end

  def use_email_as_name?
    user.full_name.blank? && user.email.present?
  end

  private
  attr_reader :user
end

after implementing UserPolicy in our UserService service things look much cleaner right now:

class UserService
  def initialize(user)
    @user = user
  end

  def name
    user_policy.use_email_as_name? ? user.email : user.full_name
  end

  def account_name
    user_policy.administrator_account_name? ? "Administrator" : "User"
  end

  private
  attr_reader :user

  def user_policy
    @_user_policy ||= UserPolicy.new(user)
  end
end

Policy logic is now separated from reading part and it’s easier to test UserService class because we can simply stub UserPolicy. However, it’s not the end of our refactoring process. We can apply modified explaining variable pattern to our policy object. Instead of moving logic to variables we would move logic directly to smaller methods with meaningful names:

class UserPolicy
  def initialize(user)
    @user = user
  end

  def administrator_account_name?
    user_signed_in? && user_is_administrator?
  end

  def use_email_as_name?
    user_does_not_have_full_name? && user_has_email?
  end

  private
  attr_reader :user

  def user_does_not_have_full_name?
    user.full_name.blank?
  end

  def user_has_email?
    user.email.present?
  end

  def user_signed_in?
    user.sign_in_count > 0
  end

  def user_is_administrator?
    user.role == "admin"
  end
end

The class is now longer but it’s self-explainable.