2012年7月25日水曜日

Webクライアントの実装(1)

はじめに

おはようございます。当ブログにアクセス頂き、ありがとうございます。
昨日美味しい生牡蠣を腹一杯食しご機嫌の、たなけんです。
本エントリでは、Clojurescriptを利用したWebクライアント開発について記載します。

Clojurescriptとは

Clojurescriptとは、ClojureでJavaScriptアプリケーションを開発するためのライブラリ群です。CoffeeScriptやJSX同様、書かれたプログラムをJavaScriptにコンパイルすることによって、アプリケーションをJavaScriptが動作する環境(webブラウザやnode.jsなどのサーバ上)で動作させることができます。

なぜClojurescriptなのか

JavaScriptの言語仕様(変数スコープ、プロトタイプ方式オブジェクト指向など)に馴染めないこと、Clojureスタイルでのプログラミングが私にとって最も効率が良いということからClojurescriptを利用することにしました。
また、ClojurescriptはGoogle Closure Toolsを利用しているため、Closureが提供するクラス方式のオブジェクトシステムや、多彩なUIコンポーネントが利用できる点も魅力でした。

Clojurescriptの導入

ひなたねこさんの記事を参考にlein-cljsbuildを導入しました。lein cljsbuild auto としておけば、core.cljsを更新したタイミングでJavaScriptにコンパイルされます。(他のツールは必要なく、lein-cljsbuildだけで完結している点が素晴らしいです)
また、emacsのClojurescript-modeはELPA経由でインストールしました。
Clojurescript用のreplもemacsから利用可能な様でしたが、今回は出力されたJavaScriptを見てデバッグを行いました。(時間を見つけて、Clojurescriptのreplも試そうと思います)

Webクライアントの実装(1)

プロジェクトファイル


プラグイン部でlein-cljsbuildの利用を宣言します。また、closure compilerで用いるコンパイルオプションをcljsbuild部で指定します。今回は生成されるJavascriptの可読性を重視したため、最適化はしない方針としました。

index.html


ヘッダ部でJavaScriptファイルを指定し、ボディ部の最後でmain関数を呼び出しています。また検索語を入力するフォーム、確定ボタン、結果表示用のdiv要素を宣言しました。

nsマクロ


通常のClojureとの違いとして、ClojureからJavaのクラスを利用する場合は:requireではなく:importを使用するのですが、ClojurescriptからClosure Libraryのクラスを利用する場合もClojureのライブラリ同様:requireで良いという点があります。
nsマクロでは、以下のライブラリの利用を宣言しました。
  • clojure.browser.event: ブラウザのイベントを操作するClosure Libraryのクラス群の薄いラップしたライブラリ
  • clojure.browser.dom: ブラウザのDOMを操作するClosure Libraryのクラス群の薄いラップしたライブラリ
  • goog.net.XhrIo: Closure Libraryが提供するJacascriptのhttpXmlRequestオブジェクトを操作するクラス


main関数


確定ボタンのクリックイベントをトリガーにdictionary-request関数を呼びます。

  • clojure.browser.event/listen関数: 引数に指定された要素、イベントによって第3引数で指定された関数が呼び出されるよう設定
  • clojure.browser.dom/get-element関数: 引数に指定されたシンボルのidに合致する要素を取得

生成されたJavaScript


dictionary-request関数


httpのGETメソッドによるリクエストを送信し、結果を引数にコールバック関数を呼び出します。
  • goog.net.XhrIo/sendメソッド: 引数のurlにhttpのGETメソッドによるリクエストを送信
  • clojure.browser.dom/get-value関数: 引数に指定された要素の値(今回の場合、フォームquery-wordに入力された文字列)を取得
生成されたJavaScript


dictionary-callback関数


httpレスポンスからJSONオブジェクトを取得し、create-list関数を用いて結果を変換し、変換された結果をdiv要素の子要素として追加します。
  • .-プロパティ名 特殊形式: JavaScriptオブジェクトのプロパティにアクセス(今回の場合entriesプロパティ)
  • goog.net.XhrIo/getResponseJsonメソッド: 取得されたレスポンス内のデータをJSONオブジェクトとして取得
  • clojure.browser.dom/append関数: 第1引数に指定された要素に、第2引数に指定された要素を子要素として追加
生成されたJavaScript

create-list関数


JSONオブジェクトを引数に取り、textプロパティから値を取得し、<ul><li>textプロパティの値</li> ... </ul>形式のdom要素を作成します。

  • clojure.browser.dom/html->dom関数: html形式の文字列からDOMツリーを生成


生成されたJavaScript

意外な落とし穴

実装を終えて早速クライアントからサーバへの疎通を確認しようと、ブラウザからアクセスしたところ、Safariでは問題なくレスポンスが処理されるが、Firefoxではレスポンスのボディ部が0バイト(サーバ側で設定したはずの値が、ボディ部に含まれていない)様な挙動を見せ、想定した通りの処理をすることができませんでした。
ブラウザ毎に異なる挙動はClosureライブラリで吸収されているはず、と思いながらも、ここから問題解決まで思ったよりも時間が掛かってしまいました。。。(次回に続く)


今回の作業は以上。最後までお読み頂きありがとうございました。
たなけん (作業時間90分)

0 件のコメント:

コメントを投稿