codeprimate-cancan 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. data/CHANGELOG.rdoc +291 -0
  2. data/Gemfile +20 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +111 -0
  5. data/Rakefile +18 -0
  6. data/init.rb +1 -0
  7. data/lib/cancan.rb +13 -0
  8. data/lib/cancan/ability.rb +298 -0
  9. data/lib/cancan/controller_additions.rb +389 -0
  10. data/lib/cancan/controller_resource.rb +222 -0
  11. data/lib/cancan/exceptions.rb +50 -0
  12. data/lib/cancan/inherited_resource.rb +19 -0
  13. data/lib/cancan/matchers.rb +14 -0
  14. data/lib/cancan/model_adapters/abstract_adapter.rb +56 -0
  15. data/lib/cancan/model_adapters/active_record_adapter.rb +165 -0
  16. data/lib/cancan/model_adapters/data_mapper_adapter.rb +34 -0
  17. data/lib/cancan/model_adapters/default_adapter.rb +7 -0
  18. data/lib/cancan/model_adapters/mongoid_adapter.rb +53 -0
  19. data/lib/cancan/model_additions.rb +31 -0
  20. data/lib/cancan/rule.rb +142 -0
  21. data/lib/generators/cancan/ability/USAGE +4 -0
  22. data/lib/generators/cancan/ability/ability_generator.rb +11 -0
  23. data/lib/generators/cancan/ability/templates/ability.rb +28 -0
  24. data/spec/README.rdoc +28 -0
  25. data/spec/cancan/ability_spec.rb +419 -0
  26. data/spec/cancan/controller_additions_spec.rb +137 -0
  27. data/spec/cancan/controller_resource_spec.rb +412 -0
  28. data/spec/cancan/exceptions_spec.rb +58 -0
  29. data/spec/cancan/inherited_resource_spec.rb +42 -0
  30. data/spec/cancan/matchers_spec.rb +33 -0
  31. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +278 -0
  32. data/spec/cancan/model_adapters/data_mapper_adapter_spec.rb +119 -0
  33. data/spec/cancan/model_adapters/default_adapter_spec.rb +7 -0
  34. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +216 -0
  35. data/spec/cancan/rule_spec.rb +39 -0
  36. data/spec/matchers.rb +13 -0
  37. data/spec/spec.opts +2 -0
  38. data/spec/spec_helper.rb +41 -0
  39. metadata +167 -0
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ describe CanCan::AccessDenied do
4
+ describe "with action and subject" do
5
+ before(:each) do
6
+ @exception = CanCan::AccessDenied.new(nil, :some_action, :some_subject)
7
+ end
8
+
9
+ it "should have action and subject accessors" do
10
+ @exception.action.should == :some_action
11
+ @exception.subject.should == :some_subject
12
+ end
13
+
14
+ it "should have 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::AccessDenied.new("Access denied!")
24
+ end
25
+
26
+ it "should have nil action and subject" do
27
+ @exception.action.should be_nil
28
+ @exception.subject.should be_nil
29
+ end
30
+
31
+ it "should have 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::AccessDenied.new
44
+ @exception.message.should == "This is a different message"
45
+ end
46
+
47
+ it "defaults to a nice message" do
48
+ @exception = CanCan::AccessDenied.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::AccessDenied.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,42 @@
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
+ stub(@controller).params { @params }
10
+ stub(@controller).current_ability { @ability }
11
+ stub(@controller_class).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
+ stub(@controller).resource { :project_resource }
17
+ CanCan::InheritedResource.new(@controller).load_resource
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
+ stub(@controller).build_resource { :project_resource }
24
+ CanCan::InheritedResource.new(@controller).load_resource
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
+ stub(@controller).association_chain { @controller.instance_variable_set(:@project, :project_resource) }
31
+ CanCan::InheritedResource.new(@controller, :parent => true).load_resource
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
+ stub(Project).accessible_by(@ability, :index) { :projects }
38
+ stub(@controller).end_of_association_chain { Project }
39
+ CanCan::InheritedResource.new(@controller).load_resource
40
+ @controller.instance_variable_get(:@projects).should == :projects
41
+ end
42
+ 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
+ mock(object).can?(: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
+ mock(object).can?(: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
+ mock(object).can?(: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
+ mock(object).can?(: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
+ RSpec.configure do |config|
5
+ config.extend WithModel
6
+ end
7
+
8
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
9
+
10
+ describe CanCan::ModelAdapters::ActiveRecordAdapter do
11
+ with_model :category do
12
+ table do |t|
13
+ t.boolean "visible"
14
+ end
15
+ model do
16
+ has_many :articles
17
+ end
18
+ end
19
+
20
+ with_model :article do
21
+ table do |t|
22
+ t.string "name"
23
+ t.boolean "published"
24
+ t.boolean "secret"
25
+ t.integer "priority"
26
+ t.integer "category_id"
27
+ end
28
+ model do
29
+ belongs_to :category
30
+ has_many :comments
31
+ end
32
+ end
33
+
34
+ with_model :comment do
35
+ table do |t|
36
+ t.boolean "spam"
37
+ t.integer "article_id"
38
+ end
39
+ model do
40
+ belongs_to :article
41
+ end
42
+ end
43
+
44
+ before(:each) do
45
+ Article.delete_all
46
+ Comment.delete_all
47
+ @ability = Object.new
48
+ @ability.extend(CanCan::Ability)
49
+ @article_table = Article.table_name
50
+ @comment_table = Comment.table_name
51
+ end
52
+
53
+ it "should be for only active record classes" do
54
+ CanCan::ModelAdapters::ActiveRecordAdapter.should_not be_for_class(Object)
55
+ CanCan::ModelAdapters::ActiveRecordAdapter.should be_for_class(Article)
56
+ CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::ActiveRecordAdapter
57
+ end
58
+
59
+ it "should find record" do
60
+ article = Article.create!
61
+ CanCan::ModelAdapters::ActiveRecordAdapter.find(Article, article.id).should == article
62
+ end
63
+
64
+ it "should not fetch any records when no abilities are defined" do
65
+ Article.create!
66
+ Article.accessible_by(@ability).should be_empty
67
+ end
68
+
69
+ it "should fetch all articles when one can read all" do
70
+ @ability.can :read, Article
71
+ article = Article.create!
72
+ Article.accessible_by(@ability).should == [article]
73
+ end
74
+
75
+ it "should fetch only the articles that are published" do
76
+ @ability.can :read, Article, :published => true
77
+ article1 = Article.create!(:published => true)
78
+ article2 = Article.create!(:published => false)
79
+ Article.accessible_by(@ability).should == [article1]
80
+ end
81
+
82
+ it "should fetch any articles which are published or secret" do
83
+ @ability.can :read, Article, :published => true
84
+ @ability.can :read, Article, :secret => true
85
+ article1 = Article.create!(:published => true, :secret => false)
86
+ article2 = Article.create!(:published => true, :secret => true)
87
+ article3 = Article.create!(:published => false, :secret => true)
88
+ article4 = Article.create!(:published => false, :secret => false)
89
+ Article.accessible_by(@ability).should == [article1, article2, article3]
90
+ end
91
+
92
+ it "should fetch only the articles that are published and not secret" do
93
+ @ability.can :read, Article, :published => true
94
+ @ability.cannot :read, Article, :secret => true
95
+ article1 = Article.create!(:published => true, :secret => false)
96
+ article2 = Article.create!(:published => true, :secret => true)
97
+ article3 = Article.create!(:published => false, :secret => true)
98
+ article4 = Article.create!(:published => false, :secret => false)
99
+ Article.accessible_by(@ability).should == [article1]
100
+ end
101
+
102
+ it "should only read comments for articles which are published" do
103
+ @ability.can :read, Comment, :article => { :published => true }
104
+ comment1 = Comment.create!(:article => Article.create!(:published => true))
105
+ comment2 = Comment.create!(:article => Article.create!(:published => false))
106
+ Comment.accessible_by(@ability).should == [comment1]
107
+ end
108
+
109
+ it "should only read comments for visible categories through articles" do
110
+ @ability.can :read, Comment, :article => { :category => { :visible => true } }
111
+ comment1 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => true)))
112
+ comment2 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => false)))
113
+ Comment.accessible_by(@ability).should == [comment1]
114
+ end
115
+
116
+ it "should allow conditions in SQL and merge with hash conditions" do
117
+ @ability.can :read, Article, :published => true
118
+ @ability.can :read, Article, ["secret=?", true]
119
+ article1 = Article.create!(:published => true, :secret => false)
120
+ article2 = Article.create!(:published => true, :secret => true)
121
+ article3 = Article.create!(:published => false, :secret => true)
122
+ article4 = Article.create!(:published => false, :secret => false)
123
+ Article.accessible_by(@ability).should == [article1, article2, article3]
124
+ end
125
+
126
+ it "should allow a scope for conditions" do
127
+ @ability.can :read, Article, Article.where(:secret => true)
128
+ article1 = Article.create!(:secret => true)
129
+ article2 = Article.create!(:secret => false)
130
+ Article.accessible_by(@ability).should == [article1]
131
+ end
132
+
133
+ it "should fetch only associated records when using with a scope for conditions" do
134
+ @ability.can :read, Article, Article.where(:secret => true)
135
+ category1 = Category.create!(:visible => false)
136
+ category2 = Category.create!(:visible => true)
137
+ article1 = Article.create!(:secret => true, :category => category1)
138
+ article2 = Article.create!(:secret => true, :category => category2)
139
+ category1.articles.accessible_by(@ability).should == [article1]
140
+ end
141
+
142
+ it "should raise an exception when trying to merge scope with other conditions" do
143
+ @ability.can :read, Article, :published => true
144
+ @ability.can :read, Article, Article.where(:secret => true)
145
+ 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.")
146
+ end
147
+
148
+ it "should not allow to fetch records when ability with just block present" do
149
+ @ability.can :read, Article do
150
+ false
151
+ end
152
+ lambda { Article.accessible_by(@ability) }.should raise_error(CanCan::Error)
153
+ end
154
+
155
+ it "should not allow to check ability on object against SQL conditions without block" do
156
+ @ability.can :read, Article, ["secret=?", true]
157
+ lambda { @ability.can? :read, Article.new }.should raise_error(CanCan::Error)
158
+ end
159
+
160
+ it "should have false conditions if no abilities match" do
161
+ @ability.model_adapter(Article, :read).conditions.should == "'t'='f'"
162
+ end
163
+
164
+ it "should return false conditions for cannot clause" do
165
+ @ability.cannot :read, Article
166
+ @ability.model_adapter(Article, :read).conditions.should == "'t'='f'"
167
+ end
168
+
169
+ it "should return SQL for single `can` definition in front of default `cannot` condition" do
170
+ @ability.cannot :read, Article
171
+ @ability.can :read, Article, :published => false, :secret => true
172
+ @ability.model_adapter(Article, :read).conditions.should orderlessly_match(%Q["#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't'])
173
+ end
174
+
175
+ it "should return true condition for single `can` definition in front of default `can` condition" do
176
+ @ability.can :read, Article
177
+ @ability.can :read, Article, :published => false, :secret => true
178
+ @ability.model_adapter(Article, :read).conditions.should == "'t'='t'"
179
+ end
180
+
181
+ it "should return `false condition` for single `cannot` definition in front of default `cannot` condition" do
182
+ @ability.cannot :read, Article
183
+ @ability.cannot :read, Article, :published => false, :secret => true
184
+ @ability.model_adapter(Article, :read).conditions.should == "'t'='f'"
185
+ end
186
+
187
+ it "should return `not (sql)` for single `cannot` definition in front of default `can` condition" do
188
+ @ability.can :read, Article
189
+ @ability.cannot :read, Article, :published => false, :secret => true
190
+ @ability.model_adapter(Article, :read).conditions.should orderlessly_match(%Q["not (#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't')])
191
+ end
192
+
193
+ it "should return appropriate sql conditions in complex case" do
194
+ @ability.can :read, Article
195
+ @ability.can :manage, Article, :id => 1
196
+ @ability.can :update, Article, :published => true
197
+ @ability.cannot :update, Article, :secret => true
198
+ @ability.model_adapter(Article, :update).conditions.should == %Q[not ("#{@article_table}"."secret" = 't') AND (("#{@article_table}"."published" = 't') OR ("#{@article_table}"."id" = 1))]
199
+ @ability.model_adapter(Article, :manage).conditions.should == {:id => 1}
200
+ @ability.model_adapter(Article, :read).conditions.should == "'t'='t'"
201
+ end
202
+
203
+ it "should not forget conditions when calling with SQL string" do
204
+ @ability.can :read, Article, :published => true
205
+ @ability.can :read, Article, ['secret=?', false]
206
+ adapter = @ability.model_adapter(Article, :read)
207
+ 2.times do
208
+ adapter.conditions.should == %Q[(secret='f') OR ("#{@article_table}"."published" = 't')]
209
+ end
210
+ end
211
+
212
+ it "should have nil joins if no rules" do
213
+ @ability.model_adapter(Article, :read).joins.should be_nil
214
+ end
215
+
216
+ it "should have nil joins if no nested hashes specified in conditions" do
217
+ @ability.can :read, Article, :published => false
218
+ @ability.can :read, Article, :secret => true
219
+ @ability.model_adapter(Article, :read).joins.should be_nil
220
+ end
221
+
222
+ it "should merge separate joins into a single array" do
223
+ @ability.can :read, Article, :project => { :blocked => false }
224
+ @ability.can :read, Article, :company => { :admin => true }
225
+ @ability.model_adapter(Article, :read).joins.inspect.should orderlessly_match([:company, :project].inspect)
226
+ end
227
+
228
+ it "should merge same joins into a single array" do
229
+ @ability.can :read, Article, :project => { :blocked => false }
230
+ @ability.can :read, Article, :project => { :admin => true }
231
+ @ability.model_adapter(Article, :read).joins.should == [:project]
232
+ end
233
+
234
+ it "should restrict articles given a MetaWhere condition" do
235
+ @ability.can :read, Article, :priority.lt => 2
236
+ article1 = Article.create!(:priority => 1)
237
+ article2 = Article.create!(:priority => 3)
238
+ Article.accessible_by(@ability).should == [article1]
239
+ @ability.should be_able_to(:read, article1)
240
+ @ability.should_not be_able_to(:read, article2)
241
+ end
242
+
243
+ it "should match any MetaWhere condition" do
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,119 @@
1
+ if ENV["MODEL_ADAPTER"] == "data_mapper"
2
+ require "spec_helper"
3
+
4
+ DataMapper.setup(:default, 'sqlite::memory:')
5
+
6
+ class Article
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, :comments
13
+ end
14
+
15
+ class Comment
16
+ include DataMapper::Resource
17
+ property :id, Serial
18
+ property :spam, Boolean, :default => false
19
+ belongs_to :article
20
+ end
21
+
22
+ DataMapper.finalize
23
+ DataMapper.auto_migrate!
24
+
25
+ describe CanCan::ModelAdapters::DataMapperAdapter do
26
+ before(:each) do
27
+ Article.destroy
28
+ Comment.destroy
29
+ @ability = Object.new
30
+ @ability.extend(CanCan::Ability)
31
+ end
32
+
33
+ it "should be for only data mapper classes" do
34
+ CanCan::ModelAdapters::DataMapperAdapter.should_not be_for_class(Object)
35
+ CanCan::ModelAdapters::DataMapperAdapter.should be_for_class(Article)
36
+ CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::DataMapperAdapter
37
+ end
38
+
39
+ it "should find record" do
40
+ article = Article.create
41
+ CanCan::ModelAdapters::DataMapperAdapter.find(Article, article.id).should == article
42
+ end
43
+
44
+ it "should not fetch any records when no abilities are defined" do
45
+ Article.create
46
+ Article.accessible_by(@ability).should be_empty
47
+ end
48
+
49
+ it "should fetch all articles when one can read all" do
50
+ @ability.can :read, Article
51
+ article = Article.create
52
+ Article.accessible_by(@ability).should == [article]
53
+ end
54
+
55
+ it "should fetch only the articles that are published" do
56
+ @ability.can :read, Article, :published => true
57
+ article1 = Article.create(:published => true)
58
+ article2 = Article.create(:published => false)
59
+ Article.accessible_by(@ability).should == [article1]
60
+ end
61
+
62
+ it "should fetch any articles which are published or secret" do
63
+ @ability.can :read, Article, :published => true
64
+ @ability.can :read, Article, :secret => true
65
+ article1 = Article.create(:published => true, :secret => false)
66
+ article2 = Article.create(:published => true, :secret => true)
67
+ article3 = Article.create(:published => false, :secret => true)
68
+ article4 = Article.create(:published => false, :secret => false)
69
+ Article.accessible_by(@ability).should == [article1, article2, article3]
70
+ end
71
+
72
+ it "should fetch only the articles that are published and not secret" do
73
+ @ability.can :read, Article, :published => true
74
+ @ability.cannot :read, Article, :secret => true
75
+ article1 = Article.create(:published => true, :secret => false)
76
+ article2 = Article.create(:published => true, :secret => true)
77
+ article3 = Article.create(:published => false, :secret => true)
78
+ article4 = Article.create(:published => false, :secret => false)
79
+ Article.accessible_by(@ability).should == [article1]
80
+ end
81
+
82
+ it "should only read comments for articles which are published" do
83
+ @ability.can :read, Comment, :article => { :published => true }
84
+ comment1 = Comment.create(:article => Article.create!(:published => true))
85
+ comment2 = Comment.create(:article => Article.create!(:published => false))
86
+ Comment.accessible_by(@ability).should == [comment1]
87
+ end
88
+
89
+ it "should allow conditions in SQL and merge with hash conditions" do
90
+ @ability.can :read, Article, :published => true
91
+ @ability.can :read, Article, ["secret=?", true]
92
+ article1 = Article.create(:published => true, :secret => false)
93
+ article4 = Article.create(:published => false, :secret => false)
94
+ Article.accessible_by(@ability).should == [article1]
95
+ end
96
+
97
+ it "should match gt comparison" do
98
+ @ability.can :read, Article, :priority.gt => 3
99
+ article1 = Article.create(:priority => 4)
100
+ article2 = Article.create(:priority => 3)
101
+ Article.accessible_by(@ability).should == [article1]
102
+ @ability.should be_able_to(:read, article1)
103
+ @ability.should_not be_able_to(:read, article2)
104
+ end
105
+
106
+ it "should match gte comparison" do
107
+ @ability.can :read, Article, :priority.gte => 3
108
+ article1 = Article.create(:priority => 4)
109
+ article2 = Article.create(:priority => 3)
110
+ article3 = Article.create(:priority => 2)
111
+ Article.accessible_by(@ability).should == [article1, article2]
112
+ @ability.should be_able_to(:read, article1)
113
+ @ability.should be_able_to(:read, article2)
114
+ @ability.should_not be_able_to(:read, article3)
115
+ end
116
+
117
+ # TODO: add more comparison specs
118
+ end
119
+ end