Operation

Operation Policies

Last updated 29 November 2016 trailblazer v1.1 v2.0

Policy

Trailblazer supports “pundit-style” policy classes. They can be hooked into operations and prevent the operation from running its #process method by raising an exception if the policy rule returned false.

Policy Classes

The format of a policy class is heavily inspired by the excellent Pundit gem. In fact, you can reuse your pundit policies without any code changes in Trailblazer.

A policy file per concept is recommendable.

class Thing::Policy
  def initialize(user, thing)
    @user, @thing = user, thing
  end

  def create?
    admin?
  end

  def admin?
    @user.admin == true
  end
  # ..
end

This class would probably be best located at app/concepts/thing/policy.rb.

Operation Policy

Use ::policy to hook the policy class along with a query action into your operation.

class Thing::Create < Trailblazer::Operation
  include Trailblazer::Operation::Policy

  policy Thing::Policy, :create?

The policy is evaluated in #setup!, raises an exception if false and thus suppresses running #process. It is a great way to protect your operations from unauthorized users.

Thing::Create.(current_user: User.find_normal_user, thing: {})

This will raise a Trailblazer::NotAuthorizedError.

Policy Creation

To instantiate the Thing::Policy object internally, per default the params[:current_user] and the operation’s model is passed into the constructor. You can override that via Operation#evaluate_policy.

Queries

After #setup!, the policy instance is available at any point in your operation code.

def process(params)
  notify_admin! if policy.admin?

This won’t raise an exception.

Pundit

Pundit policy classes can be used directly in operations.

class Thing::Create < Trailblazer::Operation
  include Trailblazer::Operation::Policy

  policy ThingPolicy, :create?

As a matter of course, you may call other rule queries on the internal policy object later on.

Guard

Instead of using policies, you can also use a simple guard.

A guard is like an inline policy that doesn’t require you to define a policy class. It is run in #setup!, too, like a real policy, but isn’t accessible in the operation after that.

class Thing::Create < Trailblazer::Operation
  include Policy::Guard

  policy do |params|
    params[:current_user].present? # true when present.
  end

If you prefer a separate class as your guard, you can provide a Callable object.

class Thing::Authorization
  include Uber::Callable # marks instance as callable.

  def call(operation, params)
    params[:current_user].present?
  end
end

Pass the guard instance to Operation::policy to register it.

class Thing::Create < Trailblazer::Operation
  include Policy::Guard
  policy Authorization.new

The same works with Proc, which will receive params only but is executed in operation context (subject to change).

Note that you can’t mix Policy and guards in one class.

Resolver

You can use policies in your builders, too. Please refer to the builder docs to learn about that.