まつもと直伝 プログラミングのオキテ 第11回を読んだ

まつもと直伝 プログラミングのオキテ 第11回 コード・ジェネレーション | 日経 xTECH(クロステック)
コードジェネレーションについての章。

なかなか面白い。常々やりたいと思ってた事がRubyを使うと簡単にできそうだ。
binding変数ってのはその場の束縛変数一式を保持してるのか!!bindingをresultに渡すと、同じ変数がtemplateの中で使えるようになる。これ、すごいね!!

それからコードジェネレーションとは直接関係ないんだけど、例として挙げられていたコードに冗長性を感じたのだが、

require 'rexml/document'

doc = REXML::Document.new(File.open("conf.xml"))
doc.root.each_element("bean") {|elem|
 printf "class %s:\n", elem.attributes["name"]
 elem.each_element("attributes") {|attr|
  printf " %s:%s\n",
attr.attributes["name"],attr.attributes["type"]
 }
}

まずattributes関数。これで属性値を取り出すんだけど、attributesって単語長いよね。xmlの性質上、要素に対して名前渡したらそれってもう属性名として解釈しちゃっていいと思うんだけど、要素に直接属性名渡せたらすっきりするよなーと思った。
それから、each_elementだけど、これも*でいいんじゃない?なんて思ったんだな。

で、しばらく考えていたら、それってRubyだったらできるじゃん!!と思って試してみた。
まず、上のelemとattrのクラスはREXML::Elementクラスのオブジェクトなのだが、以下の二つのメソッド(*, [])をを追加してみた。
こんな感じ。

class REXML::Element
  def * *name, &block
      each_element *name, &block
  end

  def [](name)
    attributes[name]
  end
end

すると、さっきのコードがこんなにシンプルになる!!

require 'rexml/document'

doc = REXML::Document.new(File.open("test.xml"))
doc.root.*('bean') { |bean|
  printf "class %s\n", bean["name"]
  bean.*('attributes') { |attr|
    printf "%s:%s\n", attr["name"], attr["type"]
  }
}

超すっきり!!
しかも、これ(*, [])XML扱うとこには今後どこにでも使い回せるじゃん!!
Rubyマジすごいよ!!
毎日新しい発見が有る。