rubocop-rails 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
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