Amrita2を理解するための一歩
やっぱり私のAmrita2に関する知識はあまりに貧弱であるため、勝手な思い込みや期待、また誤解が多過ぎます。そこできちんとAmrita2を理解しようと思い、コードを読むことにしました。しかしAmrita2はなかなか複雑ですからすぐには理解できません。とりあえず以下のようなスクリプトでAmrita2の挙動を把握していくことから始めたいと思います。
require "rubygems" require "amrita2" $src = <<AM2 << div < << a [['a', 'b', 'c', 'd']] < <span>a</span> AM2 tmpl = Amrita2::Template.new($src) tmpl.set_trace(STDOUT) tmpl.setup tmpl.instance_eval do puts "--- PreProcessor ---" puts @preprocessor.process($src) end puts "--- Render ---" puts tmpl.render_with({:hello => "HELLO"})
この実行結果は次のようになります。
include Amrita2 include Amrita2::Runtime def render_with(value,__binding__,__cnt__ = -1 ) __stream__ = '' $_ = value if $_.kind_of?(Amrita2::Enum) and not $_.kind_of?(ScalarData) $_.each_with_index do |v, i| __stream__.concat(render_with(v, __binding__, i) ) end return __stream__ end Tracer << "%s:%s:%s" % ["<root>","in", $_.inspect] case $_ when DictionaryData __stream__.concat("<div>") __stream__.concat(XX606266418Instance.render_with($_, __binding__)) __stream__.concat("</div>") else __stream__.concat("<div>") __stream__.concat(XX606266418Instance.render_with($_, __binding__)) __stream__.concat("</div>") end Tracer << "%s:%s:%s" % ["<root>","out", __stream__] __stream__ end class XX606266418 include Amrita2 include Amrita2::Runtime def render_with(value,__binding__,__cnt__ = -1 ) __stream__ = '' $_ = value if __cnt__ < 0 $_ = eval("['a', 'b', 'c', 'd']", __binding__) if $_.kind_of?(Amrita2::Enum) and not $_.kind_of?(ScalarData) $_.each_with_index do |v, i| __stream__.concat(render_with(v, __binding__, i) ) end return __stream__ end end Tracer << "%s:%s:%s" % ["<a>","in", $_.inspect] case $_ when DictionaryData __stream__.concat("<a>") __stream__.concat(C000) __stream__.concat(C001) __stream__.concat(C002) __stream__.concat("</a>") when ScalarData __stream__.concat("<a>") __stream__.concat($_.amrita_value) __stream__.concat("</a>") else __stream__.concat("<a>") __stream__.concat(C000) __stream__.concat(C001) __stream__.concat(C002) __stream__.concat("</a>") end Tracer << "%s:%s:%s" % ["<a>","out", __stream__] __stream__ end C000 = " " C001 = "a" C002 = "\n" end XX606266418Instance = XX606266418.new --- PreProcessor --- <div ><a am:for="['a', 'b', 'c', 'd']" > <span>a</span> </a></div> --- Render --- <root>:in:{:hello=>"HELLO"} <a>:in:"a" <a>:out:<a>a</a> <a>:in:"b" <a>:out:<a>b</a> <a>:in:"c" <a>:out:<a>c</a> <a>:in:"d" <a>:out:<a>d</a> <root>:out:<div><a>a</a><a>b</a><a>c</a><a>d</a></div> <div><a>a</a><a>b</a><a>c</a><a>d</a></div>
これならAmXMLを展開した結果と展開済みrender_withを同時に眺められますので、一粒で二度美味しい感じです!おかげで"XX606266418"や"XX606266418Instance"などの非常に興味深い点を発見することが出来ました。この辺の定数やクラスは果たしてきちんと remove_const されているのでしょうか。Webアプリケーションやデーモンさんを書く時にはちょっと気になりますね。あと無意味なcase文があったりするのですが、これはなぜこうなっているのでしょう。まずはこのコードを追い掛けてみて動作を把握してから、この辺の秘密を調べていきたいと思います。
しかし
<< a [['a', 'b', 'c', 'd']] <
のような書き方はアリなのかなぁ。この辺を応用すると色々出来そうな感じ。
追記
syntax highlight と行番号を付与するために次のように改良しました。
require "rubygems" require "amrita2" $src = <<AM2 <<div :hello | Each[:class => ["a", "b"]] >> AM2 tmpl = Amrita2::Template.new($src) tmpl.set_trace(:all) do |msg| if msg.split("\n").size > 1 IO.popen("highlight -l -A --syntax rb", "r+") do |io| io.puts msg io.close_write puts io.readlines end else puts msg end end tmpl.setup tmpl.instance_eval do puts "--- PreProcessor ---" puts @preprocessor.process($src) end puts "--- Render ---" puts tmpl.render_with(:hello => "Hello, World!")
こうするとエラーを起こした時にどこがどうなっているか分かりやすくなって良い感じです。