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:CouchDBのFutonにくっついている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つのデータベース上に複数のアプリケーションをホストすることができる」という言明は正しそうだ。