brew vendor-gems: commit updates.
This commit is contained in:
parent
346d2087f1
commit
c1e35b7f8b
@ -81,7 +81,7 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-progressbar-1.11
|
|||||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-2.0.0/lib"
|
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-2.0.0/lib"
|
||||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-1.17.0/lib"
|
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-1.17.0/lib"
|
||||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.11.3/lib"
|
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.11.3/lib"
|
||||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rails-2.10.1/lib"
|
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rails-2.11.0/lib"
|
||||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-2.4.0/lib"
|
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-2.4.0/lib"
|
||||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-sorbet-0.6.2/lib"
|
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-sorbet-0.6.2/lib"
|
||||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.5.1/lib"
|
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.5.1/lib"
|
||||||
|
|||||||
@ -83,6 +83,16 @@ Rails/ActiveSupportAliases:
|
|||||||
Enabled: true
|
Enabled: true
|
||||||
VersionAdded: '0.48'
|
VersionAdded: '0.48'
|
||||||
|
|
||||||
|
Rails/AddColumnIndex:
|
||||||
|
Description: >-
|
||||||
|
Rails migrations don't make use of a given `index` key, but also
|
||||||
|
doesn't given an error when it's used, so it makes it seem like an
|
||||||
|
index might be used.
|
||||||
|
Enabled: pending
|
||||||
|
VersionAdded: '2.11'
|
||||||
|
Include:
|
||||||
|
- db/migrate/*.rb
|
||||||
|
|
||||||
Rails/AfterCommitOverride:
|
Rails/AfterCommitOverride:
|
||||||
Description: >-
|
Description: >-
|
||||||
This cop enforces that there is only one call to `after_commit`
|
This cop enforces that there is only one call to `after_commit`
|
||||||
@ -193,7 +203,7 @@ Rails/Date:
|
|||||||
such as Date.today, Date.current etc.
|
such as Date.today, Date.current etc.
|
||||||
Enabled: true
|
Enabled: true
|
||||||
VersionAdded: '0.30'
|
VersionAdded: '0.30'
|
||||||
VersionChanged: '0.33'
|
VersionChanged: '2.11'
|
||||||
# The value `strict` disallows usage of `Date.today`, `Date.current`,
|
# The value `strict` disallows usage of `Date.today`, `Date.current`,
|
||||||
# `Date#to_time` etc.
|
# `Date#to_time` etc.
|
||||||
# The value `flexible` allows usage of `Date.current`, `Date.yesterday`, etc
|
# The value `flexible` allows usage of `Date.current`, `Date.yesterday`, etc
|
||||||
@ -203,6 +213,7 @@ Rails/Date:
|
|||||||
SupportedStyles:
|
SupportedStyles:
|
||||||
- strict
|
- strict
|
||||||
- flexible
|
- flexible
|
||||||
|
AllowToTime: true
|
||||||
|
|
||||||
Rails/DefaultScope:
|
Rails/DefaultScope:
|
||||||
Description: 'Avoid use of `default_scope`.'
|
Description: 'Avoid use of `default_scope`.'
|
||||||
@ -240,6 +251,12 @@ Rails/DynamicFindBy:
|
|||||||
AllowedReceivers:
|
AllowedReceivers:
|
||||||
- Gem::Specification
|
- Gem::Specification
|
||||||
|
|
||||||
|
Rails/EagerEvaluationLogMessage:
|
||||||
|
Description: 'Checks that blocks are used for interpolated strings passed to `Rails.logger.debug`.'
|
||||||
|
Reference: 'https://guides.rubyonrails.org/debugging_rails_applications.html#impact-of-logs-on-performance'
|
||||||
|
Enabled: pending
|
||||||
|
VersionAdded: '2.11'
|
||||||
|
|
||||||
Rails/EnumHash:
|
Rails/EnumHash:
|
||||||
Description: 'Prefer hash syntax over array syntax when defining enums.'
|
Description: 'Prefer hash syntax over array syntax when defining enums.'
|
||||||
StyleGuide: 'https://rails.rubystyle.guide#enums'
|
StyleGuide: 'https://rails.rubystyle.guide#enums'
|
||||||
@ -262,8 +279,10 @@ Rails/EnvironmentComparison:
|
|||||||
|
|
||||||
Rails/EnvironmentVariableAccess:
|
Rails/EnvironmentVariableAccess:
|
||||||
Description: 'Do not access `ENV` directly after initialization.'
|
Description: 'Do not access `ENV` directly after initialization.'
|
||||||
Enabled: pending
|
# TODO: Set to `pending` status in RuboCop Rails 2 series when migration doc will be written.
|
||||||
|
Enabled: false
|
||||||
VersionAdded: '2.10'
|
VersionAdded: '2.10'
|
||||||
|
VersionChanged: '2.11'
|
||||||
Include:
|
Include:
|
||||||
- app/**/*.rb
|
- app/**/*.rb
|
||||||
- lib/**/*.rb
|
- lib/**/*.rb
|
||||||
@ -286,6 +305,11 @@ Rails/Exit:
|
|||||||
Exclude:
|
Exclude:
|
||||||
- lib/**/*.rake
|
- lib/**/*.rake
|
||||||
|
|
||||||
|
Rails/ExpandedDateRange:
|
||||||
|
Description: 'Checks for expanded date range.'
|
||||||
|
Enabled: pending
|
||||||
|
VersionAdded: '2.11'
|
||||||
|
|
||||||
Rails/FilePath:
|
Rails/FilePath:
|
||||||
Description: 'Use `Rails.root.join` for file path joining.'
|
Description: 'Use `Rails.root.join` for file path joining.'
|
||||||
Enabled: true
|
Enabled: true
|
||||||
@ -301,6 +325,8 @@ Rails/FindBy:
|
|||||||
StyleGuide: 'https://rails.rubystyle.guide#find_by'
|
StyleGuide: 'https://rails.rubystyle.guide#find_by'
|
||||||
Enabled: true
|
Enabled: true
|
||||||
VersionAdded: '0.30'
|
VersionAdded: '0.30'
|
||||||
|
VersionChanged: '2.11'
|
||||||
|
IgnoreWhereFirst: true
|
||||||
Include:
|
Include:
|
||||||
- app/models/**/*.rb
|
- app/models/**/*.rb
|
||||||
|
|
||||||
@ -362,11 +388,20 @@ Rails/HttpStatus:
|
|||||||
Description: 'Enforces use of symbolic or numeric value to define HTTP status.'
|
Description: 'Enforces use of symbolic or numeric value to define HTTP status.'
|
||||||
Enabled: true
|
Enabled: true
|
||||||
VersionAdded: '0.54'
|
VersionAdded: '0.54'
|
||||||
|
VersionChanged: '2.11'
|
||||||
EnforcedStyle: symbolic
|
EnforcedStyle: symbolic
|
||||||
SupportedStyles:
|
SupportedStyles:
|
||||||
- numeric
|
- numeric
|
||||||
- symbolic
|
- symbolic
|
||||||
|
|
||||||
|
Rails/I18nLocaleAssignment:
|
||||||
|
Description: 'Prefer the usage of `I18n.with_locale` instead of manually updating `I18n.locale` value.'
|
||||||
|
Enabled: 'pending'
|
||||||
|
VersionAdded: '2.11'
|
||||||
|
Include:
|
||||||
|
- spec/**/*.rb
|
||||||
|
- test/**/*.rb
|
||||||
|
|
||||||
Rails/IgnoredSkipActionFilterOption:
|
Rails/IgnoredSkipActionFilterOption:
|
||||||
Description: 'Checks that `if` and `only` (or `except`) are not used together as options of `skip_*` action filter.'
|
Description: 'Checks that `if` and `only` (or `except`) are not used together as options of `skip_*` action filter.'
|
||||||
Reference: 'https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-_normalize_callback_options'
|
Reference: 'https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-_normalize_callback_options'
|
||||||
@ -784,6 +819,13 @@ Rails/UnknownEnv:
|
|||||||
- test
|
- test
|
||||||
- production
|
- production
|
||||||
|
|
||||||
|
Rails/UnusedIgnoredColumns:
|
||||||
|
Description: 'Remove a column that does not exist from `ignored_columns`.'
|
||||||
|
Enabled: pending
|
||||||
|
VersionAdded: '2.11'
|
||||||
|
Include:
|
||||||
|
- app/models/**/*.rb
|
||||||
|
|
||||||
Rails/Validation:
|
Rails/Validation:
|
||||||
Description: 'Use validates :attribute, hash of validations.'
|
Description: 'Use validates :attribute, hash of validations.'
|
||||||
Enabled: true
|
Enabled: true
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Rails
|
||||||
|
# This cop checks for migrations using `add_column` that have an `index`
|
||||||
|
# key. `add_column` does not accept `index`, but also does not raise an
|
||||||
|
# error for extra keys, so it is possible to mistakenly add the key without
|
||||||
|
# realizing it will not actually add an index.
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# # bad (will not add an index)
|
||||||
|
# add_column :table, :column, :integer, index: true
|
||||||
|
#
|
||||||
|
# # good
|
||||||
|
# add_column :table, :column, :integer
|
||||||
|
# add_index :table, :column
|
||||||
|
#
|
||||||
|
class AddColumnIndex < Base
|
||||||
|
extend AutoCorrector
|
||||||
|
include RangeHelp
|
||||||
|
|
||||||
|
MSG = '`add_column` does not accept an `index` key, use `add_index` instead.'
|
||||||
|
RESTRICT_ON_SEND = %i[add_column].freeze
|
||||||
|
|
||||||
|
# @!method add_column_with_index(node)
|
||||||
|
def_node_matcher :add_column_with_index, <<~PATTERN
|
||||||
|
(
|
||||||
|
send nil? :add_column $_table $_column
|
||||||
|
<(hash <$(pair {(sym :index) (str "index")} $_) ...>) ...>
|
||||||
|
)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
table, column, pair, value = add_column_with_index(node)
|
||||||
|
return unless pair
|
||||||
|
|
||||||
|
add_offense(pair) do |corrector|
|
||||||
|
corrector.remove(index_range(pair))
|
||||||
|
|
||||||
|
add_index = "add_index #{table.source}, #{column.source}"
|
||||||
|
add_index_opts = ''
|
||||||
|
|
||||||
|
if value.hash_type?
|
||||||
|
hash = value.loc.expression.adjust(begin_pos: 1, end_pos: -1).source.strip
|
||||||
|
add_index_opts = ", #{hash}"
|
||||||
|
end
|
||||||
|
|
||||||
|
corrector.insert_after(node, "\n#{add_index}#{add_index_opts}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def index_range(pair_node)
|
||||||
|
range_with_surrounding_comma(
|
||||||
|
range_with_surrounding_space(range: pair_node.loc.expression, side: :left),
|
||||||
|
:left
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -12,20 +12,21 @@ module RuboCop
|
|||||||
# The cop also reports warnings when you are using `to_time` method,
|
# The cop also reports warnings when you are using `to_time` method,
|
||||||
# because it doesn't know about Rails time zone either.
|
# because it doesn't know about Rails time zone either.
|
||||||
#
|
#
|
||||||
# Two styles are supported for this cop. When EnforcedStyle is 'strict'
|
# Two styles are supported for this cop. When `EnforcedStyle` is 'strict'
|
||||||
# then the Date methods `today`, `current`, `yesterday`, and `tomorrow`
|
# then the Date methods `today`, `current`, `yesterday`, and `tomorrow`
|
||||||
# are prohibited and the usage of both `to_time`
|
# are prohibited and the usage of both `to_time`
|
||||||
# and 'to_time_in_current_zone' are reported as warning.
|
# and 'to_time_in_current_zone' are reported as warning.
|
||||||
#
|
#
|
||||||
# When EnforcedStyle is 'flexible' then only `Date.today` is prohibited
|
# When `EnforcedStyle` is `flexible` then only `Date.today` is prohibited.
|
||||||
# and only `to_time` is reported as warning.
|
#
|
||||||
|
# And you can set a warning for `to_time` with `AllowToTime: false`.
|
||||||
|
# `AllowToTime` is `true` by default to prevent false positive on `DateTime` object.
|
||||||
#
|
#
|
||||||
# @example EnforcedStyle: strict
|
# @example EnforcedStyle: strict
|
||||||
# # bad
|
# # bad
|
||||||
# Date.current
|
# Date.current
|
||||||
# Date.yesterday
|
# Date.yesterday
|
||||||
# Date.today
|
# Date.today
|
||||||
# date.to_time
|
|
||||||
#
|
#
|
||||||
# # good
|
# # good
|
||||||
# Time.zone.today
|
# Time.zone.today
|
||||||
@ -34,7 +35,6 @@ module RuboCop
|
|||||||
# @example EnforcedStyle: flexible (default)
|
# @example EnforcedStyle: flexible (default)
|
||||||
# # bad
|
# # bad
|
||||||
# Date.today
|
# Date.today
|
||||||
# date.to_time
|
|
||||||
#
|
#
|
||||||
# # good
|
# # good
|
||||||
# Time.zone.today
|
# Time.zone.today
|
||||||
@ -43,6 +43,13 @@ module RuboCop
|
|||||||
# Date.yesterday
|
# Date.yesterday
|
||||||
# date.in_time_zone
|
# date.in_time_zone
|
||||||
#
|
#
|
||||||
|
# @example AllowToTime: true (default)
|
||||||
|
# # good
|
||||||
|
# date.to_time
|
||||||
|
#
|
||||||
|
# @example AllowToTime: false
|
||||||
|
# # bad
|
||||||
|
# date.to_time
|
||||||
class Date < Base
|
class Date < Base
|
||||||
include ConfigurableEnforcedStyle
|
include ConfigurableEnforcedStyle
|
||||||
|
|
||||||
@ -73,7 +80,7 @@ module RuboCop
|
|||||||
|
|
||||||
def on_send(node)
|
def on_send(node)
|
||||||
return unless node.receiver && bad_methods.include?(node.method_name)
|
return unless node.receiver && bad_methods.include?(node.method_name)
|
||||||
|
return if allow_to_time? && node.method?(:to_time)
|
||||||
return if safe_chain?(node) || safe_to_time?(node)
|
return if safe_chain?(node) || safe_to_time?(node)
|
||||||
|
|
||||||
check_deprecated_methods(node)
|
check_deprecated_methods(node)
|
||||||
@ -139,6 +146,10 @@ module RuboCop
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def allow_to_time?
|
||||||
|
cop_config.fetch('AllowToTime', true)
|
||||||
|
end
|
||||||
|
|
||||||
def good_days
|
def good_days
|
||||||
style == :strict ? [] : %i[current yesterday tomorrow]
|
style == :strict ? [] : %i[current yesterday tomorrow]
|
||||||
end
|
end
|
||||||
@ -37,6 +37,7 @@ module RuboCop
|
|||||||
|
|
||||||
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
|
||||||
|
IGNORED_ARGUMENT_TYPES = %i[hash splat].freeze
|
||||||
|
|
||||||
def on_send(node)
|
def on_send(node)
|
||||||
return if node.receiver.nil? && !inherit_active_record_base?(node) || allowed_invocation?(node)
|
return if node.receiver.nil? && !inherit_active_record_base?(node) || allowed_invocation?(node)
|
||||||
@ -44,7 +45,7 @@ module RuboCop
|
|||||||
method_name = node.method_name
|
method_name = node.method_name
|
||||||
static_name = static_method_name(method_name)
|
static_name = static_method_name(method_name)
|
||||||
return unless static_name
|
return unless static_name
|
||||||
return if node.arguments.any?(&:splat_type?)
|
return if node.arguments.any? { |argument| IGNORED_ARGUMENT_TYPES.include?(argument.type) }
|
||||||
|
|
||||||
message = format(MSG, static_name: static_name, method: method_name)
|
message = format(MSG, static_name: static_name, method: method_name)
|
||||||
add_offense(node, message: message) do |corrector|
|
add_offense(node, message: message) do |corrector|
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Rails
|
||||||
|
# This cop checks that blocks are used for interpolated strings passed to
|
||||||
|
# `Rails.logger.debug`.
|
||||||
|
#
|
||||||
|
# By default, Rails production environments use the `:info` log level.
|
||||||
|
# At the `:info` log level, `Rails.logger.debug` statements do not result
|
||||||
|
# in log output. However, Ruby must eagerly evaluate interpolated string
|
||||||
|
# arguments passed as method arguments. Passing a block to
|
||||||
|
# `Rails.logger.debug` prevents costly evaluation of interpolated strings
|
||||||
|
# when no output would be produced anyway.
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# #bad
|
||||||
|
# Rails.logger.debug "The time is #{Time.zone.now}."
|
||||||
|
#
|
||||||
|
# #good
|
||||||
|
# Rails.logger.debug { "The time is #{Time.zone.now}." }
|
||||||
|
#
|
||||||
|
class EagerEvaluationLogMessage < Base
|
||||||
|
extend AutoCorrector
|
||||||
|
|
||||||
|
MSG = 'Pass a block to `Rails.logger.debug`.'
|
||||||
|
RESTRICT_ON_SEND = %i[debug].freeze
|
||||||
|
|
||||||
|
def_node_matcher :interpolated_string_passed_to_debug, <<~PATTERN
|
||||||
|
(send
|
||||||
|
(send
|
||||||
|
(const {cbase nil?} :Rails)
|
||||||
|
:logger
|
||||||
|
)
|
||||||
|
:debug
|
||||||
|
$(dstr ...)
|
||||||
|
)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
return if node.parent&.block_type?
|
||||||
|
|
||||||
|
interpolated_string_passed_to_debug(node) do |arguments|
|
||||||
|
message = format(MSG)
|
||||||
|
|
||||||
|
range = replacement_range(node)
|
||||||
|
replacement = replacement_source(node, arguments)
|
||||||
|
|
||||||
|
add_offense(range, message: message) do |corrector|
|
||||||
|
corrector.replace(range, replacement)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def replacement_range(node)
|
||||||
|
stop = node.loc.expression.end
|
||||||
|
start = node.loc.selector.end
|
||||||
|
|
||||||
|
if node.parenthesized_call?
|
||||||
|
stop.with(begin_pos: start.begin_pos)
|
||||||
|
else
|
||||||
|
stop.with(begin_pos: start.begin_pos + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def replacement_source(node, arguments)
|
||||||
|
if node.parenthesized_call?
|
||||||
|
" { #{arguments.source} }"
|
||||||
|
else
|
||||||
|
"{ #{arguments.source} }"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Rails
|
||||||
|
# This cop checks for expanded date range. It only compatible `..` range is targeted.
|
||||||
|
# Incompatible `...` range is ignored.
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# # bad
|
||||||
|
# date.beginning_of_day..date.end_of_day
|
||||||
|
# date.beginning_of_week..date.end_of_week
|
||||||
|
# date.beginning_of_month..date.end_of_month
|
||||||
|
# date.beginning_of_quarter..date.end_of_quarter
|
||||||
|
# date.beginning_of_year..date.end_of_year
|
||||||
|
#
|
||||||
|
# # good
|
||||||
|
# date.all_day
|
||||||
|
# date.all_week
|
||||||
|
# date.all_month
|
||||||
|
# date.all_quarter
|
||||||
|
# date.all_year
|
||||||
|
#
|
||||||
|
class ExpandedDateRange < Base
|
||||||
|
extend AutoCorrector
|
||||||
|
extend TargetRailsVersion
|
||||||
|
|
||||||
|
MSG = 'Use `%<preferred_method>s` instead.'
|
||||||
|
|
||||||
|
minimum_target_rails_version 5.1
|
||||||
|
|
||||||
|
def_node_matcher :expanded_date_range, <<~PATTERN
|
||||||
|
(irange
|
||||||
|
(send
|
||||||
|
$_ {:beginning_of_day :beginning_of_week :beginning_of_month :beginning_of_quarter :beginning_of_year})
|
||||||
|
(send
|
||||||
|
$_ {:end_of_day :end_of_week :end_of_month :end_of_quarter :end_of_year}))
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
PREFERRED_METHODS = {
|
||||||
|
beginning_of_day: 'all_day',
|
||||||
|
beginning_of_week: 'all_week',
|
||||||
|
beginning_of_month: 'all_month',
|
||||||
|
beginning_of_quarter: 'all_quarter',
|
||||||
|
beginning_of_year: 'all_year'
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
MAPPED_DATE_RANGE_METHODS = {
|
||||||
|
beginning_of_day: :end_of_day,
|
||||||
|
beginning_of_week: :end_of_week,
|
||||||
|
beginning_of_month: :end_of_month,
|
||||||
|
beginning_of_quarter: :end_of_quarter,
|
||||||
|
beginning_of_year: :end_of_year
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
def on_irange(node)
|
||||||
|
return unless expanded_date_range(node)
|
||||||
|
|
||||||
|
begin_node = node.begin
|
||||||
|
end_node = node.end
|
||||||
|
return unless same_receiver?(begin_node, end_node)
|
||||||
|
|
||||||
|
beginning_method = begin_node.method_name
|
||||||
|
end_method = end_node.method_name
|
||||||
|
return unless use_mapped_methods?(beginning_method, end_method)
|
||||||
|
|
||||||
|
preferred_method = "#{begin_node.receiver.source}.#{PREFERRED_METHODS[beginning_method]}"
|
||||||
|
|
||||||
|
add_offense(node, message: format(MSG, preferred_method: preferred_method)) do |corrector|
|
||||||
|
corrector.replace(node, preferred_method)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def same_receiver?(begin_node, end_node)
|
||||||
|
begin_node.receiver.source == end_node.receiver.source
|
||||||
|
end
|
||||||
|
|
||||||
|
def use_mapped_methods?(beginning_method, end_method)
|
||||||
|
MAPPED_DATE_RANGE_METHODS[beginning_method] == end_method
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -3,16 +3,27 @@
|
|||||||
module RuboCop
|
module RuboCop
|
||||||
module Cop
|
module Cop
|
||||||
module Rails
|
module Rails
|
||||||
# This cop is used to identify usages of `where.first` and
|
# This cop is used to identify usages of `where.take` and change them to use `find_by` instead.
|
||||||
# change them to use `find_by` instead.
|
#
|
||||||
|
# And `where(...).first` can return different results from `find_by`.
|
||||||
|
# (They order records differently, so the "first" record can be different.)
|
||||||
|
#
|
||||||
|
# If you also want to detect `where.first`, you can set `IgnoreWhereFirst` to false.
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
# # bad
|
# # bad
|
||||||
# User.where(name: 'Bruce').first
|
|
||||||
# User.where(name: 'Bruce').take
|
# User.where(name: 'Bruce').take
|
||||||
#
|
#
|
||||||
# # good
|
# # good
|
||||||
# User.find_by(name: 'Bruce')
|
# User.find_by(name: 'Bruce')
|
||||||
|
#
|
||||||
|
# @example IgnoreWhereFirst: true (default)
|
||||||
|
# # good
|
||||||
|
# User.where(name: 'Bruce').first
|
||||||
|
#
|
||||||
|
# @example IgnoreWhereFirst: false
|
||||||
|
# # bad
|
||||||
|
# User.where(name: 'Bruce').first
|
||||||
class FindBy < Base
|
class FindBy < Base
|
||||||
include RangeHelp
|
include RangeHelp
|
||||||
extend AutoCorrector
|
extend AutoCorrector
|
||||||
@ -20,12 +31,8 @@ module RuboCop
|
|||||||
MSG = 'Use `find_by` instead of `where.%<method>s`.'
|
MSG = 'Use `find_by` instead of `where.%<method>s`.'
|
||||||
RESTRICT_ON_SEND = %i[first take].freeze
|
RESTRICT_ON_SEND = %i[first take].freeze
|
||||||
|
|
||||||
def_node_matcher :where_first?, <<~PATTERN
|
|
||||||
(send ({send csend} _ :where ...) {:first :take})
|
|
||||||
PATTERN
|
|
||||||
|
|
||||||
def on_send(node)
|
def on_send(node)
|
||||||
return unless where_first?(node)
|
return if ignore_where_first? && node.method?(:first)
|
||||||
|
|
||||||
range = range_between(node.receiver.loc.selector.begin_pos, node.loc.selector.end_pos)
|
range = range_between(node.receiver.loc.selector.begin_pos, node.loc.selector.end_pos)
|
||||||
|
|
||||||
@ -38,9 +45,6 @@ module RuboCop
|
|||||||
private
|
private
|
||||||
|
|
||||||
def autocorrect(corrector, node)
|
def autocorrect(corrector, node)
|
||||||
# Don't autocorrect where(...).first, because it can return different
|
|
||||||
# results from find_by. (They order records differently, so the
|
|
||||||
# 'first' record can be different.)
|
|
||||||
return if node.method?(:first)
|
return if node.method?(:first)
|
||||||
|
|
||||||
where_loc = node.receiver.loc.selector
|
where_loc = node.receiver.loc.selector
|
||||||
@ -49,6 +53,10 @@ module RuboCop
|
|||||||
corrector.replace(where_loc, 'find_by')
|
corrector.replace(where_loc, 'find_by')
|
||||||
corrector.replace(first_loc, '')
|
corrector.replace(first_loc, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ignore_where_first?
|
||||||
|
cop_config.fetch('IgnoreWhereFirst', true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -11,12 +11,14 @@ module RuboCop
|
|||||||
# render json: { foo: 'bar' }, status: 200
|
# render json: { foo: 'bar' }, status: 200
|
||||||
# render plain: 'foo/bar', status: 304
|
# render plain: 'foo/bar', status: 304
|
||||||
# redirect_to root_url, status: 301
|
# redirect_to root_url, status: 301
|
||||||
|
# head 200
|
||||||
#
|
#
|
||||||
# # good
|
# # good
|
||||||
# render :foo, status: :ok
|
# render :foo, status: :ok
|
||||||
# render json: { foo: 'bar' }, status: :ok
|
# render json: { foo: 'bar' }, status: :ok
|
||||||
# render plain: 'foo/bar', status: :not_modified
|
# render plain: 'foo/bar', status: :not_modified
|
||||||
# redirect_to root_url, status: :moved_permanently
|
# redirect_to root_url, status: :moved_permanently
|
||||||
|
# head :ok
|
||||||
#
|
#
|
||||||
# @example EnforcedStyle: numeric
|
# @example EnforcedStyle: numeric
|
||||||
# # bad
|
# # bad
|
||||||
@ -24,23 +26,26 @@ module RuboCop
|
|||||||
# render json: { foo: 'bar' }, status: :not_found
|
# render json: { foo: 'bar' }, status: :not_found
|
||||||
# render plain: 'foo/bar', status: :not_modified
|
# render plain: 'foo/bar', status: :not_modified
|
||||||
# redirect_to root_url, status: :moved_permanently
|
# redirect_to root_url, status: :moved_permanently
|
||||||
|
# head :ok
|
||||||
#
|
#
|
||||||
# # good
|
# # good
|
||||||
# render :foo, status: 200
|
# render :foo, status: 200
|
||||||
# render json: { foo: 'bar' }, status: 404
|
# render json: { foo: 'bar' }, status: 404
|
||||||
# render plain: 'foo/bar', status: 304
|
# render plain: 'foo/bar', status: 304
|
||||||
# redirect_to root_url, status: 301
|
# redirect_to root_url, status: 301
|
||||||
|
# head 200
|
||||||
#
|
#
|
||||||
class HttpStatus < Base
|
class HttpStatus < Base
|
||||||
include ConfigurableEnforcedStyle
|
include ConfigurableEnforcedStyle
|
||||||
extend AutoCorrector
|
extend AutoCorrector
|
||||||
|
|
||||||
RESTRICT_ON_SEND = %i[render redirect_to].freeze
|
RESTRICT_ON_SEND = %i[render redirect_to head].freeze
|
||||||
|
|
||||||
def_node_matcher :http_status, <<~PATTERN
|
def_node_matcher :http_status, <<~PATTERN
|
||||||
{
|
{
|
||||||
(send nil? {:render :redirect_to} _ $hash)
|
(send nil? {:render :redirect_to} _ $hash)
|
||||||
(send nil? {:render :redirect_to} $hash)
|
(send nil? {:render :redirect_to} $hash)
|
||||||
|
(send nil? :head ${int sym} ...)
|
||||||
}
|
}
|
||||||
PATTERN
|
PATTERN
|
||||||
|
|
||||||
@ -49,8 +54,12 @@ module RuboCop
|
|||||||
PATTERN
|
PATTERN
|
||||||
|
|
||||||
def on_send(node)
|
def on_send(node)
|
||||||
http_status(node) do |hash_node|
|
http_status(node) do |hash_node_or_status_code|
|
||||||
status = status_code(hash_node)
|
status = if hash_node_or_status_code.hash_type?
|
||||||
|
status_code(hash_node_or_status_code)
|
||||||
|
else
|
||||||
|
hash_node_or_status_code
|
||||||
|
end
|
||||||
return unless status
|
return unless status
|
||||||
|
|
||||||
checker = checker_class.new(status)
|
checker = checker_class.new(status)
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Rails
|
||||||
|
# This cop checks for the use of `I18n.locale=` method.
|
||||||
|
#
|
||||||
|
# The `locale` attribute persists for the rest of the Ruby runtime, potentially causing
|
||||||
|
# unexpected behavior at a later time.
|
||||||
|
# Using `I18n.with_locale` ensures the code passed in the block is the only place `I18n.locale` is affected.
|
||||||
|
# It eliminates the possibility of a `locale` sticking around longer than intended.
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# # bad
|
||||||
|
# I18n.locale = :fr
|
||||||
|
#
|
||||||
|
# # good
|
||||||
|
# I18n.with_locale(:fr) do
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
class I18nLocaleAssignment < Base
|
||||||
|
MSG = 'Use `I18n.with_locale` with block instead of `I18n.locale=`.'
|
||||||
|
RESTRICT_ON_SEND = %i[locale=].freeze
|
||||||
|
|
||||||
|
def_node_matcher :i18n_locale_assignment?, <<~PATTERN
|
||||||
|
(send (const {nil? cbase} :I18n) :locale= ...)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
return unless i18n_locale_assignment?(node)
|
||||||
|
|
||||||
|
add_offense(node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -33,16 +33,12 @@ module RuboCop
|
|||||||
|
|
||||||
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.'
|
||||||
RELATIVE_DATE_METHODS = %i[since from_now after ago until before yesterday tomorrow].freeze
|
RELATIVE_DATE_METHODS = %i[since from_now after ago until before yesterday tomorrow].to_set.freeze
|
||||||
|
|
||||||
def on_casgn(node)
|
def on_casgn(node)
|
||||||
return if node.children[2]&.block_type?
|
nested_relative_date(node) do |method_name|
|
||||||
|
add_offense(node, message: message(method_name)) do |corrector|
|
||||||
node.each_descendant(:send) do |send_node|
|
autocorrect(corrector, node)
|
||||||
relative_date?(send_node) do |method_name|
|
|
||||||
add_offense(node, message: message(method_name)) do |corrector|
|
|
||||||
autocorrect(corrector, node)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -55,7 +51,7 @@ module RuboCop
|
|||||||
lhs.children.zip(rhs.children).each do |(name, value)|
|
lhs.children.zip(rhs.children).each do |(name, value)|
|
||||||
next unless name.casgn_type?
|
next unless name.casgn_type?
|
||||||
|
|
||||||
relative_date?(value) do |method_name|
|
nested_relative_date(value) do |method_name|
|
||||||
add_offense(offense_range(name, value), message: message(method_name)) do |corrector|
|
add_offense(offense_range(name, value), message: message(method_name)) do |corrector|
|
||||||
autocorrect(corrector, node)
|
autocorrect(corrector, node)
|
||||||
end
|
end
|
||||||
@ -64,7 +60,7 @@ module RuboCop
|
|||||||
end
|
end
|
||||||
|
|
||||||
def on_or_asgn(node)
|
def on_or_asgn(node)
|
||||||
relative_date_or_assignment?(node) do |method_name|
|
relative_date_or_assignment(node) do |method_name|
|
||||||
add_offense(node, message: format(MSG, method_name: method_name))
|
add_offense(node, message: format(MSG, method_name: method_name))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -93,20 +89,22 @@ module RuboCop
|
|||||||
range_between(name.loc.expression.begin_pos, value.loc.expression.end_pos)
|
range_between(name.loc.expression.begin_pos, value.loc.expression.end_pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
def relative_date_method?(method_name)
|
def nested_relative_date(node, &callback)
|
||||||
RELATIVE_DATE_METHODS.include?(method_name)
|
return if node.block_type?
|
||||||
|
|
||||||
|
node.each_child_node do |child|
|
||||||
|
nested_relative_date(child, &callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
relative_date(node, &callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
def_node_matcher :relative_date_or_assignment?, <<~PATTERN
|
def_node_matcher :relative_date_or_assignment, <<~PATTERN
|
||||||
(:or_asgn (casgn _ _) (send _ $#relative_date_method?))
|
(:or_asgn (casgn _ _) (send _ $RELATIVE_DATE_METHODS))
|
||||||
PATTERN
|
PATTERN
|
||||||
|
|
||||||
def_node_matcher :relative_date?, <<~PATTERN
|
def_node_matcher :relative_date, <<~PATTERN
|
||||||
{
|
(send _ $RELATIVE_DATE_METHODS)
|
||||||
({erange irange} _ (send _ $#relative_date_method?))
|
|
||||||
({erange irange} (send _ $#relative_date_method?) _)
|
|
||||||
(send _ $#relative_date_method?)
|
|
||||||
}
|
|
||||||
PATTERN
|
PATTERN
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -50,7 +50,7 @@ module RuboCop
|
|||||||
(class
|
(class
|
||||||
(const nil? _)
|
(const nil? _)
|
||||||
(send
|
(send
|
||||||
(const (const nil? :ActiveRecord) :Migration)
|
(const (const {nil? cbase} :ActiveRecord) :Migration)
|
||||||
:[]
|
:[]
|
||||||
(float _))
|
(float _))
|
||||||
_)
|
_)
|
||||||
@ -8,41 +8,33 @@ module RuboCop
|
|||||||
# Built on top of Ruby on Rails style guide (https://rails.rubystyle.guide#time)
|
# Built on top of Ruby on Rails style guide (https://rails.rubystyle.guide#time)
|
||||||
# and the article http://danilenko.org/2012/7/6/rails_timezones/
|
# and the article http://danilenko.org/2012/7/6/rails_timezones/
|
||||||
#
|
#
|
||||||
# Two styles are supported for this cop. When EnforcedStyle is 'strict'
|
# Two styles are supported for this cop. When `EnforcedStyle` is 'strict'
|
||||||
# then only use of Time.zone is allowed.
|
# then only use of `Time.zone` is allowed.
|
||||||
#
|
#
|
||||||
# When EnforcedStyle is 'flexible' then it's also allowed
|
# When EnforcedStyle is 'flexible' then it's also allowed
|
||||||
# to use Time.in_time_zone.
|
# to use `Time#in_time_zone`.
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# # bad
|
||||||
|
# Time.now
|
||||||
|
# Time.parse('2015-03-02T19:05:37')
|
||||||
|
#
|
||||||
|
# # good
|
||||||
|
# Time.current
|
||||||
|
# Time.zone.now
|
||||||
|
# Time.zone.parse('2015-03-02T19:05:37')
|
||||||
|
# Time.zone.parse('2015-03-02T19:05:37Z') # Respect ISO 8601 format with timezone specifier.
|
||||||
#
|
#
|
||||||
# @example EnforcedStyle: strict
|
# @example EnforcedStyle: strict
|
||||||
# # `strict` means that `Time` should be used with `zone`.
|
# # `strict` means that `Time` should be used with `zone`.
|
||||||
#
|
#
|
||||||
# # bad
|
# # bad
|
||||||
# Time.now
|
|
||||||
# Time.parse('2015-03-02T19:05:37')
|
|
||||||
#
|
|
||||||
# # bad
|
|
||||||
# Time.current
|
|
||||||
# Time.at(timestamp).in_time_zone
|
# Time.at(timestamp).in_time_zone
|
||||||
#
|
#
|
||||||
# # good
|
|
||||||
# Time.zone.now
|
|
||||||
# Time.zone.parse('2015-03-02T19:05:37')
|
|
||||||
# Time.zone.parse('2015-03-02T19:05:37Z') # Respect ISO 8601 format with timezone specifier.
|
|
||||||
#
|
|
||||||
# @example EnforcedStyle: flexible (default)
|
# @example EnforcedStyle: flexible (default)
|
||||||
# # `flexible` allows usage of `in_time_zone` instead of `zone`.
|
# # `flexible` allows usage of `in_time_zone` instead of `zone`.
|
||||||
#
|
#
|
||||||
# # bad
|
|
||||||
# Time.now
|
|
||||||
# Time.parse('2015-03-02T19:05:37')
|
|
||||||
#
|
|
||||||
# # good
|
# # good
|
||||||
# Time.zone.now
|
|
||||||
# Time.zone.parse('2015-03-02T19:05:37')
|
|
||||||
#
|
|
||||||
# # good
|
|
||||||
# Time.current
|
|
||||||
# Time.at(timestamp).in_time_zone
|
# Time.at(timestamp).in_time_zone
|
||||||
class TimeZone < Base
|
class TimeZone < Base
|
||||||
include ConfigurableEnforcedStyle
|
include ConfigurableEnforcedStyle
|
||||||
@ -59,7 +51,7 @@ module RuboCop
|
|||||||
|
|
||||||
GOOD_METHODS = %i[zone zone_default find_zone find_zone!].freeze
|
GOOD_METHODS = %i[zone zone_default find_zone find_zone!].freeze
|
||||||
|
|
||||||
DANGEROUS_METHODS = %i[now local new parse at current].freeze
|
DANGEROUS_METHODS = %i[now local new parse at].freeze
|
||||||
|
|
||||||
ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601
|
ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601
|
||||||
jisx0301 rfc3339 httpdate to_i to_f].freeze
|
jisx0301 rfc3339 httpdate to_i to_f].freeze
|
||||||
@ -6,8 +6,8 @@ module RuboCop
|
|||||||
# This cop checks for the use of `Time.zone=` method.
|
# This cop checks for the use of `Time.zone=` method.
|
||||||
#
|
#
|
||||||
# The `zone` attribute persists for the rest of the Ruby runtime, potentially causing
|
# The `zone` attribute persists for the rest of the Ruby runtime, potentially causing
|
||||||
# unexpected behaviour at a later time.
|
# unexpected behavior at a later time.
|
||||||
# Using `Time.use_zone` ensures the code passed in block is the only place Time.zone is affected.
|
# Using `Time.use_zone` ensures the code passed in the block is the only place Time.zone is affected.
|
||||||
# It eliminates the possibility of a `zone` sticking around longer than intended.
|
# It eliminates the possibility of a `zone` sticking around longer than intended.
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
@ -19,7 +19,7 @@ module RuboCop
|
|||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
class TimeZoneAssignment < Base
|
class TimeZoneAssignment < Base
|
||||||
MSG = 'Use `Time.use_zone` with blocks instead of `Time.zone=`.'
|
MSG = 'Use `Time.use_zone` with block instead of `Time.zone=`.'
|
||||||
RESTRICT_ON_SEND = %i[zone=].freeze
|
RESTRICT_ON_SEND = %i[zone=].freeze
|
||||||
|
|
||||||
def_node_matcher :time_zone_assignement?, <<~PATTERN
|
def_node_matcher :time_zone_assignement?, <<~PATTERN
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Rails
|
||||||
|
# This cop suggests you remove a column that does not exist in the schema from `ignored_columns`.
|
||||||
|
# `ignored_columns` is necessary to drop a column from RDBMS, but you don't need it after the migration
|
||||||
|
# to drop the column. You avoid forgetting to remove `ignored_columns` by this cop.
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# # bad
|
||||||
|
# class User < ApplicationRecord
|
||||||
|
# self.ignored_columns = [:already_removed_column]
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # good
|
||||||
|
# class User < ApplicationRecord
|
||||||
|
# self.ignored_columns = [:still_existing_column]
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
class UnusedIgnoredColumns < Base
|
||||||
|
include ActiveRecordHelper
|
||||||
|
|
||||||
|
MSG = 'Remove `%<column_name>s` from `ignored_columns` because the column does not exist.'
|
||||||
|
RESTRICT_ON_SEND = %i[ignored_columns=].freeze
|
||||||
|
|
||||||
|
def_node_matcher :ignored_columns, <<~PATTERN
|
||||||
|
(send self :ignored_columns= $array)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def_node_matcher :column_name, <<~PATTERN
|
||||||
|
({str sym} $_)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
return unless (columns = ignored_columns(node))
|
||||||
|
return unless schema
|
||||||
|
|
||||||
|
table = table(node)
|
||||||
|
return unless table
|
||||||
|
|
||||||
|
columns.children.each do |column_node|
|
||||||
|
check_column_existence(column_node, table)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_column_existence(column_node, table)
|
||||||
|
column_name = column_name(column_node)
|
||||||
|
return unless column_name
|
||||||
|
return if table.with_column?(name: column_name.to_s)
|
||||||
|
|
||||||
|
message = format(MSG, column_name: column_name)
|
||||||
|
add_offense(column_node, message: message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def class_node(node)
|
||||||
|
node.each_ancestor.find(&:class_type?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def table(node)
|
||||||
|
klass = class_node(node)
|
||||||
|
schema.table_by(name: table_name(klass))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
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