module_inst.rb

    你无法创建命名模块的后代,因此不允许这样做:

    1. end
    2. module MyOtherMod < MyMod
    3. end

    但是,与其它类一样,允许创建 Module 类的后代:

    1. class X < Module
    2. end

    实际上,Class 类本身就是 Module 类的后代。它继承了 Module 的行为并添加了一些重要的新行为 - 特别是创建对象的能力。你可以通过运行 modules_classes.rb 程序来显示此继承链以验证 Module 是 Class 的超类:

    modules_classes.rb
    1. Class
    2. Module #=> is the superclass of Class
    3. Object #=> is the superclass of Module

    以下模块内置于 Ruby 解释器中:

    1. Comparable, Enumerable, FileTest, GC, Kernel, Math, ObjectSpace, Precision, Process, Signal

    Comparable 是一个 mixin 模块,允许包含类以实现比较运算符。包含类必须定义 <=> 运算符,该运算符将接收器对象与另一个对象进行比较,返回 -1,0 或 +1,具体取决于接收器对象是否小于,等于或大于另一个对象。Comparable 使用 <=> 来实现常规的比较运算符(<<===>=>)和 between? 方法。

    Enumerable 是为枚举(enumeration)提供的 mix-in 模块。包含类必须提供 each 方法。

    FileTest 是一个包含文件测试功能的模块;它的方法也可以从 File 类访问。

    GC 模块为 Ruby 的标记和垃圾回收清除机制提供了一个接口。一些底层方法也可以通过 ObjectSpace 模块获得。

    Kernel 是 Object 类包含的模块;它定义了 Ruby 的“内置”(‘built-in)方法。

    Math 是一个包含了基本三角函数和超越函数功能的模块函数(module functions)的模块。它具有相同定义和名称的“实例方法”(instance methods)和模块方法。

    ObjectSpace 是一个包含了与垃圾回收工具交互的例程,并且允许你使用迭代器遍历所有活动对象的模块。

    Process 是操纵进程(processes)的模块。它的所有方法都是模块方法(module methods)。

    Signal 是用于处理发送到正在运行的进程的信号的模块。可用信号名称列表及其解释取决于操作系统。

    以下是三个最常用的Ruby模块的简要概述…

    Kernel

    最重要的预定义模块是 Kernel,它提供了许多“标准”(standard)Ruby 方法,如 getsputsprintrequire。与许多 Ruby 类库一样,Kernel 是用 C 语言编写的。虽然 Kernel 实际上是“内置于”(built into)Ruby 解释器,但从概念上讲它可以被视为一个 mixed-in 模块,就像普通的 Ruby mixin 一样,它使得它的方法可以直接用于任何需要它的类。由于它混入到了 Object 类中,所有其它 Ruby 类都继承自该类,因此 Kernel 的方法是全部可访问的。

    Math

    math.rb

    Math 模块的方法以“模块”(module)和“实例”(instance)方法的形式同时提供,因此可以通过将 Math 混入到类中或从外部通过使用模块名称,点和方法名来访问模块方法;你可以使用双冒号访问常量:

    1. puts( Math.sqrt(144) )
    2. puts( Math::PI )

    Comparable

    compare.rb

    Comparable 模块通过将模块混入到你的类中并定义 <=> 方法,来提供一种巧妙的方式定义你自己的比较’运算符’(operators)<<===>=>。然后,你可以指定将当前对象中的某些值与其他值进行比较的规则。例如,你可能会比较两个整数,两个字符串的长度或一些更不常用的值(例如数组中字符串的位置)。我在我的示例程序 compare.rb 中选择了这种不常用的比较类型。这里使用了一个虚构的数组中的字符串索引,以便将一个人的名字与另一个人的名字进行比较。小的索引(例如位于索引 0 处的 “hobbit”)被认为是小于大的索引(例如位于索引 6 处的 “dragon”):

    1. class Being
    2. include Comparable
    3. BEINGS = ['hobbit','dwarf','elf','orc','giant','oliphant','dragon']
    4. attr_accessor :name
    5. def <=> (anOtherName)
    6. BEINGS.index[@name]<=>BEINGS.index[anOtherName]
    7. def initialize( aName )
    8. @name = aName
    9. end
    10. end
    11. elf = Being.new('elf')
    12. orc = Being.new('orc')
    13. puts( elf.name < orc.name ) #=> true
    14. puts( elf.name > giant.name ) #=> false

    与类一样,你可以使用双冒号作用域解析运算符(scope resolution operator)来访问模块内声明的常量(包括类和其它模块)。例如,假设你有嵌套的模块和类,如下所示:

    你可以使用 :: 运算符访问 Class1,如下所示:

    1. OuterMod::InnerMod::Class1
    有关类中常量的作用域解析的介绍,请参见第 2 章…

    每个模块和类都有自己的作用域,这意味着可以在不同的作用域中使用单个常量名称。既然如此,你可以使用 :: 运算符在确定的作用域内指定常量:

    1. Scope1::Scope2::Scope3 #...etc

    如果在常量名称的最开头使用此运算符,则会产生“打破”(breaking out)当前作用域并访问“顶级”(top level)作用域的效果:

    1. ::ACONST # refers to ACONST at „top level‟ scope

    以下程序提供了作用域运算符的一些示例:

    scope_resolution.rb
    1. ACONST = "hello" # We'll call this the "top-level" constant
    2. module OuterMod
    3. module InnerMod
    4. ACONST=10
    5. class Class1
    6. class Class2
    7. module XYZ
    8. class ABC
    9. ACONST=100
    10. def xyz
    11. puts( ::ACONST ) #<= this prints the „top-level‟ constant
    12. end
    13. end
    14. end
    15. end
    16. end
    17. end
    18. end
    19. puts(OuterMod::InnerMod::ACONST)
    20. #=> displays 10
    21. puts(OuterMod::InnerMod::Class1::Class2::XYZ::ABC::ACONST)
    22. #=> displays 100
    23. ob = OuterMod::InnerMod::Class1::Class2::XYZ::ABC.new
    24. ob.xyz
    25. #=> displays hello
    module_func.rb
    1. module MyModule
    2. def sayHi
    3. return "hi!"
    4. def sayGoodbye
    5. return "Goodbye"
    6. end
    7. module_function :sayHi
    8. end

    现在,sayHi 方法可以混入到一个类中并用作实例方法:

    1. class MyClass
    2. include MyModule
    3. def speak
    4. puts(sayHi)
    5. puts(sayGoodbye)
    6. end
    7. end

    它可以用作模块方法,使用点符号:

    由于这里的 sayGoodbye 方法不是模块函数(module function),因此不能以这种方式使用:

    1. puts(MyModule.sayGoodbye) #=> Error: undefined
    2. method

    Ruby 在其一些标准模块,例如 Math(在 Ruby 库文件,complex.rb 中)),中使用 module_function 来创建模块和实例方法的“匹配对”(matching pairs)。

    你可以使用 extend 方法将模块的方法添加到特定对象(而不是整个类),如下所示:

    extend.rb
    1. module A
    2. def method_a
    3. puts( 'hello from a' )
    4. end
    5. end
    6. class MyClass
    7. def mymethod
    8. puts( 'hello from mymethod of class MyClass' )
    9. end
    10. end
    11. ob = MyClass.new
    12. ob.mymethod
    13. ob.extend(A)

    现在对象 ob 用模块 A 进行了扩展(extend),它可以访问该模块的实例方法 method_a

    1. ob.method_a

    实际上,你可以一次用多个模块来扩展对象。这里,模块 BC 扩展了对象 ob

    1. ob.extend(B, C)

    当使用包含了与对象类中方法同名的方法的模块扩展对象时,模块中的方法将替换该类中的方法。所以,我们假设 ob 用这个类来扩展…

    1. module C
    2. def mymethod
    3. puts( 'hello from mymethod of module C' )
    4. end
    5. end

    现在,当你调用 ob.mymethod 时,将显示字符串 ‘hello from mymethod of module C’ 而不是之前显示的 ‘hello from mymethod of class MyClass’。

    你可以通过使用 freeze 方法来“冻结”(freezing)它,以阻止对象被扩展:

      任何进一步扩展此对象的尝试都将导致运行时错误(runtime error)。为了避免这样的错误,你可以使用 方法来测试对象是否已被冻结: