класс Module
A Module — это коллекция методов и констант. Методы в модуле могут быть методами экземпляра или методами модуля. Методы экземпляра появляются как методы в классе, когда модуль включён, методы модуля — нет. Наоборот, методы модуля могут вызываться без создания encapsulating object, в то время как методы экземпляра — нет. (См. Module#module_function.)
В описаниях ниже параметр sym относится к символу, который является либо строкой в кавычках, либо Symbol (например, :name).
module Mod
include Math
CONST = 1
def meth
# ...
end
end
Mod.class #=> Module
Mod.constants #=> [:CONST, :PI, :E]
Mod.instance_methods #=> [:meth]
Публичные методы класса
static VALUE
rb_mod_s_constants(int argc, VALUE *argv, VALUE mod)
{
const rb_cref_t *cref = rb_vm_cref();
VALUE klass;
VALUE cbase = 0;
void *data = 0;
if (argc > 0 || mod != rb_cModule) {
return rb_mod_constants(argc, argv, mod);
}
while (cref) {
klass = CREF_CLASS(cref);
if (!CREF_PUSHED_BY_EVAL(cref) &&
!NIL_P(klass)) {
data = rb_mod_const_at(CREF_CLASS(cref), data);
if (!cbase) {
cbase = klass;
}
}
cref = CREF_NEXT(cref);
}
if (cbase) {
data = rb_mod_const_of(cbase, data);
}
return rb_const_list(data);
} В первом варианте возвращает массив имён всех констант, доступных из точки вызова. Этот список включает имена всех модулей и классов, определённых в глобальной области видимости.
Module.constants.first(4) # => [:ARGF, :ARGV, :ArgumentError, :Array] Module.constants.include?(:SEEK_SET) # => false class IO Module.constants.include?(:SEEK_SET) # => true end
Во втором варианте вызывается метод экземпляра constants.
static VALUE
rb_mod_nesting(void)
{
VALUE ary = rb_ary_new();
const rb_cref_t *cref = rb_vm_cref();
while (cref && CREF_NEXT(cref)) {
VALUE klass = CREF_CLASS(cref);
if (!CREF_PUSHED_BY_EVAL(cref) &&
!NIL_P(klass)) {
rb_ary_push(ary, klass);
}
cref = CREF_NEXT(cref);
}
return ary;
} Возвращает список Modules вложенных в точке вызова.
module M1
module M2
$a = Module.nesting
end
end
$a #=> [M1::M2, M1]
$a[0].name #=> "M1::M2"
static VALUE
rb_mod_initialize(VALUE module)
{
if (rb_block_given_p()) {
rb_mod_module_exec(1, &module, module);
}
return Qnil;
} Создаёт новый анонимный модуль. Если задан блок, он передаётся объекту модуля, и блок вычисляется в контексте этого модуля, как module_eval.
fred = Module.new do
def meth1
"hello"
end
def meth2
"bye"
end
end
a = "my string"
a.extend(fred) #=> "my string"
a.meth1 #=> "hello"
a.meth2 #=> "bye"
Присвойте модуль константе (имя, начинающееся с заглавной буквы), если вы хотите обращаться с ним как с обычным модулем.
static VALUE
rb_mod_s_used_modules(void)
{
const rb_cref_t *cref = rb_vm_cref();
VALUE ary = rb_ary_new();
while(cref) {
if(!NIL_P(CREF_REFINEMENTS(cref))) {
rb_hash_foreach(CREF_REFINEMENTS(cref), used_modules_i, ary);
}
cref = CREF_NEXT(cref);
}
return rb_funcall(ary, rb_intern("uniq"), 0);
} Возвращает массив всех модулей, используемых в текущей области видимости. Порядок модулей в результирующем массиве не определён.
module A refine Object do end end module B refine Object do end end using A using B p Module.used_modules
выводит:
[B, A]
Общедоступные методы экземпляров
static VALUE
rb_mod_lt(VALUE mod, VALUE arg)
{
if (mod == arg) return Qfalse;
return rb_class_inherited_p(mod, arg);
} Возвращает true, если mod является подклассом other. Возвращает nil если между ними нет отношения. (Рассматривайте отношение в терминах определения класса: «class A < B» подразумевает «A < B».)
VALUE
rb_class_inherited_p(VALUE mod, VALUE arg)
{
if (mod == arg) return Qtrue;
if (!CLASS_OR_MODULE_P(arg) && !RB_TYPE_P(arg, T_ICLASS)) {
rb_raise(rb_eTypeError, "compared with non class/module");
}
if (class_search_ancestor(mod, RCLASS_ORIGIN(arg))) {
return Qtrue;
}
/* not mod < arg; check if mod > arg */
if (class_search_ancestor(arg, mod)) {
return Qfalse;
}
return Qnil;
} Возвращает true, если mod является подклассом other или совпадает с other. Возвращает nil если между ними нет отношения. (Рассматривайте отношение в терминах определения класса: «class A < B» подразумевает «A < B».)
static VALUE
rb_mod_cmp(VALUE mod, VALUE arg)
{
VALUE cmp;
if (mod == arg) return INT2FIX(0);
if (!CLASS_OR_MODULE_P(arg)) {
return Qnil;
}
cmp = rb_class_inherited_p(mod, arg);
if (NIL_P(cmp)) return Qnil;
if (cmp) {
return INT2FIX(-1);
}
return INT2FIX(1);
} Сравнение — Возвращает -1, 0, +1 или nil в зависимости от того, содержит ли module other_module, являются ли они одинаковыми или module включается в other_module.
Возвращает nil если у module нет отношения с other_module, если other_module не является модулем или если два значения несравнимы.
MJIT_FUNC_EXPORTED VALUE
rb_obj_equal(VALUE obj1, VALUE obj2)
{
if (obj1 == obj2) return Qtrue;
return Qfalse;
} Равенство — На уровне Object, == возвращает true только если obj и other являются одним и тем же объектом. Как правило, этот метод переопределяется в дочерних классах для придания классу специфического значения.
В отличие от ==, метод equal? никогда не должен переопределяться подклассами, так как он используется для определения тождества объектов (то есть, a.equal?(b) тогда и только тогда, когда a является тем же объектом, что и b):
obj = "a" other = obj.dup obj == other #=> true obj.equal? other #=> false obj.equal? obj #=> true
Метод eql? возвращает true если obj и other ссылаются на один и тот же ключ хеша. Это используется методом Hash для проверки членов на равенство. Для объектов класса Object, eql? является синонимом ==. Подклассы обычно продолжают эту традицию, альянсируя eql? со своим переопределённым методом ==, но есть исключения. Например, типы Numeric выполняют преобразование типов через ==, но не через eql?, поэтому:
1 == 1.0 #=> true 1.eql? 1.0 #=> false
static VALUE
rb_mod_eqq(VALUE mod, VALUE arg)
{
return rb_obj_is_kind_of(arg, mod);
} Равенство по типу — Возвращает true если obj является экземпляром mod или экземпляром одного из потомков mod. Ограниченно полезно для модулей, но может использоваться в операциях case для классификации объектов по классу.
static VALUE
rb_mod_gt(VALUE mod, VALUE arg)
{
if (mod == arg) return Qfalse;
return rb_mod_ge(mod, arg);
} Возвращает true, если mod является предком other. Возвращает nil если между ними нет отношения. (Рассматривайте отношение в терминах определения класса: «class A < B» подразумевает «B > A».)
static VALUE
rb_mod_ge(VALUE mod, VALUE arg)
{
if (!CLASS_OR_MODULE_P(arg)) {
rb_raise(rb_eTypeError, "compared with non class/module");
}
return rb_class_inherited_p(arg, mod);
} Возвращает true, если mod является предком other или оба модуля одинаковы. Возвращает nil если между ними нет отношения. (Рассматривайте отношение в терминах определения класса: «class A < B» подразумевает «B > A».)
static VALUE
rb_mod_alias_method(VALUE mod, VALUE newname, VALUE oldname)
{
ID oldid = rb_check_id(&oldname);
if (!oldid) {
rb_print_undef_str(mod, oldname);
}
rb_alias(mod, rb_to_id(newname), oldid);
return mod;
} Создаёт new_name как копию метода old_name. Это может использоваться для сохранения доступа к методам, которые переопределены.
module Mod
alias_method :orig_exit, :exit
def exit(code=0)
puts "Exiting with code #{code}"
orig_exit(code)
end
end
include Mod
exit(99)
результат:
Exiting with code 99
VALUE
rb_mod_ancestors(VALUE mod)
{
VALUE p, ary = rb_ary_new();
for (p = mod; p; p = RCLASS_SUPER(p)) {
if (BUILTIN_TYPE(p) == T_ICLASS) {
rb_ary_push(ary, RBASIC(p)->klass);
}
else if (p == RCLASS_ORIGIN(p)) {
rb_ary_push(ary, p);
}
}
return ary;
} Возвращает список модулей, включённых/добавленных в mod (включая mod само по себе).
module Mod include Math include Comparable prepend Enumerable end Mod.ancestors #=> [Enumerable, Mod, Comparable, Math] Math.ancestors #=> [Math] Enumerable.ancestors #=> [Enumerable]
VALUE
rb_mod_attr(int argc, VALUE *argv, VALUE klass)
{
if (argc == 2 && (argv[1] == Qtrue || argv[1] == Qfalse)) {
rb_warning("optional boolean argument is obsoleted");
rb_attr(klass, id_for_attr(klass, argv[0]), 1, RTEST(argv[1]), TRUE);
return Qnil;
}
return rb_mod_attr_reader(argc, argv, klass);
} Первый вариант эквивалентен attr_reader. Второй эквивалентен attr_accessor(name), но устарел. Последний эквивалентен attr_reader(name), но устарел.
static VALUE
rb_mod_attr_accessor(int argc, VALUE *argv, VALUE klass)
{
int i;
for (i=0; i<argc; i++) {
rb_attr(klass, id_for_attr(klass, argv[i]), TRUE, TRUE, TRUE);
}
return Qnil;
} Определяет атрибут с именем symbol для этого модуля, создавая переменную экземпляра (@name) и соответствующий метод доступа для её чтения. Также создаёт метод name= для задания атрибута. Аргументы типа String преобразуются в символы.
module Mod attr_accessor(:one, :two) end Mod.instance_methods.sort #=> [:one, :one=, :two, :two=]
static VALUE
rb_mod_attr_reader(int argc, VALUE *argv, VALUE klass)
{
int i;
for (i=0; i<argc; i++) {
rb_attr(klass, id_for_attr(klass, argv[i]), TRUE, FALSE, TRUE);
}
return Qnil;
} Создаёт переменные экземпляров и соответствующие методы, возвращающие значение каждой переменной экземпляра. Эквивалентно вызову «attr:name'' для каждого имени по очереди. Аргументы типа String преобразуются в символы.
static VALUE
rb_mod_attr_writer(int argc, VALUE *argv, VALUE klass)
{
int i;
for (i=0; i<argc; i++) {
rb_attr(klass, id_for_attr(klass, argv[i]), FALSE, TRUE, TRUE);
}
return Qnil;
} Создаёт метод доступа для задания атрибута symbol.id2name. Аргументы типа String преобразуются в символы.
static VALUE
rb_mod_autoload(VALUE mod, VALUE sym, VALUE file)
{
ID id = rb_to_id(sym);
FilePathValue(file);
rb_autoload_str(mod, id, file);
return Qnil;
} Регистрирует filename для загрузки (с помощью Kernel::require) в первый раз, когда на module (который может быть String или символом) обращаются в пространстве имён mod.
module A end A.autoload(:B, "b") A::B.doit # autoloads "b"
static VALUE
rb_mod_autoload_p(VALUE mod, VALUE sym)
{
ID id = rb_check_id(&sym);
if (!id) {
return Qnil;
}
return rb_autoload_p(mod, id);
} Возвращает filename для загрузки, если name зарегистрирован как autoload в пространстве имён mod.
module A end A.autoload(:B, "b") A.autoload?(:B) #=> "b"
VALUE
rb_mod_module_eval(int argc, const VALUE *argv, VALUE mod)
{
return specific_eval(argc, argv, mod, mod);
} Вычисляет строку или блок в контексте mod, за исключением случаев, когда задан блок, поиск констант/переменных класса не затрагивается. Это можно использовать для добавления методов к классу. module_eval возвращает результат вычисления своего аргумента. Дополнительные параметры filename и lineno задают текст для сообщений об ошибках.
class Thing
end
a = %q{def hello() "Hello there!" end}
Thing.module_eval(a)
puts Thing.new.hello()
Thing.module_eval("invalid code", "dummy", 123)
возвращает:
Hello there!
dummy:123:in `module_eval': undefined local variable
or method `code' for Thing:Class VALUE
rb_mod_module_exec(int argc, const VALUE *argv, VALUE mod)
{
return yield_under(mod, mod, argc, argv);
} Вычисляет заданный блок в контексте класса/модуля. Определённый в блоке метод будет принадлежать получателю. Все аргументы, передаваемые методу, будут переданы блоку. Это можно использовать, если блоку необходимо получить доступ к переменным экземпляра.
class Thing
end
Thing.class_exec{
def hello() "Hello there!" end
}
puts Thing.new.hello()
возвращает:
Hello there!
static VALUE
rb_mod_cvar_defined(VALUE obj, VALUE iv)
{
ID id = id_for_var(obj, iv, a, class);
if (!id) {
return Qfalse;
}
return rb_cvar_defined(obj, id);
} Возвращает true, если заданная переменная класса определена в obj. String аргументы преобразуются в символы.
class Fred @@foo = 99 end Fred.class_variable_defined?(:@@foo) #=> true Fred.class_variable_defined?(:@@bar) #=> false
static VALUE
rb_mod_cvar_get(VALUE obj, VALUE iv)
{
ID id = id_for_var(obj, iv, a, class);
if (!id) {
rb_name_err_raise("uninitialized class variable %1$s in %2$s",
obj, iv);
}
return rb_cvar_get(obj, id);
} Возвращает значение заданной переменной класса (или вызывает исключение NameError). Часть имени переменной @@ должна быть включена для обычных переменных класса. String аргументы преобразуются в символы.
class Fred @@foo = 99 end Fred.class_variable_get(:@@foo) #=> 99
static VALUE
rb_mod_cvar_set(VALUE obj, VALUE iv, VALUE val)
{
ID id = id_for_var(obj, iv, a, class);
if (!id) id = rb_intern_str(iv);
rb_cvar_set(obj, id, val);
return val;
} Устанавливает переменную класса с именем symbol в заданный объект. Если имя переменной класса передано как строка, эта строка преобразуется в символ.
class Fred
@@foo = 99
def foo
@@foo
end
end
Fred.class_variable_set(:@@foo, 101) #=> 101
Fred.new.foo #=> 101
VALUE
rb_mod_class_variables(int argc, const VALUE *argv, VALUE mod)
{
bool inherit = TRUE;
st_table *tbl;
if (rb_check_arity(argc, 0, 1)) inherit = RTEST(argv[0]);
if (inherit) {
tbl = mod_cvar_of(mod, 0);
}
else {
tbl = mod_cvar_at(mod, 0);
}
return cvar_list(tbl);
} Возвращает массив имён переменных класса в mod. Это включает имена переменных класса во всех включённых модулях, если параметр inherit не установлен в false.
class One @@var1 = 1 end class Two < One @@var2 = 2 end One.class_variables #=> [:@@var1] Two.class_variables #=> [:@@var2, :@@var1] Two.class_variables(false) #=> [:@@var2]
static VALUE
rb_mod_const_defined(int argc, VALUE *argv, VALUE mod)
{
VALUE name, recur;
rb_encoding *enc;
const char *pbeg, *p, *path, *pend;
ID id;
rb_check_arity(argc, 1, 2);
name = argv[0];
recur = (argc == 1) ? Qtrue : argv[1];
if (SYMBOL_P(name)) {
if (!rb_is_const_sym(name)) goto wrong_name;
id = rb_check_id(&name);
if (!id) return Qfalse;
return RTEST(recur) ? rb_const_defined(mod, id) : rb_const_defined_at(mod, id);
}
path = StringValuePtr(name);
enc = rb_enc_get(name);
if (!rb_enc_asciicompat(enc)) {
rb_raise(rb_eArgError, "invalid class path encoding (non ASCII)");
}
pbeg = p = path;
pend = path + RSTRING_LEN(name);
if (p >= pend || !*p) {
wrong_name:
rb_name_err_raise(wrong_constant_name, mod, name);
}
if (p + 2 < pend && p[0] == ':' && p[1] == ':') {
mod = rb_cObject;
p += 2;
pbeg = p;
}
while (p < pend) {
VALUE part;
long len, beglen;
while (p < pend && *p != ':') p++;
if (pbeg == p) goto wrong_name;
id = rb_check_id_cstr(pbeg, len = p-pbeg, enc);
beglen = pbeg-path;
if (p < pend && p[0] == ':') {
if (p + 2 >= pend || p[1] != ':') goto wrong_name;
p += 2;
pbeg = p;
}
if (!id) {
part = rb_str_subseq(name, beglen, len);
OBJ_FREEZE(part);
if (!rb_is_const_name(part)) {
name = part;
goto wrong_name;
}
else {
return Qfalse;
}
}
if (!rb_is_const_id(id)) {
name = ID2SYM(id);
goto wrong_name;
}
if (RTEST(recur)) {
if (!rb_const_defined(mod, id))
return Qfalse;
mod = rb_const_get(mod, id);
}
else {
if (!rb_const_defined_at(mod, id))
return Qfalse;
mod = rb_const_get_at(mod, id);
}
if (p < pend && !RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) {
rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module",
QUOTE(name));
}
}
return Qtrue;
} Указывает, содержит ли mod или его предки константу с данным именем:
Float.const_defined?(:EPSILON) #=> true, found in Float itself
Float.const_defined?("String") #=> true, found in Object (ancestor)
BasicObject.const_defined?(:Hash) #=> false
Если mod является Module, дополнительно проверяются Object и его предки:
Math.const_defined?(:String) #=> true, found in Object
В каждом из проверяемых классов или модулей, если константа отсутствует, но есть автозагрузка для неё, true возвращается непосредственно без автозагрузки:
module Admin autoload :User, 'admin/user' end Admin.const_defined?(:User) #=> true
Если константа не найдена, обратный вызов const_missing не вызывается, и метод возвращает false.
Если inherit равно false, поиск проверяет только константы в получателе:
IO.const_defined?(:SYNC) #=> true, found in File::Constants (ancestor) IO.const_defined?(:SYNC, false) #=> false, not found in IO itself
В этом случае применяется та же логика автозагрузки.
Если аргумент не является допустимым именем константы, поднимается NameError с сообщением «неверное имя константы name»:
Hash.const_defined? 'foobar' #=> NameError: wrong constant name foobar
static VALUE
rb_mod_const_get(int argc, VALUE *argv, VALUE mod)
{
VALUE name, recur;
rb_encoding *enc;
const char *pbeg, *p, *path, *pend;
ID id;
rb_check_arity(argc, 1, 2);
name = argv[0];
recur = (argc == 1) ? Qtrue : argv[1];
if (SYMBOL_P(name)) {
if (!rb_is_const_sym(name)) goto wrong_name;
id = rb_check_id(&name);
if (!id) return rb_const_missing(mod, name);
return RTEST(recur) ? rb_const_get(mod, id) : rb_const_get_at(mod, id);
}
path = StringValuePtr(name);
enc = rb_enc_get(name);
if (!rb_enc_asciicompat(enc)) {
rb_raise(rb_eArgError, "invalid class path encoding (non ASCII)");
}
pbeg = p = path;
pend = path + RSTRING_LEN(name);
if (p >= pend || !*p) {
wrong_name:
rb_name_err_raise(wrong_constant_name, mod, name);
}
if (p + 2 < pend && p[0] == ':' && p[1] == ':') {
mod = rb_cObject;
p += 2;
pbeg = p;
}
while (p < pend) {
VALUE part;
long len, beglen;
while (p < pend && *p != ':') p++;
if (pbeg == p) goto wrong_name;
id = rb_check_id_cstr(pbeg, len = p-pbeg, enc);
beglen = pbeg-path;
if (p < pend && p[0] == ':') {
if (p + 2 >= pend || p[1] != ':') goto wrong_name;
p += 2;
pbeg = p;
}
if (!RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) {
rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module",
QUOTE(name));
}
if (!id) {
part = rb_str_subseq(name, beglen, len);
OBJ_FREEZE(part);
if (!rb_is_const_name(part)) {
name = part;
goto wrong_name;
}
else if (!rb_method_basic_definition_p(CLASS_OF(mod), id_const_missing)) {
part = rb_str_intern(part);
mod = rb_const_missing(mod, part);
continue;
}
else {
rb_mod_const_missing(mod, part);
}
}
if (!rb_is_const_id(id)) {
name = ID2SYM(id);
goto wrong_name;
}
mod = RTEST(recur) ? rb_const_get(mod, id) : rb_const_get_at(mod, id);
}
return mod;
} Ищет константу с заданным именем в mod. Если inherit установлено, поиск также будет производиться в предках (и Object если mod является Module).
Значение константы возвращается, если определение найдено, в противном случае генерируется NameError.
Math.const_get(:PI) #=> 3.14159265358979
Этот метод будет рекурсивно искать имена констант, если предоставлено имя класса с именованным пространством. Например:
module Foo; class Bar; end end Object.const_get 'Foo::Bar'
Флаг inherit учитывается при каждом поиске. Например:
module Foo
class Bar
VAL = 10
end
class Baz < Bar; end
end
Object.const_get 'Foo::Baz::VAL' # => 10
Object.const_get 'Foo::Baz::VAL', false # => NameError
Если аргумент не является допустимым именем константы, будет генерироваться NameError с предупреждением «неверное имя константы».
Object.const_get 'foobar' #=> NameError: wrong constant name foobar
VALUE
rb_mod_const_missing(VALUE klass, VALUE name)
{
VALUE ref = GET_EC()->private_const_reference;
rb_vm_pop_cfunc_frame();
if (ref) {
rb_name_err_raise("private constant %2$s::%1$s referenced",
ref, name);
}
uninitialized_constant(klass, name);
UNREACHABLE_RETURN(Qnil);
} Вызывается при обращении к неопределённой константе в mod. Ему передаётся символ неопределённой константы, и он возвращает значение, используемое для этой константы. Следующий код является примером того же:
def Foo.const_missing(name) name # return the constant name as Symbol end Foo::UNDEFINED_CONST #=> :UNDEFINED_CONST: symbol returned
В следующем примере, когда происходит ссылка на неопределённую константу, она пытается загрузить файл, имя которого является строчной версией константы (следовательно, предполагается, что класс Fred находится в файле fred.rb). Если найдено, возвращается загруженный класс. Таким образом, он реализует функцию автозагрузки, аналогичную Kernel#autoload и Module#autoload.
def Object.const_missing(name)
@looked_for ||= {}
str_name = name.to_s
raise "Class not found: #{name}" if @looked_for[str_name]
@looked_for[str_name] = 1
file = str_name.downcase
require file
klass = const_get(name)
return klass if klass
raise "Class not found: #{name}"
end
static VALUE
rb_mod_const_set(VALUE mod, VALUE name, VALUE value)
{
ID id = id_for_setter(mod, name, const, wrong_constant_name);
if (!id) id = rb_intern_str(name);
rb_const_set(mod, id, value);
return value;
} Устанавливает именованную константу в заданный объект, возвращая этот объект. Создаёт новую константу, если константа с данным именем ранее не существовала.
Math.const_set("HIGH_SCHOOL_PI", 22.0/7.0) #=> 3.14285714285714
Math::HIGH_SCHOOL_PI - Math::PI #=> 0.00126448926734968
Если sym или str не являются допустимым именем константы, будет генерироваться NameError с предупреждением «неверное имя константы».
Object.const_set('foobar', 42) #=> NameError: wrong constant name foobar
VALUE
rb_mod_constants(int argc, const VALUE *argv, VALUE mod)
{
bool inherit = TRUE;
if (rb_check_arity(argc, 0, 1)) inherit = RTEST(argv[0]);
if (inherit) {
return rb_const_list(rb_mod_const_of(mod, 0));
}
else {
return rb_local_constants(mod);
}
} Возвращает массив имён констант, доступных в mod. Это включает имена констант во всех включённых модулях (пример в начале раздела), если параметр inherit не установлен в false.
Реализация не гарантирует порядок, в котором константы генерируются.
IO.constants.include?(:SYNC) #=> true IO.constants(false).include?(:SYNC) #=> false
См. также Module::const_defined?.
static VALUE
rb_mod_define_method(int argc, VALUE *argv, VALUE mod)
{
ID id;
VALUE body;
VALUE name;
const rb_cref_t *cref = rb_vm_cref_in_context(mod, mod);
const rb_scope_visibility_t default_scope_visi = {METHOD_VISI_PUBLIC, FALSE};
const rb_scope_visibility_t *scope_visi = &default_scope_visi;
int is_method = FALSE;
if (cref) {
scope_visi = CREF_SCOPE_VISI(cref);
}
rb_check_arity(argc, 1, 2);
name = argv[0];
id = rb_check_id(&name);
if (argc == 1) {
#if PROC_NEW_REQUIRES_BLOCK
body = rb_block_lambda();
#else
const rb_execution_context_t *ec = GET_EC();
VALUE block_handler = rb_vm_frame_block_handler(ec->cfp);
if (block_handler == VM_BLOCK_HANDLER_NONE) rb_raise(rb_eArgError, proc_without_block);
switch (vm_block_handler_type(block_handler)) {
case block_handler_type_proc:
body = VM_BH_TO_PROC(block_handler);
break;
case block_handler_type_symbol:
body = rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler));
break;
case block_handler_type_iseq:
case block_handler_type_ifunc:
body = rb_vm_make_lambda(ec, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc);
}
#endif
}
else {
body = argv[1];
if (rb_obj_is_method(body)) {
is_method = TRUE;
}
else if (rb_obj_is_proc(body)) {
is_method = FALSE;
}
else {
rb_raise(rb_eTypeError,
"wrong argument type %s (expected Proc/Method)",
rb_obj_classname(body));
}
}
if (!id) id = rb_to_id(name);
if (is_method) {
struct METHOD *method = (struct METHOD *)DATA_PTR(body);
if (method->me->owner != mod && !RB_TYPE_P(method->me->owner, T_MODULE) &&
!RTEST(rb_class_inherited_p(mod, method->me->owner))) {
if (FL_TEST(method->me->owner, FL_SINGLETON)) {
rb_raise(rb_eTypeError,
"can't bind singleton method to a different class");
}
else {
rb_raise(rb_eTypeError,
"bind argument must be a subclass of % "PRIsVALUE,
method->me->owner);
}
}
rb_method_entry_set(mod, id, method->me, scope_visi->method_visi);
if (scope_visi->module_func) {
rb_method_entry_set(rb_singleton_class(mod), id, method->me, METHOD_VISI_PUBLIC);
}
RB_GC_GUARD(body);
}
else {
VALUE procval = rb_proc_dup(body);
if (vm_proc_iseq(procval) != NULL) {
rb_proc_t *proc;
GetProcPtr(procval, proc);
proc->is_lambda = TRUE;
proc->is_from_method = TRUE;
}
rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)procval, scope_visi->method_visi);
if (scope_visi->module_func) {
rb_add_method(rb_singleton_class(mod), id, VM_METHOD_TYPE_BMETHOD, (void *)body, METHOD_VISI_PUBLIC);
}
}
return ID2SYM(id);
} Определяет метод экземпляра в получателе. Параметр method может быть Proc, Method или UnboundMethod объектом. Если указан блок, он используется в качестве тела метода. Этот блок вычисляется с использованием instance_eval.
class A
def fred
puts "In Fred"
end
def create_method(name, &block)
self.class.define_method(name, &block)
end
define_method(:wilma) { puts "Charge it!" }
end
class B < A
define_method(:barney, instance_method(:fred))
end
a = B.new
a.barney
a.wilma
a.create_method(:betty) { p self }
a.betty
возвращает:
In Fred Charge it! #<B:0x401b39e8>
VALUE
rb_mod_deprecate_constant(int argc, const VALUE *argv, VALUE obj)
{
set_const_visibility(obj, argc, argv, CONST_DEPRECATED, CONST_DEPRECATED);
return obj;
} Делает список существующих констант устаревшими.
static VALUE
rb_mod_freeze(VALUE mod)
{
rb_class_name(mod);
return rb_obj_freeze(mod);
} Предотвращает дальнейшие изменения в mod.
Этот метод возвращает self.
static VALUE
rb_mod_include(int argc, VALUE *argv, VALUE module)
{
int i;
ID id_append_features, id_included;
CONST_ID(id_append_features, "append_features");
CONST_ID(id_included, "included");
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
for (i = 0; i < argc; i++)
Check_Type(argv[i], T_MODULE);
while (argc--) {
rb_funcall(argv[argc], id_append_features, 1, module);
rb_funcall(argv[argc], id_included, 1, module);
}
return module;
} Вызывает Module.append_features для каждого параметра в обратном порядке.
VALUE
rb_mod_include_p(VALUE mod, VALUE mod2)
{
VALUE p;
Check_Type(mod2, T_MODULE);
for (p = RCLASS_SUPER(mod); p; p = RCLASS_SUPER(p)) {
if (BUILTIN_TYPE(p) == T_ICLASS) {
if (RBASIC(p)->klass == mod2) return Qtrue;
}
}
return Qfalse;
} Возвращает true, если модуль включён в mod или один из предков mod.
module A end class B include A end class C < B end B.include?(A) #=> true C.include?(A) #=> true A.include?(A) #=> false
VALUE
rb_mod_included_modules(VALUE mod)
{
VALUE ary = rb_ary_new();
VALUE p;
VALUE origin = RCLASS_ORIGIN(mod);
for (p = RCLASS_SUPER(mod); p; p = RCLASS_SUPER(p)) {
if (p != origin && BUILTIN_TYPE(p) == T_ICLASS) {
VALUE m = RBASIC(p)->klass;
if (RB_TYPE_P(m, T_MODULE))
rb_ary_push(ary, m);
}
}
return ary;
} Возвращает список модулей, включённых в mod.
module Mixin end module Outer include Mixin end Mixin.included_modules #=> [] Outer.included_modules #=> [Mixin]
static VALUE
rb_mod_instance_method(VALUE mod, VALUE vid)
{
ID id = rb_check_id(&vid);
if (!id) {
rb_method_name_error(mod, vid);
}
return mnew(mod, Qundef, id, rb_cUnboundMethod, FALSE);
} Возвращает UnboundMethod, представляющую заданный метод экземпляра в mod.
class Interpreter
def do_a() print "there, "; end
def do_d() print "Hello "; end
def do_e() print "!\n"; end
def do_v() print "Dave"; end
Dispatcher = {
"a" => instance_method(:do_a),
"d" => instance_method(:do_d),
"e" => instance_method(:do_e),
"v" => instance_method(:do_v)
}
def interpret(string)
string.each_char {|b| Dispatcher[b].bind(self).call }
end
end
interpreter = Interpreter.new
interpreter.interpret('dave')
выводит:
Hello there, Dave!
VALUE
rb_class_instance_methods(int argc, const VALUE *argv, VALUE mod)
{
return class_instance_method_list(argc, argv, mod, 0, ins_methods_i);
} Возвращает массив, содержащий имена публичных и защищённых методов экземпляров в получателе. Для модуля — это публичные и защищённые методы; для класса — методы экземпляров (не синглтонов). Если необязательный параметр равен false, методы предков не включаются.
module A def method1() end end class B include A def method2() end end class C < B def method3() end end A.instance_methods(false) #=> [:method1] B.instance_methods(false) #=> [:method2] B.instance_methods(true).include?(:method1) #=> true C.instance_methods(false) #=> [:method3] C.instance_methods.include?(:method2) #=> true
static VALUE
rb_mod_method_defined(int argc, VALUE *argv, VALUE mod)
{
rb_method_visibility_t visi = check_definition_visibility(mod, argc, argv);
return (visi == METHOD_VISI_PUBLIC || visi == METHOD_VISI_PROTECTED) ? Qtrue : Qfalse;
} Возвращает true, если именованный метод определён в mod. Если inherit установлено, поиск также будет производиться в предках mod. Публичные и защищённые методы совпадают. Аргументы типа String преобразуются в символы.
module A def method1() end def protected_method1() end protected :protected_method1 end class B def method2() end def private_method2() end private :private_method2 end class C < B include A def method3() end end A.method_defined? :method1 #=> true C.method_defined? "method1" #=> true C.method_defined? "method2" #=> true C.method_defined? "method2", true #=> true C.method_defined? "method2", false #=> false C.method_defined? "method3" #=> true C.method_defined? "protected_method1" #=> true C.method_defined? "method4" #=> false C.method_defined? "private_method2" #=> false
VALUE
rb_mod_module_eval(int argc, const VALUE *argv, VALUE mod)
{
return specific_eval(argc, argv, mod, mod);
} Вычисляет строку или блок в контексте mod, за исключением случаев, когда задан блок, поиск констант/переменных класса не затрагивается. Это можно использовать для добавления методов в класс. module_eval возвращает результат вычисления своего аргумента. Необязательные параметры filename и lineno задают текст сообщений об ошибках.
class Thing
end
a = %q{def hello() "Hello there!" end}
Thing.module_eval(a)
puts Thing.new.hello()
Thing.module_eval("invalid code", "dummy", 123)
выводит:
Hello there!
dummy:123:in `module_eval': undefined local variable
or method `code' for Thing:Class VALUE
rb_mod_module_exec(int argc, const VALUE *argv, VALUE mod)
{
return yield_under(mod, mod, argc, argv);
} Вычисляет заданный блок в контексте класса/модуля. Метод, определённый в блоке, будет принадлежать получателю. Любые аргументы, переданные методу, будут переданы блоку. Это можно использовать, если блоку необходимо получить доступ к переменным экземпляра.
class Thing
end
Thing.class_exec{
def hello() "Hello there!" end
}
puts Thing.new.hello()
выводит:
Hello there!
VALUE
rb_mod_name(VALUE mod)
{
int permanent;
VALUE path = classname(mod, &permanent);
if (!NIL_P(path)) return rb_str_dup(path);
return path;
} Возвращает имя модуля mod. Возвращает nil для анонимных модулей.
static VALUE
rb_mod_prepend(int argc, VALUE *argv, VALUE module)
{
int i;
ID id_prepend_features, id_prepended;
CONST_ID(id_prepend_features, "prepend_features");
CONST_ID(id_prepended, "prepended");
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
for (i = 0; i < argc; i++)
Check_Type(argv[i], T_MODULE);
while (argc--) {
rb_funcall(argv[argc], id_prepend_features, 1, module);
rb_funcall(argv[argc], id_prepended, 1, module);
}
return module;
} Вызывает Module.prepend_features для каждого параметра в обратном порядке.
static VALUE
rb_mod_private_method(int argc, VALUE *argv, VALUE obj)
{
set_method_visibility(rb_singleton_class(obj), argc, argv, METHOD_VISI_PRIVATE);
return obj;
} Делает существующие методы класса закрытыми. Часто используется для скрытия конструктора по умолчанию new.
String аргументы преобразуются в символы.
class SimpleSingleton # Not thread safe
private_class_method :new
def SimpleSingleton.create(*args, &block)
@me = new(*args, &block) if ! @me
@me
end
end
VALUE
rb_mod_private_constant(int argc, const VALUE *argv, VALUE obj)
{
set_const_visibility(obj, argc, argv, CONST_PRIVATE, CONST_VISIBILITY_MASK);
return obj;
} Делает список существующих констант закрытыми.
VALUE
rb_class_private_instance_methods(int argc, const VALUE *argv, VALUE mod)
{
return class_instance_method_list(argc, argv, mod, 0, ins_methods_priv_i);
} Возвращает список закрытых методов экземпляров, определённых в mod. Если необязательный параметр равен false, методы предков не включаются.
module Mod def method1() end private :method1 def method2() end end Mod.instance_methods #=> [:method2] Mod.private_instance_methods #=> [:method1]
static VALUE
rb_mod_private_method_defined(int argc, VALUE *argv, VALUE mod)
{
return check_definition(mod, argc, argv, METHOD_VISI_PRIVATE);
} Возвращает true, если именованный закрытый метод определён в mod. Если inherit установлено, поиск также будет производиться в предках mod. Аргументы типа String преобразуются в символы.
module A def method1() end end class B private def method2() end end class C < B include A def method3() end end A.method_defined? :method1 #=> true C.private_method_defined? "method1" #=> false C.private_method_defined? "method2" #=> true C.private_method_defined? "method2", true #=> true C.private_method_defined? "method2", false #=> false C.method_defined? "method2" #=> false
VALUE
rb_class_protected_instance_methods(int argc, const VALUE *argv, VALUE mod)
{
return class_instance_method_list(argc, argv, mod, 0, ins_methods_prot_i);
} Возвращает список защищённых методов экземпляров, определённых в mod. Если необязательный параметр равен false, методы предков не включаются.
static VALUE
rb_mod_protected_method_defined(int argc, VALUE *argv, VALUE mod)
{
return check_definition(mod, argc, argv, METHOD_VISI_PROTECTED);
} Возвращает true, если именованный защищённый метод определён в mod. Если inherit установлено, поиск также будет производиться в предках mod. Аргументы типа String преобразуются в символы.
module A def method1() end end class B protected def method2() end end class C < B include A def method3() end end A.method_defined? :method1 #=> true C.protected_method_defined? "method1" #=> false C.protected_method_defined? "method2" #=> true C.protected_method_defined? "method2", true #=> true C.protected_method_defined? "method2", false #=> false C.method_defined? "method2" #=> true
static VALUE
rb_mod_public_method(int argc, VALUE *argv, VALUE obj)
{
set_method_visibility(rb_singleton_class(obj), argc, argv, METHOD_VISI_PUBLIC);
return obj;
} Делает список существующих методов класса публичными.
String аргументы преобразуются в символы.
VALUE
rb_mod_public_constant(int argc, const VALUE *argv, VALUE obj)
{
set_const_visibility(obj, argc, argv, CONST_PUBLIC, CONST_VISIBILITY_MASK);
return obj;
} Делает список существующих констант публичными.
static VALUE
rb_mod_public_instance_method(VALUE mod, VALUE vid)
{
ID id = rb_check_id(&vid);
if (!id) {
rb_method_name_error(mod, vid);
}
return mnew(mod, Qundef, id, rb_cUnboundMethod, TRUE);
} Аналогично instance_method, ищет только публичные методы.
VALUE
rb_class_public_instance_methods(int argc, const VALUE *argv, VALUE mod)
{
return class_instance_method_list(argc, argv, mod, 0, ins_methods_pub_i);
} Возвращает список общедоступных методов экземпляра, определенных в mod. Если необязательный параметр равен false, методы предков не включаются.
static VALUE
rb_mod_public_method_defined(int argc, VALUE *argv, VALUE mod)
{
return check_definition(mod, argc, argv, METHOD_VISI_PUBLIC);
} Возвращает true, если указанный общедоступный метод определен в mod. Если inherit установлено, поиск также будет производиться у предков mod. String аргументы преобразуются в символы.
module A def method1() end end class B protected def method2() end end class C < B include A def method3() end end A.method_defined? :method1 #=> true C.public_method_defined? "method1" #=> true C.public_method_defined? "method1", true #=> true C.public_method_defined? "method1", false #=> true C.public_method_defined? "method2" #=> false C.method_defined? "method2" #=> true
VALUE
rb_mod_remove_cvar(VALUE mod, VALUE name)
{
const ID id = id_for_var_message(mod, name, class, "wrong class variable name %1$s");
st_data_t val, n = id;
if (!id) {
not_defined:
rb_name_err_raise("class variable %1$s not defined for %2$s",
mod, name);
}
rb_check_frozen(mod);
if (RCLASS_IV_TBL(mod) && st_delete(RCLASS_IV_TBL(mod), &n, &val)) {
return (VALUE)val;
}
if (rb_cvar_defined(mod, id)) {
rb_name_err_raise("cannot remove %1$s for %2$s", mod, ID2SYM(id));
}
goto not_defined;
} Удаляет определение символа, возвращая значение этой константы.
class Dummy @@var = 99 puts @@var remove_class_variable(:@@var) p(defined? @@var) end
Результат:
99 nil
static VALUE
rb_mod_remove_method(int argc, VALUE *argv, VALUE mod)
{
int i;
for (i = 0; i < argc; i++) {
VALUE v = argv[i];
ID id = rb_check_id(&v);
if (!id) {
rb_name_err_raise("method `%1$s' not defined in %2$s",
mod, v);
}
remove_method(mod, id);
}
return mod;
} Удаляет метод, идентифицируемый символом symbol, из текущего класса. Пример см. в Module.undef_method. String аргументы преобразуются в символы.
static VALUE
rb_mod_singleton_p(VALUE klass)
{
if (RB_TYPE_P(klass, T_CLASS) && FL_TEST(klass, FL_SINGLETON))
return Qtrue;
return Qfalse;
} Возвращает true, если mod является классом-синглтоном, или false, если это обычный класс или модуль.
class C end C.singleton_class? #=> false C.singleton_class.singleton_class? #=> true
static VALUE
rb_mod_to_s(VALUE klass)
{
ID id_defined_at;
VALUE refined_class, defined_at;
if (FL_TEST(klass, FL_SINGLETON)) {
VALUE s = rb_usascii_str_new2("#<Class:");
VALUE v = rb_ivar_get(klass, id__attached__);
if (CLASS_OR_MODULE_P(v)) {
rb_str_append(s, rb_inspect(v));
}
else {
rb_str_append(s, rb_any_to_s(v));
}
rb_str_cat2(s, ">");
return s;
}
refined_class = rb_refinement_module_get_refined_class(klass);
if (!NIL_P(refined_class)) {
VALUE s = rb_usascii_str_new2("#<refinement:");
rb_str_concat(s, rb_inspect(refined_class));
rb_str_cat2(s, "@");
CONST_ID(id_defined_at, "__defined_at__");
defined_at = rb_attr_get(klass, id_defined_at);
rb_str_concat(s, rb_inspect(defined_at));
rb_str_cat2(s, ">");
return s;
}
return rb_str_dup(rb_class_name(klass));
} Возвращает строку, представляющую этот модуль или класс. Для базовых классов и модулей это имя. Для синглтонов отображается информация о связанном объекте.
static VALUE
rb_mod_undef_method(int argc, VALUE *argv, VALUE mod)
{
int i;
for (i = 0; i < argc; i++) {
VALUE v = argv[i];
ID id = rb_check_id(&v);
if (!id) {
rb_method_name_error(mod, v);
}
rb_undef(mod, id);
}
return mod;
} Запрещает текущему классу реагировать на вызовы именованного метода. В отличие от remove_method, которое удаляет метод из конкретного класса, Ruby всё ещё будет искать возможного получателя в родительских классах и смешанных модулях. String аргументы преобразуются в символы.
class Parent
def hello
puts "In parent"
end
end
class Child < Parent
def hello
puts "In child"
end
end
c = Child.new
c.hello
class Child
remove_method :hello # remove from child, still in parent
end
c.hello
class Child
undef_method :hello # prevent any calls to 'hello'
end
c.hello
Результат:
In child In parent prog.rb:23: undefined method `hello' for #<Child:0x401b3bb4> (NoMethodError)
Закрытые методы экземпляра
static VALUE
rb_mod_append_features(VALUE module, VALUE include)
{
if (!CLASS_OR_MODULE_P(include)) {
Check_Type(include, T_CLASS);
}
rb_include_module(include, module);
return module;
} Когда этот модуль включается в другой, Ruby вызывает append_features в этом модуле, передавая принимающий модуль в mod. По умолчанию Ruby добавляет константы, методы и переменные модуля этого модуля в mod, если этот модуль еще не добавлен в mod или один из его предков. См. также Module#include.
static VALUE
rb_mod_extend_object(VALUE mod, VALUE obj)
{
rb_extend_object(obj, mod);
return obj;
} Расширяет указанный объект, добавляя константы и методы этого модуля (которые добавляются как методы-синглтоны). Это метод обратного вызова, используемый Object#extend.
module Picky
def Picky.extend_object(o)
if String === o
puts "Can't add Picky to a String"
else
puts "Picky added to #{o.class}"
super
end
end
end
(s = Array.new).extend Picky # Call Object.extend
(s = "quick brown fox").extend Picky
выводит:
Picky added to Array Can't add Picky to a String
static VALUE
rb_obj_dummy(void)
{
return Qnil;
} Эквивалент included, но для расширенных модулей.
module A
def self.extended(mod)
puts "#{self} extended in #{mod}"
end
end
module Enumerable
extend A
end
# => prints "A extended in Enumerable"
static VALUE
rb_obj_dummy(void)
{
return Qnil;
} Обратный вызов, вызываемый всякий раз, когда получатель включается в другой модуль или класс. Его следует использовать вместо Module.append_features, если ваш код хочет выполнить какое-либо действие при включении модуля в другой.
module A
def A.included(mod)
puts "#{self} included in #{mod}"
end
end
module Enumerable
include A
end
# => prints "A included in Enumerable"
static VALUE
rb_obj_dummy(void)
{
return Qnil;
} Вызывается как обратный вызов всякий раз, когда к получателю добавляется метод экземпляра.
module Chatty
def self.method_added(method_name)
puts "Adding #{method_name.inspect}"
end
def self.some_class_method() end
def some_instance_method() end
end
выводит:
Adding :some_instance_method
static VALUE
rb_obj_dummy(void)
{
return Qnil;
} Вызывается как обратный вызов всякий раз, когда из получателя удаляется метод экземпляра.
module Chatty
def self.method_removed(method_name)
puts "Removing #{method_name.inspect}"
end
def self.some_class_method() end
def some_instance_method() end
class << self
remove_method :some_class_method
end
remove_method :some_instance_method
end
выводит:
Removing :some_instance_method
static VALUE
rb_obj_dummy(void)
{
return Qnil;
} Не задокументировано
static VALUE
rb_mod_modfunc(int argc, VALUE *argv, VALUE module)
{
int i;
ID id;
const rb_method_entry_t *me;
if (!RB_TYPE_P(module, T_MODULE)) {
rb_raise(rb_eTypeError, "module_function must be called for modules");
}
if (argc == 0) {
rb_scope_module_func_set();
return module;
}
set_method_visibility(module, argc, argv, METHOD_VISI_PRIVATE);
for (i = 0; i < argc; i++) {
VALUE m = module;
id = rb_to_id(argv[i]);
for (;;) {
me = search_method(m, id, 0);
if (me == 0) {
me = search_method(rb_cObject, id, 0);
}
if (UNDEFINED_METHOD_ENTRY_P(me)) {
rb_print_undef(module, id, METHOD_VISI_UNDEF);
}
if (me->def->type != VM_METHOD_TYPE_ZSUPER) {
break; /* normal case: need not to follow 'super' link */
}
m = RCLASS_SUPER(m);
if (!m)
break;
}
rb_method_entry_set(rb_singleton_class(module), id, me, METHOD_VISI_PUBLIC);
}
return module;
} Создаёт методы модуля для именованных методов. Эти методы могут вызываться с модулем в качестве получателя, а также становятся доступными как методы экземпляра для классов, которые включают модуль. Методы Module являются копиями оригиналов и поэтому могут быть изменены независимо. Версии методов экземпляров сделаны закрытыми. Если используется без аргументов, все последующие определённые методы станут методами модуля. Аргументы String преобразуются в символы.
module Mod
def one
"This is one"
end
module_function :one
end
class Cls
include Mod
def call_one
one
end
end
Mod.one #=> "This is one"
c = Cls.new
c.call_one #=> "This is one"
module Mod
def one
"This is the new one"
end
end
Mod.one #=> "This is one"
c.call_one #=> "This is the new one"
static VALUE
rb_mod_prepend_features(VALUE module, VALUE prepend)
{
if (!CLASS_OR_MODULE_P(prepend)) {
Check_Type(prepend, T_CLASS);
}
rb_prepend_module(prepend, module);
return module;
} Когда этот модуль предваряется в другом, Ruby вызывает prepend_features в этом модуле, передавая принимающий модуль в mod. По умолчанию Ruby перекрывает константы, методы и переменные модуля этого модуля в mod, если этот модуль ещё не добавлен в mod или один из его предков. См. также Module#prepend.
static VALUE
rb_obj_dummy(void)
{
return Qnil;
} Эквивалент included, но для предваряемых модулей.
module A
def self.prepended(mod)
puts "#{self} prepended to #{mod}"
end
end
module Enumerable
prepend A
end
# => prints "A prepended to Enumerable"
static VALUE
rb_mod_private(int argc, VALUE *argv, VALUE module)
{
return set_visibility(argc, argv, module, METHOD_VISI_PRIVATE);
} Без аргументов устанавливает стандартную видимость для последующих определённых методов как закрытых. С аргументами устанавливает видимость указанных методов как закрытых. Аргументы String преобразуются в символы.
module Mod def a() end def b() end private def c() end private :a end Mod.private_instance_methods #=> [:a, :c]
Обратите внимание, что чтобы показать закрытый метод на RDoc, используйте :doc:.
static VALUE
rb_mod_protected(int argc, VALUE *argv, VALUE module)
{
return set_visibility(argc, argv, module, METHOD_VISI_PROTECTED);
} Без аргументов устанавливает стандартную видимость для последующих определённых методов как защищённых. С аргументами устанавливает видимость указанных методов как защищённых. Аргументы String преобразуются в символы.
Если у метода есть защищённая видимость, он доступен только там, где контекст self совпадает с методом (определение метода или instance_eval). Это поведение отличается от защищённого метода в Java. Обычно нужно использовать private.
Обратите внимание, что защищённый метод медленнее, потому что он не может использовать кэш.
Чтобы показать закрытый метод на RDoc, используйте :doc: вместо этого.
static VALUE
rb_mod_public(int argc, VALUE *argv, VALUE module)
{
return set_visibility(argc, argv, module, METHOD_VISI_PUBLIC);
} Без аргументов устанавливает стандартную видимость для последующих определённых методов как открытых. С аргументами устанавливает видимость указанных методов как открытых. Аргументы String преобразуются в символы.
static VALUE
rb_mod_refine(VALUE module, VALUE klass)
{
VALUE refinement;
ID id_refinements, id_activated_refinements,
id_refined_class, id_defined_at;
VALUE refinements, activated_refinements;
rb_thread_t *th = GET_THREAD();
VALUE block_handler = rb_vm_frame_block_handler(th->ec->cfp);
if (block_handler == VM_BLOCK_HANDLER_NONE) {
rb_raise(rb_eArgError, "no block given");
}
if (vm_block_handler_type(block_handler) != block_handler_type_iseq) {
rb_raise(rb_eArgError, "can't pass a Proc as a block to Module#refine");
}
ensure_class_or_module(klass);
CONST_ID(id_refinements, "__refinements__");
refinements = rb_attr_get(module, id_refinements);
if (NIL_P(refinements)) {
refinements = hidden_identity_hash_new();
rb_ivar_set(module, id_refinements, refinements);
}
CONST_ID(id_activated_refinements, "__activated_refinements__");
activated_refinements = rb_attr_get(module, id_activated_refinements);
if (NIL_P(activated_refinements)) {
activated_refinements = hidden_identity_hash_new();
rb_ivar_set(module, id_activated_refinements,
activated_refinements);
}
refinement = rb_hash_lookup(refinements, klass);
if (NIL_P(refinement)) {
VALUE superclass = refinement_superclass(klass);
refinement = rb_module_new();
RCLASS_SET_SUPER(refinement, superclass);
FL_SET(refinement, RMODULE_IS_REFINEMENT);
CONST_ID(id_refined_class, "__refined_class__");
rb_ivar_set(refinement, id_refined_class, klass);
CONST_ID(id_defined_at, "__defined_at__");
rb_ivar_set(refinement, id_defined_at, module);
rb_hash_aset(refinements, klass, refinement);
add_activated_refinement(activated_refinements, klass, refinement);
}
rb_yield_refine_block(refinement, activated_refinements);
return refinement;
} Уточнить mod в получателе.
Возвращает модуль, где определены уточнённые методы.
VALUE
rb_mod_remove_const(VALUE mod, VALUE name)
{
const ID id = id_for_var(mod, name, a, constant);
if (!id) {
rb_name_err_raise("constant %2$s::%1$s not defined",
mod, name);
}
return rb_const_remove(mod, id);
} Удаляет определение указанной константы, возвращая предыдущее значение этой константы. Если эта константа ссылалась на модуль, это не изменит имя модуля и может привести к путанице.
static VALUE
mod_using(VALUE self, VALUE module)
{
rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
if (prev_frame_func()) {
rb_raise(rb_eRuntimeError,
"Module#using is not permitted in methods");
}
if (prev_cfp && prev_cfp->self != self) {
rb_raise(rb_eRuntimeError, "Module#using is not called on self");
}
if (rb_block_given_p()) {
ignored_block(module, "Module#");
}
rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module);
return self;
} Импортирует уточнения классов из module в текущее определение класса или модуля.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.