class Cancannible::Preloader

Attributes

cancan_ability_object[RW]
grantee[RW]

Public Class Methods

new(grantee, cancan_ability_object) click to toggle source
# File lib/cancannible/preloader.rb, line 9
def initialize(grantee, cancan_ability_object)
  self.grantee = grantee
  self.cancan_ability_object = cancan_ability_object
end
preload_abilities!(grantee, cancan_ability_object) click to toggle source
# File lib/cancannible/preloader.rb, line 2
def self.preload_abilities!(grantee, cancan_ability_object)
  new(grantee, cancan_ability_object).preload!
end

Public Instance Methods

preload!() click to toggle source
# File lib/cancannible/preloader.rb, line 14
def preload!
  return unless grantee.respond_to?(:inherited_permissions)

  # load inherited permissions to CanCan Abilities
  preload_abilities_from_permissions(grantee.inherited_permissions)
  # load user-based permissions from database to CanCan Abilities
  preload_abilities_from_permissions(grantee.permissions.reload)
  # return the ability object
  cancan_ability_object
end
preload_abilities_from_permissions(perms) click to toggle source
# File lib/cancannible/preloader.rb, line 25
def preload_abilities_from_permissions(perms)
  perms.each do |permission|
    ability = permission.ability.to_sym
    action = permission.asserted ? :can : :cannot

    resource_type,model_resource = resolve_resource_type(permission.resource_type)

    if !resource_type || resource_type.is_a?(Symbol)
      # nil or symbolic resource types: apply generic unrestricted permission to the resource_type
      cancan_ability_object.send( action,  ability, resource_type )
      next
    else
      # model-based resource types: skip if we cannot get a model instance
      next unless model_resource
    end

    if permission.resource_id.nil?
      if action == :cannot
        # apply generic unrestricted permission to the class
        cancan_ability_object.send(action, ability, resource_type)
      else
        refinements = resolve_resource_refinements(ability,model_resource)

        if refinements.empty?
          # apply generic unrestricted permission to the class
          cancan_ability_object.send(action, ability, resource_type)
        else
          secondary_refinements = resolve_resource_refinements(ability,model_resource,2).presence || [{}]
          refinements.each do |refinement|
            secondary_refinements.each do |secondary_refinement|
              cancan_ability_object.send( action,  ability, resource_type, refinement.merge(secondary_refinement))
            end
          end
        end
      end
    elsif resource_type.find_by_id(permission.resource_id)
      cancan_ability_object.send( action,  ability, resource_type, id: permission.resource_id)
    end
  end
end
resolve_resource_refinements(ability, model_resource, stage=1) click to toggle source
# File lib/cancannible/preloader.rb, line 74
def resolve_resource_refinements(ability, model_resource, stage=1)
  Array(Cancannible.refinements[stage-1]).each_with_object([]) do |refinement,memo|
    refinement_attributes = refinement.dup

    allow_nil = !!(refinement_attributes.delete(:allow_nil))

    refinement_if_condition = refinement_attributes.delete(:if)
    next if refinement_if_condition.respond_to?(:call) && !refinement_if_condition.call(grantee,model_resource)

    refinement_scope = Array(refinement_attributes.delete(:scope))
    next if refinement_scope.present? &&  !refinement_scope.include?(ability)

    refinement_except = Array(refinement_attributes.delete(:except))
    next if refinement_except.present? &&  refinement_except.include?(ability)

    refinement_attribute_names = refinement_attributes.keys.map{|k| "#{k}" }
    next unless (refinement_attribute_names - model_resource.attribute_names).empty?

    restriction = {}
    refinement_attributes.each do |key,value|
      if value.is_a?(Symbol)
        if grantee.respond_to?(value)
          restriction[key] = if allow_nil
            Array(grantee.send(value)) + [nil]
          else
            grantee.send(value)
          end
        end
      else
        restriction[key] = value
      end
    end
    memo.push(restriction) if restriction.present?
  end
end
resolve_resource_type(given_resource_type) click to toggle source
# File lib/cancannible/preloader.rb, line 66
def resolve_resource_type(given_resource_type)
  model_resource = nil
  resource_type = given_resource_type
  resource_type = resource_type == resource_type.downcase ? resource_type.to_sym : resource_type.constantize rescue nil
  model_resource = resource_type.respond_to?(:new) ? resource_type.new : resource_type rescue nil
  [resource_type,model_resource]
end