класс Module
Модуль — это набор методов и констант. Методы модуля могут быть методами экземпляра или методами модуля. Методы экземпляра становятся методами класса, в который включён модуль, а методы модуля — нет. И наоборот, методы модуля можно вызывать без создания объекта-обёртки, а методы экземпляра — нельзя. (См. 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(VALUE _)
{
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)
{
return rb_mod_initialize_exec(module);
} Создаёт новый анонимный модуль. Если передан блок, ему передаётся объект модуля, а сам блок выполняется в контексте этого модуля, как в 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(VALUE _)
{
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_s_used_refinements(VALUE _)
{
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_refinements_i, ary);
}
cref = CREF_NEXT(cref);
}
return ary;
} Возвращает массив всех модулей, используемых в текущей области видимости. Порядок модулей в результирующем массиве не определён.
module A refine Object do end end module B refine Object do end end using A using B p Module.used_refinements
результат:
[#<refinement:Object@B>, #<refinement:Object@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. Возвращает false, если mod совпадает с other или mod является предком other. Возвращает nil, если эти два объекта не связаны. (Представьте эту связь в терминах определения класса: «class A < B» подразумевает «A < B».)
VALUE
rb_class_inherited_p(VALUE mod, VALUE arg)
{
if (mod == arg) return Qtrue;
if (RB_TYPE_P(arg, T_CLASS) && RB_TYPE_P(mod, T_CLASS)) {
// comparison between classes
size_t mod_depth = RCLASS_SUPERCLASS_DEPTH(mod);
size_t arg_depth = RCLASS_SUPERCLASS_DEPTH(arg);
if (arg_depth < mod_depth) {
// check if mod < arg
return RCLASS_SUPERCLASSES(mod)[arg_depth] == arg ?
Qtrue :
Qnil;
}
else if (arg_depth > mod_depth) {
// check if mod > arg
return RCLASS_SUPERCLASSES(arg)[mod_depth] == mod ?
Qfalse :
Qnil;
}
else {
// Depths match, and we know they aren't equal: no relation
return Qnil;
}
}
else {
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 не является модулем или если эти два значения несравнимы.
VALUE
rb_obj_equal(VALUE obj1, VALUE obj2)
{
return RBOOL(obj1 == obj2);
} Равенство — на уровне 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 для проверки равенства элементов. Для любой пары объектов, для которой eql? возвращает true, значения hash обоих объектов должны быть равны. Поэтому любой подкласс, переопределяющий eql?, должен также соответствующим образом переопределить 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);
} Равенство при сопоставлении с case — возвращает true, если obj является экземпляром 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. Возвращает false, если mod совпадает с other или является потомком 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);
}
VALUE id = rb_to_id(newname);
rb_alias(mod, id, oldid);
return ID2SYM(id);
} Создаёт новую копию метода old_name под именем new_name. Это можно использовать, чтобы сохранить доступ к переопределяемым методам.
module Mod
alias_method :orig_exit, :exit #=> :orig_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();
VALUE refined_class = Qnil;
if (BUILTIN_TYPE(mod) == T_MODULE && FL_TEST(mod, RMODULE_IS_REFINEMENT)) {
refined_class = rb_refinement_module_get_refined_class(mod);
}
for (p = mod; p; p = RCLASS_SUPER(p)) {
if (p == refined_class) break;
if (p != RCLASS_ORIGIN(p)) continue;
if (BUILTIN_TYPE(p) == T_ICLASS) {
rb_ary_push(ary, METACLASS_OF(p));
}
else {
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)) {
ID id = id_for_attr(klass, argv[0]);
VALUE names = rb_ary_new();
rb_category_warning(RB_WARN_CATEGORY_DEPRECATED, "optional boolean argument is obsoleted");
rb_attr(klass, id, 1, RTEST(argv[1]), TRUE);
rb_ary_push(names, ID2SYM(id));
if (argv[1] == Qtrue) rb_ary_push(names, ID2SYM(rb_id_attrset(id)));
return names;
}
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;
VALUE names = rb_ary_new2(argc * 2);
for (i=0; i<argc; i++) {
ID id = id_for_attr(klass, argv[i]);
rb_attr(klass, id, TRUE, TRUE, TRUE);
rb_ary_push(names, ID2SYM(id));
rb_ary_push(names, ID2SYM(rb_id_attrset(id)));
}
return names;
} Определяет именованный атрибут для этого модуля, где имя — symbol.id2name, создавая переменную экземпляра (@name) и соответствующий метод доступа для чтения её значения. Также создаёт метод с именем name= для установки атрибута. Аргументы типа String преобразуются в символы. Возвращает массив имён определённых методов в виде символов.
module Mod attr_accessor(:one, :two) #=> [:one, :one=, :two, :two=] end Mod.instance_methods.sort #=> [:one, :one=, :two, :two=]
static VALUE
rb_mod_attr_reader(int argc, VALUE *argv, VALUE klass)
{
int i;
VALUE names = rb_ary_new2(argc);
for (i=0; i<argc; i++) {
ID id = id_for_attr(klass, argv[i]);
rb_attr(klass, id, TRUE, FALSE, TRUE);
rb_ary_push(names, ID2SYM(id));
}
return names;
} Создаёт переменные экземпляра и соответствующие методы, возвращающие значение каждой переменной экземпляра. Эквивалентно вызову «attr:name» для каждого имени по очереди. Аргументы типа String преобразуются в символы. Возвращает массив имён определённых методов в виде символов.
static VALUE
rb_mod_attr_writer(int argc, VALUE *argv, VALUE klass)
{
int i;
VALUE names = rb_ary_new2(argc);
for (i=0; i<argc; i++) {
ID id = id_for_attr(klass, argv[i]);
rb_attr(klass, id, FALSE, TRUE, TRUE);
rb_ary_push(names, ID2SYM(rb_id_attrset(id)));
}
return names;
} Создаёт метод доступа, позволяющий присваивать значение атрибуту 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) при первом обращении к const (который может быть String или символом) в пространстве имён mod.
module A end A.autoload(:B, "b") A::B.doit # autoloads "b"
Если const в mod определён как autoload, имя загружаемого файла заменяется на filename. Если const определён, но не как autoload, ничего не происходит.
static VALUE
rb_mod_autoload_p(int argc, VALUE *argv, VALUE mod)
{
int recur = (rb_check_arity(argc, 1, 2) == 1) ? TRUE : RTEST(argv[1]);
VALUE sym = argv[0];
ID id = rb_check_id(&sym);
if (!id) {
return Qnil;
}
return rb_autoload_at_p(mod, id, recur);
} Возвращает filename загружаемого файла, если name зарегистрирован как autoload в пространстве имён mod или одного из его предков.
module A end A.autoload(:B, "b") A.autoload?(:B) #=> "b"
Если inherit имеет значение false, поиск проверяет autoload только у получателя:
class A autoload :CONST, "const.rb" end class B < A end B.autoload?(:CONST) #=> "const.rb", found in A (ancestor) B.autoload?(:CONST, false) #=> nil, not found in B itself
Вычисляет строку или блок в контексте 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 Вычисляет переданный блок в контексте класса/модуля. Метод, определённый в блоке, будет принадлежать получателю. Все аргументы, переданные методу, будут переданы блоку. Этот метод можно использовать, если блоку нужен доступ к переменным экземпляра.
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, 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, 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, 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) {
goto wrong_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 0
mod = rb_const_search(mod, id, beglen > 0 || !RTEST(recur), RTEST(recur), FALSE);
if (UNDEF_P(mod)) return Qfalse;
#else
if (!RTEST(recur)) {
if (!rb_const_defined_at(mod, id))
return Qfalse;
if (p == pend) return Qtrue;
mod = rb_const_get_at(mod, id);
}
else if (beglen == 0) {
if (!rb_const_defined(mod, id))
return Qfalse;
if (p == pend) return Qtrue;
mod = rb_const_get(mod, id);
}
else {
if (!rb_const_defined_from(mod, id))
return Qfalse;
if (p == pend) return Qtrue;
mod = rb_const_get_from(mod, id);
}
#endif
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;
wrong_name:
rb_name_err_raise(wrong_constant_name, mod, name);
UNREACHABLE_RETURN(Qundef);
} Проверяет, есть ли у 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
Если в любом из проверяемых классов или модулей константа отсутствует, но для неё настроен autoload, 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 с сообщением «wrong constant name 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) {
goto wrong_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;
}
#if 0
mod = rb_const_get_0(mod, id, beglen > 0 || !RTEST(recur), RTEST(recur), FALSE);
#else
if (!RTEST(recur)) {
mod = rb_const_get_at(mod, id);
}
else if (beglen == 0) {
mod = rb_const_get(mod, id);
}
else {
mod = rb_const_get_from(mod, id);
}
#endif
}
return mod;
wrong_name:
rb_name_err_raise(wrong_constant_name, mod, name);
UNREACHABLE_RETURN(Qundef);
} Ищет в 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 с предупреждением «wrong constant name».
Object.const_get 'foobar' #=> NameError: wrong constant name foobar
VALUE
rb_mod_const_missing(VALUE klass, VALUE name)
{
rb_execution_context_t *ec = GET_EC();
VALUE ref = ec->private_const_reference;
rb_vm_pop_cfunc_frame();
if (ref) {
ec->private_const_reference = 0;
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
Как видно из примера выше, const_missing не обязан создавать отсутствующую константу в mod, хотя часто это происходит как побочный эффект. При вызове вызывающая сторона получает возвращаемое значение метода. Если константа также определена, последующие поиски не вызовут const_missing и, как обычно, вернут значение, хранящееся в константе. В противном случае const_missing будет вызван снова.
В следующем примере при обращении к неопределённой константе метод const_missing пытается загрузить файл, путь к которому представляет собой имя константы в нижнем регистре (предполагается, что класс Fred находится в файле fred.rb). Если в результате загрузки файла константа определяется как побочный эффект, метод возвращает хранящееся в ней значение. Это реализует функцию автозагрузки, похожую на Kernel#autoload и Module#autoload, хотя между ними есть важные различия.
def Object.const_missing(name)
@looked_for ||= {}
str_name = name.to_s
raise "Constant not found: #{name}" if @looked_for[str_name]
@looked_for[str_name] = 1
file = str_name.downcase
require file
const_get(name, false)
end
static VALUE
rb_mod_const_set(VALUE mod, VALUE name, VALUE value)
{
ID id = id_for_var(mod, name, const);
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 с предупреждением «wrong constant name».
Object.const_set('foobar', 42) #=> NameError: wrong constant name foobar
static VALUE
rb_mod_const_source_location(int argc, VALUE *argv, VALUE mod)
{
VALUE name, recur, loc = Qnil;
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 Qnil;
return RTEST(recur) ? rb_const_source_location(mod, id) : rb_const_source_location_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) {
goto wrong_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 Qnil;
}
}
if (!rb_is_const_id(id)) {
name = ID2SYM(id);
goto wrong_name;
}
if (p < pend) {
if (RTEST(recur)) {
mod = rb_const_get(mod, id);
}
else {
mod = rb_const_get_at(mod, id);
}
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));
}
}
else {
if (RTEST(recur)) {
loc = rb_const_source_location(mod, id);
}
else {
loc = rb_const_source_location_at(mod, id);
}
break;
}
recur = Qfalse;
}
return loc;
wrong_name:
rb_name_err_raise(wrong_constant_name, mod, name);
UNREACHABLE_RETURN(Qundef);
} Возвращает имя файла исходного кода Ruby и номер строки, содержащей определение указанной константы. Если именованная константа не найдена, возвращается nil. Если константа найдена, но её местоположение в исходном коде извлечь нельзя (константа определена в коде C), возвращается пустой массив.
Параметр inherit указывает, следует ли выполнять поиск в mod.ancestors (по умолчанию — true).
# test.rb:
class A # line 1
C1 = 1
C2 = 2
end
module M # line 6
C3 = 3
end
class B < A # line 10
include M
C4 = 4
end
class A # continuation of A definition
C2 = 8 # constant redefinition; warned yet allowed
end
p B.const_source_location('C4') # => ["test.rb", 12]
p B.const_source_location('C3') # => ["test.rb", 7]
p B.const_source_location('C1') # => ["test.rb", 2]
p B.const_source_location('C3', false) # => nil -- don't lookup in ancestors
p A.const_source_location('C2') # => ["test.rb", 16] -- actual (last) definition place
p Object.const_source_location('B') # => ["test.rb", 10] -- top-level constant could be looked through Object
p Object.const_source_location('A') # => ["test.rb", 1] -- class reopening is NOT considered new definition
p B.const_source_location('A') # => ["test.rb", 1] -- because Object is in ancestors
p M.const_source_location('A') # => ["test.rb", 1] -- Object is not ancestor, but additionally checked for modules
p Object.const_source_location('A::C1') # => ["test.rb", 2] -- nesting is supported
p Object.const_source_location('String') # => [] -- constant is defined in C code
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)
{
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;
if (cref) {
scope_visi = CREF_SCOPE_VISI(cref);
}
return rb_mod_define_method_with_visibility(argc, argv, mod, scope_visi);
} Определяет метод экземпляра в получателе. Параметр method может быть объектом Proc, Method или UnboundMethod. Если указан блок, он используется как тело метода. Если у блока или параметра method есть параметры, они используются как параметры метода. Этот блок вычисляется с помощью 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!" }
define_method(:flint) {|name| puts "I'm #{name}!"}
end
class B < A
define_method(:barney, instance_method(:fred))
end
a = B.new
a.barney
a.wilma
a.flint('Dino')
a.create_method(:betty) { p self }
a.betty
результат:
In Fred Charge it! I'm Dino! #<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;
} Помечает список существующих констант как устаревшие. При попытке обратиться к ним будет выдано предупреждение.
module HTTP NotFound = Exception.new NOT_FOUND = NotFound # previous version of the library used this name deprecate_constant :NOT_FOUND end HTTP::NOT_FOUND # warning: constant HTTP::NOT_FOUND is deprecated
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");
if (BUILTIN_TYPE(module) == T_MODULE && FL_TEST(module, RMODULE_IS_REFINEMENT)) {
rb_raise(rb_eTypeError, "Refinement#include has been removed");
}
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
for (i = 0; i < argc; i++) {
Check_Type(argv[i], T_MODULE);
if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
rb_raise(rb_eTypeError, "Cannot include refinement");
}
}
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 && !FL_TEST(p, RICLASS_IS_ORIGIN)) {
if (METACLASS_OF(p) == mod2) return Qtrue;
}
}
return Qfalse;
} Возвращает true, если module включён или добавлен в начало списка предков 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 && RCLASS_ORIGIN(p) == p && BUILTIN_TYPE(p) == T_ICLASS) {
VALUE m = METACLASS_OF(p);
if (RB_TYPE_P(m, T_MODULE))
rb_ary_push(ary, m);
}
}
return ary;
} Возвращает список модулей, включённых или добавленных в начало списка предков mod либо одного из его предков.
module Sub end module Mixin prepend Sub end module Outer include Mixin end Mixin.included_modules #=> [Sub] Outer.included_modules #=> [Sub, 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_unbound(mod, 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);
} Возвращает массив, содержащий имена открытых и защищённых методов экземпляра получателя. Для модуля это открытые и защищённые методы; для класса — методы экземпляра (не singleton-методы). Если необязательный параметр равен 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
Обратите внимание: изменения видимости методов в текущем классе, а также псевдонимы, этим методом считаются методами текущего класса:
class C < B alias method4 method2 protected :method2 end C.instance_methods(false).sort #=> [:method2, :method3, :method4]
static VALUE
rb_mod_method_defined(int argc, VALUE *argv, VALUE mod)
{
rb_method_visibility_t visi = check_definition_visibility(mod, argc, argv);
return RBOOL(visi == METHOD_VISI_PUBLIC || visi == METHOD_VISI_PROTECTED);
} Возвращает 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
static VALUE
rb_mod_module_eval_internal(int argc, const VALUE *argv, VALUE mod)
{
return specific_eval(argc, argv, mod, FALSE, RB_PASS_CALLED_KEYWORDS);
} Вычисляет строку или блок в контексте 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 static VALUE
rb_mod_module_exec_internal(int argc, const VALUE *argv, VALUE mod)
{
return yield_under(mod, FALSE, argc, argv, RB_PASS_CALLED_KEYWORDS);
} Вычисляет переданный блок в контексте класса/модуля. Метод, определённый в блоке, будет принадлежать получателю. Все аргументы, переданные методу, будут переданы блоку. Это можно использовать, если блоку необходим доступ к переменным экземпляра.
class Thing
end
Thing.class_exec{
def hello() "Hello there!" end
}
puts Thing.new.hello()
результат:
Hello there!
VALUE
rb_mod_name(VALUE mod)
{
// YJIT needs this function to not allocate.
bool permanent;
return classname(mod, &permanent);
} Возвращает имя модуля mod. Для анонимных модулей возвращает nil.
static VALUE
rb_mod_prepend(int argc, VALUE *argv, VALUE module)
{
int i;
ID id_prepend_features, id_prepended;
if (BUILTIN_TYPE(module) == T_MODULE && FL_TEST(module, RMODULE_IS_REFINEMENT)) {
rb_raise(rb_eTypeError, "Refinement#prepend has been removed");
}
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);
if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
rb_raise(rb_eTypeError, "Cannot prepend refinement");
}
}
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 преобразуются в символы. Также принимается Array из символов и/или строк.
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 преобразуются в символы. Также принимается Array из символов и/или строк.
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_unbound(mod, 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
static VALUE
mod_refinements(VALUE self)
{
ID id_refinements;
VALUE refinements;
CONST_ID(id_refinements, "__refinements__");
refinements = rb_attr_get(self, id_refinements);
if (NIL_P(refinements)) {
return rb_ary_new();
}
return rb_hash_values(refinements);
} Возвращает массив Refinement, определённых внутри получателя.
module A refine Integer do end refine String do end end p A.refinements
результат:
[#<refinement:Integer@A>, #<refinement:String@A>]
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;
if (!id) {
goto not_defined;
}
rb_check_frozen(mod);
val = rb_ivar_delete(mod, id, Qundef);
if (!UNDEF_P(val)) {
return (VALUE)val;
}
if (rb_cvar_defined(mod, id)) {
rb_name_err_raise("cannot remove %1$s for %2$s", mod, ID2SYM(id));
}
not_defined:
rb_name_err_raise("class variable %1$s not defined for %2$s",
mod, name);
UNREACHABLE_RETURN(Qundef);
} Удаляет именованную переменную класса из получателя и возвращает её значение.
class Example @@var = 99 puts 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 преобразуются в символы.
VALUE
rb_mod_set_temporary_name(VALUE mod, VALUE name)
{
// We don't allow setting the name if the classpath is already permanent:
if (RCLASS_EXT(mod)->permanent_classpath) {
rb_raise(rb_eRuntimeError, "can't change permanent name");
}
if (NIL_P(name)) {
// Set the temporary classpath to NULL (anonymous):
RB_VM_LOCK_ENTER();
set_sub_temporary_name(mod, 0);
RB_VM_LOCK_LEAVE();
}
else {
// Ensure the name is a string:
StringValue(name);
if (RSTRING_LEN(name) == 0) {
rb_raise(rb_eArgError, "empty class/module name");
}
if (is_constant_path(name)) {
rb_raise(rb_eArgError, "the temporary name must not be a constant path to avoid confusion");
}
name = rb_str_new_frozen(name);
// Set the temporary classpath to the given name:
RB_VM_LOCK_ENTER();
set_sub_temporary_name(mod, name);
RB_VM_LOCK_LEAVE();
}
return mod;
} Задаёт временное имя модуля. Это имя отображается при интроспекции модуля и связанных с ним объектов, например экземпляров, констант и методов.
Имя должно быть nil или непустой строкой, не являющейся допустимым путём к константе (чтобы не путать постоянные и временные имена).
Метод может быть полезен для различения динамически создаваемых классов и модулей без присвоения их константам.
Если модулю присваивается постоянное имя посредством присвоения константе, временное имя сбрасывается. Временное имя нельзя присвоить модулю, у которого уже есть постоянное имя.
Если заданное имя равно nil, модуль снова становится анонимным.
Пример:
m = Module.new # => #<Module:0x0000000102c68f38>
m.name #=> nil
m.set_temporary_name("fake_name") # => fake_name
m.name #=> "fake_name"
m.set_temporary_name(nil) # => #<Module:0x0000000102c68f38>
m.name #=> nil
c = Class.new
c.set_temporary_name("MyClass(with description)")
c.new # => #<MyClass(with description):0x0....>
c::M = m
c::M.name #=> "MyClass(with description)::M"
# Assigning to a constant replaces the name with a permanent one
C = c
C.name #=> "C"
C::M.name #=> "C::M"
c.new # => #<C:0x0....>
static VALUE
rb_mod_singleton_p(VALUE klass)
{
return RBOOL(RCLASS_SINGLETON_P(klass));
} Возвращает true, если mod является singleton-классом, или false, если это обычный класс или модуль.
class C end C.singleton_class? #=> false C.singleton_class.singleton_class? #=> true
VALUE
rb_mod_to_s(VALUE klass)
{
ID id_defined_at;
VALUE refined_class, defined_at;
if (RCLASS_SINGLETON_P(klass)) {
VALUE s = rb_usascii_str_new2("#<Class:");
VALUE v = RCLASS_ATTACHED_OBJECT(klass);
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_class_name(klass);
} Возвращает строку, представляющую этот модуль или класс. Для обычных классов и модулей это имя. Для singleton-объектов также выводятся сведения об объекте, к которому они относятся.
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)
VALUE
rb_class_undefined_instance_methods(VALUE mod)
{
VALUE include_super = Qfalse;
return class_instance_method_list(1, &include_super, mod, 0, ins_methods_undef_i);
} Возвращает список неопределённых методов экземпляра, объявленных в mod. Неопределённые методы предков не включаются.
Приватные методы экземпляра
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.
#define rb_obj_mod_const_added rb_obj_dummy1
Вызывается как обратный вызов каждый раз, когда принимающему объекту присваивается константа
module Chatty
def self.const_added(const_name)
super
puts "Added #{const_name.inspect}"
end
FOO = 1
end
выводит:
Added :FOO
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
#define rb_obj_mod_extended rb_obj_dummy1
Аналог included, но для расширенных модулей.
module A
def self.extended(mod)
puts "#{self} extended in #{mod}"
end
end
module Enumerable
extend A
end
# => prints "A extended in Enumerable"
#define rb_obj_mod_included rb_obj_dummy1
Обратный вызов, вызываемый каждый раз, когда принимающий объект подключается к другому модулю или классу. Если код должен выполнять какое-либо действие при подключении модуля к другому модулю, следует использовать этот метод вместо 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"
#define rb_obj_mod_method_added rb_obj_dummy1
Вызывается как обратный вызов каждый раз, когда к принимающему объекту добавляется метод экземпляра.
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
#define rb_obj_mod_method_removed rb_obj_dummy1
Вызывается как обратный вызов каждый раз, когда у принимающего объекта удаляется метод экземпляра.
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
#define rb_obj_mod_method_undefined rb_obj_dummy1
Вызывается как обратный вызов каждый раз, когда у принимающего объекта становится неопределённым метод экземпляра.
module Chatty
def self.method_undefined(method_name)
puts "Undefining #{method_name.inspect}"
end
def self.some_class_method() end
def some_instance_method() end
class << self
undef_method :some_class_method
end
undef_method :some_instance_method
end
выводит:
Undefining :some_instance_method
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 Qnil;
}
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);
}
if (argc == 1) {
return argv[0];
}
return rb_ary_new_from_values(argc, argv);
} Создаёт функции модуля для указанных методов. Эти функции можно вызывать, указав модуль в качестве получателя; они также становятся доступны как методы экземпляра для классов, включающих этот модуль. Функции модуля являются копиями исходных методов, поэтому их можно изменять независимо. Версии методов экземпляра становятся приватными. Если аргументы не указаны, все последующие определяемые методы становятся функциями модуля. Аргументы String преобразуются в символы. Если передан один аргумент, он возвращается. Если аргумент не передан, возвращается nil. Если передано несколько аргументов, они возвращаются в виде массива.
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.
#define rb_obj_mod_prepended rb_obj_dummy1
Аналог 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 преобразуются в символы. Также принимается Array из символов и/или строк. Если передан один аргумент, он возвращается. Если аргумент не передан, возвращается nil. Если передано несколько аргументов, они возвращаются в виде массива.
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 преобразуются в символы. Также принимается Array из символов и/или строк. Если передан один аргумент, он возвращается. Если аргумент не передан, возвращается nil. Если передано несколько аргументов, они возвращаются в виде массива.
Метод с защищённой видимостью можно вызывать только в том случае, если 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 преобразуются в символы. Также принимается Array из символов и/или строк. Если передан один аргумент, он возвращается. Если аргумент не передан, возвращается nil. Если передано несколько аргументов, они возвращаются в виде массива.
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_refinement_new();
RCLASS_SET_SUPER(refinement, superclass);
RUBY_ASSERT(BUILTIN_TYPE(refinement) == T_MODULE);
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) {
undefined_constant(mod, name);
}
return rb_const_remove(mod, id);
} Удаляет определение указанной константы и возвращает её предыдущее значение. Если эта константа ссылалась на модуль, его имя не изменится, что может привести к путанице.
static VALUE
rb_mod_ruby2_keywords(int argc, VALUE *argv, VALUE module)
{
int i;
VALUE origin_class = RCLASS_ORIGIN(module);
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
rb_check_frozen(module);
for (i = 0; i < argc; i++) {
VALUE v = argv[i];
ID name = rb_check_id(&v);
rb_method_entry_t *me;
VALUE defined_class;
if (!name) {
rb_print_undef_str(module, v);
}
me = search_method(origin_class, name, &defined_class);
if (!me && RB_TYPE_P(module, T_MODULE)) {
me = search_method(rb_cObject, name, &defined_class);
}
if (UNDEFINED_METHOD_ENTRY_P(me) ||
UNDEFINED_REFINED_METHOD_P(me->def)) {
rb_print_undef(module, name, METHOD_VISI_UNDEF);
}
if (module == defined_class || origin_class == defined_class) {
switch (me->def->type) {
case VM_METHOD_TYPE_ISEQ:
if (ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_rest &&
!ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_post &&
!ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_kw &&
!ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_kwrest) {
ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.ruby2_keywords = 1;
rb_clear_method_cache(module, name);
}
else {
rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method accepts keywords or post arguments or method does not accept argument splat)", QUOTE_ID(name));
}
break;
case VM_METHOD_TYPE_BMETHOD: {
VALUE procval = me->def->body.bmethod.proc;
if (vm_block_handler_type(procval) == block_handler_type_proc) {
procval = vm_proc_to_block_handler(VM_BH_TO_PROC(procval));
}
if (vm_block_handler_type(procval) == block_handler_type_iseq) {
const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(procval);
const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq);
if (ISEQ_BODY(iseq)->param.flags.has_rest &&
!ISEQ_BODY(iseq)->param.flags.has_post &&
!ISEQ_BODY(iseq)->param.flags.has_kw &&
!ISEQ_BODY(iseq)->param.flags.has_kwrest) {
ISEQ_BODY(iseq)->param.flags.ruby2_keywords = 1;
rb_clear_method_cache(module, name);
}
else {
rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method accepts keywords or post arguments or method does not accept argument splat)", QUOTE_ID(name));
}
break;
}
}
/* fallthrough */
default:
rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method not defined in Ruby)", QUOTE_ID(name));
break;
}
}
else {
rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (can only set in method defining module)", QUOTE_ID(name));
}
}
return Qnil;
} Для указанных имён методов помечает метод как передающий ключевые слова через обычное раскрытие аргументов. Этот метод следует вызывать только для методов, принимающих раскрытие аргументов (*args), но не явные ключевые слова и не раскрытие ключевых слов. Метод помечается так, что при вызове с ключевыми аргументами последний аргумент-хеш помечается специальным флагом. Если он является последним элементом обычного раскрытия аргументов в вызове другого метода, а этот вызов не содержит явных ключевых слов или раскрытия ключевых слов, последний элемент интерпретируется как ключевые слова. Иными словами, ключевые слова передаются через этот метод другим методам.
Этот метод следует использовать только для методов, делегирующих ключевые слова другому методу, и только для обеспечения обратной совместимости с версиями Ruby до 3.0. Подробности о причинах появления ruby2_keywords, а также о том, когда и как его использовать, см. на странице www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/.
Вероятно, в какой-то момент этот метод будет удалён, поскольку он существует только для обеспечения обратной совместимости. Так как его нет в версиях Ruby до 2.7, перед вызовом проверьте, отвечает ли модуль на этот метод:
module Mod
def foo(meth, *args, &block)
send(:"do_#{meth}", *args, &block)
end
ruby2_keywords(:foo) if respond_to?(:ruby2_keywords, true)
end
Однако учитывайте, что если метод ruby2_keywords будет удалён, поведение метода foo, использующего описанный выше подход, изменится: метод перестанет передавать ключевые слова.
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–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.