cancancan 1.17.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. data/cancancan.gemspec +10 -11
  3. data/init.rb +2 -0
  4. data/lib/cancan/ability/actions.rb +93 -0
  5. data/lib/cancan/ability/rules.rb +96 -0
  6. data/lib/cancan/ability/strong_parameter_support.rb +41 -0
  7. data/lib/cancan/ability.rb +87 -198
  8. data/lib/cancan/class_matcher.rb +30 -0
  9. data/lib/cancan/conditions_matcher.rb +147 -0
  10. data/lib/cancan/config.rb +101 -0
  11. data/lib/cancan/controller_additions.rb +13 -30
  12. data/lib/cancan/controller_resource.rb +33 -225
  13. data/lib/cancan/controller_resource_builder.rb +26 -0
  14. data/lib/cancan/controller_resource_finder.rb +42 -0
  15. data/lib/cancan/controller_resource_loader.rb +120 -0
  16. data/lib/cancan/controller_resource_name_finder.rb +23 -0
  17. data/lib/cancan/controller_resource_sanitizer.rb +32 -0
  18. data/lib/cancan/exceptions.rb +24 -4
  19. data/lib/cancan/matchers.rb +12 -1
  20. data/lib/cancan/model_adapters/abstract_adapter.rb +22 -1
  21. data/lib/cancan/model_adapters/active_record_4_adapter.rb +25 -44
  22. data/lib/cancan/model_adapters/active_record_5_adapter.rb +61 -0
  23. data/lib/cancan/model_adapters/active_record_adapter.rb +157 -83
  24. data/lib/cancan/model_adapters/conditions_extractor.rb +75 -0
  25. data/lib/cancan/model_adapters/conditions_normalizer.rb +49 -0
  26. data/lib/cancan/model_adapters/default_adapter.rb +2 -0
  27. data/lib/cancan/model_adapters/sti_normalizer.rb +47 -0
  28. data/lib/cancan/model_adapters/strategies/base.rb +40 -0
  29. data/lib/cancan/model_adapters/strategies/joined_alias_each_rule_as_exists_subquery.rb +93 -0
  30. data/lib/cancan/model_adapters/strategies/joined_alias_exists_subquery.rb +31 -0
  31. data/lib/cancan/model_adapters/strategies/left_join.rb +11 -0
  32. data/lib/cancan/model_adapters/strategies/subquery.rb +18 -0
  33. data/lib/cancan/model_additions.rb +6 -2
  34. data/lib/cancan/parameter_validators.rb +9 -0
  35. data/lib/cancan/relevant.rb +29 -0
  36. data/lib/cancan/rule.rb +67 -90
  37. data/lib/cancan/rules_compressor.rb +23 -0
  38. data/lib/cancan/sti_detector.rb +12 -0
  39. data/lib/cancan/unauthorized_message_resolver.rb +24 -0
  40. data/lib/cancan/version.rb +3 -1
  41. data/lib/cancan.rb +15 -10
  42. data/lib/cancancan.rb +2 -0
  43. data/lib/generators/cancan/ability/ability_generator.rb +3 -1
  44. data/lib/generators/cancan/ability/templates/ability.rb +9 -9
  45. metadata +64 -86
  46. data/.gitignore +0 -15
  47. data/.rspec +0 -1
  48. data/.rubocop.yml +0 -39
  49. data/.rubocop_todo.yml +0 -54
  50. data/.travis.yml +0 -39
  51. data/Appraisals +0 -105
  52. data/CHANGELOG.rdoc +0 -536
  53. data/CONTRIBUTING.md +0 -23
  54. data/Gemfile +0 -3
  55. data/LICENSE +0 -22
  56. data/README.md +0 -234
  57. data/Rakefile +0 -13
  58. data/gemfiles/activerecord_3.2.gemfile +0 -18
  59. data/gemfiles/activerecord_4.0.gemfile +0 -19
  60. data/gemfiles/activerecord_4.1.gemfile +0 -19
  61. data/gemfiles/activerecord_4.2.gemfile +0 -21
  62. data/gemfiles/activerecord_5.0.gemfile +0 -20
  63. data/gemfiles/mongoid_2.x.gemfile +0 -18
  64. data/gemfiles/sequel_3.x.gemfile +0 -18
  65. data/lib/cancan/inherited_resource.rb +0 -20
  66. data/lib/cancan/model_adapters/active_record_3_adapter.rb +0 -16
  67. data/lib/cancan/model_adapters/mongoid_adapter.rb +0 -80
  68. data/lib/cancan/model_adapters/sequel_adapter.rb +0 -87
  69. data/spec/README.rdoc +0 -27
  70. data/spec/cancan/ability_spec.rb +0 -553
  71. data/spec/cancan/controller_additions_spec.rb +0 -164
  72. data/spec/cancan/controller_resource_spec.rb +0 -645
  73. data/spec/cancan/exceptions_spec.rb +0 -58
  74. data/spec/cancan/inherited_resource_spec.rb +0 -71
  75. data/spec/cancan/matchers_spec.rb +0 -29
  76. data/spec/cancan/model_adapters/active_record_4_adapter_spec.rb +0 -160
  77. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +0 -415
  78. data/spec/cancan/model_adapters/default_adapter_spec.rb +0 -7
  79. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +0 -246
  80. data/spec/cancan/model_adapters/sequel_adapter_spec.rb +0 -129
  81. data/spec/cancan/rule_spec.rb +0 -52
  82. data/spec/matchers.rb +0 -13
  83. data/spec/spec.opts +0 -2
  84. data/spec/spec_helper.rb +0 -27
  85. data/spec/support/ability.rb +0 -6
@@ -1,553 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe CanCan::Ability do
4
- before(:each) do
5
- (@ability = double).extend(CanCan::Ability)
6
- end
7
-
8
- it 'is able to :read anything' do
9
- @ability.can :read, :all
10
- expect(@ability.can?(:read, String)).to be(true)
11
- expect(@ability.can?(:read, 123)).to be(true)
12
- end
13
-
14
- it "does not have permission to do something it doesn't know about" do
15
- expect(@ability.can?(:foodfight, String)).to be(false)
16
- end
17
-
18
- it 'passes true to `can?` when non false/nil is returned in block' do
19
- @ability.can :read, :all
20
- @ability.can :read, Symbol do |_sym|
21
- 'foo' # TODO: test that sym is nil when no instance is passed
22
- end
23
- expect(@ability.can?(:read, :some_symbol)).to be(true)
24
- end
25
-
26
- it 'passes nil to a block when no instance is passed' do
27
- @ability.can :read, Symbol do |sym|
28
- expect(sym).to be_nil
29
- true
30
- end
31
- expect(@ability.can?(:read, Symbol)).to be(true)
32
- end
33
-
34
- it 'passes to previous rule, if block returns false or nil' do
35
- @ability.can :read, Symbol
36
- @ability.can :read, Integer do |i|
37
- i < 5
38
- end
39
- @ability.can :read, Integer do |i|
40
- i > 10
41
- end
42
- expect(@ability.can?(:read, Symbol)).to be(true)
43
- expect(@ability.can?(:read, 11)).to be(true)
44
- expect(@ability.can?(:read, 1)).to be(true)
45
- expect(@ability.can?(:read, 6)).to be(false)
46
- end
47
-
48
- it 'overrides earlier rules with later ones (even if a different exact subject)' do
49
- @ability.cannot :read, Numeric
50
- @ability.can :read, Integer
51
-
52
- expect(@ability.can?(:read, 6)).to be(true)
53
- end
54
-
55
- it 'performs can(_, :all) before other checks when can(_, :all) is defined before' do
56
- @ability.can :manage, :all
57
- @ability.can :edit, String do |_string|
58
- raise 'Performed a check for :edit before the check for :all'
59
- end
60
- expect { @ability.can? :edit, 'a' }.to_not raise_exception
61
- end
62
-
63
- it 'performs can(_, :all) before other checks when can(_, :all) is defined after' do
64
- @ability.can :edit, String do |_string|
65
- raise 'Performed a check for :edit before the check for :all'
66
- end
67
- @ability.can :manage, :all
68
- expect { @ability.can? :edit, 'a' }.to_not raise_exception
69
- end
70
-
71
- it 'does not pass class with object if :all objects are accepted' do
72
- @ability.can :preview, :all do |object|
73
- expect(object).to eq(123)
74
- @block_called = true
75
- end
76
- @ability.can?(:preview, 123)
77
- expect(@block_called).to be(true)
78
- end
79
-
80
- it 'does not call block when only class is passed, only return true' do
81
- @block_called = false
82
- @ability.can :preview, :all do |_object|
83
- @block_called = true
84
- end
85
- expect(@ability.can?(:preview, Hash)).to be(true)
86
- expect(@block_called).to be(false)
87
- end
88
-
89
- it 'passes only object for global manage actions' do
90
- @ability.can :manage, String do |object|
91
- expect(object).to eq('foo')
92
- @block_called = true
93
- end
94
- expect(@ability.can?(:stuff, 'foo')).to be(true)
95
- expect(@block_called).to be(true)
96
- end
97
-
98
- it 'makes alias for update or destroy actions to modify action' do
99
- @ability.alias_action :update, :destroy, to: :modify
100
- @ability.can :modify, :all
101
- expect(@ability.can?(:update, 123)).to be(true)
102
- expect(@ability.can?(:destroy, 123)).to be(true)
103
- end
104
-
105
- it 'allows deeply nested aliased actions' do
106
- @ability.alias_action :increment, to: :sort
107
- @ability.alias_action :sort, to: :modify
108
- @ability.can :modify, :all
109
- expect(@ability.can?(:increment, 123)).to be(true)
110
- end
111
-
112
- it 'raises an Error if alias target is an exist action' do
113
- expect { @ability.alias_action :show, to: :show }
114
- .to raise_error(CanCan::Error, "You can't specify target (show) as alias because it is real action name")
115
- end
116
-
117
- it 'always calls block with arguments when passing no arguments to can' do
118
- @ability.can do |action, object_class, object|
119
- expect(action).to eq(:foo)
120
- expect(object_class).to eq(123.class)
121
- expect(object).to eq(123)
122
- @block_called = true
123
- end
124
- @ability.can?(:foo, 123)
125
- expect(@block_called).to be(true)
126
- end
127
-
128
- it 'passes nil to object when comparing class with can check' do
129
- @ability.can do |action, object_class, object|
130
- expect(action).to eq(:foo)
131
- expect(object_class).to eq(Hash)
132
- expect(object).to be_nil
133
- @block_called = true
134
- end
135
- @ability.can?(:foo, Hash)
136
- expect(@block_called).to be(true)
137
- end
138
-
139
- it 'automatically makes alias for index and show into read calls' do
140
- @ability.can :read, :all
141
- expect(@ability.can?(:index, 123)).to be(true)
142
- expect(@ability.can?(:show, 123)).to be(true)
143
- end
144
-
145
- it 'automatically makes alias for new and edit into create and update respectively' do
146
- @ability.can :create, :all
147
- @ability.can :update, :all
148
- expect(@ability.can?(:new, 123)).to be(true)
149
- expect(@ability.can?(:edit, 123)).to be(true)
150
- end
151
-
152
- it 'does not respond to prepare (now using initialize)' do
153
- expect(@ability).to_not respond_to(:prepare)
154
- end
155
-
156
- it 'offers cannot? method which is simply invert of can?' do
157
- expect(@ability.cannot?(:tie, String)).to be(true)
158
- end
159
-
160
- it 'is able to specify multiple actions and match any' do
161
- @ability.can [:read, :update], :all
162
- expect(@ability.can?(:read, 123)).to be(true)
163
- expect(@ability.can?(:update, 123)).to be(true)
164
- expect(@ability.can?(:count, 123)).to be(false)
165
- end
166
-
167
- it 'is able to specify multiple classes and match any' do
168
- @ability.can :update, [String, Range]
169
- expect(@ability.can?(:update, 'foo')).to be(true)
170
- expect(@ability.can?(:update, 1..3)).to be(true)
171
- expect(@ability.can?(:update, 123)).to be(false)
172
- end
173
-
174
- it 'checks if there is a permission for any of given subjects' do
175
- @ability.can :update, [String, Range]
176
- expect(@ability.can?(:update, any: ['foo', 1..3])).to be(true)
177
- expect(@ability.can?(:update, any: [1..3, 'foo'])).to be(true)
178
- expect(@ability.can?(:update, any: [123, 'foo'])).to be(true)
179
- expect(@ability.can?(:update, any: [123, 1.0])).to be(false)
180
- end
181
-
182
- it 'lists all permissions' do
183
- @ability.can :manage, :all
184
- @ability.can :learn, Range
185
- @ability.cannot :read, String
186
- @ability.cannot :read, Hash
187
- @ability.cannot :preview, Array
188
-
189
- expected_list = { can: { manage: ['all'],
190
- learn: ['Range'] },
191
- cannot: { read: %w(String Hash),
192
- index: %w(String Hash),
193
- show: %w(String Hash),
194
- preview: ['Array'] } }
195
-
196
- expect(@ability.permissions).to eq(expected_list)
197
- end
198
-
199
- it 'supports custom objects in the rule' do
200
- @ability.can :read, :stats
201
- expect(@ability.can?(:read, :stats)).to be(true)
202
- expect(@ability.can?(:update, :stats)).to be(false)
203
- expect(@ability.can?(:read, :nonstats)).to be(false)
204
- expect(@ability.can?(:read, any: [:stats, :nonstats])).to be(true)
205
- expect(@ability.can?(:read, any: [:nonstats, :neitherstats])).to be(false)
206
- end
207
-
208
- it 'checks ancestors of class' do
209
- @ability.can :read, Numeric
210
- expect(@ability.can?(:read, Integer)).to be(true)
211
- expect(@ability.can?(:read, 1.23)).to be(true)
212
- expect(@ability.can?(:read, 'foo')).to be(false)
213
- expect(@ability.can?(:read, any: [Integer, String])).to be(true)
214
- end
215
-
216
- it "supports 'cannot' method to define what user cannot do" do
217
- @ability.can :read, :all
218
- @ability.cannot :read, Integer
219
- expect(@ability.can?(:read, 'foo')).to be(true)
220
- expect(@ability.can?(:read, 123)).to be(false)
221
- expect(@ability.can?(:read, any: %w(foo bar))).to be(true)
222
- expect(@ability.can?(:read, any: [123, 'foo'])).to be(false)
223
- expect(@ability.can?(:read, any: [123, 456])).to be(false)
224
- end
225
-
226
- it 'passes to previous rule, if block returns false or nil' do
227
- @ability.can :read, :all
228
- @ability.cannot :read, Integer do |int|
229
- int > 10 ? nil : (int > 5)
230
- end
231
-
232
- expect(@ability.can?(:read, 'foo')).to be(true)
233
- expect(@ability.can?(:read, 3)).to be(true)
234
- expect(@ability.can?(:read, 8)).to be(false)
235
- expect(@ability.can?(:read, 123)).to be(true)
236
- expect(@ability.can?(:read, any: [123, 8])).to be(true)
237
- expect(@ability.can?(:read, any: [8, 9])).to be(false)
238
- end
239
-
240
- it 'always returns `false` for single cannot definition' do
241
- @ability.cannot :read, Integer do |int|
242
- int > 10 ? nil : (int > 5)
243
- end
244
- expect(@ability.can?(:read, 'foo')).to be(false)
245
- expect(@ability.can?(:read, 3)).to be(false)
246
- expect(@ability.can?(:read, 8)).to be(false)
247
- expect(@ability.can?(:read, 123)).to be(false)
248
- end
249
-
250
- it 'passes to previous cannot definition, if block returns false or nil' do
251
- @ability.cannot :read, :all
252
- @ability.can :read, Integer do |int|
253
- int > 10 ? nil : (int > 5)
254
- end
255
- expect(@ability.can?(:read, 'foo')).to be(false)
256
- expect(@ability.can?(:read, 3)).to be(false)
257
- expect(@ability.can?(:read, 10)).to be(true)
258
- expect(@ability.can?(:read, 123)).to be(false)
259
- end
260
-
261
- it 'appends aliased actions' do
262
- @ability.alias_action :update, to: :modify
263
- @ability.alias_action :destroy, to: :modify
264
- expect(@ability.aliased_actions[:modify]).to eq([:update, :destroy])
265
- end
266
-
267
- it 'clears aliased actions' do
268
- @ability.alias_action :update, to: :modify
269
- @ability.clear_aliased_actions
270
- expect(@ability.aliased_actions[:modify]).to be_nil
271
- end
272
-
273
- it 'passes additional arguments to block from can?' do
274
- @ability.can :read, Integer do |int, x|
275
- int > x
276
- end
277
-
278
- expect(@ability.can?(:read, 2, 1)).to be(true)
279
- expect(@ability.can?(:read, 2, 3)).to be(false)
280
- expect(@ability.can?(:read, { any: [4, 5] }, 3)).to be(true)
281
- expect(@ability.can?(:read, { any: [2, 3] }, 3)).to be(false)
282
- end
283
-
284
- it 'uses conditions as third parameter and determine abilities from it' do
285
- @ability.can :read, Range, begin: 1, end: 3
286
-
287
- expect(@ability.can?(:read, 1..3)).to be(true)
288
- expect(@ability.can?(:read, 1..4)).to be(false)
289
- expect(@ability.can?(:read, Range)).to be(true)
290
- expect(@ability.can?(:read, any: [1..3, 1..4])).to be(true)
291
- expect(@ability.can?(:read, any: [1..4, 2..4])).to be(false)
292
- end
293
-
294
- it 'allows an array of options in conditions hash' do
295
- @ability.can :read, Range, begin: [1, 3, 5]
296
-
297
- expect(@ability.can?(:read, 1..3)).to be(true)
298
- expect(@ability.can?(:read, 2..4)).to be(false)
299
- expect(@ability.can?(:read, 3..5)).to be(true)
300
- expect(@ability.can?(:read, any: [2..4, 3..5])).to be(true)
301
- expect(@ability.can?(:read, any: [2..4, 2..5])).to be(false)
302
- end
303
-
304
- it 'allows a range of options in conditions hash' do
305
- @ability.can :read, Range, begin: 1..3
306
- expect(@ability.can?(:read, 1..10)).to be(true)
307
- expect(@ability.can?(:read, 3..30)).to be(true)
308
- expect(@ability.can?(:read, 4..40)).to be(false)
309
- end
310
-
311
- it 'allows a range of time in conditions hash' do
312
- @ability.can :read, Range, begin: 1.day.from_now..3.days.from_now
313
- expect(@ability.can?(:read, 1.day.from_now..10.days.from_now)).to be(true)
314
- expect(@ability.can?(:read, 2.days.from_now..20.days.from_now)).to be(true)
315
- expect(@ability.can?(:read, 4.days.from_now..40.days.from_now)).to be(false)
316
- end
317
-
318
- it 'allows nested hashes in conditions hash' do
319
- @ability.can :read, Range, begin: { to_i: 5 }
320
- expect(@ability.can?(:read, 5..7)).to be(true)
321
- expect(@ability.can?(:read, 6..8)).to be(false)
322
- end
323
-
324
- it "matches any element passed in to nesting if it's an array (for has_many associations)" do
325
- @ability.can :read, Range, to_a: { to_i: 3 }
326
- expect(@ability.can?(:read, 1..5)).to be(true)
327
- expect(@ability.can?(:read, 4..6)).to be(false)
328
- end
329
-
330
- it 'accepts a set as a condition value' do
331
- expect(object_with_foo_2 = double(foo: 2)).to receive(:foo)
332
- expect(object_with_foo_3 = double(foo: 3)).to receive(:foo)
333
- @ability.can :read, Object, foo: [1, 2, 5].to_set
334
- expect(@ability.can?(:read, object_with_foo_2)).to be(true)
335
- expect(@ability.can?(:read, object_with_foo_3)).to be(false)
336
- end
337
-
338
- it 'does not match subjects return nil for methods that must match nested a nested conditions hash' do
339
- expect(object_with_foo = double(foo: :bar)).to receive(:foo)
340
- @ability.can :read, Array, first: { foo: :bar }
341
- expect(@ability.can?(:read, [object_with_foo])).to be(true)
342
- expect(@ability.can?(:read, [])).to be(false)
343
- end
344
-
345
- it 'matches strings but not substrings specified in a conditions hash' do
346
- @ability.can :read, String, presence: 'declassified'
347
- expect(@ability.can?(:read, 'declassified')).to be(true)
348
- expect(@ability.can?(:read, 'classified')).to be(false)
349
- end
350
-
351
- it 'does not stop at cannot definition when comparing class' do
352
- @ability.can :read, Range
353
- @ability.cannot :read, Range, begin: 1
354
- expect(@ability.can?(:read, 2..5)).to be(true)
355
- expect(@ability.can?(:read, 1..5)).to be(false)
356
- expect(@ability.can?(:read, Range)).to be(true)
357
- end
358
-
359
- it 'stops at cannot definition when no hash is present' do
360
- @ability.can :read, :all
361
- @ability.cannot :read, Range
362
- expect(@ability.can?(:read, 1..5)).to be(false)
363
- expect(@ability.can?(:read, Range)).to be(false)
364
- end
365
-
366
- it 'allows to check ability for Module' do
367
- module B
368
- end
369
- class A
370
- include B
371
- end
372
- @ability.can :read, B
373
- expect(@ability.can?(:read, A)).to be(true)
374
- expect(@ability.can?(:read, A.new)).to be(true)
375
- end
376
-
377
- it 'passes nil to a block for ability on Module when no instance is passed' do
378
- module B
379
- end
380
- class A
381
- include B
382
- end
383
- @ability.can :read, B do |sym|
384
- expect(sym).to be_nil
385
- true
386
- end
387
- expect(@ability.can?(:read, B)).to be(true)
388
- expect(@ability.can?(:read, A)).to be(true)
389
- end
390
-
391
- it 'checks permissions through association when passing a hash of subjects' do
392
- @ability.can :read, Range, string: { length: 3 }
393
-
394
- expect(@ability.can?(:read, 'foo' => Range)).to be(true)
395
- expect(@ability.can?(:read, 'foobar' => Range)).to be(false)
396
- expect(@ability.can?(:read, 123 => Range)).to be(true)
397
- expect(@ability.can?(:read, any: [{ 'foo' => Range }, { 'foobar' => Range }])).to be(true)
398
- expect(@ability.can?(:read, any: [{ 'food' => Range }, { 'foobar' => Range }])).to be(false)
399
- end
400
-
401
- it 'checks permissions correctly when passing a hash of subjects with multiple definitions' do
402
- @ability.can :read, Range, string: { length: 4 }
403
- @ability.can [:create, :read], Range, string: { upcase: 'FOO' }
404
-
405
- expect(@ability.can?(:read, 'foo' => Range)).to be(true)
406
- expect(@ability.can?(:read, 'foobar' => Range)).to be(false)
407
- expect(@ability.can?(:read, 1234 => Range)).to be(true)
408
- expect(@ability.can?(:read, any: [{ 'foo' => Range }, { 'foobar' => Range }])).to be(true)
409
- expect(@ability.can?(:read, any: [{ 'foo.bar' => Range }, { 'foobar' => Range }])).to be(false)
410
- end
411
-
412
- it 'allows to check ability on Hash-like object' do
413
- class Container < Hash
414
- end
415
- @ability.can :read, Container
416
- expect(@ability.can?(:read, Container.new)).to be(true)
417
- end
418
-
419
- it "has initial attributes based on hash conditions of 'new' action" do
420
- @ability.can :manage, Range, foo: 'foo', hash: { skip: 'hashes' }
421
- @ability.can :create, Range, bar: 123, array: %w(skip arrays)
422
- @ability.can :new, Range, baz: 'baz', range: 1..3
423
- @ability.cannot :new, Range, ignore: 'me'
424
- expect(@ability.attributes_for(:new, Range)).to eq(foo: 'foo', bar: 123, baz: 'baz')
425
- end
426
-
427
- it 'raises access denied exception if ability us unauthorized to perform a certain action' do
428
- begin
429
- @ability.authorize! :read, :foo, 1, 2, 3, message: 'Access denied!'
430
- rescue CanCan::AccessDenied => e
431
- expect(e.message).to eq('Access denied!')
432
- expect(e.action).to eq(:read)
433
- expect(e.subject).to eq(:foo)
434
- else
435
- raise 'Expected CanCan::AccessDenied exception to be raised'
436
- end
437
- end
438
-
439
- it 'does not raise access denied exception if ability is authorized to perform an action and return subject' do
440
- @ability.can :read, :foo
441
- expect do
442
- expect(@ability.authorize!(:read, :foo)).to eq(:foo)
443
- end.to_not raise_error
444
- end
445
-
446
- it 'knows when block is used in conditions' do
447
- @ability.can :read, :foo
448
- expect(@ability).to_not have_block(:read, :foo)
449
- @ability.can :read, :foo do |_foo|
450
- false
451
- end
452
- expect(@ability).to have_block(:read, :foo)
453
- end
454
-
455
- it 'knows when raw sql is used in conditions' do
456
- @ability.can :read, :foo
457
- expect(@ability).to_not have_raw_sql(:read, :foo)
458
- @ability.can :read, :foo, 'false'
459
- expect(@ability).to have_raw_sql(:read, :foo)
460
- end
461
-
462
- it 'raises access denied exception with default message if not specified' do
463
- begin
464
- @ability.authorize! :read, :foo
465
- rescue CanCan::AccessDenied => e
466
- e.default_message = 'Access denied!'
467
- expect(e.message).to eq('Access denied!')
468
- else
469
- raise 'Expected CanCan::AccessDenied exception to be raised'
470
- end
471
- end
472
-
473
- it 'determines model adapterO class by asking AbstractAdapter' do
474
- adapter_class = double
475
- model_class = double
476
- allow(CanCan::ModelAdapters::AbstractAdapter).to receive(:adapter_class).with(model_class) { adapter_class }
477
- allow(adapter_class).to receive(:new).with(model_class, []) { :adapter_instance }
478
- expect(@ability.model_adapter(model_class, :read)).to eq(:adapter_instance)
479
- end
480
-
481
- it "raises an error when attempting to use a block with a hash condition since it's not likely what they want" do
482
- expect do
483
- @ability.can :read, Array, published: true do
484
- false
485
- end
486
- end.to raise_error(CanCan::Error,
487
- 'You are not able to supply a block with a hash of conditions in read Array ability. '\
488
- 'Use either one.')
489
- end
490
-
491
- describe 'unauthorized message' do
492
- after(:each) do
493
- I18n.backend = nil
494
- end
495
-
496
- it 'uses action/subject in i18n' do
497
- I18n.backend.store_translations :en, unauthorized: { update: { array: 'foo' } }
498
- expect(@ability.unauthorized_message(:update, Array)).to eq('foo')
499
- expect(@ability.unauthorized_message(:update, [1, 2, 3])).to eq('foo')
500
- expect(@ability.unauthorized_message(:update, :missing)).to be_nil
501
- end
502
-
503
- it 'uses symbol as subject directly' do
504
- I18n.backend.store_translations :en, unauthorized: { has: { cheezburger: 'Nom nom nom. I eated it.' } }
505
- expect(@ability.unauthorized_message(:has, :cheezburger)).to eq('Nom nom nom. I eated it.')
506
- end
507
-
508
- it "falls back to 'manage' and 'all'" do
509
- I18n.backend.store_translations :en, unauthorized: {
510
- manage: { all: 'manage all', array: 'manage array' },
511
- update: { all: 'update all', array: 'update array' }
512
- }
513
- expect(@ability.unauthorized_message(:update, Array)).to eq('update array')
514
- expect(@ability.unauthorized_message(:update, Hash)).to eq('update all')
515
- expect(@ability.unauthorized_message(:foo, Array)).to eq('manage array')
516
- expect(@ability.unauthorized_message(:foo, Hash)).to eq('manage all')
517
- end
518
-
519
- it 'follows aliased actions' do
520
- I18n.backend.store_translations :en, unauthorized: { modify: { array: 'modify array' } }
521
- @ability.alias_action :update, to: :modify
522
- expect(@ability.unauthorized_message(:update, Array)).to eq('modify array')
523
- expect(@ability.unauthorized_message(:edit, Array)).to eq('modify array')
524
- end
525
-
526
- it 'has variables for action and subject' do
527
- # old syntax for now in case testing with old I18n
528
- I18n.backend.store_translations :en, unauthorized: { manage: { all: '%{action} %{subject}' } }
529
- expect(@ability.unauthorized_message(:update, Array)).to eq('update array')
530
- expect(@ability.unauthorized_message(:update, ArgumentError)).to eq('update argument error')
531
- expect(@ability.unauthorized_message(:edit, 1..3)).to eq('edit range')
532
- end
533
- end
534
-
535
- describe '#merge' do
536
- it 'adds the rules from the given ability' do
537
- @ability.can :use, :tools
538
- (another_ability = double).extend(CanCan::Ability)
539
- another_ability.can :use, :search
540
-
541
- @ability.merge(another_ability)
542
- expect(@ability.can?(:use, :search)).to be(true)
543
- expect(@ability.send(:rules).size).to eq(2)
544
- end
545
-
546
- it 'can add an empty ability' do
547
- (another_ability = double).extend(CanCan::Ability)
548
-
549
- @ability.merge(another_ability)
550
- expect(@ability.send(:rules).size).to eq(0)
551
- end
552
- end
553
- end