Amrita2 quick start guide

Step 1: hello world

This is the 'hello world' for Amrita2.

sources

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

output

<html>
  <body>
    <h1>hello world</h1>
    <p>Amrita2 is a html template library for Ruby</p>
  </body>
</html>

description

There are two kind of objects in this example: Presentation Object(PO) and Template Object(TO).

Amrita2::TemplateFile is a TO which accepts HTML template from a file. This is how to create a Amrita2::TemplateFile object.

require "amrita2/template"
include Amrita2
tmpl = TemplateFile.new("template.html")

TO treats an element with id attribute as a dynamic element and will get the data for it from PO data using id attribute's value as method name.

Almost any Ruby object can be a Presentation Object only if PO has methods for dynamic elements.

class PO
  def title
    "hello world"
  end

  def body
    "Amrita2 is a html template libraly for Ruby"
  end
end

And a Hash can be used for PO.

data = {
  :title => "hello world",
  :body => "Amrita2 is a html template libraly for Ruby"
}
tmpl = TemplateFile.new("template.html")
tmpl.expand(STDOUT, data)

If this data was given to the TO, same result will be produced.

Step 2: Iteration and nested struct

PO methods returns a value for template expansion. This sample describes how TO expand values.

sources

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

output

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

Array expansion

If a passive PO method returns Array ( or an Enumerable object)

def array_data
  [1, 2, 3]
end

then the matching element will be expanded many times.

From

<h1>array data</h1>
<span id='array_data'> </span>

To

<h1>array data</h1>
<span>1</span><span>2</span><span>3</span>

You can delete <span> tags using Template Spec if you like.

Nested Struct

If the element has inner struct like this

<p id='nested_struct'>
   <span id='hour' />:<span id='min' />:<span id='sec' />
</p>

then the PO method must return an object with the same methods with ID

def nested_struct
  Time.gm(2005,2,23, 4,5,6)
end

A Time Object has hour, min, sec as method. These methods will be called by TO and the result of the method will be inserted into the matching element.

<h1>nested struct</h1>
<p>
   <span>4</span>:<span>5</span>:<span>6</span>
</p>

Array of Struct

If a PO method returns an Array of Struct

def nested_structs
  [Time.gm(2005,2,23, 4,5,6),Time.gm(2005,2,24, 4,5,6) ]
end

then the element will be expanded two time each for an element of the Array.

From

<p id='nested_structs'>
   <span id='day' />
</p>

To

<h1>array of nested struct</h1>
<p>
   <span>23</span>
</p><p>
   <span>24</span>
</p>

This is true for any levels of nest and any combination of struct and array.

Step 3: attribute handling

sources

Amrita2 has many ways to attribute handling.

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

output

<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 object

AttrArray is a Object for a PO require TO to replace attributes.

If PO passes a AttrArray Object ( a(..) { .. } is shortcut for it),

data = { 
  :a1 => a(:href=>'http://amrita2.rubyforge.org/') { 'replaced' },
}

The attribute and text is inserted to the matching template element.

So this template with data above

<h1><a id='a1'>replacing attributes</a></h1>

makes the result:

<h1><a href='http://amrita2.rubyforge.org/'>replaced</a></h1>

If the body part ( { ... } ) was ommited,

data = { 
   :a1 => a(:href=>'http://amrita2.rubyforge.org/')
}

only attribute will be replaced

<a href='http://amrita2.rubyforge.org/'>replacing attributes</a>

amrita:type attribute

If you put a amrita:type attribute in the template,

<a id='a2' amrita:type='link'>Amrita2</a>

Amrita2 will treat the element in a diffrent way. In this case, the type is 'link' which means the value will be inserted to the href part of the element instead of the contained text of the element.

So you can pass just the url alone for the element.

data = { 
  :a2 => 'http://amrita2.rubyforge.org/',
}

The result is

<a href='http://amrita2.rubyforge.org/'>Amrita2</a>

use_args

amrita:type='use_args' means '$1' in template will be replaced with the data given.

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

If you give this data to this template above

data {
  :rubytalk=>132204,
}

the result will be

<a href='http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/132204'>
  [ruby-talk:132204]
</a>

If two or more args are needed

<a id='raa'  amrita:type='use_args' 
   href="http://raa.ruby-lang.org/list.rhtml?name=$1">
   [$2]
</a>

Give an Array for it.

data {
  :raa =>['amrita', 'amrita: HTML template library']
}

The result is

<a href='http://raa.ruby-lang.org/list.rhtml?name=amrita'>
  [amrita: HTML template library]
</a>

use_original_element

If you use an attribute or text in the template and make the result from it, use the 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>

And pass a Proc to it,

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

The result is

<p>See <a href='http://www.ruby-lang.org/ja/'>Ruby homepage(Japanese)</a> for detail
</p>

'amrita:type's

There are many 'amrita:type's

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)

And you can add new types to Amrita2 if you like to.

The interface of this plugin is very complicated and not fixed yet. So it is not documented well. Mail me if you want to use this feature. I may write a plugin or base of it for you.

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

sources

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

How to use ERB in Amrita2

Use amrita:type='erb' and put the partial ERB source in ![CDATA[.....]]. Because <% %> is not allowed in XML.

<span id='header_title' amrita:type='erb'><![CDATA[
  <title><%= TITLE %></title>
]]></span>

And pass a Binding Object to it.

TITLE='Amrita2 with ERB '
b = binding 
data = { 
  :header_title => b,
}

Get the Binding at the place where all variables used in the ERB template are visible.

In this case, a constant 'TITLE' is used by the ERB source, so binding should be called where TITLE is visible.

pass a value to ERB

You can pass a value to ERB element.

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

The string ' Sample' will be assigned to $_ and you can use it in ERB source.

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

making a table with ERB

If you want to construct an element with a method like this,

def make_link(url, text)
  "<a href='#{url}'>#{text}</a>"
end

get the binding where you can use the method, and pass the data for it

data {
  :item => [binding, ["Ruby", "http://www.ruby-lang.org/", "Matz"  ] ]
}

This data and method can be used in the ERB source

<span id='item' amrita:type='erb'><![CDATA[
<tr>
  <td><%= make_link($_[1], $_[0]) %></td>
  <td><%= $_[2] %></td>
</tr>
]]></span>

The result is

<tr>
  <td><a href='http://www.ruby-lang.org/'>Ruby</a></td>
  <td>Matz</td>
</tr>

You can make a table if you pass a array of pair of binding and data.

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

next step

for Ruby on Rails Programer

for programmers co-working with designers or Dreamweaver guys

for who need good performance and like to know internals of Amrita2

for who loves Amrita2 and like to know every details of it