cancancan 1.17.0 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/cancancan.gemspec +4 -6
  3. data/lib/cancan.rb +2 -11
  4. data/lib/cancan/ability.rb +1 -1
  5. data/lib/cancan/controller_additions.rb +3 -24
  6. data/lib/cancan/controller_resource.rb +9 -25
  7. data/lib/cancan/matchers.rb +7 -1
  8. data/lib/cancan/model_adapters/active_record_4_adapter.rb +1 -3
  9. data/lib/cancan/model_adapters/active_record_adapter.rb +2 -2
  10. data/lib/cancan/rule.rb +2 -2
  11. data/lib/cancan/version.rb +1 -1
  12. metadata +22 -69
  13. data/.gitignore +0 -15
  14. data/.rspec +0 -1
  15. data/.rubocop.yml +0 -39
  16. data/.rubocop_todo.yml +0 -54
  17. data/.travis.yml +0 -39
  18. data/Appraisals +0 -105
  19. data/CHANGELOG.rdoc +0 -536
  20. data/CONTRIBUTING.md +0 -23
  21. data/Gemfile +0 -3
  22. data/LICENSE +0 -22
  23. data/README.md +0 -234
  24. data/Rakefile +0 -13
  25. data/gemfiles/activerecord_3.2.gemfile +0 -18
  26. data/gemfiles/activerecord_4.0.gemfile +0 -19
  27. data/gemfiles/activerecord_4.1.gemfile +0 -19
  28. data/gemfiles/activerecord_4.2.gemfile +0 -21
  29. data/gemfiles/activerecord_5.0.gemfile +0 -20
  30. data/gemfiles/mongoid_2.x.gemfile +0 -18
  31. data/gemfiles/sequel_3.x.gemfile +0 -18
  32. data/lib/cancan/inherited_resource.rb +0 -20
  33. data/lib/cancan/model_adapters/active_record_3_adapter.rb +0 -16
  34. data/lib/cancan/model_adapters/mongoid_adapter.rb +0 -80
  35. data/lib/cancan/model_adapters/sequel_adapter.rb +0 -87
  36. data/spec/README.rdoc +0 -27
  37. data/spec/cancan/ability_spec.rb +0 -553
  38. data/spec/cancan/controller_additions_spec.rb +0 -164
  39. data/spec/cancan/controller_resource_spec.rb +0 -645
  40. data/spec/cancan/exceptions_spec.rb +0 -58
  41. data/spec/cancan/inherited_resource_spec.rb +0 -71
  42. data/spec/cancan/matchers_spec.rb +0 -29
  43. data/spec/cancan/model_adapters/active_record_4_adapter_spec.rb +0 -160
  44. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +0 -415
  45. data/spec/cancan/model_adapters/default_adapter_spec.rb +0 -7
  46. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +0 -246
  47. data/spec/cancan/model_adapters/sequel_adapter_spec.rb +0 -129
  48. data/spec/cancan/rule_spec.rb +0 -52
  49. data/spec/matchers.rb +0 -13
  50. data/spec/spec.opts +0 -2
  51. data/spec/spec_helper.rb +0 -27
  52. data/spec/support/ability.rb +0 -6
@@ -1,20 +0,0 @@
1
- module CanCan
2
- # For use with Inherited Resources
3
- class InheritedResource < ControllerResource # :nodoc:
4
- def load_resource_instance
5
- if parent?
6
- @controller.send :association_chain
7
- @controller.instance_variable_get("@#{instance_name}")
8
- elsif new_actions.include? @params[:action].to_sym
9
- resource = @controller.send :build_resource
10
- assign_attributes(resource)
11
- else
12
- @controller.send :resource
13
- end
14
- end
15
-
16
- def resource_base
17
- @controller.send :end_of_association_chain
18
- end
19
- end
20
- end
@@ -1,16 +0,0 @@
1
- module CanCan
2
- module ModelAdapters
3
- class ActiveRecord3Adapter < AbstractAdapter
4
- include ActiveRecordAdapter
5
- def self.for_class?(model_class)
6
- model_class <= ActiveRecord::Base
7
- end
8
-
9
- private
10
-
11
- def build_relation(*where_conditions)
12
- @model_class.where(*where_conditions).includes(joins)
13
- end
14
- end
15
- end
16
- end
@@ -1,80 +0,0 @@
1
- module CanCan
2
- module ModelAdapters
3
- class MongoidAdapter < AbstractAdapter
4
- def self.for_class?(model_class)
5
- model_class <= Mongoid::Document
6
- end
7
-
8
- def self.override_conditions_hash_matching?(subject, conditions)
9
- conditions.any? do |k, _v|
10
- key_is_not_symbol = -> { !k.is_a?(Symbol) }
11
- subject_value_is_array = lambda do
12
- subject.respond_to?(k) && subject.send(k).is_a?(Array)
13
- end
14
-
15
- key_is_not_symbol.call || subject_value_is_array.call
16
- end
17
- end
18
-
19
- def self.matches_conditions_hash?(subject, conditions)
20
- # To avoid hitting the db, retrieve the raw Mongo selector from
21
- # the Mongoid Criteria and use Mongoid::Matchers#matches?
22
- subject.matches?(subject.class.where(conditions).selector)
23
- end
24
-
25
- def database_records
26
- if @rules.empty?
27
- @model_class.where(_id: { '$exists' => false, '$type' => 7 }) # return no records in Mongoid
28
- elsif @rules.size == 1 && @rules[0].conditions.is_a?(Mongoid::Criteria)
29
- @rules[0].conditions
30
- else
31
- # we only need to process can rules if
32
- # there are no rules with empty conditions
33
- database_records_from_multiple_rules
34
- end
35
- end
36
-
37
- def database_records_from_multiple_rules
38
- rules = @rules.reject { |rule| rule.conditions.empty? && rule.base_behavior }
39
- process_can_rules = @rules.count == rules.count
40
-
41
- rules.inject(@model_class.all) do |records, rule|
42
- if process_can_rules && rule.base_behavior
43
- records.or simplify_relations(@model_class, rule.conditions)
44
- elsif !rule.base_behavior
45
- records.excludes simplify_relations(@model_class, rule.conditions)
46
- else
47
- records
48
- end
49
- end
50
- end
51
-
52
- private
53
-
54
- # Look for criteria on relations and replace with simple id queries
55
- # eg.
56
- # {user: {:tags.all => []}} becomes {"user_id" => {"$in" => [__, ..]}}
57
- # {user: {:session => {:tags.all => []}}} becomes {"user_id" => {"session_id" => {"$in" => [__, ..]} }}
58
- def simplify_relations(model_class, conditions)
59
- model_relations = model_class.relations.with_indifferent_access
60
- Hash[
61
- conditions.map do |k, v|
62
- if (relation = model_relations[k])
63
- relation_class_name = relation[:class_name].blank? ? k.to_s.classify : relation[:class_name]
64
- v = simplify_relations(relation_class_name.constantize, v)
65
- relation_ids = relation_class_name.constantize.where(v).distinct(:_id)
66
- k = "#{k}_id"
67
- v = { '$in' => relation_ids }
68
- end
69
- [k, v]
70
- end
71
- ]
72
- end
73
- end
74
- end
75
- end
76
-
77
- # simplest way to add `accessible_by` to all Mongoid Documents
78
- module Mongoid::Document::ClassMethods
79
- include CanCan::ModelAdditions::ClassMethods
80
- end
@@ -1,87 +0,0 @@
1
- module CanCan
2
- module ModelAdapters
3
- class SequelAdapter < AbstractAdapter
4
- def self.for_class?(model_class)
5
- model_class <= Sequel::Model
6
- end
7
-
8
- def self.find(model_class, id)
9
- model_class[id]
10
- end
11
-
12
- def self.override_condition_matching?(_subject, _name, value)
13
- value.is_a?(Hash)
14
- end
15
-
16
- def self.matches_condition?(subject, name, value)
17
- obj = subject.send(name)
18
- if obj.nil?
19
- false
20
- else
21
- value.each do |k, v|
22
- if v.is_a?(Hash)
23
- return false unless matches_condition?(obj, k, v)
24
- elsif obj.send(k) != v
25
- return false
26
- end
27
- end
28
- end
29
- end
30
-
31
- def database_records
32
- if @rules.empty?
33
- @model_class.where('1=0')
34
- else
35
- # only need to process can rules if there are no can rule with empty conditions
36
- rules = @rules.reject { |rule| rule.base_behavior && rule.conditions.empty? }
37
- rules.reject!(&:base_behavior) if rules.count < @rules.count
38
-
39
- can_condition_added = false
40
- rules.reverse.inject(@model_class.dataset) do |records, rule|
41
- normalized_conditions = normalize_conditions(rule.conditions)
42
- if rule.base_behavior
43
- if can_condition_added
44
- records.or normalized_conditions
45
- else
46
- can_condition_added = true
47
- records.where normalized_conditions
48
- end
49
- else
50
- records.exclude normalized_conditions
51
- end
52
- end
53
- end
54
- end
55
-
56
- private
57
-
58
- def normalize_conditions(conditions, model_class = @model_class)
59
- return conditions unless conditions.is_a? Hash
60
- conditions.each_with_object({}) do |(name, value), result_hash|
61
- if value.is_a? Hash
62
- value = value.dup
63
- association_class = model_class.association_reflection(name).associated_class
64
- nested_resulted = value.each_with_object({}) do |(k, v), nested|
65
- if v.is_a?(Hash)
66
- value.delete(k)
67
- nested_class = association_class.association_reflection(k).associated_class
68
- nested[k] = nested_class.where(normalize_conditions(v, association_class))
69
- else
70
- nested[k] = v
71
- end
72
- nested
73
- end
74
- result_hash[name] = association_class.where(nested_resulted)
75
- else
76
- result_hash[name] = value
77
- end
78
- result_hash
79
- end
80
- end
81
- end
82
- end
83
- end
84
-
85
- Sequel::Model.class_eval do
86
- include CanCan::ModelAdditions
87
- end
data/spec/README.rdoc DELETED
@@ -1,27 +0,0 @@
1
- = CanCan Specs
2
-
3
- == Running the specs
4
-
5
- To run the specs first run the +bundle+ command to install the necessary gems and the +rake+ command to run the specs.
6
-
7
- bundle
8
-
9
- Then run the appraisal command to install all the necessary test sets:
10
-
11
- appraisal install
12
-
13
- You can then run all test sets:
14
-
15
- appraisal rake
16
-
17
- Or individual ones:
18
-
19
- appraisal activerecord_3.2 rake
20
-
21
- A list of the tests is in the +Appraisal+ file.
22
-
23
- The specs support Ruby 1.8.7+
24
-
25
- == Model Adapters
26
-
27
- The model adapter ENV setting has been removed and replaced with the +Appraisal+ file.
@@ -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