rubocop-rails 2.5.0 → 2.7.1

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