rubocop-rails 2.5.0 → 2.7.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 +2 -2
- data/config/default.yml +156 -9
- data/lib/rubocop/cop/mixin/active_record_helper.rb +22 -0
- data/lib/rubocop/cop/mixin/index_method.rb +8 -1
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +145 -0
- data/lib/rubocop/cop/rails/content_tag.rb +69 -0
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -3
- data/lib/rubocop/cop/rails/default_scope.rb +54 -0
- data/lib/rubocop/cop/rails/delegate.rb +2 -4
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +40 -15
- data/lib/rubocop/cop/rails/exit.rb +2 -2
- data/lib/rubocop/cop/rails/file_path.rb +2 -1
- data/lib/rubocop/cop/rails/find_by_id.rb +103 -0
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +1 -1
- data/lib/rubocop/cop/rails/http_status.rb +2 -0
- data/lib/rubocop/cop/rails/inquiry.rb +34 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +0 -4
- data/lib/rubocop/cop/rails/link_to_blank.rb +3 -3
- data/lib/rubocop/cop/rails/mailer_name.rb +80 -0
- data/lib/rubocop/cop/rails/match_route.rb +119 -0
- data/lib/rubocop/cop/rails/negate_include.rb +39 -0
- data/lib/rubocop/cop/rails/pick.rb +55 -0
- data/lib/rubocop/cop/rails/pluck.rb +59 -0
- data/lib/rubocop/cop/rails/pluck_id.rb +58 -0
- data/lib/rubocop/cop/rails/pluck_in_where.rb +36 -0
- data/lib/rubocop/cop/rails/presence.rb +2 -6
- data/lib/rubocop/cop/rails/rake_environment.rb +17 -0
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +80 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +0 -3
- data/lib/rubocop/cop/rails/render_inline.rb +40 -0
- data/lib/rubocop/cop/rails/render_plain_text.rb +76 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +1 -1
- data/lib/rubocop/cop/rails/save_bang.rb +6 -7
- data/lib/rubocop/cop/rails/short_i18n.rb +76 -0
- data/lib/rubocop/cop/rails/skips_model_validations.rb +46 -8
- data/lib/rubocop/cop/rails/time_zone.rb +1 -3
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +12 -12
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +28 -6
- data/lib/rubocop/cop/rails/unknown_env.rb +18 -6
- data/lib/rubocop/cop/rails/where_exists.rb +68 -0
- data/lib/rubocop/cop/rails_cops.rb +17 -0
- data/lib/rubocop/rails/schema_loader.rb +10 -10
- data/lib/rubocop/rails/schema_loader/schema.rb +48 -14
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +27 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cffb4c138d270540b5821e23b2c5450a1939d4b01a2720ce400a8bb510ff90c
|
4
|
+
data.tar.gz: e0702c6ae4a7a43a01034b32d64ad67bac55659d6a5ec471685bda3c6d2a43fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ce7f6953d24239b8ce71a2e759da5007eb94a752f34ccb8d738273f5f75c63f57664042964f9766652c16e076996bb294edafc7d687a923c60ae2f56328afd3
|
7
|
+
data.tar.gz: b810b20d35548b34813946fff138c204fe0ce4f64a68b2f09e63a69c102ad44cf476a543b8bcf87f9caf42d5312ec277e14d23650100df21fb27fb14cc246216
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -78,13 +78,13 @@ Rails/FindBy:
|
|
78
78
|
|
79
79
|
## Documentation
|
80
80
|
|
81
|
-
You can read a lot more about RuboCop Rails in its [official docs](https://docs.rubocop.org/
|
81
|
+
You can read a lot more about RuboCop Rails in its [official docs](https://docs.rubocop.org/rubocop-rails/).
|
82
82
|
|
83
83
|
## Compatibility
|
84
84
|
|
85
85
|
Rails cops support the following versions:
|
86
86
|
|
87
|
-
- Rails 4.
|
87
|
+
- Rails 4.2+
|
88
88
|
|
89
89
|
## Contributing
|
90
90
|
|
data/config/default.yml
CHANGED
@@ -37,6 +37,14 @@ Rails/ActiveRecordAliases:
|
|
37
37
|
VersionAdded: '0.53'
|
38
38
|
SafeAutoCorrect: false
|
39
39
|
|
40
|
+
Rails/ActiveRecordCallbacksOrder:
|
41
|
+
Description: 'Order callback declarations in the order in which they will be executed.'
|
42
|
+
StyleGuide: 'https://rails.rubystyle.guide/#callbacks-order'
|
43
|
+
Enabled: 'pending'
|
44
|
+
VersionAdded: '2.7'
|
45
|
+
Include:
|
46
|
+
- app/models/**/*.rb
|
47
|
+
|
40
48
|
Rails/ActiveRecordOverride:
|
41
49
|
Description: >-
|
42
50
|
Check for overriding Active Record methods instead of using
|
@@ -57,22 +65,30 @@ Rails/ActiveSupportAliases:
|
|
57
65
|
Rails/ApplicationController:
|
58
66
|
Description: 'Check that controllers subclass ApplicationController.'
|
59
67
|
Enabled: true
|
68
|
+
SafeAutoCorrect: false
|
60
69
|
VersionAdded: '2.4'
|
70
|
+
VersionChanged: '2.5'
|
61
71
|
|
62
72
|
Rails/ApplicationJob:
|
63
73
|
Description: 'Check that jobs subclass ApplicationJob.'
|
64
74
|
Enabled: true
|
75
|
+
SafeAutoCorrect: false
|
65
76
|
VersionAdded: '0.49'
|
77
|
+
VersionChanged: '2.5'
|
66
78
|
|
67
79
|
Rails/ApplicationMailer:
|
68
80
|
Description: 'Check that mailers subclass ApplicationMailer.'
|
69
81
|
Enabled: true
|
82
|
+
SafeAutoCorrect: false
|
70
83
|
VersionAdded: '2.4'
|
84
|
+
VersionChanged: '2.5'
|
71
85
|
|
72
86
|
Rails/ApplicationRecord:
|
73
87
|
Description: 'Check that models subclass ApplicationRecord.'
|
74
88
|
Enabled: true
|
89
|
+
SafeAutoCorrect: false
|
75
90
|
VersionAdded: '0.49'
|
91
|
+
VersionChanged: '2.5'
|
76
92
|
|
77
93
|
Rails/AssertNot:
|
78
94
|
Description: 'Use `assert_not` instead of `assert !`.'
|
@@ -84,7 +100,7 @@ Rails/AssertNot:
|
|
84
100
|
Rails/BelongsTo:
|
85
101
|
Description: >-
|
86
102
|
Use `optional: true` instead of `required: false` for
|
87
|
-
`belongs_to` relations
|
103
|
+
`belongs_to` relations.
|
88
104
|
Enabled: true
|
89
105
|
VersionAdded: '0.62'
|
90
106
|
|
@@ -111,6 +127,14 @@ Rails/BulkChangeTable:
|
|
111
127
|
Include:
|
112
128
|
- db/migrate/*.rb
|
113
129
|
|
130
|
+
Rails/ContentTag:
|
131
|
+
Description: 'Use `tag` instead of `content_tag`.'
|
132
|
+
Reference:
|
133
|
+
- 'https://github.com/rails/rails/issues/25195'
|
134
|
+
- 'https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag'
|
135
|
+
Enabled: true
|
136
|
+
VersionAdded: '2.6'
|
137
|
+
|
114
138
|
Rails/CreateTableWithTimestamps:
|
115
139
|
Description: >-
|
116
140
|
Checks the migration for which timestamps are not included
|
@@ -137,6 +161,12 @@ Rails/Date:
|
|
137
161
|
- strict
|
138
162
|
- flexible
|
139
163
|
|
164
|
+
Rails/DefaultScope:
|
165
|
+
Description: 'Avoid use of `default_scope`.'
|
166
|
+
StyleGuide: 'https://rails.rubystyle.guide#avoid-default-scope'
|
167
|
+
Enabled: false
|
168
|
+
VersionAdded: '2.7'
|
169
|
+
|
140
170
|
Rails/Delegate:
|
141
171
|
Description: 'Prefer delegate method for delegations.'
|
142
172
|
Enabled: true
|
@@ -157,8 +187,14 @@ Rails/DynamicFindBy:
|
|
157
187
|
StyleGuide: 'https://rails.rubystyle.guide#find_by'
|
158
188
|
Enabled: true
|
159
189
|
VersionAdded: '0.44'
|
190
|
+
VersionChanged: '2.6'
|
191
|
+
# The `Whitelist` has been deprecated, Please use `AllowedMethods` instead.
|
160
192
|
Whitelist:
|
161
193
|
- find_by_sql
|
194
|
+
AllowedMethods:
|
195
|
+
- find_by_sql
|
196
|
+
AllowedReceivers:
|
197
|
+
- Gem::Specification
|
162
198
|
|
163
199
|
Rails/EnumHash:
|
164
200
|
Description: 'Prefer hash syntax over array syntax when defining enums.'
|
@@ -176,7 +212,7 @@ Rails/EnumUniqueness:
|
|
176
212
|
- app/models/**/*.rb
|
177
213
|
|
178
214
|
Rails/EnvironmentComparison:
|
179
|
-
Description: "Favor `Rails.env.production?` over `Rails.env == 'production'
|
215
|
+
Description: "Favor `Rails.env.production?` over `Rails.env == 'production'`."
|
180
216
|
Enabled: true
|
181
217
|
VersionAdded: '0.52'
|
182
218
|
|
@@ -212,6 +248,14 @@ Rails/FindBy:
|
|
212
248
|
Include:
|
213
249
|
- app/models/**/*.rb
|
214
250
|
|
251
|
+
Rails/FindById:
|
252
|
+
Description: >-
|
253
|
+
Favor the use of `find` over `where.take!`, `find_by!`, and `find_by_id!` when you
|
254
|
+
need to retrieve a single record by primary key when you expect it to be found.
|
255
|
+
StyleGuide: 'https://rails.rubystyle.guide/#find'
|
256
|
+
Enabled: 'pending'
|
257
|
+
VersionAdded: '2.7'
|
258
|
+
|
215
259
|
Rails/FindEach:
|
216
260
|
Description: 'Prefer all.find_each over all.find.'
|
217
261
|
StyleGuide: 'https://rails.rubystyle.guide#find-each'
|
@@ -237,7 +281,7 @@ Rails/HasManyOrHasOneDependent:
|
|
237
281
|
- app/models/**/*.rb
|
238
282
|
|
239
283
|
Rails/HelperInstanceVariable:
|
240
|
-
Description: 'Do not use instance variables in helpers'
|
284
|
+
Description: 'Do not use instance variables in helpers.'
|
241
285
|
Enabled: true
|
242
286
|
VersionAdded: '2.0'
|
243
287
|
Include:
|
@@ -278,6 +322,12 @@ Rails/IndexWith:
|
|
278
322
|
Enabled: true
|
279
323
|
VersionAdded: '2.5'
|
280
324
|
|
325
|
+
Rails/Inquiry:
|
326
|
+
Description: "Prefer Ruby's comparison operators over Active Support's `Array#inquiry` and `String#inquiry`."
|
327
|
+
StyleGuide: 'https://rails.rubystyle.guide/#inquiry'
|
328
|
+
Enabled: 'pending'
|
329
|
+
VersionAdded: '2.7'
|
330
|
+
|
281
331
|
Rails/InverseOf:
|
282
332
|
Description: 'Checks for associations where the inverse cannot be determined automatically.'
|
283
333
|
Enabled: true
|
@@ -303,8 +353,33 @@ Rails/LinkToBlank:
|
|
303
353
|
Enabled: true
|
304
354
|
VersionAdded: '0.62'
|
305
355
|
|
356
|
+
Rails/MailerName:
|
357
|
+
Description: 'Mailer should end with `Mailer` suffix.'
|
358
|
+
StyleGuide: 'https://rails.rubystyle.guide/#mailer-name'
|
359
|
+
Enabled: 'pending'
|
360
|
+
VersionAdded: '2.7'
|
361
|
+
Include:
|
362
|
+
- app/mailers/**/*.rb
|
363
|
+
|
364
|
+
Rails/MatchRoute:
|
365
|
+
Description: >-
|
366
|
+
Don't use `match` to define any routes unless there is a need to map multiple request types
|
367
|
+
among [:get, :post, :patch, :put, :delete] to a single action using the `:via` option.
|
368
|
+
StyleGuide: 'https://rails.rubystyle.guide/#no-match-routes'
|
369
|
+
Enabled: 'pending'
|
370
|
+
VersionAdded: '2.7'
|
371
|
+
Include:
|
372
|
+
- config/routes.rb
|
373
|
+
- config/routes/**/*.rb
|
374
|
+
|
375
|
+
Rails/NegateInclude:
|
376
|
+
Description: 'Prefer `collection.exclude?(obj)` over `!collection.include?(obj)`.'
|
377
|
+
StyleGuide: 'https://rails.rubystyle.guide#exclude'
|
378
|
+
Enabled: 'pending'
|
379
|
+
VersionAdded: '2.7'
|
380
|
+
|
306
381
|
Rails/NotNullColumn:
|
307
|
-
Description: 'Do not add a NOT NULL column without a default value'
|
382
|
+
Description: 'Do not add a NOT NULL column without a default value.'
|
308
383
|
Enabled: true
|
309
384
|
VersionAdded: '0.43'
|
310
385
|
Include:
|
@@ -326,6 +401,32 @@ Rails/OutputSafety:
|
|
326
401
|
Enabled: true
|
327
402
|
VersionAdded: '0.41'
|
328
403
|
|
404
|
+
Rails/Pick:
|
405
|
+
Description: 'Prefer `pick` over `pluck(...).first`.'
|
406
|
+
StyleGuide: 'https://rails.rubystyle.guide#pick'
|
407
|
+
Enabled: true
|
408
|
+
Safe: false
|
409
|
+
VersionAdded: '2.6'
|
410
|
+
|
411
|
+
Rails/Pluck:
|
412
|
+
Description: 'Prefer `pluck` over `map { ... }`.'
|
413
|
+
StyleGuide: 'https://rails.rubystyle.guide#pluck'
|
414
|
+
Enabled: 'pending'
|
415
|
+
VersionAdded: '2.7'
|
416
|
+
|
417
|
+
Rails/PluckId:
|
418
|
+
Description: 'Use `ids` instead of `pluck(:id)` or `pluck(primary_key)`.'
|
419
|
+
StyleGuide: 'https://rails.rubystyle.guide/#ids'
|
420
|
+
Enabled: false
|
421
|
+
Safe: false
|
422
|
+
VersionAdded: '2.7'
|
423
|
+
|
424
|
+
Rails/PluckInWhere:
|
425
|
+
Description: 'Use `select` instead of `pluck` in `where` query methods.'
|
426
|
+
Enabled: 'pending'
|
427
|
+
Safe: false
|
428
|
+
VersionAdded: '2.7'
|
429
|
+
|
329
430
|
Rails/PluralizationGrammar:
|
330
431
|
Description: 'Checks for incorrect grammar when using methods like `3.day.ago`.'
|
331
432
|
Enabled: true
|
@@ -353,6 +454,7 @@ Rails/RakeEnvironment:
|
|
353
454
|
Enabled: true
|
354
455
|
Safe: false
|
355
456
|
VersionAdded: '2.4'
|
457
|
+
VersionChanged: '2.6'
|
356
458
|
Include:
|
357
459
|
- '**/Rakefile'
|
358
460
|
- '**/*.rake'
|
@@ -379,6 +481,11 @@ Rails/RedundantAllowNil:
|
|
379
481
|
Include:
|
380
482
|
- app/models/**/*.rb
|
381
483
|
|
484
|
+
Rails/RedundantForeignKey:
|
485
|
+
Description: 'Checks for associations where the `:foreign_key` option is redundant.'
|
486
|
+
Enabled: true
|
487
|
+
VersionAdded: '2.6'
|
488
|
+
|
382
489
|
Rails/RedundantReceiverInWithOptions:
|
383
490
|
Description: 'Checks for redundant receiver in `with_options`.'
|
384
491
|
Enabled: true
|
@@ -407,6 +514,20 @@ Rails/RelativeDateConstant:
|
|
407
514
|
VersionChanged: '0.59'
|
408
515
|
AutoCorrect: false
|
409
516
|
|
517
|
+
Rails/RenderInline:
|
518
|
+
Description: 'Prefer using a template over inline rendering.'
|
519
|
+
StyleGuide: 'https://rails.rubystyle.guide/#inline-rendering'
|
520
|
+
Enabled: 'pending'
|
521
|
+
VersionAdded: '2.7'
|
522
|
+
|
523
|
+
Rails/RenderPlainText:
|
524
|
+
Description: 'Prefer `render plain:` over `render text:`.'
|
525
|
+
StyleGuide: 'https://rails.rubystyle.guide/#plain-text-rendering'
|
526
|
+
Enabled: 'pending'
|
527
|
+
VersionAdded: '2.7'
|
528
|
+
# Convert only when `content_type` is explicitly set to `text/plain`.
|
529
|
+
ContentTypeCompatibility: true
|
530
|
+
|
410
531
|
Rails/RequestReferer:
|
411
532
|
Description: 'Use consistent syntax for request.referer.'
|
412
533
|
Enabled: true
|
@@ -426,7 +547,7 @@ Rails/ReversibleMigration:
|
|
426
547
|
- db/migrate/*.rb
|
427
548
|
|
428
549
|
Rails/SafeNavigation:
|
429
|
-
Description: "Use Ruby's safe navigation operator (`&.`) instead of `try
|
550
|
+
Description: "Use Ruby's safe navigation operator (`&.`) instead of `try!`."
|
430
551
|
Enabled: true
|
431
552
|
VersionAdded: '0.43'
|
432
553
|
# This will convert usages of `try` to use safe navigation as well as `try!`.
|
@@ -464,6 +585,16 @@ Rails/ScopeArgs:
|
|
464
585
|
Include:
|
465
586
|
- app/models/**/*.rb
|
466
587
|
|
588
|
+
Rails/ShortI18n:
|
589
|
+
Description: 'Use the short form of the I18n methods: `t` instead of `translate` and `l` instead of `localize`.'
|
590
|
+
StyleGuide: 'https://rails.rubystyle.guide/#short-i18n'
|
591
|
+
Enabled: 'pending'
|
592
|
+
VersionAdded: '2.7'
|
593
|
+
EnforcedStyle: conservative
|
594
|
+
SupportedStyles:
|
595
|
+
- conservative
|
596
|
+
- aggressive
|
597
|
+
|
467
598
|
Rails/SkipsModelValidations:
|
468
599
|
Description: >-
|
469
600
|
Use methods that skips model validations with caution.
|
@@ -471,20 +602,27 @@ Rails/SkipsModelValidations:
|
|
471
602
|
Reference: 'https://guides.rubyonrails.org/active_record_validations.html#skipping-validations'
|
472
603
|
Enabled: true
|
473
604
|
VersionAdded: '0.47'
|
474
|
-
VersionChanged: '
|
475
|
-
|
605
|
+
VersionChanged: '2.7'
|
606
|
+
ForbiddenMethods:
|
476
607
|
- decrement!
|
477
608
|
- decrement_counter
|
478
609
|
- increment!
|
479
610
|
- increment_counter
|
611
|
+
- insert
|
612
|
+
- insert!
|
613
|
+
- insert_all
|
614
|
+
- insert_all!
|
480
615
|
- toggle!
|
481
616
|
- touch
|
617
|
+
- touch_all
|
482
618
|
- update_all
|
483
619
|
- update_attribute
|
484
620
|
- update_column
|
485
621
|
- update_columns
|
486
622
|
- update_counters
|
487
|
-
|
623
|
+
- upsert
|
624
|
+
- upsert_all
|
625
|
+
AllowedMethods: []
|
488
626
|
|
489
627
|
Rails/TimeZone:
|
490
628
|
Description: 'Checks the correct usage of time zone aware methods.'
|
@@ -505,7 +643,7 @@ Rails/UniqBeforePluck:
|
|
505
643
|
Description: 'Prefer the use of uniq or distinct before pluck.'
|
506
644
|
Enabled: true
|
507
645
|
VersionAdded: '0.40'
|
508
|
-
VersionChanged: '
|
646
|
+
VersionChanged: '2.6'
|
509
647
|
EnforcedStyle: conservative
|
510
648
|
SupportedStyles:
|
511
649
|
- conservative
|
@@ -535,3 +673,12 @@ Rails/Validation:
|
|
535
673
|
VersionChanged: '0.41'
|
536
674
|
Include:
|
537
675
|
- app/models/**/*.rb
|
676
|
+
|
677
|
+
Rails/WhereExists:
|
678
|
+
Description: 'Prefer `exists?(...)` over `where(...).exists?`.'
|
679
|
+
Enabled: 'pending'
|
680
|
+
VersionAdded: '2.7'
|
681
|
+
|
682
|
+
# Accept `redirect_to(...) and return` and similar cases.
|
683
|
+
Style/AndOr:
|
684
|
+
EnforcedStyle: conditionals
|
@@ -6,6 +6,8 @@ module RuboCop
|
|
6
6
|
module ActiveRecordHelper
|
7
7
|
extend NodePattern::Macros
|
8
8
|
|
9
|
+
WHERE_METHODS = %i[where rewhere].freeze
|
10
|
+
|
9
11
|
def_node_search :find_set_table_name, <<~PATTERN
|
10
12
|
(send self :table_name= {str sym})
|
11
13
|
PATTERN
|
@@ -14,6 +16,21 @@ module RuboCop
|
|
14
16
|
(send nil? :belongs_to {str sym} ...)
|
15
17
|
PATTERN
|
16
18
|
|
19
|
+
def external_dependency_checksum
|
20
|
+
return @external_dependency_checksum if defined?(@external_dependency_checksum)
|
21
|
+
|
22
|
+
schema_path = RuboCop::Rails::SchemaLoader.db_schema_path
|
23
|
+
return nil if schema_path.nil?
|
24
|
+
|
25
|
+
schema_code = File.read(schema_path)
|
26
|
+
|
27
|
+
@external_dependency_checksum ||= Digest::SHA1.hexdigest(schema_code)
|
28
|
+
end
|
29
|
+
|
30
|
+
def schema
|
31
|
+
RuboCop::Rails::SchemaLoader.load(target_ruby_version)
|
32
|
+
end
|
33
|
+
|
17
34
|
def table_name(class_node)
|
18
35
|
table_name = find_set_table_name(class_node).to_a.last&.first_argument
|
19
36
|
return table_name.value.to_s if table_name
|
@@ -57,6 +74,11 @@ module RuboCop
|
|
57
74
|
break pair.value.value.to_s
|
58
75
|
end
|
59
76
|
end
|
77
|
+
|
78
|
+
def in_where?(node)
|
79
|
+
send_node = node.each_ancestor(:send).first
|
80
|
+
send_node && WHERE_METHODS.include?(send_node.method_name)
|
81
|
+
end
|
60
82
|
end
|
61
83
|
end
|
62
84
|
end
|
@@ -112,7 +112,14 @@ module RuboCop
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def self.from_map_to_h(node, match)
|
115
|
-
strip_trailing_chars =
|
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
|
+
|
116
123
|
new(match, node.children.first, 0, strip_trailing_chars)
|
117
124
|
end
|
118
125
|
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks that Active Record callbacks are declared
|
7
|
+
# in the order in which they will be executed.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# class Person < ApplicationRecord
|
12
|
+
# after_commit :after_commit_callback
|
13
|
+
# before_validation :before_validation_callback
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# class Person < ApplicationRecord
|
18
|
+
# before_validation :before_validation_callback
|
19
|
+
# after_commit :after_commit_callback
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
class ActiveRecordCallbacksOrder < Cop
|
23
|
+
MSG = '`%<current>s` is supposed to appear before `%<previous>s`.'
|
24
|
+
|
25
|
+
CALLBACKS_IN_ORDER = %i[
|
26
|
+
after_initialize
|
27
|
+
before_validation
|
28
|
+
after_validation
|
29
|
+
before_save
|
30
|
+
around_save
|
31
|
+
before_create
|
32
|
+
around_create
|
33
|
+
after_create
|
34
|
+
before_update
|
35
|
+
around_update
|
36
|
+
after_update
|
37
|
+
before_destroy
|
38
|
+
around_destroy
|
39
|
+
after_destroy
|
40
|
+
after_save
|
41
|
+
after_commit
|
42
|
+
after_rollback
|
43
|
+
after_find
|
44
|
+
after_touch
|
45
|
+
].freeze
|
46
|
+
|
47
|
+
CALLBACKS_ORDER_MAP = Hash[
|
48
|
+
CALLBACKS_IN_ORDER.map.with_index { |name, index| [name, index] }
|
49
|
+
].freeze
|
50
|
+
|
51
|
+
def on_class(class_node)
|
52
|
+
previous_index = -1
|
53
|
+
previous_callback = nil
|
54
|
+
|
55
|
+
defined_callbacks(class_node).each do |node|
|
56
|
+
callback = node.method_name
|
57
|
+
index = CALLBACKS_ORDER_MAP[callback]
|
58
|
+
|
59
|
+
if index < previous_index
|
60
|
+
message = format(MSG, current: callback,
|
61
|
+
previous: previous_callback)
|
62
|
+
add_offense(node, message: message)
|
63
|
+
end
|
64
|
+
previous_index = index
|
65
|
+
previous_callback = callback
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Autocorrect by swapping between two nodes autocorrecting them
|
70
|
+
def autocorrect(node)
|
71
|
+
previous = left_siblings_of(node).find do |sibling|
|
72
|
+
callback?(sibling)
|
73
|
+
end
|
74
|
+
|
75
|
+
current_range = source_range_with_comment(node)
|
76
|
+
previous_range = source_range_with_comment(previous)
|
77
|
+
|
78
|
+
lambda do |corrector|
|
79
|
+
corrector.insert_before(previous_range, current_range.source)
|
80
|
+
corrector.remove(current_range)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def defined_callbacks(class_node)
|
87
|
+
class_def = class_node.body
|
88
|
+
|
89
|
+
if class_def
|
90
|
+
class_def.each_child_node.select { |c| callback?(c) }
|
91
|
+
else
|
92
|
+
[]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def callback?(node)
|
97
|
+
node.send_type? && CALLBACKS_ORDER_MAP.key?(node.method_name)
|
98
|
+
end
|
99
|
+
|
100
|
+
def left_siblings_of(node)
|
101
|
+
siblings_of(node)[0, node.sibling_index]
|
102
|
+
end
|
103
|
+
|
104
|
+
def siblings_of(node)
|
105
|
+
node.parent.children
|
106
|
+
end
|
107
|
+
|
108
|
+
def source_range_with_comment(node)
|
109
|
+
begin_pos = begin_pos_with_comment(node)
|
110
|
+
end_pos = end_position_for(node)
|
111
|
+
|
112
|
+
Parser::Source::Range.new(buffer, begin_pos, end_pos)
|
113
|
+
end
|
114
|
+
|
115
|
+
def end_position_for(node)
|
116
|
+
end_line = buffer.line_for_position(node.loc.expression.end_pos)
|
117
|
+
buffer.line_range(end_line).end_pos
|
118
|
+
end
|
119
|
+
|
120
|
+
def begin_pos_with_comment(node)
|
121
|
+
annotation_line = node.first_line - 1
|
122
|
+
first_comment = nil
|
123
|
+
|
124
|
+
processed_source.comments_before_line(annotation_line)
|
125
|
+
.reverse_each do |comment|
|
126
|
+
if comment.location.line == annotation_line
|
127
|
+
first_comment = comment
|
128
|
+
annotation_line -= 1
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
start_line_position(first_comment || node)
|
133
|
+
end
|
134
|
+
|
135
|
+
def start_line_position(node)
|
136
|
+
buffer.line_range(node.loc.line).begin_pos - 1
|
137
|
+
end
|
138
|
+
|
139
|
+
def buffer
|
140
|
+
processed_source.buffer
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|