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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +156 -9
  5. data/lib/rubocop/cop/mixin/active_record_helper.rb +22 -0
  6. data/lib/rubocop/cop/mixin/index_method.rb +8 -1
  7. data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +145 -0
  8. data/lib/rubocop/cop/rails/content_tag.rb +69 -0
  9. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -3
  10. data/lib/rubocop/cop/rails/default_scope.rb +54 -0
  11. data/lib/rubocop/cop/rails/delegate.rb +2 -4
  12. data/lib/rubocop/cop/rails/dynamic_find_by.rb +40 -15
  13. data/lib/rubocop/cop/rails/exit.rb +2 -2
  14. data/lib/rubocop/cop/rails/file_path.rb +2 -1
  15. data/lib/rubocop/cop/rails/find_by_id.rb +103 -0
  16. data/lib/rubocop/cop/rails/http_positional_arguments.rb +1 -1
  17. data/lib/rubocop/cop/rails/http_status.rb +2 -0
  18. data/lib/rubocop/cop/rails/inquiry.rb +34 -0
  19. data/lib/rubocop/cop/rails/inverse_of.rb +0 -4
  20. data/lib/rubocop/cop/rails/link_to_blank.rb +3 -3
  21. data/lib/rubocop/cop/rails/mailer_name.rb +80 -0
  22. data/lib/rubocop/cop/rails/match_route.rb +119 -0
  23. data/lib/rubocop/cop/rails/negate_include.rb +39 -0
  24. data/lib/rubocop/cop/rails/pick.rb +55 -0
  25. data/lib/rubocop/cop/rails/pluck.rb +59 -0
  26. data/lib/rubocop/cop/rails/pluck_id.rb +58 -0
  27. data/lib/rubocop/cop/rails/pluck_in_where.rb +36 -0
  28. data/lib/rubocop/cop/rails/presence.rb +2 -6
  29. data/lib/rubocop/cop/rails/rake_environment.rb +17 -0
  30. data/lib/rubocop/cop/rails/redundant_foreign_key.rb +80 -0
  31. data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +0 -3
  32. data/lib/rubocop/cop/rails/render_inline.rb +40 -0
  33. data/lib/rubocop/cop/rails/render_plain_text.rb +76 -0
  34. data/lib/rubocop/cop/rails/safe_navigation.rb +1 -1
  35. data/lib/rubocop/cop/rails/save_bang.rb +6 -7
  36. data/lib/rubocop/cop/rails/short_i18n.rb +76 -0
  37. data/lib/rubocop/cop/rails/skips_model_validations.rb +46 -8
  38. data/lib/rubocop/cop/rails/time_zone.rb +1 -3
  39. data/lib/rubocop/cop/rails/uniq_before_pluck.rb +12 -12
  40. data/lib/rubocop/cop/rails/unique_validation_without_index.rb +28 -6
  41. data/lib/rubocop/cop/rails/unknown_env.rb +18 -6
  42. data/lib/rubocop/cop/rails/where_exists.rb +68 -0
  43. data/lib/rubocop/cop/rails_cops.rb +17 -0
  44. data/lib/rubocop/rails/schema_loader.rb +10 -10
  45. data/lib/rubocop/rails/schema_loader/schema.rb +48 -14
  46. data/lib/rubocop/rails/version.rb +1 -1
  47. metadata +27 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd5bcec81363b7952d67bc476f82e25fbe0e0a8e4eb62e490712ddb5cfb58807
4
- data.tar.gz: 0eeec3b789aed712f41d0f842620ec0fd871dc884fe791819256b5777a0edb9d
3
+ metadata.gz: 8cffb4c138d270540b5821e23b2c5450a1939d4b01a2720ce400a8bb510ff90c
4
+ data.tar.gz: e0702c6ae4a7a43a01034b32d64ad67bac55659d6a5ec471685bda3c6d2a43fe
5
5
  SHA512:
6
- metadata.gz: 4fb1e35ca0fab64ed16959e29d2144b5cb9aafaf5064d04c67f1bee3cba9eaa4cc2c7c87b71dc240d10c1d11a8361ccacb3cb7002b9e14ef0a3908bfe271f79d
7
- data.tar.gz: 108de4501d9f50c50d7d3e23c93574a1a2eee389d5abef80b7e9982d3d7dba524dc263cf76986ae3caa1ab202fc4c7b20ce8106ac82dab5c43e7a578f4fe04ef
6
+ metadata.gz: 0ce7f6953d24239b8ce71a2e759da5007eb94a752f34ccb8d738273f5f75c63f57664042964f9766652c16e076996bb294edafc7d687a923c60ae2f56328afd3
7
+ data.tar.gz: b810b20d35548b34813946fff138c204fe0ce4f64a68b2f09e63a69c102ad44cf476a543b8bcf87f9caf42d5312ec277e14d23650100df21fb27fb14cc246216
@@ -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
@@ -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/projects/rails/).
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.0+
87
+ - Rails 4.2+
88
88
 
89
89
  ## Contributing
90
90
 
@@ -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: '0.60'
475
- Blacklist:
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
- Whitelist: []
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: '0.47'
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 = node.parent&.block_type? ? 0 : '.to_h'.length
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