cancancan 1.8.0 → 1.14.0

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +12 -11
  3. data/Appraisals +23 -16
  4. data/CHANGELOG.rdoc +75 -0
  5. data/CONTRIBUTING.md +2 -2
  6. data/README.md +217 -0
  7. data/cancancan.gemspec +5 -8
  8. data/gemfiles/{activerecord_3.1.gemfile → activerecord_4.0.gemfile} +2 -1
  9. data/gemfiles/{activerecord_3.0.gemfile → activerecord_4.1.gemfile} +2 -3
  10. data/gemfiles/activerecord_4.2.gemfile +18 -0
  11. data/gemfiles/sequel_3.x.gemfile +0 -1
  12. data/lib/cancan/ability.rb +94 -17
  13. data/lib/cancan/controller_additions.rb +1 -1
  14. data/lib/cancan/controller_resource.rb +32 -24
  15. data/lib/cancan/matchers.rb +29 -6
  16. data/lib/cancan/model_adapters/active_record_3_adapter.rb +16 -0
  17. data/lib/cancan/model_adapters/active_record_4_adapter.rb +55 -0
  18. data/lib/cancan/model_adapters/active_record_adapter.rb +9 -41
  19. data/lib/cancan/model_adapters/mongoid_adapter.rb +23 -2
  20. data/lib/cancan/rule.rb +10 -5
  21. data/lib/cancan/version.rb +1 -1
  22. data/lib/cancan.rb +11 -2
  23. data/lib/generators/cancan/ability/templates/ability.rb +1 -1
  24. data/spec/README.rdoc +2 -2
  25. data/spec/cancan/ability_spec.rb +174 -111
  26. data/spec/cancan/controller_additions_spec.rb +8 -8
  27. data/spec/cancan/controller_resource_spec.rb +96 -79
  28. data/spec/cancan/model_adapters/active_record_4_adapter_spec.rb +154 -0
  29. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +92 -64
  30. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +26 -6
  31. data/spec/cancan/model_adapters/sequel_adapter_spec.rb +49 -65
  32. data/spec/matchers.rb +2 -2
  33. data/spec/spec_helper.rb +3 -2
  34. metadata +19 -40
  35. data/README.rdoc +0 -183
  36. data/gemfiles/datamapper_1.x.gemfile +0 -10
  37. data/lib/cancan/model_adapters/data_mapper_adapter.rb +0 -34
  38. data/spec/cancan/model_adapters/data_mapper_adapter_spec.rb +0 -119
@@ -61,14 +61,11 @@ module CanCan
61
61
  #
62
62
  # Also see the RSpec Matchers to aid in testing.
63
63
  def can?(action, subject, *extra_args)
64
- subject = extract_subjects(subject)
65
-
66
- match = subject.map do |subject|
67
- relevant_rules_for_match(action, subject).detect do |rule|
68
- rule.matches_conditions?(action, subject, extra_args)
64
+ match = extract_subjects(subject).lazy.map do |a_subject|
65
+ relevant_rules_for_match(action, a_subject).detect do |rule|
66
+ rule.matches_conditions?(action, a_subject, extra_args)
69
67
  end
70
- end.compact.first
71
-
68
+ end.reject(&:nil?).first
72
69
  match ? match.base_behavior : false
73
70
  end
74
71
  # Convenience method which works the same as "can?" but returns the opposite value.
@@ -119,7 +116,7 @@ module CanCan
119
116
  # can :read, :stats
120
117
  # can? :read, :stats # => true
121
118
  #
122
- # IMPORTANT: Neither a hash of conditions or a block will be used when checking permission on a class.
119
+ # IMPORTANT: Neither a hash of conditions nor a block will be used when checking permission on a class.
123
120
  #
124
121
  # can :update, Project, :priority => 3
125
122
  # can? :update, Project # => true
@@ -133,7 +130,7 @@ module CanCan
133
130
  # end
134
131
  #
135
132
  def can(action = nil, subject = nil, conditions = nil, &block)
136
- rules << Rule.new(true, action, subject, conditions, block)
133
+ add_rule(Rule.new(true, action, subject, conditions, block))
137
134
  end
138
135
 
139
136
  # Defines an ability which cannot be done. Accepts the same arguments as "can".
@@ -149,7 +146,7 @@ module CanCan
149
146
  # end
150
147
  #
151
148
  def cannot(action = nil, subject = nil, conditions = nil, &block)
152
- rules << Rule.new(false, action, subject, conditions, block)
149
+ add_rule(Rule.new(false, action, subject, conditions, block))
153
150
  end
154
151
 
155
152
  # Alias one or more actions into another one.
@@ -246,12 +243,49 @@ module CanCan
246
243
  end
247
244
 
248
245
  def merge(ability)
249
- ability.send(:rules).each do |rule|
250
- rules << rule.dup
246
+ ability.rules.each do |rule|
247
+ add_rule(rule.dup)
251
248
  end
252
249
  self
253
250
  end
254
251
 
252
+ # Return a hash of permissions for the user in the format of:
253
+ # {
254
+ # can: can_hash,
255
+ # cannot: cannot_hash
256
+ # }
257
+ #
258
+ # Where can_hash and cannot_hash are formatted thusly:
259
+ # {
260
+ # action: array_of_objects
261
+ # }
262
+ def permissions
263
+ permissions_list = {:can => {}, :cannot => {}}
264
+
265
+ rules.each do |rule|
266
+ subjects = rule.subjects
267
+ expand_actions(rule.actions).each do |action|
268
+ if(rule.base_behavior)
269
+ permissions_list[:can][action] ||= []
270
+ permissions_list[:can][action] += subjects.map(&:to_s)
271
+ else
272
+ permissions_list[:cannot][action] ||= []
273
+ permissions_list[:cannot][action] += subjects.map(&:to_s)
274
+ end
275
+ end
276
+ end
277
+
278
+ permissions_list
279
+ end
280
+
281
+ protected
282
+
283
+ # Must be protected as an ability can merge with other abilities.
284
+ # This means that an ability must expose their rules with another ability.
285
+ def rules
286
+ @rules ||= []
287
+ end
288
+
255
289
  private
256
290
 
257
291
  def unauthorized_message_keys(action, subject)
@@ -285,7 +319,7 @@ module CanCan
285
319
 
286
320
  # It translates to an array the subject or the hash with multiple subjects given to can?.
287
321
  def extract_subjects(subject)
288
- subject = if subject.respond_to?(:keys) && subject.key?(:any)
322
+ if subject.kind_of?(Hash) && subject.key?(:any)
289
323
  subject[:any]
290
324
  else
291
325
  [subject]
@@ -302,21 +336,64 @@ module CanCan
302
336
  results
303
337
  end
304
338
 
305
- def rules
306
- @rules ||= []
339
+ def add_rule(rule)
340
+ rules << rule
341
+ add_rule_to_index(rule, rules.size - 1)
342
+ end
343
+
344
+ def add_rule_to_index(rule, position)
345
+ @rules_index ||= Hash.new { |h, k| h[k] = [] }
346
+
347
+ subjects = rule.subjects.compact
348
+ subjects << :all if subjects.empty?
349
+
350
+ subjects.each do |subject|
351
+ @rules_index[subject] << position
352
+ end
353
+ end
354
+
355
+ def alternative_subjects(subject)
356
+ subject = subject.class unless subject.is_a?(Module)
357
+ [:all, *subject.ancestors, subject.class.to_s]
307
358
  end
308
359
 
309
360
  # Returns an array of Rule instances which match the action and subject
310
361
  # This does not take into consideration any hash conditions or block statements
311
362
  def relevant_rules(action, subject)
312
- relevant = rules.select do |rule|
363
+ return [] unless @rules
364
+ relevant = possible_relevant_rules(subject).select do |rule|
313
365
  rule.expanded_actions = expand_actions(rule.actions)
314
366
  rule.relevant? action, subject
315
367
  end
316
- relevant.reverse!
368
+ relevant.reverse!.uniq!
369
+ optimize_order! relevant
317
370
  relevant
318
371
  end
319
372
 
373
+ # Optimizes the order of the rules, so that rules with the :all subject are evaluated first.
374
+ def optimize_order!(rules)
375
+ first_can_in_group = -1
376
+ rules.each_with_index do |rule, i|
377
+ (first_can_in_group = -1) and next unless rule.base_behavior
378
+ (first_can_in_group = i) and next if first_can_in_group == -1
379
+ if rule.subjects == [:all]
380
+ rules[i] = rules[first_can_in_group]
381
+ rules[first_can_in_group] = rule
382
+ first_can_in_group += 1
383
+ end
384
+ end
385
+ end
386
+
387
+ def possible_relevant_rules(subject)
388
+ if subject.is_a?(Hash)
389
+ rules
390
+ else
391
+ positions = @rules_index.values_at(subject, *alternative_subjects(subject))
392
+ positions.flatten!.sort!
393
+ positions.map { |i| @rules[i] }
394
+ end
395
+ end
396
+
320
397
  def relevant_rules_for_match(action, subject)
321
398
  relevant_rules(action, subject).each do |rule|
322
399
  if rule.only_raw_sql?
@@ -294,7 +294,7 @@ module CanCan
294
294
 
295
295
  def self.included(base)
296
296
  base.extend ClassMethods
297
- base.helper_method :can?, :cannot?, :current_ability
297
+ base.helper_method :can?, :cannot?, :current_ability if base.respond_to? :helper_method
298
298
  end
299
299
 
300
300
  # Raises a CanCan::AccessDenied exception if the current_ability cannot
@@ -46,17 +46,12 @@ module CanCan
46
46
  @options.has_key?(:parent) ? @options[:parent] : @name && @name != name_from_controller.to_sym
47
47
  end
48
48
 
49
- def skip?(behavior) # This could probably use some refactoring
50
- options = @controller.class.cancan_skipper[behavior][@name]
51
- if options.nil?
52
- false
53
- elsif options == {}
54
- true
55
- elsif options[:except] && ![options[:except]].flatten.include?(@params[:action].to_sym)
56
- true
57
- elsif [options[:only]].flatten.include?(@params[:action].to_sym)
58
- true
59
- end
49
+ def skip?(behavior)
50
+ return false unless options = @controller.class.cancan_skipper[behavior][@name]
51
+
52
+ options == {} ||
53
+ options[:except] && !action_exists_in?(options[:except]) ||
54
+ action_exists_in?(options[:only])
60
55
  end
61
56
 
62
57
  protected
@@ -123,7 +118,11 @@ module CanCan
123
118
  end
124
119
 
125
120
  def authorization_action
126
- parent? ? :show : @params[:action].to_sym
121
+ parent? ? parent_authorization_action : @params[:action].to_sym
122
+ end
123
+
124
+ def parent_authorization_action
125
+ @options[:parent_action] || :show
127
126
  end
128
127
 
129
128
  def id_param
@@ -220,22 +219,29 @@ module CanCan
220
219
  end
221
220
 
222
221
  def resource_params
223
- if param_actions.include?(@params[:action].to_sym) && params_method.present?
222
+ if parameters_require_sanitizing? && params_method.present?
224
223
  return case params_method
225
224
  when Symbol then @controller.send(params_method)
226
225
  when String then @controller.instance_eval(params_method)
227
226
  when Proc then params_method.call(@controller)
228
227
  end
229
- elsif @options[:class]
230
- params_key = extract_key(@options[:class])
231
- return @params[params_key] if @params[params_key]
228
+ else
229
+ resource_params_by_namespaced_name
232
230
  end
231
+ end
233
232
 
234
- resource_params_by_namespaced_name
233
+ def parameters_require_sanitizing?
234
+ save_actions.include?(@params[:action].to_sym) || resource_params_by_namespaced_name.present?
235
235
  end
236
236
 
237
237
  def resource_params_by_namespaced_name
238
- @params[extract_key(namespaced_name)]
238
+ if @options[:instance_name] && @params.has_key?(extract_key(@options[:instance_name]))
239
+ @params[extract_key(@options[:instance_name])]
240
+ elsif @options[:class] && @params.has_key?(extract_key(@options[:class]))
241
+ @params[extract_key(@options[:class])]
242
+ else
243
+ @params[extract_key(namespaced_name)]
244
+ end
239
245
  end
240
246
 
241
247
  def params_method
@@ -252,17 +258,15 @@ module CanCan
252
258
  end
253
259
 
254
260
  def namespace
255
- @params[:controller].split(/::|\//)[0..-2]
261
+ @params[:controller].split('/')[0..-2]
256
262
  end
257
263
 
258
264
  def namespaced_name
259
- [namespace, name.camelize].join('::').singularize.camelize.constantize
260
- rescue NameError
261
- name
265
+ ([namespace, name] * '/').singularize.camelize.safe_constantize || name
262
266
  end
263
267
 
264
268
  def name_from_controller
265
- @params[:controller].sub("Controller", "").underscore.split('/').last.singularize
269
+ @params[:controller].split('/').last.singularize
266
270
  end
267
271
 
268
272
  def instance_name
@@ -277,12 +281,16 @@ module CanCan
277
281
  [:new, :create] + Array(@options[:new])
278
282
  end
279
283
 
280
- def param_actions
284
+ def save_actions
281
285
  [:create, :update]
282
286
  end
283
287
 
284
288
  private
285
289
 
290
+ def action_exists_in?(options)
291
+ Array(options).include?(@params[:action].to_sym)
292
+ end
293
+
286
294
  def extract_key(value)
287
295
  value.to_s.underscore.gsub('/', '_')
288
296
  end
@@ -1,10 +1,10 @@
1
- rspec_module = defined?(RSpec::Core) ? 'RSpec' : 'Spec' # for RSpec 1 compatability
1
+ rspec_module = defined?(RSpec::Core) ? 'RSpec' : 'Spec' # RSpec 1 compatability
2
2
 
3
3
  if rspec_module == 'RSpec'
4
4
  require 'rspec/core'
5
5
  require 'rspec/expectations'
6
6
  else
7
- ActiveSupport::Deprecation.warn("RSpec v1 will not be supported in the CanCanCan >= 2.0.0")
7
+ ActiveSupport::Deprecation.warn('RSpec < 3 will not be supported in the CanCanCan >= 2.0.0')
8
8
  end
9
9
 
10
10
  Kernel.const_get(rspec_module)::Matchers.define :be_able_to do |*args|
@@ -12,11 +12,34 @@ Kernel.const_get(rspec_module)::Matchers.define :be_able_to do |*args|
12
12
  ability.can?(*args)
13
13
  end
14
14
 
15
- failure_message_for_should do |ability|
16
- "expected to be able to #{args.map(&:inspect).join(" ")}"
15
+ # Check that RSpec is < 2.99
16
+ if !respond_to?(:failure_message) && respond_to?(:failure_message_for_should)
17
+ alias :failure_message :failure_message_for_should
17
18
  end
18
19
 
19
- failure_message_for_should_not do |ability|
20
- "expected not to be able to #{args.map(&:inspect).join(" ")}"
20
+ if !respond_to?(:failure_message_when_negated) && respond_to?(:failure_message_for_should_not)
21
+ alias :failure_message_when_negated :failure_message_for_should_not
22
+ end
23
+
24
+ failure_message do
25
+ resource = args[1]
26
+ if resource.instance_of?(Class)
27
+ "expected to be able to #{args.map(&:to_s).join(' ')}"
28
+ else
29
+ "expected to be able to #{args.map(&:inspect).join(' ')}"
30
+ end
31
+ end
32
+
33
+ failure_message_when_negated do
34
+ resource = args[1]
35
+ if resource.instance_of?(Class)
36
+ "expected not to be able to #{args.map(&:to_s).join(' ')}"
37
+ else
38
+ "expected not to be able to #{args.map(&:inspect).join(' ')}"
39
+ end
40
+ end
41
+
42
+ description do
43
+ "be able to #{args.map(&:to_s).join(' ')}"
21
44
  end
22
45
  end
@@ -0,0 +1,16 @@
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
@@ -0,0 +1,55 @@
1
+ module CanCan
2
+ module ModelAdapters
3
+ class ActiveRecord4Adapter < AbstractAdapter
4
+ include ActiveRecordAdapter
5
+ def self.for_class?(model_class)
6
+ model_class <= ActiveRecord::Base
7
+ end
8
+
9
+ private
10
+
11
+ # As of rails 4, `includes()` no longer causes active record to
12
+ # look inside the where clause to decide to outer join tables
13
+ # you're using in the where. Instead, `references()` is required
14
+ # in addition to `includes()` to force the outer join.
15
+ def build_relation(*where_conditions)
16
+ relation = @model_class.where(*where_conditions)
17
+ relation = relation.includes(joins).references(joins) if joins.present?
18
+ relation
19
+ end
20
+
21
+ def self.override_condition_matching?(subject, name, value)
22
+ # ActiveRecord introduced enums in version 4.1.
23
+ ActiveRecord::VERSION::MINOR >= 1 &&
24
+ subject.class.defined_enums.include?(name.to_s)
25
+ end
26
+
27
+ def self.matches_condition?(subject, name, value)
28
+ # Get the mapping from enum strings to values.
29
+ enum = subject.class.send(name.to_s.pluralize)
30
+ # Get the value of the attribute as an integer.
31
+ attribute = enum[subject.send(name)]
32
+ # Check to see if the value matches the condition.
33
+ value.is_a?(Enumerable) ?
34
+ (value.include? attribute) :
35
+ attribute == value
36
+ end
37
+
38
+ # Rails 4.2 deprecates `sanitize_sql_hash_for_conditions`
39
+ def sanitize_sql(conditions)
40
+ if ActiveRecord::VERSION::MINOR >= 2 && Hash === conditions
41
+ table = Arel::Table.new(@model_class.send(:table_name))
42
+
43
+ conditions = ActiveRecord::PredicateBuilder.resolve_column_aliases @model_class, conditions
44
+ conditions = @model_class.send(:expand_hash_conditions_for_aggregates, conditions)
45
+
46
+ ActiveRecord::PredicateBuilder.build_from_hash(@model_class, conditions, table).map { |b|
47
+ @model_class.send(:connection).visitor.compile b
48
+ }.join(' AND ')
49
+ else
50
+ @model_class.send(:sanitize_sql, conditions)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,41 +1,6 @@
1
1
  module CanCan
2
2
  module ModelAdapters
3
- class ActiveRecordAdapter < AbstractAdapter
4
- def self.for_class?(model_class)
5
- model_class <= ActiveRecord::Base
6
- end
7
-
8
- def self.override_condition_matching?(subject, name, value)
9
- name.kind_of?(MetaWhere::Column) if defined? MetaWhere
10
- end
11
-
12
- def self.matches_condition?(subject, name, value)
13
- subject_value = subject.send(name.column)
14
- if name.method.to_s.ends_with? "_any"
15
- value.any? { |v| meta_where_match? subject_value, name.method.to_s.sub("_any", ""), v }
16
- elsif name.method.to_s.ends_with? "_all"
17
- value.all? { |v| meta_where_match? subject_value, name.method.to_s.sub("_all", ""), v }
18
- else
19
- meta_where_match? subject_value, name.method, value
20
- end
21
- end
22
-
23
- def self.meta_where_match?(subject_value, method, value)
24
- case method.to_sym
25
- when :eq then subject_value == value
26
- when :not_eq then subject_value != value
27
- when :in then value.include?(subject_value)
28
- when :not_in then !value.include?(subject_value)
29
- when :lt then subject_value < value
30
- when :lteq then subject_value <= value
31
- when :gt then subject_value > value
32
- when :gteq then subject_value >= value
33
- when :matches then subject_value =~ Regexp.new("^" + Regexp.escape(value).gsub("%", ".*") + "$", true)
34
- when :does_not_match then !meta_where_match?(subject_value, :matches, value)
35
- else raise NotImplemented, "The #{method} MetaWhere condition is not supported."
36
- end
37
- end
38
-
3
+ module ActiveRecordAdapter
39
4
  # Returns conditions intended to be used inside a database query. Normally you will not call this
40
5
  # method directly, but instead go through ModelAdditions#accessible_by.
41
6
  #
@@ -67,7 +32,7 @@ module CanCan
67
32
  conditions.inject({}) do |result_hash, (name, value)|
68
33
  if value.kind_of? Hash
69
34
  value = value.dup
70
- association_class = model_class.reflect_on_association(name).class_name.constantize
35
+ association_class = model_class.reflect_on_association(name).klass.name.constantize
71
36
  nested = value.inject({}) do |nested,(k,v)|
72
37
  if v.kind_of? Hash
73
38
  value.delete(k)
@@ -99,11 +64,10 @@ module CanCan
99
64
  if override_scope
100
65
  @model_class.where(nil).merge(override_scope)
101
66
  elsif @model_class.respond_to?(:where) && @model_class.respond_to?(:joins)
102
- mergeable_conditions = @rules.select {|rule| rule.unmergeable? }.blank?
103
- if mergeable_conditions
104
- @model_class.where(conditions).includes(joins)
67
+ if mergeable_conditions?
68
+ build_relation(conditions)
105
69
  else
106
- @model_class.where(*(@rules.map(&:conditions))).includes(joins)
70
+ build_relation(*(@rules.map(&:conditions)))
107
71
  end
108
72
  else
109
73
  @model_class.all(:conditions => conditions, :joins => joins)
@@ -112,6 +76,10 @@ module CanCan
112
76
 
113
77
  private
114
78
 
79
+ def mergeable_conditions?
80
+ @rules.find {|rule| rule.unmergeable? }.blank?
81
+ end
82
+
115
83
  def override_scope
116
84
  conditions = @rules.map(&:conditions).compact
117
85
  if defined?(ActiveRecord::Relation) && conditions.any? { |c| c.kind_of?(ActiveRecord::Relation) }
@@ -35,15 +35,36 @@ module CanCan
35
35
 
36
36
  rules.inject(@model_class.all) do |records, rule|
37
37
  if process_can_rules && rule.base_behavior
38
- records.or rule.conditions
38
+ records.or simplify_relations(@model_class, rule.conditions)
39
39
  elsif !rule.base_behavior
40
- records.excludes rule.conditions
40
+ records.excludes simplify_relations(@model_class, rule.conditions)
41
41
  else
42
42
  records
43
43
  end
44
44
  end
45
45
  end
46
46
  end
47
+
48
+ private
49
+ # Look for criteria on relations and replace with simple id queries
50
+ # eg.
51
+ # {user: {:tags.all => []}} becomes {"user_id" => {"$in" => [__, ..]}}
52
+ # {user: {:session => {:tags.all => []}}} becomes {"user_id" => {"session_id" => {"$in" => [__, ..]} }}
53
+ def simplify_relations model_class, conditions
54
+ model_relations = model_class.relations.with_indifferent_access
55
+ Hash[
56
+ conditions.map {|k,v|
57
+ if relation = model_relations[k]
58
+ relation_class_name = relation[:class_name].blank? ? k.to_s.classify : relation[:class_name]
59
+ v = simplify_relations(relation_class_name.constantize, v)
60
+ relation_ids = relation_class_name.constantize.where(v).only(:id).map(&:id)
61
+ k = "#{k}_id"
62
+ v = { "$in" => relation_ids }
63
+ end
64
+ [k,v]
65
+ }
66
+ ]
67
+ end
47
68
  end
48
69
  end
49
70
  end
data/lib/cancan/rule.rb CHANGED
@@ -91,7 +91,11 @@ module CanCan
91
91
  end
92
92
 
93
93
  def matches_subject_class?(subject)
94
- @subjects.any? { |sub| sub.kind_of?(Module) && (subject.kind_of?(sub) || subject.class.to_s == sub.to_s || subject.kind_of?(Module) && subject.ancestors.include?(sub)) }
94
+ @subjects.any? do |sub|
95
+ sub.kind_of?(Module) && (subject.kind_of?(sub) ||
96
+ subject.class.to_s == sub.to_s ||
97
+ (subject.kind_of?(Module) && subject.ancestors.include?(sub)))
98
+ end
95
99
  end
96
100
 
97
101
  # Checks if the given subject matches the given conditions hash.
@@ -108,15 +112,15 @@ module CanCan
108
112
 
109
113
  conditions.all? do |name, value|
110
114
  if adapter.override_condition_matching?(subject, name, value)
111
- return adapter.matches_condition?(subject, name, value)
115
+ adapter.matches_condition?(subject, name, value)
116
+ else
117
+ condition_match?(subject.send(name), value)
112
118
  end
113
-
114
- condition_match?(subject.send(name), value)
115
119
  end
116
120
  end
117
121
 
118
122
  def nested_subject_matches_conditions?(subject_hash)
119
- parent, child = subject_hash.first
123
+ parent, _child = subject_hash.first
120
124
  matches_conditions_hash?(parent, @conditions[parent.class.name.downcase.to_sym] || {})
121
125
  end
122
126
 
@@ -136,6 +140,7 @@ module CanCan
136
140
  case value
137
141
  when Hash then hash_condition_match?(attribute, value)
138
142
  when String then attribute == value
143
+ when Range then value.cover?(attribute)
139
144
  when Enumerable then value.include?(attribute)
140
145
  else attribute == value
141
146
  end
@@ -1,3 +1,3 @@
1
1
  module CanCan
2
- VERSION = "1.8.0"
2
+ VERSION = "1.14.0"
3
3
  end
data/lib/cancan.rb CHANGED
@@ -9,7 +9,16 @@ require 'cancan/inherited_resource'
9
9
 
10
10
  require 'cancan/model_adapters/abstract_adapter'
11
11
  require 'cancan/model_adapters/default_adapter'
12
- require 'cancan/model_adapters/active_record_adapter' if defined? ActiveRecord
13
- require 'cancan/model_adapters/data_mapper_adapter' if defined? DataMapper
12
+
13
+ if defined? ActiveRecord
14
+ require 'cancan/model_adapters/active_record_adapter'
15
+ if ActiveRecord.respond_to?(:version) &&
16
+ ActiveRecord.version >= Gem::Version.new("4")
17
+ require 'cancan/model_adapters/active_record_4_adapter'
18
+ else
19
+ require 'cancan/model_adapters/active_record_3_adapter'
20
+ end
21
+ end
22
+
14
23
  require 'cancan/model_adapters/mongoid_adapter' if defined?(Mongoid) && defined?(Mongoid::Document)
15
24
  require 'cancan/model_adapters/sequel_adapter' if defined? Sequel
@@ -27,6 +27,6 @@ class Ability
27
27
  # can :update, Article, :published => true
28
28
  #
29
29
  # See the wiki for details:
30
- # https://github.com/bryanrite/cancancan/wiki/Defining-Abilities
30
+ # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities
31
31
  end
32
32
  end
data/spec/README.rdoc CHANGED
@@ -6,7 +6,7 @@ To run the specs first run the +bundle+ command to install the necessary gems an
6
6
 
7
7
  bundle
8
8
 
9
- Then run the appraisal command to install all the necssary test sets:
9
+ Then run the appraisal command to install all the necessary test sets:
10
10
 
11
11
  appraisal install
12
12
 
@@ -16,7 +16,7 @@ You can then run all test sets:
16
16
 
17
17
  Or individual ones:
18
18
 
19
- appraisal rails_3.0 rake
19
+ appraisal activerecord_3.2 rake
20
20
 
21
21
  A list of the tests is in the +Appraisal+ file.
22
22