類別 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]
公開類別方法
在第一個形式中,傳回呼叫點可存取的所有常數名稱陣列。此清單包含在全域範圍中定義的所有模組和類別名稱。
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_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); }
傳回在呼叫點巢狀的 Modules
清單。
module M1 module M2 $a = Module.nesting end end $a #=> [M1::M2, M1] $a[0].name #=> "M1::M2"
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; }
建立新的匿名模組。如果提供區塊,則將模組物件傳遞給它,且區塊會在這個模組的內容中評估,例如 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_initialize(VALUE module) { return rb_mod_initialize_exec(module); }
傳回目前範圍中使用的所有模組陣列。結果陣列中模組的順序未定義。
module A refine Object do end end module B refine Object do end end using A using B p Module.used_modules
產生
[B, A]
static VALUE rb_mod_s_used_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_refinements
產生
[#<refinement:Object@B>, #<refinement:Object@A>]
static VALUE rb_mod_s_used_refinements(VALUE _) { const rb_cref_t *cref = rb_vm_cref(); VALUE ary = rb_ary_new(); while (cref) { if (!NIL_P(CREF_REFINEMENTS(cref))) { rb_hash_foreach(CREF_REFINEMENTS(cref), used_refinements_i, ary); } cref = CREF_NEXT(cref); } return ary; }
公開實例方法
如果 mod 是 other 的子類別,則傳回 true。如果 mod 與 other 相同或 mod 是 other 的祖先,則傳回 false
。如果這兩個之間沒有關係,則傳回 nil
。(請從類別定義的角度思考關係:「類別 A < B」表示「A < B」)
static VALUE rb_mod_lt(VALUE mod, VALUE arg) { if (mod == arg) return Qfalse; return rb_class_inherited_p(mod, arg); }
如果 mod 是 other 的子類別或與 other 相同,則傳回 true。如果這兩個之間沒有關係,則傳回 nil
。(請從類別定義的角度思考關係:「類別 A < B」表示「A < B」)
VALUE rb_class_inherited_p(VALUE mod, VALUE arg) { if (mod == arg) return Qtrue; if (RB_TYPE_P(arg, T_CLASS) && RB_TYPE_P(mod, T_CLASS)) { // comparison between classes size_t mod_depth = RCLASS_SUPERCLASS_DEPTH(mod); size_t arg_depth = RCLASS_SUPERCLASS_DEPTH(arg); if (arg_depth < mod_depth) { // check if mod < arg return RCLASS_SUPERCLASSES(mod)[arg_depth] == arg ? Qtrue : Qnil; } else if (arg_depth > mod_depth) { // check if mod > arg return RCLASS_SUPERCLASSES(arg)[mod_depth] == mod ? Qfalse : Qnil; } else { // Depths match, and we know they aren't equal: no relation return Qnil; } } else { if (!CLASS_OR_MODULE_P(arg) && !RB_TYPE_P(arg, T_ICLASS)) { rb_raise(rb_eTypeError, "compared with non class/module"); } if (class_search_ancestor(mod, RCLASS_ORIGIN(arg))) { return Qtrue; } /* not mod < arg; check if mod > arg */ if (class_search_ancestor(arg, mod)) { return Qfalse; } return Qnil; } }
比較—傳回 -1、0、+1 或 nil,視 module
是否包含 other_module
、它們是否相同,或 module
是否由 other_module
包含而定。
如果 module
與 other_module
沒有關係、如果 other_module
不是模組,或如果這兩個值無法比較,則傳回 nil
。
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); }
相等性 — 在 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
如果 obj
和 other
參照同一個雜湊金鑰,eql?
方法會傳回 true
。這由 Hash
用於測試成員的相等性。對於任何 eql?
傳回 true
的物件對,兩個物件的 hash
值都必須相等。因此,任何覆寫 eql?
的子類別也應適當地覆寫 hash
。
對於 Object
類別的物件,eql?
與 ==
同義。子類別通常會延續此傳統,將 eql?
別名設定為它們覆寫的 ==
方法,但也有例外。Numeric
型別會在 ==
中執行型別轉換,但在 eql?
中不會執行,因此
1 == 1.0 #=> true 1.eql? 1.0 #=> false
VALUE rb_obj_equal(VALUE obj1, VALUE obj2) { return RBOOL(obj1 == obj2); }
大小寫相等性 — 如果 obj 是 mod 的執行個體或 mod 的後代之一的執行個體,則傳回 true
。對模組的用途有限,但可用於 case
陳述式中,以依類別分類物件。
static VALUE rb_mod_eqq(VALUE mod, VALUE arg) { return rb_obj_is_kind_of(arg, mod); }
如果 mod 是 other 的祖先,則傳回 true。如果 mod 與 other 相同,或 mod 是 other 的後代,則傳回 false
。如果兩個模組之間沒有關係,則傳回 nil
。(請從類別定義的角度思考關係:「class A < B」表示「B > A」)
static VALUE rb_mod_gt(VALUE mod, VALUE arg) { if (mod == arg) return Qfalse; return rb_mod_ge(mod, arg); }
如果 mod 是 other 的祖先,或兩個模組相同,則傳回 true。如果兩個模組之間沒有關係,則傳回 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); }
將 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
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); }
傳回在 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_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; }
第一個形式等同於 attr_reader
。第二個形式等同於 attr_accessor(name)
,但已不建議使用。最後一個形式等同於 attr_reader(name)
,但已不建議使用。傳回已定義方法名稱的陣列,形式為符號。
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); }
為這個模組定義一個命名屬性,其中名稱為 symbol.id2name
,建立一個實例變數(@name
)和一個對應的存取方法來讀取它。也會建立一個稱為 name=
的方法來設定屬性。 String
參數會轉換為符號。傳回已定義方法名稱的陣列,形式為符號。
module Mod attr_accessor(:one, :two) #=> [:one, :one=, :two, :two=] end Mod.instance_methods.sort #=> [:one, :one=, :two, :two=]
static VALUE rb_mod_attr_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; }
建立實例變數和對應的方法,傳回每個實例變數的值。等同於對每個名稱依序呼叫 “attr
:name”。 String
參數會轉換為符號。傳回已定義方法名稱的陣列,形式為符號。
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; }
建立一個存取方法,允許指定屬性 symbol.id2name
。 String
參數會轉換為符號。傳回已定義方法名稱的陣列,形式為符號。
static VALUE rb_mod_attr_writer(int argc, VALUE *argv, VALUE klass) { int i; VALUE names = rb_ary_new2(argc); for (i=0; i<argc; i++) { ID id = id_for_attr(klass, argv[i]); rb_attr(klass, id, FALSE, TRUE, TRUE); rb_ary_push(names, ID2SYM(rb_id_attrset(id))); } return names; }
Registers _filename_ to be loaded (using Kernel::require) the first time that _const_ (which may be a String or a symbol) is accessed in the namespace of _mod_. module A end A.autoload(:B, "b") A::B.doit # autoloads "b"
如果 mod 中的 const 被定義為自動載入,則要載入的檔案名稱會被替換為 filename。如果 const 已被定義,但不是作為自動載入,則不執行任何動作。
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; }
如果 name 已在 mod 或其祖先的命名空間中註冊為 autoload
,則傳回要載入的 filename。
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_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 的內容中,但當給定區塊時,不會影響常數/類別變數的查詢。這可以用來新增方法到類別中。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!
如果給定的類別變數在 obj 中定義,則傳回 true
。 String
引數會轉換為符號。
class Fred @@foo = 99 end Fred.class_variable_defined?(:@@foo) #=> true Fred.class_variable_defined?(:@@bar) #=> false
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); }
傳回給定類別變數的值(或擲出 NameError
例外)。對於一般類別變數,應包含變數名稱的 @@
部分。 String
引數會轉換為符號。
class Fred @@foo = 99 end Fred.class_variable_get(:@@foo) #=> 99
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); }
將由 symbol 命名的類別變數設定為給定的物件。如果類別變數名稱傳遞為字串,則該字串會轉換為符號。
class Fred @@foo = 99 def foo @@foo end end Fred.class_variable_set(:@@foo, 101) #=> 101 Fred.new.foo #=> 101
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; }
傳回 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]
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 或其祖先是否有一個具有給定名稱的常數
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_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 中檢查具有給定名稱的常數。如果設定 inherit
,查詢也會搜尋祖先(以及 mod 是 Module
時的 Object
)。
如果找到定義,則會傳回常數的值,否則會引發 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
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 中對未定義的常數進行參考時呼叫。它會傳遞一個未定義常數的符號,並傳回要使用於該常數的值。以下程式碼是相同的範例
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
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); }
將命名常數設定為給定的物件,傳回該物件。如果先前不存在具有給定名稱的常數,則會建立新的常數。
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_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; }
傳回包含常數定義的 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
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); }
傳回 mod 中可存取的常數名稱陣列。這包含任何包含模組中的常數名稱(本節開頭的範例),除非 inherit 參數設定為 false
。
實作不保證常數產生的順序。
IO.constants.include?(:SYNC) #=> true IO.constants(false).include?(:SYNC) #=> false
另請參閱 Module#const_defined?
。
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); } }
在接收器中定義執行個體方法。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>
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); }
使現有常數清單過時。嘗試參照它們會產生警告。
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
VALUE rb_mod_deprecate_constant(int argc, const VALUE *argv, VALUE obj) { set_const_visibility(obj, argc, argv, CONST_DEPRECATED, CONST_DEPRECATED); return obj; }
防止進一步修改 mod。
此方法傳回 self。
static VALUE rb_mod_freeze(VALUE mod) { rb_class_name(mod); return rb_obj_freeze(mod); }
依反向順序對每個參數呼叫 Module.append_features
。
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 包含或前置於 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
VALUE rb_mod_include_p(VALUE mod, VALUE mod2) { VALUE p; Check_Type(mod2, T_MODULE); for (p = RCLASS_SUPER(mod); p; p = RCLASS_SUPER(p)) { if (BUILTIN_TYPE(p) == T_ICLASS && !FL_TEST(p, RICLASS_IS_ORIGIN)) { if (METACLASS_OF(p) == mod2) return Qtrue; } } return Qfalse; }
傳回包含或前置於 mod 或 mod 的其中一個祖先的模組清單。
module Sub end module Mixin prepend Sub end module Outer include Mixin end Mixin.included_modules #=> [Sub] Outer.included_modules #=> [Sub, Mixin]
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; }
傳回一個 `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!
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); }
傳回一個陣列,其中包含接收器中公開和受保護的實例方法名稱。對於模組,這些是公開和受保護的方法;對於類別,這些是實例(非單例)方法。如果選用參數為 `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]
VALUE rb_class_instance_methods(int argc, const VALUE *argv, VALUE mod) { return class_instance_method_list(argc, argv, mod, 0, ins_methods_i); }
如果 `mod` 定義了指定的方法,則傳回 `true`。如果設定 `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_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 的內容中,但當給定區塊時,不會影響常數/類別變數的查詢。這可以用來新增方法到類別中。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_eval_internal(int argc, const VALUE *argv, VALUE mod) { return specific_eval(argc, argv, mod, FALSE, 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_module_exec_internal(int argc, const VALUE *argv, VALUE mod) { return yield_under(mod, FALSE, argc, argv, RB_PASS_CALLED_KEYWORDS); }
傳回模組 `mod` 的名稱。對於匿名模組,傳回 nil。
VALUE rb_mod_name(VALUE mod) { bool permanent; return classname(mod, &permanent); }
以反向順序對每個參數呼叫 `Module.prepend_features
`。
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; }
將現有的類別方法設為私人。通常用來隱藏預設建構函式 `new`。
class SimpleSingleton # Not thread safe private_class_method :new def SimpleSingleton.create(*args, &block) @me = new(*args, &block) if ! @me @me end end
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; }
將現有常數清單設為私人。
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; }
傳回 模組 中定義的私人實例方法清單。如果選用參數為 false
,則不會包含任何祖先的方法。
module Mod def method1() end private :method1 def method2() end end Mod.instance_methods #=> [:method2] Mod.private_instance_methods #=> [:method1]
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); }
如果 模組 定義了指定的私人方法,則傳回 true
。如果設定 inherit,則查詢也會搜尋 模組 的祖先。字串
參數會轉換成符號。
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
static VALUE rb_mod_private_method_defined(int argc, VALUE *argv, VALUE mod) { return check_definition(mod, argc, argv, METHOD_VISI_PRIVATE); }
傳回 模組 中定義的受保護實例方法清單。如果選用參數為 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); }
如果 模組 定義了指定的受保護方法,則傳回 true
。如果設定 inherit,則查詢也會搜尋 模組 的祖先。字串
參數會轉換成符號。
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_protected_method_defined(int argc, VALUE *argv, VALUE mod) { return check_definition(mod, argc, argv, METHOD_VISI_PROTECTED); }
將現有常數清單設為公開。
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; }
類似於 instance_method,只搜尋公開方法。
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); }
傳回 模組 中定義的公開實例方法清單。如果選用參數為 false
,則不會包含任何祖先的方法。
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 定義,則傳回 true
。如果設定 inherit,查詢也會搜尋 mod 的祖先。String
參數會轉換為符號。
module A def method1() end end class B protected def method2() end end class C < B include A def method3() end end A.method_defined? :method1 #=> true C.public_method_defined? "method1" #=> true C.public_method_defined? "method1", true #=> true C.public_method_defined? "method1", false #=> true C.public_method_defined? "method2" #=> false C.method_defined? "method2" #=> true
static VALUE rb_mod_public_method_defined(int argc, VALUE *argv, VALUE mod) { return check_definition(mod, argc, argv, METHOD_VISI_PUBLIC); }
傳回在接收器內定義的模組陣列。
module A refine Integer do end refine String do end end p A.refinements
產生
[#<refinement:Integer@A>, #<refinement:String@A>]
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); }
從接收器移除已命名的類別變數,並傳回該變數的值。
class Example @@var = 99 puts remove_class_variable(:@@var) p(defined? @@var) end
產生
99 nil
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); }
從目前類別移除由 symbol 識別的方法。如需範例,請參閱 Module#undef_method
。String
參數會轉換為符號。
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; }
設定模組的暫時名稱。此名稱會反映在模組的自省中,以及與其相關的值中,例如執行個體、常數和方法。
名稱應該是 nil
或非空白字串,且不是有效的常數名稱 (以避免混淆永久名稱和暫時名稱)。
此方法可協助區分動態產生的類別和模組,而無須將它們指定給常數。
如果模組透過指定給常數而取得永久名稱,則會捨棄暫時名稱。無法將暫時名稱指定給具有永久名稱的模組。
如果給定的名稱是 nil
,則模組會再次變成匿名。
範例
m = Module.new # => #<Module:0x0000000102c68f38> m.name #=> nil m.set_temporary_name("fake_name") # => fake_name m.name #=> "fake_name" m.set_temporary_name(nil) # => #<Module:0x0000000102c68f38> m.name #=> nil c = Class.new c.set_temporary_name("MyClass(with description)") c.new # => #<MyClass(with description):0x0....> c::M = m c::M.name #=> "MyClass(with description)::M" # Assigning to a constant replaces the name with a permanent one C = c C.name #=> "C" C::M.name #=> "C::M" c.new # => #<C:0x0....>
VALUE rb_mod_set_temporary_name(VALUE mod, VALUE name) { // We don't allow setting the name if the classpath is already permanent: if (RCLASS_EXT(mod)->permanent_classpath) { rb_raise(rb_eRuntimeError, "can't change permanent name"); } if (NIL_P(name)) { // Set the temporary classpath to NULL (anonymous): RCLASS_SET_CLASSPATH(mod, 0, FALSE); } 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"); } // Set the temporary classpath to the given name: RCLASS_SET_CLASSPATH(mod, name, FALSE); } return mod; }
如果 mod 是單例類別,則傳回 true
;如果是普通類別或模組,則傳回 false
。
class C end C.singleton_class? #=> false C.singleton_class.singleton_class? #=> true
static VALUE rb_mod_singleton_p(VALUE klass) { return RBOOL(RB_TYPE_P(klass, T_CLASS) && FL_TEST(klass, FL_SINGLETON)); }
傳回表示此模組或類別的字串。對於基本類別和模組,這是名稱。對於單例,我們也會顯示所附加對象的資訊。
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 = 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); }
防止目前的類別回應對命名方法的呼叫。這與 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_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; }
傳回在 mod 中定義的未定義實例方法清單。不會包含任何祖先的未定義方法。
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); }
私人實例方法
當這個模組包含在另一個模組中時,Ruby 會在這個模組中呼叫 append_features
,傳遞接收模組給 mod。Ruby 的預設實作是將這個模組的常數、方法和模組變數新增到 mod 中,如果這個模組尚未新增到 mod 或其祖先之一。另請參閱 Module#include
。
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; }
在接收器上指派常數時呼叫為回呼。
module Chatty def self.const_added(const_name) super puts "Added #{const_name.inspect}" end FOO = 1 end
產生
Added :FOO
#define rb_obj_mod_const_added rb_obj_dummy1
透過新增這個模組的常數和方法(新增為單例方法)來延伸指定物件。這是 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_mod_extend_object(VALUE mod, VALUE obj) { rb_extend_object(obj, mod); return obj; }
等同於 included
,但適用於延伸模組。
module A def self.extended(mod) puts "#{self} extended in #{mod}" end end module Enumerable extend A end # => prints "A extended in Enumerable"
#define rb_obj_mod_extended rb_obj_dummy1
在接收器包含在另一個模組或類別中時呼叫的回呼。如果您的程式碼想要在模組包含在另一個模組中時執行某些動作,應優先使用此方法而非 Module.append_features
。
module A def A.included(mod) puts "#{self} included in #{mod}" end end module Enumerable include A end # => prints "A included in Enumerable"
#define rb_obj_mod_included rb_obj_dummy1
在實例方法新增到接收器時呼叫為回呼。
module Chatty def self.method_added(method_name) puts "Adding #{method_name.inspect}" end def self.some_class_method() end def some_instance_method() end end
產生
Adding :some_instance_method
#define rb_obj_mod_method_added rb_obj_dummy1
在實例方法從接收器中移除時呼叫為回呼。
module Chatty def self.method_removed(method_name) puts "Removing #{method_name.inspect}" end def self.some_class_method() end def some_instance_method() end class << self remove_method :some_class_method end remove_method :some_instance_method end
產生
Removing :some_instance_method
#define rb_obj_mod_method_removed 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
#define rb_obj_mod_method_undefined rb_obj_dummy1
為指定的 method 建立 module function。這些 function 可以使用 module 作為接收器呼叫,並會變成混入 module 的類別的執行個體 method。 Module
function 是原始函式的副本,因此可以獨立變更。執行個體 method 版本會設為私人。如果沒有參數,之後定義的 method 會變成 module function。 String
參數會轉換成符號。如果傳遞單一參數,會傳回該參數。如果沒有傳遞參數,會傳回 nil。如果傳遞多個參數,會將參數傳回為陣列。
module Mod def one "This is one" end module_function :one end class Cls include Mod def call_one one end end Mod.one #=> "This is one" c = Cls.new c.call_one #=> "This is one" module Mod def one "This is the new one" end end Mod.one #=> "This is one" c.call_one #=> "This is the new one"
static VALUE rb_mod_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); }
當這個 module 在另一個 module 中置於開頭時,Ruby 會呼叫這個 module 中的 prepend_features
,並將接收的 module 傳遞給 mod。Ruby 的預設實作會將這個 module 的常數、method 和 module 變數覆蓋到 mod,前提是這個 module 尚未加入到 mod 或其祖先中。另請參閱 Module#prepend
。
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; }
等同於 included
,但適用於置於開頭的 module。
module A def self.prepended(mod) puts "#{self} prepended to #{mod}" end end module Enumerable prepend A end # => prints "A prepended to Enumerable"
#define rb_obj_mod_prepended rb_obj_dummy1
沒有參數時,會將之後定義的 method 的預設可見性設為私人。有參數時,會將指定的 method 設為私人可見性。 String
參數會轉換成符號。也會接受符號和/或字串的 Array
。如果傳遞單一參數,會傳回該參數。如果沒有傳遞參數,會傳回 nil。如果傳遞多個參數,會將參數傳回為陣列。
module Mod def a() end def b() end private def c() end private :a end Mod.private_instance_methods #=> [:a, :c]
請注意,要在 RDoc
上顯示私人 method,請使用 :doc:
。
static VALUE rb_mod_private(int argc, VALUE *argv, VALUE module) { return set_visibility(argc, argv, module, METHOD_VISI_PRIVATE); }
若無參數,設定後續定義方法的預設可見性為 protected。若有參數,設定命名方法具有 protected 可見性。String
參數會轉換為符號。系統也會接受符號和/或字串的 Array
。若傳遞單一參數,系統會傳回該參數。若未傳遞參數,系統會傳回 nil。若傳遞多個參數,系統會傳回這些參數作為陣列。
若方法具有 protected 可見性,只有當內容的 self
與方法相同時,才能呼叫該方法(方法定義或 instance_eval)。此行為與 Java 的 protected 方法不同。通常應使用 private
。
請注意,protected 方法很慢,因為它無法使用內聯快取。
若要在 RDoc
上顯示私人方法,請使用 :doc:
,而非此方法。
static VALUE rb_mod_protected(int argc, VALUE *argv, VALUE module) { return set_visibility(argc, argv, module, METHOD_VISI_PROTECTED); }
在接收器中精緻化 mod。
傳回模組,其中定義了精緻化方法。
static VALUE rb_mod_refine(VALUE module, VALUE klass) { VALUE refinement; ID id_refinements, id_activated_refinements, id_refined_class, id_defined_at; VALUE refinements, activated_refinements; rb_thread_t *th = GET_THREAD(); VALUE block_handler = rb_vm_frame_block_handler(th->ec->cfp); if (block_handler == VM_BLOCK_HANDLER_NONE) { rb_raise(rb_eArgError, "no block given"); } if (vm_block_handler_type(block_handler) != block_handler_type_iseq) { rb_raise(rb_eArgError, "can't pass a Proc as a block to Module#refine"); } ensure_class_or_module(klass); CONST_ID(id_refinements, "__refinements__"); refinements = rb_attr_get(module, id_refinements); if (NIL_P(refinements)) { refinements = hidden_identity_hash_new(); rb_ivar_set(module, id_refinements, refinements); } CONST_ID(id_activated_refinements, "__activated_refinements__"); activated_refinements = rb_attr_get(module, id_activated_refinements); if (NIL_P(activated_refinements)) { activated_refinements = hidden_identity_hash_new(); rb_ivar_set(module, id_activated_refinements, activated_refinements); } refinement = rb_hash_lookup(refinements, klass); if (NIL_P(refinement)) { VALUE superclass = refinement_superclass(klass); refinement = rb_refinement_new(); RCLASS_SET_SUPER(refinement, superclass); RUBY_ASSERT(BUILTIN_TYPE(refinement) == T_MODULE); FL_SET(refinement, RMODULE_IS_REFINEMENT); CONST_ID(id_refined_class, "__refined_class__"); rb_ivar_set(refinement, id_refined_class, klass); CONST_ID(id_defined_at, "__defined_at__"); rb_ivar_set(refinement, id_defined_at, module); rb_hash_aset(refinements, klass, refinement); add_activated_refinement(activated_refinements, klass, refinement); } rb_yield_refine_block(refinement, activated_refinements); return refinement; }
移除指定常數的定義,傳回該常數的先前值。若該常數參考模組,此舉不會變更該模組的名稱,可能會造成混淆。
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); }
對於指定的方法名稱,標記方法為透過一般引數散列傳遞關鍵字。此方法僅應呼叫在接受引數散列 (*args
) 但不接受明確關鍵字或關鍵字散列的方法上。它標記方法,使得若方法以關鍵字引數呼叫,最後的 hash 引數會標記為特殊旗標,使得若它是傳遞至另一方法呼叫的最後一個一般引數散列元素,且該方法呼叫未包含明確關鍵字或關鍵字散列,最後一個元素會詮釋為關鍵字。換句話說,關鍵字會透過方法傳遞至其他方法。
這應僅用於將關鍵字委派給其他方法的方法,且僅用於與 3.0 之前的 Ruby 版本的向後相容性。詳情請參閱 www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0,了解 ruby2_keywords
存在的理由以及何時及如何使用它。
此方法可能會在某個時間點移除,因為它僅存在於向後相容性中。由於它不存在於 2.7 之前的 Ruby 版本中,因此在呼叫它之前,請檢查模組是否回應此方法
module Mod def foo(meth, *args, &block) send(:"do_#{meth}", *args, &block) end ruby2_keywords(:foo) if respond_to?(:ruby2_keywords, true) end
但是,請注意,如果移除 ruby2_keywords
方法,則使用上述方法的 foo
方法的行為將會變更,以便該方法不會傳遞關鍵字。
static VALUE 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_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 %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(iseq)->param.flags.has_rest && !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 %s (method accepts keywords or method does not accept argument splat)", rb_id2name(name)); } break; } } /* 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; }
將類別精緻化從 module 匯入目前類別或模組定義中。
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; }