rubocop-vibe 0.3.0 → 0.5.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e14c979862da1db7feb6dc28697248359d9320450a1bbfca24db50101d9f4c43
4
- data.tar.gz: e13a6d3fe34d9a24edc775dba8800e901a2a5a59ca5b3f35f516838e4ca79853
3
+ metadata.gz: f214588afca7a72328401c384b819415ddae3ac7e4fa2c214bfb7225f4253b56
4
+ data.tar.gz: d4a9267839bcd11320ca97f7ef64f17134618f7efee156b4f35b8f1a2a07ad1e
5
5
  SHA512:
6
- metadata.gz: 1d89ddcdf3d94bb0aeca2ce767157e305a75990b540f2bd91d1e9255e26e29915da0eb7b3188d293a96b0d4bf2ce4ac25c700f4601238705e82f7c62dbdaaeac
7
- data.tar.gz: 064b6dfe86613f8af93718a277db85d6bd705d7580fa27fe69469c41579aa0e4653c14dd4bcc48b56dd1d808c752921b2f69336816fe0ef3fcbb1098dd0d22f2
6
+ metadata.gz: bb99fe49c2b921d70e07a6d2c6c4ea035fa76867a3391803f586575a7631d19d176af76791778aedf33178bfa6cb8f9fa3aebe1a66950b14b88c7dfd3823b101
7
+ data.tar.gz: 4527a16179dbb9b2fa026639f47409a706ba393564b50cc0f6f5544df4b23c9faf6abfc720e319c1a88e92b7f40e2eed553eb720853345134d6d8f754791a9c9
data/config/default.yml CHANGED
@@ -62,6 +62,12 @@ Style/StringLiterals:
62
62
  Style/StringLiteralsInInterpolation:
63
63
  EnforcedStyle: double_quotes
64
64
 
65
+ Vibe/AttrOrder:
66
+ Description: "Enforces alphabetical ordering of attr_reader, attr_writer, and attr_accessor arguments."
67
+ Enabled: true
68
+ SafeAutoCorrect: true
69
+ VersionAdded: "0.5.0"
70
+
65
71
  Vibe/BlankLineAfterAssignment:
66
72
  Description: "Enforces a blank line after variable assignments when followed by other code."
67
73
  Enabled: true
@@ -98,12 +104,30 @@ Vibe/ConsecutiveIndexedAssignmentAlignment:
98
104
  SafeAutoCorrect: true
99
105
  VersionAdded: "0.3.0"
100
106
 
107
+ Vibe/ConsecutiveInstanceVariableAssignmentAlignment:
108
+ Description: "Enforces alignment of consecutive instance variable assignments at the = operator."
109
+ Enabled: true
110
+ SafeAutoCorrect: true
111
+ VersionAdded: "0.4.0"
112
+
101
113
  Vibe/ConsecutiveLetAlignment:
102
114
  Description: "Enforces alignment of consecutive let declarations at the { brace."
103
115
  Enabled: true
104
116
  SafeAutoCorrect: true
105
117
  VersionAdded: "0.2.0"
106
118
 
119
+ Vibe/ConsecutiveScopeAlignment:
120
+ Description: "Enforces alignment of consecutive scope declarations at the -> arrow."
121
+ Enabled: true
122
+ SafeAutoCorrect: true
123
+ VersionAdded: "0.5.0"
124
+
125
+ Vibe/ConstantAlphaOrder:
126
+ Description: "Enforces alphabetical ordering of consecutive constant declarations by name."
127
+ Enabled: true
128
+ SafeAutoCorrect: false
129
+ VersionAdded: "0.5.0"
130
+
107
131
  Vibe/DescribeBlockOrder:
108
132
  Description: "Enforces consistent ordering of describe blocks in RSpec files."
109
133
  Enabled: true
@@ -122,6 +146,18 @@ Vibe/IsExpectedOneLiner:
122
146
  SafeAutoCorrect: true
123
147
  VersionAdded: "0.1.0"
124
148
 
149
+ Vibe/KeywordArgumentOrder:
150
+ Description: "Enforces alphabetical ordering of keyword arguments and their YARD documentation."
151
+ Enabled: true
152
+ SafeAutoCorrect: true
153
+ VersionAdded: "0.5.0"
154
+
155
+ Vibe/LetOrder:
156
+ Description: "Enforces alphabetical ordering of consecutive let declarations."
157
+ Enabled: true
158
+ SafeAutoCorrect: true
159
+ VersionAdded: "0.4.0"
160
+
125
161
  Vibe/MultilineHashArgumentStyle:
126
162
  Description: "Enforces one-per-line and alphabetical ordering for hash arguments in multiline method calls."
127
163
  Enabled: true
@@ -184,3 +220,15 @@ Vibe/ServiceCallMethod:
184
220
  Description: "Service objects should define `self.call` and `call` methods."
185
221
  Enabled: true
186
222
  VersionAdded: "0.1.0"
223
+
224
+ Vibe/ValidateAfterValidates:
225
+ Description: "Enforces that validate calls appear after validates declarations in Rails models."
226
+ Enabled: true
227
+ SafeAutoCorrect: true
228
+ VersionAdded: "0.5.0"
229
+
230
+ Vibe/ValidatesAlphaOrder:
231
+ Description: "Enforces alphabetical ordering of consecutive validates declarations by attribute name."
232
+ Enabled: true
233
+ SafeAutoCorrect: true
234
+ VersionAdded: "0.5.0"
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Vibe
6
+ # Enforces alphabetical ordering of arguments to `attr_reader`,
7
+ # `attr_writer`, and `attr_accessor` declarations.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # attr_reader :id, :content, :timestamp, :raw
12
+ #
13
+ # # good
14
+ # attr_reader :content, :id, :raw, :timestamp
15
+ #
16
+ # # bad
17
+ # attr_accessor :zebra, :apple
18
+ #
19
+ # # good
20
+ # attr_accessor :apple, :zebra
21
+ class AttrOrder < Base
22
+ extend AutoCorrector
23
+
24
+ MSG = "Order `%<method>s` arguments alphabetically."
25
+
26
+ ATTR_METHODS = %i(attr_reader attr_writer attr_accessor).freeze
27
+
28
+ # Check attr_* method calls for alphabetical ordering.
29
+ #
30
+ # @param [RuboCop::AST::Node] node The send node.
31
+ # @return [void]
32
+ def on_send(node)
33
+ return unless attr_method?(node)
34
+ return unless node.arguments.size > 1
35
+ return if all_symbols?(node.arguments) && alphabetically_ordered?(node.arguments)
36
+
37
+ add_offense(node, message: format(MSG, method: node.method_name)) do |corrector|
38
+ autocorrect(corrector, node)
39
+ end
40
+ end
41
+ alias on_csend on_send
42
+
43
+ private
44
+
45
+ # Check if the node is an attr_* method call.
46
+ #
47
+ # @param [RuboCop::AST::Node] node The send node.
48
+ # @return [Boolean]
49
+ def attr_method?(node)
50
+ node.receiver.nil? && ATTR_METHODS.include?(node.method_name)
51
+ end
52
+
53
+ # Check if all arguments are symbols.
54
+ #
55
+ # @param [Array<RuboCop::AST::Node>] arguments The arguments.
56
+ # @return [Boolean]
57
+ def all_symbols?(arguments)
58
+ arguments.all?(&:sym_type?)
59
+ end
60
+
61
+ # Check if arguments are alphabetically ordered.
62
+ #
63
+ # @param [Array<RuboCop::AST::Node>] arguments The arguments.
64
+ # @return [Boolean]
65
+ def alphabetically_ordered?(arguments)
66
+ names = arguments.map { |arg| arg.value.to_s }
67
+
68
+ names == names.sort
69
+ end
70
+
71
+ # Auto-correct by reordering arguments alphabetically.
72
+ #
73
+ # @param [RuboCop::AST::Corrector] corrector The corrector.
74
+ # @param [RuboCop::AST::Node] node The send node.
75
+ # @return [void]
76
+ def autocorrect(corrector, node)
77
+ sorted_args = node.arguments.sort_by { |arg| arg.value.to_s }
78
+ sorted_source = sorted_args.map(&:source).join(", ")
79
+
80
+ args_range = node.first_argument.source_range.join(node.last_argument.source_range)
81
+
82
+ corrector.replace(args_range, sorted_source)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -53,6 +53,7 @@ module RuboCop
53
53
  end
54
54
  end
55
55
  alias on_numblock on_block
56
+ alias on_itblock on_block
56
57
 
57
58
  # Check method definitions for assignment statements.
58
59
  #
@@ -51,6 +51,7 @@ module RuboCop
51
51
  end
52
52
  end
53
53
  alias on_numblock on_block
54
+ alias on_itblock on_block
54
55
 
55
56
  private
56
57
 
@@ -36,20 +36,13 @@ module RuboCop
36
36
  class ClassOrganization < Base
37
37
  extend AutoCorrector
38
38
 
39
+ CLASS_MSG = "Class elements should be ordered: includes → constants → initialize → " \
40
+ "class methods → instance methods → protected → private."
39
41
  MODEL_MSG = "Model elements should be ordered: concerns → constants → associations → " \
40
42
  "validations → callbacks → scopes → class methods → instance methods → " \
41
43
  "protected → private."
42
- CLASS_MSG = "Class elements should be ordered: includes → constants → initialize → " \
43
- "class methods → instance methods → protected → private."
44
44
 
45
45
  ASSOCIATIONS = %i(belongs_to has_one has_many has_and_belongs_to_many).freeze
46
- VALIDATIONS = %i(
47
- validates validate validates_each validates_with
48
- validates_absence_of validates_acceptance_of validates_confirmation_of
49
- validates_exclusion_of validates_format_of validates_inclusion_of
50
- validates_length_of validates_numericality_of validates_presence_of
51
- validates_size_of validates_uniqueness_of validates_associated
52
- ).freeze
53
46
  CALLBACKS = %i(
54
47
  before_validation after_validation
55
48
  before_save after_save around_save
@@ -59,6 +52,15 @@ module RuboCop
59
52
  after_commit after_rollback
60
53
  after_initialize after_find after_touch
61
54
  ).freeze
55
+ CLASS_PRIORITIES = {
56
+ concerns: 10,
57
+ constants: 20,
58
+ initialize: 30,
59
+ class_methods: 40,
60
+ instance_methods: 50,
61
+ protected_methods: 60,
62
+ private_methods: 70
63
+ }.freeze
62
64
  MODEL_PRIORITIES = {
63
65
  concerns: 10,
64
66
  constants: 20,
@@ -71,15 +73,13 @@ module RuboCop
71
73
  protected_methods: 90,
72
74
  private_methods: 100
73
75
  }.freeze
74
- CLASS_PRIORITIES = {
75
- concerns: 10,
76
- constants: 20,
77
- initialize: 30,
78
- class_methods: 40,
79
- instance_methods: 50,
80
- protected_methods: 60,
81
- private_methods: 70
82
- }.freeze
76
+ VALIDATIONS = %i(
77
+ validates validate validates_each validates_with
78
+ validates_absence_of validates_acceptance_of validates_confirmation_of
79
+ validates_exclusion_of validates_format_of validates_inclusion_of
80
+ validates_length_of validates_numericality_of validates_presence_of
81
+ validates_size_of validates_uniqueness_of validates_associated
82
+ ).freeze
83
83
  VISIBILITY_CATEGORIES = {
84
84
  protected: :protected_methods,
85
85
  private: :private_methods,
@@ -40,6 +40,7 @@ module RuboCop
40
40
  end
41
41
  end
42
42
  alias on_numblock on_block
43
+ alias on_itblock on_block
43
44
 
44
45
  # Check method definitions for assignment alignment.
45
46
  #
@@ -37,6 +37,7 @@ module RuboCop
37
37
  end
38
38
  end
39
39
  alias on_numblock on_block
40
+ alias on_itblock on_block
40
41
 
41
42
  # Check method definitions for indexed assignment alignment.
42
43
  #
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Vibe
6
+ # Enforces alignment of consecutive instance variable assignments at the `=` operator.
7
+ #
8
+ # Consecutive assignments (with no blank lines between) should align their
9
+ # `=` operators for better readability. Groups are broken by blank lines.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # @user = create(:user)
14
+ # @character = create(:character)
15
+ # @input = "test"
16
+ #
17
+ # # good
18
+ # @user = create(:user)
19
+ # @character = create(:character)
20
+ # @input = "test"
21
+ #
22
+ # # good - blank line breaks the group
23
+ # @user = create(:user)
24
+ # @character = create(:character)
25
+ #
26
+ # @service = Users::Activate.new
27
+ # @activation = service.call
28
+ class ConsecutiveInstanceVariableAssignmentAlignment < Base
29
+ extend AutoCorrector
30
+ include AlignmentHelpers
31
+
32
+ MSG = "Align consecutive instance variable assignments at the = operator."
33
+
34
+ # Check block nodes for assignment alignment.
35
+ #
36
+ # @param [RuboCop::AST::Node] node The block node.
37
+ # @return [void]
38
+ def on_block(node)
39
+ if node.body
40
+ check_assignments_in_body(node.body)
41
+ end
42
+ end
43
+ alias on_numblock on_block
44
+ alias on_itblock on_block
45
+
46
+ # Check method definitions for assignment alignment.
47
+ #
48
+ # @param [RuboCop::AST::Node] node The def node.
49
+ # @return [void]
50
+ def on_def(node)
51
+ if node.body
52
+ check_assignments_in_body(node.body)
53
+ end
54
+ end
55
+ alias on_defs on_def
56
+
57
+ private
58
+
59
+ # Check assignments in a body node.
60
+ #
61
+ # @param [RuboCop::AST::Node] body The body node.
62
+ # @return [void]
63
+ def check_assignments_in_body(body)
64
+ statements = extract_statements(body)
65
+
66
+ return if statements.size < 2
67
+
68
+ groups = group_consecutive_statements(statements, &:ivasgn_type?)
69
+
70
+ groups.each { |group| check_group_alignment(group) }
71
+ end
72
+
73
+ # Check alignment for a group of assignments.
74
+ #
75
+ # @param [Array<RuboCop::AST::Node>] group The assignment group.
76
+ # @return [void]
77
+ def check_group_alignment(group)
78
+ columns = group.map { |asgn| asgn.loc.operator.column }
79
+ target_column = columns.max
80
+
81
+ group.each do |asgn|
82
+ current_column = asgn.loc.operator.column
83
+
84
+ next if current_column == target_column
85
+
86
+ add_offense(asgn.loc.name) do |corrector|
87
+ autocorrect_alignment(corrector, asgn, target_column)
88
+ end
89
+ end
90
+ end
91
+
92
+ # Auto-correct the alignment of an assignment.
93
+ #
94
+ # @param [RuboCop::AST::Corrector] corrector The corrector.
95
+ # @param [RuboCop::AST::Node] asgn The assignment node.
96
+ # @param [Integer] target_column The target column for alignment.
97
+ # @return [void]
98
+ def autocorrect_alignment(corrector, asgn, target_column)
99
+ variable_name_end = asgn.loc.name.end_pos
100
+ operator_start = asgn.loc.operator.begin_pos
101
+ total_spaces = calculate_total_spaces(asgn, target_column)
102
+
103
+ corrector.replace(
104
+ range_between(variable_name_end, operator_start),
105
+ " " * total_spaces
106
+ )
107
+ end
108
+
109
+ # Calculate total spaces needed for alignment.
110
+ #
111
+ # @param [RuboCop::AST::Node] asgn The assignment node.
112
+ # @param [Integer] target_column The target column for alignment.
113
+ # @return [Integer] The number of spaces (minimum 1).
114
+ def calculate_total_spaces(asgn, target_column)
115
+ current_column = asgn.loc.operator.column
116
+ current_spaces = asgn.loc.operator.begin_pos - asgn.loc.name.end_pos
117
+ spaces_needed = target_column - current_column
118
+
119
+ [1, current_spaces + spaces_needed].max
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -50,6 +50,7 @@ module RuboCop
50
50
  check_lets_in_body(node.body)
51
51
  end
52
52
  alias on_numblock on_block
53
+ alias on_itblock on_block
53
54
 
54
55
  private
55
56
 
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Vibe
6
+ # Enforces alignment of consecutive `scope` declarations at the `->` arrow.
7
+ #
8
+ # Consecutive `scope` declarations (with no blank lines between) should align
9
+ # their `->` arrows for better readability. Groups are broken by blank lines or
10
+ # non-scope statements.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # scope :between, ->(start, stop) { where(created_at: start..stop) }
15
+ # scope :for_website, ->(website_id) { where(website_id: website_id) }
16
+ #
17
+ # # good
18
+ # scope :between, ->(start, stop) { where(created_at: start..stop) }
19
+ # scope :for_website, ->(website_id) { where(website_id: website_id) }
20
+ #
21
+ # # good - blank line breaks the group
22
+ # scope :between, ->(start, stop) { where(created_at: start..stop) }
23
+ #
24
+ # scope :for_website, ->(website_id) { where(website_id: website_id) }
25
+ class ConsecutiveScopeAlignment < Base
26
+ extend AutoCorrector
27
+ include AlignmentHelpers
28
+
29
+ MSG = "Align consecutive scope declarations at the `->` arrow."
30
+
31
+ # Check class nodes for scope alignment.
32
+ #
33
+ # @param [RuboCop::AST::Node] node The class node.
34
+ # @return [void]
35
+ def on_class(node)
36
+ if node.body
37
+ check_scopes_in_body(node.body)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ # Check scope declarations in a body node.
44
+ #
45
+ # @param [RuboCop::AST::Node] body The body node.
46
+ # @return [void]
47
+ def check_scopes_in_body(body)
48
+ statements = extract_statements(body)
49
+
50
+ return if statements.size < 2
51
+
52
+ groups = group_consecutive_statements(statements) { |s| scope_declaration?(s) }
53
+
54
+ groups.each { |group| check_group_alignment(group) }
55
+ end
56
+
57
+ # Check if a node is a scope declaration with a lambda.
58
+ #
59
+ # @param [RuboCop::AST::Node] node The node to check.
60
+ # @return [Boolean]
61
+ def scope_declaration?(node)
62
+ return false unless node.send_type?
63
+ return false unless node.method?(:scope)
64
+ return false unless node.receiver.nil?
65
+ return false unless node.arguments[1]&.block_type?
66
+
67
+ node.arguments[1].method?(:lambda)
68
+ end
69
+
70
+ # Get the source range of the `->` arrow in a scope declaration.
71
+ #
72
+ # @param [RuboCop::AST::Node] scope_node The scope send node.
73
+ # @return [Parser::Source::Range]
74
+ def arrow_range(scope_node)
75
+ scope_node.arguments[1].send_node.loc.selector
76
+ end
77
+
78
+ # Check alignment for a group of scope declarations.
79
+ #
80
+ # @param [Array<RuboCop::AST::Node>] group The scope group.
81
+ # @return [void]
82
+ def check_group_alignment(group)
83
+ columns = group.map { |scope| arrow_range(scope).column }
84
+ target_column = columns.max
85
+
86
+ group.each do |scope|
87
+ current_column = arrow_range(scope).column
88
+
89
+ next if current_column == target_column
90
+
91
+ add_offense(scope.first_argument) do |corrector|
92
+ autocorrect_alignment(corrector, scope, target_column)
93
+ end
94
+ end
95
+ end
96
+
97
+ # Auto-correct the alignment of a scope declaration.
98
+ #
99
+ # @param [RuboCop::AST::Corrector] corrector The corrector.
100
+ # @param [RuboCop::AST::Node] scope_node The scope send node.
101
+ # @param [Integer] target_column The target column for alignment.
102
+ # @return [void]
103
+ def autocorrect_alignment(corrector, scope_node, target_column)
104
+ arrow = arrow_range(scope_node)
105
+ comma_end = scope_node.first_argument.source_range.end_pos + 1
106
+ arrow_start = arrow.begin_pos
107
+ total_spaces = calculate_total_spaces(scope_node, target_column)
108
+
109
+ corrector.replace(
110
+ range_between(comma_end, arrow_start),
111
+ " " * total_spaces
112
+ )
113
+ end
114
+
115
+ # Calculate total spaces needed for alignment.
116
+ #
117
+ # @param [RuboCop::AST::Node] scope_node The scope send node.
118
+ # @param [Integer] target_column The target column for alignment.
119
+ # @return [Integer] The number of spaces (minimum 1).
120
+ def calculate_total_spaces(scope_node, target_column)
121
+ arrow = arrow_range(scope_node)
122
+ comma_end = scope_node.first_argument.source_range.end_pos + 1
123
+ current_column = arrow.column
124
+ current_spaces = arrow.begin_pos - comma_end
125
+ spaces_needed = target_column - current_column
126
+
127
+ [1, current_spaces + spaces_needed].max
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Vibe
6
+ # Enforces alphabetical ordering of consecutive constant declarations by name.
7
+ #
8
+ # Consecutive constant declarations (with no blank lines between) should be
9
+ # alphabetically ordered by constant name for better readability and
10
+ # easier scanning. Groups are broken by blank lines or non-constant statements.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # class MyClass
15
+ # ZEBRA = 1
16
+ # ALPHA = 2
17
+ # end
18
+ #
19
+ # # good
20
+ # class MyClass
21
+ # ALPHA = 2
22
+ # ZEBRA = 1
23
+ # end
24
+ #
25
+ # # good - blank line breaks the group
26
+ # class MyClass
27
+ # ZEBRA = 1
28
+ #
29
+ # ALPHA = 2
30
+ # end
31
+ class ConstantAlphaOrder < Base
32
+ extend AutoCorrector
33
+ include AlignmentHelpers
34
+
35
+ MSG = "Order constants alphabetically by name."
36
+
37
+ # Check block nodes for constant ordering.
38
+ #
39
+ # @param [RuboCop::AST::Node] node The block node.
40
+ # @return [void]
41
+ def on_block(node)
42
+ if node.body
43
+ check_constants_in_body(node.body)
44
+ end
45
+ end
46
+ alias on_itblock on_block
47
+ alias on_numblock on_block
48
+
49
+ # Check class nodes for constant ordering.
50
+ #
51
+ # @param [RuboCop::AST::Node] node The class node.
52
+ # @return [void]
53
+ def on_class(node)
54
+ if node.body
55
+ check_constants_in_body(node.body)
56
+ end
57
+ end
58
+
59
+ # Check module nodes for constant ordering.
60
+ #
61
+ # @param [RuboCop::AST::Node] node The module node.
62
+ # @return [void]
63
+ def on_module(node)
64
+ if node.body
65
+ check_constants_in_body(node.body)
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # Check constant declarations in a body node.
72
+ #
73
+ # @param [RuboCop::AST::Node] body The body node.
74
+ # @return [void]
75
+ def check_constants_in_body(body)
76
+ statements = extract_statements(body)
77
+
78
+ return if statements.size < 2
79
+
80
+ groups = group_consecutive_statements(statements, &:casgn_type?)
81
+
82
+ groups.each { |group| check_group_order(group) }
83
+ end
84
+
85
+ # Check ordering for a group of constant declarations.
86
+ #
87
+ # @param [Array<RuboCop::AST::Node>] group The constants group.
88
+ # @return [void]
89
+ def check_group_order(group)
90
+ return if alphabetically_ordered?(group)
91
+
92
+ violations = find_ordering_violations(group)
93
+
94
+ violations.each do |constant|
95
+ add_offense(constant) do |corrector|
96
+ autocorrect(corrector, group)
97
+ end
98
+ end
99
+ end
100
+
101
+ # Check if constant declarations are alphabetically ordered.
102
+ #
103
+ # @param [Array<RuboCop::AST::Node>] group The constants group.
104
+ # @return [Boolean]
105
+ def alphabetically_ordered?(group)
106
+ names = group.map { |c| c.name.to_s }
107
+
108
+ names == names.sort
109
+ end
110
+
111
+ # Find constant declarations that violate ordering.
112
+ #
113
+ # @param [Array<RuboCop::AST::Node>] group The constants group.
114
+ # @return [Array<RuboCop::AST::Node>] Constants that violate ordering.
115
+ def find_ordering_violations(group)
116
+ violations = []
117
+
118
+ group.each_cons(2) do |current, following|
119
+ violations << following if current.name.to_s > following.name.to_s
120
+ end
121
+
122
+ violations.uniq
123
+ end
124
+
125
+ # Auto-correct by reordering constant declarations.
126
+ #
127
+ # @param [RuboCop::AST::Corrector] corrector The corrector.
128
+ # @param [Array<RuboCop::AST::Node>] group The constants group.
129
+ # @return [void]
130
+ def autocorrect(corrector, group)
131
+ sorted = group.sort_by { |c| c.name.to_s }
132
+
133
+ group.each_with_index do |constant, index|
134
+ sorted_constant = sorted[index]
135
+
136
+ next if constant == sorted_constant
137
+
138
+ corrector.replace(constant, sorted_constant.source)
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end