Posted about 5 years ago. Visible to the public.

Policy Objects

The Policy Objects design pattern is similar to Service Objects, but is responsible for read operations while Service Objects are responsible for write operations. Policy Objects encapsulate complex business rules and can easily be replaced by other Policy Objects with different rules. For example, we can check if a guest user is able to retrieve certain resources using a guest Policy Object. If the user is an admin, we can easily change this guest Policy Object to an admin Policy Object that contains admin rules.

Example

Before a user creates a project, the Controller checks whether the current user is a manager, whether they have permission to create a project, whether the number of current user projects is under the maximum, and checks the presence of blocks on the creation of projects in Redis key/value storage. In this case, we can make the Model and Controller skinny by moving this logic to a policy object.

Copy
class CreateProjectPolicy def initialize(user, redis_client) @user = user @redis_client = redis_client end def allowed? @user.manager? && below_project_limit && !project_creation_blocked end private def below_project_limit @user.projects.count < Project.max_count end def project_creation_blocked @redis_client.get('projects_creation_blocked') == '1' end end
Copy
class ProjectsController < ApplicationController def create if policy.allowed? @project = Project.create!(project_params) render json: @project, status: :created else head :unauthorized end end private def project_params params.require(:project).permit(:name, :description) end def policy CreateProjectPolicy.new(current_user, redis) end def redis Redis.current end end
Copy
class User < ActiveRecord::Base enum role: [:manager, :employee, :guest] end

The result is a clean Controller and Model. The policy object encapsulates the permission check logic, and all external dependencies are injected from the Controller into the policy object. All classes do their own work and no one else’s.

Owner of this card:

Avatar
Alexander M
Last edit:
about 5 years ago
by Alexander M
Tags:
Software-Architecture
Posted by Alexander M to Ruby and RoR knowledge base
This website uses short-lived cookies to improve usability.
Accept or learn more