класс Method
Объекты 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)>]
Открытые методы экземпляра
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"]
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 равны, если они привязаны к одному объекту, ссылаются на одно и то же определение метода, а классы или модули, в которых определены методы, совпадают.
Вызывает meth с указанными аргументами и возвращает значение, возвращённое методом.
m = 12.method("+")
m.call(3) #=> 15
m.call(20) #=> 32
Использование Method#=== позволяет использовать объект метода в качестве цели для предложения when в операторе case.
require 'prime' case 1373 when Prime.method(:prime?) # ... end
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 с указанными аргументами и возвращает значение, возвращённое методом.
m = 12.method("+")
m.call(3) #=> 15
m.call(20) #=> 32
Использование Method#=== позволяет использовать объект метода в качестве цели для предложения when в операторе case.
require 'prime' case 1373 when Prime.method(:prime?) # ... end
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
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.
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
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"
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]
Два объекта Method равны, если они привязаны к одному объекту, ссылаются на одно и то же определение метода, а классы или модули, в которых определены методы, совпадают.
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.
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#+(_)>""
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);
} Возвращает имя метода.
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
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
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]]
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
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 (то есть является встроенным).
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, если в суперклассе нет такого метода.
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, соответствующий этому методу.
Возвращает понятное человеку описание базового метода.
"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#+(_)>""
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.