Module: Axn::Webhooks::Outbound::CallableArity
- Defined in:
- lib/axn/webhooks/outbound/callable_arity.rb
Overview
Whether callable.call(*args) actually works for exactly count positional arguments — via
#parameters, which correctly distinguishes required/optional/rest/keyword params uniformly
across a lambda, a non-strict Proc, and a plain object's #call Method. #arity alone can't
tell "one required positional" apart from "one required KEYWORD" (same arity, one raises) or
"needs 2+ positional, has a splat" (negative arity, but still too few args at count == 1) —
both boot-time-valid under a bare arity check, both raising ArgumentError on the very first
real invocation (Codex P2 findings, on backoff, user_agent, and the signing secret).
Class Method Summary collapse
-
.accepted_keywords(callable) ⇒ Object
Which keyword names
callable.call(**kwargs)actually accepts::allfor a callable that double-splats (accepts anything), else the Array of Symbol names it declares (required and optional alike). - .accepts?(callable, count) ⇒ Boolean
-
.accepts_positional?(callable) ⇒ Boolean
Whether
callabledeclares at least one POSITIONAL parameter (required, optional, or rest) -- used bySigner::CustomSignerto detect the historical "single options-hash positional" custom-signer shape (sign { |options| … }, no keyword params at all). -
.prefers_zero_args?(callable) ⇒ Boolean
For a newly-introduced 0-OR-1-arity callable (PRO-3214's per-subscriber
secret/headers): prefer a zero-arg call whenever genuinely possible. -
.raw_arity(callable) ⇒ Object
Shared by
zero_arity?/prefers_zero_args?: raw#arity, falling back toMethod#arityvia#callfor a plain callable object with none of its own. -
.required_keywords(callable) ⇒ Object
Which keyword names
callable.call(**kwargs)REQUIRES — a strict subset ofaccepted_keywords(excludes optional:keyparams). -
.zero_arity?(callable) ⇒ Boolean
The ORIGINAL
subscribers/to:resolver dispatch rule, preserved byte-for-byte (Codex P2 finding): "pass the event unless the callable's raw arity is EXACTLY zero." Deliberately raw #arity, not #parameters-based: a Proc (non-lambda) with a single OPTIONAL/default param reports arity0(a Ruby quirk -- lambda-with-default reports NEGATIVE instead), and that quirk is exactly what a pre-existingproc { |event = :all| … }resolver already relied on to keep using its own default.
Class Method Details
.accepted_keywords(callable) ⇒ Object
Which keyword names callable.call(**kwargs) actually accepts: :all for a callable that
double-splats (accepts anything), else the Array of Symbol names it declares (required and
optional alike). Used to filter a fixed kwarg set down to what a caller-supplied signing
block declares (CustomSigner), so a block written against today's (id:, timestamp:, body:) contract keeps working byte-for-byte when a widened caller starts also offering
url:/subscriber: — those become a plain ArgumentError from a filtered call, not a
silent widening the block didn't ask for.
35 36 37 38 39 40 |
# File 'lib/axn/webhooks/outbound/callable_arity.rb', line 35 def accepted_keywords(callable) params = callable.respond_to?(:parameters) ? callable.parameters : callable.method(:call).parameters return :all if params.any? { |(type, _)| type == :keyrest } params.select { |(type, _)| %i[key keyreq].include?(type) }.map { |(_, name)| name } end |
.accepts?(callable, count) ⇒ Boolean
16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/axn/webhooks/outbound/callable_arity.rb', line 16 def accepts?(callable, count) params = callable.respond_to?(:parameters) ? callable.parameters : callable.method(:call).parameters return false if params.any? { |(type, _)| type == :keyreq } required = params.count { |(type, _)| type == :req } return false if required > count optional = params.count { |(type, _)| type == :opt } rest = params.any? { |(type, _)| type == :rest } required + optional + (rest ? 1 : 0) >= count end |
.accepts_positional?(callable) ⇒ Boolean
Whether callable declares at least one POSITIONAL parameter (required, optional, or
rest) -- used by Signer::CustomSigner to detect the historical "single options-hash
positional" custom-signer shape (sign { |options| … }, no keyword params at all).
92 93 94 95 |
# File 'lib/axn/webhooks/outbound/callable_arity.rb', line 92 def accepts_positional?(callable) params = callable.respond_to?(:parameters) ? callable.parameters : callable.method(:call).parameters params.any? { |(type, _)| %i[req opt rest].include?(type) } end |
.prefers_zero_args?(callable) ⇒ Boolean
For a newly-introduced 0-OR-1-arity callable (PRO-3214's per-subscriber secret/
headers): prefer a zero-arg call whenever genuinely possible. Raw arity, not
#parameters-based accepts?: a plain proc { |subscriber| … } (NO default) reports its
param as :opt via #parameters -- indistinguishable from a genuine default by that
API -- but its raw arity is still the correct POSITIVE 1, so this is the one signal that
tells "has a real default/rest" apart from "merely tolerates a missing arg, Proc-style,
but was never given one to default from" (Codex P1 finding: passing nil in place of the
subscriber for exactly this shape).
ONLY arity 0 (a truly empty/all-defaulted signature) or -1 (Ruby's -(required + 1)
encoding with required == 0 -- zero REQUIRED params, any number of optional/rest ones)
genuinely means "callable with zero args". A more negative arity still has a required
LEADING param: ->(subscriber, cache = nil) is arity -2 (required == 1) and raises if
actually called with zero args -- arity <= 0 wrongly matched it too (Codex P1 finding,
round 5: passed nothing, so a resolver shaped exactly like this raised on every attempt).
79 80 81 |
# File 'lib/axn/webhooks/outbound/callable_arity.rb', line 79 def prefers_zero_args?(callable) [0, -1].include?(raw_arity(callable)) end |
.raw_arity(callable) ⇒ Object
Shared by zero_arity?/prefers_zero_args?: raw #arity, falling back to
Method#arity via #call for a plain callable object with none of its own.
85 86 87 |
# File 'lib/axn/webhooks/outbound/callable_arity.rb', line 85 def raw_arity(callable) callable.respond_to?(:arity) ? callable.arity : callable.method(:call).arity end |
.required_keywords(callable) ⇒ Object
Which keyword names callable.call(**kwargs) REQUIRES — a strict subset of
accepted_keywords (excludes optional :key params). Used to catch a callable that
needs a keyword outside a fixed supplied set (e.g. sign { |id:, vendor:| … }, where
vendor: is never one of the kwargs this gem passes a signer) — the one shape that
genuinely fails on every call, as opposed to a callable that simply ignores some/all of
what it's offered (which Ruby's own Proc/block semantics already tolerate fine).
48 49 50 51 |
# File 'lib/axn/webhooks/outbound/callable_arity.rb', line 48 def required_keywords(callable) params = callable.respond_to?(:parameters) ? callable.parameters : callable.method(:call).parameters params.select { |(type, _)| type == :keyreq }.map { |(_, name)| name } end |
.zero_arity?(callable) ⇒ Boolean
The ORIGINAL subscribers/to: resolver dispatch rule, preserved byte-for-byte (Codex P2
finding): "pass the event unless the callable's raw arity is EXACTLY zero." Deliberately
raw #arity, not #parameters-based: a Proc (non-lambda) with a single OPTIONAL/default
param reports arity 0 (a Ruby quirk -- lambda-with-default reports NEGATIVE instead),
and that quirk is exactly what a pre-existing proc { |event = :all| … } resolver already
relied on to keep using its own default. Only made callable-object-safe here (falls back
to Method#arity via #call) -- the dispatch RULE itself is unchanged.
60 61 62 |
# File 'lib/axn/webhooks/outbound/callable_arity.rb', line 60 def zero_arity?(callable) raw_arity(callable).zero? end |