From f9b181af05bb998e6adabb92803ac284aae4745d Mon Sep 17 00:00:00 2001 From: Ryan Bates Date: Tue, 8 Mar 2011 17:08:26 -0800 Subject: [PATCH] allow Active Record scope to be passed as Ability conditions - closes #257 --- .../model_adapters/active_record_adapter.rb | 16 +++++++++++++++- lib/cancan/rule.rb | 2 +- .../model_adapters/active_record_adapter_spec.rb | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/lib/cancan/model_adapters/active_record_adapter.rb b/lib/cancan/model_adapters/active_record_adapter.rb index 3ffc91c..c8a4a16 100644 --- a/lib/cancan/model_adapters/active_record_adapter.rb +++ b/lib/cancan/model_adapters/active_record_adapter.rb @@ -55,7 +55,9 @@ module CanCan end def database_records - if @model_class.respond_to?(:where) && @model_class.respond_to?(:joins) + if override_scope + override_scope + elsif @model_class.respond_to?(:where) && @model_class.respond_to?(:joins) @model_class.where(conditions).joins(joins) else @model_class.scoped(:conditions => conditions, :joins => joins) @@ -64,6 +66,18 @@ module CanCan private + def override_scope + conditions = @rules.map(&:conditions).compact + if conditions.any? { |c| c.kind_of?(ActiveRecord::Relation) } + if conditions.size == 1 + conditions.first + else + rule = @rules.detect { |rule| rule.conditions.kind_of?(ActiveRecord::Relation) } + raise Error, "Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for #{rule.actions.first} #{rule.subjects.first} ability." + end + end + end + def merge_conditions(sql, conditions_hash, behavior) if conditions_hash.blank? behavior ? true_sql : false_sql diff --git a/lib/cancan/rule.rb b/lib/cancan/rule.rb index 2a5ee74..42bd478 100644 --- a/lib/cancan/rule.rb +++ b/lib/cancan/rule.rb @@ -3,7 +3,7 @@ module CanCan # it holds the information about a "can" call made on Ability and provides # helpful methods to determine permission checking and conditions hash generation. class Rule # :nodoc: - attr_reader :base_behavior, :actions, :conditions + attr_reader :base_behavior, :subjects, :actions, :conditions attr_writer :expanded_actions # The first argument when initializing is the base_behavior which is a true/false diff --git a/spec/cancan/model_adapters/active_record_adapter_spec.rb b/spec/cancan/model_adapters/active_record_adapter_spec.rb index c3b1bf8..1a867d0 100644 --- a/spec/cancan/model_adapters/active_record_adapter_spec.rb +++ b/spec/cancan/model_adapters/active_record_adapter_spec.rb @@ -110,10 +110,25 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record" @ability.can :read, Article, :published => true @ability.can :read, Article, ["secret=?", true] article1 = Article.create!(:published => true, :secret => false) + article2 = Article.create!(:published => true, :secret => true) + article3 = Article.create!(:published => false, :secret => true) article4 = Article.create!(:published => false, :secret => false) + Article.accessible_by(@ability).should == [article1, article2, article3] + end + + it "should allow a scope for conditions" do + @ability.can :read, Article, Article.where(:secret => true) + article1 = Article.create!(:secret => true) + article2 = Article.create!(:secret => false) Article.accessible_by(@ability).should == [article1] end + it "should raise an exception when trying to merge scope with other conditions" do + @ability.can :read, Article, :published => true + @ability.can :read, Article, Article.where(:secret => true) + lambda { Article.accessible_by(@ability) }.should raise_error(CanCan::Error, "Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for read Article ability.") + end + it "should not allow to fetch records when ability with just block present" do @ability.can :read, Article do false