Cgi_ja
Path: docs/Cgi_ja
Modified: Thu Nov 07 16:33:20 JST 2002

amritaをCGIで使う

概略

このドキュメントは、いくつかのサンプルを示してamritaをCGIで使う方法を説明します。 サンプルとしてブックマークのシステムを使います。

このサンプルは下記のURLでデモしています。

www.walrus-ruby.org/amrita/

モデルクラス

最初にモデルクラスを作成します。

  !/usr/bin/ruby

  require 'amrita/template'
  class Item
    include Amrita::ExpandByMember
    attr_reader :group, :name, :url

    def initialize(group, name, url)
      @group, @name, @url = group, name, url
    end
    def to_s
      %Q[#{@group}|#{@name}|#{@url}]
    end

    def link
      e(:a, :href=>@url) { @url }
    end
  end
  class BookmarkList
    attr_reader :groups

    def initialize
      @groups = {}
    end
    def load_from_file(path)
      File::open(path) do |f|
        f.each do |line|
          begin
            add_new_item(*line.chomp.split('|'))
          rescue
          end
        end
      end
    end

    def save_to_file(path)
      File::open(path, "w") do |f|
        @groups.each do |k, v|
          v.each do |data|
            f.puts data.to_s
          end
        end
      end
    end
    def add_new_item(group="", name="", url="", *x)
      item = Item.new(group, name, url)
      @groups[group] ||= []
      @groups[group] << item
    end
  end

  if __FILE__ == $0
    require 'runit/testcase'
    require 'runit/cui/testrunner'
    class TestBMModel < RUNIT::TestCase
      def test_item
        item = Item.new("aa", "bb", "http://www.xxx.com/")
        assert_equal("aa", item.group)
        assert_equal("bb", item.name)
        assert_equal("http://www.xxx.com/", item.url)
      end

      def test_bookmarkmodel
        bm = BookmarkList.new
        assert_equal(0, bm.groups.size())
        assert_equal({}, bm.groups)
        bm.add_new_item("g", "nm", "http://www.xxx.com/")
        assert_equal(1, bm.groups.size())
        assert_equal(1, bm.groups["g"].size())
        assert_equal("nm", bm.groups["g"][0].name)
        assert_equal("http://www.xxx.com/", bm.groups["g"][0].url)
      end

      def test_load
        bm = BookmarkList.new
        bm.load_from_file("bookmark.dat.sample")
        assert_equal(3, bm.groups.size())
        assert_equal(3, bm.groups["BBS"].size())
        assert_equal("2ch", bm.groups["BBS"][0].name)
        assert_equal("http://www.ruby-lang.org/", bm.groups["Script Languages"][0].url)
      end

      def test_save
        tmp = "/tmp/bmtest#{$$}"
        bm = BookmarkList.new
        bm.load_from_file("bookmark.dat.sample")
        bm.add_new_item("html", "amrita", "http://kari.to/amrita/")
        assert_equal(4, bm.groups.size())
        assert_equal(3, bm.groups["BBS"].size())
        assert_equal("2ch", bm.groups["BBS"][0].name)
        assert_equal("http://www.ruby-lang.org/", bm.groups["Script Languages"][0].url)
        assert_equal(1, bm.groups["html"].size())
        assert_equal("amrita", bm.groups["html"][0].name)

        bm.save_to_file(tmp)
        bm = BookmarkList.new
        bm.load_from_file(tmp)
        assert_equal(4, bm.groups.size())
        assert_equal(3, bm.groups["BBS"].size())
        assert_equal("2ch", bm.groups["BBS"][0].name)
        assert_equal("http://www.ruby-lang.org/", bm.groups["Script Languages"][0].url)
        assert_equal(1, bm.groups["html"].size())
        assert_equal("amrita", bm.groups["html"][0].name)
      ensure
        File::unlink tmp
      end
    end

    if ARGV.size == 0
      RUNIT::CUI::TestRunner.run(TestBMModel.suite)
    else
      ARGV.each do |method|
        RUNIT::CUI::TestRunner.run(TestBMModel.new(method))
      end
    end
  end

Item というクラスはブックマークの項目に対応します。 このクラスには、group, name, url. という3つのアトリビュートが存在します。

BookmarkListItem の集まりです。 グループごとにいくつかの Item を保持していて、 ファイルに保存したりロードしたりできます。

このモデルクラスはHTMLに関する処理を含んでいません。 そのため、添付のテストスクリプトのように簡単にユニットテストをすることができます。

bookmark.cgi

bookmark.cgi はブックマークを表示して、新しいエントリーの入力を受けつけます。

テンプレートファイル

bookmark.cgi はこのテンプレートを使用します。

  <html>
  <body>

  <h1>amrita bookmark sample</h1>
    <div id="groups">
      <h1 id="group_name"></h1>
      <table border="1">
        <tr><th>name</th><th>url</th></tr>
        <tr id=items>
          <td id="name"></td>
          <td id="link"></td>
        </tr>
      </table>
    </div>
    <hr>
    <form  id="form" method="post">
      <table>
        <tr>
          <th>group:</th>
          <td id=group_sel></td>
        </tr>
        <tr>
          <th>new_group:</th>
          <td><input name="group" type="text">
        </td>
        </tr>
        <tr>
          <th>title:</th>
          <td><input name="title" type="text">
        </td>
        </tr>
        <tr>
          <th>url:</th>
          <td><input name="url" type="text">
        </td>
        </tr>
          <tr><th>
          <td><input value="newitem" type="submit">
        </td>
        </tr>
      </table>
    </form>

  </body>
  </html>

コード

これが bookmark.cgi のコードです。

  !/usr/bin/ruby

  require 'cgi'
  require 'amrita/template'
  require 'bmmodel'
  include Amrita
  DATAFILE_PATH="bookmark.dat"
  TEMPLATE_PATH="bookmark.html"
  CACHE_PATH="/tmp/bookmark"

  def make_model_data(bm, selected_group)
    groups = bm.groups.keys.sort
    data = {
      :groups => groups.collect do |k|
        {
          :group_name=>k,
          :items=>bm.groups[k]
        }
      end ,
      :form => {
        :group_sel=>e(:select, :name=>"group_sel") {
          groups.collect do |g|
            if g == selected_group
              e(:option, :value=>g, :selected=>"selected") { g }
            else
              e(:option, :value=>g) { g }
            end
          end
        },
      }
    }

    data
  end
  def generate_output(bm, group)
    Amrita::TemplateFileWithCache::set_cache_dir(CACHE_PATH)
    tmpl = Amrita::TemplateFileWithCache[TEMPLATE_PATH]
    tmpl.use_compiler = true
    tmpl.expand($stdout, make_model_data(bm,group))
  end

  def main
    bm = BookmarkList.new
    bm.load_from_file(DATAFILE_PATH)
    cgi = CGI.new
    url = cgi['url'][0]
    group = ""
    if url
      group = (cgi['group'][0]).to_s
      group = (cgi['group_sel'][0]).to_s if group == ""
      name = (cgi['title'][0]).to_s
      name = url if name == ""
      bm.add_new_item(group, name, url)
      bm.save_to_file(DATAFILE_PATH)
    end
    puts cgi.header
    generate_output(bm, group)
  end
  main

フォームの要素を作成する

新しい項目を入力した後のグループの選択肢は、 最後に入力したグループ名がデフォルトに設定されています。 その処理を行なっているのはここです。

    :form => {
      :group_sel=>e(:select, :name=>"group_sel") {
        groups.collect do |g|
          if g == selected_group
            e(:option, :value=>g, :selected=>"selected") { g }
          else
            e(:option, :value=>g) { g }
          end
        end
      },
    }

このコードが以下のHTMLを生成します。

   <td>
     <select name="group_sel">
       <option value="BBS">BBS</option>
       <option value="Script Languages" selected="selected">Script Languages</option>
       <option value="TestXSS">TestXSS</option>
     </select>
   </td>

このHTMLがテンプレート内の group_sel に対応する所に挿入されます。

コンパイラを使用する

  Amrita::TemplateFileWithCache::set_cache_dir(CACHE_PATH)
  tmpl = Amrita::TemplateFileWithCache[TEMPLATE_PATH]
  tmpl.use_compiler = true
  tmpl.expand($stdout, make_model_data(bm,group))

Amrita::TemplateFileWithCacheAmrita::TemplateFile に コンパイルされたコードをキャッシュして再利用する機能を追加したものです。

CACHE_PATH の中にTEMPLATE_PATH に対応するキャッシュデータが存在し、 テンプレートより新しければ、そのコンパイルされたコードを自動的に再利用します。

注意: このディレクトリは他のユーザが修正できないように確実に設定してください。

現状のバージョンでは、amritaはキャッシュの内容をチェックしません。 もし、ここを修正されるとamritaに任意のコードを実行させることが可能になって非常に危険です。

ここの記述が理解できない場合は、TemplateFileWithCache::set_cache_dirは使用しないでください。


Amrita Script をCGIで使用する

これはAmritaScriptで書かれたブックマークの表示プログラムです。

  <html>
  <body>

  <amritascript> <!--
    require "bmmodel"
    include Amrita
    bm = BookmarkList.new
    bm.load_from_file("bookmark.dat")
    groups = bm.groups.keys.sort

    data = {
      :groups => groups.collect do |k|
        {
          :group_name=>k,
          :items=>bm.groups[k].collect do |item|
          {
            :name=>item.name,
            :link=>a(:href=>item.url) { item.url }
          }
        end
        }
      end
    }
  //--></amritascript>
    <div id="groups">
      <h1 id="group_name"></h1>
      <table border="1">
        <tr><th>name</th><th>url</th></tr>
        <tr id="items">
          <td id="name">name</td>
          <td><a id="link">url with link</a></td>
        </tr>
      </table>
    </div>

  </body>
  </html>

apache配下で実行するには

  * httpd.confを修正して<tt>AllowOverride FileInfo</tt> と <tt>Options ExecCGI</tt> を+cgi-bin+ ディレクトリに設定してください

  * bin/amshandlerをそのディレクトリにコピーしてください
  * .htaccess に以下の記述を追加します。

    AddHandler amrita-script ams
    Action amrita-script /amrita/cgi-bin/amshandler

bookmark.cgi を mod_ruby で使用する

bookmark.cgi は mod_ruby配下で実行することもできます。 httpd.confに下記の指定を行なってください。

  LoadModule ruby_module /usr/lib/apache/mod_ruby.so
  RubyRequire apache/ruby-run

  Alias /amrita/cgi-bin/ /home/tnaka/cvswork/amrita/sample/cgi/
  <Location /amrita/cgi-bin>
    Options ExecCGI
    SetHandler ruby-object
    RubyHandler Apache::RubyRun.instance

    SetEnv AmritaCacheDir /tmp/bookmark # be careful
  </Location>