rubocop-rails 2.14.2 → 2.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +23 -2
- data/config/default.yml +190 -12
- data/config/obsoletion.yml +10 -0
- data/lib/rubocop/cop/mixin/active_record_helper.rb +3 -6
- data/lib/rubocop/cop/mixin/active_record_migrations_helper.rb +1 -3
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +1 -1
- data/lib/rubocop/cop/mixin/index_method.rb +7 -17
- data/lib/rubocop/cop/mixin/migrations_helper.rb +1 -1
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +112 -0
- data/lib/rubocop/cop/rails/action_controller_test_case.rb +2 -2
- data/lib/rubocop/cop/rails/action_filter.rb +2 -2
- data/lib/rubocop/cop/rails/action_order.rb +116 -0
- data/lib/rubocop/cop/rails/active_record_aliases.rb +3 -4
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +7 -4
- data/lib/rubocop/cop/rails/active_record_override.rb +2 -5
- data/lib/rubocop/cop/rails/active_support_aliases.rb +1 -1
- data/lib/rubocop/cop/rails/active_support_on_load.rb +70 -0
- data/lib/rubocop/cop/rails/add_column_index.rb +3 -6
- data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
- data/lib/rubocop/cop/rails/application_controller.rb +2 -2
- data/lib/rubocop/cop/rails/application_job.rb +3 -3
- data/lib/rubocop/cop/rails/application_mailer.rb +2 -2
- data/lib/rubocop/cop/rails/application_record.rb +2 -2
- data/lib/rubocop/cop/rails/arel_star.rb +2 -2
- data/lib/rubocop/cop/rails/assert_not.rb +1 -1
- data/lib/rubocop/cop/rails/attribute_default_block_value.rb +1 -1
- data/lib/rubocop/cop/rails/belongs_to.rb +2 -5
- data/lib/rubocop/cop/rails/blank.rb +10 -11
- data/lib/rubocop/cop/rails/bulk_change_table.rb +8 -25
- data/lib/rubocop/cop/rails/compact_blank.rb +6 -2
- data/lib/rubocop/cop/rails/content_tag.rb +6 -7
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +16 -3
- data/lib/rubocop/cop/rails/date.rb +12 -17
- data/lib/rubocop/cop/rails/default_scope.rb +1 -1
- data/lib/rubocop/cop/rails/delegate.rb +24 -18
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +2 -2
- data/lib/rubocop/cop/rails/deprecated_active_model_errors_methods.rb +63 -3
- data/lib/rubocop/cop/rails/dot_separated_keys.rb +71 -0
- data/lib/rubocop/cop/rails/duplicate_association.rb +2 -2
- data/lib/rubocop/cop/rails/duplicate_scope.rb +1 -1
- data/lib/rubocop/cop/rails/duration_arithmetic.rb +4 -4
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +26 -14
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +6 -2
- data/lib/rubocop/cop/rails/enum_hash.rb +3 -4
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +3 -6
- data/lib/rubocop/cop/rails/environment_comparison.rb +3 -4
- data/lib/rubocop/cop/rails/environment_variable_access.rb +1 -1
- data/lib/rubocop/cop/rails/exit.rb +1 -1
- data/lib/rubocop/cop/rails/expanded_date_range.rb +39 -23
- data/lib/rubocop/cop/rails/file_path.rb +41 -24
- data/lib/rubocop/cop/rails/find_by.rb +1 -1
- data/lib/rubocop/cop/rails/find_by_id.rb +3 -3
- data/lib/rubocop/cop/rails/find_each.rb +14 -4
- data/lib/rubocop/cop/rails/freeze_time.rb +79 -0
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +1 -1
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +14 -8
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +3 -3
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +23 -12
- data/lib/rubocop/cop/rails/http_status.rb +6 -11
- data/lib/rubocop/cop/rails/i18n_lazy_lookup.rb +3 -1
- data/lib/rubocop/cop/rails/i18n_locale_assignment.rb +1 -1
- data/lib/rubocop/cop/rails/i18n_locale_texts.rb +2 -2
- data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +5 -14
- data/lib/rubocop/cop/rails/index_by.rb +2 -2
- data/lib/rubocop/cop/rails/index_with.rb +2 -2
- data/lib/rubocop/cop/rails/inquiry.rb +1 -1
- data/lib/rubocop/cop/rails/inverse_of.rb +4 -10
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +22 -16
- data/lib/rubocop/cop/rails/link_to_blank.rb +2 -5
- data/lib/rubocop/cop/rails/mailer_name.rb +5 -5
- data/lib/rubocop/cop/rails/match_route.rb +1 -1
- data/lib/rubocop/cop/rails/migration_class_name.rb +2 -2
- data/lib/rubocop/cop/rails/negate_include.rb +2 -2
- data/lib/rubocop/cop/rails/not_null_column.rb +10 -7
- data/lib/rubocop/cop/rails/order_by_id.rb +2 -3
- data/lib/rubocop/cop/rails/output.rb +7 -9
- data/lib/rubocop/cop/rails/output_safety.rb +6 -2
- data/lib/rubocop/cop/rails/pick.rb +1 -1
- data/lib/rubocop/cop/rails/pluck.rb +45 -13
- data/lib/rubocop/cop/rails/pluck_id.rb +2 -2
- data/lib/rubocop/cop/rails/pluck_in_where.rb +1 -1
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +2 -3
- data/lib/rubocop/cop/rails/presence.rb +22 -13
- data/lib/rubocop/cop/rails/present.rb +10 -13
- data/lib/rubocop/cop/rails/rake_environment.rb +3 -3
- data/lib/rubocop/cop/rails/read_write_attribute.rb +2 -2
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +5 -7
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +3 -3
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +3 -3
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +31 -27
- data/lib/rubocop/cop/rails/redundant_travel_back.rb +1 -1
- data/lib/rubocop/cop/rails/reflection_class_name.rb +35 -2
- data/lib/rubocop/cop/rails/refute_methods.rb +1 -5
- data/lib/rubocop/cop/rails/relative_date_constant.rb +5 -8
- data/lib/rubocop/cop/rails/render_inline.rb +1 -1
- data/lib/rubocop/cop/rails/render_plain_text.rb +1 -1
- data/lib/rubocop/cop/rails/request_referer.rb +2 -3
- data/lib/rubocop/cop/rails/require_dependency.rb +2 -2
- data/lib/rubocop/cop/rails/response_parsed_body.rb +57 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +15 -63
- data/lib/rubocop/cop/rails/reversible_migration_method_definition.rb +5 -6
- data/lib/rubocop/cop/rails/root_join_chain.rb +1 -1
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +238 -0
- data/lib/rubocop/cop/rails/root_public_path.rb +59 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +8 -13
- data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +2 -4
- data/lib/rubocop/cop/rails/save_bang.rb +12 -24
- data/lib/rubocop/cop/rails/schema_comment.rb +1 -1
- data/lib/rubocop/cop/rails/scope_args.rb +1 -1
- data/lib/rubocop/cop/rails/short_i18n.rb +3 -6
- data/lib/rubocop/cop/rails/skips_model_validations.rb +3 -4
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +10 -7
- data/lib/rubocop/cop/rails/strip_heredoc.rb +56 -0
- data/lib/rubocop/cop/rails/table_name_assignment.rb +1 -1
- data/lib/rubocop/cop/rails/three_state_boolean_column.rb +73 -0
- data/lib/rubocop/cop/rails/time_zone.rb +34 -32
- data/lib/rubocop/cop/rails/time_zone_assignment.rb +4 -4
- data/lib/rubocop/cop/rails/to_formatted_s.rb +46 -0
- data/lib/rubocop/cop/rails/to_s_with_argument.rb +78 -0
- data/lib/rubocop/cop/rails/top_level_hash_with_indifferent_access.rb +49 -0
- data/lib/rubocop/cop/rails/transaction_exit_statement.rb +17 -12
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +3 -6
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +14 -7
- data/lib/rubocop/cop/rails/unknown_env.rb +3 -5
- data/lib/rubocop/cop/rails/unused_ignored_columns.rb +7 -2
- data/lib/rubocop/cop/rails/validation.rb +5 -13
- data/lib/rubocop/cop/rails/where_equals.rb +2 -2
- data/lib/rubocop/cop/rails/where_exists.rb +3 -3
- data/lib/rubocop/cop/rails/where_missing.rb +118 -0
- data/lib/rubocop/cop/rails/where_not.rb +2 -2
- data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
- data/lib/rubocop/cop/rails_cops.rb +16 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +8 -5
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop/rails.rb +1 -1
- data/lib/rubocop-rails.rb +19 -0
- metadata +23 -9
- data/bin/console +0 -11
- data/bin/setup +0 -7
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Favor `Rails.public_path` over `Rails.root` with `'public'`
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# Rails.root.join('public')
|
11
|
+
# Rails.root.join('public/file.pdf')
|
12
|
+
# Rails.root.join('public', 'file.pdf')
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# Rails.public_path
|
16
|
+
# Rails.public_path.join('file.pdf')
|
17
|
+
# Rails.public_path.join('file.pdf')
|
18
|
+
#
|
19
|
+
class RootPublicPath < Base
|
20
|
+
extend AutoCorrector
|
21
|
+
|
22
|
+
MSG = 'Use `Rails.public_path`.'
|
23
|
+
|
24
|
+
RESTRICT_ON_SEND = %i[join].to_set.freeze
|
25
|
+
|
26
|
+
PATTERN = %r{\Apublic(/|\z)}.freeze
|
27
|
+
|
28
|
+
def_node_matcher :rails_root_public, <<~PATTERN
|
29
|
+
(send
|
30
|
+
(send
|
31
|
+
$(const {nil? cbase} :Rails) :root) :join
|
32
|
+
(str $#public_path?) $...)
|
33
|
+
PATTERN
|
34
|
+
|
35
|
+
def on_send(node)
|
36
|
+
return unless (rails, maybe_public_path, other_args = rails_root_public(node))
|
37
|
+
|
38
|
+
add_offense(node) do |corrector|
|
39
|
+
first_args = maybe_public_path.gsub(PATTERN, '')
|
40
|
+
|
41
|
+
args = other_args.map(&:source)
|
42
|
+
args.unshift("'#{first_args}'") unless first_args.empty?
|
43
|
+
|
44
|
+
replacement = "#{rails.source}.public_path"
|
45
|
+
replacement += ".join(#{args.join(', ')})" unless args.empty?
|
46
|
+
|
47
|
+
corrector.replace(node, replacement)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def public_path?(string)
|
54
|
+
PATTERN.match?(string)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -3,8 +3,9 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
7
|
-
# to convert `try`. It will convert code to use safe navigation
|
6
|
+
# Converts usages of `try!` to `&.`. It can also be configured
|
7
|
+
# to convert `try`. It will convert code to use safe navigation
|
8
|
+
# if the target Ruby version is set to 2.3+
|
8
9
|
#
|
9
10
|
# @example ConvertTry: false (default)
|
10
11
|
# # bad
|
@@ -39,6 +40,9 @@ module RuboCop
|
|
39
40
|
class SafeNavigation < Base
|
40
41
|
include RangeHelp
|
41
42
|
extend AutoCorrector
|
43
|
+
extend TargetRubyVersion
|
44
|
+
|
45
|
+
minimum_target_ruby_version 2.3
|
42
46
|
|
43
47
|
MSG = 'Use safe navigation (`&.`) instead of `%<try>s`.'
|
44
48
|
RESTRICT_ON_SEND = %i[try try!].freeze
|
@@ -47,15 +51,6 @@ module RuboCop
|
|
47
51
|
(send _ ${:try :try!} $_ ...)
|
48
52
|
PATTERN
|
49
53
|
|
50
|
-
# Monkey patching for `Style/RedundantSelf` of RuboCop core.
|
51
|
-
# rubocop:disable Style/ClassAndModuleChildren
|
52
|
-
class Style::RedundantSelf
|
53
|
-
def self.autocorrect_incompatible_with
|
54
|
-
[Rails::SafeNavigation]
|
55
|
-
end
|
56
|
-
end
|
57
|
-
# rubocop:enable Style/ClassAndModuleChildren
|
58
|
-
|
59
54
|
def self.autocorrect_incompatible_with
|
60
55
|
[Style::RedundantSelf]
|
61
56
|
end
|
@@ -75,10 +70,10 @@ module RuboCop
|
|
75
70
|
|
76
71
|
def autocorrect(corrector, node)
|
77
72
|
method_node, *params = *node.arguments
|
78
|
-
method = method_node.source[1
|
73
|
+
method = method_node.source[1..]
|
79
74
|
|
80
75
|
range = if node.receiver
|
81
|
-
range_between(node.loc.dot.begin_pos, node.
|
76
|
+
range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
|
82
77
|
else
|
83
78
|
corrector.insert_before(node, 'self')
|
84
79
|
node
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks to make sure safe navigation isn't used with `blank?` in
|
7
7
|
# a conditional.
|
8
8
|
#
|
9
9
|
# @safety
|
@@ -31,9 +31,7 @@ module RuboCop
|
|
31
31
|
class SafeNavigationWithBlank < Base
|
32
32
|
extend AutoCorrector
|
33
33
|
|
34
|
-
MSG =
|
35
|
-
'Avoid calling `blank?` with the safe navigation operator ' \
|
36
|
-
'in conditionals.'
|
34
|
+
MSG = 'Avoid calling `blank?` with the safe navigation operator in conditionals.'
|
37
35
|
|
38
36
|
def_node_matcher :safe_navigation_blank_in_conditional?, <<~PATTERN
|
39
37
|
(if $(csend ... :blank?) ...)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Identifies possible cases where Active Record save! or related
|
7
7
|
# should be used instead of save because the model might have failed to
|
8
8
|
# save and an exception is better than unhandled failure.
|
9
9
|
#
|
@@ -37,7 +37,7 @@ module RuboCop
|
|
37
37
|
#
|
38
38
|
# update_attributes
|
39
39
|
#
|
40
|
-
# # After running rubocop --safe-
|
40
|
+
# # After running rubocop --safe-autocorrect
|
41
41
|
# def update_attributes
|
42
42
|
# end
|
43
43
|
#
|
@@ -121,18 +121,12 @@ module RuboCop
|
|
121
121
|
include NegativeConditional
|
122
122
|
extend AutoCorrector
|
123
123
|
|
124
|
-
MSG = 'Use `%<prefer>s` instead of `%<current>s` if the return '
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
'always truthy.'
|
131
|
-
|
132
|
-
CREATE_PERSIST_METHODS = %i[create create_or_find_by
|
133
|
-
first_or_create find_or_create_by].freeze
|
134
|
-
MODIFY_PERSIST_METHODS = %i[save
|
135
|
-
update update_attributes destroy].freeze
|
124
|
+
MSG = 'Use `%<prefer>s` instead of `%<current>s` if the return value is not checked.'
|
125
|
+
CREATE_MSG = "#{MSG} Or check `persisted?` on model returned from `%<current>s`."
|
126
|
+
CREATE_CONDITIONAL_MSG = '`%<current>s` returns a model which is always truthy.'
|
127
|
+
|
128
|
+
CREATE_PERSIST_METHODS = %i[create create_or_find_by first_or_create find_or_create_by].freeze
|
129
|
+
MODIFY_PERSIST_METHODS = %i[save update update_attributes destroy].freeze
|
136
130
|
RESTRICT_ON_SEND = (CREATE_PERSIST_METHODS + MODIFY_PERSIST_METHODS).freeze
|
137
131
|
|
138
132
|
def self.joining_forces
|
@@ -244,8 +238,7 @@ module RuboCop
|
|
244
238
|
parent = node.parent
|
245
239
|
return false unless parent
|
246
240
|
|
247
|
-
operator_or_single_negative?(parent) ||
|
248
|
-
(conditional?(parent) && node == parent.condition)
|
241
|
+
operator_or_single_negative?(parent) || (conditional?(parent) && node == parent.condition)
|
249
242
|
end
|
250
243
|
|
251
244
|
def operator_or_single_negative?(node)
|
@@ -294,9 +287,7 @@ module RuboCop
|
|
294
287
|
# NameSpace::Const != ::Const
|
295
288
|
# Const != NameSpace::Const
|
296
289
|
def const_matches?(const, allowed_const)
|
297
|
-
parts = allowed_const.split('::').reverse.zip(
|
298
|
-
const.split('::').reverse
|
299
|
-
)
|
290
|
+
parts = allowed_const.split('::').reverse.zip(const.split('::').reverse)
|
300
291
|
parts.all? do |(allowed_part, const_part)|
|
301
292
|
allowed_part == const_part.to_s
|
302
293
|
end
|
@@ -335,9 +326,7 @@ module RuboCop
|
|
335
326
|
end
|
336
327
|
|
337
328
|
def persist_method?(node, methods = RESTRICT_ON_SEND)
|
338
|
-
methods.include?(node.method_name) &&
|
339
|
-
expected_signature?(node) &&
|
340
|
-
!allowed_receiver?(node)
|
329
|
+
methods.include?(node.method_name) && expected_signature?(node) && !allowed_receiver?(node)
|
341
330
|
end
|
342
331
|
|
343
332
|
# Check argument signature as no arguments or one hash
|
@@ -345,8 +334,7 @@ module RuboCop
|
|
345
334
|
!node.arguments? ||
|
346
335
|
(node.arguments.one? &&
|
347
336
|
node.method_name != :destroy &&
|
348
|
-
(node.first_argument.hash_type? ||
|
349
|
-
!node.first_argument.literal?))
|
337
|
+
(node.first_argument.hash_type? || !node.first_argument.literal?))
|
350
338
|
end
|
351
339
|
end
|
352
340
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Enforces the use of the `comment` option when adding a new table or column
|
7
7
|
# to the database during a migration.
|
8
8
|
#
|
9
9
|
# @example
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Enforces that short forms of `I18n` methods are used:
|
7
7
|
# `t` instead of `translate` and `l` instead of `localize`.
|
8
8
|
#
|
9
9
|
# This cop has two different enforcement modes. When the EnforcedStyle
|
@@ -44,15 +44,12 @@ module RuboCop
|
|
44
44
|
|
45
45
|
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
46
46
|
|
47
|
-
PREFERRED_METHODS = {
|
48
|
-
translate: :t,
|
49
|
-
localize: :l
|
50
|
-
}.freeze
|
47
|
+
PREFERRED_METHODS = { translate: :t, localize: :l }.freeze
|
51
48
|
|
52
49
|
RESTRICT_ON_SEND = PREFERRED_METHODS.keys.freeze
|
53
50
|
|
54
51
|
def_node_matcher :long_i18n?, <<~PATTERN
|
55
|
-
(send {nil? (const nil? :I18n)} ${:translate :localize} ...)
|
52
|
+
(send {nil? (const {nil? cbase} :I18n)} ${:translate :localize} ...)
|
56
53
|
PATTERN
|
57
54
|
|
58
55
|
def on_send(node)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks for the use of methods which skip
|
7
7
|
# validations which are listed in
|
8
8
|
# https://guides.rubyonrails.org/active_record_validations.html#skipping-validations
|
9
9
|
#
|
@@ -57,7 +57,7 @@ module RuboCop
|
|
57
57
|
|
58
58
|
def_node_matcher :good_touch?, <<~PATTERN
|
59
59
|
{
|
60
|
-
(send (const nil? :FileUtils) :touch ...)
|
60
|
+
(send (const {nil? cbase} :FileUtils) :touch ...)
|
61
61
|
(send _ :touch {true false})
|
62
62
|
}
|
63
63
|
PATTERN
|
@@ -93,8 +93,7 @@ module RuboCop
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def allowed_method?(node)
|
96
|
-
METHODS_WITH_ARGUMENTS.include?(node.method_name.to_s) &&
|
97
|
-
!node.arguments?
|
96
|
+
METHODS_WITH_ARGUMENTS.include?(node.method_name.to_s) && !node.arguments?
|
98
97
|
end
|
99
98
|
|
100
99
|
def forbidden_methods
|
@@ -8,7 +8,7 @@ module RuboCop
|
|
8
8
|
#
|
9
9
|
# @safety
|
10
10
|
# Some SQL syntax (e.g. PostgreSQL comments and functions) requires newlines
|
11
|
-
# to be preserved in order to work, thus
|
11
|
+
# to be preserved in order to work, thus autocorrection for this cop is not safe.
|
12
12
|
#
|
13
13
|
# @example
|
14
14
|
# # bad
|
@@ -48,6 +48,7 @@ module RuboCop
|
|
48
48
|
SQL = 'SQL'
|
49
49
|
SQUISH = '.squish'
|
50
50
|
MSG = 'Use `%<expect>s` instead of `%<current>s`.'
|
51
|
+
SQL_IDENTIFIER_MARKERS = /(".+?")|('.+?')|(\[.+?\])/.freeze
|
51
52
|
|
52
53
|
def on_heredoc(node)
|
53
54
|
return unless offense_detected?(node)
|
@@ -60,7 +61,7 @@ module RuboCop
|
|
60
61
|
private
|
61
62
|
|
62
63
|
def offense_detected?(node)
|
63
|
-
sql_heredoc?(node) && !using_squish?(node)
|
64
|
+
sql_heredoc?(node) && !using_squish?(node) && !singleline_comments_present?(node)
|
64
65
|
end
|
65
66
|
|
66
67
|
def sql_heredoc?(node)
|
@@ -71,12 +72,14 @@ module RuboCop
|
|
71
72
|
node.parent&.send_type? && node.parent&.method?(:squish)
|
72
73
|
end
|
73
74
|
|
75
|
+
def singleline_comments_present?(node)
|
76
|
+
sql = node.children.map { |c| c.is_a?(String) ? c : c.source }.join('\n')
|
77
|
+
|
78
|
+
sql.gsub(SQL_IDENTIFIER_MARKERS, '').include?('--')
|
79
|
+
end
|
80
|
+
|
74
81
|
def message(node)
|
75
|
-
format(
|
76
|
-
MSG,
|
77
|
-
expect: "#{node.source}#{SQUISH}",
|
78
|
-
current: node.source
|
79
|
-
)
|
82
|
+
format(MSG, expect: "#{node.source}#{SQUISH}", current: node.source)
|
80
83
|
end
|
81
84
|
end
|
82
85
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Enforces the use of squiggly heredoc over `strip_heredoc`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# <<EOS.strip_heredoc
|
12
|
+
# some text
|
13
|
+
# EOS
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# <<-EOS.strip_heredoc
|
17
|
+
# some text
|
18
|
+
# EOS
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# <<~EOS
|
22
|
+
# some text
|
23
|
+
# EOS
|
24
|
+
#
|
25
|
+
class StripHeredoc < Base
|
26
|
+
extend AutoCorrector
|
27
|
+
extend TargetRubyVersion
|
28
|
+
|
29
|
+
MSG = 'Use squiggly heredoc (`<<~`) instead of `strip_heredoc`.'
|
30
|
+
RESTRICT_ON_SEND = %i[strip_heredoc].freeze
|
31
|
+
|
32
|
+
minimum_target_ruby_version 2.3
|
33
|
+
|
34
|
+
def on_send(node)
|
35
|
+
return unless (receiver = node.receiver)
|
36
|
+
return unless receiver.str_type? || receiver.dstr_type?
|
37
|
+
return unless receiver.respond_to?(:heredoc?) && receiver.heredoc?
|
38
|
+
|
39
|
+
register_offense(node, receiver)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def register_offense(node, heredoc)
|
45
|
+
add_offense(node) do |corrector|
|
46
|
+
squiggly_heredoc = heredoc.source.sub(/\A<<(-|~)?/, '<<~')
|
47
|
+
|
48
|
+
corrector.replace(heredoc, squiggly_heredoc)
|
49
|
+
corrector.remove(node.loc.dot)
|
50
|
+
corrector.remove(node.loc.selector)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Enforces the absence of explicit table name assignment.
|
7
7
|
#
|
8
8
|
# `self.table_name=` should only be used for very good reasons,
|
9
9
|
# such as not having control over the database, or working
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Enforces that boolean columns are created with default values (`false` or `true`) and
|
7
|
+
# `NOT NULL` constraint.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# add_column :users, :active, :boolean
|
12
|
+
# t.column :active, :boolean
|
13
|
+
# t.boolean :active
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# add_column :users, :active, :boolean, default: true, null: false
|
17
|
+
# t.column :active, :boolean, default: true, null: false
|
18
|
+
# t.boolean :active, default: true, null: false
|
19
|
+
#
|
20
|
+
class ThreeStateBooleanColumn < Base
|
21
|
+
MSG = 'Boolean columns should always have a default value and a `NOT NULL` constraint.'
|
22
|
+
|
23
|
+
RESTRICT_ON_SEND = %i[add_column column boolean].freeze
|
24
|
+
|
25
|
+
def_node_matcher :three_state_boolean?, <<~PATTERN
|
26
|
+
{
|
27
|
+
(send nil? :add_column _ $_ {(sym :boolean) (str "boolean")} $_ ?)
|
28
|
+
(send !nil? :column $_ {(sym :boolean) (str "boolean")} $_ ?)
|
29
|
+
(send !nil? :boolean $_ $_ ?)
|
30
|
+
}
|
31
|
+
PATTERN
|
32
|
+
|
33
|
+
def_node_matcher :required_options?, <<~PATTERN
|
34
|
+
(hash <(pair (sym :default) !nil?) (pair (sym :null) false) ...>)
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
def_node_search :change_column_null?, <<~PATTERN
|
38
|
+
(send nil? :change_column_null {(sym %1) (str %1)} {(sym %2) (str %2)} false)
|
39
|
+
PATTERN
|
40
|
+
|
41
|
+
def on_send(node)
|
42
|
+
three_state_boolean?(node) do |column_node, options_node|
|
43
|
+
options_node = options_node.first
|
44
|
+
|
45
|
+
return if required_options?(options_node)
|
46
|
+
|
47
|
+
def_node = node.each_ancestor(:def, :defs).first
|
48
|
+
table_node = table_node(node)
|
49
|
+
if def_node && (table_node.nil? || change_column_null?(def_node, table_node.value, column_node.value))
|
50
|
+
return
|
51
|
+
end
|
52
|
+
|
53
|
+
add_offense(node)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def table_node(node)
|
60
|
+
case node.method_name
|
61
|
+
when :add_column
|
62
|
+
node.first_argument
|
63
|
+
when :column, :boolean
|
64
|
+
ancestor = node.each_ancestor(:block).find do |n|
|
65
|
+
n.method?(:create_table) || n.method?(:change_table)
|
66
|
+
end
|
67
|
+
ancestor&.send_node&.first_argument
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks for the use of Time methods without zone.
|
7
7
|
#
|
8
8
|
# Built on top of Ruby on Rails style guide (https://rails.rubystyle.guide#time)
|
9
9
|
# and the article http://danilenko.org/2012/7/6/rails_timezones/
|
@@ -28,38 +28,34 @@ module RuboCop
|
|
28
28
|
# Time.zone.parse('2015-03-02T19:05:37')
|
29
29
|
# Time.zone.parse('2015-03-02T19:05:37Z') # Respect ISO 8601 format with timezone specifier.
|
30
30
|
#
|
31
|
-
# @example EnforcedStyle: strict
|
32
|
-
# # `strict` means that `Time` should be used with `zone`.
|
33
|
-
#
|
34
|
-
# # bad
|
35
|
-
# Time.at(timestamp).in_time_zone
|
36
|
-
#
|
37
31
|
# @example EnforcedStyle: flexible (default)
|
38
32
|
# # `flexible` allows usage of `in_time_zone` instead of `zone`.
|
39
33
|
#
|
40
34
|
# # good
|
41
35
|
# Time.at(timestamp).in_time_zone
|
36
|
+
#
|
37
|
+
# @example EnforcedStyle: strict
|
38
|
+
# # `strict` means that `Time` should be used with `zone`.
|
39
|
+
#
|
40
|
+
# # bad
|
41
|
+
# Time.at(timestamp).in_time_zone
|
42
42
|
class TimeZone < Base
|
43
43
|
include ConfigurableEnforcedStyle
|
44
44
|
extend AutoCorrector
|
45
45
|
|
46
|
-
MSG = 'Do not use `%<current>s` without zone. Use `%<prefer>s` '
|
47
|
-
'instead.'
|
46
|
+
MSG = 'Do not use `%<current>s` without zone. Use `%<prefer>s` instead.'
|
48
47
|
|
49
|
-
MSG_ACCEPTABLE = 'Do not use `%<current>s` without zone. '
|
50
|
-
'Use one of %<prefer>s instead.'
|
48
|
+
MSG_ACCEPTABLE = 'Do not use `%<current>s` without zone. Use one of %<prefer>s instead.'
|
51
49
|
|
52
|
-
MSG_LOCALTIME = 'Do not use `Time.localtime` without '
|
53
|
-
'offset or zone.'
|
50
|
+
MSG_LOCALTIME = 'Do not use `Time.localtime` without offset or zone.'
|
54
51
|
|
55
52
|
GOOD_METHODS = %i[zone zone_default find_zone find_zone!].freeze
|
56
53
|
|
57
54
|
DANGEROUS_METHODS = %i[now local new parse at].freeze
|
58
55
|
|
59
|
-
ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601
|
60
|
-
jisx0301 rfc3339 httpdate to_i to_f].freeze
|
56
|
+
ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601 jisx0301 rfc3339 httpdate to_i to_f].freeze
|
61
57
|
|
62
|
-
TIMEZONE_SPECIFIER = /[A-z]/.freeze
|
58
|
+
TIMEZONE_SPECIFIER = /([A-z]|[+-]\d{2}:?\d{2})\z/.freeze
|
63
59
|
|
64
60
|
def on_const(node)
|
65
61
|
mod, klass = *node
|
@@ -74,7 +70,7 @@ module RuboCop
|
|
74
70
|
|
75
71
|
def autocorrect(corrector, node)
|
76
72
|
# add `.zone`: `Time.at` => `Time.zone.at`
|
77
|
-
corrector.insert_after(node.children[0]
|
73
|
+
corrector.insert_after(node.children[0], '.zone')
|
78
74
|
|
79
75
|
case node.method_name
|
80
76
|
when :current
|
@@ -85,7 +81,7 @@ module RuboCop
|
|
85
81
|
end
|
86
82
|
|
87
83
|
# prefer `Time` over `DateTime` class
|
88
|
-
corrector.replace(node.children.first
|
84
|
+
corrector.replace(node.children.first, 'Time') if strict?
|
89
85
|
remove_redundant_in_time_zone(corrector, node)
|
90
86
|
end
|
91
87
|
|
@@ -100,13 +96,11 @@ module RuboCop
|
|
100
96
|
# remove redundant `.in_time_zone` from `Time.zone.now.in_time_zone`
|
101
97
|
def remove_redundant_in_time_zone(corrector, node)
|
102
98
|
time_methods_called = extract_method_chain(node)
|
103
|
-
return unless time_methods_called.include?(:in_time_zone) ||
|
104
|
-
time_methods_called.include?(:zone)
|
99
|
+
return unless time_methods_called.include?(:in_time_zone) || time_methods_called.include?(:zone)
|
105
100
|
|
106
101
|
while node&.send_type?
|
107
102
|
if node.children.last == :in_time_zone
|
108
|
-
in_time_zone_with_dot =
|
109
|
-
node.loc.selector.adjust(begin_pos: -1)
|
103
|
+
in_time_zone_with_dot = node.loc.selector.adjust(begin_pos: -1)
|
110
104
|
corrector.remove(in_time_zone_with_dot)
|
111
105
|
end
|
112
106
|
node = node.parent
|
@@ -132,7 +126,7 @@ module RuboCop
|
|
132
126
|
end
|
133
127
|
|
134
128
|
def attach_timezone_specifier?(date)
|
135
|
-
date.respond_to?(:value) && TIMEZONE_SPECIFIER.match?(date.value.to_s
|
129
|
+
date.respond_to?(:value) && TIMEZONE_SPECIFIER.match?(date.value.to_s)
|
136
130
|
end
|
137
131
|
|
138
132
|
def build_message(klass, method_name, node)
|
@@ -144,9 +138,7 @@ module RuboCop
|
|
144
138
|
)
|
145
139
|
else
|
146
140
|
safe_method_name = safe_method(method_name, node)
|
147
|
-
format(MSG,
|
148
|
-
current: "#{klass}.#{method_name}",
|
149
|
-
prefer: "Time.zone.#{safe_method_name}")
|
141
|
+
format(MSG, current: "#{klass}.#{method_name}", prefer: "Time.zone.#{safe_method_name}")
|
150
142
|
end
|
151
143
|
end
|
152
144
|
|
@@ -227,10 +219,7 @@ module RuboCop
|
|
227
219
|
end
|
228
220
|
|
229
221
|
def acceptable_methods(klass, method_name, node)
|
230
|
-
acceptable = [
|
231
|
-
"`Time.zone.#{safe_method(method_name, node)}`",
|
232
|
-
"`#{klass}.current`"
|
233
|
-
]
|
222
|
+
acceptable = ["`Time.zone.#{safe_method(method_name, node)}`", "`#{klass}.current`"]
|
234
223
|
|
235
224
|
ACCEPTED_METHODS.each do |am|
|
236
225
|
acceptable << "`#{klass}.#{method_name}.#{am}`"
|
@@ -239,12 +228,25 @@ module RuboCop
|
|
239
228
|
acceptable
|
240
229
|
end
|
241
230
|
|
242
|
-
# Time.new can be called with a time zone offset
|
231
|
+
# Time.new, Time.at, and Time.now can be called with a time zone offset
|
243
232
|
# When it is, that should be considered safe
|
244
233
|
# Example:
|
245
234
|
# Time.new(1988, 3, 15, 3, 0, 0, "-05:00")
|
246
235
|
def offset_provided?(node)
|
247
|
-
node.
|
236
|
+
case node.method_name
|
237
|
+
when :new
|
238
|
+
node.arguments.size == 7 || offset_option_provided?(node)
|
239
|
+
when :at, :now
|
240
|
+
offset_option_provided?(node)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def offset_option_provided?(node)
|
245
|
+
options = node.last_argument
|
246
|
+
options&.hash_type? &&
|
247
|
+
options.each_pair.any? do |pair|
|
248
|
+
pair.key.sym_type? && pair.key.value == :in && !pair.value.nil_type?
|
249
|
+
end
|
248
250
|
end
|
249
251
|
end
|
250
252
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Rails
|
6
|
-
#
|
6
|
+
# Checks for the use of `Time.zone=` method.
|
7
7
|
#
|
8
8
|
# The `zone` attribute persists for the rest of the Ruby runtime, potentially causing
|
9
9
|
# unexpected behavior at a later time.
|
@@ -22,12 +22,12 @@ module RuboCop
|
|
22
22
|
MSG = 'Use `Time.use_zone` with block instead of `Time.zone=`.'
|
23
23
|
RESTRICT_ON_SEND = %i[zone=].freeze
|
24
24
|
|
25
|
-
def_node_matcher :
|
26
|
-
(send (const nil? :Time) :zone= ...)
|
25
|
+
def_node_matcher :time_zone_assignment?, <<~PATTERN
|
26
|
+
(send (const {nil? cbase} :Time) :zone= ...)
|
27
27
|
PATTERN
|
28
28
|
|
29
29
|
def on_send(node)
|
30
|
-
return unless
|
30
|
+
return unless time_zone_assignment?(node)
|
31
31
|
|
32
32
|
add_offense(node)
|
33
33
|
end
|