rubocop-rails 2.23.1 → 2.26.2
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/README.md +16 -2
- data/config/default.yml +32 -7
- data/lib/rubocop/cop/mixin/active_record_helper.rb +6 -1
- data/lib/rubocop/cop/mixin/target_rails_version.rb +29 -2
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +2 -0
- data/lib/rubocop/cop/rails/action_order.rb +1 -5
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +1 -5
- data/lib/rubocop/cop/rails/active_support_on_load.rb +21 -1
- data/lib/rubocop/cop/rails/application_record.rb +4 -0
- data/lib/rubocop/cop/rails/bulk_change_table.rb +10 -4
- data/lib/rubocop/cop/rails/compact_blank.rb +29 -8
- data/lib/rubocop/cop/rails/dangerous_column_names.rb +1 -2
- data/lib/rubocop/cop/rails/date.rb +2 -2
- data/lib/rubocop/cop/rails/enum_hash.rb +31 -8
- data/lib/rubocop/cop/rails/enum_syntax.rb +128 -0
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +29 -7
- data/lib/rubocop/cop/rails/expanded_date_range.rb +1 -1
- data/lib/rubocop/cop/rails/file_path.rb +1 -1
- data/lib/rubocop/cop/rails/find_by.rb +1 -1
- data/lib/rubocop/cop/rails/http_status.rb +12 -2
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +1 -1
- data/lib/rubocop/cop/rails/link_to_blank.rb +2 -2
- data/lib/rubocop/cop/rails/not_null_column.rb +93 -13
- data/lib/rubocop/cop/rails/pick.rb +4 -0
- data/lib/rubocop/cop/rails/pluck_in_where.rb +17 -8
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +29 -15
- data/lib/rubocop/cop/rails/present.rb +0 -2
- data/lib/rubocop/cop/rails/redundant_active_record_all_method.rb +0 -29
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +9 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +1 -1
- data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
- data/lib/rubocop/cop/rails/render_plain_text.rb +6 -3
- data/lib/rubocop/cop/rails/request_referer.rb +1 -1
- data/lib/rubocop/cop/rails/reversible_migration.rb +1 -1
- data/lib/rubocop/cop/rails/root_pathname_methods.rb +15 -11
- data/lib/rubocop/cop/rails/save_bang.rb +2 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +8 -3
- data/lib/rubocop/cop/rails/time_zone.rb +2 -1
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +12 -4
- data/lib/rubocop/cop/rails/unknown_env.rb +1 -1
- data/lib/rubocop/cop/rails/unused_ignored_columns.rb +6 -0
- data/lib/rubocop/cop/rails/validation.rb +8 -3
- data/lib/rubocop/cop/rails/where_equals.rb +28 -12
- data/lib/rubocop/cop/rails/where_exists.rb +3 -3
- data/lib/rubocop/cop/rails/where_missing.rb +5 -1
- data/lib/rubocop/cop/rails/where_not.rb +11 -6
- data/lib/rubocop/cop/rails/where_range.rb +203 -0
- data/lib/rubocop/cop/rails_cops.rb +2 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +2 -1
- data/lib/rubocop/rails/schema_loader.rb +5 -15
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +10 -8
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Rails
|
|
6
|
+
# Looks for enums written with keyword arguments syntax.
|
|
7
|
+
#
|
|
8
|
+
# Defining enums with keyword arguments syntax is deprecated and will be removed in Rails 8.0.
|
|
9
|
+
# Positional arguments should be used instead:
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# enum status: { active: 0, archived: 1 }, _prefix: true
|
|
14
|
+
#
|
|
15
|
+
# # good
|
|
16
|
+
# enum :status, { active: 0, archived: 1 }, prefix: true
|
|
17
|
+
#
|
|
18
|
+
class EnumSyntax < Base
|
|
19
|
+
extend AutoCorrector
|
|
20
|
+
extend TargetRubyVersion
|
|
21
|
+
extend TargetRailsVersion
|
|
22
|
+
|
|
23
|
+
minimum_target_ruby_version 3.0
|
|
24
|
+
minimum_target_rails_version 7.0
|
|
25
|
+
|
|
26
|
+
MSG = 'Enum defined with keyword arguments in `%<enum>s` enum declaration. Use positional arguments instead.'
|
|
27
|
+
MSG_OPTIONS = 'Enum defined with deprecated options in `%<enum>s` enum declaration. Remove the `_` prefix.'
|
|
28
|
+
RESTRICT_ON_SEND = %i[enum].freeze
|
|
29
|
+
|
|
30
|
+
# From https://github.com/rails/rails/blob/v7.2.1/activerecord/lib/active_record/enum.rb#L231
|
|
31
|
+
OPTION_NAMES = %w[prefix suffix scopes default instance_methods].freeze
|
|
32
|
+
UNDERSCORED_OPTION_NAMES = OPTION_NAMES.map { |option| "_#{option}" }.freeze
|
|
33
|
+
|
|
34
|
+
def_node_matcher :enum?, <<~PATTERN
|
|
35
|
+
(send nil? :enum (hash $...))
|
|
36
|
+
PATTERN
|
|
37
|
+
|
|
38
|
+
def_node_matcher :enum_with_options?, <<~PATTERN
|
|
39
|
+
(send nil? :enum $_ ${array hash} $_)
|
|
40
|
+
PATTERN
|
|
41
|
+
|
|
42
|
+
def on_send(node)
|
|
43
|
+
check_and_correct_keyword_args(node)
|
|
44
|
+
check_enum_options(node)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def check_and_correct_keyword_args(node)
|
|
50
|
+
enum?(node) do |pairs|
|
|
51
|
+
pairs.each do |pair|
|
|
52
|
+
next if option_key?(pair)
|
|
53
|
+
|
|
54
|
+
correct_keyword_args(node, pair.key, pair.value, pairs[1..])
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def check_enum_options(node)
|
|
60
|
+
enum_with_options?(node) do |key, _, options|
|
|
61
|
+
options.children.each do |option|
|
|
62
|
+
next unless option_key?(option)
|
|
63
|
+
|
|
64
|
+
add_offense(option.key, message: format(MSG_OPTIONS, enum: enum_name_value(key))) do |corrector|
|
|
65
|
+
corrector.replace(option.key, option.key.source.delete_prefix('_'))
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def correct_keyword_args(node, key, values, options)
|
|
72
|
+
add_offense(values, message: format(MSG, enum: enum_name_value(key))) do |corrector|
|
|
73
|
+
# TODO: Multi-line autocorrect could be implemented in the future.
|
|
74
|
+
next if multiple_enum_definitions?(node)
|
|
75
|
+
|
|
76
|
+
preferred_syntax = "enum #{enum_name(key)}, #{values.source}#{correct_options(options)}"
|
|
77
|
+
|
|
78
|
+
corrector.replace(node, preferred_syntax)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def multiple_enum_definitions?(node)
|
|
83
|
+
keys = node.first_argument.keys.map { |key| key.source.delete_prefix('_') }
|
|
84
|
+
filterred_keys = keys.filter { |key| !OPTION_NAMES.include?(key) }
|
|
85
|
+
filterred_keys.size >= 2
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def enum_name_value(key)
|
|
89
|
+
case key.type
|
|
90
|
+
when :sym, :str
|
|
91
|
+
key.value
|
|
92
|
+
else
|
|
93
|
+
key.source
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def enum_name(elem)
|
|
98
|
+
case elem.type
|
|
99
|
+
when :str
|
|
100
|
+
elem.value.dump
|
|
101
|
+
when :sym
|
|
102
|
+
elem.value.inspect
|
|
103
|
+
else
|
|
104
|
+
elem.source
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def option_key?(pair)
|
|
109
|
+
UNDERSCORED_OPTION_NAMES.include?(pair.key.source)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def correct_options(options)
|
|
113
|
+
corrected_options = options.map do |pair|
|
|
114
|
+
name = if pair.key.source[0] == '_'
|
|
115
|
+
pair.key.source[1..]
|
|
116
|
+
else
|
|
117
|
+
pair.key.source
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
"#{name}: #{pair.value.source}"
|
|
121
|
+
end.join(', ')
|
|
122
|
+
|
|
123
|
+
", #{corrected_options}" unless corrected_options.empty?
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -7,6 +7,18 @@ module RuboCop
|
|
|
7
7
|
#
|
|
8
8
|
# @example
|
|
9
9
|
# # bad
|
|
10
|
+
# enum :status, { active: 0, archived: 0 }
|
|
11
|
+
#
|
|
12
|
+
# # good
|
|
13
|
+
# enum :status, { active: 0, archived: 1 }
|
|
14
|
+
#
|
|
15
|
+
# # bad
|
|
16
|
+
# enum :status, [:active, :archived, :active]
|
|
17
|
+
#
|
|
18
|
+
# # good
|
|
19
|
+
# enum :status, [:active, :archived]
|
|
20
|
+
#
|
|
21
|
+
# # bad
|
|
10
22
|
# enum status: { active: 0, archived: 0 }
|
|
11
23
|
#
|
|
12
24
|
# # good
|
|
@@ -24,6 +36,10 @@ module RuboCop
|
|
|
24
36
|
RESTRICT_ON_SEND = %i[enum].freeze
|
|
25
37
|
|
|
26
38
|
def_node_matcher :enum?, <<~PATTERN
|
|
39
|
+
(send nil? :enum $_ ${array hash} ...)
|
|
40
|
+
PATTERN
|
|
41
|
+
|
|
42
|
+
def_node_matcher :enum_with_old_syntax?, <<~PATTERN
|
|
27
43
|
(send nil? :enum (hash $...))
|
|
28
44
|
PATTERN
|
|
29
45
|
|
|
@@ -32,15 +48,17 @@ module RuboCop
|
|
|
32
48
|
PATTERN
|
|
33
49
|
|
|
34
50
|
def on_send(node)
|
|
35
|
-
enum?(node) do |
|
|
51
|
+
enum?(node) do |key, args|
|
|
52
|
+
consecutive_duplicates(args.values).each do |item|
|
|
53
|
+
add_offense(item, message: message(key, item))
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
enum_with_old_syntax?(node) do |pairs|
|
|
36
58
|
pairs.each do |pair|
|
|
37
59
|
enum_values(pair) do |key, args|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
next unless duplicates?(items)
|
|
41
|
-
|
|
42
|
-
consecutive_duplicates(items).each do |item|
|
|
43
|
-
add_offense(item, message: format(MSG, value: item.source, enum: enum_name(key)))
|
|
60
|
+
consecutive_duplicates(args.values).each do |item|
|
|
61
|
+
add_offense(item, message: message(key, item))
|
|
44
62
|
end
|
|
45
63
|
end
|
|
46
64
|
end
|
|
@@ -57,6 +75,10 @@ module RuboCop
|
|
|
57
75
|
key.source
|
|
58
76
|
end
|
|
59
77
|
end
|
|
78
|
+
|
|
79
|
+
def message(key, item)
|
|
80
|
+
format(MSG, value: item.source, enum: enum_name(key))
|
|
81
|
+
end
|
|
60
82
|
end
|
|
61
83
|
end
|
|
62
84
|
end
|
|
@@ -51,7 +51,7 @@ module RuboCop
|
|
|
51
51
|
return if allow?(begin_node, end_node)
|
|
52
52
|
|
|
53
53
|
preferred_method = preferred_method(begin_node)
|
|
54
|
-
if begin_node.method?(:beginning_of_week) && begin_node.arguments.one?
|
|
54
|
+
if begin_node.method?(:beginning_of_week) && begin_node.arguments.one? && end_node.arguments.one?
|
|
55
55
|
return unless same_argument?(begin_node, end_node)
|
|
56
56
|
|
|
57
57
|
preferred_method << "(#{begin_node.first_argument.source})"
|
|
@@ -97,7 +97,7 @@ module RuboCop
|
|
|
97
97
|
return unless node.arguments.any? { |e| rails_root_nodes?(e) }
|
|
98
98
|
|
|
99
99
|
register_offense(node, require_to_s: true) do |corrector|
|
|
100
|
-
autocorrect_file_join(corrector, node)
|
|
100
|
+
autocorrect_file_join(corrector, node) unless node.first_argument.array_type?
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
|
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
return if node.method?(:first)
|
|
60
60
|
|
|
61
61
|
where_loc = node.receiver.loc.selector
|
|
62
|
-
first_loc = range_between(node.
|
|
62
|
+
first_loc = range_between(node.receiver.source_range.end_pos, node.loc.selector.end_pos)
|
|
63
63
|
|
|
64
64
|
corrector.replace(where_loc, 'find_by')
|
|
65
65
|
corrector.replace(first_loc, '')
|
|
@@ -13,6 +13,8 @@ module RuboCop
|
|
|
13
13
|
# render plain: 'foo/bar', status: 304
|
|
14
14
|
# redirect_to root_url, status: 301
|
|
15
15
|
# head 200
|
|
16
|
+
# assert_response 200
|
|
17
|
+
# assert_redirected_to '/some/path', status: 301
|
|
16
18
|
#
|
|
17
19
|
# # good
|
|
18
20
|
# render :foo, status: :ok
|
|
@@ -20,6 +22,8 @@ module RuboCop
|
|
|
20
22
|
# render plain: 'foo/bar', status: :not_modified
|
|
21
23
|
# redirect_to root_url, status: :moved_permanently
|
|
22
24
|
# head :ok
|
|
25
|
+
# assert_response :ok
|
|
26
|
+
# assert_redirected_to '/some/path', status: :moved_permanently
|
|
23
27
|
#
|
|
24
28
|
# @example EnforcedStyle: numeric
|
|
25
29
|
# # bad
|
|
@@ -28,6 +32,8 @@ module RuboCop
|
|
|
28
32
|
# render plain: 'foo/bar', status: :not_modified
|
|
29
33
|
# redirect_to root_url, status: :moved_permanently
|
|
30
34
|
# head :ok
|
|
35
|
+
# assert_response :ok
|
|
36
|
+
# assert_redirected_to '/some/path', status: :moved_permanently
|
|
31
37
|
#
|
|
32
38
|
# # good
|
|
33
39
|
# render :foo, status: 200
|
|
@@ -35,18 +41,22 @@ module RuboCop
|
|
|
35
41
|
# render plain: 'foo/bar', status: 304
|
|
36
42
|
# redirect_to root_url, status: 301
|
|
37
43
|
# head 200
|
|
44
|
+
# assert_response 200
|
|
45
|
+
# assert_redirected_to '/some/path', status: 301
|
|
38
46
|
#
|
|
39
47
|
class HttpStatus < Base
|
|
40
48
|
include ConfigurableEnforcedStyle
|
|
41
49
|
extend AutoCorrector
|
|
42
50
|
|
|
43
|
-
RESTRICT_ON_SEND = %i[render redirect_to head].freeze
|
|
51
|
+
RESTRICT_ON_SEND = %i[render redirect_to head assert_response assert_redirected_to].freeze
|
|
44
52
|
|
|
45
53
|
def_node_matcher :http_status, <<~PATTERN
|
|
46
54
|
{
|
|
47
55
|
(send nil? {:render :redirect_to} _ $hash)
|
|
48
56
|
(send nil? {:render :redirect_to} $hash)
|
|
49
|
-
(send nil? :head ${int sym} ...)
|
|
57
|
+
(send nil? {:head :assert_response} ${int sym} ...)
|
|
58
|
+
(send nil? :assert_redirected_to _ $hash ...)
|
|
59
|
+
(send nil? :assert_redirected_to $hash ...)
|
|
50
60
|
}
|
|
51
61
|
PATTERN
|
|
52
62
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Rails
|
|
6
|
-
# Checks for calls to `link_to` that contain a
|
|
6
|
+
# Checks for calls to `link_to`, `link_to_if`, and `link_to_unless` methods that contain a
|
|
7
7
|
# `target: '_blank'` but no `rel: 'noopener'`. This can be a security
|
|
8
8
|
# risk as the loaded page will have control over the previous page
|
|
9
9
|
# and could change its location for phishing purposes.
|
|
@@ -24,7 +24,7 @@ module RuboCop
|
|
|
24
24
|
extend AutoCorrector
|
|
25
25
|
|
|
26
26
|
MSG = 'Specify a `:rel` option containing noopener.'
|
|
27
|
-
RESTRICT_ON_SEND = %i[link_to].freeze
|
|
27
|
+
RESTRICT_ON_SEND = %i[link_to link_to_if link_to_unless].freeze
|
|
28
28
|
|
|
29
29
|
def_node_matcher :blank_target?, <<~PATTERN
|
|
30
30
|
(pair {(sym :target) (str "target")} {(str "_blank") (sym :_blank)})
|
|
@@ -3,24 +3,42 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Rails
|
|
6
|
-
# Checks for add_column
|
|
6
|
+
# Checks for add_column calls with a NOT NULL constraint without a default
|
|
7
|
+
# value.
|
|
7
8
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
9
|
+
# This cop only applies when adding a column to an existing table, since
|
|
10
|
+
# existing records will not have a value for the new column. New tables
|
|
11
|
+
# can freely use NOT NULL columns without defaults, since there are no
|
|
12
|
+
# records that could violate the constraint.
|
|
13
|
+
#
|
|
14
|
+
# If you need to add a NOT NULL column to an existing table, you must add
|
|
15
|
+
# it as nullable first, back-fill the data, and then use
|
|
16
|
+
# `change_column_null`. Alternatively, you could add the column with a
|
|
17
|
+
# default first to have the database automatically backfill existing rows,
|
|
18
|
+
# and then use `change_column_default` to remove the default.
|
|
19
|
+
#
|
|
20
|
+
# `TEXT` cannot have a default value in MySQL.
|
|
21
|
+
# The cop will automatically detect an adapter from `development`
|
|
22
|
+
# environment in `config/database.yml` or the environment variable
|
|
23
|
+
# `DATABASE_URL` when the `Database` option is not set. If the database
|
|
24
|
+
# is MySQL, this cop ignores offenses for `TEXT` columns.
|
|
13
25
|
#
|
|
14
26
|
# @example
|
|
15
27
|
# # bad
|
|
16
28
|
# add_column :users, :name, :string, null: false
|
|
17
29
|
# add_reference :products, :category, null: false
|
|
30
|
+
# change_table :users do |t|
|
|
31
|
+
# t.string :name, null: false
|
|
32
|
+
# end
|
|
18
33
|
#
|
|
19
34
|
# # good
|
|
20
35
|
# add_column :users, :name, :string, null: true
|
|
21
36
|
# add_column :users, :name, :string, null: false, default: ''
|
|
37
|
+
# change_table :users do |t|
|
|
38
|
+
# t.string :name, null: false, default: ''
|
|
39
|
+
# end
|
|
22
40
|
# add_reference :products, :category
|
|
23
|
-
#
|
|
41
|
+
# change_column_null :products, :category_id, false
|
|
24
42
|
class NotNullColumn < Base
|
|
25
43
|
include DatabaseTypeResolvable
|
|
26
44
|
|
|
@@ -35,6 +53,22 @@ module RuboCop
|
|
|
35
53
|
(send nil? :add_reference _ _ (hash $...))
|
|
36
54
|
PATTERN
|
|
37
55
|
|
|
56
|
+
def_node_matcher :change_table?, <<~PATTERN
|
|
57
|
+
(block (send nil? :change_table ...) (args (arg $_)) _)
|
|
58
|
+
PATTERN
|
|
59
|
+
|
|
60
|
+
def_node_matcher :add_not_null_column_in_change_table?, <<~PATTERN
|
|
61
|
+
(send (lvar $_) :column _ $_ (hash $...))
|
|
62
|
+
PATTERN
|
|
63
|
+
|
|
64
|
+
def_node_matcher :add_not_null_column_via_shortcut_in_change_table?, <<~PATTERN
|
|
65
|
+
(send (lvar $_) $_ _ (hash $...))
|
|
66
|
+
PATTERN
|
|
67
|
+
|
|
68
|
+
def_node_matcher :add_not_null_reference_in_change_table?, <<~PATTERN
|
|
69
|
+
(send (lvar $_) :add_reference _ _ (hash $...))
|
|
70
|
+
PATTERN
|
|
71
|
+
|
|
38
72
|
def_node_matcher :null_false?, <<~PATTERN
|
|
39
73
|
(pair (sym :null) (false))
|
|
40
74
|
PATTERN
|
|
@@ -48,16 +82,25 @@ module RuboCop
|
|
|
48
82
|
check_add_reference(node)
|
|
49
83
|
end
|
|
50
84
|
|
|
85
|
+
def on_block(node)
|
|
86
|
+
check_change_table(node)
|
|
87
|
+
end
|
|
88
|
+
alias on_numblock on_block
|
|
89
|
+
|
|
51
90
|
private
|
|
52
91
|
|
|
92
|
+
def check_column(type, pairs)
|
|
93
|
+
if type.respond_to?(:value)
|
|
94
|
+
return if type.value == :virtual || type.value == 'virtual'
|
|
95
|
+
return if (type.value == :text || type.value == 'text') && database == MYSQL
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
check_pairs(pairs)
|
|
99
|
+
end
|
|
100
|
+
|
|
53
101
|
def check_add_column(node)
|
|
54
102
|
add_not_null_column?(node) do |type, pairs|
|
|
55
|
-
|
|
56
|
-
return if type.value == :virtual || type.value == 'virtual'
|
|
57
|
-
return if (type.value == :text || type.value == 'text') && database == MYSQL
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
check_pairs(pairs)
|
|
103
|
+
check_column(type, pairs)
|
|
61
104
|
end
|
|
62
105
|
end
|
|
63
106
|
|
|
@@ -67,6 +110,43 @@ module RuboCop
|
|
|
67
110
|
end
|
|
68
111
|
end
|
|
69
112
|
|
|
113
|
+
def check_add_column_in_change_table(node, table)
|
|
114
|
+
add_not_null_column_in_change_table?(node) do |receiver, type, pairs|
|
|
115
|
+
next unless receiver == table
|
|
116
|
+
|
|
117
|
+
check_column(type, pairs)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def check_add_column_via_shortcut_in_change_table(node, table)
|
|
122
|
+
add_not_null_column_via_shortcut_in_change_table?(node) do |receiver, type, pairs|
|
|
123
|
+
next unless receiver == table
|
|
124
|
+
|
|
125
|
+
check_column(type, pairs)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def check_add_reference_in_change_table(node, table)
|
|
130
|
+
add_not_null_reference_in_change_table?(node) do |receiver, pairs|
|
|
131
|
+
next unless receiver == table
|
|
132
|
+
|
|
133
|
+
check_pairs(pairs)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def check_change_table(node)
|
|
138
|
+
change_table?(node) do |table|
|
|
139
|
+
next unless node.body
|
|
140
|
+
|
|
141
|
+
children = node.body.begin_type? ? node.body.children : [node.body]
|
|
142
|
+
children.each do |child|
|
|
143
|
+
check_add_column_in_change_table(child, table)
|
|
144
|
+
check_add_column_via_shortcut_in_change_table(child, table)
|
|
145
|
+
check_add_reference_in_change_table(child, table)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
70
150
|
def check_pairs(pairs)
|
|
71
151
|
return if pairs.any? { |pair| default_option?(pair) }
|
|
72
152
|
|
|
@@ -9,6 +9,10 @@ module RuboCop
|
|
|
9
9
|
# `pick` avoids. When called on an Active Record relation, `pick` adds a
|
|
10
10
|
# limit to the query so that only one value is fetched from the database.
|
|
11
11
|
#
|
|
12
|
+
# Note that when `pick` is added to a relation with an existing limit, it
|
|
13
|
+
# causes a subquery to be added. In most cases this is undesirable, and
|
|
14
|
+
# care should be taken while resolving this violation.
|
|
15
|
+
#
|
|
12
16
|
# @safety
|
|
13
17
|
# This cop is unsafe because `pluck` is defined on both `ActiveRecord::Relation` and `Enumerable`,
|
|
14
18
|
# whereas `pick` is only defined on `ActiveRecord::Relation` in Rails 6.0. This was addressed
|
|
@@ -7,17 +7,26 @@ module RuboCop
|
|
|
7
7
|
# and can be replaced with `select`.
|
|
8
8
|
#
|
|
9
9
|
# Since `pluck` is an eager method and hits the database immediately,
|
|
10
|
-
# using `select` helps to avoid additional database queries
|
|
10
|
+
# using `select` helps to avoid additional database queries by running as
|
|
11
|
+
# a subquery.
|
|
11
12
|
#
|
|
12
|
-
# This cop has two
|
|
13
|
-
#
|
|
14
|
-
# (
|
|
13
|
+
# This cop has two modes of enforcement. When the `EnforcedStyle` is set
|
|
14
|
+
# to `conservative` (the default), only calls to `pluck` on a constant
|
|
15
|
+
# (e.g. a model class) within `where` are considered offenses.
|
|
15
16
|
#
|
|
16
17
|
# @safety
|
|
17
|
-
# When
|
|
18
|
-
# `where`
|
|
19
|
-
#
|
|
20
|
-
# `ActiveRecord::Relation` instance
|
|
18
|
+
# When `EnforcedStyle` is set to `aggressive`, all calls to `pluck`
|
|
19
|
+
# within `where` are considered offenses. This might lead to false
|
|
20
|
+
# positives because the check cannot distinguish between calls to
|
|
21
|
+
# `pluck` on an `ActiveRecord::Relation` instance and calls to `pluck`
|
|
22
|
+
# on an `Array` instance.
|
|
23
|
+
#
|
|
24
|
+
# Additionally, when using a subquery with the SQL `IN` operator,
|
|
25
|
+
# databases like PostgreSQL and MySQL can't optimize complex queries as
|
|
26
|
+
# well. They need to scan all records of the outer table against the
|
|
27
|
+
# subquery result sequentially, rather than using an index. This can
|
|
28
|
+
# cause significant performance issues compared to writing the query
|
|
29
|
+
# differently or using `pluck`.
|
|
21
30
|
#
|
|
22
31
|
# @example
|
|
23
32
|
# # bad
|
|
@@ -10,25 +10,39 @@ module RuboCop
|
|
|
10
10
|
# # bad
|
|
11
11
|
# 3.day.ago
|
|
12
12
|
# 1.months.ago
|
|
13
|
+
# 5.megabyte
|
|
14
|
+
# 1.gigabytes
|
|
13
15
|
#
|
|
14
16
|
# # good
|
|
15
17
|
# 3.days.ago
|
|
16
18
|
# 1.month.ago
|
|
19
|
+
# 5.megabytes
|
|
20
|
+
# 1.gigabyte
|
|
17
21
|
class PluralizationGrammar < Base
|
|
18
22
|
extend AutoCorrector
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
SINGULAR_METHODS = {
|
|
25
|
+
second: :seconds,
|
|
26
|
+
minute: :minutes,
|
|
27
|
+
hour: :hours,
|
|
28
|
+
day: :days,
|
|
29
|
+
week: :weeks,
|
|
30
|
+
fortnight: :fortnights,
|
|
31
|
+
month: :months,
|
|
32
|
+
year: :years,
|
|
33
|
+
byte: :bytes,
|
|
34
|
+
kilobyte: :kilobytes,
|
|
35
|
+
megabyte: :megabytes,
|
|
36
|
+
gigabyte: :gigabytes,
|
|
37
|
+
terabyte: :terabytes,
|
|
38
|
+
petabyte: :petabytes,
|
|
39
|
+
exabyte: :exabytes,
|
|
40
|
+
zettabyte: :zettabytes
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
RESTRICT_ON_SEND = SINGULAR_METHODS.keys + SINGULAR_METHODS.values
|
|
44
|
+
|
|
45
|
+
PLURAL_METHODS = SINGULAR_METHODS.invert.freeze
|
|
32
46
|
|
|
33
47
|
MSG = 'Prefer `%<number>s.%<correct>s`.'
|
|
34
48
|
|
|
@@ -86,15 +100,15 @@ module RuboCop
|
|
|
86
100
|
end
|
|
87
101
|
|
|
88
102
|
def pluralize(method_name)
|
|
89
|
-
|
|
103
|
+
SINGULAR_METHODS.fetch(method_name.to_sym).to_s
|
|
90
104
|
end
|
|
91
105
|
|
|
92
106
|
def singularize(method_name)
|
|
93
|
-
|
|
107
|
+
PLURAL_METHODS.fetch(method_name.to_sym).to_s
|
|
94
108
|
end
|
|
95
109
|
|
|
96
110
|
def duration_method?(method_name)
|
|
97
|
-
|
|
111
|
+
SINGULAR_METHODS.key?(method_name) || PLURAL_METHODS.key?(method_name)
|
|
98
112
|
end
|
|
99
113
|
end
|
|
100
114
|
end
|
|
@@ -3,35 +3,6 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Rails
|
|
6
|
-
# TODO: In the future, please support only RuboCop 1.52+ and use `RuboCop::Cop::AllowedReceivers`:
|
|
7
|
-
# https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/cop/mixin/allowed_receivers.rb
|
|
8
|
-
# At that time, this duplicated module implementation can be removed.
|
|
9
|
-
module AllowedReceivers
|
|
10
|
-
def allowed_receiver?(receiver)
|
|
11
|
-
receiver_name = receiver_name(receiver)
|
|
12
|
-
|
|
13
|
-
allowed_receivers.include?(receiver_name)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def receiver_name(receiver)
|
|
17
|
-
return receiver_name(receiver.receiver) if receiver.receiver && !receiver.receiver.const_type?
|
|
18
|
-
|
|
19
|
-
if receiver.send_type?
|
|
20
|
-
if receiver.receiver
|
|
21
|
-
"#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
|
|
22
|
-
else
|
|
23
|
-
receiver.method_name.to_s
|
|
24
|
-
end
|
|
25
|
-
else
|
|
26
|
-
receiver.source
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def allowed_receivers
|
|
31
|
-
cop_config.fetch('AllowedReceivers', [])
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
6
|
# Detect redundant `all` used as a receiver for Active Record query methods.
|
|
36
7
|
#
|
|
37
8
|
# For the methods `delete_all` and `destroy_all`, this cop will only check cases where the receiver is a model.
|
|
@@ -40,7 +40,7 @@ module RuboCop
|
|
|
40
40
|
def on_send(node)
|
|
41
41
|
association_with_foreign_key(node) do |type, name, options, foreign_key_pair, foreign_key|
|
|
42
42
|
if redundant?(node, type, name, options, foreign_key)
|
|
43
|
-
add_offense(foreign_key_pair
|
|
43
|
+
add_offense(foreign_key_pair) do |corrector|
|
|
44
44
|
range = range_with_surrounding_space(foreign_key_pair.source_range, side: :left)
|
|
45
45
|
range = range_with_surrounding_comma(range, :left)
|
|
46
46
|
|
|
@@ -39,6 +39,9 @@ module RuboCop
|
|
|
39
39
|
MSG = 'Remove explicit presence validation for %<association>s.'
|
|
40
40
|
RESTRICT_ON_SEND = %i[validates].freeze
|
|
41
41
|
|
|
42
|
+
# From https://github.com/rails/rails/blob/7a0bf93b9dd291c7f61121a41b3a813ac8857e6a/activemodel/lib/active_model/validations/validates.rb#L157-L159
|
|
43
|
+
NON_VALIDATION_OPTIONS = %i[if unless on allow_blank allow_nil strict].freeze
|
|
44
|
+
|
|
42
45
|
minimum_target_rails_version 5.0
|
|
43
46
|
|
|
44
47
|
# @!method presence_validation?(node)
|
|
@@ -170,6 +173,12 @@ module RuboCop
|
|
|
170
173
|
|
|
171
174
|
def on_send(node)
|
|
172
175
|
presence_validation?(node) do |all_keys, options, presence|
|
|
176
|
+
# If presence is the only validation option and other non-validation options
|
|
177
|
+
# are present, removing it will cause rails to error.
|
|
178
|
+
used_option_keys = options.keys.select(&:sym_type?).map(&:value)
|
|
179
|
+
remaining_validations = used_option_keys - NON_VALIDATION_OPTIONS - [:presence]
|
|
180
|
+
return if remaining_validations.none? && options.keys.length > 1
|
|
181
|
+
|
|
173
182
|
keys = non_optional_belongs_to(node.parent, all_keys)
|
|
174
183
|
return if keys.none?
|
|
175
184
|
|
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
|
43
43
|
return if reflection_class_name.value.send_type? && reflection_class_name.value.receiver.nil?
|
|
44
44
|
return if reflection_class_name.value.lvar_type? && str_assigned?(reflection_class_name)
|
|
45
45
|
|
|
46
|
-
add_offense(reflection_class_name
|
|
46
|
+
add_offense(reflection_class_name) do |corrector|
|
|
47
47
|
autocorrect(corrector, reflection_class_name)
|
|
48
48
|
end
|
|
49
49
|
end
|