When I delegate methods on instances of class A
to $delegation_target
as below:
$delegation_target = ""
class A
def method_missing *args, ≺ $delegation_target.send(*args, &pr) end
def respond_to_missing? *args; $delegation_target.respond_to?(*args) end
end
the arity
of the methods on instances of A
returns -1
irrespective of the arity
of the methods on $delegation_target
:
def $delegation_target.foo; end
A.new.method(:foo).arity # => -1
def $delegation_target.foo arg1; end
A.new.method(:foo).arity # => -1
def $delegation_target.foo arg1, arg2; end
A.new.method(:foo).arity # => -1
def $delegation_target.foo arg1, arg2, arg3; end
A.new.method(:foo).arity # => -1
Where does this -1
come from? And, is there a way to make it so that, for any possible method name m
, A.new.method(m).arity
returns the arity of $delegation_target.method(m)
(if it is defined)?
Object#method
handles respond_to_missing?
& method_missing
in a special way. Let's dive into the Ruby C source and see what happens:
Starting at Object#method
, we call mnew
, which creates a new Method
object for the object it's called on and the id passed. In the source for mnew
we can easily see the special handling when the method is not defined, but respond_to_missing?
returns true
when given the id:
if (UNDEFINED_METHOD_ENTRY_P(me)) {
ID rmiss = rb_intern("respond_to_missing?");
VALUE sym = ID2SYM(id);
if (obj != Qundef && !rb_method_basic_definition_p(klass, rmiss)) {
if (RTEST(rb_funcall(obj, rmiss, 2, sym, scope ? Qfalse : Qtrue))) {
def = ALLOC(rb_method_definition_t);
def->type = VM_METHOD_TYPE_MISSING;
def->original_id = id;
// ...
The def->type = VM_METHOD_TYPE_MISSING;
is important. Finding the definition for VM_METHOD_TYPE_MISSING
, we see that it is a "wrapper for method_missing(id)
". So essentially the method that is returned is really just method_missing
, with the first argument already specified as the id of the method you were originally trying to get.
We can confirm our suspicions by verifying that the arity of method_missing
is the same as what we're getting:
A.new.method(:method_missing).arity #=> -1
As an aside, an arity of -1
means that the method can take an unlimited number of arguments.
As for whether you can have it return the "real" arity of the method being called… no, you can't. For one, Ruby makes no assumptions about what happens in your method_missing
, and doesn't even know that it's simply delegating to some other method.