Refactoring Ruby on Rails

Ruby on Rails / Refactoring – policy object pattern

Ruby on Rails / Refactoring – policy object pattern January 29, 2018Leave a comment

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. 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.

Do you want to read about given topic?

Hit me on twitter or use contact form and let me know!

Download free RSpec & TDD ebook

Do you want to earn more or jump to the next level in your company? Do you know that testing skills are one of the most desired skills? There is only first step: start testing and do it right. My ebook can help you. Subscribe to the newsletter to get a free copy of the book.

Leave a Reply

Your email address will not be published. Required fields are marked *