rubocop-rails 2.4.1 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +6 -2
  4. data/config/default.yml +71 -6
  5. data/lib/rubocop-rails.rb +3 -0
  6. data/lib/rubocop/cop/mixin/active_record_helper.rb +77 -0
  7. data/lib/rubocop/cop/mixin/index_method.rb +161 -0
  8. data/lib/rubocop/cop/rails/content_tag.rb +82 -0
  9. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -3
  10. data/lib/rubocop/cop/rails/delegate.rb +1 -3
  11. data/lib/rubocop/cop/rails/dynamic_find_by.rb +40 -15
  12. data/lib/rubocop/cop/rails/environment_comparison.rb +60 -14
  13. data/lib/rubocop/cop/rails/exit.rb +2 -2
  14. data/lib/rubocop/cop/rails/file_path.rb +1 -0
  15. data/lib/rubocop/cop/rails/http_positional_arguments.rb +2 -2
  16. data/lib/rubocop/cop/rails/http_status.rb +2 -0
  17. data/lib/rubocop/cop/rails/index_by.rb +56 -0
  18. data/lib/rubocop/cop/rails/index_with.rb +59 -0
  19. data/lib/rubocop/cop/rails/inverse_of.rb +0 -4
  20. data/lib/rubocop/cop/rails/link_to_blank.rb +1 -3
  21. data/lib/rubocop/cop/rails/pick.rb +51 -0
  22. data/lib/rubocop/cop/rails/presence.rb +2 -6
  23. data/lib/rubocop/cop/rails/rake_environment.rb +24 -6
  24. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +80 -0
  25. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +0 -3
  26. data/lib/rubocop/cop/rails/refute_methods.rb +52 -26
  27. data/lib/rubocop/cop/rails/reversible_migration.rb +6 -1
  28. data/lib/rubocop/cop/rails/save_bang.rb +16 -9
  29. data/lib/rubocop/cop/rails/skips_model_validations.rb +4 -1
  30. data/lib/rubocop/cop/rails/time_zone.rb +1 -3
  31. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +16 -16
  32. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +155 -0
  33. data/lib/rubocop/cop/rails/unknown_env.rb +7 -6
  34. data/lib/rubocop/cop/rails_cops.rb +8 -0
  35. data/lib/rubocop/rails/inject.rb +1 -1
  36. data/lib/rubocop/rails/schema_loader.rb +61 -0
  37. data/lib/rubocop/rails/schema_loader/schema.rb +190 -0
  38. data/lib/rubocop/rails/version.rb +1 -1
  39. metadata +32 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9d8cbc9fa9fc0752a334a92dd7af50f2a33331ac1b9604dc7c1f27efbc141e6
4
- data.tar.gz: 81833c9b0dbc71254f75f18eb59a13147025efdff95342c3ba1855e773301fc2
3
+ metadata.gz: b6b55ee8df864d6eaee4a491f4324343ee2274eafcb352952a81ff045832aa22
4
+ data.tar.gz: 344ea324a589dc25ce3e87366c4af33bde4e392acf6acaccf9b9296066680824
5
5
  SHA512:
6
- metadata.gz: a5ffc7a5e763076a13e5addebf2b4321ebfddbbb19e752bfe9697003eb4451e5d5eebc4ced8c2f3f55e1e10f24a81670e14b7ec48ee748d1c15996085618b5c8
7
- data.tar.gz: 0e8b3557bfa819b2fb1304f992a48845afbb52db0c8c7beb970bbcc09c4345c5f9d2f9f273efb2692374f3a03c28c6d615824af20ca669c40359299dca53f41a
6
+ metadata.gz: 5bdccc2a5e2a45533cb5bb33b2df26872f806dd590c13d640d7449315e56ec6250aebdb876fb37812bb3dd47cdaeda61c6049bc5cf98b01b473a616e9061491b
7
+ data.tar.gz: 6f8673f2e8d0068bcdbe1599936793461bab38f4b333a4ac6e22e03e7162851d6fc20543ec58e3f2fabdf404764b09f8ff47e6a5c23d492f0c8bb28acd012296
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-18 Bozhidar Batsov
1
+ Copyright (c) 2012-20 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -18,7 +18,7 @@ gem install rubocop-rails
18
18
  or if you use bundler put this in your `Gemfile`
19
19
 
20
20
  ```ruby
21
- gem 'rubocop-rails'
21
+ gem 'rubocop-rails', require: false
22
22
  ```
23
23
 
24
24
  ## Usage
@@ -76,11 +76,15 @@ Rails/FindBy:
76
76
  - lib/example.rb
77
77
  ```
78
78
 
79
+ ## Documentation
80
+
81
+ You can read a lot more about RuboCop Rails in its [official docs](https://docs.rubocop.org/rubocop-rails/).
82
+
79
83
  ## Compatibility
80
84
 
81
85
  Rails cops support the following versions:
82
86
 
83
- - Rails 4.0+
87
+ - Rails 4.2+
84
88
 
85
89
  ## Contributing
86
90
 
@@ -1,5 +1,9 @@
1
1
  # Common configuration.
2
2
 
3
+ inherit_mode:
4
+ merge:
5
+ - Exclude
6
+
3
7
  AllCops:
4
8
  Exclude:
5
9
  - bin/*
@@ -53,22 +57,30 @@ Rails/ActiveSupportAliases:
53
57
  Rails/ApplicationController:
54
58
  Description: 'Check that controllers subclass ApplicationController.'
55
59
  Enabled: true
60
+ SafeAutoCorrect: false
56
61
  VersionAdded: '2.4'
62
+ VersionChanged: '2.5'
57
63
 
58
64
  Rails/ApplicationJob:
59
65
  Description: 'Check that jobs subclass ApplicationJob.'
60
66
  Enabled: true
67
+ SafeAutoCorrect: false
61
68
  VersionAdded: '0.49'
69
+ VersionChanged: '2.5'
62
70
 
63
71
  Rails/ApplicationMailer:
64
72
  Description: 'Check that mailers subclass ApplicationMailer.'
65
73
  Enabled: true
74
+ SafeAutoCorrect: false
66
75
  VersionAdded: '2.4'
76
+ VersionChanged: '2.5'
67
77
 
68
78
  Rails/ApplicationRecord:
69
79
  Description: 'Check that models subclass ApplicationRecord.'
70
80
  Enabled: true
81
+ SafeAutoCorrect: false
71
82
  VersionAdded: '0.49'
83
+ VersionChanged: '2.5'
72
84
 
73
85
  Rails/AssertNot:
74
86
  Description: 'Use `assert_not` instead of `assert !`.'
@@ -80,7 +92,7 @@ Rails/AssertNot:
80
92
  Rails/BelongsTo:
81
93
  Description: >-
82
94
  Use `optional: true` instead of `required: false` for
83
- `belongs_to` relations'
95
+ `belongs_to` relations.
84
96
  Enabled: true
85
97
  VersionAdded: '0.62'
86
98
 
@@ -107,6 +119,14 @@ Rails/BulkChangeTable:
107
119
  Include:
108
120
  - db/migrate/*.rb
109
121
 
122
+ Rails/ContentTag:
123
+ Description: 'Use `tag` instead of `content_tag`.'
124
+ Reference:
125
+ - 'https://github.com/rails/rails/issues/25195'
126
+ - 'https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag'
127
+ Enabled: true
128
+ VersionAdded: '2.6'
129
+
110
130
  Rails/CreateTableWithTimestamps:
111
131
  Description: >-
112
132
  Checks the migration for which timestamps are not included
@@ -153,8 +173,14 @@ Rails/DynamicFindBy:
153
173
  StyleGuide: 'https://rails.rubystyle.guide#find_by'
154
174
  Enabled: true
155
175
  VersionAdded: '0.44'
176
+ VersionChanged: '2.6'
177
+ # The `Whitelist` has been deprecated, Please use `AllowedMethods` instead.
156
178
  Whitelist:
157
179
  - find_by_sql
180
+ AllowedMethods:
181
+ - find_by_sql
182
+ AllowedReceivers:
183
+ - Gem::Specification
158
184
 
159
185
  Rails/EnumHash:
160
186
  Description: 'Prefer hash syntax over array syntax when defining enums.'
@@ -172,7 +198,7 @@ Rails/EnumUniqueness:
172
198
  - app/models/**/*.rb
173
199
 
174
200
  Rails/EnvironmentComparison:
175
- Description: "Favor `Rails.env.production?` over `Rails.env == 'production'`"
201
+ Description: "Favor `Rails.env.production?` over `Rails.env == 'production'`."
176
202
  Enabled: true
177
203
  VersionAdded: '0.52'
178
204
 
@@ -233,7 +259,7 @@ Rails/HasManyOrHasOneDependent:
233
259
  - app/models/**/*.rb
234
260
 
235
261
  Rails/HelperInstanceVariable:
236
- Description: 'Do not use instance variables in helpers'
262
+ Description: 'Do not use instance variables in helpers.'
237
263
  Enabled: true
238
264
  VersionAdded: '2.0'
239
265
  Include:
@@ -264,6 +290,16 @@ Rails/IgnoredSkipActionFilterOption:
264
290
  Include:
265
291
  - app/controllers/**/*.rb
266
292
 
293
+ Rails/IndexBy:
294
+ Description: 'Prefer `index_by` over `each_with_object` or `map`.'
295
+ Enabled: true
296
+ VersionAdded: '2.5'
297
+
298
+ Rails/IndexWith:
299
+ Description: 'Prefer `index_with` over `each_with_object` or `map`.'
300
+ Enabled: true
301
+ VersionAdded: '2.5'
302
+
267
303
  Rails/InverseOf:
268
304
  Description: 'Checks for associations where the inverse cannot be determined automatically.'
269
305
  Enabled: true
@@ -290,7 +326,7 @@ Rails/LinkToBlank:
290
326
  VersionAdded: '0.62'
291
327
 
292
328
  Rails/NotNullColumn:
293
- Description: 'Do not add a NOT NULL column without a default value'
329
+ Description: 'Do not add a NOT NULL column without a default value.'
294
330
  Enabled: true
295
331
  VersionAdded: '0.43'
296
332
  Include:
@@ -312,6 +348,12 @@ Rails/OutputSafety:
312
348
  Enabled: true
313
349
  VersionAdded: '0.41'
314
350
 
351
+ Rails/Pick:
352
+ Description: 'Prefer `pick` over `pluck(...).first`.'
353
+ Enabled: true
354
+ Safe: false
355
+ VersionAdded: '2.6'
356
+
315
357
  Rails/PluralizationGrammar:
316
358
  Description: 'Checks for incorrect grammar when using methods like `3.day.ago`.'
317
359
  Enabled: true
@@ -339,9 +381,12 @@ Rails/RakeEnvironment:
339
381
  Enabled: true
340
382
  Safe: false
341
383
  VersionAdded: '2.4'
384
+ VersionChanged: '2.6'
342
385
  Include:
343
386
  - '**/Rakefile'
344
387
  - '**/*.rake'
388
+ Exclude:
389
+ - 'lib/capistrano/tasks/**/*.rake'
345
390
 
346
391
  Rails/ReadWriteAttribute:
347
392
  Description: >-
@@ -363,6 +408,11 @@ Rails/RedundantAllowNil:
363
408
  Include:
364
409
  - app/models/**/*.rb
365
410
 
411
+ Rails/RedundantForeignKey:
412
+ Description: 'Checks for associations where the `:foreign_key` option is redundant.'
413
+ Enabled: true
414
+ VersionAdded: '2.6'
415
+
366
416
  Rails/RedundantReceiverInWithOptions:
367
417
  Description: 'Checks for redundant receiver in `with_options`.'
368
418
  Enabled: true
@@ -377,6 +427,10 @@ Rails/RefuteMethods:
377
427
  Description: 'Use `assert_not` methods instead of `refute` methods.'
378
428
  Enabled: true
379
429
  VersionAdded: '0.56'
430
+ EnforcedStyle: assert_not
431
+ SupportedStyles:
432
+ - assert_not
433
+ - refute
380
434
  Include:
381
435
  - '**/test/**/*'
382
436
 
@@ -406,7 +460,7 @@ Rails/ReversibleMigration:
406
460
  - db/migrate/*.rb
407
461
 
408
462
  Rails/SafeNavigation:
409
- Description: "Use Ruby's safe navigation operator (`&.`) instead of `try!`"
463
+ Description: "Use Ruby's safe navigation operator (`&.`) instead of `try!`."
410
464
  Enabled: true
411
465
  VersionAdded: '0.43'
412
466
  # This will convert usages of `try` to use safe navigation as well as `try!`.
@@ -485,13 +539,20 @@ Rails/UniqBeforePluck:
485
539
  Description: 'Prefer the use of uniq or distinct before pluck.'
486
540
  Enabled: true
487
541
  VersionAdded: '0.40'
488
- VersionChanged: '0.47'
542
+ VersionChanged: '2.6'
489
543
  EnforcedStyle: conservative
490
544
  SupportedStyles:
491
545
  - conservative
492
546
  - aggressive
493
547
  AutoCorrect: false
494
548
 
549
+ Rails/UniqueValidationWithoutIndex:
550
+ Description: 'Uniqueness validation should be with a unique index.'
551
+ Enabled: true
552
+ VersionAdded: '2.5'
553
+ Include:
554
+ - app/models/**/*.rb
555
+
495
556
  Rails/UnknownEnv:
496
557
  Description: 'Use correct environment name.'
497
558
  Enabled: true
@@ -508,3 +569,7 @@ Rails/Validation:
508
569
  VersionChanged: '0.41'
509
570
  Include:
510
571
  - app/models/**/*.rb
572
+
573
+ # Accept `redirect_to(...) and return` and similar cases.
574
+ Style/AndOr:
575
+ EnforcedStyle: conditionals
@@ -2,10 +2,13 @@
2
2
 
3
3
  require 'rubocop'
4
4
  require 'rack/utils'
5
+ require 'active_support/inflector'
5
6
 
6
7
  require_relative 'rubocop/rails'
7
8
  require_relative 'rubocop/rails/version'
8
9
  require_relative 'rubocop/rails/inject'
10
+ require_relative 'rubocop/rails/schema_loader'
11
+ require_relative 'rubocop/rails/schema_loader/schema'
9
12
 
10
13
  RuboCop::Rails::Inject.defaults!
11
14
 
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # A mixin to extend cops for Active Record features
6
+ module ActiveRecordHelper
7
+ extend NodePattern::Macros
8
+
9
+ def_node_search :find_set_table_name, <<~PATTERN
10
+ (send self :table_name= {str sym})
11
+ PATTERN
12
+
13
+ def_node_search :find_belongs_to, <<~PATTERN
14
+ (send nil? :belongs_to {str sym} ...)
15
+ PATTERN
16
+
17
+ def external_dependency_checksum
18
+ return @external_dependency_checksum if defined?(@external_dependency_checksum)
19
+
20
+ schema_path = RuboCop::Rails::SchemaLoader.db_schema_path
21
+ return nil if schema_path.nil?
22
+
23
+ schema_code = File.read(schema_path)
24
+
25
+ @external_dependency_checksum ||= Digest::SHA1.hexdigest(schema_code)
26
+ end
27
+
28
+ def schema
29
+ RuboCop::Rails::SchemaLoader.load(target_ruby_version)
30
+ end
31
+
32
+ def table_name(class_node)
33
+ table_name = find_set_table_name(class_node).to_a.last&.first_argument
34
+ return table_name.value.to_s if table_name
35
+
36
+ namespaces = class_node.each_ancestor(:class, :module)
37
+ [class_node, *namespaces]
38
+ .reverse
39
+ .map { |klass| klass.identifier.children[1] }.join('_')
40
+ .tableize
41
+ end
42
+
43
+ # Resolve relation into column name.
44
+ # It just returns column_name if the column exists.
45
+ # Or it tries to resolve column_name as a relation.
46
+ # It returns `nil` if it can't resolve.
47
+ #
48
+ # @param name [String]
49
+ # @param class_node [RuboCop::AST::Node]
50
+ # @param table [RuboCop::Rails::SchemaLoader::Table]
51
+ # @return [String, nil]
52
+ def resolve_relation_into_column(name:, class_node:, table:)
53
+ return name if table.with_column?(name: name)
54
+
55
+ find_belongs_to(class_node) do |belongs_to|
56
+ next unless belongs_to.first_argument.value.to_s == name
57
+
58
+ fk = foreign_key_of(belongs_to) || "#{name}_id"
59
+ return fk if table.with_column?(name: fk)
60
+ end
61
+ nil
62
+ end
63
+
64
+ def foreign_key_of(belongs_to)
65
+ options = belongs_to.last_argument
66
+ return unless options.hash_type?
67
+
68
+ options.each_pair.find do |pair|
69
+ next unless pair.key.sym_type? && pair.key.value == :foreign_key
70
+ next unless pair.value.sym_type? || pair.value.str_type?
71
+
72
+ break pair.value.value.to_s
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for Rails/IndexBy and Rails/IndexWith
6
+ module IndexMethod # rubocop:disable Metrics/ModuleLength
7
+ def on_block(node)
8
+ on_bad_each_with_object(node) do |*match|
9
+ handle_possible_offense(node, match, 'each_with_object')
10
+ end
11
+ end
12
+
13
+ def on_send(node)
14
+ on_bad_map_to_h(node) do |*match|
15
+ handle_possible_offense(node, match, 'map { ... }.to_h')
16
+ end
17
+
18
+ on_bad_hash_brackets_map(node) do |*match|
19
+ handle_possible_offense(node, match, 'Hash[map { ... }]')
20
+ end
21
+ end
22
+
23
+ def on_csend(node)
24
+ on_bad_map_to_h(node) do |*match|
25
+ handle_possible_offense(node, match, 'map { ... }.to_h')
26
+ end
27
+ end
28
+
29
+ def autocorrect(node)
30
+ lambda do |corrector|
31
+ correction = prepare_correction(node)
32
+ execute_correction(corrector, node, correction)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # @abstract Implemented with `def_node_matcher`
39
+ def on_bad_each_with_object(_node)
40
+ raise NotImplementedError
41
+ end
42
+
43
+ # @abstract Implemented with `def_node_matcher`
44
+ def on_bad_map_to_h(_node)
45
+ raise NotImplementedError
46
+ end
47
+
48
+ # @abstract Implemented with `def_node_matcher`
49
+ def on_bad_hash_brackets_map(_node)
50
+ raise NotImplementedError
51
+ end
52
+
53
+ def handle_possible_offense(node, match, match_desc)
54
+ captures = extract_captures(match)
55
+
56
+ return if captures.noop_transformation?
57
+
58
+ add_offense(
59
+ node,
60
+ message: "Prefer `#{new_method_name}` over `#{match_desc}`."
61
+ )
62
+ end
63
+
64
+ def extract_captures(match)
65
+ argname, body_expr = *match
66
+ Captures.new(argname, body_expr)
67
+ end
68
+
69
+ def new_method_name
70
+ raise NotImplementedError
71
+ end
72
+
73
+ def prepare_correction(node)
74
+ if (match = on_bad_each_with_object(node))
75
+ Autocorrection.from_each_with_object(node, match)
76
+ elsif (match = on_bad_map_to_h(node))
77
+ Autocorrection.from_map_to_h(node, match)
78
+ elsif (match = on_bad_hash_brackets_map(node))
79
+ Autocorrection.from_hash_brackets_map(node, match)
80
+ else
81
+ raise 'unreachable'
82
+ end
83
+ end
84
+
85
+ def execute_correction(corrector, node, correction)
86
+ correction.strip_prefix_and_suffix(node, corrector)
87
+ correction.set_new_method_name(new_method_name, corrector)
88
+
89
+ captures = extract_captures(correction.match)
90
+ correction.set_new_arg_name(captures.transformed_argname, corrector)
91
+ correction.set_new_body_expression(
92
+ captures.transforming_body_expr,
93
+ corrector
94
+ )
95
+ end
96
+
97
+ # Internal helper class to hold match data
98
+ Captures = Struct.new(
99
+ :transformed_argname,
100
+ :transforming_body_expr
101
+ ) do
102
+ def noop_transformation?
103
+ transforming_body_expr.lvar_type? &&
104
+ transforming_body_expr.children == [transformed_argname]
105
+ end
106
+ end
107
+
108
+ # Internal helper class to hold autocorrect data
109
+ Autocorrection = Struct.new(:match, :block_node, :leading, :trailing) do # rubocop:disable Metrics/BlockLength
110
+ def self.from_each_with_object(node, match)
111
+ new(match, node, 0, 0)
112
+ end
113
+
114
+ def self.from_map_to_h(node, match)
115
+ strip_trailing_chars = 0
116
+
117
+ unless node.parent&.block_type?
118
+ map_range = node.children.first.source_range
119
+ node_range = node.source_range
120
+ strip_trailing_chars = node_range.end_pos - map_range.end_pos
121
+ end
122
+
123
+ new(match, node.children.first, 0, strip_trailing_chars)
124
+ end
125
+
126
+ def self.from_hash_brackets_map(node, match)
127
+ new(match, node.children.last, 'Hash['.length, ']'.length)
128
+ end
129
+
130
+ def strip_prefix_and_suffix(node, corrector)
131
+ expression = node.loc.expression
132
+ corrector.remove_leading(expression, leading)
133
+ corrector.remove_trailing(expression, trailing)
134
+ end
135
+
136
+ def set_new_method_name(new_method_name, corrector)
137
+ range = block_node.send_node.loc.selector
138
+ if (send_end = block_node.send_node.loc.end)
139
+ # If there are arguments (only true in the `each_with_object` case)
140
+ range = range.begin.join(send_end)
141
+ end
142
+ corrector.replace(range, new_method_name)
143
+ end
144
+
145
+ def set_new_arg_name(transformed_argname, corrector)
146
+ corrector.replace(
147
+ block_node.arguments.loc.expression,
148
+ "|#{transformed_argname}|"
149
+ )
150
+ end
151
+
152
+ def set_new_body_expression(transforming_body_expr, corrector)
153
+ corrector.replace(
154
+ block_node.body.loc.expression,
155
+ transforming_body_expr.loc.expression.source
156
+ )
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end