Understanding Ruby Singleton Classes - Contextual Development

foobar = Array.new

def foobar.size
  "Hello World!"
end

foobar.size  # => "Hello World!"
foobar.class # => Array

bizbat = Array.new
bizbat.size  # => 0
When you add a method to a specific object, Ruby inserts a new, anonymous class into the inheritance hierarchy as a container to hold these types of methods.

More Ways to Skin a Singleton:

module Foo
  def foo
    "Hello World!"
  end
end

foobar = []
foobar.extend(Foo)
foobar.singleton_methods # => ["foo"]

or

foobar = []

class << foobar
  def foo
    "Hello World!"
  end
end

foobar.singleton_methods # => ["foo"]

Ruby classes are actually objects instantiated from the Class class. Their names are constants that point to this object instantiated from the Class class. While this object holds the instance methods for objects instantiated from it, its so-called class methods are kept on a singleton class.

在 ruby 中,直接對一個 object(e.g. from Array.new) 定義 method 的時候,

  1. ruby 就會從 Array 繼承一個 anonymous class 
  2. 將你定義的 method 寫在 anonymous class 裡面 
  3. 然後將 object 的 class 換成 anonymous class

此時,這個 anonymous class 就是一個 singleton class。
但是從外部是看不到的,會認為 object 是一個 class Array 的東西。

所有在 singleton class 中的 methods 就叫做 singleton methods。

實際上 ruby 只有 instance methods,所有的 class methods 都是 singleton class (between class and Class) 的 singleton methods。

singleton methods 在現實中會用的情形之一就是 test mocking。

 

由這個機制延伸來理解 Object#extend首先了解 extend 與 include 的基本用途後,

會看到 extend (新增 methods 到目前 "instance")的用法是:

 

obj = Object.new
obj.extend InstanceMethods
puts obj.an_instance_method      # >> You called an_instance_method on Object

 

在 irb 中自己打一遍,就能發現,所有被 extend 加入的 methods 都會顯示在 obj.singleton_methods 中。

也就是說,對 obj 作 extend 的意思與最上面的「直接對 object 再定義 methods 」的最後結果是一樣的!

都是產生一個 singleton class,把加入的 method 都放到 class 裡面變成 singleton_methods。

 

如果是寫成這樣:

class MyClass
  include InstanceMethods
  extend ClassMethods
end

在這裡的 extend ClassMethods 與 MyClass.new.extend(ClassMethods) 是一樣的意思,能直接寫在 class 定義裡面是因為 class 繼承自 Object,而 Object#extend

也就是說,與上面一樣,就是產生 singleton methods,

因為這裡的對象是 class MyClass,這些 singleton methods 也就是所謂 MyClass 的 class methods。

所以這行就是定義 class methods。

有了這個觀察到的現象,就不難理解這樣的 code:

module Rake  include Test::Unit::Assertions  def run_tests # etc.  end  # what does the next line do?  extend selfend

在這裡的 extend selfRake.new.extend(Rake) 是一樣的意思,

Rake.new.extend(Rake) 的結果就是產生了一個 singleton class,裡面的 methods 都是自己原來的 singleton methods。

也就是說,這行就是直接跟 ruby 說,我在這個 module Rake 裡面定義的所有 method 就是我的 module method(class method of class Module)。

→→→終極簡寫法。