rubocop-fourshark 0.8.3 → 0.8.4

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: dbfa3d2a442ba57bc74ba0171563f0bcadb6f4460b769654963066462ca0b61f
4
- data.tar.gz: '097de5b926870d292e63aa39f79aacc8342fe1909e34809bf9bb2f066c309a22'
3
+ metadata.gz: 4cdbe5b265ec54b100ac589171adad9038c99fee37dac3008cb56bba16e1a002
4
+ data.tar.gz: 8dfc81f74e7af24ad5518bf67ab735e9d75557cf2db03d4401ee46ba1c471f61
5
5
  SHA512:
6
- metadata.gz: 36791e28a8f0c14fa70a4990d953207412390095deda3386d9aa4edb233447ef4576d645b4ea499414c5e3ecac44fe9e4d2845033d83d607453a303655d6e32a
7
- data.tar.gz: 17bbb761062d3b28d03238fcf94df33bc29cc2f85ba839038469e7cf39d5c7bd0a6ced5402fac62d6660c7eb5d7c57e8971aa118f3ce1283412f7ddd366f223a
6
+ metadata.gz: 684f7b5b0f1064064eb545240a7f2f290207c9d0e7f759363e26d2607b2fe0d48fda173e8e48827769fa625f3eefb5ed6591ffdbdc770bf29f63bebbe156c50f
7
+ data.tar.gz: 19a91ad805511e8a3f19d903d18eeb007c5a2c1d4e2329733336e12d6b041cad998466f0fe080556830fbf8a90be6736f568b33eaf1d803b3e2e4c94b0dd8555
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [0.8.4] - 2026-08-07
2
+
3
+ ### Fixed
4
+
5
+ - `Style/DisallowDelegate` — own state reached through a call on it is no longer flagged
6
+ - `Style/DisallowDelegate` — an argument rooted at the collaborator being forwarded to is flagged again
7
+ - `Style/DisallowDelegate` — own state read through an explicit `self` is no longer flagged
8
+ - `Style/DisallowDelegate` — a call reached through safe navigation counts as the call it is, on both the receiver and the argument side
9
+
1
10
  ## [0.8.3] - 2026-08-06
2
11
 
3
12
  ### Fixed
@@ -141,7 +141,7 @@ module RuboCop
141
141
  #
142
142
  # Example:
143
143
  # "UserAccount" → "user_account"
144
- # "PlanStatementAudit::Row" → "plan_statement_audit/row"
144
+ # "InvoiceLineItem::Row" → "invoice_line_item/row"
145
145
  def camel_to_snake(name)
146
146
  return '' if name.nil?
147
147
 
@@ -127,7 +127,7 @@ module RuboCop
127
127
  match && match[1]
128
128
  end
129
129
 
130
- # "UserAccount" → "user_account"; "Plan::Statement" → "plan/statement"
130
+ # "UserAccount" → "user_account"; "Order::LineItem" → "order/line_item"
131
131
  def camel_to_snake(name)
132
132
  name.gsub('::', '/')
133
133
  .gsub(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
@@ -37,34 +37,46 @@ module RuboCop
37
37
  # contributes something the caller would otherwise have to reach in and
38
38
  # take, and the result is a simpler API on the object that owns the data.
39
39
  #
40
- # Three limits keep that exemption from swallowing the rule. The receiver
41
- # must not itself be a chain Remove Middle Man on a message chain IS the
42
- # caller navigating, so composing own state does not excuse it. Own state
43
- # is an instance variable or a receiverless call, so a method parameter
44
- # forwarded through does not qualify and a setter stays flagged. And own
45
- # state is read through a splat, a double splat, an array or a block pass,
46
- # because the wrapper does not change whose state the argument carries.
47
- #
48
- # An argument derived from own state through another call
49
- # `variable.label(value.to_s)` is still flagged. Recursing into an
50
- # argument's receiver would also exempt `Lock.lock_key(company_id:
51
- # user.company_id)`, which is delegation, so the conservative reading wins.
40
+ # Own state is an instance variable, `self`, or a receiverless call,
41
+ # reached directly, through a wrapper, or through a call on it `record`,
42
+ # `record.owner_id` and `[record.owner_id]` all carry the object's own
43
+ # data. A method PARAMETER does not: it arrives as an `lvar`, so a setter
44
+ # handing its argument to a collaborator stays flagged.
45
+ #
46
+ # Two limits keep the exemption from swallowing the rule.
47
+ #
48
+ # The receiver must not itself be a chain. Remove Middle Man on a message
49
+ # chain IS the caller navigating, so composing own state does not excuse
50
+ # it.
51
+ #
52
+ # An argument rooted at the collaborator being forwarded to does not earn
53
+ # the exemption. `author.name(author.locale)` hands the collaborator back
54
+ # its own data, which is the echo this rule forbids, not composition, so
55
+ # with no other argument to carry it the forward stays flagged. Rooted at
56
+ # the object's OWN state the argument may be a chain of any depth — the
57
+ # object still supplied what the collaborator needed, and how far it
58
+ # reached inside itself to build the value is its own business.
59
+ #
60
+ # A forward to a collaborator that passes NO argument is the one that
61
+ # republishes, and it is always flagged. That is the line — supplying data
62
+ # the collaborator needs is composition, echoing back what the collaborator
63
+ # already knows is delegation.
52
64
  #
53
65
  # @example
54
66
  # # bad
55
- # delegate :name, to: :commission
67
+ # delegate :name, to: :author
56
68
  #
57
69
  # # bad — same promise, written by hand
58
70
  # def name
59
- # commission.name
71
+ # author.name
60
72
  # end
61
73
  #
62
74
  # # good — the caller navigates
63
- # statement.commission.name
75
+ # post.author.name
64
76
  #
65
77
  # # good — the object answers about itself, not for a collaborator
66
78
  # def lock_key
67
- # self.class.lock_key(company_id: company_id)
79
+ # self.class.lock_key(owner_id: owner_id)
68
80
  # end
69
81
  #
70
82
  # # good — `each` is the Enumerable contract this class implements
@@ -78,12 +90,22 @@ module RuboCop
78
90
  #
79
91
  # # good — composes its own attribute, so the caller cannot just navigate
80
92
  # def output
81
- # variable.output(value)
93
+ # formatter.output(value)
94
+ # end
95
+ #
96
+ # # good — names an expression built from its own record; not a forward
97
+ # def lock_key
98
+ # Registry.lock_key(owner_id: record.owner_id)
82
99
  # end
83
100
  #
84
101
  # # bad — a chain does not stop being a chain because an argument rode along
85
102
  # def starts_at
86
- # commission.plan.period.starts_at(calendar)
103
+ # schedule.window.period.starts_at(calendar)
104
+ # end
105
+ #
106
+ # # bad — the argument is the collaborator's own data handed back to it
107
+ # def name
108
+ # author.name(author.locale)
87
109
  # end
88
110
  #
89
111
  class DisallowDelegate < ::RuboCop::Cop::Base
@@ -154,32 +176,43 @@ module RuboCop
154
176
  mixes_in_enumerable?(body)
155
177
  end
156
178
 
157
- def composes_own_state?(body)
158
- return false if chained_receiver?(body.receiver)
159
-
160
- body.arguments.any? { |argument| own_state?(argument) }
161
- end
162
-
163
179
  # Remove Middle Man on a message chain IS the caller navigating, so an
164
180
  # argument riding along does not earn the exemption.
165
- def chained_receiver?(receiver)
166
- return false unless receiver.send_type?
181
+ def composes_own_state?(body)
182
+ return false if call_with_receiver?(body.receiver)
167
183
 
168
- !receiver.receiver.nil?
184
+ body.arguments.any? { |argument| own_state?(argument, body.receiver) }
169
185
  end
170
186
 
171
187
  # A method parameter reaches the body as an `lvar`, so only an instance
172
- # variable or a receiverless call counts as the object's own state. An
173
- # anonymous block pass carries a nil child, hence the first guard.
174
- def own_state?(argument)
175
- return false if argument.nil?
188
+ # variable, `self`, or a receiverless call counts as the object's own
189
+ # state read directly, through a wrapper, or through a call on it.
190
+ # Anything rooted at the collaborator is that collaborator's own data
191
+ # coming back, which is the echo the rule forbids. The first guard
192
+ # refuses anything that is not a node, which covers both the nil child
193
+ # an anonymous block pass carries and a wrapper whose children are raw
194
+ # Ruby values rather than nodes.
195
+ def own_state?(argument, collaborator)
196
+ return false unless argument.is_a?(::RuboCop::AST::Node)
197
+ return false if argument == collaborator
176
198
  return true if argument.ivar_type?
177
- return true if argument.send_type? && argument.receiver.nil?
178
- return argument.children.any? { |child| own_state?(child) } if wrapper?(argument)
199
+ return true if argument.self_type?
200
+ return own_state?(argument.receiver, collaborator) if call_with_receiver?(argument)
201
+ return true if argument.send_type?
202
+ return argument.children.any? { |child| own_state?(child, collaborator) } if wrapper?(argument)
179
203
 
180
204
  false
181
205
  end
182
206
 
207
+ # `call_type?` is the `send`/`csend` union, so safe navigation counts as
208
+ # the call it is — a `&.` link neither escapes the chain limit nor stops
209
+ # own state from being recognized.
210
+ def call_with_receiver?(node)
211
+ return false unless node.call_type?
212
+
213
+ !node.receiver.nil?
214
+ end
215
+
183
216
  def wrapper?(argument)
184
217
  ARGUMENT_WRAPPERS.include?(argument.type)
185
218
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Fourshark
5
- VERSION = '0.8.3'
5
+ VERSION = '0.8.4'
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.3
4
+ version: 0.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Ribeiro