類別 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
。(從類別定義的角度思考關係:「class 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
。(從類別定義的角度思考關係:「class A < B」表示「A < B」。)
VALUE rb_class_inherited_p(VALUE mod, VALUE arg) { if (mod == arg) return Qtrue; if (RB_TYPE_P(arg, T_CLASS) && RB_TYPE_P(mod, T_CLASS)) { // comparison between classes size_t mod_depth = RCLASS_SUPERCLASS_DEPTH(mod); size_t arg_depth = RCLASS_SUPERCLASS_DEPTH(arg); if (arg_depth < mod_depth) { // check if mod < arg return RCLASS_SUPERCLASSES(mod)[arg_depth] == arg ? Qtrue : Qnil; } else if (arg_depth > mod_depth) { // check if mod > arg return RCLASS_SUPERCLASSES(arg)[mod_depth] == mod ? Qfalse : Qnil; } else { // Depths match, and we know they aren't equal: no relation return Qnil; } } else { if (!CLASS_OR_MODULE_P(arg) && !RB_TYPE_P(arg, T_ICLASS)) { rb_raise(rb_eTypeError, "compared with non class/module"); } if (class_search_ancestor(mod, RCLASS_ORIGIN(arg))) { return Qtrue; } /* not mod < arg; check if mod > arg */ if (class_search_ancestor(arg, mod)) { return Qfalse; } return Qnil; } }
比較—傳回 -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.equal?(b)
唯當 a
與 b
是同一個物件時才成立)
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
。(從類別定義的角度思考這個關係:「類別 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; }
傳回一個表示在 mod 中的指定實例方法的 UnboundMethod
。
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
。
String
參數會轉換為符號。陣列 Array
的符號和/或字串也會被接受。
class SimpleSingleton # Not thread safe private_class_method :new def SimpleSingleton.create(*args, &block) @me = new(*args, &block) if ! @me @me end end
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; }
傳回在 mod 中定義的私有實例方法清單。如果選用參數為 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); }
如果 mod 定義了命名私有方法,則傳回 true
。如果設定 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
static VALUE rb_mod_private_method_defined(int argc, VALUE *argv, VALUE mod) { return check_definition(mod, argc, argv, METHOD_VISI_PRIVATE); }
傳回在 mod 中定義的受保護實例方法清單。如果選用參數為 false
,則不會包含任何祖先的方法。
VALUE rb_class_protected_instance_methods(int argc, const VALUE *argv, VALUE mod) { return class_instance_method_list(argc, argv, mod, 0, ins_methods_prot_i); }
如果 mod 定義了命名受保護方法,則傳回 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.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); }
傳回在 mod 中定義的公開實例方法清單。如果選用參數為 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
針對指定的方法建立模組函式。這些函式可以用模組作為接收器呼叫,並在混入模組的類別中成為實例方法。 Module
函式是原始函式的副本,因此可以獨立變更。實例方法版本設為私人。如果沒有參數,後續定義的方法會變成模組函式。 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); }
當這個模組在另一個模組中插入時,Ruby 會呼叫這個模組中的 prepend_features
,並將接收模組傳遞給 mod。如果這個模組尚未新增到 mod 或其祖先,Ruby 的預設實作會將這個模組的常數、方法和模組變數覆蓋到 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 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
如果沒有參數,會將後續定義的方法的預設可見性設為私人。如果傳遞參數,會將指定的方法設為私人可見性。 String
參數會轉換為符號。系統也會接受符號和/或字串的 Array
。如果傳遞單一參數,會傳回該參數。如果沒有傳遞參數,會傳回 nil。如果傳遞多個參數,會以陣列形式傳回這些參數。
module Mod def a() end def b() end private def c() end private :a end Mod.private_instance_methods #=> [:a, :c]
請注意,若要在 RDoc
上顯示私人方法,請使用 :doc:
。
static VALUE rb_mod_private(int argc, VALUE *argv, VALUE module) { return set_visibility(argc, argv, module, METHOD_VISI_PRIVATE); }
如果沒有參數,會將後續定義的方法的預設可見性設為受保護。如果傳遞參數,會將指定的方法設為受保護可見性。 String
參數會轉換為符號。系統也會接受符號和/或字串的 Array
。如果傳遞單一參數,會傳回該參數。如果沒有傳遞參數,會傳回 nil。如果傳遞多個參數,會以陣列形式傳回這些參數。
如果一個方法具有受保護的可見性,它只能在上下文的self
與方法相同時呼叫。(方法定義或 instance_eval)。此行為與 Java 的受保護方法不同。通常應使用private
。
請注意,受保護的方法很慢,因為它無法使用內聯快取。
若要在 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
) 但不接受明確關鍵字或關鍵字散列的方法上。它將方法標記為,如果使用關鍵字引數呼叫該方法,最後一個雜湊引數會標記為特殊旗標,如果它是傳遞給另一個方法呼叫的最後一個一般引數散列元素,而且該方法呼叫不包含明確關鍵字或關鍵字散列,最後一個元素會被解釋為關鍵字。換句話說,關鍵字會透過方法傳遞給其他方法。
這只應使用在將關鍵字委派給另一方法的方法上,而且只用於與 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; }