Ruby supports two forms of objectified methods. Class
Method
is used to represent methods that are associated with a particular object: these method objects are bound to that object. Bound method objects for an object can be created using Object#method
.
Ruby also supports unbound methods; methods objects that are not associated with a particular object. These can be created either by calling Module#instance_method
or by calling unbind on a bound method object. The result of both of these is an UnboundMethod
object.
Unbound methods can only be called after they are bound to an object. That object must be a kind_of? the method’s original class.
class Square
def area
@side * @side
end
def initialize(side)
@side = side
end
end
area_un = Square.instance_method(:area)
s = Square.new(12)
area = area_un.bind(s)
area.call #=> 144
Unbound methods are a reference to the method at the time it was objectified: subsequent changes to the underlying class will not affect the unbound method.
class Test
def test
:original
end
end
um = Test.instance_method(:test)
class Test
def test
:modified
end
end
t = Test.new
t.test #=> :modified
um.bind(t).call #=> :original
- #
- A
- B
- C
- E
- H
- I
- N
- O
- P
- S
- T
Instance Public methods
meth == other_meth → true or false Link
Two unbound method objects are equal if they refer to the same method definition.
Array.instance_method(:each_slice) == Enumerable.instance_method(:each_slice)
#=> true
Array.instance_method(:sum) == Enumerable.instance_method(:sum)
#=> false, Array redefines the method for efficiency
Source: show
#define unbound_method_eq method_eq
meth.arity → integer Link
Returns an indication of the number of arguments accepted by a method. Returns a nonnegative integer for methods that take a fixed number of arguments. For Ruby methods that take a variable number of arguments, returns -n-1, where n is the number of required arguments. Keyword arguments will be considered as a single additional argument, that argument being mandatory if any keyword argument is mandatory. For methods written in C, returns -1 if the call takes a variable number of arguments.
class C
def one; end
def two(a); end
def three(*a); end
def four(a, b); end
def five(a, b, *c); end
def six(a, b, *c, &d); end
def seven(a, b, x:0); end
def eight(x:, y:); end
def nine(x:, y:, **z); end
def ten(*a, x:, y:); end
end
c = C.new
c.method(:one).arity #=> 0
c.method(:two).arity #=> 1
c.method(:three).arity #=> -1
c.method(:four).arity #=> 2
c.method(:five).arity #=> -3
c.method(:six).arity #=> -3
c.method(:seven).arity #=> -3
c.method(:eight).arity #=> 1
c.method(:nine).arity #=> 1
c.method(:ten).arity #=> -2
"cat".method(:size).arity #=> 0
"cat".method(:replace).arity #=> 1
"cat".method(:squeeze).arity #=> -1
"cat".method(:count).arity #=> -1
Source: show
static VALUE method_arity_m(VALUE method) { int n = method_arity(method); return INT2FIX(n); }
umeth.bind(obj) → method Link
Bind umeth to obj. If Klass was the class from which umeth was obtained, obj.kind_of?(Klass)
must be true.
class A
def test
puts "In test, class = #{self.class}"
end
end
class B < A
end
class C < B
end
um = B.instance_method(:test)
bm = um.bind(C.new)
bm.call
bm = um.bind(B.new)
bm.call
bm = um.bind(A.new)
bm.call
produces:
In test, class = C
In test, class = B
prog.rb:16:in `bind': bind argument must be an instance of B (TypeError)
from prog.rb:16
Source: show
static VALUE umethod_bind(VALUE method, VALUE recv) { VALUE methclass, klass, iclass; const rb_method_entry_t *me; const struct METHOD *data; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, true); struct METHOD *bound; method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound); RB_OBJ_WRITE(method, &bound->recv, recv); RB_OBJ_WRITE(method, &bound->klass, klass); RB_OBJ_WRITE(method, &bound->iclass, iclass); RB_OBJ_WRITE(method, &bound->owner, methclass); RB_OBJ_WRITE(method, &bound->me, me); return method; }
umeth.bind_call(recv, args, ...) → obj Link
Bind umeth to recv and then invokes the method with the specified arguments. This is semantically equivalent to umeth.bind(recv).call(args, ...)
.
Source: show
static VALUE umethod_bind_call(int argc, VALUE *argv, VALUE method) { rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); VALUE recv = argv[0]; argc--; argv++; VALUE passed_procval = rb_block_given_p() ? rb_block_proc() : Qnil; rb_execution_context_t *ec = GET_EC(); const struct METHOD *data; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); const rb_callable_method_entry_t *cme = rb_callable_method_entry(CLASS_OF(recv), data->me->called_id); if (data->me == (const rb_method_entry_t *)cme) { vm_passed_block_handler_set(ec, proc_to_block_handler(passed_procval)); return rb_vm_call_kw(ec, recv, cme->called_id, argc, argv, cme, RB_PASS_CALLED_KEYWORDS); } else { VALUE methclass, klass, iclass; const rb_method_entry_t *me; convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, false); struct METHOD bound = { recv, klass, 0, methclass, me }; return call_method_data(ec, &bound, argc, argv, passed_procval, RB_PASS_CALLED_KEYWORDS); } }
method.clone → new_method Link
Returns a clone of this method.
class A
def foo
return "bar"
end
end
m = A.new.method(:foo)
m.call # => "bar"
n = m.clone.call # => "bar"
Source: show
static VALUE method_clone(VALUE self) { VALUE clone; struct METHOD *orig, *data; TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig); clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data); rb_obj_clone_setup(self, clone, Qnil); RB_OBJ_WRITE(clone, &data->recv, orig->recv); RB_OBJ_WRITE(clone, &data->klass, orig->klass); RB_OBJ_WRITE(clone, &data->iclass, orig->iclass); RB_OBJ_WRITE(clone, &data->owner, orig->owner); RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me)); return clone; }
meth.eql?(other_meth) → true or false Link
Two unbound method objects are equal if they refer to the same method definition.
Array.instance_method(:each_slice) == Enumerable.instance_method(:each_slice)
#=> true
Array.instance_method(:sum) == Enumerable.instance_method(:sum)
#=> false, Array redefines the method for efficiency
meth.hash → integer Link
Returns a hash value corresponding to the method object.
See also Object#hash
.
Source: show
static VALUE method_hash(VALUE method) { struct METHOD *m; st_index_t hash; TypedData_Get_Struct(method, struct METHOD, &method_data_type, m); hash = rb_hash_start((st_index_t)m->recv); hash = rb_hash_method_entry(hash, m->me); hash = rb_hash_end(hash); return ST2FIX(hash); }
meth.inspect → string Link
Returns a human-readable description of the underlying method.
"cat".method(:count).inspect #=> "#<Method: String#count(*)>"
(1..3).method(:map).inspect #=> "#<Method: Range(Enumerable)#map()>"
In the latter case, the method description includes the “owner” of the original method (Enumerable
module, which is included into Range
).
inspect
also provides, when possible, method argument names (call sequence) and source location.
require 'net/http'
Net::HTTP.method(:get).inspect
#=> "#<Method: Net::HTTP.get(uri_or_host, path=..., port=...) <skip>/lib/ruby/2.7.0/net/http.rb:457>"
...
in argument definition means argument is optional (has some default value).
For methods defined in C (language core and extensions), location and argument names can’t be extracted, and only generic information is provided in form of *
(any number of arguments) or _
(some positional argument).
"cat".method(:count).inspect #=> "#<Method: String#count(*)>"
"cat".method(:+).inspect #=> "#<Method: String#+(_)>""
Source: show
static VALUE method_inspect(VALUE method) { struct METHOD *data; VALUE str; const char *sharp = "#"; VALUE mklass; VALUE defined_class; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); str = rb_sprintf("#<% "PRIsVALUE": ", rb_obj_class(method)); mklass = data->iclass; if (!mklass) mklass = data->klass; if (RB_TYPE_P(mklass, T_ICLASS)) { /* TODO: I'm not sure why mklass is T_ICLASS. * UnboundMethod#bind() can set it as T_ICLASS at convert_umethod_to_method_components() * but not sure it is needed. */ mklass = RBASIC_CLASS(mklass); } if (data->me->def->type == VM_METHOD_TYPE_ALIAS) { defined_class = data->me->def->body.alias.original_me->owner; } else { defined_class = method_entry_defined_class(data->me); } if (RB_TYPE_P(defined_class, T_ICLASS)) { defined_class = RBASIC_CLASS(defined_class); } if (data->recv == Qundef) { // UnboundMethod rb_str_buf_append(str, rb_inspect(defined_class)); } else if (FL_TEST(mklass, FL_SINGLETON)) { VALUE v = RCLASS_ATTACHED_OBJECT(mklass); if (UNDEF_P(data->recv)) { rb_str_buf_append(str, rb_inspect(mklass)); } else if (data->recv == v) { rb_str_buf_append(str, rb_inspect(v)); sharp = "."; } else { rb_str_buf_append(str, rb_inspect(data->recv)); rb_str_buf_cat2(str, "("); rb_str_buf_append(str, rb_inspect(v)); rb_str_buf_cat2(str, ")"); sharp = "."; } } else { mklass = data->klass; if (FL_TEST(mklass, FL_SINGLETON)) { VALUE v = RCLASS_ATTACHED_OBJECT(mklass); if (!(RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE))) { do { mklass = RCLASS_SUPER(mklass); } while (RB_TYPE_P(mklass, T_ICLASS)); } } rb_str_buf_append(str, rb_inspect(mklass)); if (defined_class != mklass) { rb_str_catf(str, "(% "PRIsVALUE")", defined_class); } } rb_str_buf_cat2(str, sharp); rb_str_append(str, rb_id2str(data->me->called_id)); if (data->me->called_id != data->me->def->original_id) { rb_str_catf(str, "(%"PRIsVALUE")", rb_id2str(data->me->def->original_id)); } if (data->me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) { rb_str_buf_cat2(str, " (not-implemented)"); } // parameter information { VALUE params = rb_method_parameters(method); VALUE pair, name, kind; const VALUE req = ID2SYM(rb_intern("req")); const VALUE opt = ID2SYM(rb_intern("opt")); const VALUE keyreq = ID2SYM(rb_intern("keyreq")); const VALUE key = ID2SYM(rb_intern("key")); const VALUE rest = ID2SYM(rb_intern("rest")); const VALUE keyrest = ID2SYM(rb_intern("keyrest")); const VALUE block = ID2SYM(rb_intern("block")); const VALUE nokey = ID2SYM(rb_intern("nokey")); int forwarding = 0; rb_str_buf_cat2(str, "("); if (RARRAY_LEN(params) == 3 && RARRAY_AREF(RARRAY_AREF(params, 0), 0) == rest && RARRAY_AREF(RARRAY_AREF(params, 0), 1) == ID2SYM('*') && RARRAY_AREF(RARRAY_AREF(params, 1), 0) == keyrest && RARRAY_AREF(RARRAY_AREF(params, 1), 1) == ID2SYM(idPow) && RARRAY_AREF(RARRAY_AREF(params, 2), 0) == block && RARRAY_AREF(RARRAY_AREF(params, 2), 1) == ID2SYM('&')) { forwarding = 1; } for (int i = 0; i < RARRAY_LEN(params); i++) { pair = RARRAY_AREF(params, i); kind = RARRAY_AREF(pair, 0); name = RARRAY_AREF(pair, 1); // FIXME: in tests it turns out that kind, name = [:req] produces name to be false. Why?.. if (NIL_P(name) || name == Qfalse) { // FIXME: can it be reduced to switch/case? if (kind == req || kind == opt) { name = rb_str_new2("_"); } else if (kind == rest || kind == keyrest) { name = rb_str_new2(""); } else if (kind == block) { name = rb_str_new2("block"); } else if (kind == nokey) { name = rb_str_new2("nil"); } } if (kind == req) { rb_str_catf(str, "%"PRIsVALUE, name); } else if (kind == opt) { rb_str_catf(str, "%"PRIsVALUE"=...", name); } else if (kind == keyreq) { rb_str_catf(str, "%"PRIsVALUE":", name); } else if (kind == key) { rb_str_catf(str, "%"PRIsVALUE": ...", name); } else if (kind == rest) { if (name == ID2SYM('*')) { rb_str_cat_cstr(str, forwarding ? "..." : "*"); } else { rb_str_catf(str, "*%"PRIsVALUE, name); } } else if (kind == keyrest) { if (name != ID2SYM(idPow)) { rb_str_catf(str, "**%"PRIsVALUE, name); } else if (i > 0) { rb_str_set_len(str, RSTRING_LEN(str) - 2); } else { rb_str_cat_cstr(str, "**"); } } else if (kind == block) { if (name == ID2SYM('&')) { if (forwarding) { rb_str_set_len(str, RSTRING_LEN(str) - 2); } else { rb_str_cat_cstr(str, "..."); } } else { rb_str_catf(str, "&%"PRIsVALUE, name); } } else if (kind == nokey) { rb_str_buf_cat2(str, "**nil"); } if (i < RARRAY_LEN(params) - 1) { rb_str_buf_cat2(str, ", "); } } rb_str_buf_cat2(str, ")"); } { // source location VALUE loc = rb_method_location(method); if (!NIL_P(loc)) { rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE, RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1)); } } rb_str_buf_cat2(str, ">"); return str; }
meth.name → symbol Link
Returns the name of the method.
Source: show
static VALUE method_name(VALUE obj) { struct METHOD *data; TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); return ID2SYM(data->me->called_id); }
meth.original_name → symbol Link
Returns the original name of the method.
class C
def foo; end
alias bar foo
end
C.instance_method(:bar).original_name # => :foo
Source: show
static VALUE method_original_name(VALUE obj) { struct METHOD *data; TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); return ID2SYM(data->me->def->original_id); }
meth.owner → class_or_module Link
Returns the class or module on which this method is defined. In other words,
meth.owner.instance_methods(false).include?(meth.name) # => true
holds as long as the method is not removed/undefined/replaced, (with private_instance_methods instead of instance_methods if the method is private).
See also Method#receiver
.
(1..3).method(:map).owner #=> Enumerable
Source: show
static VALUE method_owner(VALUE obj) { struct METHOD *data; TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); return data->owner; }
meth.parameters → array Link
Returns the parameter information of this method.
def foo(bar); end
method(:foo).parameters #=> [[:req, :bar]]
def foo(bar, baz, bat, &blk); end
method(:foo).parameters #=> [[:req, :bar], [:req, :baz], [:req, :bat], [:block, :blk]]
def foo(bar, *args); end
method(:foo).parameters #=> [[:req, :bar], [:rest, :args]]
def foo(bar, baz, *args, &blk); end
method(:foo).parameters #=> [[:req, :bar], [:req, :baz], [:rest, :args], [:block, :blk]]
Source: show
static VALUE rb_method_parameters(VALUE method) { return method_def_parameters(rb_method_def(method)); }
meth.source_location → [String, Integer] Link
Returns the Ruby source filename and line number containing this method or nil if this method was not defined in Ruby (i.e. native).
Source: show
VALUE rb_method_location(VALUE method) { return method_def_location(rb_method_def(method)); }
meth.super_method → method Link
Returns a Method
of superclass which would be called when super is used or nil if there is no method on superclass.
Source: show
static VALUE method_super_method(VALUE method) { const struct METHOD *data; VALUE super_class, iclass; ID mid; const rb_method_entry_t *me; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); iclass = data->iclass; if (!iclass) return Qnil; if (data->me->def->type == VM_METHOD_TYPE_ALIAS && data->me->defined_class) { super_class = RCLASS_SUPER(rb_find_defined_class_by_owner(data->me->defined_class, data->me->def->body.alias.original_me->owner)); mid = data->me->def->body.alias.original_me->def->original_id; } else { super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass)); mid = data->me->def->original_id; } if (!super_class) return Qnil; me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, mid, &iclass); if (!me) return Qnil; return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE); }
meth.to_s → string Link
Returns a human-readable description of the underlying method.
"cat".method(:count).inspect #=> "#<Method: String#count(*)>"
(1..3).method(:map).inspect #=> "#<Method: Range(Enumerable)#map()>"
In the latter case, the method description includes the “owner” of the original method (Enumerable
module, which is included into Range
).
inspect
also provides, when possible, method argument names (call sequence) and source location.
require 'net/http'
Net::HTTP.method(:get).inspect
#=> "#<Method: Net::HTTP.get(uri_or_host, path=..., port=...) <skip>/lib/ruby/2.7.0/net/http.rb:457>"
...
in argument definition means argument is optional (has some default value).
For methods defined in C (language core and extensions), location and argument names can’t be extracted, and only generic information is provided in form of *
(any number of arguments) or _
(some positional argument).
"cat".method(:count).inspect #=> "#<Method: String#count(*)>"
"cat".method(:+).inspect #=> "#<Method: String#+(_)>""