BasicObject


先前一直談到,Object是Ruby中所有物件的父類別,如果定義類別時沒有指定父類別,那預設就是繼承Object,然而從Ruby 1.9之後,Object更上層還有一個BasicObject類別:
>> class Some; end
=> nil
>> Some.superclass
=> Object
>> Some.superclass.superclass
=> BasicObject
>>


Object實際上繼承了BasicObject,並含括了Kernel模組,可以使用ancestors方法得知類別的父類別與含括的模組。例如:
>> Object.ancestors
=> [Object, Kernel, BasicObject]
>>


BasicObject上定義了少量的實例方法:
>> BasicObject.instance_methods
=> [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]
>>


instance_methods可以取得public、protected的實例方法清單,如果傳入false,則可取得該類別階層定義的實例方法。以下可以看出,Object繼承BasicObject後,實際上並沒有定義實例方法:
>> Object.instance_methods(false)
=> []
>>


Object含括了Kernel模組,每個實例預設的實例方法,實際上都是BasicObject與Kernel模組提供:
>> Object.instance_methods
=> [:nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone,
 :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust
, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_
methods, :protected_methods, :private_methods, :public_methods, :instance_variab
les, :instance_variable_get, :instance_variable_set, :instance_variable_defined?
, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :re
spond_to_missing?, :extend, :display, :method, :public_method, :define_singleton
_method, :__id__, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :insta
nce_eval, :instance_exec, :__send__]
>> method_list = Object.instance_methods - Kernel.instance_methods(false)
=> [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]
>> method_list - BasicObject.instance_methods(false)
=> []
>>


BasicObject幾乎什麼功能都沒有,這代表了你要教它什麼都可以,具體而言,你可以繼承BasicObject,定義出自己的繼承體系,而不用擔心太多方法名稱衝突問題。

例如,你也許會想要有以下的程式碼:
xml = Xml.new
xml.person do
    xml.name("caterpillar")
    xml.phone("0970933933")
    xml.mail("caterpillar@openhome.cc")
    xml.address do
        xml.office("台北市....")
        xml.home("高雄市....")
    end
end
puts xml.list

產生出以下的XML:
<person>
  <name>caterpillar</name>
  <phone>0970933933</phone>
  <mail>caterpillar@openhome.cc</mail>
  <address>
    <office>台北市....</office>
    <home>高雄市....</home>
  </address>
</person>

那麼你可以定義以下的類別:
# encoding: Big5
class Xml < BasicObject
attr_reader :list
def initialize
@list = ""
@level = 0
end

def indent
@list << " " * @level
end

def method_missing(mth, *args, &block)
indent
@list << "<#{mth}>"
@list << "#{args[0]}" if args[0]
@list << "\n" if block
@level += 2

yield if block

@level -= 2
@list << " " * @level if block
@list << "</#{mth}>\n"
end
end

method_missing是BasicObject 定義的private方法,如果有個訊息傳送給物件,而在物件的方法查找中都沒有方法可以回應該訊息,就會呼叫method_missing方法,傳入方 法、參數與程式區塊。為了突顯以上Xml繼承BasicObject的好處,可以故意使用與Kernel上定義的實例方法相同的名稱:
xml = Xml.new
xml.hash do
    xml.public_send("orz")
    xml.freeze("XD")
    xml.inspect("zzz")
end
puts xml.list

由於繼承自BasicObject,不用擔心會與Kernel發生方法上的名稱衝突問題,因而會產生以下的XML:
<hash>
  <public_send>orz</public_send>
  <freeze>XD</freeze>
  <inspect>zzz</inspect>
</hash>