類別 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]

公開類別方法

constants → array 按一下以切換來源
常數(繼承)→ 陣列

在第一個形式中,傳回呼叫點可存取的所有常數名稱陣列。此清單包含在全域範圍中定義的所有模組和類別名稱。

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;
}
new → mod 按一下以切換來源
new {|mod| 區塊} → mod

建立新的匿名模組。如果提供區塊,則將模組物件傳遞給它,且區塊會在這個模組的內容中評估,例如 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);
}
used_modules → 陣列 按一下以切換來源

傳回目前範圍中使用的所有模組陣列。結果陣列中模組的順序未定義。

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);
}
used_refinements → 陣列 按一下以切換來源

傳回目前範圍中使用的所有模組陣列。結果陣列中模組的順序未定義。

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、false 或 nil 按一下以切換來源

如果 modother 的子類別,則傳回 true。如果 modother 相同或 modother 的祖先,則傳回 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 → true、false 或 nil 按一下以切換來源

如果 modother 的子類別或與 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;
    }
}
module <=> other_module → -1、0、+1 或 nil 按一下以切換來源

比較—傳回 -1、0、+1 或 nil,視 module 是否包含 other_module、它們是否相同,或 module 是否由 other_module 包含而定。

如果 moduleother_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);
}
obj == other → true 或 false 按一下以切換來源
equal?(other) → true 或 false
eql?(other) → true 或 false

相等性 — 在 Object 層級中,== 僅在 objother 為同一個物件時傳回 true。通常,此方法會在後代類別中覆寫,以提供類別特定的意義。

== 不同,equal? 方法不應由子類別覆寫,因為它用於判斷物件身分(即,當且僅當 ab 為同一個物件時,a.equal?(b)true

obj = "a"
other = obj.dup

obj == other      #=> true
obj.equal? other  #=> false
obj.equal? obj    #=> true

如果 objother 參照同一個雜湊金鑰,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);
}
mod === obj → true 或 false 按一下以切換來源

大小寫相等性 — 如果 objmod 的執行個體或 mod 的後代之一的執行個體,則傳回 true。對模組的用途有限,但可用於 case 陳述式中,以依類別分類物件。

static VALUE
rb_mod_eqq(VALUE mod, VALUE arg)
{
    return rb_obj_is_kind_of(arg, mod);
}
mod > other → true、false 或 nil 按一下以切換來源

如果 modother 的祖先,則傳回 true。如果 modother 相同,或 modother 的後代,則傳回 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、false 或 nil 按一下以切換來源

如果 modother 的祖先,或兩個模組相同,則傳回 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);
}
alias_method(new_name, old_name) → symbol 按一下以切換來源

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);
}
ancestors → array 按一下以切換來源

傳回在 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(name, ...) → array 按一下以切換來源
attr(name, true) → array
attr(name, false) → array

第一個形式等同於 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);
}
attr_accessor(symbol, ...) → array 按一下以切換來源
attr_accessor(string, ...) → array

為這個模組定義一個命名屬性,其中名稱為 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_reader(symbol, ...) → array 按一下以切換來源
attr(symbol, ...) → array
attr_reader(string, ...) → array
attr(string, ...) → array

建立實例變數和對應的方法,傳回每個實例變數的值。等同於對每個名稱依序呼叫 “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;
}
attr_writer(symbol, ...) → array 按一下以切換來源
attr_writer(string, ...) → array

建立一個存取方法,允許指定屬性 symbol.id2nameString 參數會轉換為符號。傳回已定義方法名稱的陣列,形式為符號。

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;
}
autoload(const, filename) → nil 按一下以切換來源
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;
}
autoload?(name, inherit=true) → String or nil 按一下以切換來源

如果 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);
}
class_eval(string [, filename [, lineno]]) → obj
class_eval {|mod| block } → obj

評估字串或區塊在 mod 的內容中,但當給定區塊時,不會影響常數/類別變數的查詢。這可以用來新增方法到類別中。module_eval 會傳回評估其引數的結果。選用的 filenamelineno 參數會設定錯誤訊息的文字。

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
別名為:module_eval
class_exec(arg...) {|var...| block } → obj

在類別/模組的內容中評估給定的區塊。在區塊中定義的方法將會屬於接收器。傳遞給方法的任何引數將會傳遞給區塊。如果區塊需要存取執行個體變數,可以使用這個方法。

class Thing
end
Thing.class_exec{
  def hello() "Hello there!" end
}
puts Thing.new.hello()

產生

Hello there!
別名為:module_exec
class_variable_defined?(symbol) → true or false 按一下以切換來源
class_variable_defined?(string) → true or false

如果給定的類別變數在 obj 中定義,則傳回 trueString 引數會轉換為符號。

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);
}
class_variable_get(symbol) → obj 按一下以切換來源
class_variable_get(string) → obj

傳回給定類別變數的值(或擲出 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);
}
class_variable_set(symbol, obj) → obj 按一下以切換來源
class_variable_set(string, obj) → obj

將由 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;
}
class_variables(inherit=true) → array 按一下以切換來源

傳回 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);
}
const_defined?(sym, inherit=true) → true or false 按一下以切換來源
const_defined?(str, inherit=true) → true or false

表示 mod 或其祖先是否有一個具有給定名稱的常數

Float.const_defined?(:EPSILON)      #=> true, found in Float itself
Float.const_defined?("String")      #=> true, found in Object (ancestor)
BasicObject.const_defined?(:Hash)   #=> false

如果 modModule,則會額外檢查 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);
}
const_get(sym, inherit=true) → obj 按一下以切換原始碼
const_get(str, inherit=true) → obj

mod 中檢查具有給定名稱的常數。如果設定 inherit,查詢也會搜尋祖先(以及 modModule 時的 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);
}
const_missing(sym) → obj 按一下以切換原始碼

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#autoloadModule#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);
}
const_set(sym, obj) → obj 按一下以切換原始碼
const_set(str, obj) → obj

將命名常數設定為給定的物件,傳回該物件。如果先前不存在具有給定名稱的常數,則會建立新的常數。

Math.const_set("HIGH_SCHOOL_PI", 22.0/7.0)   #=> 3.14285714285714
Math::HIGH_SCHOOL_PI - Math::PI              #=> 0.00126448926734968

如果 symstr 不是有效的常數名稱,則會引發 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;
}
const_source_location(sym, inherit=true) → [String, Integer] 按一下以切換原始碼
const_source_location(str, inherit=true) → [String, Integer]

傳回包含常數定義的 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);
}
constants(inherit=true) → array 按一下以切換來源

傳回 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);
    }
}
define_method(symbol, method) → symbol 按一下以切換來源
define_method(symbol) { block } → symbol

在接收器中定義執行個體方法。method 參數可以是 ProcMethodUnboundMethod 物件。如果指定區塊,則將其用作方法主體。如果區塊或 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);
}
deprecate_constant(symbol, ...) → mod 按一下以切換來源

使現有常數清單過時。嘗試參照它們會產生警告。

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;
}
freeze → mod 按一下以切換來源

防止進一步修改 mod

此方法傳回 self。

static VALUE
rb_mod_freeze(VALUE mod)
{
    rb_class_name(mod);
    return rb_obj_freeze(mod);
}
include(module, ...) → self 按一下以切換來源

依反向順序對每個參數呼叫 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;
}
include?(module) → true or false 按一下以切換來源

如果 module 包含或前置於 modmod 的其中一個祖先,則傳回 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;
}
included_modules → array 按一下以切換來源

傳回包含或前置於 modmod 的其中一個祖先的模組清單。

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;
}
檢查
別名為:to_s
instance_method(symbol) → unbound_method 按一下以切換來源

傳回一個 `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);
}
instance_methods(include_super=true) → array 按一下以切換來源

傳回一個陣列,其中包含接收器中公開和受保護的實例方法名稱。對於模組,這些是公開和受保護的方法;對於類別,這些是實例(非單例)方法。如果選用參數為 `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);
}
method_defined?(symbol, inherit=true) → true 或 false 按一下以切換來源
method_defined?(string, inherit=true) → true 或 false

如果 `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);
}
module_eval(string [, filename [, lineno]]) → obj 按一下以切換來源
module_eval {|mod| block } → obj

評估字串或區塊在 mod 的內容中,但當給定區塊時,不會影響常數/類別變數的查詢。這可以用來新增方法到類別中。module_eval 會傳回評估其引數的結果。選用的 filenamelineno 參數會設定錯誤訊息的文字。

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_eval
module_exec(arg...) {|var...| block } → obj 按一下以切換來源

在類別/模組的內容中評估給定的區塊。在區塊中定義的方法將會屬於接收器。傳遞給方法的任何引數將會傳遞給區塊。如果區塊需要存取執行個體變數,可以使用這個方法。

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);
}
別名為:class_exec
name → string 按一下以切換來源

傳回模組 `mod` 的名稱。對於匿名模組,傳回 nil。

VALUE
rb_mod_name(VALUE mod)
{
    bool permanent;
    return classname(mod, &permanent);
}
prepend(module, ...) → self 按一下以切換來源

以反向順序對每個參數呼叫 `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;
}
private_class_method(symbol, ...) → mod 按一下以切換來源
private_class_method(string, ...) → mod
private_class_method(array) → mod

將現有的類別方法設為私人。通常用來隱藏預設建構函式 `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;
}
private_constant(符號, ...) → 模組 按一下以切換來源

將現有常數清單設為私人。

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;
}
private_instance_methods(include_super=true) → 陣列 按一下以切換來源

傳回 模組 中定義的私人實例方法清單。如果選用參數為 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);
}
private_method_defined?(符號, inherit=true) → true 或 false 按一下以切換來源
private_method_defined?(字串, inherit=true) → true 或 false

如果 模組 定義了指定的私人方法,則傳回 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);
}
protected_instance_methods(include_super=true) → 陣列 按一下以切換來源

傳回 模組 中定義的受保護實例方法清單。如果選用參數為 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);
}
protected_method_defined?(符號, inherit=true) → true 或 false 按一下以切換來源
protected_method_defined?(字串, inherit=true) → true 或 false

如果 模組 定義了指定的受保護方法,則傳回 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);
}
public_class_method(符號, ...) → 模組 按一下以切換來源
public_class_method(字串, ...) → 模組
public_class_method(陣列) → 模組

將現有類別方法清單設為公開。

字串 參數會轉換成符號。也會接受 陣列 的符號和/或字串。

static VALUE
rb_mod_public_method(int argc, VALUE *argv, VALUE obj)
{
    set_method_visibility(rb_singleton_class(obj), argc, argv, METHOD_VISI_PUBLIC);
    return obj;
}
public_constant(符號, ...) → 模組 按一下以切換來源

將現有常數清單設為公開。

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;
}
public_instance_method(符號) → 未繫結方法 按一下以切換來源

類似於 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);
}
public_instance_methods(include_super=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);
}
public_method_defined?(符號, inherit=true) → true 或 false 按一下以切換來源
public_method_defined?(字串, inherit=true) → true 或 false

如果已命名公開方法由 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);
}
refinements → array 按一下以切換來源

傳回在接收器內定義的模組陣列。

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);
}
remove_class_variable(sym) → obj 按一下以切換來源

從接收器移除已命名的類別變數,並傳回該變數的值。

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);
}
remove_method(symbol) → self 按一下以切換來源
remove_method(string) → self

從目前類別移除由 symbol 識別的方法。如需範例,請參閱 Module#undef_methodString 參數會轉換為符號。

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;
}
set_temporary_name(string) → self 按一下以切換來源
set_temporary_name(nil) → self

設定模組的暫時名稱。此名稱會反映在模組的自省中,以及與其相關的值中,例如執行個體、常數和方法。

名稱應該是 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;
}
singleton_class? → true 或 false 按一下以切換來源

如果 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));
}
to_s → string 按一下以切換來源

傳回表示此模組或類別的字串。對於基本類別和模組,這是名稱。對於單例,我們也會顯示所附加對象的資訊。

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);
}
別名為:inspect
undef_method(symbol) → self 按一下以切換來源
undef_method(字串) → self

防止目前的類別回應對命名方法的呼叫。這與 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;
}
undefined_instance_methods → 陣列 按一下以切換來源

傳回在 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);
}

私人實例方法

append_features(mod) → mod 按一下以切換來源

當這個模組包含在另一個模組中時,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;
}
const_added(const_name) 按一下以切換來源

在接收器上指派常數時呼叫為回呼。

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
extend_object(obj) → obj 按一下以切換來源

透過新增這個模組的常數和方法(新增為單例方法)來延伸指定物件。這是 Object#extend 使用的回呼方法。

module Picky
  def Picky.extend_object(o)
    if String === o
      puts "Can't add Picky to a String"
    else
      puts "Picky added to #{o.class}"
      super
    end
  end
end
(s = Array.new).extend Picky  # Call Object.extend
(s = "quick brown fox").extend Picky

產生

Picky added to Array
Can't add Picky to a String
static VALUE
rb_mod_extend_object(VALUE mod, VALUE obj)
{
    rb_extend_object(obj, mod);
    return obj;
}
extended(othermod) 按一下以切換來源

等同於 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
included(othermod) 按一下以切換來源

在接收器包含在另一個模組或類別中時呼叫的回呼。如果您的程式碼想要在模組包含在另一個模組中時執行某些動作,應優先使用此方法而非 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
method_added(method_name) 按一下以切換來源

在實例方法新增到接收器時呼叫為回呼。

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
method_removed(method_name) 按一下以切換來源

在實例方法從接收器中移除時呼叫為回呼。

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
method_undefined(method_name) 按一下以切換來源

在實例方法從接收器中未定義時呼叫為回呼。

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_function → nil 按一下以切換來源
module_function(method_name) → method_name
module_function(method_name, method_name, ...) → array

為指定的 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);
}
prepend_features(mod) → mod 按一下以切換來源

當這個 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;
}
prepended(othermod) 按一下以切換來源

等同於 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
private → nil 按一下以切換來源
private(method_name) → method_name
private(method_name, method_name, ...) → array
private(array) → array

沒有參數時,會將之後定義的 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 → nil 按一下以切換來源
protected(method_name) → method_name
protected(method_name, method_name, ...) → array
protected(array) → array

若無參數,設定後續定義方法的預設可見性為 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);
}
public → nil 按一下以切換來源
public(method_name) → method_name
public(method_name, method_name, ...) → array
public(array) → array

若無參數,設定後續定義方法的預設可見性為 public。若有參數,設定命名方法具有 public 可見性。String 參數會轉換為符號。系統也會接受符號和/或字串的 Array。若傳遞單一參數,系統會傳回該參數。若未傳遞參數,系統會傳回 nil。若傳遞多個參數,系統會傳回這些參數作為陣列。

static VALUE
rb_mod_public(int argc, VALUE *argv, VALUE module)
{
    return set_visibility(argc, argv, module, METHOD_VISI_PUBLIC);
}
refine(mod) { block } → module 按一下以切換來源

在接收器中精緻化 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;
}
remove_const(sym) → obj 按一下以切換來源

移除指定常數的定義,傳回該常數的先前值。若該常數參考模組,此舉不會變更該模組的名稱,可能會造成混淆。

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);
}
ruby2_keywords(method_name, ...) → nil 按一下以切換來源

對於指定的方法名稱,標記方法為透過一般引數散列傳遞關鍵字。此方法僅應呼叫在接受引數散列 (*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;
}
using(module) → self 按一下以切換來源

將類別精緻化從 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;
}