class Method - RDoc Documentation

# class Method

`Method` objects are created by `Object#method`, and are associated with a particular object (not just with a class). They may be used to invoke the method within the object, and as a block associated with an iterator. They may also be unbound from one object (creating an `UnboundMethod`) and bound to another.

    class Thing
      def square(n)
        n*n
      end
    end
    thing = Thing.new
    meth  = thing.method(:square)

    meth.call(9)                 #=> 81
    [ 1, 2, 3 ].collect(&meth)   #=> [1, 4, 9]

    [ 1, 2, 3 ].each(&method(:puts)) #=> prints 1, 2, 3

    require 'date'
    %w[2017-03-01 2017-03-02].collect(&Date.method(:parse))
    #=> [#<Date: 2017-03-01 ((2457814j,0s,0n),+0s,2299161j)>, #<Date: 2017-03-02 ((2457815j,0s,0n),+0s,2299161j)>]

### Public Instance Methods

meth << g → a_proc click to toggle source

Returns a proc that is the composition of this method and the given g. The returned proc takes a variable number of arguments, calls g with them then calls this method with the result.

    def f(x)
      x * x
    end

    f = self.method(:f)
    g = proc {|x| x + x }
    p (f << g).call(2) #=> 16

    static VALUE
    rb_method_compose_to_left(VALUE self, VALUE g)
    {
        g = to_callable(g);
        self = method_to_proc(self);
        return proc_compose_to_left(self, g);
    }

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 the classes defining the methods 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;
    }

Also aliased as: eql?
===(*args)

Invokes the meth with the specified arguments, returning the method’s return value.

    m = 12.method("+")
    m.call(3)    #=> 15
    m.call(20)   #=> 32

Alias for: call
meth >> g → a_proc click to toggle source

Returns a proc that is the composition of this method and the given g. The returned proc takes a variable number of arguments, calls this method with them then calls g with the result.

    def f(x)
      x * x
    end

    f = self.method(:f)
    g = proc {|x| x + x }
    p (f >> g).call(2) #=> 8

    static VALUE
    rb_method_compose_to_right(VALUE self, VALUE g)
    {
        g = to_callable(g);
        self = method_to_proc(self);
        return proc_compose_to_right(self, g);
    }

[](*args)

Invokes the meth with the specified arguments, returning the method’s return value.

    m = 12.method("+")
    m.call(3)    #=> 15
    m.call(20)   #=> 32

Alias for: call
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);
    }

call(args, ...) → obj click to toggle source

Invokes the meth with the specified arguments, returning the method’s return value.

    m = 12.method("+")
    m.call(3)    #=> 15
    m.call(20)   #=> 32

    static VALUE
    rb_method_call_pass_called_kw(int argc, const VALUE *argv, VALUE method)
    {
        VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil;
        return rb_method_call_with_block_kw(argc, argv, method, procval, RB_PASS_CALLED_KEYWORDS);
    }

Also aliased as: ===, []
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->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;
    }

curry → proc click to toggle source
curry(arity) → proc

Returns a curried proc based on the method. When the proc is called with a number of arguments that is lower than the method’s arity, then another curried proc is returned. Only when enough arguments have been supplied to satisfy the method signature, will the method actually be called.

The optional arity argument should be supplied when currying methods with variable arguments to determine how many arguments are needed before the method is called.

    def foo(a,b,c)
      [a, b, c]
    end

    proc  = self.method(:foo).curry
    proc2 = proc.call(1, 2)          #=> #<Proc>
    proc2.call(3)                    #=> [1,2,3]

    def vararg(*args)
      args
    end

    proc = self.method(:vararg).curry(4)
    proc2 = proc.call(:x)      #=> #<Proc>
    proc3 = proc2.call(:y, :z) #=> #<Proc>
    proc3.call(:a)             #=> [:x, :y, :z, :a]

    static VALUE
    rb_method_curry(int argc, const VALUE *argv, VALUE self)
    {
        VALUE proc = method_to_proc(self);
        return proc_curry(argc, argv, proc);
    }

eql?(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 and the classes defining the methods are the same class or module.

Alias for: ==
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 ST2FIX(hash);
    }

inspect → string click to toggle source

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#+(_)>""

    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 (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 {
            mklass = data->klass;
            if (FL_TEST(mklass, FL_SINGLETON)) {
                VALUE v = rb_ivar_get(mklass, attached);
                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, "(");

            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('*')) {
                        forwarding = 1;
                        rb_str_cat_cstr(str, "...");
                    }
                    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 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;
    }

Also aliased as: to_s
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 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

    static VALUE
    method_owner(VALUE obj)
    {
        struct METHOD *data;
        TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
        return data->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)
    {
        return method_def_parameters(rb_method_def(method));
    }

private? → true or false click to toggle source

Returns whether the method is private.

    static VALUE
    method_private_p(VALUE method)
    {
        const struct METHOD *data;
        TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
        return RBOOL(METHOD_ENTRY_VISI(data->me) == METHOD_VISI_PRIVATE);
    }

protected? → true or false click to toggle source

Returns whether the method is protected.

    static VALUE
    method_protected_p(VALUE method)
    {
        const struct METHOD *data;
        TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
        return RBOOL(METHOD_ENTRY_VISI(data->me) == METHOD_VISI_PROTECTED);
    }

public? → true or false click to toggle source

Returns whether the method is public.

    static VALUE
    method_public_p(VALUE method)
    {
        const struct METHOD *data;
        TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
        return RBOOL(METHOD_ENTRY_VISI(data->me) == METHOD_VISI_PUBLIC);
    }

receiver → object click to toggle source

Returns the bound receiver of the method object.

    (1..3).method(:map).receiver # => 1..3

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

        TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
        return data->recv;
    }

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

to_proc → proc click to toggle source

Returns a `Proc` object corresponding to this method.

    static VALUE
    method_to_proc(VALUE method)
    {
        VALUE procval;
        rb_proc_t *proc;

        /*
         * class Method
         *   def to_proc
         *     lambda{|*args|
         *       self.call(*args)
         *     }
         *   end
         * end
         */
        procval = rb_block_call(rb_mRubyVMFrozenCore, idLambda, 0, 0, bmcall, method);
        GetProcPtr(procval, proc);
        proc->is_from_method = 1;
        return procval;
    }

to_s → string

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#+(_)>""

Alias for: inspect
unbind → unbound_method click to toggle source

Dissociates meth from its current receiver. The resulting `UnboundMethod` can subsequently be bound to a new object of the same class (see `UnboundMethod`).

    static VALUE
    method_unbind(VALUE obj)
    {
        VALUE method;
        struct METHOD *orig, *data;

        TypedData_Get_Struct(obj, struct METHOD, &method_data_type, orig);
        method = TypedData_Make_Struct(rb_cUnboundMethod, struct METHOD,
                                       &method_data_type, data);
        RB_OBJ_WRITE(method, &data->recv, Qundef);
        RB_OBJ_WRITE(method, &data->klass, orig->klass);
        RB_OBJ_WRITE(method, &data->iclass, orig->iclass);
        RB_OBJ_WRITE(method, &data->owner, orig->owner);
        RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(orig->me));

        return method;
    }