CouchDBでRelaxアプリケーション開発 :: 10分で作る FeedReader -> それ"データベース"でできるよ編の準備(2)

昨日の段階で、CouchDBに登録されているJSONのFeedを表示することはできるようになったので、今度は、CouchDB上でFormを作って巡回するフィードのURIを登録できるようにする。

CouchAppにおいて、アプリケーションサーバーのごとく、ドキュメントから動的にページを生成するには、デザインドキュメントのlists, showsなどを用いるが、それ以外に、_attachments に添付ファイルとしてHTMLをおいておけば、静的なページに対応するのは容易だ。しかも、昨今のjQueryパワーを使えば、十分に動的なページになる。

デフォルトで作成される _attachements/index.html は次のようになっている。

<!DOCTYPE html>
<html>
  <head>
    <title>Generated CouchApp</title>
    <link rel="stylesheet" href="style/main.css" type="text/css">
  </head>
  <body>
    <h1>Generated CouchApp</h1>
    <ul id="view"></ul>
  </body>
  <script src="/_utils/script/json2.js"></script>
  <script src="/_utils/script/jquery.js?1.2.6"></script>
  <script src="/_utils/script/jquery.couch.js?0.8.0"></script>
  <script type="text/javascript" charset="utf-8">
    $(function() {
      var dbname = document.location.href.split('/')[3];
      var design = unescape(document.location.href).split('/')[5];
      var DB = $.couch.db(dbname);
      DB.view(design+"/example", { reduce: false, success: function(json) {
        $("#view").html(json.rows.map(function(row) {
          return '<li>'+row.key+'</li>';
        }).join(''));
      }});
    });
  </script>
</html>

そしてこれは、jQuery から CouchDB にアクセスする方法を示している*1

というわけで、同じように _attachments/form.html を作ろう。

<!DOCTYPE html>
<html>
  <head>
    <title>Add Feed URI</title>
    <link rel="stylesheet" href="style/main.css" type="text/css">
  </head>
  <body>
    <h1>Add Feed URI</h1>
    <form>
      <input id="uri"      type="text"   name="uri"      value="" style="width: 400px"/>
      <input id="submit"   type="button" name="submit"   value="Submit" />
    </form>
  </body>
  <script src="/_utils/script/json2.js"></script>
  <script src="/_utils/script/jquery.js?1.2.6"></script>
  <script src="/_utils/script/jquery.couch.js?0.8.0"></script>
  <script type="text/javascript" charset="utf-8">
      $(function(){
        var dbname = document.location.href.split('/')[3];
        var DB     = $.couch.db(dbname);
        $("#submit").bind("click", function(){
          var feed_uri = $("#uri").val();
          DB.saveDoc(
            // Document
            {
              _id      :  feed_uri,
              doc_type :  "Feed"
            },
            // Option (Callback, etc)
            {
              success: function(json){
                alert("Success!!");
                // navigate to show
                // /relax_reader/_design/relax_reader/_show/feed/{uri}
                document.location.href =  ["", dbname, "_design", "relax_reader", "_show", "feed", encodeURIComponent(feed_uri) ].join('/');
              }
            }
          );
        });
      });
  </script>
</html>

これでフォームができます。試してみたところ、DB.saveDoc() は失敗する(たとえば同じURIを登録しようとするとCouchDB上で_revの衝突を検知する、などの機能で4XXが返る場合)と、alertするのがデフォルトの挙動の模様。

とりあえず、例外は無視して、成功した場合、CouchDB側は2XXを返すだけなので、ナビゲーションはjQueryでやる。せっかくなので、show 機能を使おう、ということで、リダイレクト(ちょっと違)する。

となると、shows の下にfeed.jsというのをおいて、フィードのレンダリングを定義しなければ、と。

CouchApp はデフォルトでJavaScript用のテンプレートエンジンを持っています(lib/helpers/template.js がその実装)。

function(doc, req) {
  // !code lib/helpers/template.js
  // !json lib.templates
  respondWith(req, {
    html : function() {
      var html = template(lib.templates.feed, {doc: doc});
      return {body:html};
    }
  })
};

てな具合にfeed.js を書いておく。 // !code とか // !json とかは、CouchApp 用のマクロ呼び出しで、push するときにファイルを埋め込むだとか、そんなことをしてる。

ちなみに、shows/example-show.js を真似ようと思ったんだけれど、このexampleファイルは、template の呼び出しが

var html = template(lib.templates.example, doc);

なっている。tempalte の第二引数は { "テンプレートで使用する変数名" : 値 } 形式で書くので、上記記述だと doc の各メンバーがテンプレート変数名になり、スキーマレスのデータでこれをやるのは、テンプレートにくる変数が何かわからない、という状態を生みかねないのでおすすめできない。。実際、shows/example-show.js はテンプレートHTMLで使用している変数とtemplate()関数に渡す変数が不一致を起こしていて、どんなドキュメントだろうが5XXエラー(JavaScriptエンジンが例外で落ちる場合)を起こすようになっていた。

さて、最後に、lib/templates/feed.html にテンプレートを置く。

<!DOCTYPE html>
<html>
  <head>
    <title>Feed :: <%= doc._id %></title>
  </head>
  <body>
    <div id="header">
      <h2><a href="/relax_reader/_design/relax_reader/index.html">Back to index</a></h2>
    </div>
    <div id="content">
      <h1>Feed :: <% doc._id %> </h1>
    </div>
  </body>
</html>

あとはテンプレートをもっとこったものにしたり、だとかいろいろあるが、とりあえずこれでほとんど終了。もうちょっとコードをきれいにしつつ、見た目もきれいにしたら、公開してみます。

ところで、10分で作るのは現状だと難しそうなのでCouchAppのテンプレートを少しハックしてごまかそうかな(w。Railsのやつも、もともとは今はなきw scaffoldなんだし。

*1:CouchDBFutonにくっついているTestSuiteのJavaScriptコードもjQueryなので参考になると思う

CouchDBにおけるアプリケーションとは何なのか?

CouchDB自体にjquery.couch.js に答えがあった。

        allApps: function(options) {
          ....
        }

これを見ていくと

                      var index, appPath, appName = ddoc._id.split('/');
                      appName.shift();
                      appName = appName.join('/');

アプリケーションとは、デザインドキュメントのことである。

                      index = ddoc.couchapp && ddoc.couchapp.index;
                      if (index) {
                        appPath = ['', name, ddoc._id, index].join('/');
                      } else if (ddoc._attachments && ddoc._attachments["index.html"]) {
                        appPath = ['', name, ddoc._id, "index.html"].join('/');
                      }

アプリケーションとして定義されたデザインドキュメントにはcouchappというメンバーがあり、couchapp.index によりインデックスページが取得できるか、couchapp.index がなければ、_attachments/index.html をインデックスページとする。

となると、「CouchDBは、1つのデータベース上に複数のアプリケーションをホストすることができる」という言明は正しそうだ。