rubocop-rspec_parity 1.2.2 → 1.2.3
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/CHANGELOG.md +4 -0
- data/lib/rubocop/cop/rspec_parity/public_method_has_spec.rb +61 -13
- data/lib/rubocop/rspec_parity/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e3c642272877446cd87c6b7d88339e284fe29fac025f153489d28398e20e73bd
|
|
4
|
+
data.tar.gz: bb91681c2f70a3a35febf88a132344a125e0f5a6735df0a2a0b5c39bcee79c97
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 83ee14e1845f77229138051716410f1cc1c9d751430800300d4e204b6a4ab07df7c0380549e200723d8ff861a8ce52a5ed02fe461ef8059a02217ffe2da3467b
|
|
7
|
+
data.tar.gz: d9300dd4aacf6711bbfc2135c9814f03cfef4b42981f6cc5c9f7c8291ee1f525ebd092cb5645e25faf31c9171311e92a2841f9cc67ba0780ee2cce6e56297057
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.2.3] - 2026-02-10
|
|
4
|
+
|
|
5
|
+
Fixed: `PublicMethodHasSpec` correctly detects visibility for `private :method_name`, `protected :method_name`, `private def method_name`, `protected def method_name`, and `private`/`protected` inside `class << self`
|
|
6
|
+
|
|
3
7
|
## [1.2.2] - 2026-02-10
|
|
4
8
|
|
|
5
9
|
Added: `PublicMethodHasSpec` now skips class methods marked with `private_class_method` (both inline and post-hoc forms)
|
|
@@ -83,16 +83,50 @@ module RuboCop
|
|
|
83
83
|
def public_method?(node)
|
|
84
84
|
return false if node.nil?
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
# Inline form: private def method_name / protected def method_name
|
|
87
|
+
if node.parent&.send_type? && VISIBILITY_METHODS.key?(node.parent.method_name)
|
|
88
|
+
return node.parent.method_name == :public
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
scope = find_enclosing_scope(node)
|
|
92
|
+
return true unless scope
|
|
88
93
|
|
|
89
|
-
|
|
94
|
+
# Post-hoc targeted form overrides section-level visibility
|
|
95
|
+
targeted = targeted_visibility(scope, node.method_name)
|
|
96
|
+
return targeted == :public unless targeted.nil?
|
|
97
|
+
|
|
98
|
+
compute_visibility(scope, node) == :public
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def find_enclosing_scope(node)
|
|
102
|
+
node.each_ancestor.find { |n| n.class_type? || n.module_type? || n.sclass_type? }
|
|
90
103
|
end
|
|
91
104
|
|
|
92
105
|
def find_class_or_module(node)
|
|
93
106
|
node.each_ancestor.find { |n| n.class_type? || n.module_type? }
|
|
94
107
|
end
|
|
95
108
|
|
|
109
|
+
def targeted_visibility(scope, method_name)
|
|
110
|
+
return nil unless scope.body
|
|
111
|
+
|
|
112
|
+
scope_children(scope).each do |child|
|
|
113
|
+
next unless targeted_visibility_call?(child, method_name)
|
|
114
|
+
|
|
115
|
+
return VISIBILITY_METHODS[child.method_name]
|
|
116
|
+
end
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def targeted_visibility_call?(node, method_name)
|
|
121
|
+
node&.send_type? &&
|
|
122
|
+
VISIBILITY_METHODS.key?(node.method_name) &&
|
|
123
|
+
node.arguments.any? { |arg| arg.sym_type? && arg.value == method_name }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def scope_children(scope)
|
|
127
|
+
scope.body.begin_type? ? scope.body.children : [scope.body]
|
|
128
|
+
end
|
|
129
|
+
|
|
96
130
|
def compute_visibility(class_or_module, target_node)
|
|
97
131
|
visibility = :public
|
|
98
132
|
class_or_module.body&.each_child_node do |child|
|
|
@@ -105,6 +139,7 @@ module RuboCop
|
|
|
105
139
|
|
|
106
140
|
def update_visibility(child, current_visibility)
|
|
107
141
|
return current_visibility unless child.send_type?
|
|
142
|
+
return current_visibility if child.arguments.any? # targeted/inline, not section-level
|
|
108
143
|
|
|
109
144
|
VISIBILITY_METHODS.fetch(child.method_name, current_visibility)
|
|
110
145
|
end
|
|
@@ -222,23 +257,17 @@ module RuboCop
|
|
|
222
257
|
return 0 unless class_node&.body
|
|
223
258
|
|
|
224
259
|
public_methods = []
|
|
260
|
+
targeted_non_public = []
|
|
225
261
|
visibility = :public
|
|
226
|
-
|
|
227
|
-
# Get all child nodes, handling both single method and begin-wrapped bodies
|
|
228
|
-
children = if class_node.body.begin_type?
|
|
229
|
-
class_node.body.children
|
|
230
|
-
else
|
|
231
|
-
[class_node.body]
|
|
232
|
-
end
|
|
262
|
+
children = scope_children(class_node)
|
|
233
263
|
|
|
234
264
|
children.each do |child|
|
|
235
265
|
next unless child
|
|
236
266
|
|
|
237
267
|
case child.type
|
|
238
268
|
when :send
|
|
239
|
-
visibility =
|
|
269
|
+
count_public_methods_handle_send(child, visibility, targeted_non_public).tap { |v| visibility = v if v }
|
|
240
270
|
when :def
|
|
241
|
-
# Only count instance methods (def), not class methods (defs)
|
|
242
271
|
if visibility == :public
|
|
243
272
|
method_name = child.method_name.to_s
|
|
244
273
|
public_methods << method_name unless excluded_method?(method_name)
|
|
@@ -246,10 +275,29 @@ module RuboCop
|
|
|
246
275
|
end
|
|
247
276
|
end
|
|
248
277
|
|
|
249
|
-
public_methods.size
|
|
278
|
+
(public_methods - targeted_non_public).size
|
|
250
279
|
end
|
|
251
280
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
252
281
|
|
|
282
|
+
def count_public_methods_handle_send(child, visibility, targeted_non_public)
|
|
283
|
+
return visibility unless VISIBILITY_METHODS.key?(child.method_name)
|
|
284
|
+
|
|
285
|
+
if child.arguments.empty?
|
|
286
|
+
VISIBILITY_METHODS[child.method_name]
|
|
287
|
+
else
|
|
288
|
+
collect_targeted_non_public(child, targeted_non_public)
|
|
289
|
+
nil
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def collect_targeted_non_public(child, targeted_non_public)
|
|
294
|
+
return if VISIBILITY_METHODS[child.method_name] == :public
|
|
295
|
+
|
|
296
|
+
child.arguments.each do |arg|
|
|
297
|
+
targeted_non_public << arg.value.to_s if arg.sym_type?
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
253
301
|
def spec_has_examples?(spec_path, class_name)
|
|
254
302
|
spec_content = File.read(spec_path)
|
|
255
303
|
escaped_class_name = Regexp.escape(class_name)
|