類別 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 按一下切換來源
constants(inherited) → 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);
}
nesting → array 按一下以切換來源

傳回在呼叫點嵌套的 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| block } → 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 → array 按一下以切換來源

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

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

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

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。(從類別定義的角度思考關係:「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 → true、false 或 nil 按一下以切換來源

如果 modother 的子類別,或與 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;
    }
}
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? 方法不應由子類別覆寫,因為它用於判斷物件身分(也就是說,a.equal?(b) 唯當 ab 是同一個物件時才成立)

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。(從類別定義的角度思考這個關係:「類別 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 或 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;
}
inspect()

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

別名為: to_s
instance_method(symbol) → unbound_method 按一下以切換原始碼

傳回一個表示在 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);
}
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

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;
}
private_constant(symbol, ...) → mod 按一下以切換來源

使現有常數的清單變為私有。

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

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

如果 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);
}
protected_instance_methods(include_super=true) → array 按一下以切換來源

傳回在 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);
}
protected_method_defined?(symbol, inherit=true) → true or false 按一下以切換來源
protected_method_defined?(string, inherit=true) → true or 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.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(symbol, ...) → mod 按一下以切換來源
public_class_method(string, ...) → mod
public_class_method(array) → mod

使現有類別方法清單變為公開。

String 參數會轉換為符號。陣列 Array 的符號和/或字串也會被接受。

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(symbol, ...) → mod 按一下以切換來源

使現有常數清單變為公開。

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(symbol) → unbound_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) → array 按一下以切換來源

傳回在 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);
}
public_method_defined?(symbol, inherit=true) → true or false 按一下以切換來源
public_method_defined?(string, inherit=true) → true or 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(string) → 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 → array 按一下切換來源

傳回在 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

針對指定的方法建立模組函式。這些函式可以用模組作為接收器呼叫,並在混入模組的類別中成為實例方法。 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);
}
prepend_features(mod) → mod 按一下以切換來源

當這個模組在另一個模組中插入時,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;
}
prepended(othermod) 按一下以切換來源

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

如果沒有參數,會將後續定義的方法的預設可見性設為私人。如果傳遞參數,會將指定的方法設為私人可見性。 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);
}
protected → nil 按一下以切換來源
protected(method_name) → method_name
protected(method_name, method_name, ...) → array
protected(array) → array

如果沒有參數,會將後續定義的方法的預設可見性設為受保護。如果傳遞參數,會將指定的方法設為受保護可見性。 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);
}
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) 但不接受明確關鍵字或關鍵字散列的方法上。它將方法標記為,如果使用關鍵字引數呼叫該方法,最後一個雜湊引數會標記為特殊旗標,如果它是傳遞給另一個方法呼叫的最後一個一般引數散列元素,而且該方法呼叫不包含明確關鍵字或關鍵字散列,最後一個元素會被解釋為關鍵字。換句話說,關鍵字會透過方法傳遞給其他方法。

這只應使用在將關鍵字委派給另一方法的方法上,而且只用於與 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;
}