Class: UnboundMethod

Inherits:
Object show all
Defined in:
eval.c

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

Instance Method Details

#==(other_meth) ⇒ Boolean

Two method objects are equal if that are bound to the same object and contain the same body.

Returns:

  • (Boolean)


# File 'eval.c'

/*
 * call-seq:
 *   meth == other_meth  => true or false
 *
 * Two method objects are equal if that are bound to the same
 * object and contain the same body.
 */


static VALUE
method_eq(method, other)
    VALUE method, other;
{
    struct METHOD *m1, *m2;

    if (TYPE(other) != T_DATA || RDATA(other)->dmark != (RUBY_DATA_FUNC)bm_mark)
    return Qfalse;
    if (CLASS_OF(method) != CLASS_OF(other))
    return Qfalse;

    Data_Get_Struct(method, struct METHOD, m1);
    Data_Get_Struct(other, struct METHOD, m2);

    if (m1->klass != m2->klass || m1->rklass != m2->rklass ||
    m1->recv != m2->recv || m1->body != m2->body)
    return Qfalse;

    return Qtrue;
}

#arityFixnum

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

Returns:



# File 'eval.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(method)
    VALUE method;
{
    struct METHOD *data;
    NODE *body;
    int n;

    Data_Get_Struct(method, struct METHOD, data);

    body = data->body;
    switch (nd_type(body)) {
      case NODE_CFUNC:
    if (body->nd_argc < 0) return INT2FIX(-1);
    return INT2FIX(body->nd_argc);
      case NODE_ZSUPER:
    return INT2FIX(-1);
      case NODE_ATTRSET:
    return INT2FIX(1);
      case NODE_IVAR:
    return INT2FIX(0);
      case NODE_BMETHOD:
    return proc_arity(body->nd_cval);
      case NODE_DMETHOD:
    return method_arity(body->nd_cval);
      case NODE_SCOPE:
    body = body->nd_next;  /* skip NODE_SCOPE */
    if (nd_type(body) == NODE_BLOCK)
        body = body->nd_head;
    if (!body) return INT2FIX(0);
    n = body->nd_cnt;
    if (body->nd_opt || body->nd_rest)
        n = -n-1;
    return INT2FIX(n);
      default:
    rb_raise(rb_eArgError, "invalid node 0x%x", nd_type(body));
   }
}

#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 'eval.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(method, recv)
    VALUE method, recv;
{
    struct METHOD *data, *bound;
    VALUE rklass = CLASS_OF(recv);

    Data_Get_Struct(method, struct METHOD, data);
    if (data->rklass != rklass) {
    if (FL_TEST(data->rklass, FL_SINGLETON)) {
        rb_raise(rb_eTypeError, "singleton method bound for a different object");
    }
    if (TYPE(data->rklass) == T_MODULE) {
        st_table *m_tbl = RCLASS(data->rklass)->m_tbl;
        while (RCLASS(rklass)->m_tbl != m_tbl) {
        rklass = RCLASS(rklass)->super;
        if (!rklass) goto not_instace;
        }
    }
    else if (!rb_obj_is_kind_of(recv, data->rklass)) {
      not_instace:
        rb_raise(rb_eTypeError, "bind argument must be an instance of %s",
             rb_class2name(data->rklass));
    }
    }

    method = Data_Make_Struct(rb_cMethod,struct METHOD,bm_mark,free,bound);
    *bound = *data;
    bound->recv = recv;
    bound->rklass = rklass;

    return method;
}

#cloneObject

MISSING: documentation



# File 'eval.c'

/*
 * MISSING: documentation
 */

static VALUE
method_clone(self)
    VALUE self;
{
    VALUE clone;
    struct METHOD *orig, *data;

    Data_Get_Struct(self, struct METHOD, orig);
    clone = Data_Make_Struct(CLASS_OF(self),struct METHOD, bm_mark, free, data);
    CLONESETUP(clone, self);
    *data = *orig;

    return clone;
}

#to_sString #inspectString

Show the name of the underlying method.

"cat".method(:count).inspect   #=> "#<Method: String#count>"

Overloads:



# File 'eval.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(method)
    VALUE method;
{
    struct METHOD *data;
    VALUE str;
    const char *s;
    const char *sharp = "#";

    Data_Get_Struct(method, struct METHOD, 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->klass, FL_SINGLETON)) {
    VALUE v = rb_iv_get(data->klass, "__attached__");

    if (data->recv == Qundef) {
        rb_str_buf_append(str, rb_inspect(data->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->rklass));
    if (data->rklass != data->klass) {
        rb_str_buf_cat2(str, "(");
        rb_str_buf_cat2(str, rb_class2name(data->klass));
        rb_str_buf_cat2(str, ")");
    }
    }
    rb_str_buf_cat2(str, sharp);
    rb_str_buf_cat2(str, rb_id2name(data->id));
    rb_str_buf_cat2(str, ">");

    return str;
}

#nameString

Returns the name of the method.

Returns:



# File 'eval.c'

/*
 *  call-seq:
 *     meth.name    => string
 *
 *  Returns the name of the method.
 */

static VALUE
method_name(obj)
    VALUE obj;
{
    struct METHOD *data;

    Data_Get_Struct(obj, struct METHOD, data);
    return rb_str_new2(rb_id2name(data->id));
}

#ownerObject

Returns the class or module that defines the method.



# File 'eval.c'

/*
 *  call-seq:
 *     meth.owner    => class_or_module
 *
 *  Returns the class or module that defines the method.
 */

static VALUE
method_owner(obj)
    VALUE obj;
{
    struct METHOD *data;

    Data_Get_Struct(obj, struct METHOD, data);
    return data->klass;
}

#to_sString #inspectString

Show the name of the underlying method.

"cat".method(:count).inspect   #=> "#<Method: String#count>"

Overloads:



# File 'eval.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(method)
    VALUE method;
{
    struct METHOD *data;
    VALUE str;
    const char *s;
    const char *sharp = "#";

    Data_Get_Struct(method, struct METHOD, 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->klass, FL_SINGLETON)) {
    VALUE v = rb_iv_get(data->klass, "__attached__");

    if (data->recv == Qundef) {
        rb_str_buf_append(str, rb_inspect(data->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->rklass));
    if (data->rklass != data->klass) {
        rb_str_buf_cat2(str, "(");
        rb_str_buf_cat2(str, rb_class2name(data->klass));
        rb_str_buf_cat2(str, ")");
    }
    }
    rb_str_buf_cat2(str, sharp);
    rb_str_buf_cat2(str, rb_id2name(data->id));
    rb_str_buf_cat2(str, ">");

    return str;
}