BlankSlateってなんですか?
Builder::XmlMarkup使っててふとBuilderのconstantsを見たら :BlankSlateとか言うのがあってなんだこれ???となる
なんだこれ???
XmlMarkupはmethod_missingを使って
irb(main):002:0> Builder::XmlMarkup.new.small {|xml| xml.medium {|xml| xml.big("core") }} => "<small><medium><big>core</big></medium></small>"
とかしてくれる、既にメタまみれな便利なクラスです*1。
しかし、method_missingを使っていると言う事は、現在のクラスに存在するメソッド名は(missingじゃ無いので)使えない、と言う事になってしまいます。ObjectはKernelをincludeしてしまってるので、結構なボキャ貧になりそうですね。
1.9からはそんな用途にぴったりのBasicObjectがあって「余分な機能なんかいらない!」という断捨離アンな人たちに羨望の眼差しで迎え入れられているのですが*2、1.8の頃にはそんなもんはない。うーん、そりゃ難儀な。
・・・と思ったら流石はruby。転んでもメタです。
かくしちゃえ
そうですよね。methodかくしちゃえば良いという当たり前。BlankSlateを見てみるとクラスメソッドとして
def hide(name) if instance_methods.include?(name.to_s) and name !~ /^(__|instance_eval)/ @hidden_methods ||= {} @hidden_methods[name.to_sym] = instance_method(name) undef_method name end end
てなのがあります。で、これを使って
instance_methods.each { |m| hide(m) }
と、自分のメソッドを根こそぎundefしてしまうという方針。@hidden_methods は後からrevealで戻せるようにしているだけです。
しかしわからないことが沢山
個人的にはなんでこのhide, reveal(あと find_hidden_method)がアンダースコア名じゃないのか良くわからないのです(
#そもそもそんな事言ってたらBasicObject使ったってequal?とかだめか、、、って ?は違反@XMLだっけ
あとわかんないのが、なんでreveal()とか用意したんだろうというのが・・・目的だけ考えると消すだけで良ーじゃん、とか思ってしまう僕はやっぱりダメな人なんでしょうか。
さらに実はこの後、KernelとObjectに対して(Objectは略)
module Kernel class << self alias_method :blank_slate_method_added, :method_added # Detect method additions to Kernel and remove them in the # BlankSlate class. def method_added(name) result = blank_slate_method_added(name) return result if self != Kernel BlankSlate.hide(name) result end end end
とかして、BlankSlateにとっての親にメソッドが追加された場合、それが見えちゃうので、自分から消す、という処理があります(親の影響からなんとか逃れようとするなんとも思春期なクラスです)。これも正直普通にやったら privateメソッドになるから、method_missingになるような・・・って自分で答え書いてた。publicされたらアウトだからか。。。
class Blank class << self def hide(name) if instance_methods.include?(name.to_s) and name !~ /^(__|instance_eval)/ undef_method name end end end instance_methods.each {|m| hide(m) } end public def foo p "hhhh" end Blank.new.foo # => "hhhh"
結論
スゴい人たちはスゴすぎてスゴいコワい