Class: UnboundMethod
Overview
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
Instance Method Summary collapse
-
#==(other_meth) ⇒ Boolean
Two method objects are equal if they are bound to the same object and refer to the same method definition.
-
#arity ⇒ Fixnum
Returns an indication of the number of arguments accepted by a method.
-
#bind(obj) ⇒ Object
Bind umeth to obj.
-
#clone ⇒ Object
MISSING: documentation.
-
#==(other_meth) ⇒ Boolean
Two method objects are equal if they are bound to the same object and refer to the same method definition.
-
#hash ⇒ Integer
Return a hash value corresponding to the method object.
-
#inspect ⇒ Object
Show the name of the underlying method.
-
#name ⇒ Object
Returns the name of the method.
-
#owner ⇒ Object
Returns the class or module that defines the method.
-
#parameters ⇒ Array
returns the parameter information of this method.
-
#source_location ⇒ Array
returns the ruby source filename and line number containing this method or nil if this method was not defined in ruby (i.e. native).
-
#to_s ⇒ Object
Show the name of the underlying method.
Instance Method Details
#==(other_meth) ⇒ Boolean
Two method objects are equal if they are bound to the same object and refer to the same method definition.
|
# File 'proc.c'
/*
* call-seq:
* meth == other_meth -> true or false
*
* Two method objects are equal if they are bound to the same
* object and refer to the same method definition.
*/
static VALUE
method_eq(VALUE method, VALUE other)
{
struct METHOD *m1, *m2;
extern int rb_method_entry_eq(rb_method_entry_t *m1, rb_method_entry_t *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;
}
|
#arity ⇒ Fixnum
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
|
# File 'proc.c'
/*
* call-seq:
* meth.arity -> fixnum
*
* 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(obj) ⇒ Object
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
|
# File 'proc.c'
/*
* call-seq:
* umeth.bind(obj) -> method
*
* Bind <i>umeth</i> to <i>obj</i>. If <code>Klass</code> was the class
* from which <i>umeth</i> was obtained,
* <code>obj.kind_of?(Klass)</code> 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
*
* <em>produces:</em>
*
* 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;
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
if (data->rclass != CLASS_OF(recv) && !rb_obj_is_kind_of(recv, data->rclass)) {
if (FL_TEST(data->rclass, 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 %s",
rb_class2name(data->rclass));
}
}
method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound);
*bound = *data;
if (bound->me.def) bound->me.def->alias_count++;
bound->recv = recv;
bound->rclass = CLASS_OF(recv);
return method;
}
|
#clone ⇒ Object
MISSING: documentation
|
# File 'proc.c'
/*
* MISSING: documentation
*/
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;
if (data->me.def) data->me.def->alias_count++;
return clone;
}
|
#==(other_meth) ⇒ Boolean
Two method objects are equal if they are bound to the same object and refer to the same method definition.
|
# File 'proc.c'
/*
* call-seq:
* meth == other_meth -> true or false
*
* Two method objects are equal if they are bound to the same
* object and refer to the same method definition.
*/
static VALUE
method_eq(VALUE method, VALUE other)
{
struct METHOD *m1, *m2;
extern int rb_method_entry_eq(rb_method_entry_t *m1, rb_method_entry_t *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;
}
|
#hash ⇒ Integer
Return a hash value corresponding to the method object.
|
# File 'proc.c'
/*
* call-seq:
* meth.hash -> integer
*
* Return 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_uint(hash, (st_index_t)m->me.def);
hash = rb_hash_end(hash);
return INT2FIX(hash);
}
|
#to_s ⇒ String #inspect ⇒ String
Show the name of the underlying method.
"cat".method(:count).inspect #=> "#<Method: String#count>"
|
# File 'proc.c'
/*
* call-seq:
* meth.to_s -> string
* meth.inspect -> string
*
* Show 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 = "#";
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, ": ");
if (FL_TEST(data->me.klass, FL_SINGLETON)) {
VALUE v = rb_iv_get(data->me.klass, "__attached__");
if (data->recv == Qundef) {
rb_str_buf_append(str, rb_inspect(data->me.klass));
}
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_cat2(str, rb_class2name(data->rclass));
if (data->rclass != data->me.klass) {
rb_str_buf_cat2(str, "(");
rb_str_buf_cat2(str, rb_class2name(data->me.klass));
rb_str_buf_cat2(str, ")");
}
}
rb_str_buf_cat2(str, sharp);
rb_str_append(str, 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;
}
|
#name ⇒ Object
Returns the name of the method.
|
# File 'proc.c'
/*
* call-seq:
* meth.name -> symbol
*
* 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);
}
|
#owner ⇒ Object
Returns the class or module that defines the method.
|
# File 'proc.c'
/*
* call-seq:
* meth.owner -> class_or_module
*
* Returns the class or module that defines the method.
*/
static VALUE
method_owner(VALUE obj)
{
struct METHOD *data;
TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
return data->me.klass;
}
|
#parameters ⇒ Array
returns the parameter information of this method
|
# File 'proc.c'
/*
* call-seq:
* meth.parameters -> array
*
* 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);
}
|
#source_location ⇒ Array
returns the ruby source filename and line number containing this method or nil if this method was not defined in ruby (i.e. native)
|
# File 'proc.c'
/*
* call-seq:
* meth.source_location -> [String, Fixnum]
*
* 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);
if (def->type == VM_METHOD_TYPE_ATTRSET || def->type == VM_METHOD_TYPE_IVAR) {
if (!def->body.attr.location)
return Qnil;
return rb_ary_dup(def->body.attr.location);
}
return iseq_location(method_get_iseq(def));
}
|
#to_s ⇒ String #inspect ⇒ String
Show the name of the underlying method.
"cat".method(:count).inspect #=> "#<Method: String#count>"
|
# File 'proc.c'
/*
* call-seq:
* meth.to_s -> string
* meth.inspect -> string
*
* Show 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 = "#";
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, ": ");
if (FL_TEST(data->me.klass, FL_SINGLETON)) {
VALUE v = rb_iv_get(data->me.klass, "__attached__");
if (data->recv == Qundef) {
rb_str_buf_append(str, rb_inspect(data->me.klass));
}
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_cat2(str, rb_class2name(data->rclass));
if (data->rclass != data->me.klass) {
rb_str_buf_cat2(str, "(");
rb_str_buf_cat2(str, rb_class2name(data->me.klass));
rb_str_buf_cat2(str, ")");
}
}
rb_str_buf_cat2(str, sharp);
rb_str_append(str, 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;
}
|