класс 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(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)
{
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(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_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 для проверки членов на равенство. Для любой пары объектов, где 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);
} Равенство по типу — Возвращает 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;
} Создаёт новую копию метода old_name под именем new_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 (p != RCLASS_ORIGIN(p)) continue;
if (BUILTIN_TYPE(p) == T_ICLASS) {
rb_ary_push(ary, RBASIC(p)->klass);
}
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)) {
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(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, поиск проверяет только автозагрузки в получателе:
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
static VALUE
rb_mod_module_eval_internal(int argc, const VALUE *argv, VALUE mod)
{
return specific_eval(argc, argv, mod, mod, 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, mod, argc, argv, RB_PASS_CALLED_KEYWORDS);
} Вычисляет заданный блок в контексте класса/модуля. Определенный в блоке метод будет принадлежать получателю. Любые аргументы, передаваемые в метод, будут переданы в блок. Это можно использовать, если блок нуждается в доступе к переменным экземпляра.
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) {
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 0
mod = rb_const_search(mod, id, beglen > 0 || !RTEST(recur), RTEST(recur), FALSE);
if (mod == Qundef) 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;
} Указывает, существует ли константа с данным именем в 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;
}
#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;
} Ищет константу с заданным именем в 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_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 с предупреждением «неверное имя константы».
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) {
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 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;
} Возвращает имя файла и номер строки Ruby, содержащие первое определение указанной константы. Если указанная константа не найдена, возвращается nil. Если константа найдена, но её местоположение в источнике не может быть извлечено (константа определена в коде C), возвращается пустой массив.
inherit определяет, следует ли искать в mod.ancestors (true по умолчанию).
# test.rb:
class A
C1 = 1
end
module M
C2 = 2
end
class B < A
include M
C3 = 3
end
class A # continuation of A definition
end
p B.const_source_location('C3') # => ["test.rb", 11]
p B.const_source_location('C2') # => ["test.rb", 6]
p B.const_source_location('C1') # => ["test.rb", 2]
p B.const_source_location('C2', false) # => nil -- don't lookup in ancestors
p Object.const_source_location('B') # => ["test.rb", 9]
p Object.const_source_location('A') # => ["test.rb", 1] -- note it is first entry, not "continuation"
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)
{
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/UnboundMethod)",
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 объектом. Если указан блок, он используется в качестве тела метода. Если у блока или параметра 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");
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 , если module включён в 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
static VALUE
rb_mod_module_eval_internal(int argc, const VALUE *argv, VALUE mod)
{
return specific_eval(argc, argv, mod, mod, 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, mod, 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)
{
int 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;
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;
} Удаляет определение sym, возвращая значение этой константы.
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_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_dummy1(VALUE _x, VALUE _y)
{
return rb_obj_dummy();
} Эквивалент 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_dummy1(VALUE _x, VALUE _y)
{
return rb_obj_dummy();
} Обратный вызов, вызываемый всякий раз, когда получатель включается в другой модуль или класс. Следует использовать его вместо 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_dummy1(VALUE _x, VALUE _y)
{
return rb_obj_dummy();
} Вызывается как обратный вызов всякий раз, когда к получателю добавляется метод экземпляра.
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_dummy1(VALUE _x, VALUE _y)
{
return rb_obj_dummy();
} Вызывается как обратный вызов всякий раз, когда из получателя удаляется метод экземпляра.
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_dummy1(VALUE _x, VALUE _y)
{
return rb_obj_dummy();
} 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_dummy1(VALUE _x, VALUE _y)
{
return rb_obj_dummy();
} Эквивалент 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);
} Без аргументов устанавливает видимость для последующих методов как private. С аргументами устанавливает заданные методы с видимостью 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);
} Без аргументов устанавливает видимость для последующих методов как protected. С аргументами устанавливает заданные методы с видимостью protected. Аргументы String преобразуются в символы.
Если метод имеет видимость protected, он вызывается только там, где 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);
} Без аргументов устанавливает видимость для последующих методов как public. С аргументами устанавливает заданные методы с видимостью 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);
if (RB_TYPE_P(klass, T_MODULE)) {
FL_SET(klass, RCLASS_REFINED_BY_ANY);
}
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) {
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_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 (me->def->body.iseq.iseqptr->body->param.flags.has_rest &&
!me->def->body.iseq.iseqptr->body->param.flags.has_kw &&
!me->def->body.iseq.iseqptr->body->param.flags.has_kwrest) {
me->def->body.iseq.iseqptr->body->param.flags.ruby2_keywords = 1;
rb_clear_method_cache_by_class(module);
}
else {
rb_warn("Skipping set of ruby2_keywords flag for %s (method accepts keywords or method does not accept argument splat)", rb_id2name(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->param.flags.has_rest &&
!iseq->body->param.flags.has_kw &&
!iseq->body->param.flags.has_kwrest) {
iseq->body->param.flags.ruby2_keywords = 1;
rb_clear_method_cache_by_class(module);
}
else {
rb_warn("Skipping set of ruby2_keywords flag for %s (method accepts keywords or method does not accept argument splat)", rb_id2name(name));
}
return Qnil;
}
}
/* fallthrough */
default:
rb_warn("Skipping set of ruby2_keywords flag for %s (method not defined in Ruby)", rb_id2name(name));
break;
}
}
else {
rb_warn("Skipping set of ruby2_keywords flag for %s (can only set in method defining module)", rb_id2name(name));
}
}
return Qnil;
} Для заданных имён методов отмечает метод как проходящий ключевые слова через обычный аргумент splat. Это следует вызывать только для методов, которые принимают аргумент splat (*args), но не явные ключевые слова или ключевой splat. Он отмечает метод так, что если метод вызывается с ключевыми аргументами, окончательный хэш-аргумент помечается специальным флагом, таким образом, если это последний элемент обычного аргумента splat для другого вызова метода, а этот вызов метода не включает явные ключевые слова или ключевой splat, последний элемент интерпретируется как ключевые слова. Другими словами, ключевые слова будут передаваться через метод другим методам.
Это следует использовать только для методов, которые делегируют ключевые слова другому методу, и только для обратной совместимости с версиями Ruby до 2.7.
Этот метод, вероятно, будет удалён в какой-то момент, так как он существует только для обратной совместимости. Поскольку он не существует в версиях 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
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;
} Импортировать уточнения класса из модуля в текущее определение класса или модуля.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.