rubocop-vibe 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 66c4596677b86001b40036ed73d0ce8300b9010cc3ea16af13436cc7a1ab3cfd
4
+ data.tar.gz: b27f79a610368b858a565c70c90fcf7db80e0b258e7efc3ce17942ccfdef79cc
5
+ SHA512:
6
+ metadata.gz: 57f43f61aba7ac210141f1d3aafff845f9b9a356ab88a42174de2344035825377bfc3ed1603d37e4b9662208f013a027be6be28cf2da40a514acb44f517469a6
7
+ data.tar.gz: fbe66437855c42030400323044fa53d3526e017fea1db8962d342aa9df7484072746a31c86791d7448c7f100b465ce72430318117d11967dfbb19fde09ee3d46
@@ -0,0 +1,126 @@
1
+ plugins:
2
+ - rubocop-performance
3
+ - rubocop-rake
4
+ - rubocop-rspec
5
+
6
+ AllCops:
7
+ Exclude:
8
+ - "tmp/**/*"
9
+ - "vendor/**/*"
10
+ NewCops: enable
11
+ TargetRubyVersion: 4.0
12
+
13
+ Layout/HashAlignment:
14
+ EnforcedColonStyle: table
15
+ EnforcedHashRocketStyle: table
16
+
17
+ Naming/RescuedExceptionsVariableName:
18
+ Enabled: true
19
+ PreferredName: error
20
+
21
+ Naming/VariableNumber:
22
+ EnforcedStyle: snake_case
23
+
24
+ Style/ArgumentsForwarding:
25
+ UseAnonymousForwarding: false
26
+
27
+ Style/Documentation:
28
+ Enabled: false
29
+
30
+ Style/DocumentationMethod:
31
+ Enabled: true
32
+ Exclude:
33
+ - "db/**/*"
34
+ - "spec/**/*"
35
+ RequireForNonPublicMethods: true
36
+
37
+ Style/ExpandPathArguments:
38
+ Enabled: true
39
+ Exclude:
40
+ - bin/*
41
+
42
+ Style/GuardClause:
43
+ Enabled: false
44
+
45
+ Style/HashSyntax:
46
+ Enabled: false
47
+
48
+ Style/IfUnlessModifier:
49
+ Enabled: false
50
+
51
+ Style/PercentLiteralDelimiters:
52
+ PreferredDelimiters:
53
+ "%i": "()"
54
+ "%w": "()"
55
+
56
+ Style/RaiseArgs:
57
+ EnforcedStyle: compact
58
+
59
+ Style/StringLiterals:
60
+ EnforcedStyle: double_quotes
61
+
62
+ Style/StringLiteralsInInterpolation:
63
+ EnforcedStyle: double_quotes
64
+
65
+ Vibe/BlankLineBeforeExpectation:
66
+ Description: "Enforces a blank line before expectation calls when there is setup code above."
67
+ Enabled: true
68
+ SafeAutoCorrect: true
69
+ VersionAdded: "0.1.0"
70
+
71
+ Vibe/ConsecutiveAssignmentAlignment:
72
+ Description: "Enforces alignment of consecutive variable assignments at the = operator."
73
+ Enabled: true
74
+ SafeAutoCorrect: true
75
+ VersionAdded: "0.1.0"
76
+
77
+ Vibe/DescribeBlockOrder:
78
+ Description: "Enforces consistent ordering of describe blocks in RSpec files."
79
+ Enabled: true
80
+ SafeAutoCorrect: false
81
+ VersionAdded: "0.1.0"
82
+
83
+ Vibe/IsExpectedOneLiner:
84
+ Description: "Enforces that is_expected is only used in one-liner it { } blocks."
85
+ Enabled: true
86
+ SafeAutoCorrect: true
87
+ VersionAdded: "0.1.0"
88
+
89
+ Vibe/ClassOrganization:
90
+ Description: "Enforces consistent organization of class definitions."
91
+ Enabled: true
92
+ SafeAutoCorrect: false
93
+ VersionAdded: "0.1.0"
94
+
95
+ Vibe/NoAssignsAttributeTesting:
96
+ Description: "Enforces that controller specs only test assignment identity, not attributes or associations."
97
+ Enabled: true
98
+ SafeAutoCorrect: false
99
+ VersionAdded: "0.1.0"
100
+
101
+ Vibe/NoRubocopDisable:
102
+ Description: "Enforces that rubocop:disable comments are not used inline."
103
+ Enabled: true
104
+ VersionAdded: "0.1.0"
105
+
106
+ Vibe/NoSkippedTests:
107
+ Description: "Enforces that tests are not skipped or marked as pending."
108
+ Enabled: true
109
+ VersionAdded: "0.1.0"
110
+
111
+ Vibe/NoUnlessGuardClause:
112
+ Description: "Enforces using positive if conditions instead of unless for guard clauses."
113
+ Enabled: true
114
+ SafeAutoCorrect: true
115
+ VersionAdded: "0.1.0"
116
+
117
+ Vibe/PreferOneLinerExpectation:
118
+ Description: "Enforces one-liner syntax for simple RSpec expectations."
119
+ Enabled: true
120
+ SafeAutoCorrect: true
121
+ VersionAdded: "0.1.0"
122
+
123
+ Vibe/ServiceCallMethod:
124
+ Description: "Service objects should define `self.call` and `call` methods."
125
+ Enabled: true
126
+ VersionAdded: "0.1.0"
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Vibe
6
+ # Enforces a blank line before expectation calls when there's setup code above.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # it "processes the record" do
11
+ # record.process
12
+ # expect(record).to be_processed
13
+ # end
14
+ #
15
+ # # good
16
+ # it "processes the record" do
17
+ # record.process
18
+ #
19
+ # expect(record).to be_processed
20
+ # end
21
+ #
22
+ # # good - no setup code above
23
+ # it "is valid" do
24
+ # expect(record).to be_valid
25
+ # end
26
+ class BlankLineBeforeExpectation < Base
27
+ extend AutoCorrector
28
+ include SpecFileHelper
29
+
30
+ MSG = "Add a blank line before expectation when there is setup code above."
31
+
32
+ # @!method example_block?(node)
33
+ # Check if block is an example block (it, specify, scenario).
34
+ def_node_matcher :example_block?, <<~PATTERN
35
+ (block (send nil? {:it :specify :scenario} ...) ...)
36
+ PATTERN
37
+
38
+ # @!method expect_call?(node)
39
+ # Check if node is an expect call.
40
+ def_node_matcher :expect_call?, <<~PATTERN
41
+ (send nil? :expect ...)
42
+ PATTERN
43
+
44
+ # Check block nodes for expect calls in example blocks.
45
+ #
46
+ # @param [RuboCop::AST::Node] node The block node.
47
+ # @return [void]
48
+ def on_block(node)
49
+ if processable_block?(node)
50
+ check_statements(extract_statements(node.body))
51
+ end
52
+ end
53
+ alias on_numblock on_block
54
+
55
+ private
56
+
57
+ # Check if the block should be processed.
58
+ #
59
+ # @param [RuboCop::AST::Node] node The block node.
60
+ # @return [Boolean]
61
+ def processable_block?(node)
62
+ spec_file? && example_block?(node) && node.body
63
+ end
64
+
65
+ # Extract statements from the block body.
66
+ #
67
+ # @param [RuboCop::AST::Node] body The block body.
68
+ # @return [Array<RuboCop::AST::Node>]
69
+ def extract_statements(body)
70
+ body.begin_type? ? body.children : [body]
71
+ end
72
+
73
+ # Check statements for missing blank lines before expectations.
74
+ #
75
+ # @param [Array<RuboCop::AST::Node>] statements The statements to check.
76
+ # @return [void]
77
+ def check_statements(statements)
78
+ statements.each_with_index do |statement, index|
79
+ next if index.zero?
80
+
81
+ check_statement_pair(statements[index - 1], statement)
82
+ end
83
+ end
84
+
85
+ # Check a pair of statements for missing blank line.
86
+ #
87
+ # @param [RuboCop::AST::Node] previous_statement The previous statement.
88
+ # @param [RuboCop::AST::Node] current_statement The current statement.
89
+ # @return [void]
90
+ def check_statement_pair(previous_statement, current_statement)
91
+ expect_node = find_expect_node(current_statement)
92
+ return unless expect_node
93
+ return if blank_line_between?(previous_statement, current_statement)
94
+ return if find_expect_node(previous_statement)
95
+
96
+ register_offense(expect_node, previous_statement)
97
+ end
98
+
99
+ # Register an offense for missing blank line.
100
+ #
101
+ # @param [RuboCop::AST::Node] expect_node The expect node.
102
+ # @param [RuboCop::AST::Node] previous_statement The previous statement.
103
+ # @return [void]
104
+ def register_offense(expect_node, previous_statement)
105
+ add_offense(expect_node.loc.selector) do |corrector|
106
+ corrector.insert_after(previous_statement, "\n")
107
+ end
108
+ end
109
+
110
+ # Find the expect node within a statement.
111
+ #
112
+ # Searches the node and its descendants for expect calls.
113
+ # Only searches within the statement's method chain, not into
114
+ # nested blocks like those passed to other methods.
115
+ #
116
+ # @param [RuboCop::AST::Node] node The statement node.
117
+ # @return [RuboCop::AST::Node] The expect node if found.
118
+ # @return [nil] When no expect call is found.
119
+ def find_expect_node(node)
120
+ return unless node.send_type?
121
+
122
+ # Check if this node itself is an expect call.
123
+ return node if expect_call?(node)
124
+
125
+ # Search descendants for expect calls.
126
+ # This includes expect { ... } which has a block attached.
127
+ node.each_descendant(:send).find { |send_node| expect_call?(send_node) }
128
+ end
129
+
130
+ # Check if there's a blank line between two statements.
131
+ #
132
+ # @param [RuboCop::AST::Node] previous_node The previous statement.
133
+ # @param [RuboCop::AST::Node] current_node The current statement.
134
+ # @return [Boolean]
135
+ def blank_line_between?(previous_node, current_node)
136
+ current_node.loc.line - previous_node.loc.last_line > 1
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end