Spec-Zone.ru › Ruby 2.2

класс UnboundMethod

Родитель:
Объект

Ruby поддерживает две формы объектифицированных методов. Класс Method используется для представления методов, связанных с конкретным объектом: эти объекты метода связаны с этим объектом. Объекты связанных методов для объекта можно создать, используя Object#method.

Ruby также поддерживает несвязанные методы; объекты методов, которые не связаны с конкретным объектом. Их можно создать, вызвав Module#instance_method или вызвав unbind на объекте связанного метода. Результатом обоих является объект UnboundMethod.

Несвязанные методы могут быть вызваны только после их привязки к объекту. Этот объект должен быть своего рода исходным классом метода.

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

Несвязанные методы — это ссылка на метод в момент его объективации: последующие изменения в базовом классе не повлияют на несвязанный метод.

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

Публичные методы экземпляров

meth == other_meth → true или false Показать исходный код
static VALUE
method_eq(VALUE method, VALUE other)
{
    struct METHOD *m1, *m2;

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

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

    return Qtrue;
}

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

arity → целое Показать исходный код
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
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

"cat".method(:size).arity      #=> 0
"cat".method(:replace).arity   #=> 1
"cat".method(:squeeze).arity   #=> -1
"cat".method(:count).arity     #=> -1
bind(obj) → метод Показать исходный код
static VALUE
umethod_bind(VALUE method, VALUE recv)
{
    struct METHOD *data, *bound;
    VALUE methclass;
    VALUE rclass;

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

    methclass = data->rclass;
    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,
                     rb_class_name(methclass));
        }
    }

    method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound);
    *bound = *data;
    bound->me = ALLOC(rb_method_entry_t);
    *bound->me = *data->me;
    if (bound->me->def) bound->me->def->alias_count++;
    rclass = CLASS_OF(recv);
    if (BUILTIN_TYPE(bound->defined_class) == T_MODULE) {
        VALUE ic = rb_class_search_ancestor(rclass, bound->defined_class);
        if (ic) {
            rclass = ic;
        }
        else {
            rclass = rb_include_class_new(methclass, rclass);
        }
    }
    bound->recv = recv;
    bound->rclass = rclass;
    data->ume = ALLOC(struct unlinked_method_entry_list_entry);

    return method;
}

Привязать umeth к obj. Если Klass был классом, из которого был получен umeth, obj.kind_of?(Klass) должно быть истинным.

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

выводит:

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
clone → новый_метод Показать исходный код
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);
    *data = *orig;
    data->me = ALLOC(rb_method_entry_t);
    *data->me = *orig->me;
    if (data->me->def) data->me->def->alias_count++;
    data->ume = ALLOC(struct unlinked_method_entry_list_entry);

    return clone;
}

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

class A
  def foo
    return "bar"
  end
end

m = A.new.method(:foo)
m.call # => "bar"
n = m.clone.call # => "bar"
eql?(other_meth) → true или false Показать исходный код
static VALUE
method_eq(VALUE method, VALUE other)
{
    struct METHOD *m1, *m2;

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

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

    return Qtrue;
}

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

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->rclass);
    hash = rb_hash_uint(hash, (st_index_t)m->recv);
    hash = rb_hash_method_entry(hash, m->me);
    hash = rb_hash_end(hash);

    return INT2FIX(hash);
}

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

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

inspect → строка Показать исходный код
static VALUE
method_inspect(VALUE method)
{
    struct METHOD *data;
    VALUE str;
    const char *s;
    const char *sharp = "#";
    VALUE mklass;

    TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
    str = rb_str_buf_new2("#<");
    s = rb_obj_classname(method);
    rb_str_buf_cat2(str, s);
    rb_str_buf_cat2(str, ": ");

    mklass = data->me->klass;
    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_class_name(data->rclass));
        if (data->rclass != mklass) {
            rb_str_buf_cat2(str, "(");
            rb_str_buf_append(str, rb_class_name(mklass));
            rb_str_buf_cat2(str, ")");
        }
    }
    rb_str_buf_cat2(str, sharp);
    rb_str_append(str, rb_id2str(data->id));
    if (data->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>"
name → символ Показать исходный код
static VALUE
method_name(VALUE obj)
{
    struct METHOD *data;

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

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

original_name → символ Показать исходный код
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 → класс_или_модуль Показать исходный код
static VALUE
method_owner(VALUE obj)
{
    struct METHOD *data;
    VALUE defined_class;

    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    defined_class = data->defined_class;

    if (RB_TYPE_P(defined_class, T_ICLASS)) {
        defined_class = RBASIC_CLASS(defined_class);
    }

    return defined_class;
}

Возвращает класс или модуль, который определяет метод.

parameters → массив Показать исходный код
static VALUE
rb_method_parameters(VALUE method)
{
    rb_iseq_t *iseq = rb_method_get_iseq(method);
    if (!iseq) {
        return unnamed_parameters(method_arity(method));
    }
    return rb_iseq_parameters(iseq, 0);
}

Возвращает информацию о параметрах этого метода.

source_location → [Строка, Целое число] Показать исходный код
VALUE
rb_method_location(VALUE method)
{
    rb_method_definition_t *def = method_get_def(method);
    return method_def_location(def);
}

Возвращает имя файла Ruby и номер строки, содержащий этот метод, или nil, если этот метод не был определен в Ruby (т.е. нативный).

super_method() Показать исходный код
static VALUE
method_super_method(VALUE method)
{
    struct METHOD *data;
    VALUE defined_class, super_class;
    rb_method_entry_t *me;

    TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
    defined_class = data->defined_class;
    if (BUILTIN_TYPE(defined_class) == T_MODULE) defined_class = data->rclass;
    super_class = RCLASS_SUPER(defined_class);
    if (!super_class) return Qnil;
    me = rb_method_entry_without_refinements(super_class, data->id, &defined_class);
    if (!me) return Qnil;
    return mnew_internal(me, defined_class,
                         super_class, data->recv, data->id,
                         rb_obj_class(method), FALSE, FALSE);
}

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

to_s → строка Показать исходный код
static VALUE
method_inspect(VALUE method)
{
    struct METHOD *data;
    VALUE str;
    const char *s;
    const char *sharp = "#";
    VALUE mklass;

    TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
    str = rb_str_buf_new2("#<");
    s = rb_obj_classname(method);
    rb_str_buf_cat2(str, s);
    rb_str_buf_cat2(str, ": ");

    mklass = data->me->klass;
    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_class_name(data->rclass));
        if (data->rclass != mklass) {
            rb_str_buf_cat2(str, "(");
            rb_str_buf_append(str, rb_class_name(mklass));
            rb_str_buf_cat2(str, ")");
        }
    }
    rb_str_buf_cat2(str, sharp);
    rb_str_append(str, rb_id2str(data->id));
    if (data->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>"

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

Spec-Zone.ru

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