Class: Cancannible::Preloader

Inherits:
Object
  • Object
show all
Defined in:
lib/cancannible/preloader.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(grantee, cancan_ability_object) ⇒ Preloader

Returns a new instance of Preloader.



9
10
11
12
# File 'lib/cancannible/preloader.rb', line 9

def initialize(grantee, cancan_ability_object)
  self.grantee = grantee
  self.cancan_ability_object = cancan_ability_object
end

Instance Attribute Details

#cancan_ability_objectObject

Returns the value of attribute cancan_ability_object.



7
8
9
# File 'lib/cancannible/preloader.rb', line 7

def cancan_ability_object
  @cancan_ability_object
end

#granteeObject

Returns the value of attribute grantee.



6
7
8
# File 'lib/cancannible/preloader.rb', line 6

def grantee
  @grantee
end

Class Method Details

.preload_abilities!(grantee, cancan_ability_object) ⇒ Object



2
3
4
# File 'lib/cancannible/preloader.rb', line 2

def self.preload_abilities!(grantee, cancan_ability_object)
  new(grantee, cancan_ability_object).preload!
end

Instance Method Details

#preload!Object



14
15
16
17
18
19
20
21
22
23
# 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) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 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) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# 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) ⇒ Object



66
67
68
69
70
71
72
# 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