brew vendor-gems: commit updates.
This commit is contained in:
parent
b07ccd0f16
commit
0a6e953d77
@ -88,7 +88,7 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-progressbar-1.11
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-2.1.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-1.24.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.12.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rails-2.12.4/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rails-2.13.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-2.7.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-sorbet-0.6.5/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.5.1/lib"
|
||||
|
||||
@ -180,6 +180,12 @@ Rails/BulkChangeTable:
|
||||
Include:
|
||||
- db/migrate/*.rb
|
||||
|
||||
Rails/CompactBlank:
|
||||
Description: 'Checks if collection can be blank-compacted with `compact_blank`.'
|
||||
Enabled: pending
|
||||
Safe: false
|
||||
VersionAdded: '2.13'
|
||||
|
||||
Rails/ContentTag:
|
||||
Description: 'Use `tag.something` instead of `tag(:something)`.'
|
||||
Reference:
|
||||
@ -245,6 +251,12 @@ Rails/DelegateAllowBlank:
|
||||
Enabled: true
|
||||
VersionAdded: '0.44'
|
||||
|
||||
Rails/DurationArithmetic:
|
||||
Description: 'Do not use duration as arithmetic operand with `Time.current`.'
|
||||
StyleGuide: 'https://rails.rubystyle.guide#duration-arithmetic'
|
||||
Enabled: pending
|
||||
VersionAdded: '2.13'
|
||||
|
||||
Rails/DynamicFindBy:
|
||||
Description: 'Use `find_by` instead of dynamic `find_by_*`.'
|
||||
StyleGuide: 'https://rails.rubystyle.guide#find_by'
|
||||
@ -612,6 +624,11 @@ Rails/RedundantForeignKey:
|
||||
Enabled: true
|
||||
VersionAdded: '2.6'
|
||||
|
||||
Rails/RedundantPresenceValidationOnBelongsTo:
|
||||
Description: 'Checks for redundant presence validation on belongs_to association.'
|
||||
Enabled: pending
|
||||
VersionAdded: '2.13'
|
||||
|
||||
Rails/RedundantReceiverInWithOptions:
|
||||
Description: 'Checks for redundant receiver in `with_options`.'
|
||||
Enabled: true
|
||||
@ -646,9 +663,9 @@ Rails/RefuteMethods:
|
||||
Rails/RelativeDateConstant:
|
||||
Description: 'Do not assign relative date to constants.'
|
||||
Enabled: true
|
||||
SafeAutoCorrect: false
|
||||
VersionAdded: '0.48'
|
||||
VersionChanged: '0.59'
|
||||
AutoCorrect: false
|
||||
VersionChanged: '2.13'
|
||||
|
||||
Rails/RenderInline:
|
||||
Description: 'Prefer using a template over inline rendering.'
|
||||
@ -695,6 +712,11 @@ Rails/ReversibleMigrationMethodDefinition:
|
||||
Include:
|
||||
- db/migrate/*.rb
|
||||
|
||||
Rails/RootJoinChain:
|
||||
Description: 'Use a single `#join` instead of chaining on `Rails.root` or `Rails.public_path`.'
|
||||
Enabled: pending
|
||||
VersionAdded: '2.13'
|
||||
|
||||
Rails/SafeNavigation:
|
||||
Description: "Use Ruby's safe navigation operator (`&.`) instead of `try!`."
|
||||
Enabled: true
|
||||
@ -727,6 +749,13 @@ Rails/SaveBang:
|
||||
AllowedReceivers: []
|
||||
SafeAutoCorrect: false
|
||||
|
||||
Rails/SchemaComment:
|
||||
Description: >-
|
||||
This cop enforces the use of the `comment` option when adding a new table or column
|
||||
to the database during a migration.
|
||||
Enabled: false
|
||||
VersionAdded: '2.13'
|
||||
|
||||
Rails/ScopeArgs:
|
||||
Description: 'Checks the arguments of ActiveRecord scopes.'
|
||||
Enabled: true
|
||||
@ -789,9 +818,9 @@ Rails/TimeZone:
|
||||
StyleGuide: 'https://rails.rubystyle.guide#time'
|
||||
Reference: 'http://danilenko.org/2012/7/6/rails_timezones'
|
||||
Enabled: true
|
||||
Safe: false
|
||||
SafeAutoCorrect: false
|
||||
VersionAdded: '0.30'
|
||||
VersionChanged: '2.10'
|
||||
VersionChanged: '2.13'
|
||||
# The value `strict` means that `Time` should be used with `zone`.
|
||||
# The value `flexible` allows usage of `in_time_zone` instead of `zone`.
|
||||
EnforcedStyle: flexible
|
||||
@ -814,13 +843,12 @@ Rails/UniqBeforePluck:
|
||||
Description: 'Prefer the use of uniq or distinct before pluck.'
|
||||
Enabled: true
|
||||
VersionAdded: '0.40'
|
||||
VersionChanged: '2.8'
|
||||
VersionChanged: '2.13'
|
||||
EnforcedStyle: conservative
|
||||
SupportedStyles:
|
||||
- conservative
|
||||
- aggressive
|
||||
SafeAutoCorrect: false
|
||||
AutoCorrect: false
|
||||
|
||||
Rails/UniqueValidationWithoutIndex:
|
||||
Description: 'Uniqueness validation should have a unique index on the database column.'
|
||||
@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
# A mixin to extend cops for Active Record features
|
||||
module ActiveRecordMigrationsHelper
|
||||
extend NodePattern::Macros
|
||||
|
||||
RAILS_ABSTRACT_SCHEMA_DEFINITIONS = %i[
|
||||
bigint binary boolean date datetime decimal float integer json string
|
||||
text time timestamp virtual
|
||||
].freeze
|
||||
RAILS_ABSTRACT_SCHEMA_DEFINITIONS_HELPERS = %i[
|
||||
column references belongs_to primary_key numeric
|
||||
].freeze
|
||||
POSTGRES_SCHEMA_DEFINITIONS = %i[
|
||||
bigserial bit bit_varying cidr citext daterange hstore inet interval
|
||||
int4range int8range jsonb ltree macaddr money numrange oid point line
|
||||
lseg box path polygon circle serial tsrange tstzrange tsvector uuid xml
|
||||
].freeze
|
||||
MYSQL_SCHEMA_DEFINITIONS = %i[
|
||||
blob tinyblob mediumblob longblob tinytext mediumtext longtext
|
||||
unsigned_integer unsigned_bigint unsigned_float unsigned_decimal
|
||||
].freeze
|
||||
|
||||
def_node_matcher :create_table_with_block?, <<~PATTERN
|
||||
(block
|
||||
(send nil? :create_table ...)
|
||||
(args (arg _var))
|
||||
_)
|
||||
PATTERN
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -6,6 +6,10 @@ module RuboCop
|
||||
# Checks that ActiveRecord aliases are not used. The direct method names
|
||||
# are more clear and easier to read.
|
||||
#
|
||||
# @safety
|
||||
# This cop is unsafe because custom `update_attributes` method call was changed to
|
||||
# `update` but the method name remained same in the method definition.
|
||||
#
|
||||
# @example
|
||||
# #bad
|
||||
# book.update_attributes!(author: 'Alice')
|
||||
@ -3,7 +3,11 @@
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Rails
|
||||
# This cop checks that controllers subclass ApplicationController.
|
||||
# This cop checks that controllers subclass `ApplicationController`.
|
||||
#
|
||||
# @safety
|
||||
# This cop's autocorrection is unsafe because it may let the logic from `ApplicationController`
|
||||
# sneak into a controller that is not purposed to inherit logic common among other controllers.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
@ -3,7 +3,11 @@
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Rails
|
||||
# This cop checks that jobs subclass ApplicationJob with Rails 5.0.
|
||||
# This cop checks that jobs subclass `ApplicationJob` with Rails 5.0.
|
||||
#
|
||||
# @safety
|
||||
# This cop's autocorrection is unsafe because it may let the logic from `ApplicationJob`
|
||||
# sneak into a job that is not purposed to inherit logic common among other jobs.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
@ -3,7 +3,11 @@
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Rails
|
||||
# This cop checks that mailers subclass ApplicationMailer with Rails 5.0.
|
||||
# This cop checks that mailers subclass `ApplicationMailer` with Rails 5.0.
|
||||
#
|
||||
# @safety
|
||||
# This cop's autocorrection is unsafe because it may let the logic from `ApplicationMailer`
|
||||
# sneak into a mailer that is not purposed to inherit logic common among other mailers.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
@ -3,7 +3,12 @@
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Rails
|
||||
# This cop checks that models subclass ApplicationRecord with Rails 5.0.
|
||||
# This cop checks that models subclass `ApplicationRecord` with Rails 5.0.
|
||||
#
|
||||
# @safety
|
||||
# This cop's autocorrection is unsafe because it may let the logic from `ApplicationRecord`
|
||||
# sneak into an Active Record model that is not purposed to inherit logic common among other
|
||||
# Active Record models.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
@ -10,6 +10,12 @@ module RuboCop
|
||||
# database to look for a column named <tt>`*`</tt> (or `"*"`) as opposed
|
||||
# to expanding the column list as one would likely expect.
|
||||
#
|
||||
# @safety
|
||||
# This cop's autocorrection is unsafe because it turns a quoted `*` into
|
||||
# an SQL `*`, unquoted. `*` is a valid column name in certain databases
|
||||
# supported by Rails, and even though it is usually a mistake,
|
||||
# it might denote legitimate access to a column named `*`.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# MyTable.arel_table["*"]
|
||||
@ -6,15 +6,16 @@ module RuboCop
|
||||
# This cop checks for code that can be written with simpler conditionals
|
||||
# using `Object#blank?` defined by Active Support.
|
||||
#
|
||||
# This cop is marked as unsafe auto-correction, because `' '.empty?` returns false,
|
||||
# but `' '.blank?` returns true. Therefore, auto-correction is not compatible
|
||||
# if the receiver is a non-empty blank string, tab, or newline meta characters.
|
||||
#
|
||||
# Interaction with `Style/UnlessElse`:
|
||||
# The configuration of `NotPresent` will not produce an offense in the
|
||||
# context of `unless else` if `Style/UnlessElse` is inabled. This is
|
||||
# to prevent interference between the auto-correction of the two cops.
|
||||
#
|
||||
# @safety
|
||||
# This cop is unsafe auto-correction, because `' '.empty?` returns false,
|
||||
# but `' '.blank?` returns true. Therefore, auto-correction is not compatible
|
||||
# if the receiver is a non-empty blank string, tab, or newline meta characters.
|
||||
#
|
||||
# @example NilOrEmpty: true (default)
|
||||
# # Converts usages of `nil? || empty?` to `blank?`
|
||||
#
|
||||
@ -0,0 +1,98 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Rails
|
||||
# Checks if collection can be blank-compacted with `compact_blank`.
|
||||
#
|
||||
# @safety
|
||||
# It is unsafe by default because false positives may occur in the
|
||||
# blank check of block arguments to the receiver object.
|
||||
#
|
||||
# For example, `[[1, 2], [3, nil]].reject { |first, second| second.blank? }` and
|
||||
# `[[1, 2], [3, nil]].compact_blank` are not compatible. The same is true for `empty?`.
|
||||
# This will work fine when the receiver is a hash object.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# collection.reject(&:blank?)
|
||||
# collection.reject(&:empty?)
|
||||
# collection.reject { |_k, v| v.blank? }
|
||||
# collection.reject { |_k, v| v.empty? }
|
||||
#
|
||||
# # good
|
||||
# collection.compact_blank
|
||||
#
|
||||
# # bad
|
||||
# collection.reject!(&:blank?)
|
||||
# collection.reject!(&:empty?)
|
||||
# collection.reject! { |_k, v| v.blank? }
|
||||
# collection.reject! { |_k, v| v.empty? }
|
||||
#
|
||||
# # good
|
||||
# collection.compact_blank!
|
||||
#
|
||||
class CompactBlank < Base
|
||||
include RangeHelp
|
||||
extend AutoCorrector
|
||||
extend TargetRailsVersion
|
||||
|
||||
MSG = 'Use `%<preferred_method>s` instead.'
|
||||
RESTRICT_ON_SEND = %i[reject reject!].freeze
|
||||
|
||||
minimum_target_rails_version 6.1
|
||||
|
||||
def_node_matcher :reject_with_block?, <<~PATTERN
|
||||
(block
|
||||
(send _ {:reject :reject!})
|
||||
$(args ...)
|
||||
(send
|
||||
$(lvar _) {:blank? :empty?}))
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :reject_with_block_pass?, <<~PATTERN
|
||||
(send _ {:reject :reject!}
|
||||
(block_pass
|
||||
(sym {:blank? :empty?})))
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
return unless bad_method?(node)
|
||||
|
||||
range = offense_range(node)
|
||||
preferred_method = preferred_method(node)
|
||||
add_offense(range, message: format(MSG, preferred_method: preferred_method)) do |corrector|
|
||||
corrector.replace(range, preferred_method)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def bad_method?(node)
|
||||
return true if reject_with_block_pass?(node)
|
||||
|
||||
if (arguments, receiver_in_block = reject_with_block?(node.parent))
|
||||
return arguments.length == 1 || use_hash_value_block_argument?(arguments, receiver_in_block)
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def use_hash_value_block_argument?(arguments, receiver_in_block)
|
||||
arguments.length == 2 && arguments[1].source == receiver_in_block.source
|
||||
end
|
||||
|
||||
def offense_range(node)
|
||||
end_pos = node.parent&.block_type? ? node.parent.loc.expression.end_pos : node.loc.expression.end_pos
|
||||
|
||||
range_between(node.loc.selector.begin_pos, end_pos)
|
||||
end
|
||||
|
||||
def preferred_method(node)
|
||||
node.method?(:reject) ? 'compact_blank' : 'compact_blank!'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -41,16 +41,11 @@ module RuboCop
|
||||
# t.datetime :updated_at, default: -> { 'CURRENT_TIMESTAMP' }
|
||||
# end
|
||||
class CreateTableWithTimestamps < Base
|
||||
include ActiveRecordMigrationsHelper
|
||||
|
||||
MSG = 'Add timestamps when creating a new table.'
|
||||
RESTRICT_ON_SEND = %i[create_table].freeze
|
||||
|
||||
def_node_matcher :create_table_with_block?, <<~PATTERN
|
||||
(block
|
||||
(send nil? :create_table ...)
|
||||
(args (arg _var))
|
||||
_)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :create_table_with_timestamps_proc?, <<~PATTERN
|
||||
(send nil? :create_table (sym _) ... (block-pass (sym :timestamps)))
|
||||
PATTERN
|
||||
@ -0,0 +1,97 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Rails
|
||||
# This cop checks if a duration is added to or subtracted from `Time.current`.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# Time.current - 1.minute
|
||||
# Time.current + 2.days
|
||||
#
|
||||
# # good - using relative would make it harder to express and read
|
||||
# Date.yesterday + 3.days
|
||||
# created_at - 1.minute
|
||||
# 3.days - 1.hour
|
||||
#
|
||||
# # good
|
||||
# 1.minute.ago
|
||||
# 2.days.from_now
|
||||
class DurationArithmetic < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Do not add or subtract duration.'
|
||||
|
||||
RESTRICT_ON_SEND = %i[+ -].freeze
|
||||
|
||||
DURATIONS = Set[:second, :seconds, :minute, :minutes, :hour, :hours,
|
||||
:day, :days, :week, :weeks, :fortnight, :fortnights]
|
||||
|
||||
# @!method duration_arithmetic_argument?(node)
|
||||
# Match duration subtraction or addition with current time.
|
||||
#
|
||||
# @example source that matches
|
||||
# Time.current - 1.hour
|
||||
#
|
||||
# @example source that matches
|
||||
# ::Time.zone.now + 1.hour
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @yield operator and duration
|
||||
def_node_matcher :duration_arithmetic_argument?, <<~PATTERN
|
||||
(send #time_current? ${ :+ :- } $#duration?)
|
||||
PATTERN
|
||||
|
||||
# @!method duration?(node)
|
||||
# Match a literal Duration
|
||||
#
|
||||
# @example source that matches
|
||||
# 1.hour
|
||||
#
|
||||
# @example source that matches
|
||||
# 9.5.weeks
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @return [Boolean] true if matches
|
||||
def_node_matcher :duration?, '(send { int float (send nil _) } DURATIONS)'
|
||||
|
||||
# @!method time_current?(node)
|
||||
# Match Time.current
|
||||
#
|
||||
# @example source that matches
|
||||
# Time.current
|
||||
#
|
||||
# @example source that matches
|
||||
# ::Time.zone.now
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @return [Boolean] true if matches
|
||||
def_node_matcher :time_current?, <<~PATTERN
|
||||
{
|
||||
(send (const _ :Time) :current)
|
||||
(send (send (const _ :Time) :zone) :now)
|
||||
}
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
duration_arithmetic_argument?(node) do |*operation|
|
||||
add_offense(node) do |corrector|
|
||||
corrector.replace(node.source_range, corrected_source(*operation))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def corrected_source(operator, duration)
|
||||
if operator == :-
|
||||
"#{duration.source}.ago"
|
||||
else
|
||||
"#{duration.source}.from_now"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -7,6 +7,10 @@ module RuboCop
|
||||
# Use `find_by` instead of dynamic method.
|
||||
# See. https://rails.rubystyle.guide#find_by
|
||||
#
|
||||
# @safety
|
||||
# It is certainly unsafe when not configured properly, i.e. user-defined `find_by_xxx`
|
||||
# method is not added to cop's `AllowedMethods`.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# User.find_by_name(name)
|
||||
@ -67,7 +67,7 @@ module RuboCop
|
||||
private
|
||||
|
||||
def in_routing_block?(node)
|
||||
!!node.each_ancestor(:block).detect { |block| ROUTING_METHODS.include?(block.send_node.method_name) }
|
||||
!!node.each_ancestor(:block).detect { |block| ROUTING_METHODS.include?(block.method_name) }
|
||||
end
|
||||
|
||||
def needs_conversion?(data)
|
||||
@ -6,6 +6,7 @@ module RuboCop
|
||||
# This cop checks that methods specified in the filter's `only` or
|
||||
# `except` options are defined within the same class or module.
|
||||
#
|
||||
# @safety
|
||||
# You can technically specify methods of superclass or methods added by
|
||||
# mixins on the filter, but these can confuse developers. If you specify
|
||||
# methods that are defined in other classes or modules, you should
|
||||
@ -8,6 +8,10 @@ module RuboCop
|
||||
# Without the `Mailer` suffix it isn't immediately apparent what's a mailer
|
||||
# and which views are related to the mailer.
|
||||
#
|
||||
# @safety
|
||||
# This cop's autocorrection is unsafe because renaming a constant is
|
||||
# always an unsafe operation.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# class User < ActionMailer::Base
|
||||
@ -6,8 +6,9 @@ module RuboCop
|
||||
# This cop enforces the use of `collection.exclude?(obj)`
|
||||
# over `!collection.include?(obj)`.
|
||||
#
|
||||
# It is marked as unsafe by default because false positive will occur for
|
||||
# a receiver object that do not have `exclude?` method. (e.g. `IPAddr`)
|
||||
# @safety
|
||||
# This cop is unsafe because false positive will occur for
|
||||
# receiver objects that do not have an `exclude?` method. (e.g. `IPAddr`)
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
@ -5,6 +5,10 @@ module RuboCop
|
||||
module Rails
|
||||
# This cop checks for the use of output calls like puts and print
|
||||
#
|
||||
# @safety
|
||||
# This cop's autocorrection is unsafe because depending on the Rails log level configuration,
|
||||
# changing from `puts` to `Rails.logger.debug` could result in no output being shown.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# puts 'A debug message'
|
||||
@ -9,6 +9,13 @@ module RuboCop
|
||||
# `pick` avoids. When called on an Active Record relation, `pick` adds a
|
||||
# limit to the query so that only one value is fetched from the database.
|
||||
#
|
||||
# @safety
|
||||
# This cop is unsafe because `pluck` is defined on both `ActiveRecord::Relation` and `Enumerable`,
|
||||
# whereas `pick` is only defined on `ActiveRecord::Relation` in Rails 6.0. This was addressed
|
||||
# in Rails 6.1 via rails/rails#38760, at which point the cop is safe.
|
||||
#
|
||||
# See: https://github.com/rubocop/rubocop-rails/pull/249
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# Model.pluck(:a).first
|
||||
@ -5,6 +5,9 @@ module RuboCop
|
||||
module Rails
|
||||
# This cop enforces the use of `ids` over `pluck(:id)` and `pluck(primary_key)`.
|
||||
#
|
||||
# @safety
|
||||
# This cop is unsafe if the receiver object is not an Active Record object.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# User.pluck(:id)
|
||||
@ -9,11 +9,12 @@ module RuboCop
|
||||
# Since `pluck` is an eager method and hits the database immediately,
|
||||
# using `select` helps to avoid additional database queries.
|
||||
#
|
||||
# This cop has two different enforcement modes. When the EnforcedStyle
|
||||
# is conservative (the default) then only calls to `pluck` on a constant
|
||||
# This cop has two different enforcement modes. When the `EnforcedStyle`
|
||||
# is `conservative` (the default) then only calls to `pluck` on a constant
|
||||
# (i.e. a model class) in the `where` is used as offenses.
|
||||
#
|
||||
# When the EnforcedStyle is aggressive then all calls to `pluck` in the
|
||||
# @safety
|
||||
# When the `EnforcedStyle` is `aggressive` then all calls to `pluck` in the
|
||||
# `where` is used as offenses. This may lead to false positives
|
||||
# as the cop cannot replace to `select` between calls to `pluck` on an
|
||||
# `ActiveRecord::Relation` instance vs a call to `pluck` on an `Array` instance.
|
||||
@ -14,6 +14,11 @@ module RuboCop
|
||||
# * The task does not need application code.
|
||||
# * The task invokes the `:environment` task.
|
||||
#
|
||||
# @safety
|
||||
# Probably not a problem in most cases, but it is possible that calling `:environment` task
|
||||
# will break a behavior. It's also slower. E.g. some task that only needs one gem to be
|
||||
# loaded to run will run significantly faster without loading the whole application.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# task :foo do
|
||||
@ -0,0 +1,192 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Rails
|
||||
# Since Rails 5.0 the default for `belongs_to` is `optional: false`
|
||||
# unless `config.active_record.belongs_to_required_by_default` is
|
||||
# explicitly set to `false`. The presence validator is added
|
||||
# automatically, and explicit presence validation is redundant.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# belongs_to :user
|
||||
# validates :user, presence: true
|
||||
#
|
||||
# # bad
|
||||
# belongs_to :user
|
||||
# validates :user_id, presence: true
|
||||
#
|
||||
# # bad
|
||||
# belongs_to :author, foreign_key: :user_id
|
||||
# validates :user_id, presence: true
|
||||
#
|
||||
# # good
|
||||
# belongs_to :user
|
||||
#
|
||||
# # good
|
||||
# belongs_to :author, foreign_key: :user_id
|
||||
#
|
||||
class RedundantPresenceValidationOnBelongsTo < Base
|
||||
include RangeHelp
|
||||
extend AutoCorrector
|
||||
extend TargetRailsVersion
|
||||
|
||||
MSG = 'Remove explicit presence validation for `%<association>s`.'
|
||||
RESTRICT_ON_SEND = %i[validates].freeze
|
||||
|
||||
minimum_target_rails_version 5.0
|
||||
|
||||
# @!method presence_validation?(node)
|
||||
# Match a `validates` statement with a presence check
|
||||
#
|
||||
# @example source that matches - by association
|
||||
# validates :user, presence: true
|
||||
#
|
||||
# @example source that matches - with presence options
|
||||
# validates :user, presence: { message: 'duplicate' }
|
||||
#
|
||||
# @example source that matches - by a foreign key
|
||||
# validates :user_id, presence: true
|
||||
def_node_matcher :presence_validation?, <<~PATTERN
|
||||
$(
|
||||
send nil? :validates
|
||||
(sym $_)
|
||||
...
|
||||
$(hash <$(pair (sym :presence) {true hash}) ...>)
|
||||
)
|
||||
PATTERN
|
||||
|
||||
# @!method optional_option?(node)
|
||||
# Match a `belongs_to` association with an optional option in a hash
|
||||
def_node_matcher :optional?, <<~PATTERN
|
||||
(send nil? :belongs_to _ ... #optional_option?)
|
||||
PATTERN
|
||||
|
||||
# @!method optional_option?(node)
|
||||
# Match an optional option in a hash
|
||||
def_node_matcher :optional_option?, <<~PATTERN
|
||||
{
|
||||
(hash <(pair (sym :optional) true) ...>) # optional: true
|
||||
(hash <(pair (sym :required) false) ...>) # required: false
|
||||
}
|
||||
PATTERN
|
||||
|
||||
# @!method any_belongs_to?(node, association:)
|
||||
# Match a class with `belongs_to` with no regard to `foreign_key` option
|
||||
#
|
||||
# @example source that matches
|
||||
# belongs_to :user
|
||||
#
|
||||
# @example source that matches - regardless of `foreign_key`
|
||||
# belongs_to :author, foreign_key: :user_id
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @param association [Symbol]
|
||||
# @return [Array<RuboCop::AST::Node>, nil] matching node
|
||||
def_node_matcher :any_belongs_to?, <<~PATTERN
|
||||
(begin
|
||||
<
|
||||
$(send nil? :belongs_to (sym %association) ...)
|
||||
...
|
||||
>
|
||||
)
|
||||
PATTERN
|
||||
|
||||
# @!method belongs_to?(node, key:, fk:)
|
||||
# Match a class with a matching association, either by name or an explicit
|
||||
# `foreign_key` option
|
||||
#
|
||||
# @example source that matches - fk matches `foreign_key` option
|
||||
# belongs_to :author, foreign_key: :user_id
|
||||
#
|
||||
# @example source that matches - key matches association name
|
||||
# belongs_to :user
|
||||
#
|
||||
# @example source that does not match - explicit `foreign_key` does not match
|
||||
# belongs_to :user, foreign_key: :account_id
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @param key [Symbol] e.g. `:user`
|
||||
# @param fk [Symbol] e.g. `:user_id`
|
||||
# @return [Array<RuboCop::AST::Node>] matching nodes
|
||||
def_node_matcher :belongs_to?, <<~PATTERN
|
||||
(begin
|
||||
<
|
||||
${
|
||||
#belongs_to_without_fk?(%key) # belongs_to :user
|
||||
#belongs_to_with_a_matching_fk?(%fk) # belongs_to :author, foreign_key: :user_id
|
||||
}
|
||||
...
|
||||
>
|
||||
)
|
||||
PATTERN
|
||||
|
||||
# @!method belongs_to_without_fk?(node, fk)
|
||||
# Match a matching `belongs_to` association, without an explicit `foreign_key` option
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @param key [Symbol] e.g. `:user`
|
||||
# @return [Array<RuboCop::AST::Node>] matching nodes
|
||||
def_node_matcher :belongs_to_without_fk?, <<~PATTERN
|
||||
{
|
||||
(send nil? :belongs_to (sym %1)) # belongs_to :user
|
||||
(send nil? :belongs_to (sym %1) !hash) # belongs_to :user, -> { not_deleted }
|
||||
(send nil? :belongs_to (sym %1) !(hash <(pair (sym :foreign_key) _) ...>))
|
||||
}
|
||||
PATTERN
|
||||
|
||||
# @!method belongs_to_with_a_matching_fk?(node, fk)
|
||||
# Match a matching `belongs_to` association with a matching explicit `foreign_key` option
|
||||
#
|
||||
# @example source that matches
|
||||
# belongs_to :author, foreign_key: :user_id
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @param fk [Symbol] e.g. `:user_id`
|
||||
# @return [Array<RuboCop::AST::Node>] matching nodes
|
||||
def_node_matcher :belongs_to_with_a_matching_fk?, <<~PATTERN
|
||||
(send nil? :belongs_to ... (hash <(pair (sym :foreign_key) (sym %1)) ...>))
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
validation, key, options, presence = presence_validation?(node)
|
||||
return unless validation
|
||||
|
||||
belongs_to = belongs_to_for(node.parent, key)
|
||||
return unless belongs_to
|
||||
return if optional?(belongs_to)
|
||||
|
||||
message = format(MSG, association: key.to_s)
|
||||
|
||||
add_offense(presence, message: message) do |corrector|
|
||||
remove_presence_validation(corrector, node, options, presence)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def belongs_to_for(model_class_node, key)
|
||||
if key.to_s.end_with?('_id')
|
||||
normalized_key = key.to_s.delete_suffix('_id').to_sym
|
||||
belongs_to?(model_class_node, key: normalized_key, fk: key)
|
||||
else
|
||||
any_belongs_to?(model_class_node, association: key)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_presence_validation(corrector, node, options, presence)
|
||||
if options.children.one?
|
||||
corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
|
||||
else
|
||||
range = range_with_surrounding_comma(
|
||||
range_with_surrounding_space(range: presence.source_range, side: :left),
|
||||
:left
|
||||
)
|
||||
corrector.remove(range)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -5,7 +5,9 @@ module RuboCop
|
||||
module Rails
|
||||
# This cop checks if the value of the option `class_name`, in
|
||||
# the definition of a reflection is a string.
|
||||
# It is marked as unsafe because it cannot be determined whether
|
||||
#
|
||||
# @safety
|
||||
# This cop is unsafe because it cannot be determined whether
|
||||
# constant or method return value specified to `class_name` is a string.
|
||||
#
|
||||
# @example
|
||||
@ -6,6 +6,9 @@ module RuboCop
|
||||
# This cop checks whether constant value isn't relative date.
|
||||
# Because the relative date will be evaluated only once.
|
||||
#
|
||||
# @safety
|
||||
# This cop's autocorrection is unsafe because its dependence on the constant is not corrected.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# class SomeClass
|
||||
@ -179,7 +179,7 @@ module RuboCop
|
||||
MSG = '%<action>s is not reversible.'
|
||||
|
||||
def_node_matcher :irreversible_schema_statement_call, <<~PATTERN
|
||||
(send nil? ${:execute :remove_belongs_to} ...)
|
||||
(send nil? ${:change_column :execute :remove_belongs_to :remove_reference} ...)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :drop_table_call, <<~PATTERN
|
||||
@ -0,0 +1,72 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Rails
|
||||
# Use a single `#join` instead of chaining on `Rails.root` or `Rails.public_path`.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# Rails.root.join('db').join('schema.rb')
|
||||
# Rails.root.join('db').join(migrate).join('migration.rb')
|
||||
# Rails.public_path.join('path').join('file.pdf')
|
||||
# Rails.public_path.join('path').join(to).join('file.pdf')
|
||||
#
|
||||
# # good
|
||||
# Rails.root.join('db', 'schema.rb')
|
||||
# Rails.root.join('db', migrate, 'migration.rb')
|
||||
# Rails.public_path.join('path', 'file.pdf')
|
||||
# Rails.public_path.join('path', to, 'file.pdf')
|
||||
#
|
||||
class RootJoinChain < Base
|
||||
extend AutoCorrector
|
||||
include RangeHelp
|
||||
|
||||
MSG = 'Use `%<root>s.join(...)` instead of chaining `#join` calls.'
|
||||
|
||||
RESTRICT_ON_SEND = %i[join].to_set.freeze
|
||||
|
||||
# @!method rails_root?(node)
|
||||
def_node_matcher :rails_root?, <<~PATTERN
|
||||
(send (const {nil? cbase} :Rails) {:root :public_path})
|
||||
PATTERN
|
||||
|
||||
# @!method join?(node)
|
||||
def_node_matcher :join?, <<~PATTERN
|
||||
(send _ :join $...)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
evidence(node) do |rails_node, args|
|
||||
add_offense(node, message: format(MSG, root: rails_node.source)) do |corrector|
|
||||
range = range_between(rails_node.loc.selector.end_pos, node.loc.expression.end_pos)
|
||||
replacement = ".join(#{args.map(&:source).join(', ')})"
|
||||
|
||||
corrector.replace(range, replacement)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def evidence(node)
|
||||
# Are we at the *end* of the join chain?
|
||||
return if join?(node.parent)
|
||||
# Is there only one join?
|
||||
return if rails_root?(node.receiver)
|
||||
|
||||
all_args = []
|
||||
|
||||
while (args = join?(node))
|
||||
all_args = args + all_args
|
||||
node = node.receiver
|
||||
end
|
||||
|
||||
rails_root?(node) do
|
||||
yield(node, all_args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -6,10 +6,19 @@ module RuboCop
|
||||
# This cop checks to make sure safe navigation isn't used with `blank?` in
|
||||
# a conditional.
|
||||
#
|
||||
# @safety
|
||||
# While the safe navigation operator is generally a good idea, when
|
||||
# checking `foo&.blank?` in a conditional, `foo` being `nil` will actually
|
||||
# do the opposite of what the author intends.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# [source,ruby]
|
||||
# ----
|
||||
# foo&.blank? #=> nil
|
||||
# foo.blank? #=> true
|
||||
# ----
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# do_something if foo&.blank?
|
||||
@ -25,6 +25,25 @@ module RuboCop
|
||||
# You can permit receivers that are giving false positives with
|
||||
# `AllowedReceivers: []`
|
||||
#
|
||||
# @safety
|
||||
# This cop's autocorrection is unsafe because a custom `update` method call would be changed to `update!`,
|
||||
# but the method name in the definition would be unchanged.
|
||||
#
|
||||
# [source,ruby]
|
||||
# ----
|
||||
# # Original code
|
||||
# def update_attributes
|
||||
# end
|
||||
#
|
||||
# update_attributes
|
||||
#
|
||||
# # After running rubocop --safe-auto-correct
|
||||
# def update_attributes
|
||||
# end
|
||||
#
|
||||
# update
|
||||
# ----
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
@ -0,0 +1,104 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Rails
|
||||
# This cop enforces the use of the `comment` option when adding a new table or column
|
||||
# to the database during a migration.
|
||||
#
|
||||
# @example
|
||||
# # bad (no comment for a new column or table)
|
||||
# add_column :table, :column, :integer
|
||||
#
|
||||
# create_table :table do |t|
|
||||
# t.type :column
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# add_column :table, :column, :integer, comment: 'Number of offenses'
|
||||
#
|
||||
# create_table :table, comment: 'Table of offenses data' do |t|
|
||||
# t.type :column, comment: 'Number of offenses'
|
||||
# end
|
||||
#
|
||||
class SchemaComment < Base
|
||||
include ActiveRecordMigrationsHelper
|
||||
|
||||
COLUMN_MSG = 'New database column without `comment`.'
|
||||
TABLE_MSG = 'New database table without `comment`.'
|
||||
RESTRICT_ON_SEND = %i[add_column create_table].freeze
|
||||
CREATE_TABLE_COLUMN_METHODS = Set[
|
||||
*(
|
||||
RAILS_ABSTRACT_SCHEMA_DEFINITIONS |
|
||||
RAILS_ABSTRACT_SCHEMA_DEFINITIONS_HELPERS |
|
||||
POSTGRES_SCHEMA_DEFINITIONS |
|
||||
MYSQL_SCHEMA_DEFINITIONS
|
||||
)
|
||||
].freeze
|
||||
|
||||
# @!method comment_present?(node)
|
||||
def_node_matcher :comment_present?, <<~PATTERN
|
||||
(hash <(pair {(sym :comment) (str "comment")} (_ [present?])) ...>)
|
||||
PATTERN
|
||||
|
||||
# @!method add_column?(node)
|
||||
def_node_matcher :add_column?, <<~PATTERN
|
||||
(send nil? :add_column _table _column _type _?)
|
||||
PATTERN
|
||||
|
||||
# @!method add_column_with_comment?(node)
|
||||
def_node_matcher :add_column_with_comment?, <<~PATTERN
|
||||
(send nil? :add_column _table _column _type #comment_present?)
|
||||
PATTERN
|
||||
|
||||
# @!method create_table?(node)
|
||||
def_node_matcher :create_table?, <<~PATTERN
|
||||
(send nil? :create_table _table _?)
|
||||
PATTERN
|
||||
|
||||
# @!method create_table?(node)
|
||||
def_node_matcher :create_table_with_comment?, <<~PATTERN
|
||||
(send nil? :create_table _table #comment_present? ...)
|
||||
PATTERN
|
||||
|
||||
# @!method t_column?(node)
|
||||
def_node_matcher :t_column?, <<~PATTERN
|
||||
(send _var CREATE_TABLE_COLUMN_METHODS ...)
|
||||
PATTERN
|
||||
|
||||
# @!method t_column_with_comment?(node)
|
||||
def_node_matcher :t_column_with_comment?, <<~PATTERN
|
||||
(send _var CREATE_TABLE_COLUMN_METHODS _column _type? #comment_present?)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
if add_column_without_comment?(node)
|
||||
add_offense(node, message: COLUMN_MSG)
|
||||
elsif create_table?(node)
|
||||
if create_table_without_comment?(node)
|
||||
add_offense(node, message: TABLE_MSG)
|
||||
elsif create_table_column_call_without_comment?(node)
|
||||
add_offense(node.parent.body, message: COLUMN_MSG)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_column_without_comment?(node)
|
||||
add_column?(node) && !add_column_with_comment?(node)
|
||||
end
|
||||
|
||||
def create_table_without_comment?(node)
|
||||
create_table?(node) && !create_table_with_comment?(node)
|
||||
end
|
||||
|
||||
def create_table_column_call_without_comment?(node)
|
||||
create_table_with_block?(node.parent) &&
|
||||
t_column?(node.parent.body) &&
|
||||
!t_column_with_comment?(node.parent.body)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -5,6 +5,8 @@ module RuboCop
|
||||
module Rails
|
||||
#
|
||||
# Checks SQL heredocs to use `.squish`.
|
||||
#
|
||||
# @safety
|
||||
# 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.
|
||||
#
|
||||
@ -14,6 +14,9 @@ module RuboCop
|
||||
# When EnforcedStyle is 'flexible' then it's also allowed
|
||||
# to use `Time#in_time_zone`.
|
||||
#
|
||||
# @safety
|
||||
# This cop's autocorrection is unsafe because it may change handling time.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# Time.now
|
||||
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