diff --git a/lib/cancan/ability.rb b/lib/cancan/ability.rb index 07fe5a4..361d10c 100644 --- a/lib/cancan/ability.rb +++ b/lib/cancan/ability.rb @@ -237,9 +237,9 @@ module CanCan conds.reverse.inject(nil) do |sql, action| behavior, condition = action if condition && condition != {} - condition = "#{subject.send(:sanitize_sql, condition)}" + condition = subject.send(:sanitize_sql, condition) case sql - when nil then condition + when nil then behavior ? condition : "not (#{condition})" when true_cond behavior ? true_cond : "not (#{condition})" when false_cond diff --git a/spec/cancan/ability_spec.rb b/spec/cancan/ability_spec.rb index af8e484..d76afd5 100644 --- a/spec/cancan/ability_spec.rb +++ b/spec/cancan/ability_spec.rb @@ -233,25 +233,41 @@ describe CanCan::Ability do @ability.conditions(:foo, Array).should == false end - it "should return appropriate sql conditions" do - obj = Class.new do - def self.sanitize_sql(hash_cond) - case hash_cond - when Hash then hash_cond.map{|name, value| "#{name}=#{value}"} - when Array - hash_cond.shift.gsub('?'){"#{hash_cond.shift.inspect}"} - when String then hash_cond - end - end - end - @ability.can :read, obj - @ability.can :manage, obj, :id => 1 - @ability.can :update, obj, :manager_id => 1 - @ability.cannot :update, obj, :self_managed => true + it "should return hash for single `can` definition" do + @ability.can :read, SqlSanitizer, :blocked => false, :user_id => 1 - @ability.sql_conditions(:update, obj).should == 'not (self_managed=true) AND ((manager_id=1) OR (id=1))' - @ability.sql_conditions(:manage, obj).should == {:id=>1} - @ability.sql_conditions(:read, obj).should == 'true=true' + @ability.sql_conditions(:read, SqlSanitizer).should == { :blocked => false, :user_id => 1 } + end + + it "should return `not (sql)` for single `cannot` definition" do + @ability.cannot :read, SqlSanitizer, :blocked => true, :user_id => 1 + + @ability.sql_conditions(:read, SqlSanitizer).should == 'not (blocked=true AND user_id=1)' + end + + it "should return `sql` for single `can` definition in front of default cannot condition" do + @ability.cannot :read, SqlSanitizer + @ability.can :read, SqlSanitizer, :blocked => false, :user_id => 1 + + @ability.sql_conditions(:read, SqlSanitizer).should == 'blocked=false AND user_id=1' + end + + it "should return `not (sql)` for single `cannot` definition in front of default can condition" do + @ability.can :read, SqlSanitizer + @ability.cannot :read, SqlSanitizer, :blocked => true, :user_id => 1 + + @ability.sql_conditions(:read, SqlSanitizer).should == 'not (blocked=true AND user_id=1)' + end + + it "should return appropriate sql conditions in complex case" do + @ability.can :read, SqlSanitizer + @ability.can :manage, SqlSanitizer, :id => 1 + @ability.can :update, SqlSanitizer, :manager_id => 1 + @ability.cannot :update, SqlSanitizer, :self_managed => true + + @ability.sql_conditions(:update, SqlSanitizer).should == 'not (self_managed=true) AND ((manager_id=1) OR (id=1))' + @ability.sql_conditions(:manage, SqlSanitizer).should == {:id=>1} + @ability.sql_conditions(:read, SqlSanitizer).should == 'true=true' end it "should has eated cheezburger" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 20c75f6..a77393c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -21,3 +21,14 @@ end # this class helps out in testing nesting class Person end + +class SqlSanitizer + def self.sanitize_sql(hash_cond) + case hash_cond + when Hash then hash_cond.map{|name, value| "#{name}=#{value}"}.join(' AND ') + when Array + hash_cond.shift.gsub('?'){"#{hash_cond.shift.inspect}"} + when String then hash_cond + end + end +end