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