Permalink
Browse files

Allow multiple abilities with associations

There are two issues with the current way cancan handles associations:

1) Records are returned multiple times in some circumstances
2) Several defined abilities prevent some records to show up under certain circumstances

This commit includes tests for both cases. It fixes both problems by changing `joins` to `includes` for the AR adapters. This could have performance implications, since `includes` will also select all columns in the associated records. We tried various ways of achieving the same thing using Arel directly, but were unable to make this work due to lack of support for outer joins in Rails 3.1.

This closes issues #724, #566 and #613
  • Loading branch information...
1 parent b4285ae commit 2ffdf46be1f5c2d7b48caa12800e7b7934cae9e6 Jonas Nicklas and Nicklas Ramhöj committed Aug 22, 2012
@@ -91,9 +91,9 @@ def database_records
elsif @model_class.respond_to?(:where) && @model_class.respond_to?(:joins)
mergeable_conditions = @rules.select {|rule| rule.unmergeable? }.blank?
if mergeable_conditions
- @model_class.where(conditions).joins(joins)
+ @model_class.where(conditions).includes(joins)
else
- @model_class.where(*(@rules.map(&:conditions))).joins(joins)
+ @model_class.where(*(@rules.map(&:conditions))).includes(joins)
end
else
@model_class.scoped(:conditions => conditions, :joins => joins)
@@ -102,6 +102,24 @@
Comment.accessible_by(@ability).should == [comment1]
end
+ it "should only read articles which are published or in visible categories" do
+ @ability.can :read, Article, :category => { :visible => true }
+ @ability.can :read, Article, :published => true
+ article1 = Article.create!(:published => true)
+ article2 = Article.create!(:published => false)
+ article3 = Article.create!(:published => false, :category => Category.create!(:visible => true))
+ Article.accessible_by(@ability).should == [article1, article3]
+ end
+
+ it "should only read categories once even if they have multiple articles" do
+ @ability.can :read, Category, :articles => { :published => true }
+ @ability.can :read, Article, :published => true
+ category = Category.create!
+ Article.create!(:published => true, :category => category)
+ Article.create!(:published => true, :category => category)
+ Category.accessible_by(@ability).should == [category]
+ end
+
it "should only read comments for visible categories through articles" do
@ability.can :read, Comment, :article => { :category => { :visible => true } }
comment1 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => true)))

0 comments on commit 2ffdf46

Please sign in to comment.