allow Active Record scope to be passed as Ability conditions - closes #257

This commit is contained in:
Ryan Bates 2011-03-08 17:08:26 -08:00
parent 80f1ab20fb
commit f9b181af05
3 changed files with 31 additions and 2 deletions

View File

@ -55,7 +55,9 @@ module CanCan
end end
def database_records 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) @model_class.where(conditions).joins(joins)
else else
@model_class.scoped(:conditions => conditions, :joins => joins) @model_class.scoped(:conditions => conditions, :joins => joins)
@ -64,6 +66,18 @@ module CanCan
private 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) def merge_conditions(sql, conditions_hash, behavior)
if conditions_hash.blank? if conditions_hash.blank?
behavior ? true_sql : false_sql behavior ? true_sql : false_sql

View File

@ -3,7 +3,7 @@ module CanCan
# it holds the information about a "can" call made on Ability and provides # it holds the information about a "can" call made on Ability and provides
# helpful methods to determine permission checking and conditions hash generation. # helpful methods to determine permission checking and conditions hash generation.
class Rule # :nodoc: class Rule # :nodoc:
attr_reader :base_behavior, :actions, :conditions attr_reader :base_behavior, :subjects, :actions, :conditions
attr_writer :expanded_actions attr_writer :expanded_actions
# The first argument when initializing is the base_behavior which is a true/false # The first argument when initializing is the base_behavior which is a true/false

View File

@ -110,10 +110,25 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
@ability.can :read, Article, :published => true @ability.can :read, Article, :published => true
@ability.can :read, Article, ["secret=?", true] @ability.can :read, Article, ["secret=?", true]
article1 = Article.create!(:published => true, :secret => false) 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) 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] Article.accessible_by(@ability).should == [article1]
end 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 it "should not allow to fetch records when ability with just block present" do
@ability.can :read, Article do @ability.can :read, Article do
false false