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"

結論

スゴい人たちはスゴすぎてスゴいコワい

*1:pythonにも withを使って似たようなことするxmlbuilderがあります

*2:余談ですが、僕は断捨離という言葉自体はあまり好きじゃなかったりします。断って、捨てて、離れる、、、じゃあ良質なものを入れる、という大事な部分は? という部分。もちろんやましたさんの著書を読むと、むしろそれこそが断捨離の極意なのですが、この単語単体ではその連想がしにくいのが理由です。