Skip to content

Instantly share code, notes, and snippets.

@AMHOL
Last active June 29, 2016 00:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AMHOL/b0f3e3c3e7b34c155acdddda3eee6f2a to your computer and use it in GitHub Desktop.
Save AMHOL/b0f3e3c3e7b34c155acdddda3eee6f2a to your computer and use it in GitHub Desktop.
module Dry
module Policy
Unauthorized = Class.new(StandardError)
def self.included(klass)
klass.extend(Dry::Container::Mixin)
klass.extend(ClassMethods)
klass.setting(:unauthorized_exception, Dry::Policy::Unauthorized)
end
module ClassMethods
def check(subject, ability, verb, **object)
key = _resolve_key(ability, verb, object.keys.first)
self[key].call(subject, object.values.first)
end
def abilities(*abilities)
abilities.each { |ability| _add_ability(ability) }
end
def ability(ability, verb, object_identitifer = nil, &policy)
register(
_resolve_key(ability, verb, object_identitifer),
&policy
)
end
def _resolve_key(*parts)
parts.compact.join(config.namespace_separator)
end
private
def _add_ability(ability)
define_method(ability) do |verb, **object|
self.class.check(subject, ability, verb, **object)
end
alias_method "#{ability}?", ability
end
end
attr_reader :subject
private :subject
def initialize(subject)
@subject = subject
end
def check(ability, verb, **object)
self.class.check(subject, ability, verb, **object)
end
def ensure!(ability, verb, **object)
check(ability, verb, **object) || fail(
self.class.config.unauthorized_exception
)
end
end
end
@AMHOL
Copy link
Author

AMHOL commented Apr 15, 2016

User = Struct.new(:id, :name, :event_ids)
Event = Struct.new(:id, :name, :owner_id)

class User
  class Policy
    include Dry::Policy

    abilities :can, :is

    ability :is, :admin do |user|
      user.id == 1
    end

    ability :is, :owner_of, :event do |user, event|
      event.owner_id == user.id
    end

    ability :can, :access, :event do |user, event|
      check(user, :is, :admin) || user.event_ids.include?(event.id)
    end
  end
end

admin = User.new(1, 'John', [])
jill = User.new(2, 'Jill', [2, 3])
jack = User.new(3, 'Jack', [1, 3])

event_1 = Event.new(1, 'Rave', 1)
event_2 = Event.new(2, 'Quiet night in', 2)
event_3 = Event.new(3, 'Clubbing', 2)

admin_policy = User::Policy.new(admin)
admin_policy.can?(:access, event: event_1)
# => true
admin_policy.ensure!(:can, :access, event: event_1) rescue 'fail'
# => true
admin_policy.can?(:access, event: event_2)
# => true
admin_policy.ensure!(:can, :access, event: event_2) rescue 'fail'
# => true
admin_policy.can?(:access, event: event_3)
# => true
admin_policy.ensure!(:can, :access, event: event_3) rescue 'fail'
# => true
admin_policy.is?(:owner_of, event: event_1)
# => true
admin_policy.ensure!(:is, :owner_of, event: event_1) rescue 'fail'
# => true
admin_policy.is?(:owner_of, event: event_2)
# => false
admin_policy.ensure!(:is, :owner_of, event: event_2) rescue 'fail'
# => "fail"
admin_policy.is?(:owner_of, event: event_3)
# => false
admin_policy.ensure!(:is, :owner_of, event: event_3) rescue 'fail'
# => "fail"


jill_policy = User::Policy.new(jill)
jill_policy.can?(:access, event: event_1)
# => false
jill_policy.ensure!(:can, :access, event: event_1) rescue 'success'
# => "success"
jill_policy.can?(:access, event: event_2)
# => true
jill_policy.ensure!(:can, :access, event: event_2) rescue 'fail'
# => true
jill_policy.can?(:access, event: event_3)
# => true
jill_policy.ensure!(:can, :access, event: event_3) rescue 'fail'
# => true
jill_policy.is?(:owner_of, event: event_1)
# => false
jill_policy.ensure!(:is, :owner_of, event: event_1) rescue 'fail'
# => "fail"
jill_policy.is?(:owner_of, event: event_2)
# => true
jill_policy.ensure!(:is, :owner_of, event: event_2) rescue 'fail'
# => true
jill_policy.is?(:owner_of, event: event_3)
# => true
jill_policy.ensure!(:is, :owner_of, event: event_3) rescue 'fail'
# => true

jack_policy = User::Policy.new(jack)
jack_policy.can?(:access, event: event_1)
# => true
jack_policy.ensure!(:can, :access, event: event_1) rescue 'fail'
# => true
jack_policy.can?(:access, event: event_2)
# => false
jack_policy.ensure!(:can, :access, event: event_2) rescue 'success'
# => "success"
jack_policy.can?(:access, event: event_3)
# => true
jack_policy.ensure!(:can, :access, event: event_3) rescue 'fail'
# => true
jack_policy.is?(:owner_of, event: event_1)
# => false
jack_policy.ensure!(:is, :owner_of, event: event_1) rescue 'fail'
# => "fail"
jack_policy.is?(:owner_of, event: event_2)
# => false
jack_policy.ensure!(:is, :owner_of, event: event_2) rescue 'fail'
# => "fail"
jack_policy.is?(:owner_of, event: event_3)
# => false
jack_policy.ensure!(:is, :owner_of, event: event_3) rescue 'fail'
# => "fail"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment