module ActionPolicy::Policy::Core

Core policy API

Attributes

record[R]
result[R]

Public Class Methods

included(base) click to toggle source
# File lib/action_policy/policy/core.rb, line 43
def included(base)
  base.extend ClassMethods

  # Generate a new class for each _policy chain_
  # in order to extend it independently
  base.module_eval do
    @result_class = Class.new(ExecutionResult)

    # we need to make this class _named_,
    # 'cause anonymous classes couldn't be marshalled
    base.const_set(:APR, @result_class)
  end
end
new(record = nil, *) click to toggle source

NEXT_RELEASE: deprecate `record` arg, migrate to `record: nil`

# File lib/action_policy/policy/core.rb, line 78
def initialize(record = nil, *)
  @record = record
end

Public Instance Methods

__apply__(rule) click to toggle source

This method performs the rule call. Override or extend it to provide custom functionality (such as caching, pre checks, etc.)

# File lib/action_policy/policy/core.rb, line 108
      def __apply__(rule) = public_send(rule)

      # Wrap code that could modify result
      # to prevent the current result modification
      def with_clean_result # :nodoc:
        was_result = @result
        yield
        @result
      ensure
        @result = was_result
      end

      # Returns a result of applying the specified rule to the specified record.
      # Under the hood a policy class for record is resolved
      # (unless it's explicitly set through `with` option).
      #
      # If record is `nil` then we uses the current policy.
      def allowed_to?(rule, record = :__undef__, **options)
        if (record == :__undef__ || record == self.record) && options.empty?
          __apply__(resolve_rule(rule))
        else
          policy_for(record: record, **options).then do |policy|
            policy.apply(policy.resolve_rule(rule))
          end
        end
      end

      # An alias for readability purposes
      def check?(*args, **hargs) = allowed_to?(*args, **hargs)

      # Returns a rule name (policy method name) for activity.
      #
      # By default, rule name is equal to activity name.
      #
      # Raises ActionPolicy::UknownRule when rule is not found in policy.
      def resolve_rule(activity)
        raise UnknownRule.new(self, activity) unless
          respond_to?(activity)
        activity
      end

      # Return annotated source code for the rule
      # NOTE: require "method_source" and "unparser" gems to be installed.
      # Otherwise returns empty string.
      def inspect_rule(rule) = PrettyPrint.print_method(self, rule)

      # Helper for printing the annotated rule source.
      # Useful for debugging: type `pp :show?` within the context of the policy
      # to preview the rule.
      def pp(rule)
        with_clean_result do
          # We need result to exist for `allowed_to?` to work correctly
          @result = self.class.result_class.new(self.class, rule)
          header = "#{self.class.name}##{rule}"
          source = inspect_rule(rule)
          $stdout.puts "#{header}\n#{source}"
        end
      end
    end
  end
end
allow!() click to toggle source
# File lib/action_policy/policy/core.rb, line 100
def allow!
  result&.load true
  throw :policy_fulfilled
end
allowed_to?(rule, record = :__undef__, **options) click to toggle source

Returns a result of applying the specified rule to the specified record. Under the hood a policy class for record is resolved (unless it's explicitly set through `with` option).

If record is `nil` then we uses the current policy.

# File lib/action_policy/policy/core.rb, line 125
def allowed_to?(rule, record = :__undef__, **options)
  if (record == :__undef__ || record == self.record) && options.empty?
    __apply__(resolve_rule(rule))
  else
    policy_for(record: record, **options).then do |policy|
      policy.apply(policy.resolve_rule(rule))
    end
  end
end
apply(rule) click to toggle source

Returns a result of applying the specified rule (true of false). Unlike simply calling a predicate rule (`policy.manage?`), `apply` also calls pre-checks.

# File lib/action_policy/policy/core.rb, line 85
def apply(rule)
  @result = self.class.result_class.new(self.class, rule)

  catch :policy_fulfilled do
    result.load __apply__(rule)
  end

  result.value
end
check?(*args, **hargs) click to toggle source

An alias for readability purposes

# File lib/action_policy/policy/core.rb, line 136
    def check?(*args, **hargs) = allowed_to?(*args, **hargs)

    # Returns a rule name (policy method name) for activity.
    #
    # By default, rule name is equal to activity name.
    #
    # Raises ActionPolicy::UknownRule when rule is not found in policy.
    def resolve_rule(activity)
      raise UnknownRule.new(self, activity) unless
        respond_to?(activity)
      activity
    end

    # Return annotated source code for the rule
    # NOTE: require "method_source" and "unparser" gems to be installed.
    # Otherwise returns empty string.
    def inspect_rule(rule) = PrettyPrint.print_method(self, rule)

    # Helper for printing the annotated rule source.
    # Useful for debugging: type `pp :show?` within the context of the policy
    # to preview the rule.
    def pp(rule)
      with_clean_result do
        # We need result to exist for `allowed_to?` to work correctly
        @result = self.class.result_class.new(self.class, rule)
        header = "#{self.class.name}##{rule}"
        source = inspect_rule(rule)
        $stdout.puts "#{header}\n#{source}"
      end
    end
  end
end
deny!() click to toggle source
# File lib/action_policy/policy/core.rb, line 95
def deny!
  result&.load false
  throw :policy_fulfilled
end
inspect_rule(rule) click to toggle source

Return annotated source code for the rule NOTE: require “method_source” and “unparser” gems to be installed. Otherwise returns empty string.

# File lib/action_policy/policy/core.rb, line 152
  def inspect_rule(rule) = PrettyPrint.print_method(self, rule)

  # Helper for printing the annotated rule source.
  # Useful for debugging: type `pp :show?` within the context of the policy
  # to preview the rule.
  def pp(rule)
    with_clean_result do
      # We need result to exist for `allowed_to?` to work correctly
      @result = self.class.result_class.new(self.class, rule)
      header = "#{self.class.name}##{rule}"
      source = inspect_rule(rule)
      $stdout.puts "#{header}\n#{source}"
    end
  end
end
pp(rule) click to toggle source

Helper for printing the annotated rule source. Useful for debugging: type `pp :show?` within the context of the policy to preview the rule.

# File lib/action_policy/policy/core.rb, line 157
def pp(rule)
  with_clean_result do
    # We need result to exist for `allowed_to?` to work correctly
    @result = self.class.result_class.new(self.class, rule)
    header = "#{self.class.name}##{rule}"
    source = inspect_rule(rule)
    $stdout.puts "#{header}\n#{source}"
  end
end
resolve_rule(activity) click to toggle source

Returns a rule name (policy method name) for activity.

By default, rule name is equal to activity name.

Raises ActionPolicy::UknownRule when rule is not found in policy.

# File lib/action_policy/policy/core.rb, line 143
def resolve_rule(activity)
  raise UnknownRule.new(self, activity) unless
    respond_to?(activity)
  activity
end