culturecode-cancan 2.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +381 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +108 -0
  6. data/Rakefile +18 -0
  7. data/init.rb +1 -0
  8. data/lib/cancan.rb +13 -0
  9. data/lib/cancan/ability.rb +348 -0
  10. data/lib/cancan/controller_additions.rb +392 -0
  11. data/lib/cancan/controller_resource.rb +266 -0
  12. data/lib/cancan/exceptions.rb +53 -0
  13. data/lib/cancan/inherited_resource.rb +20 -0
  14. data/lib/cancan/matchers.rb +14 -0
  15. data/lib/cancan/model_adapters/abstract_adapter.rb +56 -0
  16. data/lib/cancan/model_adapters/active_record_adapter.rb +172 -0
  17. data/lib/cancan/model_adapters/data_mapper_adapter.rb +34 -0
  18. data/lib/cancan/model_adapters/default_adapter.rb +7 -0
  19. data/lib/cancan/model_adapters/mongoid_adapter.rb +54 -0
  20. data/lib/cancan/model_additions.rb +29 -0
  21. data/lib/cancan/rule.rb +178 -0
  22. data/lib/generators/cancan/ability/USAGE +5 -0
  23. data/lib/generators/cancan/ability/ability_generator.rb +16 -0
  24. data/lib/generators/cancan/ability/templates/ability.rb +24 -0
  25. data/lib/generators/cancan/ability/templates/ability_spec.rb +16 -0
  26. data/lib/generators/cancan/ability/templates/ability_test.rb +10 -0
  27. data/spec/README.rdoc +28 -0
  28. data/spec/cancan/ability_spec.rb +541 -0
  29. data/spec/cancan/controller_additions_spec.rb +118 -0
  30. data/spec/cancan/controller_resource_spec.rb +551 -0
  31. data/spec/cancan/exceptions_spec.rb +58 -0
  32. data/spec/cancan/inherited_resource_spec.rb +58 -0
  33. data/spec/cancan/matchers_spec.rb +33 -0
  34. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +278 -0
  35. data/spec/cancan/model_adapters/data_mapper_adapter_spec.rb +120 -0
  36. data/spec/cancan/model_adapters/default_adapter_spec.rb +7 -0
  37. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +226 -0
  38. data/spec/cancan/rule_spec.rb +55 -0
  39. data/spec/matchers.rb +13 -0
  40. data/spec/spec_helper.rb +49 -0
  41. metadata +194 -0
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ describe CanCan::Unauthorized do
4
+ describe "with action and subject" do
5
+ before(:each) do
6
+ @exception = CanCan::Unauthorized.new(nil, :some_action, :some_subject)
7
+ end
8
+
9
+ it "has action and subject accessors" do
10
+ @exception.action.should == :some_action
11
+ @exception.subject.should == :some_subject
12
+ end
13
+
14
+ it "has a changable default message" do
15
+ @exception.message.should == "You are not authorized to access this page."
16
+ @exception.default_message = "Unauthorized!"
17
+ @exception.message.should == "Unauthorized!"
18
+ end
19
+ end
20
+
21
+ describe "with only a message" do
22
+ before(:each) do
23
+ @exception = CanCan::Unauthorized.new("Access denied!")
24
+ end
25
+
26
+ it "has nil action and subject" do
27
+ @exception.action.should be_nil
28
+ @exception.subject.should be_nil
29
+ end
30
+
31
+ it "has passed message" do
32
+ @exception.message.should == "Access denied!"
33
+ end
34
+ end
35
+
36
+ describe "i18n in the default message" do
37
+ after(:each) do
38
+ I18n.backend = nil
39
+ end
40
+
41
+ it "uses i18n for the default message" do
42
+ I18n.backend.store_translations :en, :unauthorized => {:default => "This is a different message"}
43
+ @exception = CanCan::Unauthorized.new
44
+ @exception.message.should == "This is a different message"
45
+ end
46
+
47
+ it "defaults to a nice message" do
48
+ @exception = CanCan::Unauthorized.new
49
+ @exception.message.should == "You are not authorized to access this page."
50
+ end
51
+
52
+ it "does not use translation if a message is given" do
53
+ @exception = CanCan::Unauthorized.new("Hey! You're not welcome here")
54
+ @exception.message.should == "Hey! You're not welcome here"
55
+ @exception.message.should_not == "You are not authorized to access this page."
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ describe CanCan::InheritedResource do
4
+ before(:each) do
5
+ @params = HashWithIndifferentAccess.new(:controller => "projects")
6
+ @controller_class = Class.new
7
+ @controller = @controller_class.new
8
+ @ability = Ability.new(nil)
9
+ @controller.stub(:params) { @params }
10
+ @controller.stub(:current_ability) { @ability }
11
+ # @controller_class.stub(:cancan_skipper) { {:authorize => {}, :load => {}} }
12
+ end
13
+
14
+ it "show should load resource through @controller.resource" do
15
+ @params.merge!(:action => "show", :id => 123)
16
+ @controller.stub(:resource) { :project_resource }
17
+ CanCan::InheritedResource.new(@controller, :load => true).process
18
+ @controller.instance_variable_get(:@project).should == :project_resource
19
+ end
20
+
21
+ it "new should load through @controller.build_resource" do
22
+ @params[:action] = "new"
23
+ @controller.stub(:build_resource) { :project_resource }
24
+ CanCan::InheritedResource.new(@controller, :load => true).process
25
+ @controller.instance_variable_get(:@project).should == :project_resource
26
+ end
27
+
28
+ it "index should load through @controller.association_chain when parent" do
29
+ @params[:action] = "index"
30
+ @controller.stub(:association_chain) { @controller.instance_variable_set(:@project, :project_resource) }
31
+ CanCan::InheritedResource.new(@controller, :load => true, :parent => true).process
32
+ @controller.instance_variable_get(:@project).should == :project_resource
33
+ end
34
+
35
+ it "index should load through @controller.end_of_association_chain" do
36
+ @params[:action] = "index"
37
+ Project.stub(:accessible_by).with(@ability, :index) { :projects }
38
+ @controller.stub(:end_of_association_chain) { Project }
39
+ CanCan::InheritedResource.new(@controller, :load => true).process
40
+ @controller.instance_variable_get(:@projects).should == :projects
41
+ end
42
+
43
+ it "should build a new resource with attributes from current ability" do
44
+ @params[:action] = "new"
45
+ @ability.can(:create, :projects, :name => "from conditions")
46
+ @controller.stub(:build_resource) { Struct.new(:name).new }
47
+ CanCan::InheritedResource.new(@controller, :load => true).process
48
+ @controller.instance_variable_get(:@project).name.should == "from conditions"
49
+ end
50
+
51
+ it "should override initial attributes with params" do
52
+ @params.merge!(:action => "new", :project => {:name => "from params"})
53
+ @ability.can(:create, :projects, :name => "from conditions")
54
+ @controller.stub(:build_resource) { Struct.new(:name).new }
55
+ CanCan::ControllerResource.new(@controller, :load => true).process
56
+ @controller.instance_variable_get(:@project).name.should == "from params"
57
+ end
58
+ end
@@ -0,0 +1,33 @@
1
+ require "spec_helper"
2
+
3
+ describe "be_able_to" do
4
+ it "delegates to can?" do
5
+ object = Object.new
6
+ object.should_receive(:can?).with(:read, 123) { true }
7
+ object.should be_able_to(:read, 123)
8
+ end
9
+
10
+ it "reports a nice failure message for should" do
11
+ object = Object.new
12
+ object.should_receive(:can?).with(:read, 123) { false }
13
+ expect do
14
+ object.should be_able_to(:read, 123)
15
+ end.should raise_error('expected to be able to :read 123')
16
+ end
17
+
18
+ it "reports a nice failure message for should not" do
19
+ object = Object.new
20
+ object.should_receive(:can?).with(:read, 123) { true }
21
+ expect do
22
+ object.should_not be_able_to(:read, 123)
23
+ end.should raise_error('expected not to be able to :read 123')
24
+ end
25
+
26
+ it "delegates additional arguments to can? and reports in failure message" do
27
+ object = Object.new
28
+ object.should_receive(:can?).with(:read, 123, 456) { false }
29
+ expect do
30
+ object.should be_able_to(:read, 123, 456)
31
+ end.should raise_error('expected to be able to :read 123 456')
32
+ end
33
+ end
@@ -0,0 +1,278 @@
1
+ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
2
+ require "spec_helper"
3
+
4
+ class Category
5
+ has_many :articles
6
+ end
7
+
8
+ class Article < ActiveRecord::Base
9
+ connection.create_table(table_name) do |t|
10
+ t.integer :category_id
11
+ t.string :name
12
+ t.boolean :published
13
+ t.boolean :secret
14
+ t.integer :priority
15
+ end
16
+ belongs_to :category
17
+ has_many :comments
18
+ end
19
+
20
+ class Comment < ActiveRecord::Base
21
+ connection.create_table(table_name) do |t|
22
+ t.integer :article_id
23
+ t.boolean :spam
24
+ end
25
+ belongs_to :article
26
+ end
27
+
28
+ describe CanCan::ModelAdapters::ActiveRecordAdapter do
29
+ before(:each) do
30
+ Article.delete_all
31
+ Comment.delete_all
32
+ @ability = Object.new
33
+ @ability.extend(CanCan::Ability)
34
+ @article_table = Article.table_name
35
+ @comment_table = Comment.table_name
36
+ end
37
+
38
+ it "is for only active record classes" do
39
+ CanCan::ModelAdapters::ActiveRecordAdapter.should_not be_for_class(Object)
40
+ CanCan::ModelAdapters::ActiveRecordAdapter.should be_for_class(Article)
41
+ CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::ActiveRecordAdapter
42
+ end
43
+
44
+ it "finds record" do
45
+ article = Article.create!
46
+ CanCan::ModelAdapters::ActiveRecordAdapter.find(Article, article.id).should == article
47
+ end
48
+
49
+ it "does not fetch any records when no abilities are defined" do
50
+ Article.create!
51
+ Article.accessible_by(@ability).should be_empty
52
+ end
53
+
54
+ it "fetches all articles when one can read all" do
55
+ @ability.can :read, :articles
56
+ article = Article.create!
57
+ Article.accessible_by(@ability).should == [article]
58
+ end
59
+
60
+ it "fetches only the articles that are published" do
61
+ @ability.can :read, :articles, :published => true
62
+ article1 = Article.create!(:published => true)
63
+ article2 = Article.create!(:published => false)
64
+ Article.accessible_by(@ability).should == [article1]
65
+ end
66
+
67
+ it "fetches any articles which are published or secret" do
68
+ @ability.can :read, :articles, :published => true
69
+ @ability.can :read, :articles, :secret => true
70
+ article1 = Article.create!(:published => true, :secret => false)
71
+ article2 = Article.create!(:published => true, :secret => true)
72
+ article3 = Article.create!(:published => false, :secret => true)
73
+ article4 = Article.create!(:published => false, :secret => false)
74
+ Article.accessible_by(@ability).should == [article1, article2, article3]
75
+ end
76
+
77
+ it "fetches only the articles that are published and not secret" do
78
+ @ability.can :read, :articles, :published => true
79
+ @ability.cannot :read, :articles, :secret => true
80
+ article1 = Article.create!(:published => true, :secret => false)
81
+ article2 = Article.create!(:published => true, :secret => true)
82
+ article3 = Article.create!(:published => false, :secret => true)
83
+ article4 = Article.create!(:published => false, :secret => false)
84
+ Article.accessible_by(@ability).should == [article1]
85
+ end
86
+
87
+ it "only reads comments for articles which are published" do
88
+ @ability.can :read, :comments, :article => { :published => true }
89
+ comment1 = Comment.create!(:article => Article.create!(:published => true))
90
+ comment2 = Comment.create!(:article => Article.create!(:published => false))
91
+ Comment.accessible_by(@ability).should == [comment1]
92
+ end
93
+
94
+ it "only reads comments for visible categories through articles" do
95
+ pending "does ActiveRecord no longer support a deep nested hash of conditions?"
96
+ @ability.can :read, :comments, :article => { :category => { :visible => true } }
97
+ comment1 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => true)))
98
+ comment2 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => false)))
99
+ Comment.accessible_by(@ability).should == [comment1]
100
+ end
101
+
102
+ it "allows conditions in SQL and merge with hash conditions" do
103
+ @ability.can :read, :articles, :published => true
104
+ @ability.can :read, :articles, ["secret=?", true]
105
+ article1 = Article.create!(:published => true, :secret => false)
106
+ article2 = Article.create!(:published => true, :secret => true)
107
+ article3 = Article.create!(:published => false, :secret => true)
108
+ article4 = Article.create!(:published => false, :secret => false)
109
+ Article.accessible_by(@ability).should == [article1, article2, article3]
110
+ end
111
+
112
+ it "allows a scope for conditions" do
113
+ @ability.can :read, :articles, Article.where(:secret => true)
114
+ article1 = Article.create!(:secret => true)
115
+ article2 = Article.create!(:secret => false)
116
+ Article.accessible_by(@ability).should == [article1]
117
+ end
118
+
119
+ it "fetches only associated records when using with a scope for conditions" do
120
+ @ability.can :read, :articles, Article.where(:secret => true)
121
+ category1 = Category.create!(:visible => false)
122
+ category2 = Category.create!(:visible => true)
123
+ article1 = Article.create!(:secret => true, :category => category1)
124
+ article2 = Article.create!(:secret => true, :category => category2)
125
+ # for some reason the objects aren't comparing equally here so it's necessary to compare by id
126
+ category1.articles.accessible_by(@ability).map(&:id).should == [article1.id]
127
+ end
128
+
129
+ it "raises an exception when trying to merge scope with other conditions" do
130
+ @ability.can :read, :articles, :published => true
131
+ @ability.can :read, :articles, Article.where(:secret => true)
132
+ 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 articles ability.")
133
+ end
134
+
135
+ it "does not allow to fetch records when ability with just block present" do
136
+ @ability.can :read, :articles do
137
+ false
138
+ end
139
+ lambda { Article.accessible_by(@ability) }.should raise_error(CanCan::Error)
140
+ end
141
+
142
+ it "does not allow to check ability on object against SQL conditions without block" do
143
+ @ability.can :read, :articles, ["secret=?", true]
144
+ lambda { @ability.can? :read, Article.new }.should raise_error(CanCan::Error)
145
+ end
146
+
147
+ it "has false conditions if no abilities match" do
148
+ @ability.model_adapter(Article, :read).conditions.should == "'t'='f'"
149
+ end
150
+
151
+ it "returns false conditions for cannot clause" do
152
+ @ability.cannot :read, :articles
153
+ @ability.model_adapter(Article, :read).conditions.should == "'t'='f'"
154
+ end
155
+
156
+ it "returns SQL for single `can` definition in front of default `cannot` condition" do
157
+ @ability.cannot :read, :articles
158
+ @ability.can :read, :articles, :published => false, :secret => true
159
+ @ability.model_adapter(Article, :read).conditions.should orderlessly_match(%Q["#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't'])
160
+ end
161
+
162
+ it "returns true condition for single `can` definition in front of default `can` condition" do
163
+ @ability.can :read, :articles
164
+ @ability.can :read, :articles, :published => false, :secret => true
165
+ @ability.model_adapter(Article, :read).conditions.should eq(:secret => true, :published => false)
166
+ end
167
+
168
+ it "returns `false condition` for single `cannot` definition in front of default `cannot` condition" do
169
+ @ability.cannot :read, :articles
170
+ @ability.cannot :read, :articles, :published => false, :secret => true
171
+ @ability.model_adapter(Article, :read).conditions.should == "'t'='f'"
172
+ end
173
+
174
+ it "returns `not (sql)` for single `cannot` definition in front of default `can` condition" do
175
+ @ability.can :read, :articles
176
+ @ability.cannot :read, :articles, :published => false, :secret => true
177
+ @ability.model_adapter(Article, :read).conditions.should orderlessly_match(%Q["not (#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't')])
178
+ end
179
+
180
+ it "returns appropriate sql conditions in complex case" do
181
+ @ability.can :read, :articles
182
+ @ability.can :access, :articles, :id => 1
183
+ @ability.can :update, :articles, :published => true
184
+ @ability.cannot :update, :articles, :secret => true
185
+ @ability.model_adapter(Article, :update).conditions.should == %Q[not ("#{@article_table}"."secret" = 't') AND (("#{@article_table}"."published" = 't') OR ("#{@article_table}"."id" = 1))]
186
+ @ability.model_adapter(Article, :access).conditions.should == {:id => 1}
187
+ @ability.model_adapter(Article, :read).conditions.should == {:id => 1} # used to be "t=t" but changed with new specificity rule (issue #321)
188
+ end
189
+
190
+ it "does not forget conditions when calling with SQL string" do
191
+ @ability.can :read, :articles, :published => true
192
+ @ability.can :read, :articles, ['secret=?', false]
193
+ adapter = @ability.model_adapter(Article, :read)
194
+ 2.times do
195
+ adapter.conditions.should == %Q[(secret='f') OR ("#{@article_table}"."published" = 't')]
196
+ end
197
+ end
198
+
199
+ it "has nil joins if no rules" do
200
+ @ability.model_adapter(Article, :read).joins.should be_nil
201
+ end
202
+
203
+ it "has nil joins if no nested hashes specified in conditions" do
204
+ @ability.can :read, :articles, :published => false
205
+ @ability.can :read, :articles, :secret => true
206
+ @ability.model_adapter(Article, :read).joins.should be_nil
207
+ end
208
+
209
+ it "merges separate joins into a single array" do
210
+ @ability.can :read, :articles, :project => { :blocked => false }
211
+ @ability.can :read, :articles, :company => { :admin => true }
212
+ @ability.model_adapter(Article, :read).joins.inspect.should orderlessly_match([:company, :project].inspect)
213
+ end
214
+
215
+ it "merges same joins into a single array" do
216
+ @ability.can :read, :articles, :project => { :blocked => false }
217
+ @ability.can :read, :articles, :project => { :admin => true }
218
+ @ability.model_adapter(Article, :read).joins.should == [:project]
219
+ end
220
+
221
+ it "restricts articles given a MetaWhere condition" do
222
+ pending
223
+ @ability.can :read, :articles, :priority.lt => 2
224
+ article1 = Article.create!(:priority => 1)
225
+ article2 = Article.create!(:priority => 3)
226
+ Article.accessible_by(@ability).should == [article1]
227
+ @ability.should be_able_to(:read, article1)
228
+ @ability.should_not be_able_to(:read, article2)
229
+ end
230
+
231
+ it "should merge MetaWhere and non-MetaWhere conditions" do
232
+ pending
233
+ @ability.can :read, Article, :priority.lt => 2
234
+ @ability.can :read, Article, :priority => 1
235
+ article1 = Article.create!(:priority => 1)
236
+ article2 = Article.create!(:priority => 3)
237
+ Article.accessible_by(@ability).should == [article1]
238
+ @ability.should be_able_to(:read, article1)
239
+ @ability.should_not be_able_to(:read, article2)
240
+ end
241
+
242
+ it "matches any MetaWhere condition" do
243
+ pending
244
+ adapter = CanCan::ModelAdapters::ActiveRecordAdapter
245
+ article1 = Article.new(:priority => 1, :name => "Hello World")
246
+ adapter.matches_condition?(article1, :priority.eq, 1).should be_true
247
+ adapter.matches_condition?(article1, :priority.eq, 2).should be_false
248
+ adapter.matches_condition?(article1, :priority.eq_any, [1, 2]).should be_true
249
+ adapter.matches_condition?(article1, :priority.eq_any, [2, 3]).should be_false
250
+ adapter.matches_condition?(article1, :priority.eq_all, [1, 1]).should be_true
251
+ adapter.matches_condition?(article1, :priority.eq_all, [1, 2]).should be_false
252
+ adapter.matches_condition?(article1, :priority.ne, 2).should be_true
253
+ adapter.matches_condition?(article1, :priority.ne, 1).should be_false
254
+ adapter.matches_condition?(article1, :priority.in, [1, 2]).should be_true
255
+ adapter.matches_condition?(article1, :priority.in, [2, 3]).should be_false
256
+ adapter.matches_condition?(article1, :priority.nin, [2, 3]).should be_true
257
+ adapter.matches_condition?(article1, :priority.nin, [1, 2]).should be_false
258
+ adapter.matches_condition?(article1, :priority.lt, 2).should be_true
259
+ adapter.matches_condition?(article1, :priority.lt, 1).should be_false
260
+ adapter.matches_condition?(article1, :priority.lteq, 1).should be_true
261
+ adapter.matches_condition?(article1, :priority.lteq, 0).should be_false
262
+ adapter.matches_condition?(article1, :priority.gt, 0).should be_true
263
+ adapter.matches_condition?(article1, :priority.gt, 1).should be_false
264
+ adapter.matches_condition?(article1, :priority.gteq, 1).should be_true
265
+ adapter.matches_condition?(article1, :priority.gteq, 2).should be_false
266
+ adapter.matches_condition?(article1, :name.like, "%ello worl%").should be_true
267
+ adapter.matches_condition?(article1, :name.like, "hello world").should be_true
268
+ adapter.matches_condition?(article1, :name.like, "hello%").should be_true
269
+ adapter.matches_condition?(article1, :name.like, "h%d").should be_true
270
+ adapter.matches_condition?(article1, :name.like, "%helo%").should be_false
271
+ adapter.matches_condition?(article1, :name.like, "hello").should be_false
272
+ adapter.matches_condition?(article1, :name.like, "hello.world").should be_false
273
+ # For some reason this is reporting "The not_matches MetaWhere condition is not supported."
274
+ # adapter.matches_condition?(article1, :name.nlike, "%helo%").should be_true
275
+ # adapter.matches_condition?(article1, :name.nlike, "%ello worl%").should be_false
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,120 @@
1
+ if ENV["MODEL_ADAPTER"] == "data_mapper"
2
+ require "spec_helper"
3
+
4
+ DataMapper.setup(:default, 'sqlite::memory:')
5
+
6
+ class DataMapperArticle
7
+ include DataMapper::Resource
8
+ property :id, Serial
9
+ property :published, Boolean, :default => false
10
+ property :secret, Boolean, :default => false
11
+ property :priority, Integer
12
+ has n, :data_mapper_comments
13
+ end
14
+
15
+ class DataMapperComment
16
+ include DataMapper::Resource
17
+ property :id, Serial
18
+ property :spam, Boolean, :default => false
19
+ belongs_to :data_mapper_article
20
+ end
21
+
22
+ DataMapper.finalize
23
+ DataMapper.auto_migrate!
24
+
25
+ describe CanCan::ModelAdapters::DataMapperAdapter do
26
+ before(:each) do
27
+ DataMapperArticle.destroy
28
+ DataMapperComment.destroy
29
+ @ability = Object.new
30
+ @ability.extend(CanCan::Ability)
31
+ end
32
+
33
+ it "is for only data mapper classes" do
34
+ CanCan::ModelAdapters::DataMapperAdapter.should_not be_for_class(Object)
35
+ CanCan::ModelAdapters::DataMapperAdapter.should be_for_class(DataMapperArticle)
36
+ CanCan::ModelAdapters::AbstractAdapter.adapter_class(DataMapperArticle).should == CanCan::ModelAdapters::DataMapperAdapter
37
+ end
38
+
39
+ it "finds record" do
40
+ article = DataMapperArticle.create
41
+ CanCan::ModelAdapters::DataMapperAdapter.find(DataMapperArticle, article.id).should == article
42
+ end
43
+
44
+ it "does not fetch any records when no abilities are defined" do
45
+ DataMapperArticle.create
46
+ DataMapperArticle.accessible_by(@ability).should be_empty
47
+ end
48
+
49
+ it "fetches all articles when one can read all" do
50
+ @ability.can :read, :data_mapper_articles
51
+ article = DataMapperArticle.create
52
+ DataMapperArticle.accessible_by(@ability).should == [article]
53
+ end
54
+
55
+ it "fetches only the articles that are published" do
56
+ @ability.can :read, :data_mapper_articles, :published => true
57
+ article1 = DataMapperArticle.create(:published => true)
58
+ article2 = DataMapperArticle.create(:published => false)
59
+ DataMapperArticle.accessible_by(@ability).should == [article1]
60
+ end
61
+
62
+ it "fetches any articles which are published or secret" do
63
+ @ability.can :read, :data_mapper_articles, :published => true
64
+ @ability.can :read, :data_mapper_articles, :secret => true
65
+ article1 = DataMapperArticle.create(:published => true, :secret => false)
66
+ article2 = DataMapperArticle.create(:published => true, :secret => true)
67
+ article3 = DataMapperArticle.create(:published => false, :secret => true)
68
+ article4 = DataMapperArticle.create(:published => false, :secret => false)
69
+ DataMapperArticle.accessible_by(@ability).should == [article1, article2, article3]
70
+ end
71
+
72
+ it "fetches only the articles that are published and not secret" do
73
+ pending "the `cannot` may require some custom SQL, maybe abstract out from Active Record adapter"
74
+ @ability.can :read, :data_mapper_articles, :published => true
75
+ @ability.cannot :read, :data_mapper_articles, :secret => true
76
+ article1 = DataMapperArticle.create(:published => true, :secret => false)
77
+ article2 = DataMapperArticle.create(:published => true, :secret => true)
78
+ article3 = DataMapperArticle.create(:published => false, :secret => true)
79
+ article4 = DataMapperArticle.create(:published => false, :secret => false)
80
+ DataMapperArticle.accessible_by(@ability).should == [article1]
81
+ end
82
+
83
+ it "only reads comments for articles which are published" do
84
+ @ability.can :read, :data_mapper_comments, :data_mapper_article => { :published => true }
85
+ comment1 = DataMapperComment.create(:data_mapper_article => DataMapperArticle.create!(:published => true))
86
+ comment2 = DataMapperComment.create(:data_mapper_article => DataMapperArticle.create!(:published => false))
87
+ DataMapperComment.accessible_by(@ability).should == [comment1]
88
+ end
89
+
90
+ it "allows conditions in SQL and merge with hash conditions" do
91
+ @ability.can :read, :data_mapper_articles, :published => true
92
+ @ability.can :read, :data_mapper_articles, ["secret=?", true]
93
+ article1 = DataMapperArticle.create(:published => true, :secret => false)
94
+ article4 = DataMapperArticle.create(:published => false, :secret => false)
95
+ DataMapperArticle.accessible_by(@ability).should == [article1]
96
+ end
97
+
98
+ it "matches gt comparison" do
99
+ @ability.can :read, :data_mapper_articles, :priority.gt => 3
100
+ article1 = DataMapperArticle.create(:priority => 4)
101
+ article2 = DataMapperArticle.create(:priority => 3)
102
+ DataMapperArticle.accessible_by(@ability).should == [article1]
103
+ @ability.should be_able_to(:read, article1)
104
+ @ability.should_not be_able_to(:read, article2)
105
+ end
106
+
107
+ it "matches gte comparison" do
108
+ @ability.can :read, :data_mapper_articles, :priority.gte => 3
109
+ article1 = DataMapperArticle.create(:priority => 4)
110
+ article2 = DataMapperArticle.create(:priority => 3)
111
+ article3 = DataMapperArticle.create(:priority => 2)
112
+ DataMapperArticle.accessible_by(@ability).should == [article1, article2]
113
+ @ability.should be_able_to(:read, article1)
114
+ @ability.should be_able_to(:read, article2)
115
+ @ability.should_not be_able_to(:read, article3)
116
+ end
117
+
118
+ # TODO: add more comparison specs
119
+ end
120
+ end