This post is the first part of a series of posts about design patterns with Ruby on Rails.
See other parts here:
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.
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 looking at 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 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.
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
The filename usually has
_policy suffix applied and the class with
the end, with meaningful names. The method names always end with
If you want to have a pure policy object, stick with these 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
class UsersPolicy def initialize(user) @user = user end def admin? confirmed? && @user.role == ‘admin’ end def confirmed? @user.email.present? && @user.confirmed_at.present? end end
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 whole app.
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.redactors.size > 0 && (current_user.role == ‘admin’ || (current_user.role == ‘moderator’ && current_user.verified_email)) # create end end end
The above condition checks are long, ugly, and unreadable, the policy object pattern can be applied.
Let’s begin creating
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? live_blog_with_redactors? && user_with_suitable_role? end private def live_blog_with_redactors? blog.mode == ‘live’ && blog.redactors.size > 0 end def user_with_suitable_role? admin_role? || moderator_user_with_verified_email? end def admin_user? user.role == ‘admin’ end def moderator_user_with_verified_email? user.role == ‘moderator` && user.verified_email end end
our controller with the policy object looks like this:
class PostsController < ApplicationController def create if PostsCreationPolicy.create?(current_user, @blog) #create end end end
Another way of refactoring complex queries is by creating small policy methods on the model class:
class User < ActiveRecord::Base def admin? role == ‘admin’ end def moderator? role == ‘moderator’ end def authorable? return true if admin? moderator? && verified_email end end
class Blog < ActiveRecord::Base def live? mode == ‘live’ end def any_redactors? redactors.any? end def publishable? live? && any_redactors? end end
now we can refactor our controller:
class PostsController < ApplicationController def create if @blog.published? && current_user.authorable? # create end end end
As you can see, it’s a much smaller and readable way to write the same logic.
The policy pattern is a small concept that gives big results. Each time you have to deal with
complex conditions, think about the policy object. 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
[book] Rails Patterns by Pawel Dabrowski