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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3715ce918bf2499ae19801f42db4966430bc7a2ae33aae165bc7676865bdfc3
4
- data.tar.gz: 48d1eac2b33b6971b80463057f859d4f2cbc5efa5d03d298484a9819cb1c1989
3
+ metadata.gz: 9358c7dd0bcd6717781203b1f4ccdd6e282851da1e52838db50acf058ce39414
4
+ data.tar.gz: f219f75c7fdaea1dff73f43ed9a1c16478e97b44f45f5242d9f21044e57cd19c
5
5
  SHA512:
6
- metadata.gz: 2cb23a767a11947a89a3792c32cb9a3da0e888c83ebd377447b71e9ec21c4643a3105bf361d445fdd1d942b3f92894e68989efcceed7ba4946077f882cdb36b4
7
- data.tar.gz: c9f15b6c4a0a3340ead5387783a0585423c44a3e299143910c145d3bf086d15924a2d0e93a87213ff2da8f059c6cd897fd05a7f04fe9d6ff29b0517e9d00885b
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Fourshark
5
- VERSION = '0.8.0'
5
+ VERSION = '0.8.2'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-fourshark
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Ribeiro