class Module
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]
Public Class Methods
Source
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。
Source
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"
Source
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"
如果您想将模块视为常规模块,请将其分配给一个常量(名称以大写字母开头)。
Source
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]
Source
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>]
Public Instance Methods
Source
static VALUE
rb_mod_lt(VALUE mod, VALUE arg)
{
if (mod == arg) return Qfalse;
return rb_class_inherited_p(mod, arg);
}
返回 self 是否是 other 的子类,如果两者之间没有关系,则返回 nil。
Float < Numeric # => true Numeric < Float # => false Float < Float # => false Float < Hash # => nil
Source
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;
}
}
如果 mod 是 other 的子类或与 other 相同,则返回 true。如果两者之间没有关系,则返回 nil。(从类定义的角度考虑关系:“class A < B” 暗示 “A < B”。)
Source
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);
}
比较 self 和 other。
返回
-
-1,如果self包含other,或者self是other的子类。 -
0,如果self和other相同。 -
1,如果other包含self,或者other是self的子类。 -
nil,如果以上情况都不成立。
示例
# Class Array includes module Enumerable. Array <=> Enumerable # => -1 Enumerable <=> Enumerable # => 0 Enumerable <=> Array # => 1 # Class File is a subclass of class IO. File <=> IO # => -1 File <=> File # => 0 IO <=> File # => 1 # Class File has no relationship to class String. File <=> String # => nil
Source
VALUE
rb_obj_equal(VALUE obj1, VALUE obj2)
{
return RBOOL(obj1 == obj2);
}
相等性 — 在 Object 级别,== 仅当 obj 和 other 是同一对象时才返回 true。通常,此方法在子类中被重写以提供特定类的含义。
与 == 不同,equal? 方法永远不应被子类重写,因为它用于确定对象标识(即,当且仅当 a 是 b 的同一个对象时,a.equal?(b) 为 true)。
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
Source
static VALUE
rb_mod_eqq(VALUE mod, VALUE arg)
{
return rb_obj_is_kind_of(arg, mod);
}
案例相等 — 如果 obj 是 mod 的实例或 mod 后代之一的实例,则返回 true。对于模块,此功能用途有限,但可在 case 语句中用于按类对对象进行分类。
Source
static VALUE
rb_mod_gt(VALUE mod, VALUE arg)
{
if (mod == arg) return Qfalse;
return rb_mod_ge(mod, arg);
}
如果 mod 是 other 的祖先,则返回 true。如果 mod 与 other 相同,或者 mod 是 other 的后代,则返回 false。如果两者之间没有关系,则返回 nil。(从类定义的角度考虑关系:“class A < B” 暗示 “B > A”。)
Source
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);
}
如果 mod 是 other 的祖先,或者两者相同,则返回 true。如果两者之间没有关系,则返回 nil。(从类定义的角度考虑关系:“class A < B” 暗示 “B > A”。)
Source
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);
}
将 new_name 设为方法 old_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
Source
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]
Source
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),但已弃用。返回定义的方法名称的数组,作为符号。
Source
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=]
Source
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 参数会被转换为符号。返回定义的方法名称的数组,作为符号。
Source
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 参数会被转换为符号。返回定义的方法名称的数组,作为符号。
Source
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,以便在 mod 的命名空间中首次访问 const(它可以是 String 或符号)时加载(使用 Kernel::require)。
module A end A.autoload(:B, "b") A::B.doit # autoloads "b"
如果 mod 中的 const 被定义为 autoload,则要加载的文件名将被替换为 filename。如果 const 已定义但不是 autoload,则不执行任何操作。
正在加载的文件不得注册为 autoload。
Source
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);
}
如果在 mod 或其祖先的命名空间中注册了 name 作为 autoload,则返回要加载的 filename。
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!
Source
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);
}
如果给定的类变量在 obj 中已定义,则返回 true。 String 参数会被转换为符号。
class Fred @@foo = 99 end Fred.class_variable_defined?(:@@foo) #=> true Fred.class_variable_defined?(:@@bar) #=> false
Source
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);
}
Source
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
Source
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]
Source
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,而无需 autoload。
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
Source
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,则查找还会搜索 mod 的祖先(如果 mod 是 Module,则还搜索 Object)。
如果找到定义,则返回常量的 value,否则将引发 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
Source
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 中未定义的常量时调用。它将未定义常量的符号传递给它,并返回一个用于该常量的 value。例如,考虑
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
Source
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
Source
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
Source
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?。
Source
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>
Source
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
Source
static VALUE
rb_mod_freeze(VALUE mod)
{
rb_class_name(mod);
return rb_obj_freeze(mod);
}
阻止对 mod 的进一步修改。
此方法返回 self。
Source
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。
Source
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 && !RICLASS_IS_ORIGIN_P(p)) {
if (METACLASS_OF(p) == mod2) return Qtrue;
}
}
return Qfalse;
}
如果 module 被包含或预置在 mod 或 mod 的某个祖先中,则返回 true。
module A end class B include A end class C < B end B.include?(A) #=> true C.include?(A) #=> true A.include?(A) #=> false
Source
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 或 mod 的某个祖先中包含或预置的模块列表。
module Sub end module Mixin prepend Sub end module Outer include Mixin end Mixin.included_modules #=> [Sub] Outer.included_modules #=> [Sub, Mixin]
Source
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!
Source
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
请注意,当前类中的方法可见性更改以及别名会被此方法视为当前类的成员。
class C < B alias method4 method2 protected :method2 end C.instance_methods(false).sort #=> [:method2, :method3, :method4]
Source
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);
}
指示命名方法是否由 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
Source
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
Source
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!
Source
VALUE
rb_mod_name(VALUE mod)
{
// YJIT needs this function to not allocate.
bool permanent;
return classname(mod, &permanent);
}
返回模块 mod 的名称。对于匿名模块,返回 nil。
Source
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。
Source
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;
}
Source
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;
}
将一系列现有常量设为私有。
Source
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]
Source
static VALUE
rb_mod_private_method_defined(int argc, VALUE *argv, VALUE mod)
{
return check_definition(mod, argc, argv, METHOD_VISI_PRIVATE);
}
指示命名私有方法是否由 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
Source
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,则不包括任何祖先的方法。
Source
static VALUE
rb_mod_protected_method_defined(int argc, VALUE *argv, VALUE mod)
{
return check_definition(mod, argc, argv, METHOD_VISI_PROTECTED);
}
指示命名保护方法是否由 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
Source
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;
}
Source
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;
}
使一系列现有常量公开。
Source
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,仅搜索公共方法。
Source
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,则不包括任何祖先的方法。
Source
static VALUE
rb_mod_public_method_defined(int argc, VALUE *argv, VALUE mod)
{
return check_definition(mod, argc, argv, METHOD_VISI_PUBLIC);
}
指示命名公共方法是否由 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
Source
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>]
Source
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
Source
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 参数会被转换为符号。
Source
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_PERMANENT_CLASSPATH_P(mod)) {
rb_raise(rb_eRuntimeError, "can't change permanent name");
}
if (NIL_P(name)) {
// Set the temporary classpath to NULL (anonymous):
RB_VM_LOCKING() {
set_sub_temporary_name(mod, 0);
}
}
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);
RB_OBJ_SET_SHAREABLE(name);
// Set the temporary classpath to the given name:
RB_VM_LOCKING() {
set_sub_temporary_name(mod, name);
}
}
return mod;
}
设置模块的临时名称。此名称会反映在模块内省和与该模块相关的 values 中,例如实例、常量和方法。
名称应为 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)") # => 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....>
Source
static VALUE
rb_mod_singleton_p(VALUE klass)
{
return RBOOL(RCLASS_SINGLETON_P(klass));
}
如果 mod 是一个单例类,则返回 true,如果它是普通类或模块,则返回 false。
class C end C.singleton_class? #=> false C.singleton_class.singleton_class? #=> true
Source
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);
}
返回一个表示此模块或类的字符串。对于基本类和模块,这是名称。对于单例,我们还显示有关我们所附加对象的_values_信息。
Source
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)
Source
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 中定义的未定义实例方法的列表。不包括任何祖先的未定义方法。
私有实例方法
Source
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。
Source
#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
如果使用 class 关键字定义类,const_added 会在 inherited 之前运行。
module M def self.const_added(const_name) super p :const_added end parent = Class.new do def self.inherited(subclass) super p :inherited end end class Child < parent end end
产生
:const_added :inherited
Source
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
Source
#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"
Source
#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"
Source
#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
Source
#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
Source
#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
Source
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"
Source
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。
Source
#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"
Source
static VALUE
rb_mod_private(int argc, VALUE *argv, VALUE module)
{
return set_visibility(argc, argv, module, METHOD_VISI_PRIVATE);
}
Source
static VALUE
rb_mod_protected(int argc, VALUE *argv, VALUE module)
{
return set_visibility(argc, argv, module, METHOD_VISI_PROTECTED);
}
将一段代码或一系列方法名称的可见性设置为 protected。可以不带参数、接受方法名称的 splat(符号或字符串)或方法名称的数组。返回接收到的参数。
与其他语言中的 protected 的重要区别
Ruby 中的受保护方法与其他语言(如 Java)中的方法不同,在 Java 中,方法被标记为受保护是为了让子类能够访问。在 Ruby 中,子类已经可以访问父类中定义的所有方法,甚至是私有方法。
将方法标记为受保护允许同一类的不同对象调用它。
一种用例是用于比较方法,例如 ==,如果我们希望公开一个方法用于同一类对象之间的比较,而又不想将该方法公开给其他类的对象。
性能考量
受保护方法比其他方法慢,因为它们无法使用内联缓存。
示例
class Account # Mark balance as protected, so that we can compare between accounts # without making it public. attr_reader :balance protected :balance def initialize(balance) @balance = balance end def >(other) # The invocation to `other.balance` is allowed because `other` is a # different object of the same class (Account). balance > other.balance end end account1 = Account.new(100) account2 = Account.new(50) account1 > account2 # => true (works) account1.balance # => NoMethodError (fails because balance is not public)
要在 RDoc 中显示私有方法,请使用 :doc: 而不是此方法。
Source
static VALUE
rb_mod_public(int argc, VALUE *argv, VALUE module)
{
return set_visibility(argc, argv, module, METHOD_VISI_PUBLIC);
}
在没有参数的情况下,将随后定义的方法的默认可见性设置为 public。有参数时,将命名方法设置为具有 public 可见性。String 参数会被转换为符号。也接受符号和/或字符串的 Array。如果传递单个参数,则返回该参数。如果未传递参数,则返回 nil。如果传递多个参数,则将参数作为数组返回。
Source
static VALUE
rb_mod_refine(VALUE module, VALUE klass)
{
/* module is the receiver of #refine, klass is a module to be refined (`mod` in the doc) */
rb_thread_t *th = GET_THREAD();
VALUE block_handler = rb_vm_frame_block_handler(th->ec->cfp);
struct rb_refinements_data data;
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);
rb_refinement_setup(&data, module, klass);
rb_yield_refine_block(data.refinement, data.refinements);
return data.refinement;
}
在接收者中改进 mod。
返回一个模块,其中定义了改进的方法。
Source
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);
}
删除给定常量的定义,并返回该常量的先前值。如果该常量指向一个模块,这不会改变该模块的名称,并可能导致混淆。
Source
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 方法的行为将发生变化,导致该方法不传递关键字。
Source
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 的类改进导入到当前类或模块定义中。