Class: RuboCop::Cop::DevDoc::Style::PreferPublicSend
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::DevDoc::Style::PreferPublicSend
- Defined in:
- lib/rubocop/cop/dev_doc/style/prefer_public_send.rb
Overview
Prefer ‘public_send` over `send` when dispatching dynamically.
## Rationale ‘public_send` respects Ruby’s method visibility. When the dispatched name should resolve to a public API method (the common case for generic validators, form helpers, attribute readers, etc.), using ‘public_send`:
-
**Surfaces typos / misconfig immediately.** A name that drifts to a private internal raises ‘NoMethodError` instead of silently invoking it.
-
**Future-proofs the call site.** If the target method later moves to private (refactor, contract narrowing), ‘public_send` raises on the next run; `send` keeps silently working — potentially masking a real bug.
-
**Documents intent.** A reader of ‘public_send` knows the author meant a public-API call. `send` leaves it ambiguous whether private access was wanted.
## Relationship to AvoidSend ‘DevDoc/Style/AvoidSend` flags dynamic dispatch (both `send` and `public_send`) as risky. This cop is orthogonal: it flags `send` specifically, including the cases AvoidSend exempts (literal-symbol args, prefix-string args). The friction asymmetry is intentional —`send` is the deeper exception, so it costs an extra disable:
obj.public_send(method_name) # AvoidSend: 1 disable
obj.public_send(:foo) # clean
obj.send(method_name) # AvoidSend + this cop: 2 disables
obj.send(:foo) # this cop only: 1 disable
## When ‘send` IS the right choice Reach for `send` only when you have an articulable reason to bypass visibility — and leave a comment so the next reader knows it’s deliberate:
-
Tests calling a private helper:
# rubocop:disable DevDoc/Style/PreferPublicSend -- unit-testing private method assert_equal 5, calculator.send(:internal_carry) # rubocop:enable DevDoc/Style/PreferPublicSend -
**Framework / introspection** code that intentionally calls private callbacks (e.g. ‘record.send(:_run_save_callbacks)`).
## Receiver-less ‘send` is exempt `send(:foo)` (no explicit receiver) calls a method on `self` and is commonly used inside a class to dispatch to its own private methods. Rewriting to `public_send` would either change semantics (the method must become public) or fail. Same exemption as `AvoidSend`.
Constant Summary collapse
- MSG =
'Prefer `public_send` over `send` — it respects method ' \ 'visibility, surfacing typos/misconfig that would otherwise ' \ 'silently invoke a private method. Disable with a reason ' \ 'when bypassing visibility is intentional.'.freeze
- RESTRICT_ON_SEND =
%i[send].freeze
Instance Method Summary collapse
Instance Method Details
#on_send(node) ⇒ Object
77 78 79 80 81 |
# File 'lib/rubocop/cop/dev_doc/style/prefer_public_send.rb', line 77 def on_send(node) return if node.receiver.nil? add_offense(node.loc.selector) end |