rubocop-rails 2.29.0 → 2.31.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 +4 -4
- data/README.md +9 -7
- data/config/default.yml +18 -2
- data/lib/rubocop/cop/mixin/active_record_helper.rb +2 -2
- data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +2 -2
- data/lib/rubocop/cop/mixin/database_type_resolvable.rb +2 -2
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +6 -1
- data/lib/rubocop/cop/mixin/index_method.rb +66 -65
- data/lib/rubocop/cop/rails/arel_star.rb +5 -5
- data/lib/rubocop/cop/rails/belongs_to.rb +1 -1
- data/lib/rubocop/cop/rails/blank.rb +1 -1
- data/lib/rubocop/cop/rails/content_tag.rb +1 -1
- data/lib/rubocop/cop/rails/delegate.rb +53 -7
- data/lib/rubocop/cop/rails/duplicate_association.rb +8 -4
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +1 -3
- data/lib/rubocop/cop/rails/file_path.rb +58 -9
- data/lib/rubocop/cop/rails/index_by.rb +9 -0
- data/lib/rubocop/cop/rails/index_with.rb +9 -0
- data/lib/rubocop/cop/rails/inquiry.rb +1 -1
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +11 -1
- data/lib/rubocop/cop/rails/output.rb +1 -2
- data/lib/rubocop/cop/rails/pluck.rb +11 -5
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +1 -1
- data/lib/rubocop/cop/rails/presence.rb +1 -1
- data/lib/rubocop/cop/rails/present.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +6 -1
- data/lib/rubocop/cop/rails/reflection_class_name.rb +2 -2
- data/lib/rubocop/cop/rails/relative_date_constant.rb +1 -1
- data/lib/rubocop/cop/rails/reversible_migration.rb +2 -1
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +6 -1
- data/lib/rubocop/cop/rails/save_bang.rb +7 -6
- data/lib/rubocop/cop/rails/schema_comment.rb +1 -1
- data/lib/rubocop/cop/rails/skips_model_validations.rb +1 -1
- data/lib/rubocop/cop/rails/strip_heredoc.rb +1 -1
- data/lib/rubocop/cop/rails/strong_parameters_expect.rb +2 -2
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +2 -2
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +10 -33
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +1 -1
- data/lib/rubocop/rails/plugin.rb +48 -0
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop/rails.rb +1 -7
- data/lib/rubocop-rails.rb +1 -5
- metadata +23 -8
- data/lib/rubocop/rails/inject.rb +0 -18
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ba4876948bedd432dc186a3c060c26c3808f1c7126b8fe69072e056a5f160d94
         | 
| 4 | 
            +
              data.tar.gz: 7f890ec180a125abe4a40ad146661e71c088596eb093eacf12d322135c7f6907
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 719fd043fd8ae7738eebb0c78abba01a23f2bc30c6d8c9825c89c7cbcc6cd48fd0077918e9bc84c9c471afecd62b69597d3f046c757606ecaec4af9daf8ef183
         | 
| 7 | 
            +
              data.tar.gz: b7173eeaf159ee8673fe2f2c16a1c92bcbb55601d1202db18e5b48d09d1d1ef0207c17a182dcfb2b39c6e686d2b82871b191d5f90c4416a1af7d7b038a12c218
         | 
    
        data/README.md
    CHANGED
    
    | @@ -5,7 +5,8 @@ | |
| 5 5 |  | 
| 6 6 | 
             
            A [RuboCop](https://github.com/rubocop/rubocop) extension focused on enforcing Rails best practices and coding conventions.
         | 
| 7 7 |  | 
| 8 | 
            -
             | 
| 8 | 
            +
            > [!IMPORTANT]
         | 
| 9 | 
            +
            > This repository manages rubocop-rails gem (>= 2.0.0). rubocop-rails gem (<= 1.5.0) has been renamed to [rubocop-rails_config](https://rubygems.org/gems/rubocop-rails_config) gem.
         | 
| 9 10 |  | 
| 10 11 | 
             
            ## Installation
         | 
| 11 12 |  | 
| @@ -31,13 +32,13 @@ ways to do this: | |
| 31 32 | 
             
            Put this into your `.rubocop.yml`.
         | 
| 32 33 |  | 
| 33 34 | 
             
            ```yaml
         | 
| 34 | 
            -
             | 
| 35 | 
            +
            plugins: rubocop-rails
         | 
| 35 36 | 
             
            ```
         | 
| 36 37 |  | 
| 37 38 | 
             
            Alternatively, use the following array notation when specifying multiple extensions.
         | 
| 38 39 |  | 
| 39 40 | 
             
            ```yaml
         | 
| 40 | 
            -
             | 
| 41 | 
            +
            plugins:
         | 
| 41 42 | 
             
              - rubocop-other-extension
         | 
| 42 43 | 
             
              - rubocop-rails
         | 
| 43 44 | 
             
            ```
         | 
| @@ -45,21 +46,22 @@ require: | |
| 45 46 | 
             
            Now you can run `rubocop` and it will automatically load the RuboCop Rails
         | 
| 46 47 | 
             
            cops together with the standard cops.
         | 
| 47 48 |  | 
| 49 | 
            +
            > [!NOTE]
         | 
| 50 | 
            +
            > The plugin system is supported in RuboCop 1.72+. In earlier versions, use `require` instead of `plugins`.
         | 
| 51 | 
            +
             | 
| 48 52 | 
             
            ### Command line
         | 
| 49 53 |  | 
| 50 54 | 
             
            ```sh
         | 
| 51 | 
            -
            $ rubocop -- | 
| 55 | 
            +
            $ rubocop --plugin rubocop-rails
         | 
| 52 56 | 
             
            ```
         | 
| 53 57 |  | 
| 54 | 
            -
            Note: `--rails` option is required while `rubocop` command supports `--rails` option.
         | 
| 55 | 
            -
             | 
| 56 58 | 
             
            ### Rake task
         | 
| 57 59 |  | 
| 58 60 | 
             
            ```ruby
         | 
| 59 61 | 
             
            require 'rubocop/rake_task'
         | 
| 60 62 |  | 
| 61 63 | 
             
            RuboCop::RakeTask.new do |task|
         | 
| 62 | 
            -
              task. | 
| 64 | 
            +
              task.plugins << 'rubocop-rails'
         | 
| 63 65 | 
             
            end
         | 
| 64 66 | 
             
            ```
         | 
| 65 67 |  | 
    
        data/config/default.yml
    CHANGED
    
    | @@ -28,7 +28,7 @@ AllCops: | |
| 28 28 | 
             
              # By specifying `MigratedSchemaVersion` option, migration files that have been migrated can be ignored.
         | 
| 29 29 | 
             
              # When `MigratedSchemaVersion: '20241231000000'` is set. Migration files lower than or equal to '20250101000000' will be ignored.
         | 
| 30 30 | 
             
              # For example, this is the timestamp in db/migrate/20250101000000_create_articles.rb.
         | 
| 31 | 
            -
              MigratedSchemaVersion:  | 
| 31 | 
            +
              MigratedSchemaVersion: '19700101000000' # NOTE: Used as a sentinel value for the UNIX epoch time.
         | 
| 32 32 |  | 
| 33 33 | 
             
            Lint/NumberConversion:
         | 
| 34 34 | 
             
              # Add Rails' duration methods to the ignore list for `Lint/NumberConversion`
         | 
| @@ -77,6 +77,20 @@ Lint/SafeNavigationChain: | |
| 77 77 | 
             
                - try!
         | 
| 78 78 | 
             
                - in?
         | 
| 79 79 |  | 
| 80 | 
            +
            Lint/UselessAccessModifier:
         | 
| 81 | 
            +
              # Add methods from `ActiveSupport::Concern` and `Module::Concerning`:
         | 
| 82 | 
            +
              # https://api.rubyonrails.org/classes/ActiveSupport/Concern.html
         | 
| 83 | 
            +
              # https://api.rubyonrails.org/classes/Module/Concerning
         | 
| 84 | 
            +
              inherit_mode:
         | 
| 85 | 
            +
                merge:
         | 
| 86 | 
            +
                  - ContextCreatingMethods
         | 
| 87 | 
            +
              ContextCreatingMethods:
         | 
| 88 | 
            +
                - class_methods
         | 
| 89 | 
            +
                - included
         | 
| 90 | 
            +
                - prepended
         | 
| 91 | 
            +
                - concern
         | 
| 92 | 
            +
                - concerning
         | 
| 93 | 
            +
             | 
| 80 94 | 
             
            Rails:
         | 
| 81 95 | 
             
              Enabled: true
         | 
| 82 96 | 
             
              DocumentationBaseURL: https://docs.rubocop.org/rubocop-rails
         | 
| @@ -353,11 +367,13 @@ Rails/Delegate: | |
| 353 367 | 
             
              Description: 'Prefer delegate method for delegations.'
         | 
| 354 368 | 
             
              Enabled: true
         | 
| 355 369 | 
             
              VersionAdded: '0.21'
         | 
| 356 | 
            -
              VersionChanged: ' | 
| 370 | 
            +
              VersionChanged: '2.30'
         | 
| 357 371 | 
             
              # When set to true, using the target object as a prefix of the
         | 
| 358 372 | 
             
              # method name without using the `delegate` method will be a
         | 
| 359 373 | 
             
              # violation. When set to false, this case is legal.
         | 
| 360 374 | 
             
              EnforceForPrefixed: true
         | 
| 375 | 
            +
              Exclude:
         | 
| 376 | 
            +
                - app/controllers/**/*.rb
         | 
| 361 377 |  | 
| 362 378 | 
             
            Rails/DelegateAllowBlank:
         | 
| 363 379 | 
             
              Description: 'Do not use allow_blank as an option to delegate.'
         | 
| @@ -87,7 +87,7 @@ module RuboCop | |
| 87 87 |  | 
| 88 88 | 
             
                    options.each_pair.find do |pair|
         | 
| 89 89 | 
             
                      next unless pair.key.sym_type? && pair.key.value == :foreign_key
         | 
| 90 | 
            -
                      next unless pair.value. | 
| 90 | 
            +
                      next unless pair.value.type?(:sym, :str)
         | 
| 91 91 |  | 
| 92 92 | 
             
                      break pair.value.value.to_s
         | 
| 93 93 | 
             
                    end
         | 
| @@ -103,7 +103,7 @@ module RuboCop | |
| 103 103 | 
             
                  end
         | 
| 104 104 |  | 
| 105 105 | 
             
                  def in_where?(node)
         | 
| 106 | 
            -
                    send_node = node.each_ancestor(: | 
| 106 | 
            +
                    send_node = node.each_ancestor(:call).first
         | 
| 107 107 | 
             
                    return false unless send_node
         | 
| 108 108 |  | 
| 109 109 | 
             
                    return true if WHERE_METHODS.include?(send_node.method_name)
         | 
| @@ -1,7 +1,12 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            module RuboCop
         | 
| 4 | 
            -
              module Cop
         | 
| 4 | 
            +
              module Cop # rubocop:disable Style/Documentation
         | 
| 5 | 
            +
                # The EnforceSuperclass module is also defined in `rubocop` (for backwards
         | 
| 6 | 
            +
                # compatibility), so here we remove it before (re)defining it, to avoid
         | 
| 7 | 
            +
                # warnings about methods in the module being redefined.
         | 
| 8 | 
            +
                remove_const(:EnforceSuperclass) if defined?(EnforceSuperclass)
         | 
| 9 | 
            +
             | 
| 5 10 | 
             
                # Common functionality for enforcing a specific superclass.
         | 
| 6 11 | 
             
                module EnforceSuperclass
         | 
| 7 12 | 
             
                  def self.included(base)
         | 
| @@ -6,6 +6,71 @@ module RuboCop | |
| 6 6 | 
             
                module IndexMethod # rubocop:disable Metrics/ModuleLength
         | 
| 7 7 | 
             
                  RESTRICT_ON_SEND = %i[each_with_object to_h map collect []].freeze
         | 
| 8 8 |  | 
| 9 | 
            +
                  # Internal helper class to hold match data
         | 
| 10 | 
            +
                  Captures = ::Struct.new(
         | 
| 11 | 
            +
                    :transformed_argname,
         | 
| 12 | 
            +
                    :transforming_body_expr
         | 
| 13 | 
            +
                  ) do
         | 
| 14 | 
            +
                    def noop_transformation?
         | 
| 15 | 
            +
                      transforming_body_expr.lvar_type? && transforming_body_expr.children == [transformed_argname]
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  # Internal helper class to hold autocorrect data
         | 
| 20 | 
            +
                  Autocorrection = ::Struct.new(:match, :block_node, :leading, :trailing) do
         | 
| 21 | 
            +
                    def self.from_each_with_object(node, match)
         | 
| 22 | 
            +
                      new(match, node, 0, 0)
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    def self.from_to_h(node, match)
         | 
| 26 | 
            +
                      new(match, node, 0, 0)
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    def self.from_map_to_h(node, match)
         | 
| 30 | 
            +
                      if node.block_literal?
         | 
| 31 | 
            +
                        strip_trailing_chars = 0
         | 
| 32 | 
            +
                      else
         | 
| 33 | 
            +
                        map_range = node.children.first.source_range
         | 
| 34 | 
            +
                        node_range = node.source_range
         | 
| 35 | 
            +
                        strip_trailing_chars = node_range.end_pos - map_range.end_pos
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                      new(match, node.children.first, 0, strip_trailing_chars)
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    def self.from_hash_brackets_map(node, match)
         | 
| 42 | 
            +
                      new(match, node.children.last, "#{node.receiver.source}[".length, ']'.length)
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    def strip_prefix_and_suffix(node, corrector)
         | 
| 46 | 
            +
                      expression = node.source_range
         | 
| 47 | 
            +
                      corrector.remove_leading(expression, leading)
         | 
| 48 | 
            +
                      corrector.remove_trailing(expression, trailing)
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    def set_new_method_name(new_method_name, corrector)
         | 
| 52 | 
            +
                      range = block_node.send_node.loc.selector
         | 
| 53 | 
            +
                      if (send_end = block_node.send_node.loc.end)
         | 
| 54 | 
            +
                        # If there are arguments (only true in the `each_with_object` case)
         | 
| 55 | 
            +
                        range = range.begin.join(send_end)
         | 
| 56 | 
            +
                      end
         | 
| 57 | 
            +
                      corrector.replace(range, new_method_name)
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    def set_new_arg_name(transformed_argname, corrector)
         | 
| 61 | 
            +
                      return unless block_node.block_type?
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                      corrector.replace(block_node.arguments, "|#{transformed_argname}|")
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    def set_new_body_expression(transforming_body_expr, corrector)
         | 
| 67 | 
            +
                      body_source = transforming_body_expr.source
         | 
| 68 | 
            +
                      body_source = "{ #{body_source} }" if transforming_body_expr.hash_type? && !transforming_body_expr.braces?
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                      corrector.replace(block_node.body, body_source)
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 9 74 | 
             
                  def on_block(node)
         | 
| 10 75 | 
             
                    on_bad_each_with_object(node) do |*match|
         | 
| 11 76 | 
             
                      handle_possible_offense(node, match, 'each_with_object')
         | 
| @@ -19,6 +84,7 @@ module RuboCop | |
| 19 84 | 
             
                  end
         | 
| 20 85 |  | 
| 21 86 | 
             
                  alias on_numblock on_block
         | 
| 87 | 
            +
                  alias on_itblock on_block
         | 
| 22 88 |  | 
| 23 89 | 
             
                  def on_send(node)
         | 
| 24 90 | 
             
                    on_bad_map_to_h(node) do |*match|
         | 
| @@ -102,71 +168,6 @@ module RuboCop | |
| 102 168 | 
             
                    correction.set_new_arg_name(captures.transformed_argname, corrector)
         | 
| 103 169 | 
             
                    correction.set_new_body_expression(captures.transforming_body_expr, corrector)
         | 
| 104 170 | 
             
                  end
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                  # Internal helper class to hold match data
         | 
| 107 | 
            -
                  Captures = ::Struct.new(
         | 
| 108 | 
            -
                    :transformed_argname,
         | 
| 109 | 
            -
                    :transforming_body_expr
         | 
| 110 | 
            -
                  ) do
         | 
| 111 | 
            -
                    def noop_transformation?
         | 
| 112 | 
            -
                      transforming_body_expr.lvar_type? && transforming_body_expr.children == [transformed_argname]
         | 
| 113 | 
            -
                    end
         | 
| 114 | 
            -
                  end
         | 
| 115 | 
            -
             | 
| 116 | 
            -
                  # Internal helper class to hold autocorrect data
         | 
| 117 | 
            -
                  Autocorrection = ::Struct.new(:match, :block_node, :leading, :trailing) do
         | 
| 118 | 
            -
                    def self.from_each_with_object(node, match)
         | 
| 119 | 
            -
                      new(match, node, 0, 0)
         | 
| 120 | 
            -
                    end
         | 
| 121 | 
            -
             | 
| 122 | 
            -
                    def self.from_to_h(node, match)
         | 
| 123 | 
            -
                      new(match, node, 0, 0)
         | 
| 124 | 
            -
                    end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                    def self.from_map_to_h(node, match)
         | 
| 127 | 
            -
                      if node.block_literal?
         | 
| 128 | 
            -
                        strip_trailing_chars = 0
         | 
| 129 | 
            -
                      else
         | 
| 130 | 
            -
                        map_range = node.children.first.source_range
         | 
| 131 | 
            -
                        node_range = node.source_range
         | 
| 132 | 
            -
                        strip_trailing_chars = node_range.end_pos - map_range.end_pos
         | 
| 133 | 
            -
                      end
         | 
| 134 | 
            -
             | 
| 135 | 
            -
                      new(match, node.children.first, 0, strip_trailing_chars)
         | 
| 136 | 
            -
                    end
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                    def self.from_hash_brackets_map(node, match)
         | 
| 139 | 
            -
                      new(match, node.children.last, "#{node.receiver.source}[".length, ']'.length)
         | 
| 140 | 
            -
                    end
         | 
| 141 | 
            -
             | 
| 142 | 
            -
                    def strip_prefix_and_suffix(node, corrector)
         | 
| 143 | 
            -
                      expression = node.source_range
         | 
| 144 | 
            -
                      corrector.remove_leading(expression, leading)
         | 
| 145 | 
            -
                      corrector.remove_trailing(expression, trailing)
         | 
| 146 | 
            -
                    end
         | 
| 147 | 
            -
             | 
| 148 | 
            -
                    def set_new_method_name(new_method_name, corrector)
         | 
| 149 | 
            -
                      range = block_node.send_node.loc.selector
         | 
| 150 | 
            -
                      if (send_end = block_node.send_node.loc.end)
         | 
| 151 | 
            -
                        # If there are arguments (only true in the `each_with_object` case)
         | 
| 152 | 
            -
                        range = range.begin.join(send_end)
         | 
| 153 | 
            -
                      end
         | 
| 154 | 
            -
                      corrector.replace(range, new_method_name)
         | 
| 155 | 
            -
                    end
         | 
| 156 | 
            -
             | 
| 157 | 
            -
                    def set_new_arg_name(transformed_argname, corrector)
         | 
| 158 | 
            -
                      return if block_node.numblock_type?
         | 
| 159 | 
            -
             | 
| 160 | 
            -
                      corrector.replace(block_node.arguments, "|#{transformed_argname}|")
         | 
| 161 | 
            -
                    end
         | 
| 162 | 
            -
             | 
| 163 | 
            -
                    def set_new_body_expression(transforming_body_expr, corrector)
         | 
| 164 | 
            -
                      body_source = transforming_body_expr.source
         | 
| 165 | 
            -
                      body_source = "{ #{body_source} }" if transforming_body_expr.hash_type? && !transforming_body_expr.braces?
         | 
| 166 | 
            -
             | 
| 167 | 
            -
                      corrector.replace(block_node.body, body_source)
         | 
| 168 | 
            -
                    end
         | 
| 169 | 
            -
                  end
         | 
| 170 171 | 
             
                end
         | 
| 171 172 | 
             
              end
         | 
| 172 173 | 
             
            end
         | 
| @@ -5,14 +5,14 @@ module RuboCop | |
| 5 5 | 
             
                module Rails
         | 
| 6 6 | 
             
                  # Prevents usage of `"*"` on an Arel::Table column reference.
         | 
| 7 7 | 
             
                  #
         | 
| 8 | 
            -
                  # Using `arel_table[" | 
| 9 | 
            -
                  # quoted asterisk (e.g.  | 
| 10 | 
            -
                  # database to look for a column named  | 
| 8 | 
            +
                  # Using `arel_table["\*"]` causes the outputted string to be a literal
         | 
| 9 | 
            +
                  # quoted asterisk (e.g. `my_model`.`*`). This causes the
         | 
| 10 | 
            +
                  # database to look for a column named `\*` (or `"*"`) as opposed
         | 
| 11 11 | 
             
                  # to expanding the column list as one would likely expect.
         | 
| 12 12 | 
             
                  #
         | 
| 13 13 | 
             
                  # @safety
         | 
| 14 | 
            -
                  #   This cop's autocorrection is unsafe because it turns a quoted  | 
| 15 | 
            -
                  #   an SQL `*`, unquoted.  | 
| 14 | 
            +
                  #   This cop's autocorrection is unsafe because it turns a quoted `\*` into
         | 
| 15 | 
            +
                  #   an SQL `*`, unquoted. `\*` is a valid column name in certain databases
         | 
| 16 16 | 
             
                  #   supported by Rails, and even though it is usually a mistake,
         | 
| 17 17 | 
             
                  #   it might denote legitimate access to a column named `*`.
         | 
| 18 18 | 
             
                  #
         | 
| @@ -123,7 +123,7 @@ module RuboCop | |
| 123 123 | 
             
                    def on_if(node)
         | 
| 124 124 | 
             
                      return unless cop_config['UnlessPresent']
         | 
| 125 125 | 
             
                      return unless node.unless?
         | 
| 126 | 
            -
                      return if node.else? && config. | 
| 126 | 
            +
                      return if node.else? && config.cop_enabled?('Style/UnlessElse')
         | 
| 127 127 |  | 
| 128 128 | 
             
                      unless_present?(node) do |method_call, receiver|
         | 
| 129 129 | 
             
                        range = unless_condition(node, method_call)
         | 
| @@ -15,6 +15,9 @@ module RuboCop | |
| 15 15 | 
             
                  # without using the `delegate` method will be a violation.
         | 
| 16 16 | 
             
                  # When set to `false`, this case is legal.
         | 
| 17 17 | 
             
                  #
         | 
| 18 | 
            +
                  # It is disabled for controllers in order to keep controller actions
         | 
| 19 | 
            +
                  # explicitly defined.
         | 
| 20 | 
            +
                  #
         | 
| 18 21 | 
             
                  # @example
         | 
| 19 22 | 
             
                  #   # bad
         | 
| 20 23 | 
             
                  #   def bar
         | 
| @@ -68,12 +71,13 @@ module RuboCop | |
| 68 71 |  | 
| 69 72 | 
             
                    def_node_matcher :delegate?, <<~PATTERN
         | 
| 70 73 | 
             
                      (def _method_name _args
         | 
| 71 | 
            -
                        (send {(send nil? _) (self)} _ ...))
         | 
| 74 | 
            +
                        (send {(send nil? _) (self) (send (self) :class) ({cvar gvar ivar} _) (const _ _)} _ ...))
         | 
| 72 75 | 
             
                    PATTERN
         | 
| 73 76 |  | 
| 74 77 | 
             
                    def on_def(node)
         | 
| 75 78 | 
             
                      return unless trivial_delegate?(node)
         | 
| 76 79 | 
             
                      return if private_or_protected_delegation(node)
         | 
| 80 | 
            +
                      return if module_function_declared?(node)
         | 
| 77 81 |  | 
| 78 82 | 
             
                      register_offense(node)
         | 
| 79 83 | 
             
                    end
         | 
| @@ -82,15 +86,40 @@ module RuboCop | |
| 82 86 |  | 
| 83 87 | 
             
                    def register_offense(node)
         | 
| 84 88 | 
             
                      add_offense(node.loc.keyword) do |corrector|
         | 
| 85 | 
            -
                         | 
| 89 | 
            +
                        receiver = determine_register_offense_receiver(node.body.receiver)
         | 
| 90 | 
            +
                        delegation = build_delegation(node, receiver)
         | 
| 86 91 |  | 
| 87 | 
            -
                         | 
| 92 | 
            +
                        corrector.replace(node, delegation)
         | 
| 93 | 
            +
                      end
         | 
| 94 | 
            +
                    end
         | 
| 88 95 |  | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 96 | 
            +
                    def determine_register_offense_receiver(receiver)
         | 
| 97 | 
            +
                      case receiver.type
         | 
| 98 | 
            +
                      when :self
         | 
| 99 | 
            +
                        'self'
         | 
| 100 | 
            +
                      when :const
         | 
| 101 | 
            +
                        full_name = full_const_name(receiver)
         | 
| 102 | 
            +
                        full_name.include?('::') ? ":'#{full_name}'" : ":#{full_name}"
         | 
| 103 | 
            +
                      when :cvar, :gvar, :ivar
         | 
| 104 | 
            +
                        ":#{receiver.source}"
         | 
| 105 | 
            +
                      else
         | 
| 106 | 
            +
                        ":#{receiver.method_name}"
         | 
| 107 | 
            +
                      end
         | 
| 108 | 
            +
                    end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                    def build_delegation(node, receiver)
         | 
| 111 | 
            +
                      delegation = ["delegate :#{node.body.method_name}", "to: #{receiver}"]
         | 
| 112 | 
            +
                      delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
         | 
| 113 | 
            +
                      delegation.join(', ')
         | 
| 114 | 
            +
                    end
         | 
| 91 115 |  | 
| 92 | 
            -
             | 
| 116 | 
            +
                    def full_const_name(node)
         | 
| 117 | 
            +
                      return unless node.const_type?
         | 
| 118 | 
            +
                      unless node.namespace
         | 
| 119 | 
            +
                        return node.absolute? ? "::#{node.source}" : node.source
         | 
| 93 120 | 
             
                      end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                      "#{full_const_name(node.namespace)}::#{node.short_name}"
         | 
| 94 123 | 
             
                    end
         | 
| 95 124 |  | 
| 96 125 | 
             
                    def trivial_delegate?(def_node)
         | 
| @@ -120,13 +149,30 @@ module RuboCop | |
| 120 149 | 
             
                    def prefixed_method_name(body)
         | 
| 121 150 | 
             
                      return '' if body.receiver.self_type?
         | 
| 122 151 |  | 
| 123 | 
            -
                      [body.receiver | 
| 152 | 
            +
                      [determine_prefixed_method_receiver_name(body.receiver), body.method_name].join('_').to_sym
         | 
| 153 | 
            +
                    end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                    def determine_prefixed_method_receiver_name(receiver)
         | 
| 156 | 
            +
                      case receiver.type
         | 
| 157 | 
            +
                      when :cvar, :gvar, :ivar
         | 
| 158 | 
            +
                        receiver.source
         | 
| 159 | 
            +
                      when :const
         | 
| 160 | 
            +
                        full_const_name(receiver)
         | 
| 161 | 
            +
                      else
         | 
| 162 | 
            +
                        receiver.method_name.to_s
         | 
| 163 | 
            +
                      end
         | 
| 124 164 | 
             
                    end
         | 
| 125 165 |  | 
| 126 166 | 
             
                    def private_or_protected_delegation(node)
         | 
| 127 167 | 
             
                      private_or_protected_inline(node) || node_visibility(node) != :public
         | 
| 128 168 | 
             
                    end
         | 
| 129 169 |  | 
| 170 | 
            +
                    def module_function_declared?(node)
         | 
| 171 | 
            +
                      node.each_ancestor(:module, :begin).any? do |ancestor|
         | 
| 172 | 
            +
                        ancestor.children.any? { |child| child.send_type? && child.method?(:module_function) }
         | 
| 173 | 
            +
                      end
         | 
| 174 | 
            +
                    end
         | 
| 175 | 
            +
             | 
| 130 176 | 
             
                    def private_or_protected_inline(node)
         | 
| 131 177 | 
             
                      processed_source[node.first_line - 1].strip.match?(/\A(private )|(protected )/)
         | 
| 132 178 | 
             
                    end
         | 
| @@ -63,11 +63,15 @@ module RuboCop | |
| 63 63 | 
             
                    private
         | 
| 64 64 |  | 
| 65 65 | 
             
                    def register_offense(name, nodes, message_template)
         | 
| 66 | 
            -
                      nodes. | 
| 67 | 
            -
                        add_offense(node, message: format(message_template, name: name)) do |corrector|
         | 
| 68 | 
            -
                          next if same_line?(nodes.last, node)
         | 
| 66 | 
            +
                      last_node = nodes.last
         | 
| 69 67 |  | 
| 70 | 
            -
             | 
| 68 | 
            +
                      nodes.each_with_index do |node, index|
         | 
| 69 | 
            +
                        add_offense(node, message: format(message_template, name: name)) do |corrector|
         | 
| 70 | 
            +
                          if index.zero?
         | 
| 71 | 
            +
                            corrector.replace(node, last_node.source)
         | 
| 72 | 
            +
                          else
         | 
| 73 | 
            +
                            corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
         | 
| 74 | 
            +
                          end
         | 
| 71 75 | 
             
                        end
         | 
| 72 76 | 
             
                      end
         | 
| 73 77 | 
             
                    end
         | 
| @@ -45,12 +45,10 @@ module RuboCop | |
| 45 45 | 
             
                      return if node.parent&.block_type?
         | 
| 46 46 |  | 
| 47 47 | 
             
                      interpolated_string_passed_to_debug(node) do |arguments|
         | 
| 48 | 
            -
                        message = format(MSG)
         | 
| 49 | 
            -
             | 
| 50 48 | 
             
                        range = replacement_range(node)
         | 
| 51 49 | 
             
                        replacement = replacement_source(node, arguments)
         | 
| 52 50 |  | 
| 53 | 
            -
                        add_offense(range | 
| 51 | 
            +
                        add_offense(range) do |corrector|
         | 
| 54 52 | 
             
                          corrector.replace(range, replacement)
         | 
| 55 53 | 
             
                        end
         | 
| 56 54 | 
             
                      end
         | 
| @@ -6,6 +6,9 @@ module RuboCop | |
| 6 6 | 
             
                  # Identifies usages of file path joining process to use `Rails.root.join` clause.
         | 
| 7 7 | 
             
                  # It is used to add uniformity when joining paths.
         | 
| 8 8 | 
             
                  #
         | 
| 9 | 
            +
                  # NOTE: This cop ignores leading slashes in string literal arguments for `Rails.root.join`
         | 
| 10 | 
            +
                  #       and multiple slashes in string literal arguments for `Rails.root.join` and `File.join`.
         | 
| 11 | 
            +
                  #
         | 
| 9 12 | 
             
                  # @example EnforcedStyle: slashes (default)
         | 
| 10 13 | 
             
                  #   # bad
         | 
| 11 14 | 
             
                  #   Rails.root.join('app', 'models', 'goober')
         | 
| @@ -97,7 +100,7 @@ module RuboCop | |
| 97 100 |  | 
| 98 101 | 
             
                    def check_for_file_join_with_rails_root(node)
         | 
| 99 102 | 
             
                      return unless file_join_nodes?(node)
         | 
| 100 | 
            -
                      return unless node.arguments | 
| 103 | 
            +
                      return unless valid_arguments_for_file_join_with_rails_root?(node.arguments)
         | 
| 101 104 |  | 
| 102 105 | 
             
                      register_offense(node, require_to_s: true) do |corrector|
         | 
| 103 106 | 
             
                        autocorrect_file_join(corrector, node) unless node.first_argument.array_type?
         | 
| @@ -108,8 +111,7 @@ module RuboCop | |
| 108 111 | 
             
                      return unless style == :slashes
         | 
| 109 112 | 
             
                      return unless rails_root_nodes?(node)
         | 
| 110 113 | 
             
                      return unless rails_root_join_nodes?(node)
         | 
| 111 | 
            -
                      return unless node.arguments | 
| 112 | 
            -
                      return unless node.arguments.all?(&:str_type?)
         | 
| 114 | 
            +
                      return unless valid_string_arguments_for_rails_root_join?(node.arguments)
         | 
| 113 115 |  | 
| 114 116 | 
             
                      register_offense(node, require_to_s: false) do |corrector|
         | 
| 115 117 | 
             
                        autocorrect_rails_root_join_with_string_arguments(corrector, node)
         | 
| @@ -120,15 +122,42 @@ module RuboCop | |
| 120 122 | 
             
                      return unless style == :arguments
         | 
| 121 123 | 
             
                      return unless rails_root_nodes?(node)
         | 
| 122 124 | 
             
                      return unless rails_root_join_nodes?(node)
         | 
| 123 | 
            -
                      return unless node.arguments | 
| 125 | 
            +
                      return unless valid_slash_separated_path_for_rails_root_join?(node.arguments)
         | 
| 124 126 |  | 
| 125 127 | 
             
                      register_offense(node, require_to_s: false) do |corrector|
         | 
| 126 128 | 
             
                        autocorrect_rails_root_join_with_slash_separated_path(corrector, node)
         | 
| 127 129 | 
             
                      end
         | 
| 128 130 | 
             
                    end
         | 
| 129 131 |  | 
| 130 | 
            -
                    def  | 
| 131 | 
            -
                       | 
| 132 | 
            +
                    def valid_arguments_for_file_join_with_rails_root?(arguments)
         | 
| 133 | 
            +
                      return false unless arguments.any? { |arg| rails_root_nodes?(arg) }
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                      arguments.none? { |arg| arg.variable? || arg.const_type? || string_contains_multiple_slashes?(arg) }
         | 
| 136 | 
            +
                    end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                    def valid_string_arguments_for_rails_root_join?(arguments)
         | 
| 139 | 
            +
                      return false unless arguments.size > 1
         | 
| 140 | 
            +
                      return false unless arguments.all?(&:str_type?)
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                      arguments.none? { |arg| string_with_leading_slash?(arg) || string_contains_multiple_slashes?(arg) }
         | 
| 143 | 
            +
                    end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                    def valid_slash_separated_path_for_rails_root_join?(arguments)
         | 
| 146 | 
            +
                      return false unless arguments.any? { |arg| string_contains_slash?(arg) }
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                      arguments.none? { |arg| string_with_leading_slash?(arg) || string_contains_multiple_slashes?(arg) }
         | 
| 149 | 
            +
                    end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                    def string_contains_slash?(node)
         | 
| 152 | 
            +
                      node.str_type? && node.value.include?(File::SEPARATOR)
         | 
| 153 | 
            +
                    end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                    def string_contains_multiple_slashes?(node)
         | 
| 156 | 
            +
                      node.str_type? && node.value.include?('//')
         | 
| 157 | 
            +
                    end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                    def string_with_leading_slash?(node)
         | 
| 160 | 
            +
                      node.str_type? && node.value.start_with?(File::SEPARATOR)
         | 
| 132 161 | 
             
                    end
         | 
| 133 162 |  | 
| 134 163 | 
             
                    def register_offense(node, require_to_s:, &block)
         | 
| @@ -173,7 +202,17 @@ module RuboCop | |
| 173 202 | 
             
                    end
         | 
| 174 203 |  | 
| 175 204 | 
             
                    def autocorrect_file_join(corrector, node)
         | 
| 205 | 
            +
                      replace_receiver_with_rails_root(corrector, node)
         | 
| 206 | 
            +
                      remove_first_argument_with_comma(corrector, node)
         | 
| 207 | 
            +
                      process_arguments(corrector, node.arguments)
         | 
| 208 | 
            +
                      append_to_string_conversion(corrector, node)
         | 
| 209 | 
            +
                    end
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                    def replace_receiver_with_rails_root(corrector, node)
         | 
| 176 212 | 
             
                      corrector.replace(node.receiver, 'Rails.root')
         | 
| 213 | 
            +
                    end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                    def remove_first_argument_with_comma(corrector, node)
         | 
| 177 216 | 
             
                      corrector.remove(
         | 
| 178 217 | 
             
                        range_with_surrounding_space(
         | 
| 179 218 | 
             
                          range_with_surrounding_comma(
         | 
| @@ -183,9 +222,19 @@ module RuboCop | |
| 183 222 | 
             
                          side: :right
         | 
| 184 223 | 
             
                        )
         | 
| 185 224 | 
             
                      )
         | 
| 186 | 
            -
             | 
| 187 | 
            -
             | 
| 225 | 
            +
                    end
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                    def process_arguments(corrector, arguments)
         | 
| 228 | 
            +
                      arguments.each do |argument|
         | 
| 229 | 
            +
                        if argument.str_type?
         | 
| 230 | 
            +
                          corrector.replace(argument, argument.value.delete_prefix('/').inspect)
         | 
| 231 | 
            +
                        elsif argument.array_type?
         | 
| 232 | 
            +
                          corrector.replace(argument, "*#{argument.source}")
         | 
| 233 | 
            +
                        end
         | 
| 188 234 | 
             
                      end
         | 
| 235 | 
            +
                    end
         | 
| 236 | 
            +
             | 
| 237 | 
            +
                    def append_to_string_conversion(corrector, node)
         | 
| 189 238 | 
             
                      corrector.insert_after(node, '.to_s')
         | 
| 190 239 | 
             
                    end
         | 
| 191 240 |  | 
| @@ -206,7 +255,7 @@ module RuboCop | |
| 206 255 |  | 
| 207 256 | 
             
                    def autocorrect_rails_root_join_with_slash_separated_path(corrector, node)
         | 
| 208 257 | 
             
                      node.arguments.each do |argument|
         | 
| 209 | 
            -
                        next unless  | 
| 258 | 
            +
                        next unless string_contains_slash?(argument)
         | 
| 210 259 |  | 
| 211 260 | 
             
                        index = argument.source.index(File::SEPARATOR)
         | 
| 212 261 | 
             
                        rest = inner_range_of(argument).adjust(begin_pos: index - 1)
         | 
| @@ -37,6 +37,9 @@ module RuboCop | |
| 37 37 | 
             
                        (numblock
         | 
| 38 38 | 
             
                          (call _ :to_h) $1
         | 
| 39 39 | 
             
                          (array $_ (lvar :_1)))
         | 
| 40 | 
            +
                        (itblock
         | 
| 41 | 
            +
                          (call _ :to_h) $:it
         | 
| 42 | 
            +
                          (array $_ (lvar :it)))
         | 
| 40 43 | 
             
                      }
         | 
| 41 44 | 
             
                    PATTERN
         | 
| 42 45 |  | 
| @@ -50,6 +53,9 @@ module RuboCop | |
| 50 53 | 
             
                          (numblock
         | 
| 51 54 | 
             
                            (call _ {:map :collect}) $1
         | 
| 52 55 | 
             
                            (array $_ (lvar :_1)))
         | 
| 56 | 
            +
                          (itblock
         | 
| 57 | 
            +
                            (call _ {:map :collect}) $:it
         | 
| 58 | 
            +
                            (array $_ (lvar :it)))
         | 
| 53 59 | 
             
                        }
         | 
| 54 60 | 
             
                        :to_h)
         | 
| 55 61 | 
             
                    PATTERN
         | 
| @@ -66,6 +72,9 @@ module RuboCop | |
| 66 72 | 
             
                          (numblock
         | 
| 67 73 | 
             
                            (call _ {:map :collect}) $1
         | 
| 68 74 | 
             
                            (array $_ (lvar :_1)))
         | 
| 75 | 
            +
                          (itblock
         | 
| 76 | 
            +
                            (call _ {:map :collect}) $:it
         | 
| 77 | 
            +
                            (array $_ (lvar :it)))
         | 
| 69 78 | 
             
                        }
         | 
| 70 79 | 
             
                      )
         | 
| 71 80 | 
             
                    PATTERN
         |