class UnboundMethod - RDoc Documentation

# 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 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

meth == other_meth → true or false click to toggle source

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;
        VALUE klass1, klass2;

        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);

        klass1 = method_entry_defined_class(m1->me);
        klass2 = method_entry_defined_class(m2->me);

        if (!rb_method_entry_eq(m1->me, m2->me) ||
            klass1 != klass2 ||
            m1->klass != m2->klass ||
            m1->recv != m2->recv) {
            return Qfalse;
        }

        return Qtrue;
    }

arity → integer click to toggle source

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

    static VALUE
    method_arity_m(VALUE method)
    {
        int n = method_arity(method);
        return INT2FIX(n);
    }

bind(obj) → method click to toggle source

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, klass;

        TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);

        methclass = data->me->owner;

        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,
                         methclass);
            }
        }

        klass  = CLASS_OF(recv);

        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, data->klass);
        RB_OBJ_WRITE(method, &bound->me, rb_method_entry_clone(data->me));

        if (RB_TYPE_P(bound->me->owner, T_MODULE)) {
            VALUE ic = rb_class_search_ancestor(klass, bound->me->owner);
            if (ic) {
                klass = ic;
            }
            else {
                klass = rb_include_class_new(methclass, klass);
            }
            RB_OBJ_WRITE(method, &bound->me, rb_method_entry_complement_defined_class(bound->me, bound->me->called_id, klass));
        }

        return method;
    }

clone → new_method click to toggle source

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);
        RB_OBJ_WRITE(clone, &data->recv, orig->recv);
        RB_OBJ_WRITE(clone, &data->klass, orig->klass);
        RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me));
        return clone;
    }

eql?(other_meth) → true or false click to toggle source

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;
        VALUE klass1, klass2;

        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);

        klass1 = method_entry_defined_class(m1->me);
        klass2 = method_entry_defined_class(m2->me);

        if (!rb_method_entry_eq(m1->me, m2->me) ||
            klass1 != klass2 ||
            m1->klass != m2->klass ||
            m1->recv != m2->recv) {
            return Qfalse;
        }

        return Qtrue;
    }

hash → integer click to toggle source

Returns a hash value corresponding to the method object.

See also Object#hash.

    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 INT2FIX(hash);
    }

inspect → string click to toggle source

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 *sharp = "#";
        VALUE mklass;
        VALUE defined_class;

        TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
        str = rb_sprintf("#<% "PRIsVALUE": ", rb_obj_class(method));
        OBJ_INFECT_RAW(str, method);

        mklass = data->klass;

        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 (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_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)");
        }
        rb_str_buf_cat2(str, ">");

        return str;
    }

name → symbol click to toggle source

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->me->called_id);
    }

original_name → symbol click to toggle source

Returns the original name of the method.

    class C
      def foo; end
      alias bar foo
    end
    C.instance_method(:bar).original_name # => :foo

    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);
    }

owner → class_or_module click to toggle source

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->owner;
    }

parameters → array click to toggle source

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]]

    static VALUE
    rb_method_parameters(VALUE method)
    {
        const rb_iseq_t *iseq = rb_method_iseq(method);
        if (!iseq) {
            return unnamed_parameters(method_arity(method));
        }
        return rb_iseq_parameters(iseq, 0);
    }

source_location → [String, Integer] click to toggle source

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)
    {
        return method_def_location(method_def(method));
    }

super_method → method click to toggle source

Returns a Method of superclass which would be called when super is used or nil if there is no method on superclass.

    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;
        super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
        mid = data->me->called_id;
        if (!super_class) return Qnil;
        me = (rb_method_entry_t *)rb_callable_method_entry_without_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);
    }

to_s → string click to toggle source

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 *sharp = "#";
        VALUE mklass;
        VALUE defined_class;

        TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
        str = rb_sprintf("#<% "PRIsVALUE": ", rb_obj_class(method));
        OBJ_INFECT_RAW(str, method);

        mklass = data->klass;

        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 (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_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)");
        }
        rb_str_buf_cat2(str, ">");

        return str;
    }