adding attributes as 3rd argument to can and can? calls
This commit is contained in:
parent
a03d35272b
commit
85efbdb8d0
|
@ -53,9 +53,9 @@ module CanCan
|
|||
# end
|
||||
#
|
||||
# Also see the RSpec Matchers to aid in testing.
|
||||
def can?(action, subject, *extra_args)
|
||||
match = relevant_rules_for_match(action, subject).detect do |rule|
|
||||
rule.matches_conditions?(action, subject, extra_args)
|
||||
def can?(action, subject, attribute = nil)
|
||||
match = relevant_rules_for_match(action, subject, attribute).detect do |rule|
|
||||
rule.matches_conditions?(action, subject, attribute)
|
||||
end
|
||||
match ? match.base_behavior : false
|
||||
end
|
||||
|
@ -121,8 +121,8 @@ module CanCan
|
|||
# # check the database and return true/false
|
||||
# end
|
||||
#
|
||||
def can(action = nil, subject = nil, conditions = nil, &block)
|
||||
rules << Rule.new(true, action, subject, conditions, block)
|
||||
def can(*args, &block)
|
||||
rules << Rule.new(true, *args, &block)
|
||||
end
|
||||
|
||||
# Defines an ability which cannot be done. Accepts the same arguments as "can".
|
||||
|
@ -137,8 +137,8 @@ module CanCan
|
|||
# product.invisible?
|
||||
# end
|
||||
#
|
||||
def cannot(action = nil, subject = nil, conditions = nil, &block)
|
||||
rules << Rule.new(false, action, subject, conditions, block)
|
||||
def cannot(*args, &block)
|
||||
rules << Rule.new(false, *args, &block)
|
||||
end
|
||||
|
||||
# Alias one or more actions into another one.
|
||||
|
@ -282,16 +282,16 @@ module CanCan
|
|||
|
||||
# Returns an array of Rule instances which match the action and subject
|
||||
# This does not take into consideration any hash conditions or block statements
|
||||
def relevant_rules(action, subject)
|
||||
def relevant_rules(action, subject, attribute = nil)
|
||||
rules.reverse.select do |rule|
|
||||
rule.expanded_actions = expand_aliases(:actions, rule.actions)
|
||||
rule.expanded_subjects = expand_aliases(:subjects, rule.subjects)
|
||||
rule.relevant? action, subject
|
||||
rule.relevant? action, subject, attribute
|
||||
end
|
||||
end
|
||||
|
||||
def relevant_rules_for_match(action, subject)
|
||||
relevant_rules(action, subject).each do |rule|
|
||||
def relevant_rules_for_match(action, subject, attribute)
|
||||
relevant_rules(action, subject, attribute).each do |rule|
|
||||
if rule.only_raw_sql?
|
||||
raise Error, "The can? and cannot? call cannot be used with a raw sql 'can' definition. The checking code cannot be determined for #{action.inspect} #{subject.inspect}"
|
||||
end
|
||||
|
@ -299,7 +299,7 @@ module CanCan
|
|||
end
|
||||
|
||||
def relevant_rules_for_query(action, subject)
|
||||
relevant_rules(action, subject).each do |rule|
|
||||
relevant_rules(action, subject, nil).each do |rule|
|
||||
if rule.only_block?
|
||||
raise Error, "The accessible_by call cannot be used with a block 'can' definition. The SQL cannot be determined for #{action.inspect} #{subject.inspect}"
|
||||
end
|
||||
|
|
|
@ -10,28 +10,29 @@ module CanCan
|
|||
# value. True for "can" and false for "cannot". The next two arguments are the action
|
||||
# and subject respectively (such as :read, @project). The third argument is a hash
|
||||
# of conditions and the last one is the block passed to the "can" call.
|
||||
def initialize(base_behavior, action, subject, conditions, block)
|
||||
raise Error, "You are not able to supply a block with a hash of conditions in #{action} #{subject} ability. Use either one." if conditions.kind_of?(Hash) && !block.nil?
|
||||
def initialize(base_behavior, action = nil, subject = nil, *extra_args, &block)
|
||||
@match_all = action.nil? && subject.nil?
|
||||
@base_behavior = base_behavior
|
||||
@actions = [action].flatten.compact.map(&:to_sym)
|
||||
@subjects = [subject].flatten.compact.map(&:to_sym)
|
||||
@conditions = conditions || {}
|
||||
@actions = [action].flatten
|
||||
@subjects = [subject].flatten
|
||||
@attributes = [extra_args.shift].flatten if extra_args.first.kind_of?(Symbol) || extra_args.first.kind_of?(Array) && extra_args.first.first.kind_of?(Symbol)
|
||||
raise Error, "You are not able to supply a block with a hash of conditions in #{action} #{subject} ability. Use either one." if extra_args.first && !block.nil?
|
||||
@conditions = extra_args.first || {}
|
||||
@block = block
|
||||
end
|
||||
|
||||
# Matches both the subject and action, not necessarily the conditions
|
||||
def relevant?(action, subject)
|
||||
# Matches the subject, action, and given attribute. Conditions are not checked here.
|
||||
def relevant?(action, subject, attribute)
|
||||
subject = subject.values.first if subject.class == Hash
|
||||
@match_all || (matches_action?(action) && matches_subject?(subject))
|
||||
@match_all || (matches_action?(action) && matches_subject?(subject) && matches_attribute?(attribute))
|
||||
end
|
||||
|
||||
# Matches the block or conditions hash
|
||||
def matches_conditions?(action, subject, extra_args)
|
||||
def matches_conditions?(action, subject, attribute)
|
||||
if @match_all
|
||||
call_block_with_all(action, subject, extra_args)
|
||||
call_block_with_all(action, subject, attribute)
|
||||
elsif @block && subject_object?(subject)
|
||||
@block.call(subject, *extra_args)
|
||||
@block.arity == 1 ? @block.call(subject) : @block.call(subject, attribute)
|
||||
elsif @conditions.kind_of?(Hash) && subject.class == Hash
|
||||
nested_subject_matches_conditions?(subject)
|
||||
elsif @conditions.kind_of?(Hash) && subject_object?(subject)
|
||||
|
@ -87,6 +88,10 @@ module CanCan
|
|||
@expanded_subjects.include?(:all) || @expanded_subjects.include?(subject.to_sym) # || matches_subject_class?(subject)
|
||||
end
|
||||
|
||||
def matches_attribute?(attribute)
|
||||
@attributes.nil? || attribute.nil? || @attributes.include?(attribute.to_sym)
|
||||
end
|
||||
|
||||
# TODO deperecate this
|
||||
def matches_subject_class?(subject)
|
||||
@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)) }
|
||||
|
@ -130,11 +135,11 @@ module CanCan
|
|||
matches_conditions_hash?(parent, @conditions[parent.class.name.downcase.to_sym] || {})
|
||||
end
|
||||
|
||||
def call_block_with_all(action, subject, extra_args)
|
||||
def call_block_with_all(action, subject, attribute)
|
||||
if subject_object? subject
|
||||
@block.call(action, subject_name(subject), subject, *extra_args)
|
||||
@block.call(action, subject_name(subject), subject, attribute)
|
||||
else
|
||||
@block.call(action, subject, nil, *extra_args)
|
||||
@block.call(action, subject, nil, attribute)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -35,11 +35,9 @@ describe CanCan::Ability do
|
|||
@ability.can?(:paint, :cars).should be_false
|
||||
end
|
||||
|
||||
it "allows strings instead of symbols" do
|
||||
@ability.can "paint", "fences"
|
||||
it "allows strings instead of symbols in ability check" do
|
||||
@ability.can :paint, :fences
|
||||
@ability.can?("paint", "fences").should be_true
|
||||
@ability.can "access", "all"
|
||||
@ability.can?("wax", "cars").should be_true
|
||||
end
|
||||
|
||||
|
||||
|
@ -182,7 +180,7 @@ describe CanCan::Ability do
|
|||
@block_called.should be_true
|
||||
end
|
||||
|
||||
it "should raise an error when attempting to use a block with a hash condition since it's not likely what they want" do
|
||||
it "raises an error when attempting to use a block with a hash condition since it's not likely what they want" do
|
||||
lambda {
|
||||
@ability.can :read, :ranges, :published => true do
|
||||
false
|
||||
|
@ -191,6 +189,52 @@ describe CanCan::Ability do
|
|||
end
|
||||
|
||||
|
||||
# Attributes
|
||||
|
||||
it "allows permission on attributes" do
|
||||
@ability.can :update, :users, :name
|
||||
@ability.can :update, :users, [:email, :age]
|
||||
@ability.can?(:update, :users, :name).should be_true
|
||||
@ability.can?(:update, :users, :email).should be_true
|
||||
@ability.can?(:update, :users, :password).should be_false
|
||||
end
|
||||
|
||||
it "allows permission on all attributes when none are given" do
|
||||
@ability.can :update, :users
|
||||
@ability.can?(:update, :users, :password).should be_true
|
||||
end
|
||||
|
||||
it "allows strings when chekcing attributes" do
|
||||
@ability.can :update, :users, :name
|
||||
@ability.can?(:update, :users, "name").should be_true
|
||||
end
|
||||
|
||||
it "combines attribute check with conditions hash" do
|
||||
@ability.can :update, :ranges, :begin => 1
|
||||
@ability.can :update, :ranges, :name, :begin => 2
|
||||
@ability.can?(:update, 1..3, :foobar).should be_true
|
||||
@ability.can?(:update, 2..4, :foobar).should be_false
|
||||
@ability.can?(:update, 2..4, :name).should be_true
|
||||
@ability.can?(:update, 3..5, :name).should be_false
|
||||
end
|
||||
|
||||
it "passes attribute to block and nil if no attribute checked" do
|
||||
@ability.can :update, :ranges do |range, attribute|
|
||||
attribute == :name
|
||||
end
|
||||
@ability.can?(:update, 1..3, :name).should be_true
|
||||
@ability.can?(:update, 2..4).should be_false
|
||||
end
|
||||
|
||||
it "passes attribute to block for global can definition" do
|
||||
@ability.can do |action, subject, object, attribute|
|
||||
attribute == :name
|
||||
end
|
||||
@ability.can?(:update, 1..3, :name).should be_true
|
||||
@ability.can?(:update, 2..4).should be_false
|
||||
end
|
||||
|
||||
|
||||
# Cannot
|
||||
|
||||
it "offers cannot? method which inverts can?" do
|
||||
|
@ -259,7 +303,7 @@ describe CanCan::Ability do
|
|||
end
|
||||
end
|
||||
|
||||
it "should raise access denied exception with default message if not specified" do
|
||||
it "raises access denied exception with default message if not specified" do
|
||||
begin
|
||||
@ability.authorize! :read, :books
|
||||
rescue CanCan::AccessDenied => e
|
||||
|
|
|
@ -4,7 +4,7 @@ require "spec_helper"
|
|||
describe CanCan::Rule do
|
||||
before(:each) do
|
||||
@conditions = {}
|
||||
@rule = CanCan::Rule.new(true, :read, :integers, @conditions, nil)
|
||||
@rule = CanCan::Rule.new(true, :read, :integers, @conditions)
|
||||
end
|
||||
|
||||
it "should return no association joins if none exist" do
|
||||
|
@ -33,7 +33,7 @@ describe CanCan::Rule do
|
|||
end
|
||||
|
||||
it "should return no association joins if conditions is nil" do
|
||||
rule = CanCan::Rule.new(true, :read, :integers, nil, nil)
|
||||
rule = CanCan::Rule.new(true, :read, :integers)
|
||||
rule.associations_hash.should == {}
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user