DEV Community

Renata Marques
Renata Marques

Posted on • Edited on

Design Patterns with Ruby on Rails part 1: Introduction and Policy Object

This post is the first part of a series of posts about design patterns with Ruby on Rails.
See other parts here:
Part 2

What are design patterns?

A pattern is the formalization of a problem/solution pair, used to make an object-oriented design decision.
In sum, patterns are a set of general rules to achieve something that is easily repeatable for other developers.

Why design patterns are important?

First, the purpose of a pattern is to codify existing design knowledge so that developers are not constantly reinventing the wheel.
Second, design patterns make communication between designers more efficient.
Untested fat models, controllers, helpers, and views are a technical disaster, if you are not testing your code, it’s harder to see the need for using patterns.
And how can you obtain design patterns benefits? applying these concepts:

  • Isolation: if the logic related to database queries is isolated then you can easily use stubs testing. The same rule applies to stuff like indexing or the 3rd part code.
  • Readability: you can say what the given code is doing in general only by reading the class name.
  • Extendability: it’s easy to modify the existing code and there is no need to change the logic in many places.
  • Single Responsibility: a method or a class has to be responsible only for one action.
  • Testability: thanks for the benefits mentioned, it becomes easier because we have to test only a small portion instead of large methods, connect to the external services and do the business logic at the same time.

Policy Object

Let’s begin with the simplest pattern, policy object is a pattern to deal with permissions, roles, and policies, you can use each time you have to check if something or someone is allowed to do the action. Gems like pundit, Cancan, and Cancancan implement this pattern.

Naming Convention

The filename usually has _policy suffix applied and the class with Policy at
the end. The method names always end with ? character.
e.g.: PostsPolicy#web_section?

A pure policy object is defined by these simple rules:

  • the return has to be a boolean value
  • the logic has to be simple
  • inside the method, we should only call methods on the passed objects

e.g.:

class PostsPolicy
  def initialize(post)
    @post = post
  end

  def web_section?
    active? && @post.section == web
  end

  def active?
    @posts.where(active: true, pending: false)
  end
end
Enter fullscreen mode Exit fullscreen mode

Simply calling other methods and comparing using the data, this is the main purpose of policy object.
They are light, simple pure Ruby objects, used for managing permissions across the project.
They are also easy to test and a perfect replacement for complex conditions.

An example of a complex condition:

class PostsController < ApplicationController
  def create
    if @blog.mode == live && @blog.authors.size > 0
    && (current_user.role == admin
    || (current_user.role == moderator && current_user.verified_email))
      # create
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

The above condition checks are long, ugly, and unreadable, the policy object pattern can be applied.

Let’s begin creating PostsCreationPolicy

class PostsCreationPolicy
  attr_reader :user, :blog

  def initialize(user, blog)
    @user = user
    @blog = blog
  end

  def self.create?(user, blog)
    new(user, blog).create?
  end

  def create?
    blog_with_authors? && author_is_allowed?
  end

  private

  def blog_with_authors?
    blog.mode == live && blog.authors.size > 0
  end

  def author_is_allowed?
    is_admin? || moderator_is_verified?
  end

  def is_admin?
    user.role == admin
  end

  def moderator_is_verified?
    user.role == moderator` && user.verified_email
  end
end
Enter fullscreen mode Exit fullscreen mode

our controller with the policy object looks like this:

class PostsController < ApplicationController
  def create
    if PostsCreationPolicy.create?(current_user, @blog)
      #create
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Policies in models

Another way of refactoring complex queries is by creating small policy methods on the model class:

class User < ActiveRecord::Base
  def is_admin?
    role == admin
  end

  def is_moderator?
    role == moderator
  end

  def is_authorable?
    return true if is_admin?

    is_moderator? && verified_email
  end
end
Enter fullscreen mode Exit fullscreen mode
class Blog < ActiveRecord::Base
  def live?
    mode == live
  end

  def any_authors?
    authors.any?
  end

  def publishable?
    live? && any_authors?
  end
end
Enter fullscreen mode Exit fullscreen mode

now we can refactor our controller:

class PostsController < ApplicationController
  def create
    if @blog.publishable? && current_user.is_authorable?
     # create
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

As you can see, it’s a much smaller and readable way to write the same logic.

Conclusion

The policy pattern is a small concept that gives big results. Consider apply policy object each time you have to deal with complex conditions.
When it comes to testing with RSpec, you don’t need to use database records, your policies are pure-ruby objects and your tests are going to be fast.

Top comments (2)

Collapse
 
davidturing37 profile image
David Turing

Great read.

The Pundit authorization gem that made this pattern very popular in the community: github.com/varvet/pundit and is a great option for authorization in your Rails project.

Collapse
 
kgilpin profile image
Kevin Gilpin

How about adding the policy check to the model object validation? Is this a good place to check permissions or should it be done as a separate step?