rubocop-fourshark 0.8.0 → 0.8.2
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 +13 -0
- data/lib/rubocop/cop/style/disallow_delegate.rb +85 -0
- data/lib/rubocop/fourshark/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: 9358c7dd0bcd6717781203b1f4ccdd6e282851da1e52838db50acf058ce39414
|
|
4
|
+
data.tar.gz: f219f75c7fdaea1dff73f43ed9a1c16478e97b44f45f5242d9f21044e57cd19c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: edd536903d83b3516f576bdcf318847c5914a9b747d52203f2153a2dc2eaf5af783a7fc9d16d1f730674d51a8bde815b576a29f64619655824a227551a2979b1
|
|
7
|
+
data.tar.gz: c063cd068bace0cdccf5e0948e94c40088b0160fc023f14ee315f4177374134bea62eedc49d42655a505579afc0d43a9248d1f2d4a6231344d624154e5b95afe
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## [0.8.2] - 2026-08-06
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
|
|
5
|
+
- `Style/DisallowDelegate` — `each` in a class including `Enumerable` is no longer flagged
|
|
6
|
+
- `Style/DisallowDelegate` — a method composing its own state into the call is no longer flagged
|
|
7
|
+
|
|
8
|
+
## [0.8.1] - 2026-08-06
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- `Style/DisallowDelegate` — a method answering about its own class is no longer flagged
|
|
13
|
+
|
|
1
14
|
## [0.8.0] - 2026-08-05
|
|
2
15
|
|
|
3
16
|
### Changed
|
|
@@ -17,6 +17,26 @@ module RuboCop
|
|
|
17
17
|
# the forwarder by hand changes what `grep` finds, not what the class
|
|
18
18
|
# claims to be responsible for.
|
|
19
19
|
#
|
|
20
|
+
# `self.class` is not a collaborator — it is the object's own class. An
|
|
21
|
+
# instance method that composes its own attributes into a same-named class
|
|
22
|
+
# method answers for a domain it owns, so it is not delegation and is not
|
|
23
|
+
# flagged.
|
|
24
|
+
#
|
|
25
|
+
# `each` in a class that includes `Enumerable` is the module's required
|
|
26
|
+
# contract, not delegation. The class has no interface without it — every
|
|
27
|
+
# method `Enumerable` provides is built on `each` — so the class is
|
|
28
|
+
# implementing its own interface rather than answering for a collaborator.
|
|
29
|
+
# It is not flagged. Any other method in such a class still is.
|
|
30
|
+
#
|
|
31
|
+
# A body that passes the object's own state as an argument is not a
|
|
32
|
+
# pass-through. Middle Man is a method that republishes a collaborator's
|
|
33
|
+
# answer verbatim, which the caller could reach by navigating; a method
|
|
34
|
+
# that supplies its own attributes contributes something the caller would
|
|
35
|
+
# otherwise have to reach in and take, and the result is a simpler API on
|
|
36
|
+
# the object that owns the data. Own state is an instance variable or a
|
|
37
|
+
# receiverless call — a method parameter forwarded through is not, so a
|
|
38
|
+
# setter passing its argument along stays flagged.
|
|
39
|
+
#
|
|
20
40
|
# @example
|
|
21
41
|
# # bad
|
|
22
42
|
# delegate :name, to: :commission
|
|
@@ -29,6 +49,25 @@ module RuboCop
|
|
|
29
49
|
# # good — the caller navigates
|
|
30
50
|
# statement.commission.name
|
|
31
51
|
#
|
|
52
|
+
# # good — the object answers about itself, not for a collaborator
|
|
53
|
+
# def lock_key
|
|
54
|
+
# self.class.lock_key(company_id: company_id)
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
57
|
+
# # good — `each` is the Enumerable contract this class implements
|
|
58
|
+
# class SearchResult
|
|
59
|
+
# include Enumerable
|
|
60
|
+
#
|
|
61
|
+
# def each(&)
|
|
62
|
+
# results.each(&)
|
|
63
|
+
# end
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
# # good — composes its own attribute, so the caller cannot just navigate
|
|
67
|
+
# def output
|
|
68
|
+
# variable.output(value)
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
32
71
|
class DisallowDelegate < ::RuboCop::Cop::Base
|
|
33
72
|
MACRO_MSG = 'Do not use automatic delegation. Delete the forwarder and let the caller navigate.'
|
|
34
73
|
FORWARDER_MSG = 'Do not forward a collaborator\'s message. Delete the forwarder and let the caller navigate.'
|
|
@@ -41,8 +80,15 @@ module RuboCop
|
|
|
41
80
|
add_offense(node, message: MACRO_MSG)
|
|
42
81
|
end
|
|
43
82
|
|
|
83
|
+
# @!method includes_enumerable?(node)
|
|
84
|
+
def_node_matcher :includes_enumerable?, <<~PATTERN
|
|
85
|
+
(send nil? :include (const {nil? cbase} :Enumerable))
|
|
86
|
+
PATTERN
|
|
87
|
+
|
|
44
88
|
def on_def(node)
|
|
45
89
|
return unless forwards_own_message?(node.body, node.method_name)
|
|
90
|
+
return if enumerable_contract?(node)
|
|
91
|
+
return if composes_own_state?(node.body)
|
|
46
92
|
|
|
47
93
|
add_offense(node.loc.name, message: FORWARDER_MSG)
|
|
48
94
|
end
|
|
@@ -53,9 +99,48 @@ module RuboCop
|
|
|
53
99
|
return false if body.nil?
|
|
54
100
|
return false unless body.send_type?
|
|
55
101
|
return false if body.receiver.nil?
|
|
102
|
+
return false if own_class?(body.receiver)
|
|
56
103
|
|
|
57
104
|
body.method?(method_name)
|
|
58
105
|
end
|
|
106
|
+
|
|
107
|
+
def own_class?(receiver)
|
|
108
|
+
return false unless receiver.send_type?
|
|
109
|
+
return false unless receiver.method?(:class)
|
|
110
|
+
return false if receiver.receiver.nil?
|
|
111
|
+
|
|
112
|
+
receiver.receiver.self_type?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def enumerable_contract?(node)
|
|
116
|
+
return false unless node.method?(:each)
|
|
117
|
+
|
|
118
|
+
enclosing = node.each_ancestor(:class, :module).first
|
|
119
|
+
return false if enclosing.nil?
|
|
120
|
+
|
|
121
|
+
enumerable_body?(enclosing.body)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def enumerable_body?(body)
|
|
125
|
+
return false if body.nil?
|
|
126
|
+
return body.children.any? { |child| includes_enumerable?(child) } if body.begin_type?
|
|
127
|
+
|
|
128
|
+
includes_enumerable?(body)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def composes_own_state?(body)
|
|
132
|
+
body.arguments.any? { |argument| own_state?(argument) }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# A method parameter reaches the body as an `lvar`, so only an instance
|
|
136
|
+
# variable or a receiverless call counts as the object's own state.
|
|
137
|
+
def own_state?(argument)
|
|
138
|
+
return true if argument.ivar_type?
|
|
139
|
+
return true if argument.send_type? && argument.receiver.nil?
|
|
140
|
+
return argument.values.any? { |value| own_state?(value) } if argument.hash_type?
|
|
141
|
+
|
|
142
|
+
false
|
|
143
|
+
end
|
|
59
144
|
end
|
|
60
145
|
end
|
|
61
146
|
end
|