modifying Ability to use symbol for subject instead of class, also adding subject aliases
This commit is contained in:
@@ -8,7 +8,7 @@ module CanCan
|
||||
#
|
||||
# def initialize(user)
|
||||
# if user.admin?
|
||||
# can :manage, :all
|
||||
# can :access, :all
|
||||
# else
|
||||
# can :read, :all
|
||||
# end
|
||||
@@ -78,11 +78,11 @@ module CanCan
|
||||
#
|
||||
# can [:update, :destroy], [Article, Comment]
|
||||
#
|
||||
# You can pass :all to match any object and :manage to match any action. Here are some examples.
|
||||
# You can pass :all to match any object and :access to match any action. Here are some examples.
|
||||
#
|
||||
# can :manage, :all
|
||||
# can :access, :all
|
||||
# can :update, :all
|
||||
# can :manage, Project
|
||||
# can :access, Project
|
||||
#
|
||||
# You can pass a hash of conditions as the third argument. Here the user can only see active projects which he owns.
|
||||
#
|
||||
@@ -158,11 +158,6 @@ module CanCan
|
||||
# can :update, Comment
|
||||
# can? :modify, Comment # => false
|
||||
#
|
||||
# Unless that exact alias is used.
|
||||
#
|
||||
# can :modify, Comment
|
||||
# can? :modify, Comment # => true
|
||||
#
|
||||
# The following aliases are added by default for conveniently mapping common controller actions.
|
||||
#
|
||||
# alias_action :index, :show, :to => :read
|
||||
@@ -172,18 +167,42 @@ module CanCan
|
||||
# This way one can use params[:action] in the controller to determine the permission.
|
||||
def alias_action(*args)
|
||||
target = args.pop[:to]
|
||||
aliased_actions[target] ||= []
|
||||
aliased_actions[target] += args
|
||||
aliases[:actions][target] ||= []
|
||||
aliases[:actions][target] += args
|
||||
end
|
||||
|
||||
# Returns a hash of aliased actions. The key is the target and the value is an array of actions aliasing the key.
|
||||
def aliased_actions
|
||||
@aliased_actions ||= default_alias_actions
|
||||
# Alias one or more subjects into another one.
|
||||
#
|
||||
# alias_subject :admins, :moderators, :to => :users
|
||||
# can :update, :users
|
||||
#
|
||||
# Then :modify permission will apply to both :update and :destroy requests.
|
||||
#
|
||||
# can? :update, :admins # => true
|
||||
# can? :update, :moderators # => true
|
||||
#
|
||||
# This only works in one direction. Passing the aliased subject into the "can?" call
|
||||
# will not work because aliases are meant to generate more generic subjects.
|
||||
#
|
||||
# alias_subject :admins, :moderators, :to => :users
|
||||
# can :update, :admins
|
||||
# can? :update, :users # => false
|
||||
#
|
||||
def alias_subject(*args)
|
||||
target = args.pop[:to]
|
||||
aliases[:subjects][target] ||= []
|
||||
aliases[:subjects][target] += args
|
||||
end
|
||||
|
||||
# Removes previously aliased actions including the defaults.
|
||||
def clear_aliased_actions
|
||||
@aliased_actions = {}
|
||||
# Returns a hash of action and subject aliases.
|
||||
def aliases
|
||||
@aliases ||= default_aliases
|
||||
end
|
||||
|
||||
# Removes previously aliased actions or subjects including the defaults.
|
||||
def clear_aliases
|
||||
aliases[:actions] = {}
|
||||
aliases[:subjects] = {}
|
||||
end
|
||||
|
||||
def model_adapter(model_class, action)
|
||||
@@ -206,7 +225,7 @@ module CanCan
|
||||
def unauthorized_message(action, subject)
|
||||
keys = unauthorized_message_keys(action, subject)
|
||||
variables = {:action => action.to_s}
|
||||
variables[:subject] = (subject.class == Class ? subject : subject.class).to_s.underscore.humanize.downcase
|
||||
variables[:subject] = (subject.kind_of?(Symbol) ? subject.to_s : subject.class.to_s.underscore.humanize.downcase.pluralize)
|
||||
message = I18n.translate(nil, variables.merge(:scope => :unauthorized, :default => keys + [""]))
|
||||
message.blank? ? nil : message
|
||||
end
|
||||
@@ -230,9 +249,9 @@ module CanCan
|
||||
private
|
||||
|
||||
def unauthorized_message_keys(action, subject)
|
||||
subject = (subject.class == Class ? subject : subject.class).name.underscore unless subject.kind_of? Symbol
|
||||
[subject, :all].map do |try_subject|
|
||||
[aliases_for_action(action), :manage].flatten.map do |try_action|
|
||||
subject = (subject.kind_of?(Symbol) ? subject.to_s : subject.class.to_s.underscore.humanize.downcase.pluralize)
|
||||
[aliases_for(:subjects, subject.to_sym), :all].flatten.map do |try_subject|
|
||||
[aliases_for(:actions, action.to_sym), :access].flatten.map do |try_action|
|
||||
:"#{try_action}.#{try_subject}"
|
||||
end
|
||||
end.flatten
|
||||
@@ -241,18 +260,18 @@ module CanCan
|
||||
# Accepts an array of actions and returns an array of actions which match.
|
||||
# This should be called before "matches?" and other checking methods since they
|
||||
# rely on the actions to be expanded.
|
||||
def expand_actions(actions)
|
||||
actions.map do |action|
|
||||
aliased_actions[action] ? [action, *expand_actions(aliased_actions[action])] : action
|
||||
def expand_aliases(type, items)
|
||||
items.map do |item|
|
||||
aliases[type][item] ? [item, *expand_aliases(type, aliases[type][item])] : item
|
||||
end.flatten
|
||||
end
|
||||
|
||||
# Given an action, it will try to find all of the actions which are aliased to it.
|
||||
# This does the opposite kind of lookup as expand_actions.
|
||||
def aliases_for_action(action)
|
||||
# This does the opposite kind of lookup as expand_aliases.
|
||||
def aliases_for(type, action)
|
||||
results = [action]
|
||||
aliased_actions.each do |aliased_action, actions|
|
||||
results += aliases_for_action(aliased_action) if actions.include? action
|
||||
aliases[type].each do |aliased_action, actions|
|
||||
results += aliases_for(type, aliased_action) if actions.include? action
|
||||
end
|
||||
results
|
||||
end
|
||||
@@ -265,7 +284,8 @@ module CanCan
|
||||
# This does not take into consideration any hash conditions or block statements
|
||||
def relevant_rules(action, subject)
|
||||
rules.reverse.select do |rule|
|
||||
rule.expanded_actions = expand_actions(rule.actions)
|
||||
rule.expanded_actions = expand_aliases(:actions, rule.actions)
|
||||
rule.expanded_subjects = expand_aliases(:subjects, rule.subjects)
|
||||
rule.relevant? action, subject
|
||||
end
|
||||
end
|
||||
@@ -286,11 +306,15 @@ module CanCan
|
||||
end
|
||||
end
|
||||
|
||||
def default_alias_actions
|
||||
def default_aliases
|
||||
{
|
||||
:read => [:index, :show],
|
||||
:create => [:new],
|
||||
:update => [:edit],
|
||||
:subjects => {},
|
||||
:actions => {
|
||||
:read => [:index, :show],
|
||||
:create => [:new],
|
||||
:update => [:edit],
|
||||
:destroy => [:delete],
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ module CanCan
|
||||
# helpful methods to determine permission checking and conditions hash generation.
|
||||
class Rule # :nodoc:
|
||||
attr_reader :base_behavior, :subjects, :actions, :conditions
|
||||
attr_writer :expanded_actions
|
||||
attr_writer :expanded_actions, :expanded_subjects
|
||||
|
||||
# The first argument when initializing is the base_behavior which is a true/false
|
||||
# value. True for "can" and false for "cannot". The next two arguments are the action
|
||||
@@ -30,11 +30,11 @@ module CanCan
|
||||
def matches_conditions?(action, subject, extra_args)
|
||||
if @match_all
|
||||
call_block_with_all(action, subject, extra_args)
|
||||
elsif @block && !subject_class?(subject)
|
||||
elsif @block && subject_object?(subject)
|
||||
@block.call(subject, *extra_args)
|
||||
elsif @conditions.kind_of?(Hash) && subject.class == Hash
|
||||
nested_subject_matches_conditions?(subject)
|
||||
elsif @conditions.kind_of?(Hash) && !subject_class?(subject)
|
||||
elsif @conditions.kind_of?(Hash) && subject_object?(subject)
|
||||
matches_conditions_hash?(subject)
|
||||
else
|
||||
# Don't stop at "cannot" definitions when there are conditions.
|
||||
@@ -72,21 +72,24 @@ module CanCan
|
||||
|
||||
private
|
||||
|
||||
def subject_class?(subject)
|
||||
klass = (subject.kind_of?(Hash) ? subject.values.first : subject).class
|
||||
klass == Class || klass == Module
|
||||
def subject_object?(subject)
|
||||
# klass = (subject.kind_of?(Hash) ? subject.values.first : subject).class
|
||||
# klass == Class || klass == Module
|
||||
!subject.kind_of?(Symbol)
|
||||
end
|
||||
|
||||
def matches_action?(action)
|
||||
@expanded_actions.include?(:manage) || @expanded_actions.include?(action)
|
||||
@expanded_actions.include?(:access) || @expanded_actions.include?(action)
|
||||
end
|
||||
|
||||
def matches_subject?(subject)
|
||||
@subjects.include?(:all) || @subjects.include?(subject) || matches_subject_class?(subject)
|
||||
subject = subject_name(subject) if subject_object? subject
|
||||
@expanded_subjects.include?(:all) || @expanded_subjects.include?(subject) # || matches_subject_class?(subject)
|
||||
end
|
||||
|
||||
# TODO deperecate this
|
||||
def matches_subject_class?(subject)
|
||||
@subjects.any? { |sub| sub.kind_of?(Module) && (subject.kind_of?(sub) || subject.class.to_s == sub.to_s || subject.kind_of?(Module) && subject.ancestors.include?(sub)) }
|
||||
@expanded_subjects.any? { |sub| sub.kind_of?(Module) && (subject.kind_of?(sub) || subject.class.to_s == sub.to_s || subject.kind_of?(Module) && subject.ancestors.include?(sub)) }
|
||||
end
|
||||
|
||||
# Checks if the given subject matches the given conditions hash.
|
||||
@@ -128,15 +131,19 @@ module CanCan
|
||||
end
|
||||
|
||||
def call_block_with_all(action, subject, extra_args)
|
||||
if subject.class == Class
|
||||
@block.call(action, subject, nil, *extra_args)
|
||||
if subject_object? subject
|
||||
@block.call(action, subject_name(subject), subject, *extra_args)
|
||||
else
|
||||
@block.call(action, subject.class, subject, *extra_args)
|
||||
@block.call(action, subject, nil, *extra_args)
|
||||
end
|
||||
end
|
||||
|
||||
def subject_name(subject)
|
||||
subject.class.to_s.underscore.humanize.downcase.pluralize.to_sym
|
||||
end
|
||||
|
||||
def model_adapter(subject)
|
||||
ModelAdapters::AbstractAdapter.adapter_class(subject_class?(subject) ? subject : subject.class)
|
||||
ModelAdapters::AbstractAdapter.adapter_class(subject_object?(subject) ? subject.class : subject)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user