これが Amrita2の 'hello world' です。
--- ../sample/hello/step1.rb --- |
require "amrita2/template" include Amrita2 class PO def title "hello world" end def body "Amrita2 is a html template libraly for Ruby" end end tmpl = TemplateFile.new("template.html") tmpl.expand(STDOUT, PO.new) |
--- ../sample/hello/template.html --- |
<html> <body> <h1 id='title'>title will be inserted here</h1> <p id='body'>body text will be inserted here</p> </body> </html> |
<html> <body> <h1>hello world</h1> <p>Amrita2 is a html template library for Ruby</p> </body> </html> |
Amrita2を使用するには Presentation Object(PO) とTemplate Object(TO). という二種類のオブジェクトが必要です
Amrita2::TemplateFile は HTML テンプレートをファイルから取りこむ、 TOです。
Amrita2::TemplateFile を作成するには次のようにします。
require "amrita2/template" include Amrita2 tmpl = TemplateFile.new("template.html") |
TO は id 属性のついたXML要素を動的な要素とみなします。 そして id 属性の値をメソッド名として、POのメソッドを呼び出すことで、 その値を取りこみます。
ほとんど任意のRubyオブジェクトが、PO(プレゼンテーションオブジェクト)と して使用でいます。 ただ、動的な要素に対応したメソッドを持っていればいいのです。
class PO def title "hello world" end def body "Amrita2 is a html template libraly for Ruby" end end |
パッシブPOメソッドは、テンプレート展開の為の値を返します。 その値がどのように使用されるかを以下のサンプルで示します。
--- ../sample/hello/step2.rb --- |
require "amrita2/template" include Amrita2 class PO def array_data [1, 2, 3] end def nested_struct Time.gm(2005,2,23, 4,5,6) end def nested_structs [Time.gm(2005,2,23, 4,5,6),Time.gm(2005,2,24, 4,5,6) ] end end tmpl = TemplateText.new <<-END <html> <body> <h1>array data</h1> <span id='array_data'> </span> <h1>nested struct</h1> <p id='nested_struct'> <span id='hour' />:<span id='min' />:<span id='sec' /> </p> <h1>array of nested struct</h1> <p id='nested_structs'> <span id='day' /> </p> </body> </html> END tmpl.expand(STDOUT, PO.new) |
<html> <body> <h1>array data</h1> <span>1</span><span>2</span><span>3</span> <h1>nested struct</h1> <p> <span>4</span>:<span>5</span>:<span>6</span> </p> <h1>array of nested struct</h1> <p> <span>23</span> </p><p> <span>24</span> </p> </body> </html> |
POメソッドが、配列(またはEnumerableなオブジェクト)を返した場合には
def array_data [1, 2, 3] end |
対応する要素は、複数回展開されます。
例えば、このようなテンプレートが
<h1>array data</h1> <span id='array_data'> </span> |
以下のようになります。
<h1>array data</h1> <span>1</span><span>2</span><span>3</span> |
このように要素が内部に構造を持っていた場合
<p id='nested_struct'> <span id='hour' />:<span id='min' />:<span id='sec' /> </p> |
PO メソッドは、内部の要素のID属性値と同じメソッド名を持つオブジェクトを返さなくてはなりません。
def nested_struct Time.gm(2005,2,23, 4,5,6) end |
Time Object は hour, min, sec というメソッドを持っています。 これらのメソッドがTOによって呼ばれて、その結果が対応する要素に挿入されます。
<h1>nested struct</h1> <p> <span>4</span>:<span>5</span>:<span>6</span> </p> |
POが構造体の配列を返却した場合
def nested_structs [Time.gm(2005,2,23, 4,5,6),Time.gm(2005,2,24, 4,5,6) ] end |
要素は、二回展開されて
<p id='nested_structs'> <span id='day' /> </p> |
このようになります。
<h1>array of nested struct</h1> <p> <span>23</span> </p><p> <span>24</span> </p> |
Amrita2 には、属性置換を行なう方法がいくつも用意されています。
--- ../sample/hello/step3.rb --- |
require "amrita2/template" include Amrita2 tmpl = TemplateText.new <<END <html> <body> <h1><a id='a1'>replacing attributes</a></h1> <a id='a2' amrita:type='link'>Amrita2</a> <p>See <a id='a3' amrita:type='use_original_element' href='http://www.ruby-lang.org/'>Ruby homepage</a> for detail </p> <a id='rubytalk' amrita:type='use_args' href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/$1"> [ruby-talk:$1] </a> <a id='raa' amrita:type='use_args' href="http://raa.ruby-lang.org/list.rhtml?name=$1"> [$2] </a> </body> </html> END data = { :a1 => a(:href=>'http://amrita2.rubyforge.org/') { 'replaced' }, :a2 => 'http://amrita2.rubyforge.org/', # use_original_element: REXML::Element object for the spec # will be passed. Edit it and return it or make a new # Element object and return it. :a3 => proc do |e| e.text += "(Japanese)" e.attributes['href'] += 'ja/' e end, # use_args: the value passed will be replaced $1, $2..... :rubytalk=>132204, :raa =>['amrita', 'amrita: HTML template library'] } tmpl.expand(STDOUT, data) |
<html> <body> <h1><a href='http://amrita2.rubyforge.org/'>replaced</a></h1> <a href='http://amrita2.rubyforge.org/'>Amrita2</a> <p>See <a href='http://www.ruby-lang.org/ja/'>Ruby homepage(Japanese)</a> for detail </p> <a href='http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/132204'> [ruby-talk:132204] </a> <a href='http://raa.ruby-lang.org/list.rhtml?name=amrita'> [amrita: HTML template library] </a> </body> </html> |
AttrArray は、POがTOに属性置換を指示する為のオブジェクトです。
PO が AttrArray オブジェクトを( a(..) { .. } は AttrArray を生成するショートカットです),
data = { :a1 => a(:href=>'http://amrita2.rubyforge.org/') { 'replaced' }, } |
属性とテキストが対応する要素に埋め込まれます。
<a href='http://amrita2.rubyforge.org/'>Amrita2</a> |
結果は以下のようになります。
data = { :a1 => a(:href=>'http://amrita2.rubyforge.org/') } |
ボディー部( { ... } )が省略されると
<a href='http://amrita2.rubyforge.org/'>replacing attributes</a> |
属性だけが置換されます。
テンプレートに amrita:type という属性が存在した場合、
<a id='a2' amrita:type='link'>Amrita2</a> |
Amrita2は、対応する要素を違う方法で扱います。 この場合は、'link'というタイプですが、 対応する値が テキストでなくhref 属性に埋め込まれます。
従って、URLを直接渡すことができます。
data = { :a2 => 'http://amrita2.rubyforge.org/', } |
結果はこうなります。
<a href='http://amrita2.rubyforge.org/'>Amrita2</a> |
amrita:type='use_args'の場合は、 テンプレートの$1,$2...という部分が、 置き換えられます。
<a id='rubytalk' amrita:type='use_args' href="http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/$1"> [ruby-talk:$1] </a> |
こういうデータを与えると
data { :rubytalk=>132204, } |
このようになります。
<a href='http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/132204'> [ruby-talk:132204] </a> |
二つ以上のパラメータが必要な場合は、
<a id='raa' amrita:type='use_args' href="http://raa.ruby-lang.org/list.rhtml?name=$1"> [$2] </a> |
配列を渡します。
data { :raa =>['amrita', 'amrita: HTML template library'] } |
このようになります。
<a href='http://raa.ruby-lang.org/list.rhtml?name=amrita'> [amrita: HTML template library] </a> |
テンプレートの中の、属性やテキストを使用して結果を作成したい場合は、 amrita:type 'use_original_element'を使用します。
<p>See <a id='a3' amrita:type='use_original_element' href='http://www.ruby-lang.org/'>Ruby homepage</a> for detail </p> |
そして、 Proc オブジェクトを渡します。
data = { :a3 => proc do |e| # A REXML::Element will be passed to the proc e.text += "(Japanese)" e.attributes['href'] += 'ja/' # modify it or make new Element and return it e end, } |
結果はこうなります。
<p>See <a href='http://www.ruby-lang.org/ja/'>Ruby homepage(Japanese)</a> for detail </p> |
'amrita:type' には以下の種類があります。
--- ../sample/spec/inline.rb --- |
require "amrita2/template" include Amrita2 include Amrita2::Runtime # for amrita:type='erb' TEMPLATE_TEXT = <<END <html> <body> <table> <tr><th>name</th><th>author</th></tr> <tr id='lang' amrita:type='use_args'> <td><a href='$2'>$1</a></td><td>$3</td> </tr> </table> <hr /> powered by <a id='link' amrita:type='link'>Amrita2</a> <br /> <span id='erb_time' amrita:type='erb'><![CDATA[ <small>It is <%= Time.now %> now </small> ]]></span> <hr /> <input id='text1' amrita:type='input' name='text1' type='text' /> <input id='cb1' amrita:type='checkbox' name='cb1' type='checkbox' /> <select id='sel1' amrita:type='select'> <option value='a'>aaa</option> <option value='b'>bbb</option> <option value='c'>ccc</option> </select> </body> </html> END table = [ ["Ruby", "http://www.ruby-lang.org/", "Matz" ], ["Perl", "http://www.perl.com/", "Larry Wall" ], ["Python", "http://www.python.org/", "Guido van Rossum" ] ] data = { :lang=>table, :link=>'http://amrita2.rubyforge.org/', :erb_time =>binding, :text1 => 'Input a text here', :cb1 => rand(2)%2 == 0, :sel1 => ARGV.shift || :a } tmpl = TemplateText.new(TEMPLATE_TEXT) tmpl.expand(STDOUT, data)output <html> <body> <table> <tr><th>name</th><th>author</th></tr> <tr> <td><a href='http://www.ruby-lang.org/'>Ruby</a></td><td>Matz</td> </tr><tr> <td><a href='http://www.perl.com/'>Perl</a></td><td>Larry Wall</td> </tr><tr> <td><a href='http://www.python.org/'>Python</a></td><td>Guido van Rossum</td> </tr> </table> <hr></hr> powered by <a href='http://amrita2.rubyforge.org/'>Amrita2</a> <br></br> <small>It is Mon Apr 11 16:12:57 JST 2005 now </small> <hr></hr> <input name='text1' type='text' value='Input a text here'/> <input name='cb1' type='checkbox'/> <select> <option selected='selected' value='a'>aaa</option> <option value='b'>bbb</option> <option value='c'>ccc</option> </select> </body> </html> |
--- ../sample/spec/erb.rb --- |
require "amrita2/template" include Amrita2 include Amrita2::Runtime # for amrita:type='erb' TEMPLATE_TEXT = <<END <html> <head> <span id='header_title' amrita:type='erb'><![CDATA[ <title><%= TITLE %></title> ]]></span> </head> <body> <span id='title' amrita:type='erb'><![CDATA[ <h1><%= TITLE + $_ %></h1> ]]></span> <table> <tr><th>name</th><th>author</th></tr> <span id='item' amrita:type='erb'><![CDATA[ <tr> <td><%= make_link($_[1], $_[0]) %></td> <td><%= $_[2] %></td> </tr> ]]></span> </table> <hr /> <br /> </body> </html> END table = [ ["Ruby", "http://www.ruby-lang.org/", "Matz" ], ["Perl", "http://www.perl.com/", "Larry Wall" ], ["Python", "http://www.python.org/", "Guido van Rossum" ] ] def make_link(url, text) "<a href='#{url}'>#{text}</a>" end TITLE='Amrita2 with ERB ' # get binding at the place where you can see 'make_link' and 'TITLE ' b = binding # and pass the binding to erb item data = { :header_title => b, :title => [b, ' Sample'], # pass an addtional args :item=> table.collect { |i| [b, i] } } tmpl = TemplateText.new(TEMPLATE_TEXT) tmpl.expand(STDOUT, data)output <html> <head> <title>Amrita2 with ERB </title> </head> <body> <h1>Amrita2 with ERB Sample</h1> <table> <tr><th>name</th><th>author</th></tr> <tr> <td><a href='http://www.ruby-lang.org/'>Ruby</a></td> <td>Matz</td> </tr> <tr> <td><a href='http://www.perl.com/'>Perl</a></td> <td>Larry Wall</td> </tr> <tr> <td><a href='http://www.python.org/'>Python</a></td> <td>Guido van Rossum</td> </tr> </table> <hr></hr> <br></br> </body> </html> |
amrita:type='erb' を使用して、ERBソースを ![CDATA[.....]]で囲みます。 <% %>という記述はXMLでは許されていないからです。
<span id='header_title' amrita:type='erb'><![CDATA[ <title><%= TITLE %></title> ]]></span> |
そして、Bindingオブジェクトを渡します。
TITLE='Amrita2 with ERB ' b = binding data = { :header_title => b, } |
Binding オブジェクトは、ERBテンプレートで使用する変数(とメソッド)が全 て見えている地点で取得してください。
この例では、TITLEという定数が使用されているので、bindingメソッドをTITLEが見え る地点で呼ぶ必要があります。
ERB テンプレートには、パラメータを渡すことができます。
data = { :title => [b, ' Sample'], # pass an addtional args } |
渡されたパラメーター(' Sample' という文字列)が、$_に代入されますので、 ERBソースの中でそれを使用することができます。
<span id='title' amrita:type='erb'><![CDATA[ <h1><%= TITLE + $_ %></h1> ]]></span> |
このようなメソッドで要素を作成したい場合、
def make_link(url, text) "<a href='#{url}'>#{text}</a>" end |
そのメソッドが見える地点で bindingを取得して、 それ用のデータを渡します。
data { :item => [binding, ["Ruby", "http://www.ruby-lang.org/", "Matz" ] ] } |
データとメソッドを、ERBの中で使用することができます。
<span id='item' amrita:type='erb'><![CDATA[ <tr> <td><%= make_link($_[1], $_[0]) %></td> <td><%= $_[2] %></td> </tr> ]]></span> |
結果はこうなります。
<tr> <td><a href='http://www.ruby-lang.org/'>Ruby</a></td> <td>Matz</td> </tr> |
Bindingとデータのペアの配列を渡せば、この方法でテーブルを作ることができます。
table = [ ["Ruby", "http://www.ruby-lang.org/", "Matz" ], ["Perl", "http://www.perl.com/", "Larry Wall" ], ["Python", "http://www.python.org/", "Guido van Rossum" ] ] b = binding data = { :item=> table.collect { |i| [b, i] } } |
--- ../sample/hello/step4.rb --- |
require "amrita2/template" include Amrita2 class PO def passive "passive PO method" end def active(m) m.active("active PO method can replace attrs", :href=>"http://www.ruby-lang.org/") end def active_with_proc proc do |m| m.active_with_proc("output in a Proc") end end def active_nest(m) m.active_nest do |mm| mm.nested("active PO method can ") mm.nested("call dynamic element method ") mm.nested("many times") end end end tmpl = TemplateText.new <<-END <html> <body> <h1>passive method</h1> <span id='passive'> </span> <h1>active method</h1> <a id='active'> </a> <div id='active_with_proc'> </div> <h1>active nested</h1> <p id='active_nest'> <span id='nested' /> </p> </body> </html> END tmpl.expand(STDOUT, PO.new) |
<html> <body> <h1>passive method</h1> <span>passive PO method</span> <h1>active method</h1> <a href='http://www.ruby-lang.org/'>active PO method can replace attrs</a> <div>output in a Proc</div> <h1>active nested</h1> <p> <span>active PO method can </span><span>call dynamic element method </span><span>many times</span> </p> </body> </html> |
If a PO method has a parameter, it will be regarded as an active PO method to which TO passes a Module compiled from the template. The active PO method calls a method of it to produce an output.
def active(m) m.active("active PO method can replace attrs", :href=>"http://www.ruby-lang.org/") end
Using this mechanisim, you can use many feature of Amrita2. Most usable one is setting an attribute.
<h1>active method</h1> <a href='http://www.ruby-lang.org/'>active PO method can replace attrs</a>
If the element has inner structures
<h1>active nested</h1> <p id='active_nest'> <span id='nested' /> </p>
The active PO method should call the method of the template module with a Block.
def active_nest(m) m.active_nest do |mm| mm.nested("active PO method") end end |
Then another Module for inner element will be passed. You can call the methods for inner elements.
<h1>active nested</h1> <p> <span>active PO method</span> </p>
You can call the method of template module any times
def active_nest(m) m.active_nest do |mm| mm.nested("active PO method can ") mm.nested("call dynamic element method ") mm.nested("many times") end end |
Then output is
<h1>active nested</h1> <p> <span>active PO method can </span> <span>call dynamic element method </span> <span>many times</span> </p>
If you don't call the method in an active PO method, then there will be no output.