Merge pull request #9476 from Homebrew/dependabot/bundler/Library/Homebrew/rubocop-rails-2.9.0
build(deps): bump rubocop-rails from 2.8.1 to 2.9.0 in /Library/Homebrew
This commit is contained in:
		
						commit
						0d7ff8fa98
					
				@ -173,6 +173,8 @@ Rails:
 | 
			
		||||
    - "Homebrew/rubocops/**/*"
 | 
			
		||||
 | 
			
		||||
# Skip these as they only apply to actual Rails and not our ActiveSupport usage.
 | 
			
		||||
Rails/ArelStar:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
Rails/Date:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
Rails/Delegate:
 | 
			
		||||
 | 
			
		||||
@ -116,10 +116,10 @@ GEM
 | 
			
		||||
    rubocop-performance (1.9.1)
 | 
			
		||||
      rubocop (>= 0.90.0, < 2.0)
 | 
			
		||||
      rubocop-ast (>= 0.4.0)
 | 
			
		||||
    rubocop-rails (2.8.1)
 | 
			
		||||
    rubocop-rails (2.9.0)
 | 
			
		||||
      activesupport (>= 4.2.0)
 | 
			
		||||
      rack (>= 1.1)
 | 
			
		||||
      rubocop (>= 0.87.0)
 | 
			
		||||
      rubocop (>= 0.90.0, < 2.0)
 | 
			
		||||
    rubocop-rspec (2.0.1)
 | 
			
		||||
      rubocop (~> 1.0)
 | 
			
		||||
      rubocop-ast (>= 1.1.0)
 | 
			
		||||
@ -159,7 +159,7 @@ GEM
 | 
			
		||||
    unf_ext (0.0.7.7)
 | 
			
		||||
    unicode-display_width (1.7.0)
 | 
			
		||||
    webrobots (0.1.2)
 | 
			
		||||
    zeitwerk (2.4.1)
 | 
			
		||||
    zeitwerk (2.4.2)
 | 
			
		||||
 | 
			
		||||
PLATFORMS
 | 
			
		||||
  ruby
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -7086,8 +7086,6 @@ end
 | 
			
		||||
class Errno::EBADRPC
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Errno::ECAPMODE = Errno::NOERROR
 | 
			
		||||
 | 
			
		||||
Errno::EDEADLOCK = Errno::NOERROR
 | 
			
		||||
 | 
			
		||||
class Errno::EDEVERR
 | 
			
		||||
@ -7108,13 +7106,6 @@ end
 | 
			
		||||
 | 
			
		||||
Errno::EIPSEC = Errno::NOERROR
 | 
			
		||||
 | 
			
		||||
class Errno::ELAST
 | 
			
		||||
  Errno = ::T.let(nil, ::T.untyped)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class Errno::ELAST
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class Errno::ENEEDAUTH
 | 
			
		||||
  Errno = ::T.let(nil, ::T.untyped)
 | 
			
		||||
end
 | 
			
		||||
@ -7136,8 +7127,6 @@ end
 | 
			
		||||
class Errno::ENOPOLICY
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Errno::ENOTCAPABLE = Errno::NOERROR
 | 
			
		||||
 | 
			
		||||
class Errno::ENOTSUP
 | 
			
		||||
  Errno = ::T.let(nil, ::T.untyped)
 | 
			
		||||
end
 | 
			
		||||
@ -7180,7 +7169,12 @@ end
 | 
			
		||||
class Errno::EPWROFF
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
Errno::EQFULL = Errno::ELAST
 | 
			
		||||
class Errno::EQFULL
 | 
			
		||||
  Errno = ::T.let(nil, ::T.untyped)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class Errno::EQFULL
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class Errno::ERPCMISMATCH
 | 
			
		||||
  Errno = ::T.let(nil, ::T.untyped)
 | 
			
		||||
@ -13519,6 +13513,7 @@ class Object
 | 
			
		||||
  def to_query(key); end
 | 
			
		||||
 | 
			
		||||
  def to_yaml(options=T.unsafe(nil)); end
 | 
			
		||||
  APPLE_GEM_HOME = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  ARGF = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  ARGV = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  BUG_REPORTS_URL = ::T.let(nil, ::T.untyped)
 | 
			
		||||
@ -13580,6 +13575,8 @@ class Object
 | 
			
		||||
  RUBY_DESCRIPTION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  RUBY_ENGINE = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  RUBY_ENGINE_VERSION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  RUBY_FRAMEWORK = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  RUBY_FRAMEWORK_VERSION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  RUBY_PATCHLEVEL = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  RUBY_PATH = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  RUBY_PLATFORM = ::T.let(nil, ::T.untyped)
 | 
			
		||||
@ -13636,11 +13633,7 @@ class OpenSSL::KDF::KDFError
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
module OpenSSL::KDF
 | 
			
		||||
  def self.hkdf(*_); end
 | 
			
		||||
 | 
			
		||||
  def self.pbkdf2_hmac(*_); end
 | 
			
		||||
 | 
			
		||||
  def self.scrypt(*_); end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class OpenSSL::OCSP::Request
 | 
			
		||||
@ -13649,29 +13642,20 @@ end
 | 
			
		||||
 | 
			
		||||
OpenSSL::PKCS7::Signer = OpenSSL::PKCS7::SignerInfo
 | 
			
		||||
 | 
			
		||||
class OpenSSL::PKey::EC
 | 
			
		||||
  EXPLICIT_CURVE = ::T.let(nil, ::T.untyped)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class OpenSSL::PKey::EC::Point
 | 
			
		||||
  def to_octet_string(_); end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
module OpenSSL::SSL
 | 
			
		||||
  OP_ALLOW_NO_DHE_KEX = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  OP_CRYPTOPRO_TLSEXT_BUG = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  OP_LEGACY_SERVER_CONNECT = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  OP_NO_ENCRYPT_THEN_MAC = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  OP_NO_RENEGOTIATION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  OP_NO_TLSv1_3 = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  OP_SAFARI_ECDHE_ECDSA_BUG = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  OP_TLSEXT_PADDING = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  SSL2_VERSION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  SSL3_VERSION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  TLS1_1_VERSION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  TLS1_2_VERSION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  TLS1_3_VERSION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  TLS1_VERSION = ::T.let(nil, ::T.untyped)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -13686,8 +13670,6 @@ class OpenSSL::SSL::SSLContext
 | 
			
		||||
 | 
			
		||||
  def alpn_select_cb=(alpn_select_cb); end
 | 
			
		||||
 | 
			
		||||
  def enable_fallback_scsv(); end
 | 
			
		||||
 | 
			
		||||
  def max_version=(version); end
 | 
			
		||||
 | 
			
		||||
  def min_version=(version); end
 | 
			
		||||
@ -30212,6 +30194,7 @@ class Socket
 | 
			
		||||
  IPV6_PATHMTU = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IPV6_RECVPATHMTU = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IPV6_USE_MIN_MTU = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IP_DONTFRAG = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IP_PORTRANGE = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IP_RECVDSTADDR = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IP_RECVIF = ::T.let(nil, ::T.untyped)
 | 
			
		||||
@ -30303,6 +30286,7 @@ module Socket::Constants
 | 
			
		||||
  IPV6_PATHMTU = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IPV6_RECVPATHMTU = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IPV6_USE_MIN_MTU = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IP_DONTFRAG = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IP_PORTRANGE = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IP_RECVDSTADDR = ::T.let(nil, ::T.untyped)
 | 
			
		||||
  IP_RECVIF = ::T.let(nil, ::T.untyped)
 | 
			
		||||
@ -32679,6 +32663,10 @@ class Zeitwerk::Loader
 | 
			
		||||
 | 
			
		||||
  def mutex2(); end
 | 
			
		||||
 | 
			
		||||
  def on_load(cpath, &block); end
 | 
			
		||||
 | 
			
		||||
  def on_load_callbacks(); end
 | 
			
		||||
 | 
			
		||||
  def preload(*paths); end
 | 
			
		||||
 | 
			
		||||
  def preloads(); end
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/i18n-1.8.5/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/minitest-5.14.2/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thread_safe-0.3.6/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tzinfo-1.2.8/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/zeitwerk-2.4.1/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/zeitwerk-2.4.2/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/activesupport-6.0.3.4/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ast-2.4.1/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/bindata-2.4.8/lib"
 | 
			
		||||
@ -79,7 +79,7 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-progressbar-1.10
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-1.7.0/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-1.3.1/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.9.1/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rails-2.8.1/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rails-2.9.0/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-2.0.1/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-sorbet-0.5.1/lib"
 | 
			
		||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.5.0/lib"
 | 
			
		||||
 | 
			
		||||
@ -98,6 +98,12 @@ Rails/ApplicationRecord:
 | 
			
		||||
  VersionAdded: '0.49'
 | 
			
		||||
  VersionChanged: '2.5'
 | 
			
		||||
 | 
			
		||||
Rails/ArelStar:
 | 
			
		||||
  Description: 'Enforces `Arel.star` instead of `"*"` for expanded columns.'
 | 
			
		||||
  Enabled: true
 | 
			
		||||
  SafeAutoCorrect: false
 | 
			
		||||
  VersionAdded: '2.9'
 | 
			
		||||
 | 
			
		||||
Rails/AssertNot:
 | 
			
		||||
  Description: 'Use `assert_not` instead of `assert !`.'
 | 
			
		||||
  Enabled: true
 | 
			
		||||
@ -105,6 +111,13 @@ Rails/AssertNot:
 | 
			
		||||
  Include:
 | 
			
		||||
    - '**/test/**/*'
 | 
			
		||||
 | 
			
		||||
Rails/AttributeDefaultBlockValue:
 | 
			
		||||
  Description: 'Pass method call in block for attribute option `default`.'
 | 
			
		||||
  Enabled: pending
 | 
			
		||||
  VersionAdded: '2.9'
 | 
			
		||||
  Include:
 | 
			
		||||
    - 'models/**/*'
 | 
			
		||||
 | 
			
		||||
Rails/BelongsTo:
 | 
			
		||||
  Description: >-
 | 
			
		||||
                  Use `optional: true` instead of `required: false` for
 | 
			
		||||
@ -269,8 +282,15 @@ Rails/FindEach:
 | 
			
		||||
  StyleGuide: 'https://rails.rubystyle.guide#find-each'
 | 
			
		||||
  Enabled: true
 | 
			
		||||
  VersionAdded: '0.30'
 | 
			
		||||
  VersionChanged: '2.9'
 | 
			
		||||
  Include:
 | 
			
		||||
    - app/models/**/*.rb
 | 
			
		||||
  IgnoredMethods:
 | 
			
		||||
    # Methods that don't work well with `find_each`.
 | 
			
		||||
    - order
 | 
			
		||||
    - limit
 | 
			
		||||
    - select
 | 
			
		||||
    - lock
 | 
			
		||||
 | 
			
		||||
Rails/HasAndBelongsToMany:
 | 
			
		||||
  Description: 'Prefer has_many :through to has_and_belongs_to_many.'
 | 
			
		||||
@ -387,7 +407,9 @@ Rails/NegateInclude:
 | 
			
		||||
  Description: 'Prefer `collection.exclude?(obj)` over `!collection.include?(obj)`.'
 | 
			
		||||
  StyleGuide: 'https://rails.rubystyle.guide#exclude'
 | 
			
		||||
  Enabled: 'pending'
 | 
			
		||||
  Safe: false
 | 
			
		||||
  VersionAdded: '2.7'
 | 
			
		||||
  VersionChanged: '2.9'
 | 
			
		||||
 | 
			
		||||
Rails/NotNullColumn:
 | 
			
		||||
  Description: 'Do not add a NOT NULL column without a default value.'
 | 
			
		||||
@ -653,6 +675,10 @@ Rails/SquishedSQLHeredocs:
 | 
			
		||||
  StyleGuide: 'https://rails.rubystyle.guide/#squished-heredocs'
 | 
			
		||||
  Enabled: 'pending'
 | 
			
		||||
  VersionAdded: '2.8'
 | 
			
		||||
  VersionChanged: '2.9'
 | 
			
		||||
  # Some SQL syntax (e.g. PostgreSQL comments and functions) requires newlines
 | 
			
		||||
  # to be preserved in order to work, thus auto-correction is not safe.
 | 
			
		||||
  SafeAutoCorrect: false
 | 
			
		||||
 | 
			
		||||
Rails/TimeZone:
 | 
			
		||||
  Description: 'Checks the correct usage of time zone aware methods.'
 | 
			
		||||
@ -705,6 +731,12 @@ Rails/Validation:
 | 
			
		||||
  Include:
 | 
			
		||||
    - app/models/**/*.rb
 | 
			
		||||
 | 
			
		||||
Rails/WhereEquals:
 | 
			
		||||
  Description: 'Pass conditions to `where` as a hash instead of manually constructing SQL.'
 | 
			
		||||
  StyleGuide: 'https://rails.rubystyle.guide/#hash-conditions'
 | 
			
		||||
  Enabled: 'pending'
 | 
			
		||||
  VersionAdded: '2.9'
 | 
			
		||||
 | 
			
		||||
Rails/WhereExists:
 | 
			
		||||
  Description: 'Prefer `exists?(...)` over `where(...).exists?`.'
 | 
			
		||||
  Enabled: 'pending'
 | 
			
		||||
@ -717,7 +749,7 @@ Rails/WhereExists:
 | 
			
		||||
 | 
			
		||||
Rails/WhereNot:
 | 
			
		||||
  Description: 'Use `where.not(...)` instead of manually constructing negated SQL in `where`.'
 | 
			
		||||
  StyleGuide: 'https://rails.rubystyle.guide/#where-not'
 | 
			
		||||
  StyleGuide: 'https://rails.rubystyle.guide/#hash-conditions'
 | 
			
		||||
  Enabled: 'pending'
 | 
			
		||||
  VersionAdded: '2.8'
 | 
			
		||||
 | 
			
		||||
@ -35,10 +35,11 @@ module RuboCop
 | 
			
		||||
        table_name = find_set_table_name(class_node).to_a.last&.first_argument
 | 
			
		||||
        return table_name.value.to_s if table_name
 | 
			
		||||
 | 
			
		||||
        namespaces = class_node.each_ancestor(:class, :module)
 | 
			
		||||
        [class_node, *namespaces]
 | 
			
		||||
        class_nodes = class_node.defined_module.each_node
 | 
			
		||||
        namespaces = class_node.each_ancestor(:class, :module).map(&:identifier)
 | 
			
		||||
        [*class_nodes, *namespaces]
 | 
			
		||||
          .reverse
 | 
			
		||||
          .map { |klass| klass.identifier.children[1] }.join('_')
 | 
			
		||||
          .map { |node| node.children[1] }.join('_')
 | 
			
		||||
          .tableize
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,40 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    # Common functionality for enforcing a specific superclass.
 | 
			
		||||
    module EnforceSuperclass
 | 
			
		||||
      def self.included(base)
 | 
			
		||||
        base.def_node_matcher :class_definition, <<~PATTERN
 | 
			
		||||
          (class (const _ !:#{base::SUPERCLASS}) #{base::BASE_PATTERN} ...)
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        base.def_node_matcher :class_new_definition, <<~PATTERN
 | 
			
		||||
          [!^(casgn {nil? cbase} :#{base::SUPERCLASS} ...)
 | 
			
		||||
           !^^(casgn {nil? cbase} :#{base::SUPERCLASS} (block ...))
 | 
			
		||||
           (send (const {nil? cbase} :Class) :new #{base::BASE_PATTERN})]
 | 
			
		||||
        PATTERN
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def on_class(node)
 | 
			
		||||
        class_definition(node) do
 | 
			
		||||
          register_offense(node.children[1])
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def on_send(node)
 | 
			
		||||
        class_new_definition(node) do
 | 
			
		||||
          register_offense(node.children.last)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      private
 | 
			
		||||
 | 
			
		||||
      def register_offense(offense_node)
 | 
			
		||||
        add_offense(offense_node) do |corrector|
 | 
			
		||||
          corrector.replace(offense_node.source_range, self.class::SUPERCLASS)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -4,6 +4,8 @@ module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    # Common functionality for Rails/IndexBy and Rails/IndexWith
 | 
			
		||||
    module IndexMethod # rubocop:disable Metrics/ModuleLength
 | 
			
		||||
      RESTRICT_ON_SEND = %i[each_with_object to_h map collect []].freeze
 | 
			
		||||
 | 
			
		||||
      def on_block(node)
 | 
			
		||||
        on_bad_each_with_object(node) do |*match|
 | 
			
		||||
          handle_possible_offense(node, match, 'each_with_object')
 | 
			
		||||
@ -32,13 +34,6 @@ module RuboCop
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def autocorrect(node)
 | 
			
		||||
        lambda do |corrector|
 | 
			
		||||
          correction = prepare_correction(node)
 | 
			
		||||
          execute_correction(corrector, node, correction)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      private
 | 
			
		||||
 | 
			
		||||
      # @abstract Implemented with `def_node_matcher`
 | 
			
		||||
@ -67,9 +62,11 @@ module RuboCop
 | 
			
		||||
        return if captures.noop_transformation?
 | 
			
		||||
 | 
			
		||||
        add_offense(
 | 
			
		||||
          node,
 | 
			
		||||
          message: "Prefer `#{new_method_name}` over `#{match_desc}`."
 | 
			
		||||
        )
 | 
			
		||||
          node, message: "Prefer `#{new_method_name}` over `#{match_desc}`."
 | 
			
		||||
        ) do |corrector|
 | 
			
		||||
          correction = prepare_correction(node)
 | 
			
		||||
          execute_correction(corrector, node, correction)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def extract_captures(match)
 | 
			
		||||
@ -119,7 +116,7 @@ module RuboCop
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # Internal helper class to hold autocorrect data
 | 
			
		||||
      Autocorrection = Struct.new(:match, :block_node, :leading, :trailing) do # rubocop:disable Metrics/BlockLength
 | 
			
		||||
      Autocorrection = Struct.new(:match, :block_node, :leading, :trailing) do
 | 
			
		||||
        def self.from_each_with_object(node, match)
 | 
			
		||||
          new(match, node, 0, 0)
 | 
			
		||||
        end
 | 
			
		||||
@ -29,8 +29,9 @@ module RuboCop
 | 
			
		||||
      #   after_filter :do_stuff
 | 
			
		||||
      #   append_around_filter :do_stuff
 | 
			
		||||
      #   skip_after_filter :do_stuff
 | 
			
		||||
      class ActionFilter < Cop
 | 
			
		||||
      class ActionFilter < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
 | 
			
		||||
 | 
			
		||||
@ -66,6 +67,8 @@ module RuboCop
 | 
			
		||||
          skip_action_callback
 | 
			
		||||
        ].freeze
 | 
			
		||||
 | 
			
		||||
        RESTRICT_ON_SEND = FILTER_METHODS + ACTION_METHODS
 | 
			
		||||
 | 
			
		||||
        def on_block(node)
 | 
			
		||||
          check_method_node(node.send_node)
 | 
			
		||||
        end
 | 
			
		||||
@ -74,24 +77,17 @@ module RuboCop
 | 
			
		||||
          check_method_node(node) unless node.receiver
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(node.loc.selector,
 | 
			
		||||
                              preferred_method(node.loc.selector.source).to_s)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def check_method_node(node)
 | 
			
		||||
          return unless bad_methods.include?(node.method_name)
 | 
			
		||||
          method_name = node.method_name
 | 
			
		||||
          return unless bad_methods.include?(method_name)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
        end
 | 
			
		||||
          message = format(MSG, prefer: preferred_method(method_name), current: method_name)
 | 
			
		||||
 | 
			
		||||
        def message(node)
 | 
			
		||||
          format(MSG, prefer: preferred_method(node.method_name),
 | 
			
		||||
                      current: node.method_name)
 | 
			
		||||
          add_offense(node.loc.selector, message: message) do |corrector|
 | 
			
		||||
            corrector.replace(node.loc.selector, preferred_method(node.loc.selector.source))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def bad_methods
 | 
			
		||||
@ -12,7 +12,9 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   #good
 | 
			
		||||
      #   Book.update!(author: 'Alice')
 | 
			
		||||
      class ActiveRecordAliases < Cop
 | 
			
		||||
      class ActiveRecordAliases < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
 | 
			
		||||
 | 
			
		||||
        ALIASES = {
 | 
			
		||||
@ -20,28 +22,22 @@ module RuboCop
 | 
			
		||||
          update_attributes!: :update!
 | 
			
		||||
        }.freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          ALIASES.each do |bad, good|
 | 
			
		||||
            next unless node.method?(bad)
 | 
			
		||||
        RESTRICT_ON_SEND = ALIASES.keys.freeze
 | 
			
		||||
 | 
			
		||||
            add_offense(node,
 | 
			
		||||
                        message: format(MSG, prefer: good, current: bad),
 | 
			
		||||
                        location: :selector,
 | 
			
		||||
                        severity: :warning)
 | 
			
		||||
            break
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          method_name = node.method_name
 | 
			
		||||
          alias_method = ALIASES[method_name]
 | 
			
		||||
 | 
			
		||||
          add_offense(
 | 
			
		||||
            node.loc.selector,
 | 
			
		||||
            message: format(MSG, prefer: alias_method, current: method_name),
 | 
			
		||||
            severity: :warning
 | 
			
		||||
          ) do |corrector|
 | 
			
		||||
            corrector.replace(node.loc.selector, alias_method)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        alias on_csend on_send
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(
 | 
			
		||||
              node.loc.selector,
 | 
			
		||||
              ALIASES[node.method_name].to_s
 | 
			
		||||
            )
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
@ -19,7 +19,9 @@ module RuboCop
 | 
			
		||||
      #     after_commit :after_commit_callback
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      class ActiveRecordCallbacksOrder < Cop
 | 
			
		||||
      class ActiveRecordCallbacksOrder < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = '`%<current>s` is supposed to appear before `%<previous>s`.'
 | 
			
		||||
 | 
			
		||||
        CALLBACKS_IN_ORDER = %i[
 | 
			
		||||
@ -55,17 +57,20 @@ module RuboCop
 | 
			
		||||
            index = CALLBACKS_ORDER_MAP[callback]
 | 
			
		||||
 | 
			
		||||
            if index < previous_index
 | 
			
		||||
              message = format(MSG, current: callback,
 | 
			
		||||
                                    previous: previous_callback)
 | 
			
		||||
              add_offense(node, message: message)
 | 
			
		||||
              message = format(MSG, current: callback, previous: previous_callback)
 | 
			
		||||
              add_offense(node, message: message) do |corrector|
 | 
			
		||||
                autocorrect(corrector, node)
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
            previous_index = index
 | 
			
		||||
            previous_callback = callback
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        # Autocorrect by swapping between two nodes autocorrecting them
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          previous = left_siblings_of(node).reverse_each.find do |sibling|
 | 
			
		||||
            callback?(sibling)
 | 
			
		||||
          end
 | 
			
		||||
@ -73,14 +78,10 @@ module RuboCop
 | 
			
		||||
          current_range = source_range_with_comment(node)
 | 
			
		||||
          previous_range = source_range_with_comment(previous)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.insert_before(previous_range, current_range.source)
 | 
			
		||||
            corrector.remove(current_range)
 | 
			
		||||
          end
 | 
			
		||||
          corrector.insert_before(previous_range, current_range.source)
 | 
			
		||||
          corrector.remove(current_range)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def defined_callbacks(class_node)
 | 
			
		||||
          class_def = class_node.body
 | 
			
		||||
 | 
			
		||||
@ -121,7 +122,7 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
          processed_source.comments_before_line(annotation_line)
 | 
			
		||||
                          .reverse_each do |comment|
 | 
			
		||||
            if comment.location.line == annotation_line
 | 
			
		||||
            if comment.location.line == annotation_line && !inline_comment?(comment)
 | 
			
		||||
              first_comment = comment
 | 
			
		||||
              annotation_line -= 1
 | 
			
		||||
            end
 | 
			
		||||
@ -130,6 +131,10 @@ module RuboCop
 | 
			
		||||
          start_line_position(first_comment || node)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def inline_comment?(comment)
 | 
			
		||||
          !comment_line?(comment.loc.expression.source_line)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def start_line_position(node)
 | 
			
		||||
          buffer.line_range(node.loc.line).begin_pos - 1
 | 
			
		||||
        end
 | 
			
		||||
@ -24,7 +24,7 @@ module RuboCop
 | 
			
		||||
      #     end
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      class ActiveRecordOverride < Cop
 | 
			
		||||
      class ActiveRecordOverride < Base
 | 
			
		||||
        MSG =
 | 
			
		||||
          'Use %<prefer>s callbacks instead of overriding the Active Record ' \
 | 
			
		||||
          'method `%<bad>s`.'
 | 
			
		||||
@ -19,8 +19,11 @@ module RuboCop
 | 
			
		||||
      #   [1, 2, 'a'].append('b')
 | 
			
		||||
      #   [1, 2, 'a'].prepend('b')
 | 
			
		||||
      #
 | 
			
		||||
      class ActiveSupportAliases < Cop
 | 
			
		||||
      class ActiveSupportAliases < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[starts_with? ends_with? append prepend].freeze
 | 
			
		||||
 | 
			
		||||
        ALIASES = {
 | 
			
		||||
          starts_with?: {
 | 
			
		||||
@ -39,30 +42,18 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          ALIASES.each_key do |aliased_method|
 | 
			
		||||
            register_offense(node, aliased_method) if
 | 
			
		||||
              public_send(aliased_method, node)
 | 
			
		||||
            next unless public_send(aliased_method, node)
 | 
			
		||||
 | 
			
		||||
            preferred_method = ALIASES[aliased_method][:original]
 | 
			
		||||
            message = format(MSG, prefer: preferred_method, current: aliased_method)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, message: message) do |corrector|
 | 
			
		||||
              next if append(node)
 | 
			
		||||
 | 
			
		||||
              corrector.replace(node.loc.selector, preferred_method)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          return false if append(node)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            method_name = node.loc.selector.source
 | 
			
		||||
            replacement = ALIASES[method_name.to_sym][:original]
 | 
			
		||||
            corrector.replace(node.loc.selector, replacement.to_s)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def register_offense(node, method_name)
 | 
			
		||||
          add_offense(
 | 
			
		||||
            node,
 | 
			
		||||
            message: format(MSG, prefer: ALIASES[method_name][:original],
 | 
			
		||||
                                 current: method_name)
 | 
			
		||||
          )
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
@ -31,7 +31,7 @@ module RuboCop
 | 
			
		||||
      #   after_create_commit :log_create_action
 | 
			
		||||
      #   after_update_commit :log_update_action
 | 
			
		||||
      #
 | 
			
		||||
      class AfterCommitOverride < Cop
 | 
			
		||||
      class AfterCommitOverride < Base
 | 
			
		||||
        MSG = 'There can only be one `after_*_commit :%<name>s` hook defined for a model.'
 | 
			
		||||
 | 
			
		||||
        AFTER_COMMIT_CALLBACKS = %i[
 | 
			
		||||
@ -16,7 +16,9 @@ module RuboCop
 | 
			
		||||
      #  class MyController < ActionController::Base
 | 
			
		||||
      #    # ...
 | 
			
		||||
      #  end
 | 
			
		||||
      class ApplicationController < Cop
 | 
			
		||||
      class ApplicationController < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Controllers should subclass `ApplicationController`.'
 | 
			
		||||
        SUPERCLASS = 'ApplicationController'
 | 
			
		||||
        BASE_PATTERN = '(const (const nil? :ActionController) :Base)'
 | 
			
		||||
@ -24,12 +26,6 @@ module RuboCop
 | 
			
		||||
        # rubocop:disable Layout/ClassStructure
 | 
			
		||||
        include RuboCop::Cop::EnforceSuperclass
 | 
			
		||||
        # rubocop:enable Layout/ClassStructure
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(node.source_range, self.class::SUPERCLASS)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
@ -16,7 +16,8 @@ module RuboCop
 | 
			
		||||
      #  class Rails4Job < ActiveJob::Base
 | 
			
		||||
      #    # ...
 | 
			
		||||
      #  end
 | 
			
		||||
      class ApplicationJob < Cop
 | 
			
		||||
      class ApplicationJob < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
        extend TargetRailsVersion
 | 
			
		||||
 | 
			
		||||
        minimum_target_rails_version 5.0
 | 
			
		||||
@ -16,7 +16,8 @@ module RuboCop
 | 
			
		||||
      #  class MyMailer < ActionMailer::Base
 | 
			
		||||
      #    # ...
 | 
			
		||||
      #  end
 | 
			
		||||
      class ApplicationMailer < Cop
 | 
			
		||||
      class ApplicationMailer < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
        extend TargetRailsVersion
 | 
			
		||||
 | 
			
		||||
        minimum_target_rails_version 5.0
 | 
			
		||||
@ -28,12 +29,6 @@ module RuboCop
 | 
			
		||||
        # rubocop:disable Layout/ClassStructure
 | 
			
		||||
        include RuboCop::Cop::EnforceSuperclass
 | 
			
		||||
        # rubocop:enable Layout/ClassStructure
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(node.source_range, self.class::SUPERCLASS)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
@ -16,7 +16,8 @@ module RuboCop
 | 
			
		||||
      #  class Rails4Model < ActiveRecord::Base
 | 
			
		||||
      #    # ...
 | 
			
		||||
      #  end
 | 
			
		||||
      class ApplicationRecord < Cop
 | 
			
		||||
      class ApplicationRecord < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
        extend TargetRailsVersion
 | 
			
		||||
 | 
			
		||||
        minimum_target_rails_version 5.0
 | 
			
		||||
@ -28,12 +29,6 @@ module RuboCop
 | 
			
		||||
        # rubocop:disable Layout/ClassStructure
 | 
			
		||||
        include RuboCop::Cop::EnforceSuperclass
 | 
			
		||||
        # rubocop:enable Layout/ClassStructure
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(node.source_range, self.class::SUPERCLASS)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
@ -0,0 +1,41 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    module Rails
 | 
			
		||||
      # This cop prevents usage of `"*"` on an Arel::Table column reference.
 | 
			
		||||
      #
 | 
			
		||||
      # Using `arel_table["*"]` causes the outputted string to be a literal
 | 
			
		||||
      # quoted asterisk (e.g. <tt>`my_model`.`*`</tt>). This causes the
 | 
			
		||||
      # database to look for a column named <tt>`*`</tt> (or `"*"`) as opposed
 | 
			
		||||
      # to expanding the column list as one would likely expect.
 | 
			
		||||
      #
 | 
			
		||||
      # @example
 | 
			
		||||
      #   # bad
 | 
			
		||||
      #   MyTable.arel_table["*"]
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   MyTable.arel_table[Arel.star]
 | 
			
		||||
      #
 | 
			
		||||
      class ArelStar < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `Arel.star` instead of `"*"` for expanded column lists.'
 | 
			
		||||
 | 
			
		||||
        RESTRICT_ON_SEND = %i[[]].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :star_bracket?, <<~PATTERN
 | 
			
		||||
          (send {const (send _ :arel_table)} :[] $(str "*"))
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless (star = star_bracket?(node))
 | 
			
		||||
 | 
			
		||||
          add_offense(star) do |corrector|
 | 
			
		||||
            corrector.replace(star.loc.expression, 'Arel.star')
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -13,23 +13,21 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   assert_not x
 | 
			
		||||
      #
 | 
			
		||||
      class AssertNot < RuboCop::Cop::Cop
 | 
			
		||||
      class AssertNot < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Prefer `assert_not` over `assert !`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[assert].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :offensive?, '(send nil? :assert (send ... :!) ...)'
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          add_offense(node) if offensive?(node)
 | 
			
		||||
        end
 | 
			
		||||
          return unless offensive?(node)
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          expression = node.loc.expression
 | 
			
		||||
          add_offense(node) do |corrector|
 | 
			
		||||
            expression = node.loc.expression
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(
 | 
			
		||||
              expression,
 | 
			
		||||
              corrected_source(expression.source)
 | 
			
		||||
            )
 | 
			
		||||
            corrector.replace(expression, corrected_source(expression.source))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,90 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    module Rails
 | 
			
		||||
      # This cop looks for `attribute` class methods that specify a `:default` option
 | 
			
		||||
      # which value is an array, string literal or method call without a block.
 | 
			
		||||
      # It will accept all other values, such as string, symbol, integer and float literals
 | 
			
		||||
      # as well as constants.
 | 
			
		||||
      #
 | 
			
		||||
      # @example
 | 
			
		||||
      #   # bad
 | 
			
		||||
      #   class User < ApplicationRecord
 | 
			
		||||
      #     attribute :confirmed_at, :datetime, default: Time.zone.now
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   class User < ApplicationRecord
 | 
			
		||||
      #     attribute :confirmed_at, :datetime, default: -> { Time.zone.now }
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      #   # bad
 | 
			
		||||
      #   class User < ApplicationRecord
 | 
			
		||||
      #     attribute :roles, :string, array: true, default: []
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   class User < ApplicationRecord
 | 
			
		||||
      #     attribute :roles, :string, array: true, default: -> { [] }
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      #   # bad
 | 
			
		||||
      #   class User < ApplicationRecord
 | 
			
		||||
      #     attribute :configuration, default: {}
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   class User < ApplicationRecord
 | 
			
		||||
      #     attribute :configuration, default: -> { {} }
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   class User < ApplicationRecord
 | 
			
		||||
      #     attribute :role, :string, default: :customer
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   class User < ApplicationRecord
 | 
			
		||||
      #     attribute :activated, :boolean, default: false
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   class User < ApplicationRecord
 | 
			
		||||
      #     attribute :login_count, :integer, default: 0
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   class User < ApplicationRecord
 | 
			
		||||
      #     FOO = 123
 | 
			
		||||
      #     attribute :custom_attribute, :integer, default: FOO
 | 
			
		||||
      #   end
 | 
			
		||||
      class AttributeDefaultBlockValue < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Pass method in a block to `:default` option.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[attribute].freeze
 | 
			
		||||
        TYPE_OFFENDERS = %i[send array hash].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :default_attribute, <<~PATTERN
 | 
			
		||||
          (send nil? :attribute _ ?_ (hash <$#attribute ...>))
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :attribute, '(pair (sym :default) $_)'
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          default_attribute(node) do |attribute|
 | 
			
		||||
            value = attribute.children.last
 | 
			
		||||
            return unless TYPE_OFFENDERS.any? { |type| value.type == type }
 | 
			
		||||
 | 
			
		||||
            add_offense(value) do |corrector|
 | 
			
		||||
              expression = default_attribute(node).children.last
 | 
			
		||||
 | 
			
		||||
              corrector.replace(value, "-> { #{expression.source} }")
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -50,7 +50,8 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      # @see https://guides.rubyonrails.org/5_0_release_notes.html
 | 
			
		||||
      # @see https://github.com/rails/rails/pull/18937
 | 
			
		||||
      class BelongsTo < Cop
 | 
			
		||||
      class BelongsTo < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
        extend TargetRailsVersion
 | 
			
		||||
 | 
			
		||||
        minimum_target_rails_version 5.0
 | 
			
		||||
@ -64,6 +65,7 @@ module RuboCop
 | 
			
		||||
          'option is deprecated and you want to use `optional: false`. ' \
 | 
			
		||||
          'In most configurations, this is the default and you can omit ' \
 | 
			
		||||
          'this option altogether'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[belongs_to].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :match_belongs_to_with_options, <<~PATTERN
 | 
			
		||||
          (send _ :belongs_to _
 | 
			
		||||
@ -72,27 +74,16 @@ module RuboCop
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          match_belongs_to_with_options(node) do |_option_node, option_value|
 | 
			
		||||
            message =
 | 
			
		||||
          match_belongs_to_with_options(node) do |option_node, option_value|
 | 
			
		||||
            message, replacement =
 | 
			
		||||
              if option_value.true_type?
 | 
			
		||||
                SUPERFLOUS_REQUIRE_TRUE_MSG
 | 
			
		||||
                [SUPERFLOUS_REQUIRE_TRUE_MSG, 'optional: false']
 | 
			
		||||
              elsif option_value.false_type?
 | 
			
		||||
                SUPERFLOUS_REQUIRE_FALSE_MSG
 | 
			
		||||
                [SUPERFLOUS_REQUIRE_FALSE_MSG, 'optional: true']
 | 
			
		||||
              end
 | 
			
		||||
 | 
			
		||||
            add_offense(node, message: message, location: :selector)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          option_node, option_value = match_belongs_to_with_options(node)
 | 
			
		||||
          return unless option_node
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            if option_value.true_type?
 | 
			
		||||
              corrector.replace(option_node.loc.expression, 'optional: false')
 | 
			
		||||
            elsif option_value.false_type?
 | 
			
		||||
              corrector.replace(option_node.loc.expression, 'optional: true')
 | 
			
		||||
            add_offense(node.loc.selector, message: message) do |corrector|
 | 
			
		||||
              corrector.replace(option_node.loc.expression, replacement)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
@ -53,11 +53,14 @@ module RuboCop
 | 
			
		||||
      #   def blank?
 | 
			
		||||
      #     !present?
 | 
			
		||||
      #   end
 | 
			
		||||
      class Blank < Cop
 | 
			
		||||
      class Blank < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG_NIL_OR_EMPTY = 'Use `%<prefer>s` instead of `%<current>s`.'
 | 
			
		||||
        MSG_NOT_PRESENT = 'Use `%<prefer>s` instead of `%<current>s`.'
 | 
			
		||||
        MSG_UNLESS_PRESENT = 'Use `if %<prefer>s` instead of ' \
 | 
			
		||||
                             '`%<current>s`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[!].freeze
 | 
			
		||||
 | 
			
		||||
        # `(send nil $_)` is not actually a valid match for an offense. Nodes
 | 
			
		||||
        # that have a single method call on the left hand side
 | 
			
		||||
@ -93,10 +96,10 @@ module RuboCop
 | 
			
		||||
            # accepts !present? if its in the body of a `blank?` method
 | 
			
		||||
            next if defining_blank?(node.parent)
 | 
			
		||||
 | 
			
		||||
            add_offense(node,
 | 
			
		||||
                        message: format(MSG_NOT_PRESENT,
 | 
			
		||||
                                        prefer: replacement(receiver),
 | 
			
		||||
                                        current: node.source))
 | 
			
		||||
            message = format(MSG_NOT_PRESENT, prefer: replacement(receiver), current: node.source)
 | 
			
		||||
            add_offense(node, message: message) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -106,10 +109,10 @@ module RuboCop
 | 
			
		||||
          nil_or_empty?(node) do |var1, var2|
 | 
			
		||||
            return unless var1 == var2
 | 
			
		||||
 | 
			
		||||
            add_offense(node,
 | 
			
		||||
                        message: format(MSG_NIL_OR_EMPTY,
 | 
			
		||||
                                        prefer: replacement(var1),
 | 
			
		||||
                                        current: node.source))
 | 
			
		||||
            message = format(MSG_NIL_OR_EMPTY, prefer: replacement(var1), current: node.source)
 | 
			
		||||
            add_offense(node, message: message) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -121,32 +124,29 @@ module RuboCop
 | 
			
		||||
          unless_present?(node) do |method_call, receiver|
 | 
			
		||||
            range = unless_condition(node, method_call)
 | 
			
		||||
 | 
			
		||||
            add_offense(node,
 | 
			
		||||
                        location: range,
 | 
			
		||||
                        message: format(MSG_UNLESS_PRESENT,
 | 
			
		||||
                                        prefer: replacement(receiver),
 | 
			
		||||
                                        current: range.source))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            method_call, variable1 = unless_present?(node)
 | 
			
		||||
 | 
			
		||||
            if method_call
 | 
			
		||||
              corrector.replace(node.loc.keyword, 'if')
 | 
			
		||||
              range = method_call.loc.expression
 | 
			
		||||
            else
 | 
			
		||||
              variable1, _variable2 = nil_or_empty?(node) || not_present?(node)
 | 
			
		||||
              range = node.loc.expression
 | 
			
		||||
            message = format(MSG_UNLESS_PRESENT, prefer: replacement(receiver), current: range.source)
 | 
			
		||||
            add_offense(range, message: message) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node)
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            corrector.replace(range, replacement(variable1))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          method_call, variable1 = unless_present?(node)
 | 
			
		||||
 | 
			
		||||
          if method_call
 | 
			
		||||
            corrector.replace(node.loc.keyword, 'if')
 | 
			
		||||
            range = method_call.loc.expression
 | 
			
		||||
          else
 | 
			
		||||
            variable1, _variable2 = nil_or_empty?(node) || not_present?(node)
 | 
			
		||||
            range = node.loc.expression
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          corrector.replace(range, replacement(variable1))
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def unless_condition(node, method_call)
 | 
			
		||||
          if node.modifier_form?
 | 
			
		||||
            node.loc.keyword.join(node.loc.expression.end)
 | 
			
		||||
@ -65,7 +65,7 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      # @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_table
 | 
			
		||||
      # @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html
 | 
			
		||||
      class BulkChangeTable < Cop
 | 
			
		||||
      class BulkChangeTable < Base
 | 
			
		||||
        MSG_FOR_CHANGE_TABLE = <<~MSG.chomp
 | 
			
		||||
          You can combine alter queries using `bulk: true` options.
 | 
			
		||||
        MSG
 | 
			
		||||
@ -18,42 +18,42 @@ module RuboCop
 | 
			
		||||
      #  tag.p('Hello world!')
 | 
			
		||||
      #  tag.br
 | 
			
		||||
      #  content_tag(name, 'Hello world!')
 | 
			
		||||
      class ContentTag < Cop
 | 
			
		||||
      class ContentTag < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
        extend TargetRailsVersion
 | 
			
		||||
 | 
			
		||||
        minimum_target_rails_version 5.1
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `tag` instead of `content_tag`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[content_tag].freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless node.method?(:content_tag)
 | 
			
		||||
 | 
			
		||||
          first_argument = node.first_argument
 | 
			
		||||
          return unless first_argument
 | 
			
		||||
 | 
			
		||||
          return if first_argument.variable? || first_argument.send_type? || first_argument.const_type?
 | 
			
		||||
 | 
			
		||||
          add_offense(node)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            if method_name?(node.first_argument)
 | 
			
		||||
              range = correction_range(node)
 | 
			
		||||
 | 
			
		||||
              rest_args = node.arguments.drop(1)
 | 
			
		||||
              replacement = "tag.#{node.first_argument.value.to_s.underscore}(#{rest_args.map(&:source).join(', ')})"
 | 
			
		||||
 | 
			
		||||
              corrector.replace(range, replacement)
 | 
			
		||||
            else
 | 
			
		||||
              corrector.replace(node.loc.selector, 'tag')
 | 
			
		||||
            end
 | 
			
		||||
          add_offense(node) do |corrector|
 | 
			
		||||
            autocorrect(corrector, node)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          if method_name?(node.first_argument)
 | 
			
		||||
            range = correction_range(node)
 | 
			
		||||
 | 
			
		||||
            rest_args = node.arguments.drop(1)
 | 
			
		||||
            replacement = "tag.#{node.first_argument.value.to_s.underscore}(#{rest_args.map(&:source).join(', ')})"
 | 
			
		||||
 | 
			
		||||
            corrector.replace(range, replacement)
 | 
			
		||||
          else
 | 
			
		||||
            corrector.replace(node.loc.selector, 'tag')
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def method_name?(node)
 | 
			
		||||
          return false unless node.str_type? || node.sym_type?
 | 
			
		||||
 | 
			
		||||
@ -40,8 +40,9 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #     t.datetime :updated_at, default: -> { 'CURRENT_TIMESTAMP' }
 | 
			
		||||
      #   end
 | 
			
		||||
      class CreateTableWithTimestamps < Cop
 | 
			
		||||
      class CreateTableWithTimestamps < Base
 | 
			
		||||
        MSG = 'Add timestamps when creating a new table.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[create_table].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :create_table_with_block?, <<~PATTERN
 | 
			
		||||
          (block
 | 
			
		||||
@ -43,7 +43,7 @@ module RuboCop
 | 
			
		||||
      #   Date.yesterday
 | 
			
		||||
      #   date.in_time_zone
 | 
			
		||||
      #
 | 
			
		||||
      class Date < Cop
 | 
			
		||||
      class Date < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
 | 
			
		||||
        MSG = 'Do not use `Date.%<method_called>s` without zone. Use ' \
 | 
			
		||||
@ -52,6 +52,8 @@ module RuboCop
 | 
			
		||||
        MSG_SEND = 'Do not use `%<method>s` on Date objects, because they ' \
 | 
			
		||||
                   'know nothing about the time zone in use.'
 | 
			
		||||
 | 
			
		||||
        RESTRICT_ON_SEND = %i[to_time to_time_in_current_zone].freeze
 | 
			
		||||
 | 
			
		||||
        BAD_DAYS = %i[today current yesterday tomorrow].freeze
 | 
			
		||||
 | 
			
		||||
        DEPRECATED_METHODS = [
 | 
			
		||||
@ -76,8 +78,7 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
          check_deprecated_methods(node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector,
 | 
			
		||||
                            message: format(MSG_SEND, method: node.method_name))
 | 
			
		||||
          add_offense(node.loc.selector, message: format(MSG_SEND, method: node.method_name))
 | 
			
		||||
        end
 | 
			
		||||
        alias on_csend on_send
 | 
			
		||||
 | 
			
		||||
@ -87,10 +88,9 @@ module RuboCop
 | 
			
		||||
          DEPRECATED_METHODS.each do |method|
 | 
			
		||||
            next unless node.method?(method[:deprecated].to_sym)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, location: :selector,
 | 
			
		||||
                              message: format(DEPRECATED_MSG,
 | 
			
		||||
                                              deprecated: method[:deprecated],
 | 
			
		||||
                                              relevant: method[:relevant]))
 | 
			
		||||
            message = format(DEPRECATED_MSG, deprecated: method[:deprecated], relevant: method[:relevant])
 | 
			
		||||
 | 
			
		||||
            add_offense(node.loc.selector, message: message)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -104,10 +104,9 @@ module RuboCop
 | 
			
		||||
          day = method_name
 | 
			
		||||
          day = 'today' if method_name == 'current'
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector,
 | 
			
		||||
                            message: format(MSG,
 | 
			
		||||
                                            method_called: method_name,
 | 
			
		||||
                                            day: day))
 | 
			
		||||
          message = format(MSG, method_called: method_name, day: day)
 | 
			
		||||
 | 
			
		||||
          add_offense(node.loc.selector, message: message)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def extract_method_chain(node)
 | 
			
		||||
@ -22,8 +22,9 @@ module RuboCop
 | 
			
		||||
      #     where(hidden: false)
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      class DefaultScope < Cop
 | 
			
		||||
      class DefaultScope < Base
 | 
			
		||||
        MSG = 'Avoid use of `default_scope`. It is better to use explicitly named scopes.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[default_scope].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :method_call?, <<~PATTERN
 | 
			
		||||
          (send nil? :default_scope ...)
 | 
			
		||||
@ -38,15 +39,21 @@ module RuboCop
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          add_offense(node, location: :selector) if method_call?(node)
 | 
			
		||||
          return unless method_call?(node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node.loc.selector)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def on_defs(node)
 | 
			
		||||
          add_offense(node, location: :name) if class_method_definition?(node)
 | 
			
		||||
          return unless class_method_definition?(node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node.loc.name)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def on_sclass(node)
 | 
			
		||||
          eigenclass_method_definition?(node) { |default_scope| add_offense(default_scope, location: :name) }
 | 
			
		||||
          eigenclass_method_definition?(node) do |default_scope|
 | 
			
		||||
            add_offense(default_scope.loc.name)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -52,7 +52,9 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   delegate :bar, to: :foo, prefix: true
 | 
			
		||||
      class Delegate < Cop
 | 
			
		||||
      class Delegate < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `delegate` to define delegations.'
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :delegate?, <<~PATTERN
 | 
			
		||||
@ -64,22 +66,20 @@ module RuboCop
 | 
			
		||||
          return unless trivial_delegate?(node)
 | 
			
		||||
          return if private_or_protected_delegation(node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :keyword)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          delegation = ["delegate :#{node.body.method_name}",
 | 
			
		||||
                        "to: :#{node.body.receiver.method_name}"]
 | 
			
		||||
 | 
			
		||||
          delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(node.source_range, delegation.join(', '))
 | 
			
		||||
          end
 | 
			
		||||
          register_offense(node)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def register_offense(node)
 | 
			
		||||
          add_offense(node.loc.keyword) do |corrector|
 | 
			
		||||
            delegation = ["delegate :#{node.body.method_name}", "to: :#{node.body.receiver.method_name}"]
 | 
			
		||||
            delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
 | 
			
		||||
 | 
			
		||||
            corrector.replace(node.source_range, delegation.join(', '))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def trivial_delegate?(def_node)
 | 
			
		||||
          delegate?(def_node) &&
 | 
			
		||||
            method_name_matches?(def_node.method_name, def_node.body) &&
 | 
			
		||||
@ -13,22 +13,21 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   delegate :foo, to: :bar, allow_nil: true
 | 
			
		||||
      class DelegateAllowBlank < Cop
 | 
			
		||||
      class DelegateAllowBlank < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = '`allow_blank` is not a valid option, use `allow_nil`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[delegate].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :allow_blank_option, <<~PATTERN
 | 
			
		||||
          (send nil? :delegate _ (hash <$(pair (sym :allow_blank) true) ...>))
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          allow_blank_option(node) do |offending_node|
 | 
			
		||||
            add_offense(offending_node)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
          return unless (offending_node = allow_blank_option(node))
 | 
			
		||||
 | 
			
		||||
        def autocorrect(pair_node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(pair_node.key.source_range, 'allow_nil')
 | 
			
		||||
          add_offense(offending_node) do |corrector|
 | 
			
		||||
            corrector.replace(offending_node.key.source_range, 'allow_nil')
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
@ -31,7 +31,9 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   Gem::Specification.find_by_name('backend').gem_dir
 | 
			
		||||
      class DynamicFindBy < Cop
 | 
			
		||||
      class DynamicFindBy < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `%<static_name>s` instead of dynamic `%<method>s`.'
 | 
			
		||||
        METHOD_PATTERN = /^find_by_(.+?)(!)?$/.freeze
 | 
			
		||||
 | 
			
		||||
@ -43,25 +45,24 @@ module RuboCop
 | 
			
		||||
          return unless static_name
 | 
			
		||||
          return if node.arguments.any?(&:splat_type?)
 | 
			
		||||
 | 
			
		||||
          add_offense(node,
 | 
			
		||||
                      message: format(MSG, static_name: static_name,
 | 
			
		||||
                                           method: method_name))
 | 
			
		||||
          message = format(MSG, static_name: static_name, method: method_name)
 | 
			
		||||
          add_offense(node, message: message) do |corrector|
 | 
			
		||||
            autocorrect(corrector, node)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
        alias on_csend on_send
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          keywords = column_keywords(node.method_name)
 | 
			
		||||
 | 
			
		||||
          return if keywords.size != node.arguments.size
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            autocorrect_method_name(corrector, node)
 | 
			
		||||
            autocorrect_argument_keywords(corrector, node, keywords)
 | 
			
		||||
          end
 | 
			
		||||
          autocorrect_method_name(corrector, node)
 | 
			
		||||
          autocorrect_argument_keywords(corrector, node, keywords)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def allowed_invocation?(node)
 | 
			
		||||
          allowed_method?(node) || allowed_receiver?(node) ||
 | 
			
		||||
            whitelisted?(node)
 | 
			
		||||
@ -17,9 +17,12 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   enum status: { active: 0, archived: 1 }
 | 
			
		||||
      #
 | 
			
		||||
      class EnumHash < Cop
 | 
			
		||||
      class EnumHash < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Enum defined as an array found in `%<enum>s` enum declaration. '\
 | 
			
		||||
              'Use hash syntax instead.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[enum].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :enum?, <<~PATTERN
 | 
			
		||||
          (send nil? :enum (hash $...))
 | 
			
		||||
@ -35,19 +38,17 @@ module RuboCop
 | 
			
		||||
              key, array = array_pair?(pair)
 | 
			
		||||
              next unless key
 | 
			
		||||
 | 
			
		||||
              add_offense(array, message: format(MSG, enum: enum_name(key)))
 | 
			
		||||
              add_offense(array, message: format(MSG, enum: enum_name(key))) do |corrector|
 | 
			
		||||
                hash = array.children.each_with_index.map do |elem, index|
 | 
			
		||||
                  "#{source(elem)} => #{index}"
 | 
			
		||||
                end.join(', ')
 | 
			
		||||
 | 
			
		||||
                corrector.replace(array.loc.expression, "{#{hash}}")
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          hash = node.children.each_with_index.map do |elem, index|
 | 
			
		||||
            "#{source(elem)} => #{index}"
 | 
			
		||||
          end.join(', ')
 | 
			
		||||
 | 
			
		||||
          ->(corrector) { corrector.replace(node.loc.expression, "{#{hash}}") }
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def enum_name(key)
 | 
			
		||||
@ -17,11 +17,12 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   enum status: [:active, :archived]
 | 
			
		||||
      class EnumUniqueness < Cop
 | 
			
		||||
      class EnumUniqueness < Base
 | 
			
		||||
        include Duplication
 | 
			
		||||
 | 
			
		||||
        MSG = 'Duplicate value `%<value>s` found in `%<enum>s` ' \
 | 
			
		||||
              'enum declaration.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[enum].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :enum?, <<~PATTERN
 | 
			
		||||
          (send nil? :enum (hash $...))
 | 
			
		||||
@ -15,12 +15,16 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   Rails.env.production?
 | 
			
		||||
      class EnvironmentComparison < Cop
 | 
			
		||||
      class EnvironmentComparison < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Favor `%<bang>sRails.env.%<env>s?` over `%<source>s`.'
 | 
			
		||||
 | 
			
		||||
        SYM_MSG = 'Do not compare `Rails.env` with a symbol, it will always ' \
 | 
			
		||||
          'evaluate to `false`.'
 | 
			
		||||
 | 
			
		||||
        RESTRICT_ON_SEND = %i[== !=].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :comparing_str_env_with_rails_env_on_lhs?, <<~PATTERN
 | 
			
		||||
          (send
 | 
			
		||||
            (send (const {nil? cbase} :Rails) :env)
 | 
			
		||||
@ -62,28 +66,28 @@ module RuboCop
 | 
			
		||||
                         comparing_str_env_with_rails_env_on_rhs?(node))
 | 
			
		||||
            env, = *env_node
 | 
			
		||||
            bang = node.method?(:!=) ? '!' : ''
 | 
			
		||||
            message = format(MSG, bang: bang, env: env, source: node.source)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, message: format(
 | 
			
		||||
              MSG, bang: bang, env: env, source: node.source
 | 
			
		||||
            ))
 | 
			
		||||
            add_offense(node, message: message) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          if comparing_sym_env_with_rails_env_on_lhs?(node) ||
 | 
			
		||||
             comparing_sym_env_with_rails_env_on_rhs?(node)
 | 
			
		||||
            add_offense(node, message: SYM_MSG)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
          return unless comparing_sym_env_with_rails_env_on_lhs?(node) || comparing_sym_env_with_rails_env_on_rhs?(node)
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            replacement = build_predicate_method(node)
 | 
			
		||||
 | 
			
		||||
            corrector.replace(node.source_range, replacement)
 | 
			
		||||
          add_offense(node, message: SYM_MSG) do |corrector|
 | 
			
		||||
            autocorrect(corrector, node)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          replacement = build_predicate_method(node)
 | 
			
		||||
 | 
			
		||||
          corrector.replace(node.source_range, replacement)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def build_predicate_method(node)
 | 
			
		||||
          if rails_env_on_lhs?(node)
 | 
			
		||||
            build_predicate_method_for_rails_env_on_lhs(node)
 | 
			
		||||
@ -23,27 +23,21 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   raise 'a bad error has happened'
 | 
			
		||||
      class Exit < Cop
 | 
			
		||||
      class Exit < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
 | 
			
		||||
        MSG = 'Do not use `exit` in Rails applications.'
 | 
			
		||||
        TARGET_METHODS = %i[exit exit!].freeze
 | 
			
		||||
        RESTRICT_ON_SEND = %i[exit exit!].freeze
 | 
			
		||||
        EXPLICIT_RECEIVERS = %i[Kernel Process].freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          add_offense(node, location: :selector) if offending_node?(node)
 | 
			
		||||
          add_offense(node.loc.selector) if offending_node?(node)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def offending_node?(node)
 | 
			
		||||
          right_method_name?(node.method_name) &&
 | 
			
		||||
            right_argument_count?(node.arguments) &&
 | 
			
		||||
            right_receiver?(node.receiver)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def right_method_name?(method_name)
 | 
			
		||||
          TARGET_METHODS.include?(method_name)
 | 
			
		||||
          right_argument_count?(node.arguments) && right_receiver?(node.receiver)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # More than 1 argument likely means it is a different
 | 
			
		||||
@ -25,7 +25,7 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   Rails.root.join('app/models/goober')
 | 
			
		||||
      #
 | 
			
		||||
      class FilePath < Cop
 | 
			
		||||
      class FilePath < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,7 @@ module RuboCop
 | 
			
		||||
                      'instead.'
 | 
			
		||||
        MSG_ARGUMENTS = 'Please use `Rails.root.join(\'path\', \'to\')` ' \
 | 
			
		||||
                        'instead.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[join].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :file_join_nodes?, <<~PATTERN
 | 
			
		||||
          (send (const nil? :File) :join ...)
 | 
			
		||||
@ -97,10 +98,10 @@ module RuboCop
 | 
			
		||||
          line_range = node.loc.column...node.loc.last_column
 | 
			
		||||
          source_range = source_range(processed_source.buffer, node.first_line,
 | 
			
		||||
                                      line_range)
 | 
			
		||||
          add_offense(node, location: source_range)
 | 
			
		||||
          add_offense(source_range)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def message(_node)
 | 
			
		||||
        def message(_range)
 | 
			
		||||
          format(style == :arguments ? MSG_ARGUMENTS : MSG_SLASHES)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
@ -13,11 +13,12 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   User.find_by(name: 'Bruce')
 | 
			
		||||
      class FindBy < Cop
 | 
			
		||||
      class FindBy < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `find_by` instead of `where.%<method>s`.'
 | 
			
		||||
        TARGET_SELECTORS = %i[first take].freeze
 | 
			
		||||
        RESTRICT_ON_SEND = %i[first take].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :where_first?, <<~PATTERN
 | 
			
		||||
          (send ({send csend} _ :where ...) {:first :take})
 | 
			
		||||
@ -26,28 +27,27 @@ module RuboCop
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless where_first?(node)
 | 
			
		||||
 | 
			
		||||
          range = range_between(node.receiver.loc.selector.begin_pos,
 | 
			
		||||
                                node.loc.selector.end_pos)
 | 
			
		||||
          range = range_between(node.receiver.loc.selector.begin_pos, node.loc.selector.end_pos)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: range,
 | 
			
		||||
                            message: format(MSG, method: node.method_name))
 | 
			
		||||
          add_offense(range, message: format(MSG, method: node.method_name)) do |corrector|
 | 
			
		||||
            autocorrect(corrector, node)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
        alias on_csend on_send
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          # Don't autocorrect where(...).first, because it can return different
 | 
			
		||||
          # results from find_by. (They order records differently, so the
 | 
			
		||||
          # 'first' record can be different.)
 | 
			
		||||
          return if node.method?(:first)
 | 
			
		||||
 | 
			
		||||
          where_loc = node.receiver.loc.selector
 | 
			
		||||
          first_loc = range_between(node.loc.dot.begin_pos,
 | 
			
		||||
                                    node.loc.selector.end_pos)
 | 
			
		||||
          first_loc = range_between(node.loc.dot.begin_pos, node.loc.selector.end_pos)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(where_loc, 'find_by')
 | 
			
		||||
            corrector.replace(first_loc, '')
 | 
			
		||||
          end
 | 
			
		||||
          corrector.replace(where_loc, 'find_by')
 | 
			
		||||
          corrector.replace(first_loc, '')
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -16,10 +16,12 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   User.find(id)
 | 
			
		||||
      #
 | 
			
		||||
      class FindById < Cop
 | 
			
		||||
      class FindById < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[take! find_by_id! find_by!].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :where_take?, <<~PATTERN
 | 
			
		||||
          (send
 | 
			
		||||
@ -38,41 +40,30 @@ module RuboCop
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          where_take?(node) do |where, id_value|
 | 
			
		||||
            range = where_take_offense_range(node, where)
 | 
			
		||||
 | 
			
		||||
            good_method = build_good_method(id_value)
 | 
			
		||||
            bad_method = build_where_take_bad_method(id_value)
 | 
			
		||||
            message = format(MSG, good_method: good_method, bad_method: bad_method)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, location: range, message: message)
 | 
			
		||||
            register_offense(range, id_value, bad_method)
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          find_by?(node) do |id_value|
 | 
			
		||||
            range = find_by_offense_range(node)
 | 
			
		||||
 | 
			
		||||
            good_method = build_good_method(id_value)
 | 
			
		||||
            bad_method = build_find_by_bad_method(node, id_value)
 | 
			
		||||
            message = format(MSG, good_method: good_method, bad_method: bad_method)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, location: range, message: message)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          if (matches = where_take?(node))
 | 
			
		||||
            where, id_value = *matches
 | 
			
		||||
            range = where_take_offense_range(node, where)
 | 
			
		||||
          elsif (id_value = find_by?(node))
 | 
			
		||||
            range = find_by_offense_range(node)
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            replacement = build_good_method(id_value)
 | 
			
		||||
            corrector.replace(range, replacement)
 | 
			
		||||
            register_offense(range, id_value, bad_method)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def register_offense(range, id_value, bad_method)
 | 
			
		||||
          good_method = build_good_method(id_value)
 | 
			
		||||
          message = format(MSG, good_method: good_method, bad_method: bad_method)
 | 
			
		||||
 | 
			
		||||
          add_offense(range, message: message) do |corrector|
 | 
			
		||||
            corrector.replace(range, good_method)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def where_take_offense_range(node, where)
 | 
			
		||||
          range_between(where.loc.selector.begin_pos, node.loc.expression.end_pos)
 | 
			
		||||
        end
 | 
			
		||||
@ -12,27 +12,30 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   User.all.find_each
 | 
			
		||||
      class FindEach < Cop
 | 
			
		||||
      #
 | 
			
		||||
      # @example IgnoredMethods: ['order']
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   User.order(:foo).each
 | 
			
		||||
      class FindEach < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `find_each` instead of `each`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[each].freeze
 | 
			
		||||
 | 
			
		||||
        SCOPE_METHODS = %i[
 | 
			
		||||
          all eager_load includes joins left_joins left_outer_joins not preload
 | 
			
		||||
          references unscoped where
 | 
			
		||||
        ].freeze
 | 
			
		||||
        IGNORED_METHODS = %i[order limit select].freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless node.receiver&.send_type? &&
 | 
			
		||||
                        node.method?(:each)
 | 
			
		||||
 | 
			
		||||
          return unless node.receiver&.send_type?
 | 
			
		||||
          return unless SCOPE_METHODS.include?(node.receiver.method_name)
 | 
			
		||||
          return if method_chain(node).any? { |m| ignored_by_find_each?(m) }
 | 
			
		||||
          return if method_chain(node).any? { |m| ignored?(m) }
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          ->(corrector) { corrector.replace(node.loc.selector, 'find_each') }
 | 
			
		||||
          range = node.loc.selector
 | 
			
		||||
          add_offense(range) do |corrector|
 | 
			
		||||
            corrector.replace(range, 'find_each')
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
@ -41,9 +44,8 @@ module RuboCop
 | 
			
		||||
          node.each_node(:send).map(&:method_name)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def ignored_by_find_each?(relation_method)
 | 
			
		||||
          # Active Record's #find_each ignores various extra parameters
 | 
			
		||||
          IGNORED_METHODS.include?(relation_method)
 | 
			
		||||
        def ignored?(relation_method)
 | 
			
		||||
          cop_config['IgnoredMethods'].include?(relation_method)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -11,13 +11,14 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   # has_many :ingredients, through: :recipe_ingredients
 | 
			
		||||
      class HasAndBelongsToMany < Cop
 | 
			
		||||
      class HasAndBelongsToMany < Base
 | 
			
		||||
        MSG = 'Prefer `has_many :through` to `has_and_belongs_to_many`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[has_and_belongs_to_many].freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless node.command?(:has_and_belongs_to_many)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
          add_offense(node.loc.selector)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -20,8 +20,9 @@ module RuboCop
 | 
			
		||||
      #     has_one :avatar, dependent: :destroy
 | 
			
		||||
      #     has_many :patients, through: :appointments
 | 
			
		||||
      #   end
 | 
			
		||||
      class HasManyOrHasOneDependent < Cop
 | 
			
		||||
      class HasManyOrHasOneDependent < Base
 | 
			
		||||
        MSG = 'Specify a `:dependent` option.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[has_many has_one].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_search :active_resource_class?, <<~PATTERN
 | 
			
		||||
          (const (const nil? :ActiveResource) :Base)
 | 
			
		||||
@ -55,7 +56,7 @@ module RuboCop
 | 
			
		||||
          return if !association_without_options?(node) && valid_options?(association_with_options?(node))
 | 
			
		||||
          return if valid_options_in_with_options_block?(node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
          add_offense(node.loc.selector)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
@ -23,7 +23,7 @@ module RuboCop
 | 
			
		||||
      #   def welcome_message(user)
 | 
			
		||||
      #     "Hello #{user.name}"
 | 
			
		||||
      #   end
 | 
			
		||||
      class HelperInstanceVariable < Cop
 | 
			
		||||
      class HelperInstanceVariable < Base
 | 
			
		||||
        MSG = 'Do not use instance variables in helpers.'
 | 
			
		||||
 | 
			
		||||
        def on_ivar(node)
 | 
			
		||||
@ -33,7 +33,7 @@ module RuboCop
 | 
			
		||||
        def on_ivasgn(node)
 | 
			
		||||
          return if node.parent.or_asgn_type?
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :name)
 | 
			
		||||
          add_offense(node.loc.name)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -17,7 +17,8 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   get :new, params: { user_id: 1 }
 | 
			
		||||
      #   get :new, **options
 | 
			
		||||
      class HttpPositionalArguments < Cop
 | 
			
		||||
      class HttpPositionalArguments < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
        extend TargetRailsVersion
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use keyword arguments instead of ' \
 | 
			
		||||
@ -25,12 +26,12 @@ module RuboCop
 | 
			
		||||
        KEYWORD_ARGS = %i[
 | 
			
		||||
          method params session body flash xhr as headers env to
 | 
			
		||||
        ].freeze
 | 
			
		||||
        HTTP_METHODS = %i[get post put patch delete head].freeze
 | 
			
		||||
        RESTRICT_ON_SEND = %i[get post put patch delete head].freeze
 | 
			
		||||
 | 
			
		||||
        minimum_target_rails_version 5.0
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :http_request?, <<~PATTERN
 | 
			
		||||
          (send nil? {#{HTTP_METHODS.map(&:inspect).join(' ')}} !nil? $_ ...)
 | 
			
		||||
          (send nil? {#{RESTRICT_ON_SEND.map(&:inspect).join(' ')}} !nil? $_ ...)
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :kwsplat_hash?, <<~PATTERN
 | 
			
		||||
@ -41,24 +42,21 @@ module RuboCop
 | 
			
		||||
          http_request?(node) do |data|
 | 
			
		||||
            return unless needs_conversion?(data)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, location: :selector,
 | 
			
		||||
                              message: format(MSG, verb: node.method_name))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
            message = format(MSG, verb: node.method_name)
 | 
			
		||||
 | 
			
		||||
        # given a pre Rails 5 method: get :new, {user_id: @user.id}, {}
 | 
			
		||||
        #
 | 
			
		||||
        # @return lambda of auto correct procedure
 | 
			
		||||
        # the result should look like:
 | 
			
		||||
        #     get :new, params: { user_id: @user.id }, session: {}
 | 
			
		||||
        # the http_method is the method used to call the controller
 | 
			
		||||
        # the controller node can be a symbol, method, object or string
 | 
			
		||||
        # that represents the path/action on the Rails controller
 | 
			
		||||
        # the data is the http parameters and environment sent in
 | 
			
		||||
        # the Rails 5 http call
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(node.loc.expression, correction(node))
 | 
			
		||||
            add_offense(node.loc.selector, message: message) do |corrector|
 | 
			
		||||
              # given a pre Rails 5 method: get :new, {user_id: @user.id}, {}
 | 
			
		||||
              #
 | 
			
		||||
              # @return lambda of auto correct procedure
 | 
			
		||||
              # the result should look like:
 | 
			
		||||
              #     get :new, params: { user_id: @user.id }, session: {}
 | 
			
		||||
              # the http_method is the method used to call the controller
 | 
			
		||||
              # the controller node can be a symbol, method, object or string
 | 
			
		||||
              # that represents the path/action on the Rails controller
 | 
			
		||||
              # the data is the http parameters and environment sent in
 | 
			
		||||
              # the Rails 5 http call
 | 
			
		||||
              corrector.replace(node.loc.expression, correction(node))
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -31,8 +31,11 @@ module RuboCop
 | 
			
		||||
      #   render plain: 'foo/bar', status: 304
 | 
			
		||||
      #   redirect_to root_url, status: 301
 | 
			
		||||
      #
 | 
			
		||||
      class HttpStatus < Cop
 | 
			
		||||
      class HttpStatus < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        RESTRICT_ON_SEND = %i[render redirect_to].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :http_status, <<~PATTERN
 | 
			
		||||
          {
 | 
			
		||||
@ -53,14 +56,9 @@ module RuboCop
 | 
			
		||||
            checker = checker_class.new(status)
 | 
			
		||||
            return unless checker.offensive?
 | 
			
		||||
 | 
			
		||||
            add_offense(checker.node, message: checker.message)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            checker = checker_class.new(node)
 | 
			
		||||
            corrector.replace(node.loc.expression, checker.preferred_style)
 | 
			
		||||
            add_offense(checker.node, message: checker.message) do |corrector|
 | 
			
		||||
              corrector.replace(checker.node.loc.expression, checker.preferred_style)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -37,18 +37,20 @@ module RuboCop
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      # @see https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-_normalize_callback_options
 | 
			
		||||
      class IgnoredSkipActionFilterOption < Cop
 | 
			
		||||
      class IgnoredSkipActionFilterOption < Base
 | 
			
		||||
        MSG = <<~MSG.chomp.freeze
 | 
			
		||||
          `%<ignore>s` option will be ignored when `%<prefer>s` and `%<ignore>s` are used together.
 | 
			
		||||
        MSG
 | 
			
		||||
 | 
			
		||||
        FILTERS = %w[
 | 
			
		||||
          :skip_after_action
 | 
			
		||||
          :skip_around_action
 | 
			
		||||
          :skip_before_action
 | 
			
		||||
          :skip_action_callback
 | 
			
		||||
        RESTRICT_ON_SEND = %i[
 | 
			
		||||
          skip_after_action
 | 
			
		||||
          skip_around_action
 | 
			
		||||
          skip_before_action
 | 
			
		||||
          skip_action_callback
 | 
			
		||||
        ].freeze
 | 
			
		||||
 | 
			
		||||
        FILTERS = RESTRICT_ON_SEND.map { |method_name| ":#{method_name}" }
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :filter_options, <<~PATTERN
 | 
			
		||||
          (send
 | 
			
		||||
            nil?
 | 
			
		||||
@ -17,8 +17,9 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   [1, 2, 3].index_by { |el| foo(el) }
 | 
			
		||||
      class IndexBy < Cop
 | 
			
		||||
      class IndexBy < Base
 | 
			
		||||
        include IndexMethod
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :on_bad_each_with_object, <<~PATTERN
 | 
			
		||||
          (block
 | 
			
		||||
@ -17,7 +17,8 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   [1, 2, 3].index_with { |el| foo(el) }
 | 
			
		||||
      class IndexWith < Cop
 | 
			
		||||
      class IndexWith < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
        extend TargetRailsVersion
 | 
			
		||||
        include IndexMethod
 | 
			
		||||
 | 
			
		||||
@ -22,15 +22,16 @@ module RuboCop
 | 
			
		||||
      #   pets = %w(cat dog)
 | 
			
		||||
      #   pets.include? 'cat'
 | 
			
		||||
      #
 | 
			
		||||
      class Inquiry < Cop
 | 
			
		||||
      class Inquiry < Base
 | 
			
		||||
        MSG = "Prefer Ruby's comparison operators over Active Support's `inquiry`."
 | 
			
		||||
        RESTRICT_ON_SEND = %i[inquiry].freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless node.method?(:inquiry) && node.arguments.empty?
 | 
			
		||||
          return unless node.arguments.empty?
 | 
			
		||||
          return unless (receiver = node.receiver)
 | 
			
		||||
          return if !receiver.str_type? && !receiver.array_type?
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
          add_offense(node.loc.selector)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -128,10 +128,11 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      # @see https://guides.rubyonrails.org/association_basics.html#bi-directional-associations
 | 
			
		||||
      # @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
 | 
			
		||||
      class InverseOf < Cop
 | 
			
		||||
      class InverseOf < Base
 | 
			
		||||
        SPECIFY_MSG = 'Specify an `:inverse_of` option.'
 | 
			
		||||
        NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to ' \
 | 
			
		||||
          'use `inverse_of: false`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :association_recv_arguments, <<~PATTERN
 | 
			
		||||
          (send $_ {:has_many :has_one :belongs_to} _ $...)
 | 
			
		||||
@ -185,7 +186,7 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
          return if options_contain_inverse_of?(options)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, message: message(options), location: :selector)
 | 
			
		||||
          add_offense(node.loc.selector, message: message(options))
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def scope?(arguments)
 | 
			
		||||
@ -82,25 +82,27 @@ module RuboCop
 | 
			
		||||
      #       @content = Article.find(params[:article_id])
 | 
			
		||||
      #     end
 | 
			
		||||
      #   end
 | 
			
		||||
      class LexicallyScopedActionFilter < Cop
 | 
			
		||||
      class LexicallyScopedActionFilter < Base
 | 
			
		||||
        MSG = '%<action>s not explicitly defined on the %<type>s.'
 | 
			
		||||
 | 
			
		||||
        FILTERS = %w[
 | 
			
		||||
          :after_action
 | 
			
		||||
          :append_after_action
 | 
			
		||||
          :append_around_action
 | 
			
		||||
          :append_before_action
 | 
			
		||||
          :around_action
 | 
			
		||||
          :before_action
 | 
			
		||||
          :prepend_after_action
 | 
			
		||||
          :prepend_around_action
 | 
			
		||||
          :prepend_before_action
 | 
			
		||||
          :skip_after_action
 | 
			
		||||
          :skip_around_action
 | 
			
		||||
          :skip_before_action
 | 
			
		||||
          :skip_action_callback
 | 
			
		||||
        RESTRICT_ON_SEND = %i[
 | 
			
		||||
          after_action
 | 
			
		||||
          append_after_action
 | 
			
		||||
          append_around_action
 | 
			
		||||
          append_before_action
 | 
			
		||||
          around_action
 | 
			
		||||
          before_action
 | 
			
		||||
          prepend_after_action
 | 
			
		||||
          prepend_around_action
 | 
			
		||||
          prepend_before_action
 | 
			
		||||
          skip_after_action
 | 
			
		||||
          skip_around_action
 | 
			
		||||
          skip_before_action
 | 
			
		||||
          skip_action_callback
 | 
			
		||||
        ].freeze
 | 
			
		||||
 | 
			
		||||
        FILTERS = RESTRICT_ON_SEND.map { |method_name| ":#{method_name}" }
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :only_or_except_filter_methods, <<~PATTERN
 | 
			
		||||
          (send
 | 
			
		||||
            nil?
 | 
			
		||||
@ -20,8 +20,11 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   link_to 'Click here', url, target: '_blank', rel: 'noreferrer'
 | 
			
		||||
      class LinkToBlank < Cop
 | 
			
		||||
      class LinkToBlank < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Specify a `:rel` option containing noopener.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[link_to].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :blank_target?, <<~PATTERN
 | 
			
		||||
          (pair {(sym :target) (str "target")} {(str "_blank") (sym :_blank)})
 | 
			
		||||
@ -35,39 +38,34 @@ module RuboCop
 | 
			
		||||
          (pair {(sym :rel) (str "rel")} (str _))
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        # rubocop:disable Metrics/CyclomaticComplexity
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless node.method?(:link_to)
 | 
			
		||||
 | 
			
		||||
          option_nodes = node.each_child_node(:hash)
 | 
			
		||||
 | 
			
		||||
          option_nodes.map(&:children).each do |options|
 | 
			
		||||
            blank = options.find { |o| blank_target?(o) }
 | 
			
		||||
            add_offense(blank) if blank && options.none? { |o| includes_noopener?(o) }
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
        # rubocop:enable Metrics/CyclomaticComplexity
 | 
			
		||||
            next unless blank && options.none? { |o| includes_noopener?(o) }
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            send_node = node.parent.parent
 | 
			
		||||
 | 
			
		||||
            option_nodes = send_node.each_child_node(:hash)
 | 
			
		||||
            rel_node = nil
 | 
			
		||||
            option_nodes.map(&:children).each do |options|
 | 
			
		||||
              rel_node ||= options.find { |o| rel_node?(o) }
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            if rel_node
 | 
			
		||||
              append_to_rel(rel_node, corrector)
 | 
			
		||||
            else
 | 
			
		||||
              add_rel(send_node, node, corrector)
 | 
			
		||||
            add_offense(blank) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node, blank, option_nodes)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, send_node, node, option_nodes)
 | 
			
		||||
          rel_node = nil
 | 
			
		||||
          option_nodes.map(&:children).each do |options|
 | 
			
		||||
            rel_node ||= options.find { |o| rel_node?(o) }
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          if rel_node
 | 
			
		||||
            append_to_rel(rel_node, corrector)
 | 
			
		||||
          else
 | 
			
		||||
            add_rel(send_node, node, corrector)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def append_to_rel(rel_node, corrector)
 | 
			
		||||
          existing_rel = rel_node.children.last.value
 | 
			
		||||
          str_range = rel_node.children.last.loc.expression.adjust(
 | 
			
		||||
@ -89,7 +87,7 @@ module RuboCop
 | 
			
		||||
        def contains_noopener?(value)
 | 
			
		||||
          return false unless value
 | 
			
		||||
 | 
			
		||||
          rel_array = value.to_s.split(' ')
 | 
			
		||||
          rel_array = value.to_s.split
 | 
			
		||||
          rel_array.include?('noopener') || rel_array.include?('noreferrer')
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
@ -23,7 +23,9 @@ module RuboCop
 | 
			
		||||
      #   class UserMailer < ApplicationMailer
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      class MailerName < Cop
 | 
			
		||||
      class MailerName < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Mailer should end with `Mailer` suffix.'
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :mailer_base_class?, <<~PATTERN
 | 
			
		||||
@ -43,7 +45,9 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
        def on_class(node)
 | 
			
		||||
          class_definition?(node) do |name_node|
 | 
			
		||||
            add_offense(name_node)
 | 
			
		||||
            add_offense(name_node) do |corrector|
 | 
			
		||||
              autocorrect(corrector, name_node)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -54,23 +58,25 @@ module RuboCop
 | 
			
		||||
          return unless casgn_parent
 | 
			
		||||
 | 
			
		||||
          name = casgn_parent.children[1]
 | 
			
		||||
          add_offense(casgn_parent, location: :name) unless mailer_suffix?(name)
 | 
			
		||||
        end
 | 
			
		||||
          return if mailer_suffix?(name)
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            if node.casgn_type?
 | 
			
		||||
              name = node.children[1]
 | 
			
		||||
              corrector.replace(node.loc.name, "#{name}Mailer")
 | 
			
		||||
            else
 | 
			
		||||
              name = node.children.last
 | 
			
		||||
              corrector.replace(node.source_range, "#{name}Mailer")
 | 
			
		||||
            end
 | 
			
		||||
          add_offense(casgn_parent.loc.name) do |corrector|
 | 
			
		||||
            autocorrect(corrector, casgn_parent)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          if node.casgn_type?
 | 
			
		||||
            name = node.children[1]
 | 
			
		||||
            corrector.replace(node.loc.name, "#{name}Mailer")
 | 
			
		||||
          else
 | 
			
		||||
            name = node.children.last
 | 
			
		||||
            corrector.replace(node.source_range, "#{name}Mailer")
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def mailer_suffix?(mailer_name)
 | 
			
		||||
          mailer_name.to_s.end_with?('Mailer')
 | 
			
		||||
        end
 | 
			
		||||
@ -20,8 +20,11 @@ module RuboCop
 | 
			
		||||
      #   match 'photos/:id', to: 'photos#show', via: [:get, :post]
 | 
			
		||||
      #   match 'photos/:id', to: 'photos#show', via: :all
 | 
			
		||||
      #
 | 
			
		||||
      class MatchRoute < Cop
 | 
			
		||||
      class MatchRoute < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `%<http_method>s` instead of `match` to define a route.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[match].freeze
 | 
			
		||||
        HTTP_METHODS = %i[get post put patch delete].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :match_method_call?, <<~PATTERN
 | 
			
		||||
@ -35,30 +38,28 @@ module RuboCop
 | 
			
		||||
            options_node = path_node.hash_type? ? path_node : options_node.first
 | 
			
		||||
 | 
			
		||||
            if options_node.nil?
 | 
			
		||||
              message = format(MSG, http_method: 'get')
 | 
			
		||||
              add_offense(node, message: message)
 | 
			
		||||
              register_offense(node, 'get')
 | 
			
		||||
            else
 | 
			
		||||
              via = extract_via(options_node)
 | 
			
		||||
              if via.size == 1 && http_method?(via.first)
 | 
			
		||||
                message = format(MSG, http_method: via.first)
 | 
			
		||||
                add_offense(node, message: message)
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
              return unless via.size == 1 && http_method?(via.first)
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          match_method_call?(node) do |path_node, options_node|
 | 
			
		||||
            options_node = options_node.first
 | 
			
		||||
 | 
			
		||||
            lambda do |corrector|
 | 
			
		||||
              corrector.replace(node, replacement(path_node, options_node))
 | 
			
		||||
              register_offense(node, via.first)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def register_offense(node, http_method)
 | 
			
		||||
          add_offense(node, message: format(MSG, http_method: http_method)) do |corrector|
 | 
			
		||||
            match_method_call?(node) do |path_node, options_node|
 | 
			
		||||
              options_node = options_node.first
 | 
			
		||||
 | 
			
		||||
              corrector.replace(node, replacement(path_node, options_node))
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :routes_draw?, <<~PATTERN
 | 
			
		||||
          (send (send _ :routes) :draw)
 | 
			
		||||
        PATTERN
 | 
			
		||||
@ -6,6 +6,9 @@ module RuboCop
 | 
			
		||||
      # This cop enforces the use of `collection.exclude?(obj)`
 | 
			
		||||
      # over `!collection.include?(obj)`.
 | 
			
		||||
      #
 | 
			
		||||
      # It is marked as unsafe by default because false positive will occur for
 | 
			
		||||
      # a receiver object that do not have `exclude?` method. (e.g. `IPAddr`)
 | 
			
		||||
      #
 | 
			
		||||
      # @example
 | 
			
		||||
      #   # bad
 | 
			
		||||
      #   !array.include?(2)
 | 
			
		||||
@ -15,22 +18,21 @@ module RuboCop
 | 
			
		||||
      #   array.exclude?(2)
 | 
			
		||||
      #   hash.exclude?(:key)
 | 
			
		||||
      #
 | 
			
		||||
      class NegateInclude < Cop
 | 
			
		||||
      class NegateInclude < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `.exclude?` and remove the negation part.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[!].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :negate_include_call?, <<~PATTERN
 | 
			
		||||
          (send (send $_ :include? $_) :!)
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          add_offense(node) if negate_include_call?(node)
 | 
			
		||||
        end
 | 
			
		||||
          return unless (receiver, obj = negate_include_call?(node))
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          negate_include_call?(node) do |receiver, obj|
 | 
			
		||||
            lambda do |corrector|
 | 
			
		||||
              corrector.replace(node, "#{receiver.source}.exclude?(#{obj.source})")
 | 
			
		||||
            end
 | 
			
		||||
          add_offense(node) do |corrector|
 | 
			
		||||
            corrector.replace(node, "#{receiver.source}.exclude?(#{obj.source})")
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
@ -16,8 +16,9 @@ module RuboCop
 | 
			
		||||
      #   add_column :users, :name, :string, null: false, default: ''
 | 
			
		||||
      #   add_reference :products, :category
 | 
			
		||||
      #   add_reference :products, :category, null: false, default: 1
 | 
			
		||||
      class NotNullColumn < Cop
 | 
			
		||||
      class NotNullColumn < Base
 | 
			
		||||
        MSG = 'Do not add a NOT NULL column without a default value.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[add_column add_reference].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :add_not_null_column?, <<~PATTERN
 | 
			
		||||
          (send nil? :add_column _ _ _ (hash $...))
 | 
			
		||||
@ -25,6 +25,7 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
        MSG = 'Do not use the `id` column for ordering. '\
 | 
			
		||||
              'Use a timestamp column to order chronologically.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[order].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :order_by_id?, <<~PATTERN
 | 
			
		||||
          (send _ :order
 | 
			
		||||
@ -37,8 +38,6 @@ module RuboCop
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless node.method?(:order)
 | 
			
		||||
 | 
			
		||||
          add_offense(offense_range(node)) if order_by_id?(node)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -13,9 +13,12 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   Rails.logger.debug 'A debug message'
 | 
			
		||||
      class Output < Cop
 | 
			
		||||
      class Output < Base
 | 
			
		||||
        MSG = 'Do not write to stdout. ' \
 | 
			
		||||
              "Use Rails's logger if you want to log."
 | 
			
		||||
        RESTRICT_ON_SEND = %i[
 | 
			
		||||
          ap p pp pretty_print print puts binwrite syswrite write write_nonblock
 | 
			
		||||
        ].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :output?, <<~PATTERN
 | 
			
		||||
          (send nil? {:ap :p :pp :pretty_print :print :puts} ...)
 | 
			
		||||
@ -35,7 +38,7 @@ module RuboCop
 | 
			
		||||
          return unless (output?(node) || io_output?(node)) &&
 | 
			
		||||
                        node.arguments?
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
          add_offense(node.loc.selector)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
@ -62,8 +62,9 @@ module RuboCop
 | 
			
		||||
      #   safe_join([user_content, " ", content_tag(:span, user_content)])
 | 
			
		||||
      #   # => ActiveSupport::SafeBuffer
 | 
			
		||||
      #   #    "<b>hi</b> <span><b>hi</b></span>"
 | 
			
		||||
      class OutputSafety < Cop
 | 
			
		||||
      class OutputSafety < Base
 | 
			
		||||
        MSG = 'Tagging a string as html safe may be a security risk.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[html_safe raw safe_concat].freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return if non_interpolated_string?(node)
 | 
			
		||||
@ -72,7 +73,7 @@ module RuboCop
 | 
			
		||||
                        looks_like_rails_raw?(node) ||
 | 
			
		||||
                        looks_like_rails_safe_concat?(node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
          add_offense(node.loc.selector)
 | 
			
		||||
        end
 | 
			
		||||
        alias on_csend on_send
 | 
			
		||||
 | 
			
		||||
@ -17,10 +17,12 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   Model.pick(:a)
 | 
			
		||||
      #   [{ a: :b, c: :d }].pick(:a, :b)
 | 
			
		||||
      class Pick < Cop
 | 
			
		||||
      class Pick < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
        extend TargetRailsVersion
 | 
			
		||||
 | 
			
		||||
        MSG = 'Prefer `pick(%<args>s)` over `pluck(%<args>s).first`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[first].freeze
 | 
			
		||||
 | 
			
		||||
        minimum_target_rails_version 6.0
 | 
			
		||||
 | 
			
		||||
@ -30,24 +32,24 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          pick_candidate?(node) do
 | 
			
		||||
            range = node.receiver.loc.selector.join(node.loc.selector)
 | 
			
		||||
            add_offense(node, location: range)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
            receiver = node.receiver
 | 
			
		||||
            receiver_selector = receiver.loc.selector
 | 
			
		||||
            node_selector = node.loc.selector
 | 
			
		||||
            range = receiver_selector.join(node_selector)
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          first_range = node.receiver.source_range.end.join(node.loc.selector)
 | 
			
		||||
            add_offense(range, message: message(receiver)) do |corrector|
 | 
			
		||||
              first_range = receiver.source_range.end.join(node_selector)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.remove(first_range)
 | 
			
		||||
            corrector.replace(node.receiver.loc.selector, 'pick')
 | 
			
		||||
              corrector.remove(first_range)
 | 
			
		||||
              corrector.replace(receiver_selector, 'pick')
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def message(node)
 | 
			
		||||
          format(MSG, args: node.receiver.arguments.map(&:source).join(', '))
 | 
			
		||||
        def message(receiver)
 | 
			
		||||
          format(MSG, args: receiver.arguments.map(&:source).join(', '))
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -17,7 +17,8 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   Post.published.pluck(:title)
 | 
			
		||||
      #   [{ a: :b, c: :d }].pluck(:a)
 | 
			
		||||
      class Pluck < Cop
 | 
			
		||||
      class Pluck < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
        extend TargetRailsVersion
 | 
			
		||||
 | 
			
		||||
        MSG = 'Prefer `pluck(:%<value>s)` over `%<method>s { |%<argument>s| %<element>s[:%<value>s] }`.'
 | 
			
		||||
@ -32,15 +33,11 @@ module RuboCop
 | 
			
		||||
          pluck_candidate?(node) do |method, argument, element, value|
 | 
			
		||||
            next unless argument == element
 | 
			
		||||
 | 
			
		||||
            add_offense(node, location: offense_range(node), message: message(method, argument, element, value))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
            message = message(method, argument, element, value)
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          _method, _argument, _element, value = pluck_candidate?(node)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(offense_range(node), "pluck(:#{value})")
 | 
			
		||||
            add_offense(offense_range(node), message: message) do |corrector|
 | 
			
		||||
              corrector.replace(offense_range(node), "pluck(:#{value})")
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -22,11 +22,13 @@ module RuboCop
 | 
			
		||||
      #     ids
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      class PluckId < Cop
 | 
			
		||||
      class PluckId < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        include ActiveRecordHelper
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `ids` instead of `%<bad_method>s`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[pluck].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :pluck_id_call?, <<~PATTERN
 | 
			
		||||
          (send _ :pluck {(sym :id) (send nil? :primary_key)})
 | 
			
		||||
@ -38,11 +40,7 @@ module RuboCop
 | 
			
		||||
          range = offense_range(node)
 | 
			
		||||
          message = format(MSG, bad_method: range.source)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: range, message: message)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
          add_offense(range, message: message) do |corrector|
 | 
			
		||||
            corrector.replace(offense_range(node), 'ids')
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
@ -34,22 +34,22 @@ module RuboCop
 | 
			
		||||
      #   # bad
 | 
			
		||||
      #   Post.where(user_id: active_users.pluck(:id))
 | 
			
		||||
      #
 | 
			
		||||
      class PluckInWhere < Cop
 | 
			
		||||
      class PluckInWhere < Base
 | 
			
		||||
        include ActiveRecordHelper
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `select` instead of `pluck` within `where` query method.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[pluck].freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless node.method?(:pluck) && in_where?(node)
 | 
			
		||||
          return unless in_where?(node)
 | 
			
		||||
          return if style == :conservative && !root_receiver(node)&.const_type?
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
        end
 | 
			
		||||
          range = node.loc.selector
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(node.loc.selector, 'select')
 | 
			
		||||
          add_offense(range) do |corrector|
 | 
			
		||||
            corrector.replace(range, 'select')
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,9 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   3.days.ago
 | 
			
		||||
      #   1.month.ago
 | 
			
		||||
      class PluralizationGrammar < Cop
 | 
			
		||||
      class PluralizationGrammar < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        SINGULAR_DURATION_METHODS = { second: :seconds,
 | 
			
		||||
                                      minute: :minutes,
 | 
			
		||||
                                      hour: :hours,
 | 
			
		||||
@ -24,21 +26,18 @@ module RuboCop
 | 
			
		||||
                                      month: :months,
 | 
			
		||||
                                      year: :years }.freeze
 | 
			
		||||
 | 
			
		||||
        RESTRICT_ON_SEND = SINGULAR_DURATION_METHODS.keys + SINGULAR_DURATION_METHODS.values
 | 
			
		||||
 | 
			
		||||
        PLURAL_DURATION_METHODS = SINGULAR_DURATION_METHODS.invert.freeze
 | 
			
		||||
 | 
			
		||||
        MSG = 'Prefer `%<number>s.%<correct>s`.'
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless duration_method?(node.method_name)
 | 
			
		||||
          return unless literal_number?(node.receiver)
 | 
			
		||||
          return unless duration_method?(node.method_name) && literal_number?(node.receiver) && offense?(node)
 | 
			
		||||
 | 
			
		||||
          return unless offense?(node)
 | 
			
		||||
          number, = *node.receiver
 | 
			
		||||
 | 
			
		||||
          add_offense(node)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
          add_offense(node, message: message(number, node.method_name)) do |corrector|
 | 
			
		||||
            method_name = node.loc.selector.source
 | 
			
		||||
 | 
			
		||||
            corrector.replace(node.loc.selector, correct_method(method_name))
 | 
			
		||||
@ -47,11 +46,8 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def message(node)
 | 
			
		||||
          number, = *node.receiver
 | 
			
		||||
 | 
			
		||||
          format(MSG, number: number,
 | 
			
		||||
                      correct: correct_method(node.method_name.to_s))
 | 
			
		||||
        def message(number, method_name)
 | 
			
		||||
          format(MSG, number: number, correct: correct_method(method_name))
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def correct_method(method_name)
 | 
			
		||||
@ -37,8 +37,9 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   a.presence || b
 | 
			
		||||
      class Presence < Cop
 | 
			
		||||
      class Presence < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
 | 
			
		||||
 | 
			
		||||
@ -76,28 +77,26 @@ module RuboCop
 | 
			
		||||
          return if ignore_if_node?(node)
 | 
			
		||||
 | 
			
		||||
          redundant_receiver_and_other(node) do |receiver, other|
 | 
			
		||||
            add_offense(node, message: message(node, receiver, other)) unless ignore_other_node?(other) || receiver.nil?
 | 
			
		||||
            return if ignore_other_node?(other) || receiver.nil?
 | 
			
		||||
 | 
			
		||||
            register_offense(node, receiver, other)
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          redundant_negative_receiver_and_other(node) do |receiver, other|
 | 
			
		||||
            add_offense(node, message: message(node, receiver, other)) unless ignore_other_node?(other) || receiver.nil?
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
            return if ignore_other_node?(other) || receiver.nil?
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            redundant_receiver_and_other(node) do |receiver, other|
 | 
			
		||||
              corrector.replace(node.source_range, replacement(receiver, other))
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            redundant_negative_receiver_and_other(node) do |receiver, other|
 | 
			
		||||
              corrector.replace(node.source_range, replacement(receiver, other))
 | 
			
		||||
            end
 | 
			
		||||
            register_offense(node, receiver, other)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def register_offense(node, receiver, other)
 | 
			
		||||
          add_offense(node, message: message(node, receiver, other)) do |corrector|
 | 
			
		||||
            corrector.replace(node.source_range, replacement(receiver, other))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def ignore_if_node?(node)
 | 
			
		||||
          node.elsif?
 | 
			
		||||
        end
 | 
			
		||||
@ -43,12 +43,15 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   something if foo.present?
 | 
			
		||||
      class Present < Cop
 | 
			
		||||
      class Present < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG_NOT_BLANK = 'Use `%<prefer>s` instead of `%<current>s`.'
 | 
			
		||||
        MSG_EXISTS_AND_NOT_EMPTY = 'Use `%<prefer>s` instead of ' \
 | 
			
		||||
                                   '`%<current>s`.'
 | 
			
		||||
        MSG_UNLESS_BLANK = 'Use `if %<prefer>s` instead of ' \
 | 
			
		||||
                           '`%<current>s`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[!].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :exists_and_not_empty?, <<~PATTERN
 | 
			
		||||
          (and
 | 
			
		||||
@ -74,10 +77,11 @@ module RuboCop
 | 
			
		||||
          return unless cop_config['NotBlank']
 | 
			
		||||
 | 
			
		||||
          not_blank?(node) do |receiver|
 | 
			
		||||
            add_offense(node,
 | 
			
		||||
                        message: format(MSG_NOT_BLANK,
 | 
			
		||||
                                        prefer: replacement(receiver),
 | 
			
		||||
                                        current: node.source))
 | 
			
		||||
            message = format(MSG_NOT_BLANK, prefer: replacement(receiver), current: node.source)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, message: message) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -87,10 +91,11 @@ module RuboCop
 | 
			
		||||
          exists_and_not_empty?(node) do |var1, var2|
 | 
			
		||||
            return unless var1 == var2
 | 
			
		||||
 | 
			
		||||
            add_offense(node,
 | 
			
		||||
                        message: format(MSG_EXISTS_AND_NOT_EMPTY,
 | 
			
		||||
                                        prefer: replacement(var1),
 | 
			
		||||
                                        current: node.source))
 | 
			
		||||
            message = format(MSG_EXISTS_AND_NOT_EMPTY, prefer: replacement(var1), current: node.source)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, message: message) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -100,7 +105,9 @@ module RuboCop
 | 
			
		||||
          exists_and_not_empty?(node) do |var1, var2|
 | 
			
		||||
            return unless var1 == var2
 | 
			
		||||
 | 
			
		||||
            add_offense(node, message: MSG_EXISTS_AND_NOT_EMPTY)
 | 
			
		||||
            add_offense(node, message: MSG_EXISTS_AND_NOT_EMPTY) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -113,25 +120,24 @@ module RuboCop
 | 
			
		||||
            range = unless_condition(node, method_call)
 | 
			
		||||
            msg = format(MSG_UNLESS_BLANK, prefer: replacement(receiver),
 | 
			
		||||
                                           current: range.source)
 | 
			
		||||
            add_offense(node, location: range, message: msg)
 | 
			
		||||
            add_offense(range, message: msg) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            method_call, variable1 = unless_blank?(node)
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          method_call, variable1 = unless_blank?(node)
 | 
			
		||||
 | 
			
		||||
            if method_call
 | 
			
		||||
              corrector.replace(node.loc.keyword, 'if')
 | 
			
		||||
              range = method_call.loc.expression
 | 
			
		||||
            else
 | 
			
		||||
              variable1, _variable2 =
 | 
			
		||||
                exists_and_not_empty?(node) || not_blank?(node)
 | 
			
		||||
              range = node.loc.expression
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            corrector.replace(range, replacement(variable1))
 | 
			
		||||
          if method_call
 | 
			
		||||
            corrector.replace(node.loc.keyword, 'if')
 | 
			
		||||
            range = method_call.loc.expression
 | 
			
		||||
          else
 | 
			
		||||
            variable1, _variable2 = exists_and_not_empty?(node) || not_blank?(node)
 | 
			
		||||
            range = node.loc.expression
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          corrector.replace(range, replacement(variable1))
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
@ -25,7 +25,9 @@ module RuboCop
 | 
			
		||||
      #     do_something
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      class RakeEnvironment < Cop
 | 
			
		||||
      class RakeEnvironment < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Include `:environment` task as a dependency for all Rake tasks.'
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :task_definition?, <<~PATTERN
 | 
			
		||||
@ -37,16 +39,12 @@ module RuboCop
 | 
			
		||||
            return if task_name(task_method) == :default
 | 
			
		||||
            return if with_dependencies?(task_method)
 | 
			
		||||
 | 
			
		||||
            add_offense(task_method)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
            add_offense(task_method) do |corrector|
 | 
			
		||||
              task_name = task_method.arguments[0]
 | 
			
		||||
              task_dependency = correct_task_dependency(task_name)
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            task_name = node.arguments[0]
 | 
			
		||||
            task_dependency = correct_task_dependency(task_name)
 | 
			
		||||
 | 
			
		||||
            corrector.replace(task_name.loc.expression, task_dependency)
 | 
			
		||||
              corrector.replace(task_name.loc.expression, task_dependency)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -23,8 +23,11 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   x = self[:attr]
 | 
			
		||||
      #   self[:attr] = val
 | 
			
		||||
      class ReadWriteAttribute < Cop
 | 
			
		||||
      class ReadWriteAttribute < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[read_attribute write_attribute].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :read_write_attribute?, <<~PATTERN
 | 
			
		||||
          {
 | 
			
		||||
@ -36,18 +39,16 @@ module RuboCop
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless read_write_attribute?(node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
        end
 | 
			
		||||
          add_offense(node.loc.selector, message: message(node)) do |corrector|
 | 
			
		||||
            case node.method_name
 | 
			
		||||
            when :read_attribute
 | 
			
		||||
              replacement = read_attribute_replacement(node)
 | 
			
		||||
            when :write_attribute
 | 
			
		||||
              replacement = write_attribute_replacement(node)
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          case node.method_name
 | 
			
		||||
          when :read_attribute
 | 
			
		||||
            replacement = read_attribute_replacement(node)
 | 
			
		||||
          when :write_attribute
 | 
			
		||||
            replacement = write_attribute_replacement(node)
 | 
			
		||||
            corrector.replace(node.source_range, replacement)
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          ->(corrector) { corrector.replace(node.source_range, replacement) }
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
@ -26,8 +26,9 @@ module RuboCop
 | 
			
		||||
      #   # Here, `nil` is valid but `''` is not
 | 
			
		||||
      #   validates :x, length: { is: 5 }, allow_nil: true, allow_blank: false
 | 
			
		||||
      #
 | 
			
		||||
      class RedundantAllowNil < Cop
 | 
			
		||||
      class RedundantAllowNil < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG_SAME =
 | 
			
		||||
          '`allow_nil` is redundant when `allow_blank` has the same value.'
 | 
			
		||||
@ -35,59 +36,56 @@ module RuboCop
 | 
			
		||||
        MSG_ALLOW_NIL_FALSE =
 | 
			
		||||
          '`allow_nil: false` is redundant when `allow_blank` is true.'
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless node.method?(:validates)
 | 
			
		||||
        RESTRICT_ON_SEND = %i[validates].freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          allow_nil, allow_blank = find_allow_nil_and_allow_blank(node)
 | 
			
		||||
          return unless allow_nil && allow_blank
 | 
			
		||||
 | 
			
		||||
          allow_nil_val = allow_nil.children.last
 | 
			
		||||
          allow_blank_val = allow_blank.children.last
 | 
			
		||||
 | 
			
		||||
          offense(allow_nil_val, allow_blank_val, allow_nil)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          prv_sib = previous_sibling(node)
 | 
			
		||||
          nxt_sib = next_sibling(node)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            if nxt_sib
 | 
			
		||||
              corrector.remove(range_between(node_beg(node), node_beg(nxt_sib)))
 | 
			
		||||
            elsif prv_sib
 | 
			
		||||
              corrector.remove(range_between(node_end(prv_sib), node_end(node)))
 | 
			
		||||
            else
 | 
			
		||||
              corrector.remove(node.loc.expression)
 | 
			
		||||
            end
 | 
			
		||||
          if allow_nil_val.type == allow_blank_val.type
 | 
			
		||||
            register_offense(allow_nil, MSG_SAME)
 | 
			
		||||
          elsif allow_nil_val.false_type? && allow_blank_val.true_type?
 | 
			
		||||
            register_offense(allow_nil, MSG_ALLOW_NIL_FALSE)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def offense(allow_nil_val, allow_blank_val, allow_nil)
 | 
			
		||||
          if allow_nil_val.type == allow_blank_val.type
 | 
			
		||||
            add_offense(allow_nil, message: MSG_SAME)
 | 
			
		||||
          elsif allow_nil_val.false_type? && allow_blank_val.true_type?
 | 
			
		||||
            add_offense(allow_nil, message: MSG_ALLOW_NIL_FALSE)
 | 
			
		||||
        def register_offense(allow_nil, message)
 | 
			
		||||
          add_offense(allow_nil, message: message) do |corrector|
 | 
			
		||||
            prv_sib = previous_sibling(allow_nil)
 | 
			
		||||
            nxt_sib = next_sibling(allow_nil)
 | 
			
		||||
 | 
			
		||||
            if nxt_sib
 | 
			
		||||
              corrector.remove(range_between(node_beg(allow_nil), node_beg(nxt_sib)))
 | 
			
		||||
            elsif prv_sib
 | 
			
		||||
              corrector.remove(range_between(node_end(prv_sib), node_end(allow_nil)))
 | 
			
		||||
            else
 | 
			
		||||
              corrector.remove(allow_nil.loc.expression)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def find_allow_nil_and_allow_blank(node)
 | 
			
		||||
          allow_nil = nil
 | 
			
		||||
          allow_blank = nil
 | 
			
		||||
          allow_nil, allow_blank = nil
 | 
			
		||||
 | 
			
		||||
          node.each_descendant do |descendant|
 | 
			
		||||
            next unless descendant.pair_type?
 | 
			
		||||
          node.each_child_node do |child_node|
 | 
			
		||||
            if child_node.pair_type?
 | 
			
		||||
              key = child_node.children.first.source
 | 
			
		||||
 | 
			
		||||
            key = descendant.children.first.source
 | 
			
		||||
              allow_nil = child_node if key == 'allow_nil'
 | 
			
		||||
              allow_blank = child_node if key == 'allow_blank'
 | 
			
		||||
            end
 | 
			
		||||
            return [allow_nil, allow_blank] if allow_nil && allow_blank
 | 
			
		||||
 | 
			
		||||
            allow_nil = descendant if key == 'allow_nil'
 | 
			
		||||
            allow_blank = descendant if key == 'allow_blank'
 | 
			
		||||
 | 
			
		||||
            break if allow_nil && allow_blank
 | 
			
		||||
            found_in_children_nodes = find_allow_nil_and_allow_blank(child_node)
 | 
			
		||||
            return found_in_children_nodes if found_in_children_nodes
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          [allow_nil, allow_blank]
 | 
			
		||||
          nil
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def previous_sibling(node)
 | 
			
		||||
@ -24,10 +24,12 @@ module RuboCop
 | 
			
		||||
      #   class Comment
 | 
			
		||||
      #     belongs_to :author, foreign_key: 'user_id'
 | 
			
		||||
      #   end
 | 
			
		||||
      class RedundantForeignKey < Cop
 | 
			
		||||
      class RedundantForeignKey < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Specifying the default value for `foreign_key` is redundant.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[belongs_to has_one has_many has_and_belongs_to_many].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :association_with_foreign_key, <<~PATTERN
 | 
			
		||||
          (send nil? ${:belongs_to :has_one :has_many :has_and_belongs_to_many} ({sym str} $_)
 | 
			
		||||
@ -38,21 +40,16 @@ module RuboCop
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          association_with_foreign_key(node) do |type, name, options, foreign_key_pair, foreign_key|
 | 
			
		||||
            if redundant?(node, type, name, options, foreign_key)
 | 
			
		||||
              add_offense(node, location: foreign_key_pair.loc.expression)
 | 
			
		||||
              add_offense(foreign_key_pair.loc.expression) do |corrector|
 | 
			
		||||
                range = range_with_surrounding_space(range: foreign_key_pair.source_range, side: :left)
 | 
			
		||||
                range = range_with_surrounding_comma(range, :left)
 | 
			
		||||
 | 
			
		||||
                corrector.remove(range)
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          _type, _name, _options, foreign_key_pair, _foreign_key = association_with_foreign_key(node)
 | 
			
		||||
          range = range_with_surrounding_space(range: foreign_key_pair.source_range, side: :left)
 | 
			
		||||
          range = range_with_surrounding_comma(range, :left)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.remove(range)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def redundant?(node, association_type, association_name, options, foreign_key)
 | 
			
		||||
@ -54,8 +54,9 @@ module RuboCop
 | 
			
		||||
      #       merger.invoke(another_receiver)
 | 
			
		||||
      #     end
 | 
			
		||||
      #   end
 | 
			
		||||
      class RedundantReceiverInWithOptions < Cop
 | 
			
		||||
      class RedundantReceiverInWithOptions < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Redundant receiver in `with_options`.'
 | 
			
		||||
 | 
			
		||||
@ -86,22 +87,22 @@ module RuboCop
 | 
			
		||||
            if send_nodes.all? { |n| same_value?(arg, n.receiver) }
 | 
			
		||||
              send_nodes.each do |send_node|
 | 
			
		||||
                receiver = send_node.receiver
 | 
			
		||||
                add_offense(send_node, location: receiver.source_range)
 | 
			
		||||
                add_offense(receiver.source_range) do |corrector|
 | 
			
		||||
                  autocorrect(corrector, send_node)
 | 
			
		||||
                end
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.remove(node.receiver.source_range)
 | 
			
		||||
            corrector.remove(node.loc.dot)
 | 
			
		||||
            corrector.remove(block_argument_range(node))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          corrector.remove(node.receiver.source_range)
 | 
			
		||||
          corrector.remove(node.loc.dot)
 | 
			
		||||
          corrector.remove(block_argument_range(node))
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def block_argument_range(node)
 | 
			
		||||
          block_node = node.each_ancestor(:block).first
 | 
			
		||||
          block_argument = block_node.children[1].source_range
 | 
			
		||||
@ -13,8 +13,9 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   has_many :accounts, class_name: 'Account'
 | 
			
		||||
      class ReflectionClassName < Cop
 | 
			
		||||
      class ReflectionClassName < Base
 | 
			
		||||
        MSG = 'Use a string value for `class_name`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :association_with_reflection, <<~PATTERN
 | 
			
		||||
          (send nil? {:has_many :has_one :belongs_to} _ _ ?
 | 
			
		||||
@ -28,7 +29,7 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          association_with_reflection(node) do |reflection_class_name|
 | 
			
		||||
            add_offense(node, location: reflection_class_name.loc.expression)
 | 
			
		||||
            add_offense(reflection_class_name.loc.expression)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
@ -28,8 +28,9 @@ module RuboCop
 | 
			
		||||
      #   refute_empty [1, 2, 3]
 | 
			
		||||
      #   refute_equal true, false
 | 
			
		||||
      #
 | 
			
		||||
      class RefuteMethods < Cop
 | 
			
		||||
      class RefuteMethods < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Prefer `%<good_method>s` over `%<bad_method>s`.'
 | 
			
		||||
 | 
			
		||||
@ -53,21 +54,19 @@ module RuboCop
 | 
			
		||||
        REFUTE_METHODS = CORRECTIONS.keys.freeze
 | 
			
		||||
        ASSERT_NOT_METHODS = CORRECTIONS.values.freeze
 | 
			
		||||
 | 
			
		||||
        RESTRICT_ON_SEND = REFUTE_METHODS + ASSERT_NOT_METHODS
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :offensive?, '(send nil? #bad_method? ...)'
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless offensive?(node)
 | 
			
		||||
 | 
			
		||||
          message = offense_message(node.method_name)
 | 
			
		||||
          add_offense(node, location: :selector, message: message)
 | 
			
		||||
        end
 | 
			
		||||
          method_name = node.method_name
 | 
			
		||||
          message = offense_message(method_name)
 | 
			
		||||
          range = node.loc.selector
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          bad_method = node.method_name
 | 
			
		||||
          good_method = convert_good_method(bad_method)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(node.loc.selector, good_method.to_s)
 | 
			
		||||
          add_offense(range, message: message) do |corrector|
 | 
			
		||||
            corrector.replace(range, convert_good_method(method_name))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -27,15 +27,18 @@ module RuboCop
 | 
			
		||||
      #       1.week.since
 | 
			
		||||
      #     end
 | 
			
		||||
      #   end
 | 
			
		||||
      class RelativeDateConstant < Cop
 | 
			
		||||
      class RelativeDateConstant < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Do not assign %<method_name>s to constants as it ' \
 | 
			
		||||
              'will be evaluated only once.'
 | 
			
		||||
 | 
			
		||||
        def on_casgn(node)
 | 
			
		||||
          relative_date_assignment?(node) do |method_name|
 | 
			
		||||
            add_offense(node, message: format(MSG, method_name: method_name))
 | 
			
		||||
            add_offense(node, message: message(method_name)) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -48,9 +51,9 @@ module RuboCop
 | 
			
		||||
            next unless name.casgn_type?
 | 
			
		||||
 | 
			
		||||
            relative_date?(value) do |method_name|
 | 
			
		||||
              add_offense(node,
 | 
			
		||||
                          location: offense_range(name, value),
 | 
			
		||||
                          message: format(MSG, method_name: method_name))
 | 
			
		||||
              add_offense(offense_range(name, value), message: message(method_name)) do |corrector|
 | 
			
		||||
                autocorrect(corrector, node)
 | 
			
		||||
              end
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
@ -61,7 +64,9 @@ module RuboCop
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          return unless node.casgn_type?
 | 
			
		||||
 | 
			
		||||
          scope, const_name, value = *node
 | 
			
		||||
@ -71,10 +76,13 @@ module RuboCop
 | 
			
		||||
          new_code = ["def self.#{const_name.downcase}",
 | 
			
		||||
                      "#{indent}#{value.source}",
 | 
			
		||||
                      'end'].join("\n#{indent}")
 | 
			
		||||
          ->(corrector) { corrector.replace(node.source_range, new_code) }
 | 
			
		||||
 | 
			
		||||
          corrector.replace(node.source_range, new_code)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
        def message(method_name)
 | 
			
		||||
          format(MSG, method_name: method_name)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def offense_range(name, value)
 | 
			
		||||
          range_between(name.loc.expression.begin_pos, value.loc.expression.end_pos)
 | 
			
		||||
@ -24,8 +24,9 @@ module RuboCop
 | 
			
		||||
      #     end
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      class RenderInline < Cop
 | 
			
		||||
      class RenderInline < Base
 | 
			
		||||
        MSG = 'Prefer using a template over inline rendering.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[render].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :render_with_inline_option?, <<~PATTERN
 | 
			
		||||
          (send nil? :render (hash <(pair {(sym :inline) (str "inline")} _) ...>))
 | 
			
		||||
@ -24,30 +24,25 @@ module RuboCop
 | 
			
		||||
      #   # bad - sets MIME type to `text/html`
 | 
			
		||||
      #   render text: 'Ruby!'
 | 
			
		||||
      #
 | 
			
		||||
      class RenderPlainText < Cop
 | 
			
		||||
      class RenderPlainText < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Prefer `render plain:` over `render text:`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[render].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :render_plain_text?, <<~PATTERN
 | 
			
		||||
          (send nil? :render $(hash <$(pair (sym :text) $_) ...>))
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          render_plain_text?(node) do |options_node, _option_node, _option_value|
 | 
			
		||||
            content_type_node = find_content_type(options_node)
 | 
			
		||||
            add_offense(node) if compatible_content_type?(content_type_node)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          render_plain_text?(node) do |options_node, option_node, option_value|
 | 
			
		||||
            content_type_node = find_content_type(options_node)
 | 
			
		||||
            rest_options = options_node.pairs - [option_node, content_type_node].compact
 | 
			
		||||
            return unless compatible_content_type?(content_type_node)
 | 
			
		||||
 | 
			
		||||
            lambda do |corrector|
 | 
			
		||||
              corrector.replace(
 | 
			
		||||
                node,
 | 
			
		||||
                replacement(rest_options, option_value)
 | 
			
		||||
              )
 | 
			
		||||
            add_offense(node) do |corrector|
 | 
			
		||||
              rest_options = options_node.pairs - [option_node, content_type_node].compact
 | 
			
		||||
 | 
			
		||||
              corrector.replace(node, replacement(rest_options, option_value))
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
@ -19,11 +19,13 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   request.referrer
 | 
			
		||||
      class RequestReferer < Cop
 | 
			
		||||
      class RequestReferer < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `request.%<prefer>s` instead of ' \
 | 
			
		||||
              '`request.%<current>s`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[referer referrer].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :referer?, <<~PATTERN
 | 
			
		||||
          (send (send nil? :request) {:referer :referrer})
 | 
			
		||||
@ -33,17 +35,15 @@ module RuboCop
 | 
			
		||||
          referer?(node) do
 | 
			
		||||
            return unless node.method?(wrong_method_name)
 | 
			
		||||
 | 
			
		||||
            add_offense(node.source_range, location: node.source_range)
 | 
			
		||||
            add_offense(node.source_range) do |corrector|
 | 
			
		||||
              corrector.replace(node, "request.#{style}")
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          ->(corrector) { corrector.replace(node, "request.#{style}") }
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def message(_node)
 | 
			
		||||
        def message(_range)
 | 
			
		||||
          format(MSG, prefer: style, current: wrong_method_name)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -175,7 +175,7 @@ module RuboCop
 | 
			
		||||
      #   end
 | 
			
		||||
      #
 | 
			
		||||
      # @see https://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html
 | 
			
		||||
      class ReversibleMigration < Cop
 | 
			
		||||
      class ReversibleMigration < Base
 | 
			
		||||
        MSG = '%<action>s is not reversible.'
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :irreversible_schema_statement_call, <<~PATTERN
 | 
			
		||||
@ -271,11 +271,7 @@ module RuboCop
 | 
			
		||||
        def check_remove_foreign_key_node(node)
 | 
			
		||||
          remove_foreign_key_call(node) do |arg|
 | 
			
		||||
            if arg.hash_type? && !all_hash_key?(arg, :to_table)
 | 
			
		||||
              add_offense(
 | 
			
		||||
                node,
 | 
			
		||||
                message: format(MSG,
 | 
			
		||||
                                action: 'remove_foreign_key(without table)')
 | 
			
		||||
              )
 | 
			
		||||
              add_offense(node, message: format(MSG, action: 'remove_foreign_key(without table)'))
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
@ -36,10 +36,12 @@ module RuboCop
 | 
			
		||||
      #   foo&.bar
 | 
			
		||||
      #   foo&.bar(baz)
 | 
			
		||||
      #   foo&.bar { |e| e.baz }
 | 
			
		||||
      class SafeNavigation < Cop
 | 
			
		||||
      class SafeNavigation < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use safe navigation (`&.`) instead of `%<try>s`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[try try!].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :try_call, <<~PATTERN
 | 
			
		||||
          (send !nil? ${:try :try!} $_ ...)
 | 
			
		||||
@ -50,24 +52,23 @@ module RuboCop
 | 
			
		||||
            return if try_method == :try && !cop_config['ConvertTry']
 | 
			
		||||
            return unless dispatch.sym_type? && dispatch.value.match?(/\w+[=!?]?/)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, message: format(MSG, try: try_method))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          method_node, *params = *node.arguments
 | 
			
		||||
          method = method_node.source[1..-1]
 | 
			
		||||
 | 
			
		||||
          range = range_between(node.loc.dot.begin_pos,
 | 
			
		||||
                                node.loc.expression.end_pos)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(range, replacement(method, params))
 | 
			
		||||
            add_offense(node, message: format(MSG, try: try_method)) do |corrector|
 | 
			
		||||
              autocorrect(corrector, node)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          method_node, *params = *node.arguments
 | 
			
		||||
          method = method_node.source[1..-1]
 | 
			
		||||
 | 
			
		||||
          range = range_between(node.loc.dot.begin_pos, node.loc.expression.end_pos)
 | 
			
		||||
 | 
			
		||||
          corrector.replace(range, replacement(method, params))
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def replacement(method, params)
 | 
			
		||||
          new_params = params.map(&:source).join(', ')
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,9 @@ module RuboCop
 | 
			
		||||
      #   do_something if foo.blank?
 | 
			
		||||
      #   do_something unless foo.blank?
 | 
			
		||||
      #
 | 
			
		||||
      class SafeNavigationWithBlank < Cop
 | 
			
		||||
      class SafeNavigationWithBlank < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG =
 | 
			
		||||
          'Avoid calling `blank?` with the safe navigation operator ' \
 | 
			
		||||
          'in conditionals.'
 | 
			
		||||
@ -31,15 +33,8 @@ module RuboCop
 | 
			
		||||
        def on_if(node)
 | 
			
		||||
          return unless safe_navigation_blank_in_conditional?(node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(
 | 
			
		||||
              safe_navigation_blank_in_conditional?(node).location.dot,
 | 
			
		||||
              '.'
 | 
			
		||||
            )
 | 
			
		||||
          add_offense(node) do |corrector|
 | 
			
		||||
            corrector.replace(safe_navigation_blank_in_conditional?(node).location.dot, '.')
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
@ -98,8 +98,9 @@ module RuboCop
 | 
			
		||||
      #   Services::Service::Mailer.update(message: 'Message')
 | 
			
		||||
      #   Service::Mailer::update
 | 
			
		||||
      #
 | 
			
		||||
      class SaveBang < Cop
 | 
			
		||||
      class SaveBang < Base
 | 
			
		||||
        include NegativeConditional
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `%<prefer>s` instead of `%<current>s` if the return ' \
 | 
			
		||||
              'value is not checked.'
 | 
			
		||||
@ -113,11 +114,10 @@ module RuboCop
 | 
			
		||||
                                    first_or_create find_or_create_by].freeze
 | 
			
		||||
        MODIFY_PERSIST_METHODS = %i[save
 | 
			
		||||
                                    update update_attributes destroy].freeze
 | 
			
		||||
        PERSIST_METHODS = (CREATE_PERSIST_METHODS +
 | 
			
		||||
                           MODIFY_PERSIST_METHODS).freeze
 | 
			
		||||
        RESTRICT_ON_SEND = (CREATE_PERSIST_METHODS + MODIFY_PERSIST_METHODS).freeze
 | 
			
		||||
 | 
			
		||||
        def join_force?(force_class)
 | 
			
		||||
          force_class == VariableForce
 | 
			
		||||
        def self.joining_forces
 | 
			
		||||
          VariableForce
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def after_leaving_scope(scope, _variable_table)
 | 
			
		||||
@ -135,7 +135,7 @@ module RuboCop
 | 
			
		||||
          return unless persist_method?(node, CREATE_PERSIST_METHODS)
 | 
			
		||||
          return if persisted_referenced?(assignment)
 | 
			
		||||
 | 
			
		||||
          add_offense_for_node(node, CREATE_MSG)
 | 
			
		||||
          register_offense(node, CREATE_MSG)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # rubocop:disable Metrics/CyclomaticComplexity
 | 
			
		||||
@ -148,25 +148,22 @@ module RuboCop
 | 
			
		||||
          return if explicit_return?(node)
 | 
			
		||||
          return if checked_immediately?(node)
 | 
			
		||||
 | 
			
		||||
          add_offense_for_node(node)
 | 
			
		||||
          register_offense(node, MSG)
 | 
			
		||||
        end
 | 
			
		||||
        # rubocop:enable Metrics/CyclomaticComplexity
 | 
			
		||||
        alias on_csend on_send
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          save_loc = node.loc.selector
 | 
			
		||||
          new_method = "#{node.method_name}!"
 | 
			
		||||
 | 
			
		||||
          ->(corrector) { corrector.replace(save_loc, new_method) }
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def add_offense_for_node(node, msg = MSG)
 | 
			
		||||
          name = node.method_name
 | 
			
		||||
          full_message = format(msg, prefer: "#{name}!", current: name.to_s)
 | 
			
		||||
        def register_offense(node, msg)
 | 
			
		||||
          current_method = node.method_name
 | 
			
		||||
          bang_method = "#{current_method}!"
 | 
			
		||||
          full_message = format(msg, prefer: bang_method, current: current_method)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector, message: full_message)
 | 
			
		||||
          range = node.loc.selector
 | 
			
		||||
          add_offense(range, message: full_message) do |corrector|
 | 
			
		||||
            corrector.replace(range, bang_method)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def right_assignment_node(assignment)
 | 
			
		||||
@ -218,7 +215,7 @@ module RuboCop
 | 
			
		||||
        def check_used_in_condition_or_compound_boolean(node)
 | 
			
		||||
          return false unless in_condition_or_compound_boolean?(node)
 | 
			
		||||
 | 
			
		||||
          add_offense_for_node(node, CREATE_CONDITIONAL_MSG) unless MODIFY_PERSIST_METHODS.include?(node.method_name)
 | 
			
		||||
          register_offense(node, CREATE_CONDITIONAL_MSG) unless MODIFY_PERSIST_METHODS.include?(node.method_name)
 | 
			
		||||
 | 
			
		||||
          true
 | 
			
		||||
        end
 | 
			
		||||
@ -318,7 +315,7 @@ module RuboCop
 | 
			
		||||
          assignment&.lvasgn_type?
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def persist_method?(node, methods = PERSIST_METHODS)
 | 
			
		||||
        def persist_method?(node, methods = RESTRICT_ON_SEND)
 | 
			
		||||
          methods.include?(node.method_name) &&
 | 
			
		||||
            expected_signature?(node) &&
 | 
			
		||||
            !allowed_receiver?(node)
 | 
			
		||||
@ -13,8 +13,9 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   scope :something, -> { where(something: true) }
 | 
			
		||||
      class ScopeArgs < Cop
 | 
			
		||||
      class ScopeArgs < Base
 | 
			
		||||
        MSG = 'Use `lambda`/`proc` instead of a plain method call.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[scope].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :scope?, '(send nil? :scope _ $send)'
 | 
			
		||||
 | 
			
		||||
@ -38,8 +38,9 @@ module RuboCop
 | 
			
		||||
      #   t :key
 | 
			
		||||
      #   l Time.now
 | 
			
		||||
      #
 | 
			
		||||
      class ShortI18n < Cop
 | 
			
		||||
      class ShortI18n < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
 | 
			
		||||
 | 
			
		||||
@ -48,6 +49,8 @@ module RuboCop
 | 
			
		||||
          localize: :l
 | 
			
		||||
        }.freeze
 | 
			
		||||
 | 
			
		||||
        RESTRICT_ON_SEND = PREFERRED_METHODS.keys.freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :long_i18n?, <<~PATTERN
 | 
			
		||||
          (send {nil? (const nil? :I18n)} ${:translate :localize} ...)
 | 
			
		||||
        PATTERN
 | 
			
		||||
@ -58,15 +61,10 @@ module RuboCop
 | 
			
		||||
          long_i18n?(node) do |method_name|
 | 
			
		||||
            good_method = PREFERRED_METHODS[method_name]
 | 
			
		||||
            message = format(MSG, good_method: good_method, bad_method: method_name)
 | 
			
		||||
            range = node.loc.selector
 | 
			
		||||
 | 
			
		||||
            add_offense(node, location: :selector, message: message)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          long_i18n?(node) do |method_name|
 | 
			
		||||
            lambda do |corrector|
 | 
			
		||||
              corrector.replace(node.loc.selector, PREFERRED_METHODS[method_name])
 | 
			
		||||
            add_offense(range, message: message) do |corrector|
 | 
			
		||||
              corrector.replace(range, PREFERRED_METHODS[method_name])
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
@ -7,7 +7,7 @@ module RuboCop
 | 
			
		||||
      # validations which are listed in
 | 
			
		||||
      # https://guides.rubyonrails.org/active_record_validations.html#skipping-validations
 | 
			
		||||
      #
 | 
			
		||||
      # Methods may be ignored from this rule by configuring a `Whitelist`.
 | 
			
		||||
      # Methods may be ignored from this rule by configuring a `AllowedMethods`.
 | 
			
		||||
      #
 | 
			
		||||
      # @example
 | 
			
		||||
      #   # bad
 | 
			
		||||
@ -26,7 +26,7 @@ module RuboCop
 | 
			
		||||
      #   user.update(website: 'example.com')
 | 
			
		||||
      #   FileUtils.touch('file')
 | 
			
		||||
      #
 | 
			
		||||
      # @example Whitelist: ["touch"]
 | 
			
		||||
      # @example AllowedMethods: ["touch"]
 | 
			
		||||
      #   # bad
 | 
			
		||||
      #   DiscussionBoard.decrement_counter(:post_count, 5)
 | 
			
		||||
      #   DiscussionBoard.increment_counter(:post_count, 5)
 | 
			
		||||
@ -35,7 +35,7 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   user.touch
 | 
			
		||||
      #
 | 
			
		||||
      class SkipsModelValidations < Cop
 | 
			
		||||
      class SkipsModelValidations < Base
 | 
			
		||||
        MSG = 'Avoid using `%<method>s` because it skips validations.'
 | 
			
		||||
 | 
			
		||||
        METHODS_WITH_ARGUMENTS = %w[decrement!
 | 
			
		||||
@ -76,7 +76,7 @@ module RuboCop
 | 
			
		||||
          return if good_touch?(node)
 | 
			
		||||
          return if good_insert?(node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
          add_offense(node.loc.selector, message: message(node))
 | 
			
		||||
        end
 | 
			
		||||
        alias on_csend on_send
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,8 @@ module RuboCop
 | 
			
		||||
    module Rails
 | 
			
		||||
      #
 | 
			
		||||
      # Checks SQL heredocs to use `.squish`.
 | 
			
		||||
      # Some SQL syntax (e.g. PostgreSQL comments and functions) requires newlines
 | 
			
		||||
      # to be preserved in order to work, thus auto-correction for this cop is not safe.
 | 
			
		||||
      #
 | 
			
		||||
      # @example
 | 
			
		||||
      #   # bad
 | 
			
		||||
@ -37,8 +39,9 @@ module RuboCop
 | 
			
		||||
      #       WHERE post_id = 1
 | 
			
		||||
      #   SQL
 | 
			
		||||
      #
 | 
			
		||||
      class SquishedSQLHeredocs < Cop
 | 
			
		||||
      class SquishedSQLHeredocs < Base
 | 
			
		||||
        include Heredoc
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        SQL = 'SQL'
 | 
			
		||||
        SQUISH = '.squish'
 | 
			
		||||
@ -47,11 +50,7 @@ module RuboCop
 | 
			
		||||
        def on_heredoc(node)
 | 
			
		||||
          return unless offense_detected?(node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
          add_offense(node) do |corrector|
 | 
			
		||||
            corrector.insert_after(node, SQUISH)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
@ -43,8 +43,9 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   Time.current
 | 
			
		||||
      #   Time.at(timestamp).in_time_zone
 | 
			
		||||
      class TimeZone < Cop
 | 
			
		||||
      class TimeZone < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Do not use `%<current>s` without zone. Use `%<prefer>s` ' \
 | 
			
		||||
              'instead.'
 | 
			
		||||
@ -71,27 +72,25 @@ module RuboCop
 | 
			
		||||
          check_time_node(klass, node.parent) if klass == :Time
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            # add `.zone`: `Time.at` => `Time.zone.at`
 | 
			
		||||
            corrector.insert_after(node.children[0].source_range, '.zone')
 | 
			
		||||
 | 
			
		||||
            case node.method_name
 | 
			
		||||
            when :current
 | 
			
		||||
              # replace `Time.zone.current` => `Time.zone.now`
 | 
			
		||||
              corrector.replace(node.loc.selector, 'now')
 | 
			
		||||
            when :new
 | 
			
		||||
              autocorrect_time_new(node, corrector)
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            # prefer `Time` over `DateTime` class
 | 
			
		||||
            corrector.replace(node.children.first.source_range, 'Time') if strict?
 | 
			
		||||
            remove_redundant_in_time_zone(corrector, node)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def autocorrect(corrector, node)
 | 
			
		||||
          # add `.zone`: `Time.at` => `Time.zone.at`
 | 
			
		||||
          corrector.insert_after(node.children[0].source_range, '.zone')
 | 
			
		||||
 | 
			
		||||
          case node.method_name
 | 
			
		||||
          when :current
 | 
			
		||||
            # replace `Time.zone.current` => `Time.zone.now`
 | 
			
		||||
            corrector.replace(node.loc.selector, 'now')
 | 
			
		||||
          when :new
 | 
			
		||||
            autocorrect_time_new(node, corrector)
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          # prefer `Time` over `DateTime` class
 | 
			
		||||
          corrector.replace(node.children.first.source_range, 'Time') if strict?
 | 
			
		||||
          remove_redundant_in_time_zone(corrector, node)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect_time_new(node, corrector)
 | 
			
		||||
          if node.arguments?
 | 
			
		||||
            corrector.replace(node.loc.selector, 'local')
 | 
			
		||||
@ -128,7 +127,9 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
          message = build_message(klass, method_name, node)
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector, message: message)
 | 
			
		||||
          add_offense(node.loc.selector, message: message) do |corrector|
 | 
			
		||||
            autocorrect(corrector, node)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def build_message(klass, method_name, node)
 | 
			
		||||
@ -193,8 +194,9 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
          return if node.arguments?
 | 
			
		||||
 | 
			
		||||
          add_offense(selector_node,
 | 
			
		||||
                      location: :selector, message: MSG_LOCALTIME)
 | 
			
		||||
          add_offense(selector_node.loc.selector, message: MSG_LOCALTIME) do |corrector|
 | 
			
		||||
            autocorrect(corrector, selector_node)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def not_danger_chain?(chain)
 | 
			
		||||
@ -45,11 +45,13 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   Model.distinct.pluck(:id)
 | 
			
		||||
      #
 | 
			
		||||
      class UniqBeforePluck < RuboCop::Cop::Cop
 | 
			
		||||
      class UniqBeforePluck < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `distinct` before `pluck`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[uniq distinct pluck].freeze
 | 
			
		||||
        NEWLINE = "\n"
 | 
			
		||||
        PATTERN = '[!^block (send (send %<type>s :pluck ...) ' \
 | 
			
		||||
                  '${:uniq :distinct} ...)]'
 | 
			
		||||
@ -69,11 +71,7 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
          return unless method
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
          add_offense(node.loc.selector) do |corrector|
 | 
			
		||||
            method = node.method_name
 | 
			
		||||
 | 
			
		||||
            corrector.remove(dot_method_with_whitespace(method, node))
 | 
			
		||||
@ -24,13 +24,13 @@ module RuboCop
 | 
			
		||||
      #   # good - even if the schema does not have a unique index
 | 
			
		||||
      #   validates :account, length: { minimum: MIN_LENGTH }
 | 
			
		||||
      #
 | 
			
		||||
      class UniqueValidationWithoutIndex < Cop
 | 
			
		||||
      class UniqueValidationWithoutIndex < Base
 | 
			
		||||
        include ActiveRecordHelper
 | 
			
		||||
 | 
			
		||||
        MSG = 'Uniqueness validation should be with a unique index.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[validates].freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless node.method?(:validates)
 | 
			
		||||
          return unless uniqueness_part(node)
 | 
			
		||||
          return if condition_part?(node)
 | 
			
		||||
          return unless schema
 | 
			
		||||
@ -17,7 +17,7 @@ module RuboCop
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   Rails.env.production?
 | 
			
		||||
      #   Rails.env == 'production'
 | 
			
		||||
      class UnknownEnv < Cop
 | 
			
		||||
      class UnknownEnv < Base
 | 
			
		||||
        MSG = 'Unknown environment `%<name>s`.'
 | 
			
		||||
        MSG_SIMILAR = 'Unknown environment `%<name>s`. ' \
 | 
			
		||||
                      'Did you mean `%<similar>s`?'
 | 
			
		||||
@ -41,7 +41,7 @@ module RuboCop
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          unknown_environment_predicate?(node) do |name|
 | 
			
		||||
            add_offense(node, location: :selector, message: message(name))
 | 
			
		||||
            add_offense(node.loc.selector, message: message(name))
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          unknown_environment_equal?(node) do |str_node|
 | 
			
		||||
@ -32,7 +32,9 @@ module RuboCop
 | 
			
		||||
      #   validates :foo, size: true
 | 
			
		||||
      #   validates :foo, uniqueness: true
 | 
			
		||||
      #
 | 
			
		||||
      class Validation < Cop
 | 
			
		||||
      class Validation < Base
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Prefer the new style validations `%<prefer>s` over ' \
 | 
			
		||||
              '`%<current>s`.'
 | 
			
		||||
 | 
			
		||||
@ -50,22 +52,20 @@ module RuboCop
 | 
			
		||||
          uniqueness
 | 
			
		||||
        ].freeze
 | 
			
		||||
 | 
			
		||||
        DENYLIST = TYPES.map { |p| "validates_#{p}_of".to_sym }.freeze
 | 
			
		||||
        RESTRICT_ON_SEND = TYPES.map { |p| "validates_#{p}_of".to_sym }.freeze
 | 
			
		||||
        ALLOWLIST = TYPES.map { |p| "validates :column, #{p}: value" }.freeze
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          return unless !node.receiver && DENYLIST.include?(node.method_name)
 | 
			
		||||
          return if node.receiver
 | 
			
		||||
 | 
			
		||||
          add_offense(node, location: :selector)
 | 
			
		||||
        end
 | 
			
		||||
          range = node.loc.selector
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          last_argument = node.arguments.last
 | 
			
		||||
          return if !last_argument.literal? && !last_argument.splat_type? &&
 | 
			
		||||
                    !frozen_array_argument?(last_argument)
 | 
			
		||||
          add_offense(range, message: message(node)) do |corrector|
 | 
			
		||||
            last_argument = node.arguments.last
 | 
			
		||||
            return if !last_argument.literal? && !last_argument.splat_type? &&
 | 
			
		||||
                      !frozen_array_argument?(last_argument)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(node.loc.selector, 'validates')
 | 
			
		||||
            corrector.replace(range, 'validates')
 | 
			
		||||
            correct_validate_type(corrector, node)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
@ -73,12 +73,13 @@ module RuboCop
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def message(node)
 | 
			
		||||
          format(MSG, prefer: preferred_method(node.method_name),
 | 
			
		||||
                      current: node.method_name)
 | 
			
		||||
          method_name = node.method_name
 | 
			
		||||
 | 
			
		||||
          format(MSG, prefer: preferred_method(method_name), current: method_name)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def preferred_method(method)
 | 
			
		||||
          ALLOWLIST[DENYLIST.index(method.to_sym)]
 | 
			
		||||
          ALLOWLIST[RESTRICT_ON_SEND.index(method.to_sym)]
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def correct_validate_type(corrector, node)
 | 
			
		||||
@ -0,0 +1,94 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    module Rails
 | 
			
		||||
      # This cop identifies places where manually constructed SQL
 | 
			
		||||
      # in `where` can be replaced with `where(attribute: value)`.
 | 
			
		||||
      #
 | 
			
		||||
      # @example
 | 
			
		||||
      #   # bad
 | 
			
		||||
      #   User.where('name = ?', 'Gabe')
 | 
			
		||||
      #   User.where('name = :name', name: 'Gabe')
 | 
			
		||||
      #   User.where('name IS NULL')
 | 
			
		||||
      #   User.where('name IN (?)', ['john', 'jane'])
 | 
			
		||||
      #   User.where('name IN (:names)', names: ['john', 'jane'])
 | 
			
		||||
      #
 | 
			
		||||
      #   # good
 | 
			
		||||
      #   User.where(name: 'Gabe')
 | 
			
		||||
      #   User.where(name: nil)
 | 
			
		||||
      #   User.where(name: ['john', 'jane'])
 | 
			
		||||
      class WhereEquals < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `%<good_method>s` instead of manually constructing SQL.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[where].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :where_method_call?, <<~PATTERN
 | 
			
		||||
          {
 | 
			
		||||
            (send _ :where (array $str_type? $_ ?))
 | 
			
		||||
            (send _ :where $str_type? $_ ?)
 | 
			
		||||
          }
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          where_method_call?(node) do |template_node, value_node|
 | 
			
		||||
            value_node = value_node.first
 | 
			
		||||
 | 
			
		||||
            range = offense_range(node)
 | 
			
		||||
 | 
			
		||||
            column_and_value = extract_column_and_value(template_node, value_node)
 | 
			
		||||
            return unless column_and_value
 | 
			
		||||
 | 
			
		||||
            good_method = build_good_method(*column_and_value)
 | 
			
		||||
            message = format(MSG, good_method: good_method)
 | 
			
		||||
 | 
			
		||||
            add_offense(range, message: message) do |corrector|
 | 
			
		||||
              corrector.replace(range, good_method)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        EQ_ANONYMOUS_RE = /\A([\w.]+)\s+=\s+\?\z/.freeze             # column = ?
 | 
			
		||||
        IN_ANONYMOUS_RE = /\A([\w.]+)\s+IN\s+\(\?\)\z/i.freeze       # column IN (?)
 | 
			
		||||
        EQ_NAMED_RE     = /\A([\w.]+)\s+=\s+:(\w+)\z/.freeze         # column = :column
 | 
			
		||||
        IN_NAMED_RE     = /\A([\w.]+)\s+IN\s+\(:(\w+)\)\z/i.freeze   # column IN (:column)
 | 
			
		||||
        IS_NULL_RE      = /\A([\w.]+)\s+IS\s+NULL\z/i.freeze         # column IS NULL
 | 
			
		||||
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def offense_range(node)
 | 
			
		||||
          range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def extract_column_and_value(template_node, value_node)
 | 
			
		||||
          value =
 | 
			
		||||
            case template_node.value
 | 
			
		||||
            when EQ_ANONYMOUS_RE, IN_ANONYMOUS_RE
 | 
			
		||||
              value_node.source
 | 
			
		||||
            when EQ_NAMED_RE, IN_NAMED_RE
 | 
			
		||||
              return unless value_node.hash_type?
 | 
			
		||||
 | 
			
		||||
              pair = value_node.pairs.find { |p| p.key.value.to_sym == Regexp.last_match(2).to_sym }
 | 
			
		||||
              pair.value.source
 | 
			
		||||
            when IS_NULL_RE
 | 
			
		||||
              'nil'
 | 
			
		||||
            else
 | 
			
		||||
              return
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
          [Regexp.last_match(1), value]
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def build_good_method(column, value)
 | 
			
		||||
          if column.include?('.')
 | 
			
		||||
            "where('#{column}' => #{value})"
 | 
			
		||||
          else
 | 
			
		||||
            "where(#{column}: #{value})"
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -36,10 +36,12 @@ module RuboCop
 | 
			
		||||
      #   User.where('name = ?', 'john').exists?
 | 
			
		||||
      #   user.posts.where(published: true).exists?
 | 
			
		||||
      #   User.where('length(name) > 10').exists?
 | 
			
		||||
      class WhereExists < Cop
 | 
			
		||||
      class WhereExists < Base
 | 
			
		||||
        include ConfigurableEnforcedStyle
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Prefer `%<good_method>s` over `%<bad_method>s`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[exists?].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :where_exists_call?, <<~PATTERN
 | 
			
		||||
          (send (send _ :where $...) :exists?)
 | 
			
		||||
@ -54,19 +56,12 @@ module RuboCop
 | 
			
		||||
            return unless convertable_args?(args)
 | 
			
		||||
 | 
			
		||||
            range = correction_range(node)
 | 
			
		||||
            message = format(MSG, good_method: build_good_method(args), bad_method: range.source)
 | 
			
		||||
            add_offense(node, location: range, message: message)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
            good_method = build_good_method(args)
 | 
			
		||||
            message = format(MSG, good_method: good_method, bad_method: range.source)
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          args = find_offenses(node)
 | 
			
		||||
 | 
			
		||||
          lambda do |corrector|
 | 
			
		||||
            corrector.replace(
 | 
			
		||||
              correction_range(node),
 | 
			
		||||
              build_good_method(args)
 | 
			
		||||
            )
 | 
			
		||||
            add_offense(range, message: message) do |corrector|
 | 
			
		||||
              corrector.replace(range, good_method)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -21,10 +21,12 @@ module RuboCop
 | 
			
		||||
      #   User.where.not(name: nil)
 | 
			
		||||
      #   User.where.not(name: ['john', 'jane'])
 | 
			
		||||
      #
 | 
			
		||||
      class WhereNot < Cop
 | 
			
		||||
      class WhereNot < Base
 | 
			
		||||
        include RangeHelp
 | 
			
		||||
        extend AutoCorrector
 | 
			
		||||
 | 
			
		||||
        MSG = 'Use `%<good_method>s` instead of manually constructing negated SQL in `where`.'
 | 
			
		||||
        RESTRICT_ON_SEND = %i[where].freeze
 | 
			
		||||
 | 
			
		||||
        def_node_matcher :where_method_call?, <<~PATTERN
 | 
			
		||||
          {
 | 
			
		||||
@ -45,21 +47,8 @@ module RuboCop
 | 
			
		||||
            good_method = build_good_method(*column_and_value)
 | 
			
		||||
            message = format(MSG, good_method: good_method)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, location: range, message: message)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect(node)
 | 
			
		||||
          where_method_call?(node) do |template_node, value_node|
 | 
			
		||||
            value_node = value_node.first
 | 
			
		||||
 | 
			
		||||
            lambda do |corrector|
 | 
			
		||||
              range = offense_range(node)
 | 
			
		||||
 | 
			
		||||
              column, value = *extract_column_and_value(template_node, value_node)
 | 
			
		||||
              replacement = build_good_method(column, value)
 | 
			
		||||
 | 
			
		||||
              corrector.replace(range, replacement)
 | 
			
		||||
            add_offense(range, message: message) do |corrector|
 | 
			
		||||
              corrector.replace(range, good_method)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require_relative 'mixin/active_record_helper'
 | 
			
		||||
require_relative 'mixin/enforce_superclass'
 | 
			
		||||
require_relative 'mixin/index_method'
 | 
			
		||||
require_relative 'mixin/target_rails_version'
 | 
			
		||||
 | 
			
		||||
@ -14,7 +15,9 @@ require_relative 'rails/application_controller'
 | 
			
		||||
require_relative 'rails/application_job'
 | 
			
		||||
require_relative 'rails/application_mailer'
 | 
			
		||||
require_relative 'rails/application_record'
 | 
			
		||||
require_relative 'rails/arel_star'
 | 
			
		||||
require_relative 'rails/assert_not'
 | 
			
		||||
require_relative 'rails/attribute_default_block_value'
 | 
			
		||||
require_relative 'rails/belongs_to'
 | 
			
		||||
require_relative 'rails/blank'
 | 
			
		||||
require_relative 'rails/bulk_change_table'
 | 
			
		||||
@ -83,5 +86,6 @@ require_relative 'rails/uniq_before_pluck'
 | 
			
		||||
require_relative 'rails/unique_validation_without_index'
 | 
			
		||||
require_relative 'rails/unknown_env'
 | 
			
		||||
require_relative 'rails/validation'
 | 
			
		||||
require_relative 'rails/where_equals'
 | 
			
		||||
require_relative 'rails/where_exists'
 | 
			
		||||
require_relative 'rails/where_not'
 | 
			
		||||
@ -13,15 +13,15 @@ module RuboCop
 | 
			
		||||
      #
 | 
			
		||||
      # @return [Schema, nil]
 | 
			
		||||
      def load(target_ruby_version)
 | 
			
		||||
        return @schema if defined?(@schema)
 | 
			
		||||
        return @load if defined?(@load)
 | 
			
		||||
 | 
			
		||||
        @schema = load!(target_ruby_version)
 | 
			
		||||
        @load = load!(target_ruby_version)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def reset!
 | 
			
		||||
        return unless instance_variable_defined?(:@schema)
 | 
			
		||||
        return unless instance_variable_defined?(:@load)
 | 
			
		||||
 | 
			
		||||
        remove_instance_variable(:@schema)
 | 
			
		||||
        remove_instance_variable(:@load)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def db_schema_path
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user