Spec-Zone.ru › Ruby 4.0

класс Method

Родительский класс:
Object

Объекты Method создаются методом Object#method и связаны с определённым объектом (а не просто с классом). Их можно использовать для вызова метода внутри объекта, а также в качестве блока, связанного с итератором. Их также можно отвязать от одного объекта (создав UnboundMethod) и привязать к другому.

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

Открытые методы экземпляра

self << g → a_proc Показать исходный код
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);
}

Возвращает proc, представляющий собой композицию заданного g и этого метода.

Возвращённый proc принимает переменное число аргументов. Сначала он вызывает g с этими аргументами, а затем вызывает self со значением, возвращённым g.

def f(ary) = ary << 'in f'

f = self.method(:f)
g = proc { |ary| ary << 'in proc' }
(f << g).call([]) # => ["in proc", "in f"]
meth == other_meth → true or false Показать исходный код
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 *)RTYPEDDATA_GET_DATA(method);
    m2 = (struct METHOD *)RTYPEDDATA_GET_DATA(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;
}

Два объекта Method равны, если они привязаны к одному объекту, ссылаются на одно и то же определение метода, а классы или модули, в которых определены методы, совпадают.

Также имеет псевдоним: eql?
method === obj → result_of_method

Вызывает meth с указанными аргументами и возвращает значение, возвращённое методом.

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

Использование Method#=== позволяет использовать объект метода в качестве цели для предложения when в операторе case.

require 'prime'

case 1373
when Prime.method(:prime?)
  # ...
end
Псевдоним для: call
self >> g → a_proc Показать исходный код
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);
}

Возвращает proc, представляющий собой композицию этого метода и заданного g.

Возвращённый proc принимает переменное число аргументов. Сначала он вызывает self с этими аргументами, а затем вызывает g со значением, возвращённым self.

def f(ary) = ary << 'in f'

f = self.method(:f)
g = proc { |ary| ary << 'in proc' }
(f >> g).call([]) # => ["in f", "in proc"]
meth[args, ...] → obj

Вызывает meth с указанными аргументами и возвращает значение, возвращённое методом.

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

Использование Method#=== позволяет использовать объект метода в качестве цели для предложения when в операторе case.

require 'prime'

case 1373
when Prime.method(:prime?)
  # ...
end
Псевдоним для: call
arity → integer Показать исходный код
static VALUE
method_arity_m(VALUE method)
{
    int n = method_arity(method);
    return INT2FIX(n);
}

Возвращает сведения о количестве аргументов, принимаемых методом. Для методов, принимающих фиксированное число аргументов, возвращает неотрицательное целое число. Для методов Ruby, принимающих переменное число аргументов, возвращает -n-1, где n — число обязательных аргументов. Именованные аргументы считаются одним дополнительным аргументом; этот аргумент является обязательным, если обязателен хотя бы один именованный аргумент. Для методов, написанных на C, возвращает -1, если вызов принимает переменное число аргументов.

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
box → box or nil Показать исходный код
static VALUE
method_box(VALUE obj)
{
    struct METHOD *data;
    const rb_box_t *box;

    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    box = data->me->def->box;
    if (!box) return Qnil;
    if (box->box_object) return box->box_object;
    rb_bug("Unexpected box on the method definition: %p", (void*) box);
    UNREACHABLE_RETURN(Qnil);
}

Возвращает Ruby::Box, в котором определён meth.

call(args, ...) → obj Показать исходный код
static VALUE
rb_method_call_pass_called_kw(int argc, const VALUE *argv, VALUE method)
{
    return rb_method_call_kw(argc, argv, method, RB_PASS_CALLED_KEYWORDS);
}

Вызывает meth с указанными аргументами и возвращает значение, возвращённое методом.

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

Использование Method#=== позволяет использовать объект метода в качестве цели для предложения when в операторе case.

require 'prime'

case 1373
when Prime.method(:prime?)
  # ...
end
Также имеет псевдонимы: ===, []
clone → new_method Показать исходный код
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);
    rb_obj_clone_setup(self, clone, Qnil);
    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;
}

Возвращает копию этого метода.

class A
  def foo
    return "bar"
  end
end

m = A.new.method(:foo)
m.call # => "bar"
n = m.clone.call # => "bar"
curry → proc Показать исходный код
curry(arity) → proc
static VALUE
rb_method_curry(int argc, const VALUE *argv, VALUE self)
{
    VALUE proc = method_to_proc(self);
    return proc_curry(argc, argv, proc);
}

Возвращает каррированный proc на основе этого метода. Если proc вызывается с числом аргументов, меньшим арности метода, возвращается другой каррированный proc. Сам метод будет вызван только после того, как будет передано достаточно аргументов для соответствия его сигнатуре.

Необязательный аргумент arity следует указывать при каррировании методов с переменным числом аргументов, чтобы определить, сколько аргументов необходимо передать до вызова метода.

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]
eql?(other_meth) → true or false

Два объекта Method равны, если они привязаны к одному объекту, ссылаются на одно и то же определение метода, а классы или модули, в которых определены методы, совпадают.

Псевдоним для: ==
hash → integer Показать исходный код
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);
}

Возвращает хеш-значение, соответствующее объекту метода.

См. также Object#hash.

inspect → 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 (UNDEF_P(data->recv)) {
        // UnboundMethod
        rb_str_buf_append(str, rb_inspect(defined_class));
    }
    else if (RCLASS_SINGLETON_P(mklass)) {
        VALUE v = RCLASS_ATTACHED_OBJECT(mklass);

        if (UNDEF_P(data->recv)) {
            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 (RCLASS_SINGLETON_P(mklass)) {
            VALUE v = RCLASS_ATTACHED_OBJECT(mklass);
            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, "(");

        if (RARRAY_LEN(params) == 3 &&
            RARRAY_AREF(RARRAY_AREF(params, 0), 0) == rest &&
            RARRAY_AREF(RARRAY_AREF(params, 0), 1) == ID2SYM('*') &&
            RARRAY_AREF(RARRAY_AREF(params, 1), 0) == keyrest &&
            RARRAY_AREF(RARRAY_AREF(params, 1), 1) == ID2SYM(idPow) &&
            RARRAY_AREF(RARRAY_AREF(params, 2), 0) == block &&
            RARRAY_AREF(RARRAY_AREF(params, 2), 1) == ID2SYM('&')) {
            forwarding = 1;
        }

        for (int i = 0; i < RARRAY_LEN(params); i++) {
            pair = RARRAY_AREF(params, i);
            kind = RARRAY_AREF(pair, 0);
            if (RARRAY_LEN(pair) > 1) {
                name = RARRAY_AREF(pair, 1);
            }
            else {
                // 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");
                }
                else {
                    name = Qnil;
                }
            }

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

Возвращает понятное человеку описание базового метода.

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
(1..3).method(:map).inspect    #=> "#<Method: Range(Enumerable)#map()>"

Во втором случае описание метода включает «владельца» исходного метода (модуль Enumerable, включённый в Range).

inspect также по возможности предоставляет имена аргументов метода (последовательность вызова) и местоположение исходного кода.

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

... в определении аргументов означает, что аргумент является необязательным (имеет значение по умолчанию).

Для методов, определённых на C (в ядре языка и расширениях), местоположение и имена аргументов извлечь нельзя, поэтому предоставляется только общая информация в виде * (любое число аргументов) или _ (некоторый позиционный аргумент).

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
"cat".method(:+).inspect       #=> "#<Method: String#+(_)>""
Также имеет псевдоним: to_s
name → symbol Показать исходный код
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 Показать исходный код
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);
}

Возвращает исходное имя метода.

class C
  def foo; end
  alias bar foo
end
C.instance_method(:bar).original_name # => :foo
owner → class_or_module Показать исходный код
static VALUE
method_owner(VALUE obj)
{
    struct METHOD *data;
    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    return data->owner;
}

Возвращает класс или модуль, в котором определён этот метод. Иными словами,

meth.owner.instance_methods(false).include?(meth.name) # => true

остаётся верным, пока метод не удалён, не отменён и не заменён (если метод является закрытым, вместо instance_methods используется private_instance_methods).

См. также Method#receiver.

(1..3).method(:map).owner #=> Enumerable
parameters → array Показать исходный код
static VALUE
rb_method_parameters(VALUE method)
{
    return method_def_parameters(rb_method_def(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]]
receiver → object Показать исходный код
static VALUE
method_receiver(VALUE obj)
{
    struct METHOD *data;

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

Возвращает получателя, к которому привязан объект метода.

(1..3).method(:map).receiver # => 1..3
source_location → [String, Integer, Integer, Integer, Integer] Показать исходный код
VALUE
rb_method_location(VALUE method)
{
    return method_def_location(rb_method_def(method));
}

Возвращает местоположение, в котором был определён метод. Возвращённый Array содержит:

(1) the Ruby source filename
(2) the line number where the definition starts
(3) the column number where the definition starts
(4) the line number where the definition ends
(5) the column number where the definitions ends

Этот метод вернёт nil, если метод определён не на Ruby (то есть является встроенным).

super_method → method Показать исходный код
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);
}

Возвращает Method суперкласса, который был бы вызван при использовании super, или nil, если в суперклассе нет такого метода.

to_proc → proc Показать исходный код
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;
}

Возвращает объект Proc, соответствующий этому методу.

to_s → string

Возвращает понятное человеку описание базового метода.

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
(1..3).method(:map).inspect    #=> "#<Method: Range(Enumerable)#map()>"

Во втором случае описание метода включает «владельца» исходного метода (модуль Enumerable, включённый в Range).

inspect также по возможности предоставляет имена аргументов метода (последовательность вызова) и местоположение исходного кода.

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

... в определении аргументов означает, что аргумент является необязательным (имеет значение по умолчанию).

Для методов, определённых на C (в ядре языка и расширениях), местоположение и имена аргументов извлечь нельзя, поэтому предоставляется только общая информация в виде * (любое число аргументов) или _ (некоторый позиционный аргумент).

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
"cat".method(:+).inspect       #=> "#<Method: String#+(_)>""
Псевдоним для: inspect
unbind → unbound_method Показать исходный код
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, Qundef);
    RB_OBJ_WRITE(method, &data->iclass, orig->iclass);
    RB_OBJ_WRITE(method, &data->owner, orig->me->owner);
    RB_OBJ_WRITE(method, &data->me, rb_method_entry_clone(orig->me));

    return method;
}

Отвязывает meth от текущего получателя. Полученный UnboundMethod впоследствии можно привязать к новому объекту того же класса (см. UnboundMethod).

Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API