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 bd755e2 commit 8976293458a79b75e9b928ade81a33892fb122d0 Jonas Nicklas and Nicklas Ramhöj committed with Aug 22, 2012
View
4 lib/cancan/model_adapters/active_record_adapter.rb
@@ -101,9 +101,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)
View
18 spec/cancan/model_adapters/active_record_adapter_spec.rb
@@ -114,6 +114,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 8976293

Please sign in to comment.