rubocop-fourshark 0.7.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 49c5ac14dc35386b05d887fcd8b53f2ebafd991014046fef3df088a8c4174816
4
- data.tar.gz: 953643562da8fcdda58cd4482246328baddff15ac4ee881468defaaf669ce218
3
+ metadata.gz: a3715ce918bf2499ae19801f42db4966430bc7a2ae33aae165bc7676865bdfc3
4
+ data.tar.gz: 48d1eac2b33b6971b80463057f859d4f2cbc5efa5d03d298484a9819cb1c1989
5
5
  SHA512:
6
- metadata.gz: b0bc1b6c88559d9b780d05bfb1f052a67b99f5eebc0be6db93e6aa934e9c62cf34ecc594c9549887dee3089874c7c733320781e5025dd664aa0642ae8a211cc4
7
- data.tar.gz: 91995ae5a6cabfccdb2374540e27d6dc3f481cbbc4ac54fc888359a237186400dda1e605de34e5169fb304bf3ec2b22ad78a3e80af83ba7ce1823eec69716cbc
6
+ metadata.gz: 2cb23a767a11947a89a3792c32cb9a3da0e888c83ebd377447b71e9ec21c4643a3105bf361d445fdd1d942b3f92894e68989efcceed7ba4946077f882cdb36b4
7
+ data.tar.gz: c9f15b6c4a0a3340ead5387783a0585423c44a3e299143910c145d3bf086d15924a2d0e93a87213ff2da8f059c6cd897fd05a7f04fe9d6ff29b0517e9d00885b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.8.0] - 2026-08-05
2
+
3
+ ### Changed
4
+
5
+ - `Style/DisallowDelegate` — a method that forwards its own name to a collaborator is flagged
6
+
7
+ ## [0.7.1] - 2026-08-05
8
+
9
+ ### Removed
10
+
11
+ - `Style/ConditionalAssignment` configuration — the stock default governs assignment from a conditional again
12
+
1
13
  ## [0.7.0] - 2026-08-05
2
14
 
3
15
  ### Added
data/README.md CHANGED
@@ -76,7 +76,6 @@ Where a 4Shark cop supersedes or contradicts a stock cop, `config/default.yml` t
76
76
 
77
77
  | Cop | Intent |
78
78
  |---|---|
79
- | `Style/ConditionalAssignment` | When a conditional decides a value, each branch assigns the variable. The cop's default (`assign_to_condition`) demands `x = if ... else ... end`, the one form the no-ternary convention rejects — converting a ternary would land straight on it. Ternaries are out of scope (`IncludeTernaryExpressions: false`): `Style/DisallowTernary` bans them, and this cop's ternary autocorrect only produces another ternary. Stock cop, configured via `EnforcedStyle`. |
80
79
  | `Style/DisallowDelegate` | Flags automatic delegation (`delegate`, `delegate_missing_to`, `def_delegator(s)`, `DelegateClass`). A `delegate` line is a macro call, not a definition, so `grep 'def foo'` and jump-to-definition find nothing — the community name for the smell is Fowler's Middle Man. Write the method, or let the caller ask the object that knows. |
81
80
  | `Style/DisallowSafeNavigation` | Flags safe navigation (`&.`). 4Shark rejects it — a `&.` chain silently swallows a `nil` that usually signals a real bug. Use an explicit conditional so the `nil` case is handled on purpose. |
82
81
  | `Style/DisallowTernary` | Flags the ternary conditional (`cond ? a : b`). `?` and `:` are punctuation that already mean other things in Ruby (`valid?`, `:symbol`), and the ternary hides a branch inside an expression where skimming misses it. Use an explicit `if`/`else`. |
data/config/default.yml CHANGED
@@ -22,17 +22,6 @@ Layout/MultilineMethodCallIndentation:
22
22
  Naming/RescuedExceptionsVariableName:
23
23
  PreferredName: exception
24
24
 
25
- # Stock cop configured (not a 4Shark cop): when a conditional decides a value,
26
- # each branch assigns the variable. The cop's default (`assign_to_condition`)
27
- # demands `x = if ... else ... end`, the one form the 4Shark no-ternary
28
- # convention rejects — converting a ternary would land straight on it.
29
- # Ternaries are out of scope: `Style/DisallowTernary` bans them outright, and
30
- # this cop's ternary autocorrect produces another ternary (`cond ? x = a : x = b`),
31
- # which stays an offense that no autocorrect can clear.
32
- Style/ConditionalAssignment:
33
- EnforcedStyle: assign_inside_condition
34
- IncludeTernaryExpressions: false
35
-
36
25
  Style/DisallowDelegate:
37
26
  Description: 'Do not use automatic delegation. Write the method explicitly.'
38
27
  Enabled: true
@@ -35,11 +35,11 @@ module RuboCop
35
35
  body = node.body
36
36
  return unless body
37
37
 
38
- if body.begin_type?
39
- statements = body.children
40
- else
41
- statements = [body]
42
- end
38
+ statements = if body.begin_type?
39
+ body.children
40
+ else
41
+ [body]
42
+ end
43
43
 
44
44
  statements.each do |statement|
45
45
  add_offense(statement.loc.selector) if association_call?(statement)
@@ -47,22 +47,22 @@ module RuboCop
47
47
 
48
48
  opposite_content = File.read(opposite_model_path)
49
49
 
50
- if inverse_name
51
- expected_inverse = inverse_name.to_s
52
- else
53
- expected_inverse = camel_to_snake(model_name).split('/').last.pluralize
54
- end
50
+ expected_inverse = if inverse_name
51
+ inverse_name.to_s
52
+ else
53
+ camel_to_snake(model_name).split('/').last.pluralize
54
+ end
55
55
 
56
56
  unless /(belongs_to|has_many|has_one)\s+:#{expected_inverse}/.match?(opposite_content)
57
57
  add_global_offense("#{target_class} is missing opposite association for #{model_name}")
58
58
  end
59
59
  end
60
60
  rescue StandardError => exception
61
- if processed_source && processed_source.buffer
62
- source_name = processed_source.file_path
63
- else
64
- source_name = 'unknown'
65
- end
61
+ source_name = if processed_source && processed_source.buffer
62
+ processed_source.file_path
63
+ else
64
+ 'unknown'
65
+ end
66
66
 
67
67
  warn "Rails/BidirectionalAssociation failed on #{source_name}: #{exception.message}"
68
68
  end
@@ -42,11 +42,11 @@ module RuboCop
42
42
 
43
43
  add_offense(node.loc.selector) if missing_inverse_of
44
44
  rescue StandardError => exception
45
- if processed_source && processed_source.buffer
46
- source_name = processed_source.file_path
47
- else
48
- source_name = 'unknown'
49
- end
45
+ source_name = if processed_source && processed_source.buffer
46
+ processed_source.file_path
47
+ else
48
+ 'unknown'
49
+ end
50
50
 
51
51
  warn "Rails/MandatoryInverseOf failed on #{source_name}: #{exception.message}"
52
52
  end
@@ -39,11 +39,11 @@ module RuboCop
39
39
  body = node.body
40
40
  return unless body
41
41
 
42
- if body.begin_type?
43
- statements = body.children
44
- else
45
- statements = [body]
46
- end
42
+ statements = if body.begin_type?
43
+ body.children
44
+ else
45
+ [body]
46
+ end
47
47
 
48
48
  MACROS.each do |macro|
49
49
  calls = statements.select { |statement| macro_call?(statement, macro) }
@@ -47,11 +47,11 @@ module RuboCop
47
47
  add_offense(node.loc.selector, message: MSG_FORBIDDEN_INVERSE) if chain_has_inverse_of?(node)
48
48
  end
49
49
  rescue StandardError => exception
50
- if processed_source && processed_source.buffer
51
- source_name = processed_source.file_path
52
- else
53
- source_name = 'unknown'
54
- end
50
+ source_name = if processed_source && processed_source.buffer
51
+ processed_source.file_path
52
+ else
53
+ 'unknown'
54
+ end
55
55
 
56
56
  warn "RSpec/InverseOfMatcher failed on #{source_name}: #{exception.message}"
57
57
  end
@@ -65,11 +65,11 @@ module RuboCop
65
65
  body = group.body
66
66
  return false unless body
67
67
 
68
- if body.begin_type?
69
- statements = body.children
70
- else
71
- statements = [body]
72
- end
68
+ statements = if body.begin_type?
69
+ body.children
70
+ else
71
+ [body]
72
+ end
73
73
 
74
74
  statements.any? { |statement| let_named?(statement, name) }
75
75
  end
@@ -77,11 +77,11 @@ module RuboCop
77
77
  def let_named?(statement, name)
78
78
  return false unless statement.is_a?(::RuboCop::AST::Node)
79
79
 
80
- if statement.respond_to?(:send_node)
81
- send = statement.send_node
82
- else
83
- send = statement
84
- end
80
+ send = if statement.respond_to?(:send_node)
81
+ statement.send_node
82
+ else
83
+ statement
84
+ end
85
85
 
86
86
  return false unless send.send_type?
87
87
  return false unless LET_METHODS.include?(send.method_name) && send.receiver.nil?
@@ -5,31 +5,56 @@ require 'rubocop'
5
5
  module RuboCop
6
6
  module Cop
7
7
  module Style
8
- # Forbids automatic delegation — `delegate`, `delegate_missing_to`,
9
- # `def_delegator`/`def_delegators` (Forwardable) and `DelegateClass`.
10
- # A delegation macro is a call, not a definition, so `grep 'def foo'` and
11
- # jump-to-definition find nothing; the community name for the smell is
12
- # Fowler's Middle Man. Write the method, or delete the wrapper and let the
13
- # caller ask the object that knows.
8
+ # Forbids delegation — a class answering a question that belongs to a
9
+ # collaborator. The community name for the smell is Fowler's Middle Man,
10
+ # and the prescribed refactoring is Remove Middle Man: delete the
11
+ # forwarder and let the caller navigate to the object that knows.
12
+ #
13
+ # Two shapes are flagged. The macro family (`delegate`,
14
+ # `delegate_missing_to`, `def_delegator`/`def_delegators`,
15
+ # `DelegateClass`) and an instance method whose entire body forwards the
16
+ # same message to a collaborator. The second is the same defect: writing
17
+ # the forwarder by hand changes what `grep` finds, not what the class
18
+ # claims to be responsible for.
14
19
  #
15
20
  # @example
16
21
  # # bad
17
- # delegate :commission, to: :user_commission
22
+ # delegate :name, to: :commission
18
23
  #
19
- # # good
20
- # def commission
21
- # user_commission.commission
24
+ # # bad — same promise, written by hand
25
+ # def name
26
+ # commission.name
22
27
  # end
23
28
  #
29
+ # # good — the caller navigates
30
+ # statement.commission.name
31
+ #
24
32
  class DisallowDelegate < ::RuboCop::Cop::Base
25
- MSG = 'Do not use automatic delegation. Write the method explicitly instead.'
33
+ MACRO_MSG = 'Do not use automatic delegation. Delete the forwarder and let the caller navigate.'
34
+ FORWARDER_MSG = 'Do not forward a collaborator\'s message. Delete the forwarder and let the caller navigate.'
26
35
 
27
36
  RESTRICT_ON_SEND = %i[delegate delegate_missing_to def_delegator def_delegators DelegateClass].freeze
28
37
 
29
38
  def on_send(node)
30
39
  return unless node.receiver.nil?
31
40
 
32
- add_offense(node)
41
+ add_offense(node, message: MACRO_MSG)
42
+ end
43
+
44
+ def on_def(node)
45
+ return unless forwards_own_message?(node.body, node.method_name)
46
+
47
+ add_offense(node.loc.name, message: FORWARDER_MSG)
48
+ end
49
+
50
+ private
51
+
52
+ def forwards_own_message?(body, method_name)
53
+ return false if body.nil?
54
+ return false unless body.send_type?
55
+ return false if body.receiver.nil?
56
+
57
+ body.method?(method_name)
33
58
  end
34
59
  end
35
60
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Fourshark
5
- VERSION = '0.7.0'
5
+ VERSION = '0.8.0'
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.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Ribeiro