rubyのメタプロに関するまとめ
特異メソッド
特定のオブジェクトに特化したメソッドの事を特異メソッドという。以下例。
hoge = "abc"
def hoge.moji?
self.upcase == self
end
hoge.moji? # => false
この場合のselfはhogeにあたる。
クラスメソッドの正体
上記、特異メソッドの形はクラスメソッドの形に似ていないだろうか?
def hoge.moji?; end
def MyClass.moji?; end
結論から言えば、クラスメソッドはクラスの特異メソッド。
クラスの中でのself(特定のオブジェクト)は、クラス自身になる。
なので、以下のように書ける。
class MyClass
def self.moji?
end
end
##特異クラス
オブジェクトのクラスを参照するにはObject#classで参照できる。しかし、見えているクラスとは別にRubyは裏に特別なクラスを持っている。それが、オブジェクトの特異クラスである。
Object#classでは特異クラスを隠されてしまうので、スコープに入れない。そこで、classキーワードを使った特別な構文があり、特異クラスのスコープに入る事ができる。
class << obj
#メソッドなど定義
end
rubyの入門書などにクラスメソッドの書き方として目にすることがあるが、
これは、特異クラスに特異メソッドを書いている事になる。
class MyClass
class << self
def method_1; end
end
end
上記、コードはクラスMyClassの特異クラスに特異メソッドを定義という意味。
つまり、クラスの特異クラスにメソッドを定義する事は、クラスメソッドを定義したことと同じ意味ということ
また、特異クラスのある場所は、特異メソッドが存在するオブジェクトにある。継承チェーンとしては以下のようになる。
オブジェクト->オブジェクトの特異クラス->オブジェクトのsuperclass
->Object
##クラス拡張とオブジェクト拡張(include,extend)
オブジェクトやクラスの拡張として、include,extentdがよく用いられる。
その例を示す。
例としてクラスにモジュールからクラスメソッドを拡張する。
includeとextendを使用するがどちらも同じ意味になるが、
includeの例をより簡単にしたのがextend。
module MyModule1
def hoge1
'hello'
end
end
class Booo
class << self
include MyModule1
end
end
class Waaaa
extend MyModule1
end
p Booo.hoge1 # => "hello"
p Waaaa.hoge1 # => "hello"
includeは特に説明はしない。extend(Object#extend)は、
レシーバーの特異クラスにモジュールをインクルードするための
ショートカットである。この場合のレシーバーはself(Waaa自身)。
##フックメソッド
特定のイベントをフックとしてRubyから呼び出されるメソッド
・Class#inherited #クラスが継承されたときに呼ばれる
・Object#initialize #オブジェクトを作成(new)するとき呼ばれる。
・Module#included #インクルードされたあとで呼ばれる。
例えば、Module#includedをオーバーライドすれば、モジュールの
ライフサイクルにプラグインできる
module MyModule
def self.included(base)
puts "MyModule は #{base} にミックスインされました"
# baseには呼び出したオブジェクト(この場合はMyClassが返ってくる)
end
end
class MyClass
include MyModule
end
実行結果 # => MyModule は MyClassにミックスインされました
この技術はRailsのActiveRecordでも使用されている。
以下ソース抜粋
module ActiveRecord
module Validation
def self.included(base)
#クラスメソッドの定義
base.extend ClassMethods
base.class_eval do
~~
~~
end
base.send :include, ActiveSupport::CallBacks
end
module ClassMethods
def validates_each(*args)
....
end
上記を簡単に説明するとActiveRecord::Validationがどこかでインクルードされたらフックメソッドincludedが呼ばれ、base.extendでクラスメソッドを定義。さらに、includeしたクラスに動的ディスパッチを使用して、ActiveSupport::CallBacksをインクルードさせている。
なお、base.include ActiveSupport::CallBacksとは書けない。
理由はModuleクラスのprivateメソッドのため。