class UnboundMethod
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 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
Public Instance Methods
Two method objects are equal if they are bound to the same object and refer to the same method definition and their owners are the same class or module.
static VALUE method_eq(VALUE method, VALUE other) { struct METHOD *m1, *m2; if (!rb_obj_is_method(other)) return Qfalse; if (CLASS_OF(method) != CLASS_OF(other)) return Qfalse; Check_TypedStruct(method, &method_data_type); m1 = (struct METHOD *)DATA_PTR(method); m2 = (struct METHOD *)DATA_PTR(other); if (!rb_method_entry_eq(m1->me, m2->me) || m1->rclass != m2->rclass || m1->recv != m2->recv) { return Qfalse; } return Qtrue; }
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. 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 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 "cat".method(:size).arity #=> 0 "cat".method(:replace).arity #=> 1 "cat".method(:squeeze).arity #=> -1 "cat".method(:count).arity #=> -1
static VALUE method_arity_m(VALUE method) { int n = method_arity(method); return INT2FIX(n); }
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
static VALUE umethod_bind(VALUE method, VALUE recv) { struct METHOD *data, *bound; VALUE methclass; VALUE rclass; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); methclass = data->rclass; if (!RB_TYPE_P(methclass, T_MODULE) && methclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, methclass)) { if (FL_TEST(methclass, FL_SINGLETON)) { rb_raise(rb_eTypeError, "singleton method called for a different object"); } else { rb_raise(rb_eTypeError, "bind argument must be an instance of % "PRIsVALUE, rb_class_name(methclass)); } } method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound); *bound = *data; bound->me = ALLOC(rb_method_entry_t); *bound->me = *data->me; if (bound->me->def) bound->me->def->alias_count++; rclass = CLASS_OF(recv); if (BUILTIN_TYPE(bound->defined_class) == T_MODULE) { VALUE ic = rb_class_search_ancestor(rclass, bound->defined_class); if (ic) { rclass = ic; } else { rclass = rb_include_class_new(methclass, rclass); } } bound->recv = recv; bound->rclass = rclass; data->ume = ALLOC(struct unlinked_method_entry_list_entry); return method; }
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"
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); CLONESETUP(clone, self); *data = *orig; data->me = ALLOC(rb_method_entry_t); *data->me = *orig->me; if (data->me->def) data->me->def->alias_count++; data->ume = ALLOC(struct unlinked_method_entry_list_entry); return clone; }
Two method objects are equal if they are bound to the same object and refer to the same method definition and their owners are the same class or module.
static VALUE method_eq(VALUE method, VALUE other) { struct METHOD *m1, *m2; if (!rb_obj_is_method(other)) return Qfalse; if (CLASS_OF(method) != CLASS_OF(other)) return Qfalse; Check_TypedStruct(method, &method_data_type); m1 = (struct METHOD *)DATA_PTR(method); m2 = (struct METHOD *)DATA_PTR(other); if (!rb_method_entry_eq(m1->me, m2->me) || m1->rclass != m2->rclass || m1->recv != m2->recv) { return Qfalse; } return Qtrue; }
Returns a hash value corresponding to the method object.
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->rclass); hash = rb_hash_uint(hash, (st_index_t)m->recv); hash = rb_hash_method_entry(hash, m->me); hash = rb_hash_end(hash); return INT2FIX(hash); }
Returns the name of the underlying method.
"cat".method(:count).inspect #=> "#<Method: String#count>"
static VALUE method_inspect(VALUE method) { struct METHOD *data; VALUE str; const char *s; const char *sharp = "#"; VALUE mklass; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); str = rb_str_buf_new2("#<"); s = rb_obj_classname(method); rb_str_buf_cat2(str, s); rb_str_buf_cat2(str, ": "); mklass = data->me->klass; if (FL_TEST(mklass, FL_SINGLETON)) { VALUE v = rb_ivar_get(mklass, attached); if (data->recv == Qundef) { 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 { rb_str_buf_append(str, rb_class_name(data->rclass)); if (data->rclass != mklass) { rb_str_buf_cat2(str, "("); rb_str_buf_append(str, rb_class_name(mklass)); rb_str_buf_cat2(str, ")"); } } rb_str_buf_cat2(str, sharp); rb_str_append(str, rb_id2str(data->id)); if (data->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)"); } rb_str_buf_cat2(str, ">"); return str; }
Returns the name of the method.
static VALUE method_name(VALUE obj) { struct METHOD *data; TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); return ID2SYM(data->id); }
Returns the original name of the method.
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); }
Returns the class or module that defines the method.
static VALUE method_owner(VALUE obj) { struct METHOD *data; VALUE defined_class; TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data); defined_class = data->defined_class; if (RB_TYPE_P(defined_class, T_ICLASS)) { defined_class = RBASIC_CLASS(defined_class); } return defined_class; }
Returns the parameter information of this method.
static VALUE rb_method_parameters(VALUE method) { rb_iseq_t *iseq = rb_method_get_iseq(method); if (!iseq) { return unnamed_parameters(method_arity(method)); } return rb_iseq_parameters(iseq, 0); }
Returns the Ruby source filename and line number containing this method or nil if this method was not defined in Ruby (i.e. native)
VALUE rb_method_location(VALUE method) { rb_method_definition_t *def = method_get_def(method); return method_def_location(def); }
Returns the name of the underlying method.
"cat".method(:count).inspect #=> "#<Method: String#count>"
static VALUE method_inspect(VALUE method) { struct METHOD *data; VALUE str; const char *s; const char *sharp = "#"; VALUE mklass; TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); str = rb_str_buf_new2("#<"); s = rb_obj_classname(method); rb_str_buf_cat2(str, s); rb_str_buf_cat2(str, ": "); mklass = data->me->klass; if (FL_TEST(mklass, FL_SINGLETON)) { VALUE v = rb_ivar_get(mklass, attached); if (data->recv == Qundef) { 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 { rb_str_buf_append(str, rb_class_name(data->rclass)); if (data->rclass != mklass) { rb_str_buf_cat2(str, "("); rb_str_buf_append(str, rb_class_name(mklass)); rb_str_buf_cat2(str, ")"); } } rb_str_buf_cat2(str, sharp); rb_str_append(str, rb_id2str(data->id)); if (data->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)"); } rb_str_buf_cat2(str, ">"); return str; }