cancancan 3.3.0 → 3.4.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: c4498ac94e1994faa4da80dc957d8c9564433d991048774f9ac2f051e60de580
4
- data.tar.gz: 74209123c4c49adcd1d2d81df1de61c5f8cc2f243fdcdb4d01d3e41731e4c266
3
+ metadata.gz: fc128fc4996aed8edaee7dcce7178ec47d06c63591bf03c7cb98dd3deec5c213
4
+ data.tar.gz: 4e3ee983874f8fbeb3f566c6e12aaba275990581e77938b73d0349ec1e867f6e
5
5
  SHA512:
6
- metadata.gz: eb7774650d12a7073d09bb713f7eedfd7d376689f6d6bb842620b325a814720172f4fec900705bcc0dd3bd90818a88d2ab7e3904ea3a5d004533e13b4bed1c4c
7
- data.tar.gz: a7a6fff07fbd7d52816dd960d00ae0baea7d50c5ed41d0abf32ac3a0c2b6bc3c557a4309944717b57aad785ad7dc394870c079b6a820b62798a373a9d9f8c2b0
6
+ metadata.gz: 4511dd58be6c2a2ce4bc28b382ece40ad367a1cf72f56d521fe2f38ca038eb6cb11a249a1d029346038cfec0e1828acadddf8ff9be14d6d59e5228b165811d49
7
+ data.tar.gz: 8d24834c3362f708a9979079eee089537975aefa25144a65eea006c137a75297f1341b6df24877c0c517b0e1c33e935a7c4d82bbedcca84e00100d0806812ff5
data/cancancan.gemspec CHANGED
@@ -25,5 +25,5 @@ Gem::Specification.new do |s|
25
25
  s.add_development_dependency 'bundler', '~> 2.0'
26
26
  s.add_development_dependency 'rake', '~> 10.1', '>= 10.1.1'
27
27
  s.add_development_dependency 'rspec', '~> 3.2', '>= 3.2.0'
28
- s.add_development_dependency 'rubocop', '~> 0.63.1'
28
+ s.add_development_dependency 'rubocop', '~> 1.26'
29
29
  end
@@ -1,3 +1,5 @@
1
+ require_relative 'sti_detector'
2
+
1
3
  # This class is responsible for matching classes and their subclasses as well as
2
4
  # upmatching classes to their ancestors.
3
5
  # This is used to generate sti connections
@@ -12,6 +14,8 @@ class SubjectClassMatcher
12
14
  def self.matching_class_check(subject, sub, has_subclasses)
13
15
  matches = matches_class_or_is_related(subject, sub)
14
16
  if has_subclasses
17
+ return matches unless StiDetector.sti_class?(sub)
18
+
15
19
  matches || subject.subclasses.include?(sub)
16
20
  else
17
21
  matches
@@ -33,16 +33,24 @@ module CanCan
33
33
  end
34
34
 
35
35
  def nested_subject_matches_conditions?(subject_hash)
36
- parent, _child = subject_hash.first
37
- matches_conditions_hash?(parent, @conditions[parent.class.name.downcase.to_sym] || {})
36
+ parent, child = subject_hash.first
37
+
38
+ matches_base_parent_conditions = matches_conditions_hash?(parent,
39
+ @conditions[parent.class.name.downcase.to_sym] || {})
40
+
41
+ adapter = model_adapter(parent)
42
+
43
+ matches_base_parent_conditions &&
44
+ (!adapter.override_nested_subject_conditions_matching?(parent, child, @conditions) ||
45
+ adapter.nested_subject_matches_conditions?(parent, child, @conditions))
38
46
  end
39
47
 
40
48
  # Checks if the given subject matches the given conditions hash.
41
- # This behavior can be overriden by a model adapter by defining two class methods:
49
+ # This behavior can be overridden by a model adapter by defining two class methods:
42
50
  # override_matching_for_conditions?(subject, conditions) and
43
51
  # matches_conditions_hash?(subject, conditions)
44
52
  def matches_conditions_hash?(subject, conditions = @conditions)
45
- return true if conditions.empty?
53
+ return true if conditions.is_a?(Hash) && conditions.empty?
46
54
 
47
55
  adapter = model_adapter(subject)
48
56
 
@@ -50,10 +58,21 @@ module CanCan
50
58
  return adapter.matches_conditions_hash?(subject, conditions)
51
59
  end
52
60
 
53
- matches_all_conditions?(adapter, conditions, subject)
61
+ matches_all_conditions?(adapter, subject, conditions)
62
+ end
63
+
64
+ def matches_all_conditions?(adapter, subject, conditions)
65
+ if conditions.is_a?(Hash)
66
+ matches_hash_conditions(adapter, subject, conditions)
67
+ elsif conditions.respond_to?(:include?)
68
+ conditions.include?(subject)
69
+ else
70
+ puts "does #{subject} match #{conditions}?"
71
+ subject == conditions
72
+ end
54
73
  end
55
74
 
56
- def matches_all_conditions?(adapter, conditions, subject)
75
+ def matches_hash_conditions(adapter, subject, conditions)
57
76
  conditions.all? do |name, value|
58
77
  if adapter.override_condition_matching?(subject, name, value)
59
78
  adapter.matches_condition?(subject, name, value)
data/lib/cancan/config.rb CHANGED
@@ -3,7 +3,11 @@
3
3
  module CanCan
4
4
  def self.valid_accessible_by_strategies
5
5
  strategies = [:left_join]
6
- strategies << :subquery unless does_not_support_subquery_strategy?
6
+
7
+ unless does_not_support_subquery_strategy?
8
+ strategies.push(:joined_alias_exists_subquery, :joined_alias_each_rule_as_exists_subquery, :subquery)
9
+ end
10
+
7
11
  strategies
8
12
  end
9
13
 
@@ -54,7 +54,7 @@ module CanCan
54
54
 
55
55
  protected
56
56
 
57
- # Returns the class used for this resource. This can be overriden by the :class option.
57
+ # Returns the class used for this resource. This can be overridden by the :class option.
58
58
  # If +false+ is passed in it will use the resource name as a symbol in which case it should
59
59
  # only be used for authorization, not loading since there's no class to load through.
60
60
  def resource_class
@@ -13,9 +13,11 @@ Kernel.const_get(rspec_module)::Matchers.define :be_able_to do |*args|
13
13
  match do |ability|
14
14
  actions = args.first
15
15
  if actions.is_a? Array
16
- break false if actions.empty?
17
-
18
- actions.all? { |action| ability.can?(action, *args[1..-1]) }
16
+ if actions.empty?
17
+ false
18
+ else
19
+ actions.all? { |action| ability.can?(action, *args[1..-1]) }
20
+ end
19
21
  else
20
22
  ability.can?(*args)
21
23
  end
@@ -3,6 +3,8 @@
3
3
  module CanCan
4
4
  module ModelAdapters
5
5
  class AbstractAdapter
6
+ attr_reader :model_class
7
+
6
8
  def self.inherited(subclass)
7
9
  @subclasses ||= []
8
10
  @subclasses.insert(0, subclass)
@@ -33,6 +35,18 @@ module CanCan
33
35
  raise NotImplemented, 'This model adapter does not support matching on a conditions hash.'
34
36
  end
35
37
 
38
+ # Used above override_conditions_hash_matching to determine if this model adapter will override the
39
+ # matching behavior for nested subject.
40
+ # If this returns true then nested_subject_matches_conditions? will be called.
41
+ def self.override_nested_subject_conditions_matching?(_parent, _child, _all_conditions)
42
+ false
43
+ end
44
+
45
+ # Override if override_nested_subject_conditions_matching? returns true
46
+ def self.nested_subject_matches_conditions?(_parent, _child, _all_conditions)
47
+ raise NotImplemented, 'This model adapter does not support matching on a nested subject.'
48
+ end
49
+
36
50
  # Used to determine if this model adapter will override the matching behavior for a specific condition.
37
51
  # If this returns true then matches_condition? will be called. See Rule#matches_conditions_hash
38
52
  def self.override_condition_matching?(_subject, _name, _value)
@@ -22,16 +22,12 @@ module CanCan
22
22
  private
23
23
 
24
24
  def build_joins_relation(relation, *where_conditions)
25
- case CanCan.accessible_by_strategy
26
- when :subquery
27
- inner = @model_class.unscoped do
28
- @model_class.left_joins(joins).where(*where_conditions)
29
- end
30
- @model_class.where(@model_class.primary_key => inner)
25
+ strategy_class.new(adapter: self, relation: relation, where_conditions: where_conditions).execute!
26
+ end
31
27
 
32
- when :left_join
33
- relation.left_joins(joins).distinct
34
- end
28
+ def strategy_class
29
+ strategy_class_name = CanCan.accessible_by_strategy.to_s.camelize
30
+ CanCan::ModelAdapters::Strategies.const_get(strategy_class_name)
35
31
  end
36
32
 
37
33
  def sanitize_sql(conditions)
@@ -11,6 +11,8 @@ module CanCan
11
11
  Gem::Version.new(ActiveRecord.version).release < Gem::Version.new(version)
12
12
  end
13
13
 
14
+ attr_reader :compressed_rules
15
+
14
16
  def initialize(model_class, rules)
15
17
  super
16
18
  @compressed_rules = RulesCompressor.new(@rules.reverse).rules_collapsed.reverse
@@ -18,6 +20,31 @@ module CanCan
18
20
  ConditionsNormalizer.normalize(model_class, @compressed_rules)
19
21
  end
20
22
 
23
+ class << self
24
+ # When belongs_to parent_id is a condition for a model,
25
+ # we want to check the parent when testing ability for a hash {parent => model}
26
+ def override_nested_subject_conditions_matching?(parent, child, all_conditions)
27
+ parent_child_conditions(parent, child, all_conditions).present?
28
+ end
29
+
30
+ # parent_id condition can be an array of integer or one integer, we check the parent against this
31
+ def nested_subject_matches_conditions?(parent, child, all_conditions)
32
+ id_condition = parent_child_conditions(parent, child, all_conditions)
33
+ return id_condition.include?(parent.id) if id_condition.is_a? Array
34
+ return id_condition == parent.id if id_condition.is_a? Integer
35
+
36
+ false
37
+ end
38
+
39
+ def parent_child_conditions(parent, child, all_conditions)
40
+ child_class = child.is_a?(Class) ? child : child.class
41
+ foreign_key = child_class.reflect_on_all_associations(:belongs_to).find do |association|
42
+ association.klass == parent.class
43
+ end&.foreign_key&.to_sym
44
+ foreign_key.nil? ? nil : all_conditions[foreign_key]
45
+ end
46
+ end
47
+
21
48
  # Returns conditions intended to be used inside a database query. Normally you will not call this
22
49
  # method directly, but instead go through ModelAdditions#accessible_by.
23
50
  #
@@ -3,7 +3,7 @@
3
3
  # this class is responsible of converting the hash of conditions
4
4
  # in "where conditions" to generate the sql query
5
5
  # it consists of a names_cache that helps calculating the next name given to the association
6
- # it tries to reflect the bahavior of ActiveRecord when generating aliases for tables.
6
+ # it tries to reflect the behavior of ActiveRecord when generating aliases for tables.
7
7
  module CanCan
8
8
  module ModelAdapters
9
9
  class ConditionsExtractor
@@ -50,18 +50,18 @@ module CanCan
50
50
  def generate_table_alias(model_class, relation_name, path_to_key)
51
51
  table_alias = model_class.reflect_on_association(relation_name).table_name.to_sym
52
52
 
53
- if alredy_used?(table_alias, relation_name, path_to_key)
53
+ if already_used?(table_alias, relation_name, path_to_key)
54
54
  table_alias = "#{relation_name.to_s.pluralize}_#{model_class.table_name}".to_sym
55
55
 
56
56
  index = 1
57
- while alredy_used?(table_alias, relation_name, path_to_key)
57
+ while already_used?(table_alias, relation_name, path_to_key)
58
58
  table_alias = "#{table_alias}_#{index += 1}".to_sym
59
59
  end
60
60
  end
61
61
  add_to_cache(table_alias, relation_name, path_to_key)
62
62
  end
63
63
 
64
- def alredy_used?(table_alias, relation_name, path_to_key)
64
+ def already_used?(table_alias, relation_name, path_to_key)
65
65
  @names_cache[table_alias].try(:exclude?, "#{path_to_key}_#{relation_name}")
66
66
  end
67
67
 
@@ -1,6 +1,6 @@
1
1
  # this class is responsible of normalizing the hash of conditions
2
2
  # by exploding has_many through associations
3
- # when a condition is defined with an has_many thorugh association this is exploded in all its parts
3
+ # when a condition is defined with an has_many through association this is exploded in all its parts
4
4
  # TODO: it could identify STI and normalize it
5
5
  module CanCan
6
6
  module ModelAdapters
@@ -1,3 +1,5 @@
1
+ require_relative '../sti_detector'
2
+
1
3
  # this class is responsible for detecting sti classes and creating new rules for the
2
4
  # relevant subclasses, using the inheritance_column as a merger
3
5
  module CanCan
@@ -20,9 +22,7 @@ module CanCan
20
22
  private
21
23
 
22
24
  def update_rule(subject, rule, rules_cache)
23
- return false unless subject.respond_to?(:descends_from_active_record?)
24
- return false if subject == :all || subject.descends_from_active_record?
25
- return false unless subject < ActiveRecord::Base
25
+ return false unless StiDetector.sti_class?(subject)
26
26
 
27
27
  rules_cache.push(build_rule_for_subclass(rule, subject))
28
28
  true
@@ -31,7 +31,7 @@ module CanCan
31
31
  # create a new rule for the subclasses that links on the inheritance_column
32
32
  def build_rule_for_subclass(rule, subject)
33
33
  CanCan::Rule.new(rule.base_behavior, rule.actions, subject.superclass,
34
- rule.conditions.merge(subject.inheritance_column => subject.name), rule.block)
34
+ rule.conditions.merge(subject.inheritance_column => subject.sti_name), rule.block)
35
35
  end
36
36
  end
37
37
  end
@@ -0,0 +1,40 @@
1
+ module CanCan
2
+ module ModelAdapters
3
+ class Strategies
4
+ class Base
5
+ attr_reader :adapter, :relation, :where_conditions
6
+
7
+ delegate(
8
+ :compressed_rules,
9
+ :extract_multiple_conditions,
10
+ :joins,
11
+ :model_class,
12
+ :quoted_primary_key,
13
+ :quoted_aliased_table_name,
14
+ :quoted_table_name,
15
+ to: :adapter
16
+ )
17
+ delegate :connection, :quoted_primary_key, to: :model_class
18
+ delegate :quote_table_name, to: :connection
19
+
20
+ def initialize(adapter:, relation:, where_conditions:)
21
+ @adapter = adapter
22
+ @relation = relation
23
+ @where_conditions = where_conditions
24
+ end
25
+
26
+ def aliased_table_name
27
+ @aliased_table_name ||= "#{model_class.table_name}_alias"
28
+ end
29
+
30
+ def quoted_aliased_table_name
31
+ @quoted_aliased_table_name ||= quote_table_name(aliased_table_name)
32
+ end
33
+
34
+ def quoted_table_name
35
+ @quoted_table_name ||= quote_table_name(model_class.table_name)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: false
2
+
3
+ module CanCan
4
+ module ModelAdapters
5
+ class Strategies
6
+ class JoinedAliasEachRuleAsExistsSubquery < Base
7
+ def execute!
8
+ model_class
9
+ .joins(
10
+ "JOIN #{quoted_table_name} AS #{quoted_aliased_table_name} ON " \
11
+ "#{quoted_aliased_table_name}.#{quoted_primary_key} = #{quoted_table_name}.#{quoted_primary_key}"
12
+ )
13
+ .where(double_exists_sql)
14
+ end
15
+
16
+ def double_exists_sql
17
+ double_exists_sql = ''
18
+
19
+ compressed_rules.each_with_index do |rule, index|
20
+ double_exists_sql << ' OR ' if index.positive?
21
+ double_exists_sql << "EXISTS (#{sub_query_for_rule(rule).to_sql})"
22
+ end
23
+
24
+ double_exists_sql
25
+ end
26
+
27
+ def sub_query_for_rule(rule)
28
+ conditions_extractor = ConditionsExtractor.new(model_class)
29
+ rule_where_conditions = extract_multiple_conditions(conditions_extractor, [rule])
30
+ joins_hash, left_joins_hash = extract_joins_from_rule(rule)
31
+ sub_query_for_rules_and_join_hashes(rule_where_conditions, joins_hash, left_joins_hash)
32
+ end
33
+
34
+ def sub_query_for_rules_and_join_hashes(rule_where_conditions, joins_hash, left_joins_hash)
35
+ model_class
36
+ .select('1')
37
+ .joins(joins_hash)
38
+ .left_joins(left_joins_hash)
39
+ .where(
40
+ "#{quoted_table_name}.#{quoted_primary_key} = " \
41
+ "#{quoted_aliased_table_name}.#{quoted_primary_key}"
42
+ )
43
+ .where(rule_where_conditions)
44
+ .limit(1)
45
+ end
46
+
47
+ def extract_joins_from_rule(rule)
48
+ joins = {}
49
+ left_joins = {}
50
+
51
+ extra_joins_recursive([], rule.conditions, joins, left_joins)
52
+ [joins, left_joins]
53
+ end
54
+
55
+ def extra_joins_recursive(current_path, conditions, joins, left_joins)
56
+ conditions.each do |key, value|
57
+ if value.is_a?(Hash)
58
+ current_path << key
59
+ extra_joins_recursive(current_path, value, joins, left_joins)
60
+ current_path.pop
61
+ else
62
+ extra_joins_recursive_merge_joins(current_path, value, joins, left_joins)
63
+ end
64
+ end
65
+ end
66
+
67
+ def extra_joins_recursive_merge_joins(current_path, value, joins, left_joins)
68
+ hash_joins = current_path_to_hash(current_path)
69
+
70
+ if value.nil?
71
+ left_joins.deep_merge!(hash_joins)
72
+ else
73
+ joins.deep_merge!(hash_joins)
74
+ end
75
+ end
76
+
77
+ # Converts an array like [:child, :grand_child] into a hash like {child: {grand_child: {}}
78
+ def current_path_to_hash(current_path)
79
+ hash_joins = {}
80
+ current_hash_joins = hash_joins
81
+
82
+ current_path.each do |path_part|
83
+ new_hash = {}
84
+ current_hash_joins[path_part] = new_hash
85
+ current_hash_joins = new_hash
86
+ end
87
+
88
+ hash_joins
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module ModelAdapters
5
+ class Strategies
6
+ class JoinedAliasExistsSubquery < Base
7
+ def execute!
8
+ model_class
9
+ .joins(
10
+ "JOIN #{quoted_table_name} AS #{quoted_aliased_table_name} ON " \
11
+ "#{quoted_aliased_table_name}.#{quoted_primary_key} = #{quoted_table_name}.#{quoted_primary_key}"
12
+ )
13
+ .where("EXISTS (#{joined_alias_exists_subquery_inner_query.to_sql})")
14
+ end
15
+
16
+ def joined_alias_exists_subquery_inner_query
17
+ model_class
18
+ .unscoped
19
+ .select('1')
20
+ .left_joins(joins)
21
+ .where(*where_conditions)
22
+ .where(
23
+ "#{quoted_table_name}.#{quoted_primary_key} = " \
24
+ "#{quoted_aliased_table_name}.#{quoted_primary_key}"
25
+ )
26
+ .limit(1)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ module CanCan
2
+ module ModelAdapters
3
+ class Strategies
4
+ class LeftJoin < Base
5
+ def execute!
6
+ relation.left_joins(joins).distinct
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ module CanCan
2
+ module ModelAdapters
3
+ class Strategies
4
+ class Subquery < Base
5
+ def execute!
6
+ build_joins_relation_subquery(where_conditions)
7
+ end
8
+
9
+ def build_joins_relation_subquery(where_conditions)
10
+ inner = model_class.unscoped do
11
+ model_class.left_joins(joins).where(*where_conditions)
12
+ end
13
+ model_class.where(model_class.primary_key => inner)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/cancan/rule.rb CHANGED
@@ -70,7 +70,7 @@ module CanCan
70
70
  end
71
71
 
72
72
  def with_scope?
73
- @conditions.is_a?(ActiveRecord::Relation)
73
+ defined?(ActiveRecord) && @conditions.is_a?(ActiveRecord::Relation)
74
74
  end
75
75
 
76
76
  def associations_hash(conditions = @conditions)
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StiDetector
4
+ def self.sti_class?(subject)
5
+ return false unless defined?(ActiveRecord::Base)
6
+ return false unless subject.respond_to?(:descends_from_active_record?)
7
+ return false if subject == :all || subject.descends_from_active_record?
8
+ return false unless subject < ActiveRecord::Base
9
+
10
+ true
11
+ end
12
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CanCan
4
- VERSION = '3.3.0'.freeze
4
+ VERSION = '3.4.0'.freeze
5
5
  end
data/lib/cancan.rb CHANGED
@@ -21,4 +21,9 @@ if defined? ActiveRecord
21
21
  require 'cancan/model_adapters/active_record_adapter'
22
22
  require 'cancan/model_adapters/active_record_4_adapter'
23
23
  require 'cancan/model_adapters/active_record_5_adapter'
24
+ require 'cancan/model_adapters/strategies/base'
25
+ require 'cancan/model_adapters/strategies/joined_alias_each_rule_as_exists_subquery'
26
+ require 'cancan/model_adapters/strategies/joined_alias_exists_subquery'
27
+ require 'cancan/model_adapters/strategies/left_join'
28
+ require 'cancan/model_adapters/strategies/subquery'
24
29
  end
@@ -4,14 +4,12 @@ class Ability
4
4
  include CanCan::Ability
5
5
 
6
6
  def initialize(user)
7
- # Define abilities for the passed in user here. For example:
7
+ # Define abilities for the user here. For example:
8
8
  #
9
- # user ||= User.new # guest user (not logged in)
10
- # if user.admin?
11
- # can :manage, :all
12
- # else
13
- # can :read, :all
14
- # end
9
+ # return unless user.present?
10
+ # can :read, :all
11
+ # return unless user.admin?
12
+ # can :manage, :all
15
13
  #
16
14
  # The first argument to `can` is the action you are giving the user
17
15
  # permission to do.
@@ -26,9 +24,9 @@ class Ability
26
24
  # objects.
27
25
  # For example, here the user can only update published articles.
28
26
  #
29
- # can :update, Article, :published => true
27
+ # can :update, Article, published: true
30
28
  #
31
29
  # See the wiki for details:
32
- # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities
30
+ # https://github.com/CanCanCommunity/cancancan/blob/develop/docs/define_check_abilities.md
33
31
  end
34
32
  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: 3.3.0
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Rodi (Renuo AG)
@@ -11,28 +11,28 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2021-06-21 00:00:00.000000000 Z
14
+ date: 2022-06-23 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: appraisal
18
18
  requirement: !ruby/object:Gem::Requirement
19
19
  requirements:
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 2.0.0
23
20
  - - "~>"
24
21
  - !ruby/object:Gem::Version
25
22
  version: '2.0'
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 2.0.0
26
26
  type: :development
27
27
  prerelease: false
28
28
  version_requirements: !ruby/object:Gem::Requirement
29
29
  requirements:
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 2.0.0
33
30
  - - "~>"
34
31
  - !ruby/object:Gem::Version
35
32
  version: '2.0'
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 2.0.0
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: bundler
38
38
  requirement: !ruby/object:Gem::Requirement
@@ -71,36 +71,36 @@ dependencies:
71
71
  name: rspec
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- version: 3.2.0
77
74
  - - "~>"
78
75
  - !ruby/object:Gem::Version
79
76
  version: '3.2'
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 3.2.0
80
80
  type: :development
81
81
  prerelease: false
82
82
  version_requirements: !ruby/object:Gem::Requirement
83
83
  requirements:
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- version: 3.2.0
87
84
  - - "~>"
88
85
  - !ruby/object:Gem::Version
89
86
  version: '3.2'
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 3.2.0
90
90
  - !ruby/object:Gem::Dependency
91
91
  name: rubocop
92
92
  requirement: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.63.1
96
+ version: '1.26'
97
97
  type: :development
98
98
  prerelease: false
99
99
  version_requirements: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 0.63.1
103
+ version: '1.26'
104
104
  description: Simple authorization solution for Rails. All permissions are stored in
105
105
  a single location.
106
106
  email: alessandro.rodi@renuo.ch
@@ -135,11 +135,17 @@ files:
135
135
  - lib/cancan/model_adapters/conditions_normalizer.rb
136
136
  - lib/cancan/model_adapters/default_adapter.rb
137
137
  - lib/cancan/model_adapters/sti_normalizer.rb
138
+ - lib/cancan/model_adapters/strategies/base.rb
139
+ - lib/cancan/model_adapters/strategies/joined_alias_each_rule_as_exists_subquery.rb
140
+ - lib/cancan/model_adapters/strategies/joined_alias_exists_subquery.rb
141
+ - lib/cancan/model_adapters/strategies/left_join.rb
142
+ - lib/cancan/model_adapters/strategies/subquery.rb
138
143
  - lib/cancan/model_additions.rb
139
144
  - lib/cancan/parameter_validators.rb
140
145
  - lib/cancan/relevant.rb
141
146
  - lib/cancan/rule.rb
142
147
  - lib/cancan/rules_compressor.rb
148
+ - lib/cancan/sti_detector.rb
143
149
  - lib/cancan/unauthorized_message_resolver.rb
144
150
  - lib/cancan/version.rb
145
151
  - lib/cancancan.rb
@@ -166,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
166
172
  - !ruby/object:Gem::Version
167
173
  version: '0'
168
174
  requirements: []
169
- rubygems_version: 3.0.6
175
+ rubygems_version: 3.3.3
170
176
  signing_key:
171
177
  specification_version: 4
172
178
  summary: Simple authorization solution for Rails.