Module: ActionPolicy::Policy::Reasons

Included in:
Base
Defined in:
lib/action_policy/policy/reasons.rb

Overview

Provides failure reasons tracking functionality. That allows you to distinguish between the reasons why authorization was rejected.

It’s helpful when you compose policies (i.e. use one policy within another).

For example:

class ApplicantPolicy < ApplicationPolicy
  def show?
    user.has_permission?(:view_applicants) &&
      allowed_to?(:show?, object.stage)
  end
end

Now when you receive an exception, you have a reasons object, which contains additional information about the failure:

rescue_from ActionPolicy::Unauthorized do |ex|
  ex.policy #=> ApplicantPolicy
  ex.rule #=> :show?
  ex.result.reasons.details  #=> { stage: [:show?] }
end

NOTE: the reason key (‘stage`) is a policy identifier (underscored class name by default). For namespaced policies it has a form of:

class Admin::UserPolicy < ApplicationPolicy
  # ..
end

reasons.details #=> { :"admin/user" => [:show?] }

You can also wrap local rules into ‘allowed_to?` to populate reasons:

class ApplicantPolicy < ApplicationPolicy
  def show?
    allowed_to?(:view_applicants?) &&
      allowed_to?(:show?, object.stage)
  end

  def view_applicants?
    user.has_permission?(:view_applicants)
  end
end

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



82
83
84
# File 'lib/action_policy/policy/reasons.rb', line 82

def included(base)
  base.result_class.include(ResultFailureReasons)
end

Instance Method Details

#allowed_to?(rule, record = :__undef__, **options) ⇒ Boolean

rubocop: disable Metrics/MethodLength

Returns:

  • (Boolean)


88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/action_policy/policy/reasons.rb', line 88

def allowed_to?(rule, record = :__undef__, **options)
  policy = nil

  succeed =
    if record == :__undef__
      policy = self
      with_clean_result { apply(rule) }
    else
      policy = policy_for(record: record, **options)

      policy.apply(rule)
    end

  result.reasons.add(policy, rule) unless succeed
  succeed
end