cancancan 3.4.0 → 3.6.1
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 +4 -4
- data/cancancan.gemspec +1 -1
- data/lib/cancan/ability/rules.rb +3 -3
- data/lib/cancan/ability/strong_parameter_support.rb +1 -1
- data/lib/cancan/conditions_matcher.rb +31 -9
- data/lib/cancan/config.rb +23 -0
- data/lib/cancan/controller_additions.rb +6 -1
- data/lib/cancan/model_adapters/abstract_adapter.rb +5 -0
- data/lib/cancan/model_adapters/active_record_adapter.rb +56 -4
- data/lib/cancan/model_adapters/sti_normalizer.rb +9 -1
- data/lib/cancan/rule.rb +1 -1
- data/lib/cancan/rules_compressor.rb +18 -0
- data/lib/cancan/version.rb +1 -1
- metadata +4 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: e0d4a5b11aba155764cf54465d5d8bf88872fed61f4a33736d45a531c619ad6e
         | 
| 4 | 
            +
              data.tar.gz: 12b3b41403f3fdaba3fdb97c30ce17e319d1bfdef05fcb7700d41ba754c75a55
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: beb6989dbb2554678fa17626b6138aab396419b6d01ed8ce3ee3781030086da48985837a01bef50ab111fb47273454df8c7e3d6ced11b10a036ffe2e494feb15
         | 
| 7 | 
            +
              data.tar.gz: 405b0e6eb4ab73f04964651ab27e106701c014cd706f74012e5f30aada5796de6541f812593b57e058031af0991890884701136229f0405900e3bc99ce3747ee
         | 
    
        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', '~> 1. | 
| 28 | 
            +
              s.add_development_dependency 'rubocop', '~> 1.31.1'
         | 
| 29 29 | 
             
            end
         | 
    
        data/lib/cancan/ability/rules.rb
    CHANGED
    
    | @@ -61,8 +61,8 @@ module CanCan | |
| 61 61 | 
             
                      next unless rule.only_raw_sql?
         | 
| 62 62 |  | 
| 63 63 | 
             
                      raise Error,
         | 
| 64 | 
            -
                            "The can? and cannot? call cannot be used with a raw sql 'can' definition."\
         | 
| 65 | 
            -
                            " | 
| 64 | 
            +
                            "The can? and cannot? call cannot be used with a raw sql 'can' definition. " \
         | 
| 65 | 
            +
                            "The checking code cannot be determined for #{action.inspect} #{subject.inspect}"
         | 
| 66 66 | 
             
                    end
         | 
| 67 67 | 
             
                  end
         | 
| 68 68 |  | 
| @@ -72,7 +72,7 @@ module CanCan | |
| 72 72 | 
             
                      rule.base_behavior == false && rule.attributes.present?
         | 
| 73 73 | 
             
                    end
         | 
| 74 74 | 
             
                    if rules.any?(&:only_block?)
         | 
| 75 | 
            -
                      raise Error, "The accessible_by call cannot be used with a block 'can' definition."\
         | 
| 75 | 
            +
                      raise Error, "The accessible_by call cannot be used with a block 'can' definition." \
         | 
| 76 76 | 
             
                        "The SQL cannot be determined for #{action.inspect} #{subject.inspect}"
         | 
| 77 77 | 
             
                    end
         | 
| 78 78 | 
             
                    rules
         | 
| @@ -31,7 +31,7 @@ module CanCan | |
| 31 31 | 
             
                    klass = subject_class?(subject) ? subject : subject.class
         | 
| 32 32 | 
             
                    # empty attributes is an 'all'
         | 
| 33 33 | 
             
                    if rule.attributes.empty? && klass < ActiveRecord::Base
         | 
| 34 | 
            -
                      klass. | 
| 34 | 
            +
                      klass.attribute_names.map(&:to_sym) - Array(klass.primary_key)
         | 
| 35 35 | 
             
                    else
         | 
| 36 36 | 
             
                      rule.attributes
         | 
| 37 37 | 
             
                    end
         | 
| @@ -18,10 +18,14 @@ module CanCan | |
| 18 18 | 
             
                  [Class, Module].include? klass
         | 
| 19 19 | 
             
                end
         | 
| 20 20 |  | 
| 21 | 
            -
                def matches_block_conditions(subject, *extra_args)
         | 
| 21 | 
            +
                def matches_block_conditions(subject, attribute, *extra_args)
         | 
| 22 22 | 
             
                  return @base_behavior if subject_class?(subject)
         | 
| 23 23 |  | 
| 24 | 
            -
                   | 
| 24 | 
            +
                  if attribute
         | 
| 25 | 
            +
                    @block.call(subject, attribute, *extra_args)
         | 
| 26 | 
            +
                  else
         | 
| 27 | 
            +
                    @block.call(subject, *extra_args)
         | 
| 28 | 
            +
                  end
         | 
| 25 29 | 
             
                end
         | 
| 26 30 |  | 
| 27 31 | 
             
                def matches_non_block_conditions(subject)
         | 
| @@ -35,11 +39,13 @@ module CanCan | |
| 35 39 | 
             
                def nested_subject_matches_conditions?(subject_hash)
         | 
| 36 40 | 
             
                  parent, child = subject_hash.first
         | 
| 37 41 |  | 
| 38 | 
            -
                  matches_base_parent_conditions = matches_conditions_hash?(parent,
         | 
| 39 | 
            -
                                                                            @conditions[parent.class.name.downcase.to_sym] || {})
         | 
| 40 | 
            -
             | 
| 41 42 | 
             
                  adapter = model_adapter(parent)
         | 
| 42 43 |  | 
| 44 | 
            +
                  parent_condition_name = adapter.parent_condition_name(parent, child)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  matches_base_parent_conditions = matches_conditions_hash?(parent,
         | 
| 47 | 
            +
                                                                            @conditions[parent_condition_name] || {})
         | 
| 48 | 
            +
             | 
| 43 49 | 
             
                  matches_base_parent_conditions &&
         | 
| 44 50 | 
             
                    (!adapter.override_nested_subject_conditions_matching?(parent, child, @conditions) ||
         | 
| 45 51 | 
             
                      adapter.nested_subject_matches_conditions?(parent, child, @conditions))
         | 
| @@ -63,16 +69,15 @@ module CanCan | |
| 63 69 |  | 
| 64 70 | 
             
                def matches_all_conditions?(adapter, subject, conditions)
         | 
| 65 71 | 
             
                  if conditions.is_a?(Hash)
         | 
| 66 | 
            -
                    matches_hash_conditions(adapter, subject, conditions)
         | 
| 72 | 
            +
                    matches_hash_conditions?(adapter, subject, conditions)
         | 
| 67 73 | 
             
                  elsif conditions.respond_to?(:include?)
         | 
| 68 74 | 
             
                    conditions.include?(subject)
         | 
| 69 75 | 
             
                  else
         | 
| 70 | 
            -
                    puts "does #{subject} match #{conditions}?"
         | 
| 71 76 | 
             
                    subject == conditions
         | 
| 72 77 | 
             
                  end
         | 
| 73 78 | 
             
                end
         | 
| 74 79 |  | 
| 75 | 
            -
                def matches_hash_conditions(adapter, subject, conditions)
         | 
| 80 | 
            +
                def matches_hash_conditions?(adapter, subject, conditions)
         | 
| 76 81 | 
             
                  conditions.all? do |name, value|
         | 
| 77 82 | 
             
                    if adapter.override_condition_matching?(subject, name, value)
         | 
| 78 83 | 
             
                      adapter.matches_condition?(subject, name, value)
         | 
| @@ -97,12 +102,29 @@ module CanCan | |
| 97 102 |  | 
| 98 103 | 
             
                def hash_condition_match?(attribute, value)
         | 
| 99 104 | 
             
                  if attribute.is_a?(Array) || (defined?(ActiveRecord) && attribute.is_a?(ActiveRecord::Relation))
         | 
| 100 | 
            -
                     | 
| 105 | 
            +
                    array_like_matches_condition_hash?(attribute, value)
         | 
| 101 106 | 
             
                  else
         | 
| 102 107 | 
             
                    attribute && matches_conditions_hash?(attribute, value)
         | 
| 103 108 | 
             
                  end
         | 
| 104 109 | 
             
                end
         | 
| 105 110 |  | 
| 111 | 
            +
                def array_like_matches_condition_hash?(attribute, value)
         | 
| 112 | 
            +
                  if attribute.any?
         | 
| 113 | 
            +
                    attribute.any? { |element| matches_conditions_hash?(element, value) }
         | 
| 114 | 
            +
                  else
         | 
| 115 | 
            +
                    # you can use `nil`s in your ability definition to tell cancancan to find
         | 
| 116 | 
            +
                    # objects that *don't* have any children in a has_many relationship.
         | 
| 117 | 
            +
                    #
         | 
| 118 | 
            +
                    # for example, given ability:
         | 
| 119 | 
            +
                    # => can :read, Article, comments: { id: nil }
         | 
| 120 | 
            +
                    # cancancan will return articles where `article.comments == []`
         | 
| 121 | 
            +
                    #
         | 
| 122 | 
            +
                    # this is implemented here. `attribute` is `article.comments`, and it's an empty array.
         | 
| 123 | 
            +
                    # the expression below returns true if this was expected.
         | 
| 124 | 
            +
                    !value.values.empty? && value.values.all?(&:nil?)
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
             | 
| 106 128 | 
             
                def call_block_with_all(action, subject, *extra_args)
         | 
| 107 129 | 
             
                  if subject.class == Class
         | 
| 108 130 | 
             
                    @block.call(action, subject, nil, *extra_args)
         | 
    
        data/lib/cancan/config.rb
    CHANGED
    
    | @@ -11,6 +11,29 @@ module CanCan | |
| 11 11 | 
             
                strategies
         | 
| 12 12 | 
             
              end
         | 
| 13 13 |  | 
| 14 | 
            +
              # You can disable the rules compressor if it's causing unexpected issues.
         | 
| 15 | 
            +
              def self.rules_compressor_enabled
         | 
| 16 | 
            +
                return @rules_compressor_enabled if defined?(@rules_compressor_enabled)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                @rules_compressor_enabled = true
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              def self.rules_compressor_enabled=(value)
         | 
| 22 | 
            +
                @rules_compressor_enabled = value
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              def self.with_rules_compressor_enabled(value)
         | 
| 26 | 
            +
                return yield if value == rules_compressor_enabled
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                begin
         | 
| 29 | 
            +
                  rules_compressor_enabled_was = rules_compressor_enabled
         | 
| 30 | 
            +
                  @rules_compressor_enabled = value
         | 
| 31 | 
            +
                  yield
         | 
| 32 | 
            +
                ensure
         | 
| 33 | 
            +
                  @rules_compressor_enabled = rules_compressor_enabled_was
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 14 37 | 
             
              # Determines how CanCan should build queries when calling accessible_by,
         | 
| 15 38 | 
             
              # if the query will contain a join. The default strategy is `:subquery`.
         | 
| 16 39 | 
             
              #
         | 
| @@ -171,6 +171,11 @@ module CanCan | |
| 171 171 | 
             
                  # [:+instance_name+]
         | 
| 172 172 | 
             
                  #   The name of the instance variable for this resource.
         | 
| 173 173 | 
             
                  #
         | 
| 174 | 
            +
                  # [:+id_param+]
         | 
| 175 | 
            +
                  #   Find using a param key other than :id. For example:
         | 
| 176 | 
            +
                  #
         | 
| 177 | 
            +
                  #     load_resource :id_param => :url # will use find(params[:url])
         | 
| 178 | 
            +
                  #
         | 
| 174 179 | 
             
                  # [:+through+]
         | 
| 175 180 | 
             
                  #   Authorize conditions on this parent resource when instance isn't available.
         | 
| 176 181 | 
             
                  #
         | 
| @@ -264,7 +269,7 @@ module CanCan | |
| 264 269 | 
             
                      next if options[:unless] && controller.send(options[:unless])
         | 
| 265 270 |  | 
| 266 271 | 
             
                      raise AuthorizationNotPerformed,
         | 
| 267 | 
            -
                            'This action failed the check_authorization because it does not authorize_resource. '\
         | 
| 272 | 
            +
                            'This action failed the check_authorization because it does not authorize_resource. ' \
         | 
| 268 273 | 
             
                            'Add skip_authorization_check to bypass this check.'
         | 
| 269 274 | 
             
                    end
         | 
| 270 275 |  | 
| @@ -35,6 +35,11 @@ module CanCan | |
| 35 35 | 
             
                    raise NotImplemented, 'This model adapter does not support matching on a conditions hash.'
         | 
| 36 36 | 
             
                  end
         | 
| 37 37 |  | 
| 38 | 
            +
                  # Override if parent condition could be under a different key in conditions
         | 
| 39 | 
            +
                  def self.parent_condition_name(parent, _child)
         | 
| 40 | 
            +
                    parent.class.name.downcase.to_sym
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 38 43 | 
             
                  # Used above override_conditions_hash_matching to determine if this model adapter will override the
         | 
| 39 44 | 
             
                  # matching behavior for nested subject.
         | 
| 40 45 | 
             
                  # If this returns true then nested_subject_matches_conditions? will be called.
         | 
| @@ -1,5 +1,8 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            # rubocop:disable Metrics/AbcSize
         | 
| 4 | 
            +
            # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 5 | 
            +
            # rubocop:disable Metrics/PerceivedComplexity
         | 
| 3 6 | 
             
            module CanCan
         | 
| 4 7 | 
             
              module ModelAdapters
         | 
| 5 8 | 
             
                class ActiveRecordAdapter < AbstractAdapter
         | 
| @@ -15,7 +18,11 @@ module CanCan | |
| 15 18 |  | 
| 16 19 | 
             
                  def initialize(model_class, rules)
         | 
| 17 20 | 
             
                    super
         | 
| 18 | 
            -
                    @compressed_rules =  | 
| 21 | 
            +
                    @compressed_rules = if CanCan.rules_compressor_enabled
         | 
| 22 | 
            +
                                          RulesCompressor.new(@rules.reverse).rules_collapsed.reverse
         | 
| 23 | 
            +
                                        else
         | 
| 24 | 
            +
                                          @rules
         | 
| 25 | 
            +
                                        end
         | 
| 19 26 | 
             
                    StiNormalizer.normalize(@compressed_rules)
         | 
| 20 27 | 
             
                    ConditionsNormalizer.normalize(model_class, @compressed_rules)
         | 
| 21 28 | 
             
                  end
         | 
| @@ -38,11 +45,53 @@ module CanCan | |
| 38 45 |  | 
| 39 46 | 
             
                    def parent_child_conditions(parent, child, all_conditions)
         | 
| 40 47 | 
             
                      child_class = child.is_a?(Class) ? child : child.class
         | 
| 48 | 
            +
                      parent_class = parent.is_a?(Class) ? parent : parent.class
         | 
| 49 | 
            +
             | 
| 41 50 | 
             
                      foreign_key = child_class.reflect_on_all_associations(:belongs_to).find do |association|
         | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 51 | 
            +
                        # Do not match on polymorphic associations or it will throw an error (klass cannot be determined)
         | 
| 52 | 
            +
                        !association.polymorphic? && association.klass == parent.class
         | 
| 53 | 
            +
                      end&.foreign_key&.to_sym
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                      # Search again in case of polymorphic associations, this time matching on the :has_many side
         | 
| 56 | 
            +
                      # via the :as option, as well as klass
         | 
| 57 | 
            +
                      foreign_key ||= parent_class.reflect_on_all_associations(:has_many).find do |has_many_assoc|
         | 
| 58 | 
            +
                        matching_parent_child_polymorphic_association(has_many_assoc, child_class)
         | 
| 59 | 
            +
                      end&.foreign_key&.to_sym
         | 
| 60 | 
            +
             | 
| 44 61 | 
             
                      foreign_key.nil? ? nil : all_conditions[foreign_key]
         | 
| 45 62 | 
             
                    end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    def matching_parent_child_polymorphic_association(parent_assoc, child_class)
         | 
| 65 | 
            +
                      return nil unless parent_assoc.klass == child_class
         | 
| 66 | 
            +
                      return nil if parent_assoc&.options[:as].nil?
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                      child_class.reflect_on_all_associations(:belongs_to).find do |child_assoc|
         | 
| 69 | 
            +
                        # Only match this way for polymorphic associations
         | 
| 70 | 
            +
                        child_assoc.polymorphic? && child_assoc.name == parent_assoc.options[:as]
         | 
| 71 | 
            +
                      end
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    def child_association_to_parent(parent, child)
         | 
| 75 | 
            +
                      child_class = child.is_a?(Class) ? child : child.class
         | 
| 76 | 
            +
                      parent_class = parent.is_a?(Class) ? parent : parent.class
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                      association = child_class.reflect_on_all_associations(:belongs_to).find do |belongs_to_assoc|
         | 
| 79 | 
            +
                        # Do not match on polymorphic associations or it will throw an error (klass cannot be determined)
         | 
| 80 | 
            +
                        !belongs_to_assoc.polymorphic? && belongs_to_assoc.klass == parent.class
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                      return association if association
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                      parent_class.reflect_on_all_associations(:has_many).each do |has_many_assoc|
         | 
| 86 | 
            +
                        association ||= matching_parent_child_polymorphic_association(has_many_assoc, child_class)
         | 
| 87 | 
            +
                      end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                      association
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    def parent_condition_name(parent, child)
         | 
| 93 | 
            +
                      child_association_to_parent(parent, child)&.name || parent.class.name.downcase.to_sym
         | 
| 94 | 
            +
                    end
         | 
| 46 95 | 
             
                  end
         | 
| 47 96 |  | 
| 48 97 | 
             
                  # Returns conditions intended to be used inside a database query. Normally you will not call this
         | 
| @@ -133,7 +182,7 @@ module CanCan | |
| 133 182 | 
             
                  def raise_override_scope_error
         | 
| 134 183 | 
             
                    rule_found = @compressed_rules.detect { |rule| rule.conditions.is_a?(ActiveRecord::Relation) }
         | 
| 135 184 | 
             
                    raise Error,
         | 
| 136 | 
            -
                          'Unable to merge an Active Record scope with other conditions. '\
         | 
| 185 | 
            +
                          'Unable to merge an Active Record scope with other conditions. ' \
         | 
| 137 186 | 
             
                          "Instead use a hash or SQL for #{rule_found.actions.first} #{rule_found.subjects.first} ability."
         | 
| 138 187 | 
             
                  end
         | 
| 139 188 |  | 
| @@ -171,6 +220,9 @@ module CanCan | |
| 171 220 | 
             
                end
         | 
| 172 221 | 
             
              end
         | 
| 173 222 | 
             
            end
         | 
| 223 | 
            +
            # rubocop:enable Metrics/PerceivedComplexity
         | 
| 224 | 
            +
            # rubocop:enable Metrics/CyclomaticComplexity
         | 
| 225 | 
            +
            # rubocop:enable Metrics/AbcSize
         | 
| 174 226 |  | 
| 175 227 | 
             
            ActiveSupport.on_load(:active_record) do
         | 
| 176 228 | 
             
              send :include, CanCan::ModelAdditions
         | 
| @@ -30,8 +30,16 @@ module CanCan | |
| 30 30 |  | 
| 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 | 
            +
                      sti_conditions = { subject.inheritance_column => subject.sti_name }
         | 
| 34 | 
            +
                      new_rule_conditions =
         | 
| 35 | 
            +
                        if rule.with_scope?
         | 
| 36 | 
            +
                          rule.conditions.where(sti_conditions)
         | 
| 37 | 
            +
                        else
         | 
| 38 | 
            +
                          rule.conditions.merge(sti_conditions)
         | 
| 39 | 
            +
                        end
         | 
| 40 | 
            +
             | 
| 33 41 | 
             
                      CanCan::Rule.new(rule.base_behavior, rule.actions, subject.superclass,
         | 
| 34 | 
            -
                                        | 
| 42 | 
            +
                                       new_rule_conditions, rule.block)
         | 
| 35 43 | 
             
                    end
         | 
| 36 44 | 
             
                  end
         | 
| 37 45 | 
             
                end
         | 
    
        data/lib/cancan/rule.rb
    CHANGED
    
    | @@ -123,7 +123,7 @@ module CanCan | |
| 123 123 | 
             
                def condition_and_block_check(conditions, block, action, subject)
         | 
| 124 124 | 
             
                  return unless conditions.is_a?(Hash) && block
         | 
| 125 125 |  | 
| 126 | 
            -
                  raise BlockAndConditionsError, 'A hash of conditions is mutually exclusive with a block. '\
         | 
| 126 | 
            +
                  raise BlockAndConditionsError, 'A hash of conditions is mutually exclusive with a block. ' \
         | 
| 127 127 | 
             
                    "Check \":#{action} #{subject}\" ability."
         | 
| 128 128 | 
             
                end
         | 
| 129 129 |  | 
| @@ -11,6 +11,7 @@ module CanCan | |
| 11 11 | 
             
                end
         | 
| 12 12 |  | 
| 13 13 | 
             
                def compress(array)
         | 
| 14 | 
            +
                  array = simplify(array)
         | 
| 14 15 | 
             
                  idx = array.rindex(&:catch_all?)
         | 
| 15 16 | 
             
                  return array unless idx
         | 
| 16 17 |  | 
| @@ -19,5 +20,22 @@ module CanCan | |
| 19 20 | 
             
                    .drop_while { |n| n.base_behavior == value.base_behavior }
         | 
| 20 21 | 
             
                    .tap { |a| a.unshift(value) unless value.cannot_catch_all? }
         | 
| 21 22 | 
             
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                # If we have A OR (!A AND anything ), then we can simplify to A OR anything
         | 
| 25 | 
            +
                # If we have A OR (A OR anything ), then we can simplify to A OR anything
         | 
| 26 | 
            +
                # If we have !A AND (A OR something), then we can simplify it to !A AND something
         | 
| 27 | 
            +
                # If we have !A AND (!A AND something), then we can simplify it to !A AND something
         | 
| 28 | 
            +
                #
         | 
| 29 | 
            +
                # So as soon as we see a condition that is the same as the previous one,
         | 
| 30 | 
            +
                # we can skip it, no matter of the base_behavior
         | 
| 31 | 
            +
                def simplify(rules)
         | 
| 32 | 
            +
                  seen = Set.new
         | 
| 33 | 
            +
                  rules.reverse_each.filter_map do |rule|
         | 
| 34 | 
            +
                    next if seen.include?(rule.conditions)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    seen.add(rule.conditions)
         | 
| 37 | 
            +
                    rule
         | 
| 38 | 
            +
                  end.reverse
         | 
| 39 | 
            +
                end
         | 
| 22 40 | 
             
              end
         | 
| 23 41 | 
             
            end
         | 
    
        data/lib/cancan/version.rb
    CHANGED
    
    
    
        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. | 
| 4 | 
            +
              version: 3.6.1
         | 
| 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:  | 
| 14 | 
            +
            date: 2024-05-28 00:00:00.000000000 Z
         | 
| 15 15 | 
             
            dependencies:
         | 
| 16 16 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 17 17 | 
             
              name: appraisal
         | 
| @@ -93,14 +93,14 @@ dependencies: | |
| 93 93 | 
             
                requirements:
         | 
| 94 94 | 
             
                - - "~>"
         | 
| 95 95 | 
             
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            -
                    version:  | 
| 96 | 
            +
                    version: 1.31.1
         | 
| 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:  | 
| 103 | 
            +
                    version: 1.31.1
         | 
| 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
         |