module Dry::Ability

Mixin class with DSL to define abilities

@example

class Ability
  include Dry::Ability.define -> do
    map_subject! :public => %w(Post Like Comment)

    map_action!  :read   => %i(index show),
                 :create => %i(new),
                 :update => %i(edit),
                 :crud   => %i(index create read show update destroy),
                 :change => %i(update destroy)

    can :read, :public
    can :

  end
end

Constants

VERSION

Attributes

account[R]

Public Class Methods

new(account) click to toggle source
# File lib/dry/ability.rb, line 55
def initialize(account)
  @account = account
end

Public Instance Methods

accessible?() click to toggle source
# File lib/dry/ability/rule.rb, line 55
def accessible?
  scope? || !@explicit_scope
end
attributes_for(action, subject) click to toggle source
# File lib/dry/ability.rb, line 81
def attributes_for(action, subject)
  rules = resolve_rules(action, subject) do
    return {}
  end
  rules.reduce({}) do |result, rule|
    result.merge!(rule.attributes_for(@account, subject)); result
  end
end
authorize!(action, subject, message: nil) click to toggle source
# File lib/dry/ability.rb, line 59
def authorize!(action, subject, message: nil)
  if can?(action, subject)
    subject
  else
    raise AccessDenied.new(message, action, subject)
  end
end
before(controller) click to toggle source
# File lib/dry/ability/resource_mediator.rb, line 72
def before(controller)
  resource_class(controller).new(self, controller).call
end
call(account, object) click to toggle source
# File lib/dry/ability/rule.rb, line 22
def call(account, object)
  if filter?
    filter.(account, object)
  else
    @constraints.blank? || run_constraints(account, object, @constraints)
  end ^ @inverse
end
can?(action, subject) click to toggle source
# File lib/dry/ability.rb, line 67
def can?(action, subject)
  rules = resolve_rules(action, subject) do
    return false
  end

  rules.reduce(true) do |result, rule|
    result && rule[@account, subject]
  end
end
cannot?(action, subject, *args) click to toggle source
# File lib/dry/ability.rb, line 77
def cannot?(action, subject, *args)
  !can?(action, subject, *args)
end
collection_action?(action_name, *) click to toggle source
# File lib/dry/ability/resource_mediator.rb, line 89
def collection_action?(action_name, *)
  @collection_actions.include?(action_name)
end
filter?() click to toggle source
# File lib/dry/ability/rule.rb, line 47
def filter?
  !@filter.nil?
end
member_action?(action_name, params) click to toggle source
# File lib/dry/ability/resource_mediator.rb, line 84
def member_action?(action_name, params)
  @new_actions.include?(action_name) || singleton? ||
    ((params[:id] || params[@id_param_key]) && !@collection_actions.include?(action_name))
end
register_to(_rules) click to toggle source
# File lib/dry/ability/rule.rb, line 59
def register_to(_rules)
  unless defined?(@_registered)
    @subjects.each do |subject|
      _rules.namespace(subject) do |_subject|
        @actions.each do |action|
          key = _subject.send(:namespaced, action)
          pred = _rules._container.delete(key)&.call
          rules_or = pred | self if pred
          _subject.register action, (rules_or || self)
        end
      end
    end
    @_registered = true
  end
end
resolve_rules(action, subject) { |: nil| ... } click to toggle source
# File lib/dry/ability.rb, line 109
def resolve_rules(action, subject)
  rules.resolve_with_mappings(action, subject) do |e|
    Rails.logger.warn { e.message }
    block_given? ? yield : nil
  end
end
resource_class(controller) click to toggle source
# File lib/dry/ability/resource_mediator.rb, line 76
def resource_class(controller)
  if defined?(::InheritedResources) && controller.is_a?(::InheritedResources::Actions)
    InheritedResource
  else
    ControllerResource
  end
end
scope?() click to toggle source
# File lib/dry/ability/rule.rb, line 51
def scope?
  !@scope.nil?
end
scope_for(action, subject) { || ... } click to toggle source
# File lib/dry/ability.rb, line 90
def scope_for(action, subject)
  rules = resolve_rules(action, subject) do
    return yield if block_given?
    if subject.respond_to?(:none)
      return subject.none
    else
      raise ArgumentError, "expected subject to be an ActiveRecord::Base class or Relation. given: #{subject}"
    end
  end
  if rules.none?(&:accessible?)
    if block_given?
      return yield
    else
      raise Error, "none of matched rules are provides scope for #{action}, #{subject}, pass block instead"
    end
  end
  rules.map { |rule| rule.scope_for(@account, subject) }.reduce(:merge)
end
|(other) click to toggle source
# File lib/dry/ability/rule.rb, line 75
def |(other)
  Or.new([self, other])
end

Private Instance Methods

call_constraint(account, object, key, constraint) click to toggle source
# File lib/dry/ability/rule.rb, line 126
def call_constraint(account, object, key, constraint)
  value = object.public_send(key)
  case constraint
  when Array
    constraint.include?(value)
  when Hash
    run_constraints(account, value, constraint)
  when Proc
    constraint.arity > 1 ? constraint.(account, value) : value == constraint.(account)
  else
    value == constraint
  end
end
run_constraints(account, object, dict) click to toggle source
# File lib/dry/ability/rule.rb, line 116
def run_constraints(account, object, dict)
  case object when Class, Symbol
    true
  else
    dict.reduce(true) do |pred, (key, value)|
      pred && call_constraint(account, object, key, value)
    end
  end
end