класс 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]
Публичные методы экземпляра
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;
} Два объекта метода равны, если они связаны с одним и тем же объектом и ссылаются на одно и то же определение метода, и их владельцы являются одним и тем же классом или модулем.
VALUE
rb_method_call(int argc, const VALUE *argv, VALUE method)
{
VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil;
return rb_method_call_with_block(argc, argv, method, procval);
} Вызывает блок с obj в качестве параметра блока, как в Proc#call. Это позволяет объекту блока быть целевым для when предложения в операторе case.
VALUE
rb_method_call(int argc, const VALUE *argv, VALUE method)
{
VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil;
return rb_method_call_with_block(argc, argv, method, procval);
} Вызывает блок, устанавливая параметры блока на значения в params, используя что-то близкое к семантике вызова метода. Возвращает значение последнего выражения, вычисленного в блоке.
a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
a_proc.call(9, 1, 2, 3) #=> [9, 18, 27]
a_proc[9, 1, 2, 3] #=> [9, 18, 27]
a_proc.(9, 1, 2, 3) #=> [9, 18, 27]
a_proc.yield(9, 1, 2, 3) #=> [9, 18, 27]
Обратите внимание, что prc.() вызывает prc.call() с указанными параметрами. Это синтаксический сахар для скрытия «call».
Для блоков, созданных с использованием lambda или ->(), генерируется ошибка, если передано неправильное количество параметров блоку. Для блоков, созданных с использованием Proc.new или Kernel.proc, дополнительные параметры игнорируются, а отсутствующие параметры устанавливаются в nil.
a_proc = proc {|a,b| [a,b] }
a_proc.call(1) #=> [1, nil]
a_proc = lambda {|a,b| [a,b] }
a_proc.call(1) # ArgumentError: wrong number of arguments (given 1, expected 2)
См. также Proc#lambda?.
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
VALUE
rb_method_call(int argc, const VALUE *argv, VALUE method)
{
VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil;
return rb_method_call_with_block(argc, argv, method, procval);
} Вызывает блок, устанавливая параметры блока на значения в params, используя что-то близкое к семантике вызова метода. Возвращает значение последнего выражения, вычисленного в блоке.
a_proc = Proc.new {|scalar, *values| values.map {|value| value*scalar } }
a_proc.call(9, 1, 2, 3) #=> [9, 18, 27]
a_proc[9, 1, 2, 3] #=> [9, 18, 27]
a_proc.(9, 1, 2, 3) #=> [9, 18, 27]
a_proc.yield(9, 1, 2, 3) #=> [9, 18, 27]
Обратите внимание, что prc.() вызывает prc.call() с указанными параметрами. Это синтаксический сахар для скрытия «call».
Для блоков, созданных с использованием lambda или ->(), генерируется ошибка, если передано неправильное количество параметров блоку. Для блоков, созданных с использованием Proc.new или Kernel.proc, дополнительные параметры игнорируются, а отсутствующие параметры устанавливаются в nil.
a_proc = proc {|a,b| [a,b] }
a_proc.call(1) #=> [1, nil]
a_proc = lambda {|a,b| [a,b] }
a_proc.call(1) # ArgumentError: wrong number of arguments (given 1, expected 2)
См. также Proc#lambda?.
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;
} Возвращает копию этого метода.
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);
} Возвращает выложенный блок на основе метода. При вызове блока с количеством аргументов, меньшим, чем арность метода, возвращается другой выложенный блок. Только когда предоставлено достаточно аргументов для удовлетворения сигнатуры метода, метод будет фактически вызван.
Необязательный аргумент 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]
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;
} Два объекта метода равны, если они связаны с одним и тем же объектом и ссылаются на одно и то же определение метода, и их владельцы являются одним и тем же классом или модулем.
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);
} Возвращает значение хеша, соответствующее объекту метода.
См. также 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));
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;
} Возвращает имя базового метода.
"cat".method(:count).inspect #=> "#<Method: String#count>"
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->me->owner;
} Возвращает класс или модуль, определяющий метод.
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);
} Возвращает информацию о параметрах этого метода.
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;
} Возвращает связанный получатель объекта метода.
VALUE
rb_method_location(VALUE method)
{
return method_def_location(method_def(method));
} Возвращает имя файла и номер строки в Ruby-источнике, содержащем этот метод, или 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;
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);
} Возвращает Метод суперкласса, который будет вызван при использовании 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_iterate(mlambda, 0, bmcall, method);
GetProcPtr(procval, proc);
proc->is_from_method = 1;
return procval;
} Возвращает объект Proc соответствующий этому методу.
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;
} Возвращает имя базового метода.
"cat".method(:count).inspect #=> "#<Method: String#count>"
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->me, rb_method_entry_clone(orig->me));
OBJ_INFECT(method, obj);
return method;
} Отсоединяет meth от текущего получателя. Результирующий UnboundMethod может быть затем привязан к новому объекту того же класса (см. UnboundMethod).
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.