rubocop-vibe 0.1.0 → 0.2.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 +4 -4
- data/config/default.yml +25 -0
- data/lib/rubocop/cop/vibe/consecutive_constant_alignment.rb +229 -0
- data/lib/rubocop/cop/vibe/consecutive_let_alignment.rb +219 -0
- data/lib/rubocop/cop/vibe/describe_block_order.rb +1 -1
- data/lib/rubocop/cop/vibe/no_rubocop_disable.rb +63 -16
- data/lib/rubocop/cop/vibe/raise_unless_block.rb +101 -0
- data/lib/rubocop/cop/vibe/rspec_stub_chain_style.rb +256 -0
- data/lib/rubocop/cop/vibe_cops.rb +4 -0
- data/lib/rubocop/vibe/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e91f63c7c86e77f854c0dd424a1d40d1fd5ad8f0407836dc74467d59e786e6ab
|
|
4
|
+
data.tar.gz: d4cdc851197a8a1fcbbdebe14551c369029469f6c2e6c4a09b50060fb40b39f8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eaebb568a44bd514577f6e3dc63a3b4a05d6c78762b5520462a0ba55119549bece813953d35cd2956fb03830a703d7a90e23d6b9c25204a3f41ee40281d87852
|
|
7
|
+
data.tar.gz: 0d66492bf54277b78dfa228bfdd7ef3cb2ba46f715ef32dc5304371d6af7ab9eff2e7c1a1ff3ca0fcd1ca4abd82ae8d01e41be9154fdb66a15b1546c504fb2cc
|
data/config/default.yml
CHANGED
|
@@ -74,6 +74,18 @@ Vibe/ConsecutiveAssignmentAlignment:
|
|
|
74
74
|
SafeAutoCorrect: true
|
|
75
75
|
VersionAdded: "0.1.0"
|
|
76
76
|
|
|
77
|
+
Vibe/ConsecutiveConstantAlignment:
|
|
78
|
+
Description: "Enforces alignment of consecutive constant assignments at the = operator."
|
|
79
|
+
Enabled: true
|
|
80
|
+
SafeAutoCorrect: true
|
|
81
|
+
VersionAdded: "0.2.0"
|
|
82
|
+
|
|
83
|
+
Vibe/ConsecutiveLetAlignment:
|
|
84
|
+
Description: "Enforces alignment of consecutive let declarations at the { brace."
|
|
85
|
+
Enabled: true
|
|
86
|
+
SafeAutoCorrect: true
|
|
87
|
+
VersionAdded: "0.2.0"
|
|
88
|
+
|
|
77
89
|
Vibe/DescribeBlockOrder:
|
|
78
90
|
Description: "Enforces consistent ordering of describe blocks in RSpec files."
|
|
79
91
|
Enabled: true
|
|
@@ -101,6 +113,7 @@ Vibe/NoAssignsAttributeTesting:
|
|
|
101
113
|
Vibe/NoRubocopDisable:
|
|
102
114
|
Description: "Enforces that rubocop:disable comments are not used inline."
|
|
103
115
|
Enabled: true
|
|
116
|
+
AllowedCops: []
|
|
104
117
|
VersionAdded: "0.1.0"
|
|
105
118
|
|
|
106
119
|
Vibe/NoSkippedTests:
|
|
@@ -120,6 +133,18 @@ Vibe/PreferOneLinerExpectation:
|
|
|
120
133
|
SafeAutoCorrect: true
|
|
121
134
|
VersionAdded: "0.1.0"
|
|
122
135
|
|
|
136
|
+
Vibe/RaiseUnlessBlock:
|
|
137
|
+
Description: "Enforces using if/end blocks instead of inline modifiers for raise statements."
|
|
138
|
+
Enabled: true
|
|
139
|
+
SafeAutoCorrect: true
|
|
140
|
+
VersionAdded: "0.2.0"
|
|
141
|
+
|
|
142
|
+
Vibe/RspecStubChainStyle:
|
|
143
|
+
Description: "Enforces that RSpec stub chains are split across lines when too long."
|
|
144
|
+
Enabled: true
|
|
145
|
+
SafeAutoCorrect: true
|
|
146
|
+
VersionAdded: "0.2.0"
|
|
147
|
+
|
|
123
148
|
Vibe/ServiceCallMethod:
|
|
124
149
|
Description: "Service objects should define `self.call` and `call` methods."
|
|
125
150
|
Enabled: true
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Vibe
|
|
6
|
+
# Enforces alignment of consecutive constant assignments at the `=` operator.
|
|
7
|
+
#
|
|
8
|
+
# Consecutive constant assignments (with no blank lines between) should align their
|
|
9
|
+
# `=` operators for better readability. Groups are broken by blank lines or non-constant
|
|
10
|
+
# statements.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # bad
|
|
14
|
+
# MINIMUM_NAME_LENGTH = 3
|
|
15
|
+
# MAXIMUM_NAME_LENGTH = 12
|
|
16
|
+
# ACTIVE_DURATION = 15.minutes
|
|
17
|
+
#
|
|
18
|
+
# # good
|
|
19
|
+
# MINIMUM_NAME_LENGTH = 3
|
|
20
|
+
# MAXIMUM_NAME_LENGTH = 12
|
|
21
|
+
# ACTIVE_DURATION = 15.minutes
|
|
22
|
+
#
|
|
23
|
+
# # good - blank line breaks the group
|
|
24
|
+
# THROTTLE_LIMIT = 10
|
|
25
|
+
# THROTTLE_PERIOD = 5
|
|
26
|
+
#
|
|
27
|
+
# DEFAULT_VALUE = 0 # Separate group, not aligned
|
|
28
|
+
class ConsecutiveConstantAlignment < Base
|
|
29
|
+
extend AutoCorrector
|
|
30
|
+
|
|
31
|
+
MSG = "Align consecutive constant assignments at the `=` operator."
|
|
32
|
+
|
|
33
|
+
# Check class nodes for constant alignment.
|
|
34
|
+
#
|
|
35
|
+
# @param [RuboCop::AST::Node] node The class node.
|
|
36
|
+
# @return [void]
|
|
37
|
+
def on_class(node)
|
|
38
|
+
if node.body
|
|
39
|
+
check_constants_in_body(node.body)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Check module nodes for constant alignment.
|
|
44
|
+
#
|
|
45
|
+
# @param [RuboCop::AST::Node] node The module node.
|
|
46
|
+
# @return [void]
|
|
47
|
+
def on_module(node)
|
|
48
|
+
if node.body
|
|
49
|
+
check_constants_in_body(node.body)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# Check constants in a body node.
|
|
56
|
+
#
|
|
57
|
+
# @param [RuboCop::AST::Node] body The body node.
|
|
58
|
+
# @return [void]
|
|
59
|
+
def check_constants_in_body(body)
|
|
60
|
+
statements = extract_statements(body)
|
|
61
|
+
return if statements.size < 2
|
|
62
|
+
|
|
63
|
+
groups = group_consecutive_constants(statements)
|
|
64
|
+
groups.each { |group| check_group_alignment(group) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Extract statements from a body node.
|
|
68
|
+
#
|
|
69
|
+
# @param [RuboCop::AST::Node] body The body node.
|
|
70
|
+
# @return [Array<RuboCop::AST::Node>]
|
|
71
|
+
def extract_statements(body)
|
|
72
|
+
if body.begin_type?
|
|
73
|
+
body.children
|
|
74
|
+
else
|
|
75
|
+
[body]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Group consecutive constant assignments together.
|
|
80
|
+
#
|
|
81
|
+
# @param [Array<RuboCop::AST::Node>] statements The statements.
|
|
82
|
+
# @return [Array<Array<RuboCop::AST::Node>>] Groups of consecutive constants.
|
|
83
|
+
def group_consecutive_constants(statements)
|
|
84
|
+
groups = []
|
|
85
|
+
current_group = []
|
|
86
|
+
previous_line = nil
|
|
87
|
+
|
|
88
|
+
statements.each do |statement|
|
|
89
|
+
current_group, previous_line = process_statement(statement, current_group, previous_line, groups)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
finalize_groups(groups, current_group)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Process a single statement for grouping.
|
|
96
|
+
#
|
|
97
|
+
# @param [RuboCop::AST::Node] statement The statement.
|
|
98
|
+
# @param [Array<RuboCop::AST::Node>] current_group The current group.
|
|
99
|
+
# @param [Integer, nil] previous_line The previous line number.
|
|
100
|
+
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
101
|
+
# @return [Array] The updated current_group and previous_line.
|
|
102
|
+
def process_statement(statement, current_group, previous_line, groups)
|
|
103
|
+
if constant_assignment?(statement)
|
|
104
|
+
current_group = handle_constant(statement, current_group, previous_line, groups)
|
|
105
|
+
else
|
|
106
|
+
save_group_if_valid(groups, current_group)
|
|
107
|
+
current_group = []
|
|
108
|
+
end
|
|
109
|
+
[current_group, statement.loc.last_line]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Check if node is a single-line constant assignment.
|
|
113
|
+
#
|
|
114
|
+
# Only single-line constants are considered for alignment to avoid
|
|
115
|
+
# conflicts with multi-line hash/array constants and Layout/ExtraSpacing.
|
|
116
|
+
#
|
|
117
|
+
# @param [RuboCop::AST::Node] node The node.
|
|
118
|
+
# @return [Boolean]
|
|
119
|
+
def constant_assignment?(node)
|
|
120
|
+
node.casgn_type? && single_line?(node)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Check if node is on a single line.
|
|
124
|
+
#
|
|
125
|
+
# @param [RuboCop::AST::Node] node The node.
|
|
126
|
+
# @return [Boolean]
|
|
127
|
+
def single_line?(node)
|
|
128
|
+
node.single_line?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Handle a constant assignment.
|
|
132
|
+
#
|
|
133
|
+
# @param [RuboCop::AST::Node] statement The constant assignment.
|
|
134
|
+
# @param [Array<RuboCop::AST::Node>] current_group The current group.
|
|
135
|
+
# @param [Integer, nil] previous_line The previous line number.
|
|
136
|
+
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
137
|
+
# @return [Array<RuboCop::AST::Node>] The updated current group.
|
|
138
|
+
def handle_constant(statement, current_group, previous_line, groups)
|
|
139
|
+
if previous_line && statement.loc.line - previous_line > 1
|
|
140
|
+
save_group_if_valid(groups, current_group)
|
|
141
|
+
current_group = []
|
|
142
|
+
end
|
|
143
|
+
current_group << statement
|
|
144
|
+
current_group
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Save group if it has multiple constant assignments.
|
|
148
|
+
#
|
|
149
|
+
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
150
|
+
# @param [Array<RuboCop::AST::Node>] group The group to potentially save.
|
|
151
|
+
# @return [void]
|
|
152
|
+
def save_group_if_valid(groups, group)
|
|
153
|
+
groups << group if group.size > 1
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Finalize groups by adding any remaining valid group.
|
|
157
|
+
#
|
|
158
|
+
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
159
|
+
# @param [Array<RuboCop::AST::Node>] current_group The current group.
|
|
160
|
+
# @return [Array<Array<RuboCop::AST::Node>>] The finalized groups.
|
|
161
|
+
def finalize_groups(groups, current_group)
|
|
162
|
+
save_group_if_valid(groups, current_group)
|
|
163
|
+
groups
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Check alignment for a group of constant assignments.
|
|
167
|
+
#
|
|
168
|
+
# @param [Array<RuboCop::AST::Node>] group The constant group.
|
|
169
|
+
# @return [void]
|
|
170
|
+
def check_group_alignment(group)
|
|
171
|
+
columns = group.map { |const| const.loc.operator.column }
|
|
172
|
+
target_column = columns.max
|
|
173
|
+
|
|
174
|
+
group.each do |const|
|
|
175
|
+
current_column = const.loc.operator.column
|
|
176
|
+
next if current_column == target_column
|
|
177
|
+
|
|
178
|
+
add_offense(const.loc.name) do |corrector|
|
|
179
|
+
autocorrect_alignment(corrector, const, target_column)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Auto-correct the alignment of a constant assignment.
|
|
185
|
+
#
|
|
186
|
+
# @param [RuboCop::AST::Corrector] corrector The corrector.
|
|
187
|
+
# @param [RuboCop::AST::Node] const The constant assignment node.
|
|
188
|
+
# @param [Integer] target_column The target column for alignment.
|
|
189
|
+
# @return [void]
|
|
190
|
+
def autocorrect_alignment(corrector, const, target_column)
|
|
191
|
+
name_end = const.loc.name.end_pos
|
|
192
|
+
operator_start = const.loc.operator.begin_pos
|
|
193
|
+
total_spaces = calculate_total_spaces(const, target_column)
|
|
194
|
+
|
|
195
|
+
corrector.replace(
|
|
196
|
+
range_between(name_end, operator_start),
|
|
197
|
+
" " * total_spaces
|
|
198
|
+
)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Calculate total spaces needed for alignment.
|
|
202
|
+
#
|
|
203
|
+
# @param [RuboCop::AST::Node] const The constant assignment node.
|
|
204
|
+
# @param [Integer] target_column The target column for alignment.
|
|
205
|
+
# @return [Integer] The number of spaces (minimum 1).
|
|
206
|
+
def calculate_total_spaces(const, target_column)
|
|
207
|
+
current_column = const.loc.operator.column
|
|
208
|
+
current_spaces = const.loc.operator.begin_pos - const.loc.name.end_pos
|
|
209
|
+
spaces_needed = target_column - current_column
|
|
210
|
+
|
|
211
|
+
[1, current_spaces + spaces_needed].max
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Create a source range between two positions.
|
|
215
|
+
#
|
|
216
|
+
# @param [Integer] start_pos The start position.
|
|
217
|
+
# @param [Integer] end_pos The end position.
|
|
218
|
+
# @return [Parser::Source::Range]
|
|
219
|
+
def range_between(start_pos, end_pos)
|
|
220
|
+
Parser::Source::Range.new(
|
|
221
|
+
processed_source.buffer,
|
|
222
|
+
start_pos,
|
|
223
|
+
end_pos
|
|
224
|
+
)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Vibe
|
|
6
|
+
# Enforces alignment of consecutive `let` declarations at the `{` brace.
|
|
7
|
+
#
|
|
8
|
+
# Consecutive `let` declarations (with no blank lines between) should align their
|
|
9
|
+
# `{` braces for better readability. Groups are broken by blank lines or non-let
|
|
10
|
+
# statements.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # bad
|
|
14
|
+
# let(:character) { instance_double(Character) }
|
|
15
|
+
# let(:damage) { 1 }
|
|
16
|
+
# let(:instance) { described_class.new }
|
|
17
|
+
#
|
|
18
|
+
# # good
|
|
19
|
+
# let(:character) { instance_double(Character) }
|
|
20
|
+
# let(:damage) { 1 }
|
|
21
|
+
# let(:instance) { described_class.new }
|
|
22
|
+
#
|
|
23
|
+
# # good - blank line breaks the group
|
|
24
|
+
# let(:user) { create(:user) }
|
|
25
|
+
# let(:character) { create(:character) }
|
|
26
|
+
#
|
|
27
|
+
# let(:service) { Users::Activate.new } # Separate group, not aligned
|
|
28
|
+
class ConsecutiveLetAlignment < Base
|
|
29
|
+
extend AutoCorrector
|
|
30
|
+
include SpecFileHelper
|
|
31
|
+
|
|
32
|
+
MSG = "Align consecutive `let` declarations at the `{` brace."
|
|
33
|
+
|
|
34
|
+
# @!method let_declaration?(node)
|
|
35
|
+
# Check if node is a let/let! declaration.
|
|
36
|
+
def_node_matcher :let_declaration?, <<~PATTERN
|
|
37
|
+
(block (send nil? {:let :let!} (sym _)) ...)
|
|
38
|
+
PATTERN
|
|
39
|
+
|
|
40
|
+
# Check describe/context blocks for let alignment.
|
|
41
|
+
#
|
|
42
|
+
# @param [RuboCop::AST::Node] node The block node.
|
|
43
|
+
# @return [void]
|
|
44
|
+
def on_block(node)
|
|
45
|
+
return unless spec_file?
|
|
46
|
+
return unless describe_or_context?(node)
|
|
47
|
+
return unless node.body
|
|
48
|
+
|
|
49
|
+
check_lets_in_body(node.body)
|
|
50
|
+
end
|
|
51
|
+
alias on_numblock on_block
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# Check if block is a describe or context block.
|
|
56
|
+
#
|
|
57
|
+
# @param [RuboCop::AST::Node] node The block node.
|
|
58
|
+
# @return [Boolean]
|
|
59
|
+
def describe_or_context?(node)
|
|
60
|
+
node.send_node && %i(describe context).include?(node.method_name)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Check let declarations in a body node.
|
|
64
|
+
#
|
|
65
|
+
# @param [RuboCop::AST::Node] body The body node.
|
|
66
|
+
# @return [void]
|
|
67
|
+
def check_lets_in_body(body)
|
|
68
|
+
statements = extract_statements(body)
|
|
69
|
+
return if statements.size < 2
|
|
70
|
+
|
|
71
|
+
groups = group_consecutive_lets(statements)
|
|
72
|
+
groups.each { |group| check_group_alignment(group) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Extract statements from a body node.
|
|
76
|
+
#
|
|
77
|
+
# @param [RuboCop::AST::Node] body The body node.
|
|
78
|
+
# @return [Array<RuboCop::AST::Node>]
|
|
79
|
+
def extract_statements(body)
|
|
80
|
+
if body.begin_type?
|
|
81
|
+
body.children
|
|
82
|
+
else
|
|
83
|
+
[body]
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Group consecutive let declarations together.
|
|
88
|
+
#
|
|
89
|
+
# @param [Array<RuboCop::AST::Node>] statements The statements.
|
|
90
|
+
# @return [Array<Array<RuboCop::AST::Node>>] Groups of consecutive lets.
|
|
91
|
+
def group_consecutive_lets(statements)
|
|
92
|
+
groups = []
|
|
93
|
+
current_group = []
|
|
94
|
+
previous_line = nil
|
|
95
|
+
|
|
96
|
+
statements.each do |statement|
|
|
97
|
+
current_group, previous_line = process_statement(statement, current_group, previous_line, groups)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
finalize_groups(groups, current_group)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Process a single statement for grouping.
|
|
104
|
+
#
|
|
105
|
+
# @param [RuboCop::AST::Node] statement The statement.
|
|
106
|
+
# @param [Array<RuboCop::AST::Node>] current_group The current group.
|
|
107
|
+
# @param [Integer, nil] previous_line The previous line number.
|
|
108
|
+
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
109
|
+
# @return [Array] The updated current_group and previous_line.
|
|
110
|
+
def process_statement(statement, current_group, previous_line, groups)
|
|
111
|
+
if let_declaration?(statement)
|
|
112
|
+
current_group = handle_let(statement, current_group, previous_line, groups)
|
|
113
|
+
else
|
|
114
|
+
save_group_if_valid(groups, current_group)
|
|
115
|
+
current_group = []
|
|
116
|
+
end
|
|
117
|
+
[current_group, statement.loc.last_line]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Handle a let declaration.
|
|
121
|
+
#
|
|
122
|
+
# @param [RuboCop::AST::Node] statement The let declaration.
|
|
123
|
+
# @param [Array<RuboCop::AST::Node>] current_group The current group.
|
|
124
|
+
# @param [Integer, nil] previous_line The previous line number.
|
|
125
|
+
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
126
|
+
# @return [Array<RuboCop::AST::Node>] The updated current group.
|
|
127
|
+
def handle_let(statement, current_group, previous_line, groups)
|
|
128
|
+
if previous_line && statement.loc.line - previous_line > 1
|
|
129
|
+
save_group_if_valid(groups, current_group)
|
|
130
|
+
current_group = []
|
|
131
|
+
end
|
|
132
|
+
current_group << statement
|
|
133
|
+
current_group
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Save group if it has multiple let declarations.
|
|
137
|
+
#
|
|
138
|
+
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
139
|
+
# @param [Array<RuboCop::AST::Node>] group The group to potentially save.
|
|
140
|
+
# @return [void]
|
|
141
|
+
def save_group_if_valid(groups, group)
|
|
142
|
+
groups << group if group.size > 1
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Finalize groups by adding any remaining valid group.
|
|
146
|
+
#
|
|
147
|
+
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
148
|
+
# @param [Array<RuboCop::AST::Node>] current_group The current group.
|
|
149
|
+
# @return [Array<Array<RuboCop::AST::Node>>] The finalized groups.
|
|
150
|
+
def finalize_groups(groups, current_group)
|
|
151
|
+
save_group_if_valid(groups, current_group)
|
|
152
|
+
groups
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Check alignment for a group of let declarations.
|
|
156
|
+
#
|
|
157
|
+
# @param [Array<RuboCop::AST::Node>] group The let group.
|
|
158
|
+
# @return [void]
|
|
159
|
+
def check_group_alignment(group)
|
|
160
|
+
columns = group.map { |let| let.loc.begin.column }
|
|
161
|
+
target_column = columns.max
|
|
162
|
+
|
|
163
|
+
group.each do |let|
|
|
164
|
+
current_column = let.loc.begin.column
|
|
165
|
+
next if current_column == target_column
|
|
166
|
+
|
|
167
|
+
add_offense(let.send_node) do |corrector|
|
|
168
|
+
autocorrect_alignment(corrector, let, target_column)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Auto-correct the alignment of a let declaration.
|
|
174
|
+
#
|
|
175
|
+
# @param [RuboCop::AST::Corrector] corrector The corrector.
|
|
176
|
+
# @param [RuboCop::AST::Node] let The let block node.
|
|
177
|
+
# @param [Integer] target_column The target column for alignment.
|
|
178
|
+
# @return [void]
|
|
179
|
+
def autocorrect_alignment(corrector, let, target_column)
|
|
180
|
+
send_node = let.send_node
|
|
181
|
+
send_end = send_node.source_range.end_pos
|
|
182
|
+
brace_start = let.loc.begin.begin_pos
|
|
183
|
+
total_spaces = calculate_total_spaces(let, target_column)
|
|
184
|
+
|
|
185
|
+
corrector.replace(
|
|
186
|
+
range_between(send_end, brace_start),
|
|
187
|
+
" " * total_spaces
|
|
188
|
+
)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Calculate total spaces needed for alignment.
|
|
192
|
+
#
|
|
193
|
+
# @param [RuboCop::AST::Node] let The let block node.
|
|
194
|
+
# @param [Integer] target_column The target column for alignment.
|
|
195
|
+
# @return [Integer] The number of spaces (minimum 1).
|
|
196
|
+
def calculate_total_spaces(let, target_column)
|
|
197
|
+
current_column = let.loc.begin.column
|
|
198
|
+
current_spaces = let.loc.begin.begin_pos - let.send_node.source_range.end_pos
|
|
199
|
+
spaces_needed = target_column - current_column
|
|
200
|
+
|
|
201
|
+
[1, current_spaces + spaces_needed].max
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Create a source range between two positions.
|
|
205
|
+
#
|
|
206
|
+
# @param [Integer] start_pos The start position.
|
|
207
|
+
# @param [Integer] end_pos The end position.
|
|
208
|
+
# @return [Parser::Source::Range]
|
|
209
|
+
def range_between(start_pos, end_pos)
|
|
210
|
+
Parser::Source::Range.new(
|
|
211
|
+
processed_source.buffer,
|
|
212
|
+
start_pos,
|
|
213
|
+
end_pos
|
|
214
|
+
)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
# Default priority for descriptions that can't be categorized (e.g., constants, variables)
|
|
60
60
|
DEFAULT_PRIORITY = 999
|
|
61
61
|
|
|
62
|
-
MODEL_ORDER
|
|
62
|
+
MODEL_ORDER = %w(class associations validations).freeze
|
|
63
63
|
CONTROLLER_ACTIONS = %w(index show new create edit update destroy).freeze
|
|
64
64
|
SPECIAL_SECTIONS = {
|
|
65
65
|
"class" => 0,
|
|
@@ -9,30 +9,37 @@ module RuboCop
|
|
|
9
9
|
# per-line basis. If a cop needs to be disabled, it should be configured
|
|
10
10
|
# globally in `.rubocop.yml` with proper justification.
|
|
11
11
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# end
|
|
12
|
+
# Specific cops can be allowed via the `AllowedCops` configuration option.
|
|
13
|
+
# If multiple cops are disabled in one comment, only the disallowed ones
|
|
14
|
+
# will be flagged.
|
|
16
15
|
#
|
|
17
|
-
# @example Bad -
|
|
18
|
-
# # rubocop_disable Style/SomeCop
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
16
|
+
# @example Bad - inline disable directive
|
|
17
|
+
# do_something # rubocop_disable Style/SomeCop
|
|
18
|
+
#
|
|
19
|
+
# @example Bad - disable without specifying a cop
|
|
20
|
+
# # rubocop_disable all
|
|
21
|
+
# do_something
|
|
23
22
|
#
|
|
24
23
|
# @example Good - fix the issue instead
|
|
25
|
-
#
|
|
26
|
-
# do_something_correctly
|
|
27
|
-
# end
|
|
24
|
+
# do_something_correctly
|
|
28
25
|
#
|
|
29
26
|
# @example Good - configure globally in .rubocop.yml
|
|
27
|
+
# # In .rubocop.yml:
|
|
30
28
|
# # Style/SomeCop:
|
|
31
29
|
# # Enabled: false
|
|
30
|
+
#
|
|
31
|
+
# @example AllowedCops: ['Rails/RakeEnvironment'] - allowed cop is not flagged
|
|
32
|
+
# # rubocop_disable Rails/RakeEnvironment
|
|
33
|
+
# task :my_task do
|
|
34
|
+
# # ...
|
|
35
|
+
# end
|
|
32
36
|
class NoRubocopDisable < Base
|
|
33
|
-
MSG
|
|
37
|
+
MSG = "Do not disable `%<cops>s`. Fix the issue or configure globally in `.rubocop.yml`."
|
|
38
|
+
MSG_NO_COP = "Do not use `# rubocop:disable`. Fix the issue or configure globally in `.rubocop.yml`."
|
|
34
39
|
|
|
35
|
-
DISABLE_PATTERN
|
|
40
|
+
DISABLE_PATTERN = /\A#\s*rubocop\s*:\s*disable\b/i
|
|
41
|
+
COP_NAME_PATTERN = %r{[A-Za-z]+/[A-Za-z0-9]+}
|
|
42
|
+
ALL_PATTERN = /\brubocop\s*:\s*disable\s+all\b/i
|
|
36
43
|
|
|
37
44
|
# Check for rubocop:disable comments.
|
|
38
45
|
#
|
|
@@ -41,7 +48,7 @@ module RuboCop
|
|
|
41
48
|
processed_source.comments.each do |comment|
|
|
42
49
|
next unless disable_comment?(comment)
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
check_disabled_cops(comment)
|
|
45
52
|
end
|
|
46
53
|
end
|
|
47
54
|
|
|
@@ -54,6 +61,46 @@ module RuboCop
|
|
|
54
61
|
def disable_comment?(comment)
|
|
55
62
|
DISABLE_PATTERN.match?(comment.text)
|
|
56
63
|
end
|
|
64
|
+
|
|
65
|
+
# Check disabled cops and flag disallowed ones.
|
|
66
|
+
#
|
|
67
|
+
# @param [Parser::Source::Comment] comment The comment to check.
|
|
68
|
+
# @return [void]
|
|
69
|
+
def check_disabled_cops(comment)
|
|
70
|
+
cops = extract_cop_names(comment.text)
|
|
71
|
+
|
|
72
|
+
if cops.empty?
|
|
73
|
+
add_offense(comment, message: MSG_NO_COP)
|
|
74
|
+
else
|
|
75
|
+
disallowed = cops.reject { |cop| allowed_cop?(cop) }
|
|
76
|
+
return if disallowed.empty?
|
|
77
|
+
|
|
78
|
+
add_offense(comment, message: format(MSG, cops: disallowed.join(", ")))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Extract cop names from a rubocop:disable comment.
|
|
83
|
+
#
|
|
84
|
+
# @param [String] text The comment text.
|
|
85
|
+
# @return [Array<String>]
|
|
86
|
+
def extract_cop_names(text)
|
|
87
|
+
text.scan(COP_NAME_PATTERN)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Check if a cop is in the allowed list.
|
|
91
|
+
#
|
|
92
|
+
# @param [String] cop The cop name.
|
|
93
|
+
# @return [Boolean]
|
|
94
|
+
def allowed_cop?(cop)
|
|
95
|
+
allowed_cops.include?(cop)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Get the list of allowed cops from configuration.
|
|
99
|
+
#
|
|
100
|
+
# @return [Array<String>]
|
|
101
|
+
def allowed_cops
|
|
102
|
+
@allowed_cops ||= Array(cop_config.fetch("AllowedCops", []))
|
|
103
|
+
end
|
|
57
104
|
end
|
|
58
105
|
end
|
|
59
106
|
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Vibe
|
|
6
|
+
# Enforces using `if/end` blocks instead of inline modifiers for `raise` statements.
|
|
7
|
+
#
|
|
8
|
+
# Inline `raise ... if/unless` statements can be harder to read because they
|
|
9
|
+
# place the condition after the action. This cop enforces converting them
|
|
10
|
+
# to `if/end` blocks for better readability.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # bad - inline unless modifier
|
|
14
|
+
# raise ArgumentError, "Invalid column" unless COLUMNS.include?(column)
|
|
15
|
+
#
|
|
16
|
+
# # good - if block with negated condition
|
|
17
|
+
# if !COLUMNS.include?(column)
|
|
18
|
+
# raise ArgumentError, "Invalid column"
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# # bad - inline if modifier
|
|
22
|
+
# raise ArgumentError, "Invalid column" if column.nil?
|
|
23
|
+
#
|
|
24
|
+
# # good - if block
|
|
25
|
+
# if column.nil?
|
|
26
|
+
# raise ArgumentError, "Invalid column"
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# # good - raise without condition
|
|
30
|
+
# raise ArgumentError, "Invalid column"
|
|
31
|
+
class RaiseUnlessBlock < Base
|
|
32
|
+
extend AutoCorrector
|
|
33
|
+
|
|
34
|
+
MSG = "Use `if/end` block instead of inline modifier for `raise`."
|
|
35
|
+
|
|
36
|
+
# Check if nodes for raise with if/unless modifier.
|
|
37
|
+
#
|
|
38
|
+
# @param [RuboCop::AST::Node] node The if node.
|
|
39
|
+
# @return [void]
|
|
40
|
+
def on_if(node)
|
|
41
|
+
return unless node.modifier_form?
|
|
42
|
+
return unless raise_call?(node.body)
|
|
43
|
+
|
|
44
|
+
add_offense(node) do |corrector|
|
|
45
|
+
autocorrect(corrector, node)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# Check if the node is a raise method call.
|
|
52
|
+
#
|
|
53
|
+
# @param [RuboCop::AST::Node] node The node to check.
|
|
54
|
+
# @return [Boolean]
|
|
55
|
+
def raise_call?(node)
|
|
56
|
+
node.send_type? && node.method?(:raise)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Autocorrect the offense by converting to if block.
|
|
60
|
+
#
|
|
61
|
+
# @param [RuboCop::Cop::Corrector] corrector The corrector.
|
|
62
|
+
# @param [RuboCop::AST::Node] node The if node.
|
|
63
|
+
# @return [void]
|
|
64
|
+
def autocorrect(corrector, node)
|
|
65
|
+
replacement = build_replacement(node)
|
|
66
|
+
corrector.replace(node, replacement)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Build the replacement code for the raise statement.
|
|
70
|
+
#
|
|
71
|
+
# @param [RuboCop::AST::Node] node The if node.
|
|
72
|
+
# @return [String] The replacement code.
|
|
73
|
+
def build_replacement(node)
|
|
74
|
+
condition = build_condition(node)
|
|
75
|
+
raise_source = node.body.source
|
|
76
|
+
base_indent = " " * node.loc.column
|
|
77
|
+
inner_indent = "#{base_indent} "
|
|
78
|
+
|
|
79
|
+
[
|
|
80
|
+
"if #{condition}",
|
|
81
|
+
"#{inner_indent}#{raise_source}",
|
|
82
|
+
"#{base_indent}end"
|
|
83
|
+
].join("\n")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Build the condition for the if block.
|
|
87
|
+
# For unless, negate the condition. For if, keep it as is.
|
|
88
|
+
#
|
|
89
|
+
# @param [RuboCop::AST::Node] node The if node.
|
|
90
|
+
# @return [String] The condition source.
|
|
91
|
+
def build_condition(node)
|
|
92
|
+
if node.unless?
|
|
93
|
+
"!#{node.condition.source}"
|
|
94
|
+
else
|
|
95
|
+
node.condition.source
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Vibe
|
|
6
|
+
# Enforces that RSpec stub chains with `.with` or `.and_return` put each
|
|
7
|
+
# chained method on its own line when the line exceeds the max line length.
|
|
8
|
+
#
|
|
9
|
+
# This improves readability by keeping each part of the stub configuration
|
|
10
|
+
# on its own line rather than creating long horizontal lines.
|
|
11
|
+
#
|
|
12
|
+
# The max line length is read from the `Layout/LineLength` cop configuration.
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# # bad (when line exceeds max length)
|
|
16
|
+
# allow(Foo).to receive(:bar).with(very_long_argument_name).and_return(result)
|
|
17
|
+
#
|
|
18
|
+
# # good (split when line is too long)
|
|
19
|
+
# allow(Foo).to receive(:bar)
|
|
20
|
+
# .with(very_long_argument_name)
|
|
21
|
+
# .and_return(result)
|
|
22
|
+
#
|
|
23
|
+
# # good - short stubs can stay on one line
|
|
24
|
+
# allow(Foo).to receive(:bar).with(arg).and_return(result)
|
|
25
|
+
#
|
|
26
|
+
# # good - simple stubs without .with or .and_return are fine on one line
|
|
27
|
+
# allow(Foo).to receive(:bar)
|
|
28
|
+
#
|
|
29
|
+
# # good - .and_return directly after receive is allowed
|
|
30
|
+
# allow(Foo).to receive(:bar).and_return(result)
|
|
31
|
+
class RspecStubChainStyle < Base
|
|
32
|
+
extend AutoCorrector
|
|
33
|
+
include SpecFileHelper
|
|
34
|
+
|
|
35
|
+
MSG = "Put each chained stub method on its own line when line is too long."
|
|
36
|
+
|
|
37
|
+
CHAIN_METHODS = %i(
|
|
38
|
+
with
|
|
39
|
+
and_return
|
|
40
|
+
and_raise
|
|
41
|
+
and_throw
|
|
42
|
+
and_yield
|
|
43
|
+
and_call_original
|
|
44
|
+
and_wrap_original
|
|
45
|
+
once
|
|
46
|
+
twice
|
|
47
|
+
thrice
|
|
48
|
+
exactly
|
|
49
|
+
at_least
|
|
50
|
+
at_most
|
|
51
|
+
ordered
|
|
52
|
+
).freeze
|
|
53
|
+
|
|
54
|
+
RECEIVE_METHODS = %i(receive receive_message_chain receive_messages have_received).freeze
|
|
55
|
+
|
|
56
|
+
# @!method allow_to_receive?(node)
|
|
57
|
+
# Check if node is an allow/expect.to call with a receive chain argument.
|
|
58
|
+
def_node_matcher :allow_to_receive?, <<~PATTERN
|
|
59
|
+
(send
|
|
60
|
+
(send nil? {:allow :expect :allow_any_instance_of :expect_any_instance_of} ...)
|
|
61
|
+
:to
|
|
62
|
+
$send)
|
|
63
|
+
PATTERN
|
|
64
|
+
|
|
65
|
+
# Check send nodes for stub chains that need line breaks.
|
|
66
|
+
#
|
|
67
|
+
# @param [RuboCop::AST::Node] node The send node.
|
|
68
|
+
# @return [void]
|
|
69
|
+
def on_send(node)
|
|
70
|
+
if spec_file?
|
|
71
|
+
allow_to_receive?(node) do |receive_chain|
|
|
72
|
+
check_receive_chain(node, receive_chain)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
alias on_csend on_send
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
# Check the receive chain for methods that should be on separate lines.
|
|
81
|
+
#
|
|
82
|
+
# @param [RuboCop::AST::Node] to_node The .to node.
|
|
83
|
+
# @param [RuboCop::AST::Node] receive_chain The receive chain argument.
|
|
84
|
+
# @return [void]
|
|
85
|
+
def check_receive_chain(to_node, receive_chain)
|
|
86
|
+
chain = extract_chain(receive_chain)
|
|
87
|
+
receive_index = find_receive_index(chain)
|
|
88
|
+
return unless receive_index
|
|
89
|
+
return unless chain_has_with?(chain)
|
|
90
|
+
return unless line_exceeds_max_length?(to_node)
|
|
91
|
+
|
|
92
|
+
methods = chain[(receive_index + 1)..]
|
|
93
|
+
check_methods_alignment(to_node, chain[receive_index], methods)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Check if the line containing the node exceeds the max line length.
|
|
97
|
+
#
|
|
98
|
+
# @param [RuboCop::AST::Node] node The node to check.
|
|
99
|
+
# @return [Boolean]
|
|
100
|
+
def line_exceeds_max_length?(node)
|
|
101
|
+
line_number = node.loc.line
|
|
102
|
+
line = processed_source.lines[line_number - 1]
|
|
103
|
+
|
|
104
|
+
line.length > max_line_length
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Get the configured max line length from Layout/LineLength.
|
|
108
|
+
#
|
|
109
|
+
# @return [Integer]
|
|
110
|
+
def max_line_length
|
|
111
|
+
config.for_cop("Layout/LineLength")["Max"] || 120
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Extract the method chain from a node.
|
|
115
|
+
#
|
|
116
|
+
# @param [RuboCop::AST::Node] node The send node.
|
|
117
|
+
# @return [Array<RuboCop::AST::Node>]
|
|
118
|
+
def extract_chain(node)
|
|
119
|
+
chain = []
|
|
120
|
+
current = node
|
|
121
|
+
|
|
122
|
+
while current&.send_type?
|
|
123
|
+
chain.unshift(current)
|
|
124
|
+
current = current.receiver
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
chain
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Find the index of the receive method in the chain.
|
|
131
|
+
#
|
|
132
|
+
# @param [Array<RuboCop::AST::Node>] chain The method chain.
|
|
133
|
+
# @return [Integer, nil]
|
|
134
|
+
def find_receive_index(chain)
|
|
135
|
+
chain.index { |n| RECEIVE_METHODS.include?(n.method_name) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Check if the chain includes a .with call.
|
|
139
|
+
#
|
|
140
|
+
# @param [Array<RuboCop::AST::Node>] chain The method chain.
|
|
141
|
+
# @return [Boolean]
|
|
142
|
+
def chain_has_with?(chain)
|
|
143
|
+
chain.any? { |n| n.method?(:with) }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Check alignment of methods after receive.
|
|
147
|
+
#
|
|
148
|
+
# @param [RuboCop::AST::Node] to_node The .to node.
|
|
149
|
+
# @param [RuboCop::AST::Node] receive_node The receive node.
|
|
150
|
+
# @param [Array<RuboCop::AST::Node>] methods The methods to check.
|
|
151
|
+
# @return [void]
|
|
152
|
+
def check_methods_alignment(to_node, receive_node, methods)
|
|
153
|
+
previous_node = receive_node
|
|
154
|
+
is_first = true
|
|
155
|
+
|
|
156
|
+
methods.each do |method_node|
|
|
157
|
+
if should_flag?(previous_node, method_node, is_first) && chainable_method?(method_node)
|
|
158
|
+
register_offense(method_node, to_node)
|
|
159
|
+
end
|
|
160
|
+
previous_node = method_node
|
|
161
|
+
is_first = false
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Check if the method should be flagged.
|
|
166
|
+
#
|
|
167
|
+
# @param [RuboCop::AST::Node] previous_node The previous node.
|
|
168
|
+
# @param [RuboCop::AST::Node] method_node The method node.
|
|
169
|
+
# @param [Boolean] is_first Whether this is the first method after receive.
|
|
170
|
+
# @return [Boolean]
|
|
171
|
+
def should_flag?(previous_node, method_node, is_first)
|
|
172
|
+
reference_line = is_first ? previous_node.loc.last_line : previous_node.loc.selector.line
|
|
173
|
+
reference_line == method_node.loc.selector.line
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Check if the method is one we care about for chaining.
|
|
177
|
+
#
|
|
178
|
+
# @param [RuboCop::AST::Node] node The method node.
|
|
179
|
+
# @return [Boolean]
|
|
180
|
+
def chainable_method?(node)
|
|
181
|
+
CHAIN_METHODS.include?(node.method_name)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Register an offense for a method that should be on its own line.
|
|
185
|
+
#
|
|
186
|
+
# @param [RuboCop::AST::Node] method_node The method node.
|
|
187
|
+
# @param [RuboCop::AST::Node] previous_node The previous node in chain.
|
|
188
|
+
# @return [void]
|
|
189
|
+
def register_offense(method_node, previous_node)
|
|
190
|
+
add_offense(method_node.loc.selector) do |corrector|
|
|
191
|
+
autocorrect_chain(corrector, method_node, previous_node)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Auto-correct by inserting a newline before the method.
|
|
196
|
+
#
|
|
197
|
+
# @param [RuboCop::Cop::Corrector] corrector The corrector.
|
|
198
|
+
# @param [RuboCop::AST::Node] method_node The method to move.
|
|
199
|
+
# @param [RuboCop::AST::Node] to_node The .to node for indentation.
|
|
200
|
+
# @return [void]
|
|
201
|
+
def autocorrect_chain(corrector, method_node, to_node)
|
|
202
|
+
dot_range = method_node.loc.dot
|
|
203
|
+
indentation = calculate_indentation(to_node)
|
|
204
|
+
replacement_start = find_replacement_start(method_node)
|
|
205
|
+
|
|
206
|
+
corrector.replace(
|
|
207
|
+
range_between(replacement_start, dot_range.begin_pos),
|
|
208
|
+
"\n#{indentation}"
|
|
209
|
+
)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Find the starting position for the replacement.
|
|
213
|
+
#
|
|
214
|
+
# @param [RuboCop::AST::Node] method_node The method node.
|
|
215
|
+
# @return [Integer]
|
|
216
|
+
def find_replacement_start(method_node)
|
|
217
|
+
method_node.receiver.source_range.end_pos
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Calculate the indentation for the new line.
|
|
221
|
+
#
|
|
222
|
+
# @param [RuboCop::AST::Node] node The node to base indentation on.
|
|
223
|
+
# @return [String]
|
|
224
|
+
def calculate_indentation(node)
|
|
225
|
+
base_column = find_chain_start_column(node)
|
|
226
|
+
" " * (base_column + 2)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Find the starting column of the chain.
|
|
230
|
+
#
|
|
231
|
+
# @param [RuboCop::AST::Node] node A node in the chain.
|
|
232
|
+
# @return [Integer]
|
|
233
|
+
def find_chain_start_column(node)
|
|
234
|
+
current = node
|
|
235
|
+
|
|
236
|
+
current = current.receiver while current.receiver&.send_type?
|
|
237
|
+
|
|
238
|
+
current.loc.column
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Create a source range between two positions.
|
|
242
|
+
#
|
|
243
|
+
# @param [Integer] start_pos The start position.
|
|
244
|
+
# @param [Integer] end_pos The end position.
|
|
245
|
+
# @return [Parser::Source::Range]
|
|
246
|
+
def range_between(start_pos, end_pos)
|
|
247
|
+
Parser::Source::Range.new(
|
|
248
|
+
processed_source.buffer,
|
|
249
|
+
start_pos,
|
|
250
|
+
end_pos
|
|
251
|
+
)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
@@ -5,6 +5,8 @@ require_relative "vibe/mixin/spec_file_helper"
|
|
|
5
5
|
require_relative "vibe/blank_line_before_expectation"
|
|
6
6
|
require_relative "vibe/class_organization"
|
|
7
7
|
require_relative "vibe/consecutive_assignment_alignment"
|
|
8
|
+
require_relative "vibe/consecutive_constant_alignment"
|
|
9
|
+
require_relative "vibe/consecutive_let_alignment"
|
|
8
10
|
require_relative "vibe/describe_block_order"
|
|
9
11
|
require_relative "vibe/is_expected_one_liner"
|
|
10
12
|
require_relative "vibe/no_assigns_attribute_testing"
|
|
@@ -12,4 +14,6 @@ require_relative "vibe/no_rubocop_disable"
|
|
|
12
14
|
require_relative "vibe/no_skipped_tests"
|
|
13
15
|
require_relative "vibe/no_unless_guard_clause"
|
|
14
16
|
require_relative "vibe/prefer_one_liner_expectation"
|
|
17
|
+
require_relative "vibe/raise_unless_block"
|
|
18
|
+
require_relative "vibe/rspec_stub_chain_style"
|
|
15
19
|
require_relative "vibe/service_call_method"
|
data/lib/rubocop/vibe/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-vibe
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tristan Dunn
|
|
@@ -90,6 +90,8 @@ files:
|
|
|
90
90
|
- lib/rubocop/cop/vibe/blank_line_before_expectation.rb
|
|
91
91
|
- lib/rubocop/cop/vibe/class_organization.rb
|
|
92
92
|
- lib/rubocop/cop/vibe/consecutive_assignment_alignment.rb
|
|
93
|
+
- lib/rubocop/cop/vibe/consecutive_constant_alignment.rb
|
|
94
|
+
- lib/rubocop/cop/vibe/consecutive_let_alignment.rb
|
|
93
95
|
- lib/rubocop/cop/vibe/describe_block_order.rb
|
|
94
96
|
- lib/rubocop/cop/vibe/is_expected_one_liner.rb
|
|
95
97
|
- lib/rubocop/cop/vibe/mixin/spec_file_helper.rb
|
|
@@ -98,6 +100,8 @@ files:
|
|
|
98
100
|
- lib/rubocop/cop/vibe/no_skipped_tests.rb
|
|
99
101
|
- lib/rubocop/cop/vibe/no_unless_guard_clause.rb
|
|
100
102
|
- lib/rubocop/cop/vibe/prefer_one_liner_expectation.rb
|
|
103
|
+
- lib/rubocop/cop/vibe/raise_unless_block.rb
|
|
104
|
+
- lib/rubocop/cop/vibe/rspec_stub_chain_style.rb
|
|
101
105
|
- lib/rubocop/cop/vibe/service_call_method.rb
|
|
102
106
|
- lib/rubocop/cop/vibe_cops.rb
|
|
103
107
|
- lib/rubocop/vibe.rb
|