XML_ja
Path: docs/XML_ja
Modified: Wed Nov 06 16:54:25 JST 2002

amrita と XML

概要

amritaはHTMLと同様、XMLに対しても使用することができます。


XHTML文書

コードと出力

コード:

  require "amrita/template"
  include Amrita

  tmpl_text = <<-END
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <head>
      <title>xhtml sample</title>
  </head>
  <body>
    <h1 id=title>title</h1>
    <p id=body>body text</p>
    <hr />
  </body>
  </html>
     END
  data = {
      :title => "SAMPLE1",
      :body => "members of this HASH will be inserted here and title"
  }

  tmpl = TemplateText.new tmpl_text
  tmpl.prettyprint = true
  tmpl.expand(STDOUT, data)

出力:

   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     <head>
       <title>xhtml sample</title>
     </head>
     <body>
       <h1>SAMPLE1</h1>
       <p>members of this HASH will be inserted here and title</p>
       <hr />
     </body>
   </html>

説明

基本的に、amrita は XHTMLテンプレートからはXHTML文書、HTML4.0テンプレートからHTML4.0文書を作成します。 よって、プログラマーは特定の標準やブラウザ、デバイスに対する適合性をいちいち気にする必要がありません。 それは、デザイナー(テンプレートを書く人)の仕事になります。

フラグを設定することだけです。 これが true に設定されていると、 <hr> のような単独のタグは <hr /> のようにXMLの形式で出力されます。


XML テンプレート

コードと出力

コード:

   data = {
      :table1=>[
         { :name=>"Ruby In A Nutshell", :author=>"Yukihiro Matsumoto, David L. Reynolds", :isbn=>"0596002149" }
         { :name=>"Programming Ruby", :author=>"David Thomas, Andrew Hunt", :isbn=>"0201710897" },
         { :name=>"The Ruby Way", :author=>"Hal Fulton", :isbn=>"0672320835" },
      ]
   }

   <booklist>
     <book id="table1">
        <title id="name" />
        <author id="author" />
        <isbn id="isbn" />
     </book>
   </booklist>
   END
   puts "------------XML output ----------------------"

出力:

   <booklist>
     <book>
       <title>Ruby In A Nutshell</title>
       <author>Yukihiro Matsumoto, David L. Reynolds</author>
       <isbn>0596002149</isbn>
     </book><book>
       <title>Programming Ruby</title>
       <author>David Thomas, Andrew Hunt</author>
       ..........

説明

   puts "------------XML output ----------------------"

amritaは必要があるまでテンプレートをロードしません。 最初に expand href="../../classes/REXML.html">REXMLベースのパーサーを使用します。

amritaでは、ひとつのモデルデータを複数のテンプレートに適用することもできます。 従って、一種類のコードでXMLとHTMLを両方出力することもできます。 詳しくは


XML文書をモデルデータとして使用する

  XML文書(データ) + HTMLテンプレート ==> HTML文書

つまり、一種のスタイルシートとしてamritaを利用する方法です。

code and output

code:

  require "amrita/template"
  include Amrita

  doc = REXML::Document.new <<END
  <booklist>
    <book isbn="0596002149">
       <title>Ruby In A Nutshell</title>
       <author>Yukihiro Matsumoto</author>
       <author>David L. Reynolds</author>
    </book>
    <book isbn="0201710897">
       <title>Programming Ruby</title>
       <author>David Thomas</author>
       <author>Andrew Hunt</author>
    </book>
    <book isbn="0672320835">
       <title>The Ruby Way</title>
       <author>Hal Fulton</author>
    </book>
  </booklist>
  END
  table = doc.elements.to_a("booklist/book").collect do |book|
    {
      :title=>book.elements['title'],
      :authors=>book.elements.to_a('author').collect do |a|
        { :name=>a }
      end,
      #:isbn=>book.attributes['isbn']
      :isbn=>e(:a, :href=>"http://www.amazon.com/exec/obidos/ASIN/#{book.attributes['isbn']}") {
        book.attributes['isbn']
      }
    }
  end

  data = { :table1=>table }
  html_tmpl = TemplateText.new <<END
  <table border="1">
    <tr><th>title</th><th>author</th><th>ISBN</th></tr>
    <tr id=table1>
      <td id="title">
      <td><span id="authors"><span id="name"></span><br></span>
      <td id="isbn">
    </tr>
  </table>
  END

  html_tmpl.prettyprint = true
  html_tmpl.set_hint(HtmlCompiler::AnyData.new)
  html_tmpl.expand(STDOUT, data)

output:

   <table border="1">
     <tr>
     <th>title</th>
     <th>author</th>
     <th>ISBN</th>
     </tr>
     <tr>
     <td>Ruby In A Nutshell</td>
     <td>Yukihiro Matsumoto<br>David L. Reynolds
     <br>
     </td>
     <td><a href="http://www.amazon.com/exec/obidos/ASIN/0596002149">0596002149</a></td>
     </tr>
     <tr>
     <td>Programming Ruby</td>
     <td>David Thomas<br>Andrew Hunt
     <br>
     </td>
     <td><a href="http://www.amazon.com/exec/obidos/ASIN/0201710897">0201710897</a></td>
     </tr>
     <tr>
     <td>The Ruby Way</td>
     <td>Hal Fulton<br>
     </td>
     <td><a href="http://www.amazon.com/exec/obidos/ASIN/0672320835">0672320835</a></td>
     </tr>
   </table>

description

   table = doc.elements.to_a("booklist/book").collect do |book|
     {
       :xxx=>.....
     }
   end

このコードは、<book>要素から含むHashの配列を生成します。 <book>要素はREXML::Elementクラスのインスタンスです。 ですから、REXMLのAPIによって、下位ノードや属性値を取り出すことができます。

       :title=>book.elements['title'],

book.elements['title']は、<book> 要素が持つ最初の <title> 要素です。

       :authors=>book.elements.to_a('author').collect do |a|
         { :name=>a }
       end,

一冊の本にはタイトルは一つしかありませんが、著者は複数いる場合があります。 従って、著者(author)は to_aによってArrayとして処理します。 Ruby の標準メソッドである、collectを使用してHashの配列を作成しています。

       :isbn=>e(:a, :href=>"http://www.amazon.com/exec/obidos/ASIN/#{book.attributes['isbn']}") {
         book.attributes['isbn']
       }

この例では、アマゾンへの直接リンクを貼ろうとしています。 e(...) { ... } は次のようなタグを生成します。

    <a href="http://www.amazon.com/exec/obidos/ASIN/0596002149">0596002149</a>

このアイディア(amritaベースのXMLスタイルシート)をさらに発展させた例が という機能としてまとめられています。 詳細は docs/Tour2 を参照してください。


Ruby のオブジェクトをXMLの項目に変換する

RubyのオブジェクトをXMLに対応させようとすると、 属性に対応するメンバーと下位要素に対応するメンバーがあると思います。

このサンプルは、ls -lのような情報をXMLとして提供する例です。

コードと出力

コード:

   amrita can use an existing class for model data.
   To show this ability, this sample uses Ruby's system class
   File::Stat.

  require "amrita/template"
  include Amrita
  class File
    class Stat
      include Amrita::ExpandByMember

      def entry(name)
        a(:name=>name, :type=>ftype) { self }
      end
      def size_or_nil
        size if ftype == "file"
      end

      def mode_str
        ret = "-rwxrwxrwx"
        /(.*)(.)(.)(.)(.)(.)(.)(.)(.)(.)$/ =~ sprintf("%b",mode)
        $~[2..10].each_with_index do |b,n|
          ret[n+1] = "-" if b != "1"
        end
        ret[0] = "d" if $~[1] == "100000"
        ret
      end
      def unix_inf
        a(:dev=>dev, :uid=>uid, :gid=>gid) { self }
      end
    end
  end

  tmpl = TemplateText.new <<END
    <file id="filelist">
       <size id="size_or_nil" />
       <mode id="mode_str" />
       <times>
         <ctime id="mtime" />
         <mtime id="mtime" />
         <atime id="atime" />
       </times>
       <unix_inf id="unix_inf">
          <inode id="ino" />
       </unix_inf>
    </file>
  END
  dir = ARGV.shift || '*'

  filelist = Dir[dir].collect do |f|
    File::stat(f).entry(f)
  end
  data = { :filelist=>filelist }
  tmpl.expand(STDOUT, data)

出力:

  <file name="CVS" type="directory">
     <mode>drwxr-xr-x</mode>
     <times>
       <ctime>Tue Sep 03 11:07:10 JST 2002</ctime>
       <mtime>Tue Sep 03 11:07:10 JST 2002</mtime>
       <atime>Thu Sep 05 07:30:39 JST 2002</atime>
     </times>
     <unix_inf uid="1000" gid="1000" dev="770">
        <inode>652250</inode>
     </unix_inf>
  </file>
  <file name="precompile.rb" type="file">
     <size>2596</size>
     <mode>-rw-r--r--</mode>
     <times>
       <ctime>Mon Aug 26 09:12:11 JST 2002</ctime>
       <mtime>Mon Aug 26 09:12:11 JST 2002</mtime>
       <atime>Thu Sep 05 09:26:48 JST 2002</atime>
     </times>
     <unix_inf uid="1000" gid="1000" dev="770">
        <inode>310411</inode>
     </unix_inf>
  </file>
  <file name="amstest.ams" type="file">
  .....

説明

    def entry(name)
      a(:name=>name, :type=>ftype) { self }
    end

このメソッドはAttrArrayオブジェクトを生成し、 一部の属性値を設定してから下位要素を自分自身で展開します。

    def size_or_nil
      size if ftype == "file"
    end

もしファイルが通常ファイルでないと、 このメソッドはnilを返し、<size> 要素は削除されます。