refactoring much of Ability class into separate CanDefinition class
This commit is contained in:
parent
232ecd5b4b
commit
bbbc8a68e0
|
@ -1,4 +1,5 @@
|
||||||
require 'cancan/ability'
|
require 'cancan/ability'
|
||||||
|
require 'cancan/can_definition'
|
||||||
require 'cancan/controller_resource'
|
require 'cancan/controller_resource'
|
||||||
require 'cancan/resource_authorization'
|
require 'cancan/resource_authorization'
|
||||||
require 'cancan/controller_additions'
|
require 'cancan/controller_additions'
|
||||||
|
|
|
@ -48,11 +48,8 @@ module CanCan
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
def can?(action, subject, *extra_args)
|
def can?(action, subject, *extra_args)
|
||||||
matching_can_definition(action, subject) do |base_behavior, defined_actions, defined_subjects, defined_conditions, defined_block|
|
can_definition = matching_can_definition(action, subject)
|
||||||
result = can_perform_action?(action, subject, defined_actions, defined_subjects, defined_conditions, defined_block, extra_args)
|
can_definition && can_definition.can?(action, subject, extra_args)
|
||||||
return base_behavior ? result : !result
|
|
||||||
end
|
|
||||||
false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Convenience method which works the same as "can?" but returns the opposite value.
|
# Convenience method which works the same as "can?" but returns the opposite value.
|
||||||
|
@ -117,8 +114,7 @@ module CanCan
|
||||||
# can? :read, :stats # => true
|
# can? :read, :stats # => true
|
||||||
#
|
#
|
||||||
def can(action, subject, conditions = nil, &block)
|
def can(action, subject, conditions = nil, &block)
|
||||||
@can_definitions ||= []
|
can_definitions << CanDefinition.new(true, action, subject, conditions, block)
|
||||||
@can_definitions << [true, action, subject, conditions, block]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Define an ability which cannot be done. Accepts the same arguments as "can".
|
# Define an ability which cannot be done. Accepts the same arguments as "can".
|
||||||
|
@ -134,8 +130,7 @@ module CanCan
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
def cannot(action, subject, conditions = nil, &block)
|
def cannot(action, subject, conditions = nil, &block)
|
||||||
@can_definitions ||= []
|
can_definitions << CanDefinition.new(false, action, subject, conditions, block)
|
||||||
@can_definitions << [false, action, subject, conditions, block]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Alias one or more actions into another one.
|
# Alias one or more actions into another one.
|
||||||
|
@ -195,22 +190,25 @@ module CanCan
|
||||||
# If the ability is defined using a block then this will raise an exception since a hash of conditions cannot be
|
# If the ability is defined using a block then this will raise an exception since a hash of conditions cannot be
|
||||||
# determined from that.
|
# determined from that.
|
||||||
def conditions(action, subject)
|
def conditions(action, subject)
|
||||||
matching_can_definition(action, subject) do |base_behavior, defined_actions, defined_subjects, defined_conditions, defined_block|
|
can_definition = matching_can_definition(action, subject)
|
||||||
raise Error, "Cannot determine ability conditions from block for #{action.inspect} #{subject.inspect}" if defined_block
|
if can_definition
|
||||||
return defined_conditions || {}
|
raise Error, "Cannot determine ability conditions from block for #{action.inspect} #{subject.inspect}" if can_definition.block
|
||||||
end
|
can_definition.conditions || {}
|
||||||
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def matching_can_definition(action, subject, &block)
|
def can_definitions
|
||||||
(@can_definitions || []).reverse.each do |base_behavior, defined_action, defined_subject, defined_conditions, defined_block|
|
@can_definitions ||= []
|
||||||
defined_actions = expand_actions(defined_action)
|
|
||||||
defined_subjects = [defined_subject].flatten
|
|
||||||
if includes_action?(defined_actions, action) && includes_subject?(defined_subjects, subject)
|
|
||||||
return block.call(base_behavior, defined_actions, defined_subjects, defined_conditions, defined_block)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def matching_can_definition(action, subject)
|
||||||
|
can_definitions.reverse.detect do |can_definition|
|
||||||
|
can_definition.expand_actions(aliased_actions)
|
||||||
|
can_definition.matches? action, subject
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -222,48 +220,6 @@ module CanCan
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def expand_actions(actions)
|
|
||||||
[actions].flatten.map do |action|
|
|
||||||
if aliased_actions[action]
|
|
||||||
[action, *aliased_actions[action]]
|
|
||||||
else
|
|
||||||
action
|
|
||||||
end
|
|
||||||
end.flatten
|
|
||||||
end
|
|
||||||
|
|
||||||
def can_perform_action?(action, subject, defined_actions, defined_subjects, defined_conditions, defined_block, extra_args)
|
|
||||||
if defined_block
|
|
||||||
block_args = []
|
|
||||||
block_args << action if defined_actions.include?(:manage)
|
|
||||||
block_args << (subject.class == Class ? subject : subject.class) if defined_subjects.include?(:all)
|
|
||||||
block_args << (subject.class == Class ? nil : subject)
|
|
||||||
block_args += extra_args
|
|
||||||
defined_block.call(*block_args)
|
|
||||||
elsif defined_conditions
|
|
||||||
if subject.class == Class
|
|
||||||
true
|
|
||||||
else
|
|
||||||
matches_conditions? subject, defined_conditions
|
|
||||||
end
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def matches_conditions?(subject, defined_conditions)
|
|
||||||
defined_conditions.all? do |name, value|
|
|
||||||
attribute = subject.send(name)
|
|
||||||
if value.kind_of?(Hash)
|
|
||||||
matches_conditions? attribute, value
|
|
||||||
elsif value.kind_of?(Array) || value.kind_of?(Range)
|
|
||||||
value.include? attribute
|
|
||||||
else
|
|
||||||
attribute == value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def includes_action?(actions, action)
|
def includes_action?(actions, action)
|
||||||
actions.include?(:manage) || actions.include?(action)
|
actions.include?(:manage) || actions.include?(action)
|
||||||
end
|
end
|
||||||
|
|
71
lib/cancan/can_definition.rb
Normal file
71
lib/cancan/can_definition.rb
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
module CanCan
|
||||||
|
# This class is used internally and should only be called through Ability.
|
||||||
|
class CanDefinition # :nodoc:
|
||||||
|
attr_reader :conditions, :block
|
||||||
|
|
||||||
|
def initialize(base_behavior, action, subject, conditions, block)
|
||||||
|
@base_behavior = base_behavior
|
||||||
|
@actions = [action].flatten
|
||||||
|
@subjects = [subject].flatten
|
||||||
|
@conditions = conditions
|
||||||
|
@block = block
|
||||||
|
end
|
||||||
|
|
||||||
|
def expand_actions(aliased_actions)
|
||||||
|
@expanded_actions = @actions.map do |action|
|
||||||
|
aliased_actions[action] ? [action, *aliased_actions[action]] : action
|
||||||
|
end.flatten
|
||||||
|
end
|
||||||
|
|
||||||
|
def matches?(action, subject)
|
||||||
|
matches_action?(action) && matches_subject?(subject)
|
||||||
|
end
|
||||||
|
|
||||||
|
def can?(action, subject, extra_args)
|
||||||
|
result = can_without_base_behavior?(action, subject, extra_args)
|
||||||
|
@base_behavior ? result : !result
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def matches_action?(action)
|
||||||
|
@expanded_actions.include?(:manage) || @expanded_actions.include?(action)
|
||||||
|
end
|
||||||
|
|
||||||
|
def matches_subject?(subject)
|
||||||
|
@subjects.include?(:all) || @subjects.include?(subject) || @subjects.any? { |c| c.kind_of?(Class) && subject.kind_of?(c) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_without_base_behavior?(action, subject, extra_args)
|
||||||
|
if @block
|
||||||
|
call_block(action, subject, extra_args)
|
||||||
|
elsif @conditions && subject.class != Class
|
||||||
|
matches_conditions? subject
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def matches_conditions?(subject, conditions = @conditions)
|
||||||
|
conditions.all? do |name, value|
|
||||||
|
attribute = subject.send(name)
|
||||||
|
if value.kind_of?(Hash)
|
||||||
|
matches_conditions? attribute, value
|
||||||
|
elsif value.kind_of?(Array) || value.kind_of?(Range)
|
||||||
|
value.include? attribute
|
||||||
|
else
|
||||||
|
attribute == value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def call_block(action, subject, extra_args)
|
||||||
|
block_args = []
|
||||||
|
block_args << action if @expanded_actions.include?(:manage)
|
||||||
|
block_args << (subject.class == Class ? subject : subject.class) if @subjects.include?(:all)
|
||||||
|
block_args << (subject.class == Class ? nil : subject)
|
||||||
|
block_args += extra_args
|
||||||
|
@block.call(*block_args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user