Rubyのモジュールでextend self
冷静になってゆっくり理解すれば、そりゃそっかという話なんだけど、Railsプロジェクトのコードでめっちゃ混乱したので、書いておく。
TL;DR
extend self
は特異クラスへのinclude
と等価というのがミソ。
混乱したコード
目にした瞬間これ本当に大丈夫なのかと思ったコード。
class ApplicationController < ActionController::Base # something class << ActionController::HttpAuthentication::Digest # seconds_to_timeoutを書き換えたい def validate_nonce(secret_key, request, value, seconds_to_timeout=60*60) # somthing super end end end
このコードを見て、いやいやそりゃ大丈夫でしょと思う人はそっとタブを閉じるといい。
僕はというとあれ、super
ってどういうこと?ってなった。
Digestの特異クラス定義式を使って特異クラスにメソッドを追加しているのだけど、super
をcallしてもメソッドが見つからず探索は終了しないかという疑問だ。
つまり、メソッドは上書きされるはずではないかと思った。
ただこのコード、こいつ動くぞ!って驚きと共に動いていた。
そこでDigestの定義を見てみる。
そうすると、モジュール自身に対してextend self
で定義してあった。
module ActionController module HttpAuthentication module Digest extend self # somthing def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60) # something end end end end
それでもなぜメソッドが上書きされないのかとか思っていたが、冒頭に書いたとおり、このモジュール自身に対してのextend self
が今回のミソで、これにより上書きされないようになる。
より理解を深めるために、簡略化したコードで考える。
簡略化したコード
上記で説明したコードの周りを調べて簡略化すると次のようになる。
A
がActionController::HttpAuthentication::Digest
でHoge
がApplicationController
、hoge
がvalidate_nonce
といった感じ。
module A extend self def hoge puts "A" end end class Base def call A.hoge end end class Test < Base class << A def hoge puts "test" super end end end Test.new.call
実行すると次のような結果だ。
$ ruby test.rb
test
A
super
によりちゃんとメソッドがcallされていることが分かる。
これを理解するために書き換えをしてみる。
module A #extend self class << self include A end def hoge puts "A" end end class Base def call A.hoge end end class Test < Base class << A def hoge puts "test" super end end end Test.new.call
$ ruby test.rb
test
A
これを見ると謎が解ける。
特異クラス定義式で定義されたhogeメソッドとモジュールのinclude
によって定義されたhogeメソッドという関係なので、特異クラス定義式のhogeメソッドが先に呼ばれ、あとからモジュールで定義されたhogeメソッドが呼ばれるというわけだ。
モジュールのinclude
に関してはメタプログラミングRubyとか見ながら、自分で試してみるとモジュールのinclude
によってモジュールが継承チェーンに挿入されていることが分かると思う。
まとめ
extend self
は怖くない。
include
うんぬんの辺りがわからない人はメタプログラミングRubyを読みましょう!
- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: アスキー・メディアワークス
- 発売日: 2010/08/28
- メディア: 大型本
- 購入: 18人 クリック: 533回
- この商品を含むブログ (125件) を見る