cancancan 2.2.0 → 2.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd789707449c90277a31d5808e55c36140b88b3aa3c2b9035640d777220b2d27
4
- data.tar.gz: 64fbbaa5657ae8b334e7096f33eb12b975a224ea7c516c0091d3d7ac94c0acbd
3
+ metadata.gz: f63f1d6ab266068caab6e7baf2b628ae72a7e03a134116eda91473861b9df359
4
+ data.tar.gz: fbf6337f058ece9a2f01c990a55398489e0ea56888f0a5ce3b9f8d5e203c31ae
5
5
  SHA512:
6
- metadata.gz: 70ba8098042c7114a7cf419a892c12794089da88046873fb6abf1c45dac3854731adb860ff95d2556f7e9ffad4543b5f53dc05579defd5ff8273bcf51476e0ce
7
- data.tar.gz: 5d390ebd7fa75ffbff3783502d6bf329d5949787b70ccd26559d40be4471c61efb8f4ea4adc2d53b6bac81f9e32e619d3569c268ff8a6277f16cab72ea162bf5
6
+ metadata.gz: 24fcc98ce0592b263add65cf0bc75a7020d75777fea7c6902216f97bbc9c13a36b4d379194a142cd8a5e5a24dc1ae82c82a61d6d99143c30a9afe11133048528
7
+ data.tar.gz: 8873b440698a941314f67a748db86c2abe33e89417ae54d9f35769c29f904edc9eff5736d0bf55d53badc494b9bca9e0ac1761c1920067238628d23dc8e0c643
@@ -8,8 +8,10 @@ require 'cancan/exceptions'
8
8
 
9
9
  require 'cancan/model_adapters/abstract_adapter'
10
10
  require 'cancan/model_adapters/default_adapter'
11
+ require 'cancan/rules_compressor'
11
12
 
12
13
  if defined? ActiveRecord
14
+ require 'cancan/model_adapters/conditions_extractor'
13
15
  require 'cancan/model_adapters/active_record_adapter'
14
16
  require 'cancan/model_adapters/active_record_4_adapter'
15
17
  require 'cancan/model_adapters/active_record_5_adapter'
@@ -384,6 +384,8 @@ module CanCan
384
384
  end
385
385
  end
386
386
 
387
- ActiveSupport.on_load(:action_controller) do
388
- include CanCan::ControllerAdditions
387
+ if defined? ActiveSupport
388
+ ActiveSupport.on_load(:action_controller) do
389
+ include CanCan::ControllerAdditions
390
+ end
389
391
  end
@@ -11,6 +11,9 @@ module CanCan
11
11
  # Raised when using check_authorization without calling authorized!
12
12
  class AuthorizationNotPerformed < Error; end
13
13
 
14
+ # Raised when using a wrong association name
15
+ class WrongAssociationName < Error; end
16
+
14
17
  # This error is raised when a user isn't allowed to access a given controller action.
15
18
  # This usually happens within a call to ControllerAdditions#authorize! but can be
16
19
  # raised manually.
@@ -1,4 +1,6 @@
1
1
  require_relative 'can_can/model_adapters/active_record_adapter/joins.rb'
2
+ require_relative 'conditions_extractor.rb'
3
+ require 'cancan/rules_compressor'
2
4
  module CanCan
3
5
  module ModelAdapters
4
6
  module ActiveRecordAdapter
@@ -20,47 +22,19 @@ module CanCan
20
22
  # query(:manage, User).conditions # => "not (self_managed = 't') AND ((manager_id = 1) OR (id = 1))"
21
23
  #
22
24
  def conditions
23
- if @rules.size == 1 && @rules.first.base_behavior
25
+ compressed_rules = RulesCompressor.new(@rules.reverse).rules_collapsed.reverse
26
+ conditions_extractor = ConditionsExtractor.new(@model_class)
27
+ if compressed_rules.size == 1 && compressed_rules.first.base_behavior
24
28
  # Return the conditions directly if there's just one definition
25
- tableized_conditions(@rules.first.conditions).dup
29
+ conditions_extractor.tableize_conditions(compressed_rules.first.conditions).dup
26
30
  else
27
- extract_multiple_conditions
31
+ extract_multiple_conditions(conditions_extractor, compressed_rules)
28
32
  end
29
33
  end
30
34
 
31
- def extract_multiple_conditions
32
- @rules.reverse.inject(false_sql) do |sql, rule|
33
- merge_conditions(sql, tableized_conditions(rule.conditions).dup, rule.base_behavior)
34
- end
35
- end
36
-
37
- def tableized_conditions(conditions, model_class = @model_class)
38
- return conditions unless conditions.is_a? Hash
39
- conditions.each_with_object({}) do |(name, value), result_hash|
40
- calculate_result_hash(model_class, name, result_hash, value)
41
- end
42
- end
43
-
44
- def calculate_result_hash(model_class, name, result_hash, value)
45
- if value.is_a? Hash
46
- association_class = model_class.reflect_on_association(name).klass.name.constantize
47
- nested_resulted = calculate_nested(model_class, name, result_hash, value.dup)
48
- result_hash.merge!(tableized_conditions(nested_resulted, association_class))
49
- else
50
- result_hash[name] = value
51
- end
52
- result_hash
53
- end
54
-
55
- def calculate_nested(model_class, name, result_hash, value)
56
- value.each_with_object({}) do |(k, v), nested|
57
- if v.is_a? Hash
58
- value.delete(k)
59
- nested[k] = v
60
- else
61
- result_hash[model_class.reflect_on_association(name).table_name.to_sym] = value
62
- end
63
- nested
35
+ def extract_multiple_conditions(conditions_extractor, rules)
36
+ rules.reverse.inject(false_sql) do |sql, rule|
37
+ merge_conditions(sql, conditions_extractor.tableize_conditions(rule.conditions).dup, rule.base_behavior)
64
38
  end
65
39
  end
66
40
 
@@ -6,7 +6,7 @@ module CanCan
6
6
  # See ModelAdditions#accessible_by
7
7
  def joins
8
8
  joins_hash = {}
9
- @rules.each do |rule|
9
+ @rules.reverse.each do |rule|
10
10
  merge_joins(joins_hash, rule.associations_hash)
11
11
  end
12
12
  clean_joins(joins_hash) unless joins_hash.empty?
@@ -0,0 +1,75 @@
1
+ # this class is responsible of converting the hash of conditions
2
+ # in "where conditions" to generate the sql query
3
+ # it consists of a names_cache that helps calculating the next name given to the association
4
+ # it tries to reflect the bahavior of ActiveRecord when generating aliases for tables.
5
+ module CanCan
6
+ module ModelAdapters
7
+ class ConditionsExtractor
8
+ def initialize(model_class)
9
+ @names_cache = { model_class.table_name => [] }.with_indifferent_access
10
+ @root_model_class = model_class
11
+ end
12
+
13
+ def tableize_conditions(conditions, model_class = @root_model_class, path_to_key = 0)
14
+ return conditions unless conditions.is_a? Hash
15
+ conditions.each_with_object({}) do |(key, value), result_hash|
16
+ if value.is_a? Hash
17
+ result_hash.merge!(calculate_result_hash(key, model_class, path_to_key, result_hash, value))
18
+ else
19
+ result_hash[key] = value
20
+ end
21
+ result_hash
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def calculate_result_hash(key, model_class, path_to_key, result_hash, value)
28
+ reflection = model_class.reflect_on_association(key)
29
+ unless reflection
30
+ raise WrongAssociationName, "association #{key} not defined in model #{model_class.name}"
31
+ end
32
+ nested_resulted = calculate_nested(model_class, result_hash, key, value.dup, path_to_key)
33
+ association_class = reflection.klass.name.constantize
34
+ tableize_conditions(nested_resulted, association_class, "#{path_to_key}_#{key}")
35
+ end
36
+
37
+ def calculate_nested(model_class, result_hash, relation_name, value, path_to_key)
38
+ value.each_with_object({}) do |(k, v), nested|
39
+ if v.is_a? Hash
40
+ value.delete(k)
41
+ nested[k] = v
42
+ else
43
+ table_alias = generate_table_alias(model_class, relation_name, path_to_key)
44
+ result_hash[table_alias] = value
45
+ end
46
+ nested
47
+ end
48
+ end
49
+
50
+ def generate_table_alias(model_class, relation_name, path_to_key)
51
+ table_alias = model_class.reflect_on_association(relation_name).table_name.to_sym
52
+
53
+ if alredy_used?(table_alias, relation_name, path_to_key)
54
+ table_alias = "#{relation_name.to_s.pluralize}_#{model_class.table_name}".to_sym
55
+
56
+ index = 1
57
+ while alredy_used?(table_alias, relation_name, path_to_key)
58
+ table_alias = "#{table_alias}_#{index += 1}".to_sym
59
+ end
60
+ end
61
+ add_to_cache(table_alias, relation_name, path_to_key)
62
+ end
63
+
64
+ def alredy_used?(table_alias, relation_name, path_to_key)
65
+ @names_cache[table_alias].try(:exclude?, "#{path_to_key}_#{relation_name}")
66
+ end
67
+
68
+ def add_to_cache(table_alias, relation_name, path_to_key)
69
+ @names_cache[table_alias] ||= []
70
+ @names_cache[table_alias] << "#{path_to_key}_#{relation_name}"
71
+ table_alias
72
+ end
73
+ end
74
+ end
75
+ end
@@ -24,6 +24,18 @@ module CanCan
24
24
  @block = block
25
25
  end
26
26
 
27
+ def can_rule?
28
+ base_behavior
29
+ end
30
+
31
+ def cannot_catch_all?
32
+ !can_rule? && catch_all?
33
+ end
34
+
35
+ def catch_all?
36
+ [nil, false, [], {}, '', ' '].include? @conditions
37
+ end
38
+
27
39
  # Matches both the subject and action, not necessarily the conditions
28
40
  def relevant?(action, subject)
29
41
  subject = subject.values.first if subject.class == Hash
@@ -0,0 +1,20 @@
1
+ require_relative 'conditions_matcher.rb'
2
+ module CanCan
3
+ class RulesCompressor
4
+ attr_reader :initial_rules, :rules_collapsed
5
+
6
+ def initialize(rules)
7
+ @initial_rules = rules
8
+ @rules_collapsed = compress(@initial_rules)
9
+ end
10
+
11
+ def compress(array)
12
+ idx = array.rindex(&:catch_all?)
13
+ return array unless idx
14
+ value = array[idx]
15
+ array[idx..-1]
16
+ .drop_while { |n| n.base_behavior == value.base_behavior }
17
+ .tap { |a| a.unshift(value) unless value.cannot_catch_all? }
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module CanCan
2
- VERSION = '2.2.0'.freeze
2
+ VERSION = '2.3.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cancancan
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Rodi (Renuo AG)
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2018-04-15 00:00:00.000000000 Z
14
+ date: 2018-09-16 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -129,9 +129,11 @@ files:
129
129
  - lib/cancan/model_adapters/active_record_5_adapter.rb
130
130
  - lib/cancan/model_adapters/active_record_adapter.rb
131
131
  - lib/cancan/model_adapters/can_can/model_adapters/active_record_adapter/joins.rb
132
+ - lib/cancan/model_adapters/conditions_extractor.rb
132
133
  - lib/cancan/model_adapters/default_adapter.rb
133
134
  - lib/cancan/model_additions.rb
134
135
  - lib/cancan/rule.rb
136
+ - lib/cancan/rules_compressor.rb
135
137
  - lib/cancan/version.rb
136
138
  - lib/cancancan.rb
137
139
  - lib/generators/cancan/ability/USAGE