README_FOR_1.0_USERS

Path: README_FOR_1.0_USERS
Last Update: Fri Dec 17 13:39:08 JST 2004

amrita2 - a html/xhtml template library for Ruby

Summary

amrita2 has two level API.

  * active template API ( which is 80% compatible with amrita 1.0)
  * passive template API (a new one with more flexiblity)

active template API

template

  <html>
     <body>
      <h1 id='title'>title will be inserted here</h1>
      <p id='body'>body text will be inserted here</p>
     </body>
  </html>

code

  require "amrita2/template"
  include Amrita2

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

result

  <html>
     <body>
      <h1>hello world</h1>
      <p>Amrita is a html template libraly for Ruby</p>
     </body>
  </html>

passive template API (a new one with more flexiblity)

  require "amrita2/template"
  include Amrita2

  tmpl = TemplateFile.new("template.html")

  tmpl.expand(STDOUT) do |m|
    # amrita2 passes a Module compiled from the template
    # with methods for dynamic elements.

    m.title("hello world")
    m.body("Amrita is a html template libraly for Ruby")
  end

will produce same result

highlights

These codes are picked up from unit test code. So it may be too complicated for summary but they work now.

template_spec

With passive template API, you can specify ‘TEMPLATE SPEC’ which controls compilation from HTML to Ruby.

      # template spec
      spec = Amrita2::define_tempalte_spec do
        dynamic_element(:aaa)
      end

      # XML document
      doc = REXML::Document.new  "<x id='aaa'>111</x>"

      # create template object from spec and document
      t = spec.create_template(doc)

      # use template
      out = ""
      t.expand_template(out) do |m|
        m.aaa('hello amrita2')
      end

      assert_equal("<x id='aaa'>hello amrita2</x>", out)

attribute

You can set attributes in any element if you set replace_attr flag in the template spec for the element.

      spec = Amrita2::define_tempalte_spec do
        # With :replace_attr option on, you can control attributes of
        # element with id 'aaa'
        dynamic_element(:aaa, :replace_attr=>true)
      end

      doc = REXML::Document.new "<a id='aaa' class='abc'><b id='bbb'>xxx</b></a>"
      t = spec.create_template(doc)

      out = ""
      t.expand_template(out) do |m|
        # set attribute value
        m.aaa(:class=>'xyz') do |m2|
          m2.bbb('yyy')
        end
      end

      assert_equal("<a class='xyz' id='aaa'><b id='bbb'>yyy</b></a>", out)

use values in template and add some strings to it

      spec = Amrita2::define_tempalte_spec do
        dynamic_element(:link, :use_original_element=>true)
      end
      doc = REXML::Document.new  "<p>See <a id='link' href='http://www.ruby-lang.org/'>Ruby</a> for detail</p>"
      t = spec.create_template(doc)

      t.expand_template(out="") do |m|
        m.link('Ruby homepage')
      end
      assert_equal("<p>See <a href='http://www.ruby-lang.org/' id='link'>Ruby homepage</a> for detail</p>", out)

      t.expand_template(out="") do |m|
        m.link do |e|
          # e is a REXML::Element object. You can modify it with REXML API
          e.attributes["href"] += 'ja/'
          e.attributes.delete("id")
          e.text += '(Japanese)'
          e
        end
      end
      assert_equal("<p>See <a href='http://www.ruby-lang.org/ja/'>Ruby(Japanese)</a> for detail</p>", out)

table manipulation

amrita2 do well with tables like this than amrita 1.0.

amrita2 can generate different lines for odd line and even line automatically.

  tmpl = REXML::Document.new <<END
  <table border='1'>
    <tr><th></th><th></th></tr>
    <tr class='detail1'><td></td><td></td></tr>
    <tr class='detail2'><td></td><td></td></tr>
  </table>
  END

The magic is ‘mach_many’ option.

  include HtmlTag
  spec = Amrita2::define_tempalte_spec(:direct_define=>true) {
    table(:matcher=>TABLE) {
      header(:matcher=>TR) {
        item1(:matcher=>TH)
        item2(:matcher=>TH)
      }
      # the element 'detail' is suposed to match two 'TR' element
      # of template with :match_many option
      detail(:matcher=>TR, :match_many=>true) {
        name(:matcher=>TD)
        author(:matcher=>TD)
      }
    }
  }

  data = [
    [ "Ruby", "matz" ],
    [ "perl", "Larry Wall" ],
    [ "python", "Guido van Rossum" ]
  ]

  spec.create_template(tmpl).expand_template(STDOUT) do |m|
    m.table do |mt|
      mt.header do |mh|
        mh.item1('name')
        mh.item2('author')
      end
      data.each_with_index do |data, i|
        # produce result with (i%2)th TR element automatically
        mt.detail(i) do |md|
          md.name(data[0])
          md.author(data[1])
        end
        # And if template has three TR elements, this code will
        # produce result with (i%3)th line with no modification
      end
    end
  end

result

  <table border='1'>
    <tr><th>name</th><th>author</th></tr>
    <tr class='detail1'><td>Ruby</td><td>matz</td></tr>
    <tr class='detail2'><td>perl</td><td>Larry Wall</td></tr>
    <tr class='detail1'><td>python</td><td>Guido van Rossum</td></tr>
  </table>

testing logic without template, makeing presentation without logic

‘TEMPLATE SPEC’ can validate HTML Template. So, once you wrote a spec and pass it to your designer, he can make a beautiful template freely. Only he must do is validating his work with the ‘TEMPLATE SPEC’.

And if you have a programmer for you, you can tell her to make the logic. She can check her code with ‘TEMPLATE SPEC’ only. She doesn’t need the template.

      require 'amrita2/test'
      spec = Amrita2::define_tempalte_spec do
        dynamic_element(:aaa)
      end

      # create dummy template object from spec without template.
      t = spec.create_dummy_template

      # the behavior of dummy template is same as real one.
      t.expand_template(nil) do |m|
        m.aaa('123')
      end

      # dummy template remembers values assigned to it.
      # You can test it
      assert_equal('123', t[:aaa])

So they both can do their job concurrently. You should only say to them, ‘Check your work against my TEMPLATE SPEC’. And the logic and presentation will go well with each other.

[Validate]