File: XML_ja

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

# amrita と XML

## 概要

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

- amritaは REXML ベースの XML パーサーを持っています
- amritaは HTMLと同様にXHTML文書を作成することができます

  XHTMLのテンプレートを用意すれば、XHTML文書を作成します

- amritaはXML文書をテンプレートとしてXML文書を作成するために使用することができます

  amrita には HTMLのDTD(タグの構造)に依存したコードはわずかしかありません。 また、それらをOFFにすることが容易にできます。 ですから、XHTML以外の一般のXML文書をテンプレートとして使用することができます。

- amritaはモデルデータをXMLドキュメントから取得することができます

  REXMLはRubyのための自然なAPIを提供しています。 そして、REXMLで読みこんだXML文書をモデルデータとして使用することが容易にできます。

- amx: XML用のもうひとつのスタイルシート

  docs/Tour2 を参照してください。

## XHTML文書

### コードと出力

コード:

      require "amrita/template"
      include Amrita

      tmpl_text = <<-END
      <?xml version="1.0"?>
      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml">
      <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.xml = true # use REXML parser
      tmpl.asxml = true
      tmpl.expand(STDOUT, data)

出力:

       <?xml version="1.0"?>
       <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
           "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
       <html xmlns="http://www.w3.org/1999/xhtml">
         <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文書を作成します。 よって、プログラマーは特定の標準やブラウザ、デバイスに対する適合性をいちいち気にする必要がありません。 それは、デザイナー(テンプレートを書く人)の仕事になります。

プログラマーがすることは Template オブジェクト `asxml` フラグを設定することだけです。 これが `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" },
          ]
       }

       xml_tmpl = TemplateText.new <<END
       <booklist>
         <book id="table1">
            <title id="name" />
            <author id="author" />
            <isbn id="isbn" />
         </book>
       </booklist>
       END

       xml_tmpl.xml = true # use REXML-based parser
       puts "------------XML output ----------------------"
       xml_tmpl.expand(STDOUT, data)

出力:

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

### 説明

       xml_tmpl.xml = true # REXMLベースのパーサーを使用する
       puts "------------XML output ----------------------"
       xml_tmpl.expand(STDOUT, data)

amritaは必要があるまでテンプレートをロードしません。 最初に `expand` が呼ばれる時に `xml` フラグが設定されていると、 amritaは REXMLベースのパーサーを使用します。

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

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

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

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

### code and output

code:

      require "amrita/template"
      require "rexml/document"
      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スタイルシート)をさらに発展させた例が sample/tour/xml3.rbにあります。そして、これは amx という機能としてまとめられています。 詳細は 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.xml = true
      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>` 要素は削除されます。