rubocop-rails 2.4.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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +92 -0
  4. data/bin/setup +7 -0
  5. data/config/default.yml +510 -0
  6. data/lib/rubocop-rails.rb +12 -0
  7. data/lib/rubocop/cop/mixin/target_rails_version.rb +16 -0
  8. data/lib/rubocop/cop/rails/action_filter.rb +111 -0
  9. data/lib/rubocop/cop/rails/active_record_aliases.rb +48 -0
  10. data/lib/rubocop/cop/rails/active_record_override.rb +82 -0
  11. data/lib/rubocop/cop/rails/active_support_aliases.rb +69 -0
  12. data/lib/rubocop/cop/rails/application_controller.rb +36 -0
  13. data/lib/rubocop/cop/rails/application_job.rb +40 -0
  14. data/lib/rubocop/cop/rails/application_mailer.rb +40 -0
  15. data/lib/rubocop/cop/rails/application_record.rb +40 -0
  16. data/lib/rubocop/cop/rails/assert_not.rb +44 -0
  17. data/lib/rubocop/cop/rails/belongs_to.rb +102 -0
  18. data/lib/rubocop/cop/rails/blank.rb +164 -0
  19. data/lib/rubocop/cop/rails/bulk_change_table.rb +293 -0
  20. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +91 -0
  21. data/lib/rubocop/cop/rails/date.rb +161 -0
  22. data/lib/rubocop/cop/rails/delegate.rb +132 -0
  23. data/lib/rubocop/cop/rails/delegate_allow_blank.rb +37 -0
  24. data/lib/rubocop/cop/rails/dynamic_find_by.rb +91 -0
  25. data/lib/rubocop/cop/rails/enum_hash.rb +75 -0
  26. data/lib/rubocop/cop/rails/enum_uniqueness.rb +65 -0
  27. data/lib/rubocop/cop/rails/environment_comparison.rb +68 -0
  28. data/lib/rubocop/cop/rails/exit.rb +67 -0
  29. data/lib/rubocop/cop/rails/file_path.rb +108 -0
  30. data/lib/rubocop/cop/rails/find_by.rb +55 -0
  31. data/lib/rubocop/cop/rails/find_each.rb +51 -0
  32. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +25 -0
  33. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +106 -0
  34. data/lib/rubocop/cop/rails/helper_instance_variable.rb +39 -0
  35. data/lib/rubocop/cop/rails/http_positional_arguments.rb +117 -0
  36. data/lib/rubocop/cop/rails/http_status.rb +160 -0
  37. data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +94 -0
  38. data/lib/rubocop/cop/rails/inverse_of.rb +246 -0
  39. data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +175 -0
  40. data/lib/rubocop/cop/rails/link_to_blank.rb +98 -0
  41. data/lib/rubocop/cop/rails/not_null_column.rb +67 -0
  42. data/lib/rubocop/cop/rails/output.rb +49 -0
  43. data/lib/rubocop/cop/rails/output_safety.rb +99 -0
  44. data/lib/rubocop/cop/rails/pluralization_grammar.rb +107 -0
  45. data/lib/rubocop/cop/rails/presence.rb +148 -0
  46. data/lib/rubocop/cop/rails/present.rb +153 -0
  47. data/lib/rubocop/cop/rails/rake_environment.rb +91 -0
  48. data/lib/rubocop/cop/rails/read_write_attribute.rb +74 -0
  49. data/lib/rubocop/cop/rails/redundant_allow_nil.rb +111 -0
  50. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +136 -0
  51. data/lib/rubocop/cop/rails/reflection_class_name.rb +37 -0
  52. data/lib/rubocop/cop/rails/refute_methods.rb +76 -0
  53. data/lib/rubocop/cop/rails/relative_date_constant.rb +102 -0
  54. data/lib/rubocop/cop/rails/request_referer.rb +56 -0
  55. data/lib/rubocop/cop/rails/reversible_migration.rb +284 -0
  56. data/lib/rubocop/cop/rails/safe_navigation.rb +85 -0
  57. data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +48 -0
  58. data/lib/rubocop/cop/rails/save_bang.rb +331 -0
  59. data/lib/rubocop/cop/rails/scope_args.rb +29 -0
  60. data/lib/rubocop/cop/rails/skips_model_validations.rb +87 -0
  61. data/lib/rubocop/cop/rails/time_zone.rb +249 -0
  62. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +105 -0
  63. data/lib/rubocop/cop/rails/unknown_env.rb +84 -0
  64. data/lib/rubocop/cop/rails/validation.rb +147 -0
  65. data/lib/rubocop/cop/rails_cops.rb +61 -0
  66. data/lib/rubocop/rails.rb +12 -0
  67. data/lib/rubocop/rails/inject.rb +18 -0
  68. data/lib/rubocop/rails/version.rb +10 -0
  69. metadata +148 -0
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks if the value of the option `class_name`, in
7
+ # the definition of a reflection is a string.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # has_many :accounts, class_name: Account
12
+ # has_many :accounts, class_name: Account.name
13
+ #
14
+ # # good
15
+ # has_many :accounts, class_name: 'Account'
16
+ class ReflectionClassName < Cop
17
+ MSG = 'Use a string value for `class_name`.'
18
+
19
+ def_node_matcher :association_with_reflection, <<~PATTERN
20
+ (send nil? {:has_many :has_one :belongs_to} _
21
+ (hash <$#reflection_class_name ...>)
22
+ )
23
+ PATTERN
24
+
25
+ def_node_matcher :reflection_class_name, <<~PATTERN
26
+ (pair (sym :class_name) [!dstr !str !sym])
27
+ PATTERN
28
+
29
+ def on_send(node)
30
+ association_with_reflection(node) do |reflection_class_name|
31
+ add_offense(node, location: reflection_class_name.loc.expression)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ #
7
+ # Use `assert_not` methods instead of `refute` methods.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # refute false
12
+ # refute_empty [1, 2, 3]
13
+ # refute_equal true, false
14
+ #
15
+ # # good
16
+ # assert_not false
17
+ # assert_not_empty [1, 2, 3]
18
+ # assert_not_equal true, false
19
+ #
20
+ class RefuteMethods < Cop
21
+ MSG = 'Prefer `%<assert_method>s` over `%<refute_method>s`.'
22
+
23
+ CORRECTIONS = {
24
+ refute: 'assert_not',
25
+ refute_empty: 'assert_not_empty',
26
+ refute_equal: 'assert_not_equal',
27
+ refute_in_delta: 'assert_not_in_delta',
28
+ refute_in_epsilon: 'assert_not_in_epsilon',
29
+ refute_includes: 'assert_not_includes',
30
+ refute_instance_of: 'assert_not_instance_of',
31
+ refute_kind_of: 'assert_not_kind_of',
32
+ refute_nil: 'assert_not_nil',
33
+ refute_operator: 'assert_not_operator',
34
+ refute_predicate: 'assert_not_predicate',
35
+ refute_respond_to: 'assert_not_respond_to',
36
+ refute_same: 'assert_not_same',
37
+ refute_match: 'assert_no_match'
38
+ }.freeze
39
+
40
+ OFFENSIVE_METHODS = CORRECTIONS.keys.freeze
41
+
42
+ def_node_matcher :offensive?, '(send nil? #refute_method? ...)'
43
+
44
+ def on_send(node)
45
+ return unless offensive?(node)
46
+
47
+ message = offense_message(node.method_name)
48
+ add_offense(node, location: :selector, message: message)
49
+ end
50
+
51
+ def autocorrect(node)
52
+ lambda do |corrector|
53
+ corrector.replace(
54
+ node.loc.selector,
55
+ CORRECTIONS[node.method_name]
56
+ )
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def refute_method?(method_name)
63
+ OFFENSIVE_METHODS.include?(method_name)
64
+ end
65
+
66
+ def offense_message(method_name)
67
+ format(
68
+ MSG,
69
+ refute_method: method_name,
70
+ assert_method: CORRECTIONS[method_name]
71
+ )
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks whether constant value isn't relative date.
7
+ # Because the relative date will be evaluated only once.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # class SomeClass
12
+ # EXPIRED_AT = 1.week.since
13
+ # end
14
+ #
15
+ # # good
16
+ # class SomeClass
17
+ # EXPIRES = 1.week
18
+ #
19
+ # def self.expired_at
20
+ # EXPIRES.since
21
+ # end
22
+ # end
23
+ #
24
+ # # good
25
+ # class SomeClass
26
+ # def self.expired_at
27
+ # 1.week.since
28
+ # end
29
+ # end
30
+ class RelativeDateConstant < Cop
31
+ include RangeHelp
32
+
33
+ MSG = 'Do not assign %<method_name>s to constants as it ' \
34
+ 'will be evaluated only once.'
35
+
36
+ def on_casgn(node)
37
+ relative_date_assignment?(node) do |method_name|
38
+ add_offense(node, message: format(MSG, method_name: method_name))
39
+ end
40
+ end
41
+
42
+ def on_masgn(node)
43
+ lhs, rhs = *node
44
+
45
+ return unless rhs&.array_type?
46
+
47
+ lhs.children.zip(rhs.children).each do |(name, value)|
48
+ next unless name.casgn_type?
49
+
50
+ relative_date?(value) do |method_name|
51
+ add_offense(node,
52
+ location: range_between(name.loc.expression.begin_pos,
53
+ value.loc.expression.end_pos),
54
+ message: format(MSG, method_name: method_name))
55
+ end
56
+ end
57
+ end
58
+
59
+ def on_or_asgn(node)
60
+ relative_date_or_assignment?(node) do |method_name|
61
+ add_offense(node, message: format(MSG, method_name: method_name))
62
+ end
63
+ end
64
+
65
+ def autocorrect(node)
66
+ return unless node.casgn_type?
67
+
68
+ scope, const_name, value = *node
69
+ return unless scope.nil?
70
+
71
+ indent = ' ' * node.loc.column
72
+ new_code = ["def self.#{const_name.downcase}",
73
+ "#{indent}#{value.source}",
74
+ 'end'].join("\n#{indent}")
75
+ ->(corrector) { corrector.replace(node.source_range, new_code) }
76
+ end
77
+
78
+ private
79
+
80
+ def_node_matcher :relative_date_assignment?, <<~PATTERN
81
+ {
82
+ (casgn _ _ (send _ ${:since :from_now :after :ago :until :before}))
83
+ (casgn _ _ ({erange irange} _ (send _ ${:since :from_now :after :ago :until :before})))
84
+ (casgn _ _ ({erange irange} (send _ ${:since :from_now :after :ago :until :before}) _))
85
+ }
86
+ PATTERN
87
+
88
+ def_node_matcher :relative_date_or_assignment?, <<~PATTERN
89
+ (:or_asgn (casgn _ _) (send _ ${:since :from_now :after :ago :until :before}))
90
+ PATTERN
91
+
92
+ def_node_matcher :relative_date?, <<~PATTERN
93
+ {
94
+ ({erange irange} _ (send _ ${:since :from_now :after :ago :until :before}))
95
+ ({erange irange} (send _ ${:since :from_now :after :ago :until :before}) _)
96
+ (send _ ${:since :from_now :after :ago :until :before})
97
+ }
98
+ PATTERN
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for consistent uses of `request.referer` or
7
+ # `request.referrer`, depending on the cop's configuration.
8
+ #
9
+ # @example EnforcedStyle: referer (default)
10
+ # # bad
11
+ # request.referrer
12
+ #
13
+ # # good
14
+ # request.referer
15
+ #
16
+ # @example EnforcedStyle: referrer
17
+ # # bad
18
+ # request.referer
19
+ #
20
+ # # good
21
+ # request.referrer
22
+ class RequestReferer < Cop
23
+ include ConfigurableEnforcedStyle
24
+
25
+ MSG = 'Use `request.%<prefer>s` instead of ' \
26
+ '`request.%<current>s`.'
27
+
28
+ def_node_matcher :referer?, <<~PATTERN
29
+ (send (send nil? :request) {:referer :referrer})
30
+ PATTERN
31
+
32
+ def on_send(node)
33
+ referer?(node) do
34
+ return unless node.method?(wrong_method_name)
35
+
36
+ add_offense(node.source_range, location: node.source_range)
37
+ end
38
+ end
39
+
40
+ def autocorrect(node)
41
+ ->(corrector) { corrector.replace(node, "request.#{style}") }
42
+ end
43
+
44
+ private
45
+
46
+ def message(_node)
47
+ format(MSG, prefer: style, current: wrong_method_name)
48
+ end
49
+
50
+ def wrong_method_name
51
+ style == :referer ? :referrer : :referer
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,284 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks whether the change method of the migration file is
7
+ # reversible.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # def change
12
+ # change_table :users do |t|
13
+ # t.remove :name
14
+ # end
15
+ # end
16
+ #
17
+ # # good
18
+ # def change
19
+ # create_table :users do |t|
20
+ # t.string :name
21
+ # end
22
+ # end
23
+ #
24
+ # # good
25
+ # def change
26
+ # reversible do |dir|
27
+ # change_table :users do |t|
28
+ # dir.up do
29
+ # t.column :name, :string
30
+ # end
31
+ #
32
+ # dir.down do
33
+ # t.remove :name
34
+ # end
35
+ # end
36
+ # end
37
+ # end
38
+ #
39
+ # @example
40
+ # # drop_table
41
+ #
42
+ # # bad
43
+ # def change
44
+ # drop_table :users
45
+ # end
46
+ #
47
+ # # good
48
+ # def change
49
+ # drop_table :users do |t|
50
+ # t.string :name
51
+ # end
52
+ # end
53
+ #
54
+ # @example
55
+ # # change_column_default
56
+ #
57
+ # # bad
58
+ # def change
59
+ # change_column_default(:suppliers, :qualification, 'new')
60
+ # end
61
+ #
62
+ # # good
63
+ # def change
64
+ # change_column_default(:posts, :state, from: nil, to: "draft")
65
+ # end
66
+ #
67
+ # @example
68
+ # # remove_column
69
+ #
70
+ # # bad
71
+ # def change
72
+ # remove_column(:suppliers, :qualification)
73
+ # end
74
+ #
75
+ # # good
76
+ # def change
77
+ # remove_column(:suppliers, :qualification, :string)
78
+ # end
79
+ #
80
+ # @example
81
+ # # remove_foreign_key
82
+ #
83
+ # # bad
84
+ # def change
85
+ # remove_foreign_key :accounts, column: :owner_id
86
+ # end
87
+ #
88
+ # # good
89
+ # def change
90
+ # remove_foreign_key :accounts, :branches
91
+ # end
92
+ #
93
+ # @example
94
+ # # change_table
95
+ #
96
+ # # bad
97
+ # def change
98
+ # change_table :users do |t|
99
+ # t.remove :name
100
+ # t.change_default :authorized, 1
101
+ # t.change :price, :string
102
+ # end
103
+ # end
104
+ #
105
+ # # good
106
+ # def change
107
+ # change_table :users do |t|
108
+ # t.string :name
109
+ # end
110
+ # end
111
+ #
112
+ # # good
113
+ # def change
114
+ # reversible do |dir|
115
+ # change_table :users do |t|
116
+ # dir.up do
117
+ # t.change :price, :string
118
+ # end
119
+ #
120
+ # dir.down do
121
+ # t.change :price, :integer
122
+ # end
123
+ # end
124
+ # end
125
+ # end
126
+ #
127
+ # @see https://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html
128
+ class ReversibleMigration < Cop
129
+ MSG = '%<action>s is not reversible.'
130
+
131
+ def_node_matcher :irreversible_schema_statement_call, <<~PATTERN
132
+ (send nil? ${:execute :remove_belongs_to} ...)
133
+ PATTERN
134
+
135
+ def_node_matcher :drop_table_call, <<~PATTERN
136
+ (send nil? :drop_table ...)
137
+ PATTERN
138
+
139
+ def_node_matcher :remove_column_call, <<~PATTERN
140
+ (send nil? :remove_column $...)
141
+ PATTERN
142
+
143
+ def_node_matcher :remove_foreign_key_call, <<~PATTERN
144
+ (send nil? :remove_foreign_key _ $_)
145
+ PATTERN
146
+
147
+ def_node_matcher :change_table_call, <<~PATTERN
148
+ (send nil? :change_table $_ ...)
149
+ PATTERN
150
+
151
+ def on_send(node)
152
+ return unless within_change_method?(node)
153
+ return if within_reversible_or_up_only_block?(node)
154
+
155
+ check_irreversible_schema_statement_node(node)
156
+ check_drop_table_node(node)
157
+ check_reversible_hash_node(node)
158
+ check_remove_column_node(node)
159
+ check_remove_foreign_key_node(node)
160
+ end
161
+
162
+ def on_block(node)
163
+ return unless within_change_method?(node)
164
+ return if within_reversible_or_up_only_block?(node)
165
+ return if node.body.nil?
166
+
167
+ check_change_table_node(node.send_node, node.body)
168
+ end
169
+
170
+ private
171
+
172
+ def check_irreversible_schema_statement_node(node)
173
+ irreversible_schema_statement_call(node) do |method_name|
174
+ add_offense(node, message: format(MSG, action: method_name))
175
+ end
176
+ end
177
+
178
+ def check_drop_table_node(node)
179
+ drop_table_call(node) do
180
+ unless node.parent.block_type?
181
+ add_offense(
182
+ node,
183
+ message: format(MSG, action: 'drop_table(without block)')
184
+ )
185
+ end
186
+ end
187
+ end
188
+
189
+ def check_reversible_hash_node(node)
190
+ return if reversible_change_table_call?(node)
191
+
192
+ add_offense(
193
+ node,
194
+ message: format(
195
+ MSG, action: "#{node.method_name}(without :from and :to)"
196
+ )
197
+ )
198
+ end
199
+
200
+ def check_remove_column_node(node)
201
+ remove_column_call(node) do |args|
202
+ if args.to_a.size < 3
203
+ add_offense(
204
+ node,
205
+ message: format(MSG, action: 'remove_column(without type)')
206
+ )
207
+ end
208
+ end
209
+ end
210
+
211
+ def check_remove_foreign_key_node(node)
212
+ remove_foreign_key_call(node) do |arg|
213
+ if arg.hash_type?
214
+ add_offense(
215
+ node,
216
+ message: format(MSG,
217
+ action: 'remove_foreign_key(without table)')
218
+ )
219
+ end
220
+ end
221
+ end
222
+
223
+ def check_change_table_node(node, block)
224
+ change_table_call(node) do |arg|
225
+ if block.send_type?
226
+ check_change_table_offense(arg, block)
227
+ else
228
+ block.each_child_node(:send) do |child_node|
229
+ check_change_table_offense(arg, child_node)
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ def check_change_table_offense(receiver, node)
236
+ method_name = node.method_name
237
+ return if receiver != node.receiver &&
238
+ reversible_change_table_call?(node)
239
+
240
+ add_offense(
241
+ node,
242
+ message: format(MSG, action: "change_table(with #{method_name})")
243
+ )
244
+ end
245
+
246
+ def reversible_change_table_call?(node)
247
+ case node.method_name
248
+ when :change, :remove
249
+ false
250
+ when :change_default, :change_column_default, :change_table_comment,
251
+ :change_column_comment
252
+ all_hash_key?(node.arguments.last, :from, :to)
253
+ else
254
+ true
255
+ end
256
+ end
257
+
258
+ def within_change_method?(node)
259
+ node.each_ancestor(:def).any? do |ancestor|
260
+ ancestor.method?(:change)
261
+ end
262
+ end
263
+
264
+ def within_reversible_or_up_only_block?(node)
265
+ node.each_ancestor(:block).any? do |ancestor|
266
+ ancestor.block_type? &&
267
+ ancestor.send_node.method?(:reversible) ||
268
+ ancestor.send_node.method?(:up_only)
269
+ end
270
+ end
271
+
272
+ def all_hash_key?(args, *keys)
273
+ return false unless args&.hash_type?
274
+
275
+ hash_keys = args.keys.map do |key|
276
+ key.children.first.to_sym
277
+ end
278
+
279
+ hash_keys & keys == keys
280
+ end
281
+ end
282
+ end
283
+ end
284
+ end