Amrita2 クィックスタートガイド

Step 1: hello world

これが 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

Step 2: パッシブPOメソッド

パッシブ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>

Step 3: 属性置換

ソース

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 オブジェクト

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 属性

テンプレートに 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>

use_args

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>

use_original_element

テンプレートの中の、属性やテキストを使用して結果を作成したい場合は、 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's

'amrita:type' には以下の種類があります。

link
for <a href='....'>....</a>
use_args
replacing $1,$2,$3 with args given
use_original_element
modify the original element in a Proc
input
for <input .... />. The value is inserted as a 'value' attribute
checkbox
for <input type='checkbox' .... />. The value is inserted as "checed='checked'"
select
for <select ..><option ...><option ...></select>
erb
ERB processing in an Amrita2 template (desribed later)
--- ../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>

Step 4: ERB processing

ソース

--- ../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>

Amrita2 で ERB を使用する方法

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が見え る地点で呼ぶ必要があります。

pass a value to ERB

ERB テンプレートには、パラメータを渡すことができます。

data = { 
  :title => [b, ' Sample'], # pass an addtional args
}

渡されたパラメーター(' Sample' という文字列)が、$_に代入されますので、 ERBソースの中でそれを使用することができます。

<span id='title' amrita:type='erb'><![CDATA[
  <h1><%= TITLE + $_ %></h1>
]]></span>

ERBでテーブルを作る

このようなメソッドで要素を作成したい場合、

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] }
}

Step 5: active PO method

ソース

--- ../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>

active PO method

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>

active PO method for nested dynamic elements

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>

call the active PO method many times

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.

next step

using Template Spec

Reverse Engineering

JavaScript Template

Parts template