class Sevn::Ability

Public Class Methods

new(packs={}) click to toggle source

Initialize ability object

Parameters:

packs

A Hash or rules to add with initialization

Returns:

self

# File lib/sevn/ability.rb, line 12
def initialize(packs={})
  raise Sevn::Errors::InitializeArgumentError.new unless packs.kind_of?(Hash)

  @rules_packs = {}

  packs.each { |name, pack| add_pack(name, pack) }
end

Public Instance Methods

allowed?(object, actions, subject, options = {}) click to toggle source

Check if object can do actions in subject

Basically this method

  1. determine which rules pack it should use, by priority it would check:

- Use pack defined in options[:use_pack]
- Use pack defined by object method :sevn_rule_pack
- Use pack defined by object's class method :sevn_rule_pack
- Underscore object's class, and look for it
  1. check if any of results include allowed action

Parameters:

actions

Symbol or Array of Symbols of the actions to check

object

object trying to access resource

subject

resource to be accessed

options

a list of options to consider when checking.

Options:

use_pack

check for actions in the specified pack instead of auto-determining the pack.

Returns:

true or false

Exceptions:

if no pack can be determined for the current subject, it will raise a NoPackError

# File lib/sevn/ability.rb, line 50
def allowed?(object, actions, subject, options = {})
  # if multiple actions passed, check all actions to be allowed
  if actions.respond_to?(:each)
    actions.all? { |action| action_allowed?(object, action, subject, options) }
  else
    # single action check
    action_allowed?(object, actions, subject, options)
  end
end
authorize!(object, actions, subject, options = {}) click to toggle source

Check if object is authorized to do actions in subject if action is not allowed it will raise an Unauthorized error

Parameters:

actions

Symbol or Array of Symbols of the actions to check

object

object trying to access resource

subject

resource to be accessed

options

a list of options to consider when checking.

Options:

use_pack

check for actions in the specified pack instead of auto-determining the pack.

Returns:

subject

Exceptions:

if object is not allowed to do action on subject, it will raise an UnauthorizedError

# File lib/sevn/ability.rb, line 83
def authorize!(object, actions, subject, options = {})
  if !allowed?(object, actions, subject, options)
    raise Sevn::Errors::UnauthorizedError.new(object, actions, subject)
  end
  subject
end

Private Instance Methods

action_allowed?(object, action, subjects, options) click to toggle source
# File lib/sevn/ability.rb, line 107
def action_allowed?(object, action, subjects, options)
  if subjects.respond_to?(:each)
    # if subjects is an Array, let's group them by class
    # check if action is allowed for the whole class or to all the subjects of that class
    subjects.group_by(&:class).all? do |class_name, subjects_of_class|
      action_allowed_for?(object, action, class_name, options) ||
      subjects_of_class.all? { |subject| action_allowed_for?(object, action, subject, options) }
    end
  else
    # if subject is a single object, check if action is allowed for that object
    action_allowed_for?(object, action, subjects, options)
  end
end
action_allowed_for?(object, action, subject, options) click to toggle source
# File lib/sevn/ability.rb, line 121
def action_allowed_for?(object, action, subject, options)
  determine_rule_pack(subject, options).allowed?(object, action, subject)
end
add_pack(name, pack) click to toggle source
# File lib/sevn/ability.rb, line 91
def add_pack(name, pack)
  if valid_rules_pack?(pack)
    @rules_packs[name.to_sym] = pack
  else
    raise Sevn::Errors::InvalidPackPassed.new
  end
end
determine_rule_pack(subject, options) click to toggle source
# File lib/sevn/ability.rb, line 125
def determine_rule_pack(subject, options)
  if options.has_key?(:use_pack)
    pack = options[:use_pack]
    @rules_packs[pack] || raise(Sevn::Errors::NoPackError.new(pack, true))
  elsif subject.kind_of?(Class)
    get_class_rule_pack(subject) || raise(Sevn::Errors::NoPackError.new(subject.name))
  else
    get_instance_rule_pack(subject) || raise(Sevn::Errors::NoPackError.new(subject.class.name))
  end
end
get_class_rule_pack(subject) click to toggle source
# File lib/sevn/ability.rb, line 136
def get_class_rule_pack(subject)
  if subject.respond_to?(:sevn_rule_pack) && pack_exist?(subject.sevn_rule_pack)
    @rules_packs[subject.sevn_rule_pack]
  elsif String.method_defined?(:underscore) && pack_exist?(subject.name.underscore.to_sym)
    @rules_packs[subject.name.underscore.to_sym]
  elsif pack_exist?(underscore(subject.name).to_sym)
    @rules_packs[underscore(subject.name).to_sym]
  end
end
get_instance_rule_pack(subject) click to toggle source
# File lib/sevn/ability.rb, line 146
def get_instance_rule_pack(subject)
  if subject.respond_to?(:sevn_rule_pack) && pack_exist?(subject.sevn_rule_pack)
    @rules_packs[subject.sevn_rule_pack]
  elsif subject.class.respond_to?(:sevn_rule_pack) && pack_exist?(subject.class.sevn_rule_pack)
    @rules_packs[subject.class.sevn_rule_pack]
  elsif String.method_defined?(:underscore) && pack_exist?(subject.class.name.underscore.to_sym)
    @rules_packs[subject.class.name.underscore.to_sym]
  elsif pack_exist?(underscore(subject.class.name).to_sym)
    @rules_packs[underscore(subject.class.name).to_sym]
  end
end
pack_exist?(name) click to toggle source
# File lib/sevn/ability.rb, line 99
def pack_exist?(name)
  @rules_packs.has_key?(name.to_sym)
end
underscore(class_name) click to toggle source

Rails adds :underscore to the String class In case the underscore method is not defined, we define our own.

# File lib/sevn/ability.rb, line 160
def underscore(class_name)
  class_name.gsub(/::/, '/').
      gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
      gsub(/([a-z\d])([A-Z])/,'\1_\2').
      tr("-", "_").
      downcase
end
valid_rules_pack?(pack) click to toggle source
# File lib/sevn/ability.rb, line 103
def valid_rules_pack?(pack)
  pack.kind_of?(Sevn::RulesPack)
end