Clojure + d3.js でWebアプリ

## はじめに
みんな大好き、ClojureでWebアプリのお話です。
Clojrueのテンプレートとかよくわからなくても、HTMLじゃなくてJSONを返すようにすれば、JSと組み合わせてカッコイイサイトが簡単にできちゃいますね！
今回は、はやりのd3.jsを使ってみました。

**注：Leiningenプロジェクトの作成とかd3.jsの設置といった基本的な説明は省きますm(_ _)m**

## 環境
* clojure 1.6.0
* Leiningen 2.4.2
* Java 1.8.0

## 基本構成
```
hoge-app
├── project.clj ①依存ライブラリ
├── resources ④静的リソースを配置
   └── public
       └── assets
           └── css
               └── hogehoge.css
       ├── demo
       │   └── hogehoge.html ⑤JSONを受け取るd3
       └── index.html
└── src
    └── hoge_app
        ├── core.clj ②ルーティングとか
        └── hogehoge.clj ③マップを返すクラス
```

## 解説
### ①使用するライブラリなど
```clj:project.clj
 :dependencies [[org.clojure/clojure "1.6.0"]
                 [ring/ring-core "1.3.0"]
                 [ring/ring-jetty-adapter "1.3.0"]
                 [compojure "1.1.8"]
                 [cheshire "5.3.1"]]
 :dev-dependencies [[ring/ring-devel "1.3.0"]]
 :plugins [[lein-ring "0.8.11"]]
 :main hoge-app.core
 :ring {:handler hoge-app.core/app}
```
<dl>
<dt>ring</dt>
<dd>RubyのRackに相当するWebアプリケーションライブラリセット</dd>
<dt>ring-jetty-adapter</dt>
<dd>leinコマンドからringサーバーを起動できる</dd>
<dt>compojure</dt>
<dd>ring対応のルーティングライブラリ</dd>
<dt>cheshire</dt>
<dd>JSONエンコーダー</dd>
</dl>

### ②ルーティング、JSON変換、エントリポイントなど
``` clj:core.clj
(ns hoge-app.core
  (:use [compojure.core]
        [ring.util.response]
        [ring.middleware.content-type]
        [ring.middleware.params])
  (:require [ring.adapter.jetty :as jetty]
            [cheshire.core :as json]
            [compojure.route :as route]
            [compojure.handler :as handler]
            [hoge-app.hogehoge :as hogehoge]))

(defn json-response [data & [status]]
  {:status (or status 200)
   :headers {"Content-Type" "application/json"}
   :body (json/generate-string data)})

(defroutes api-routes
  (GET "/demo/d3/hogehoge" []
       (json-response (hogehoge/return-map))) ;---①
  (route/resources "/") ;---②
  (route/files "/demo/" {:root "demo"}) ;---③
  (route/not-found "Page not found"))

(defn wrap-dir-index
  [handler]
  (fn [req]
    (handler (update-in req [:uri] #(if (= "/" %) "/index.html" %)))))

(def app (-> api-routes
             wrap-params
             wrap-content-type
             wrap-dir-index
             handler/site))

(defn -main
  [& args]
  (let [port (or (first *command-line-args*) 8080)]
    (jetty/run-jetty app {:port port})))
```
<dl>
<dt>json-response</dt>
<dd>clojureのマップを受け取って、JSON形式のレスポンスを返す関数<br>cheshireのgenerate-stringメソッドで、マップをJSONに変換する
<dt>api-routes</dt>
<dd>ルーティングの設定。<br>①はJSONを得るリクエストの設定。hogehogeクラスのreturn-mapメソッドを"/demo/d3/hogehoge"にルーティング（メソッドの戻り値は上記のjson-responseでJSON化）。<br>②は静的リソースのルート。<br>③で/demo/以下のファイルアクセスを可能にする</dd>
<dt>wrap-dir-index</dt>
<dd>"/"でindex.htmlを表示できるようにする（今回の主題とはあまり関係ない…）</dd>
<dt>app</dt>
<dd>threadマクロで上記関数群をまとめる</dd>
<dt>-main</dt>
<dd>エントリポイント</dd>
</dl>

### ③マップを返すhogehogeクラス
```clj:hogehoge.clj
(ns hoge-app.hogehoge)

(def sample-data [["hoge" [["1/1/1" 10 ]
                           ["2/2/2" 20 ]]
                  ["piyo" [["3/3/3" 30]
                           ["4/4/4" 40 ]]])

(defn make-map-seq [data]
  (map #(array-map :age (get % 0) :power (get % 1)) data))

(defn merge-to-map [data]
  (apply merge (map #(array-map (keyword (get % 0)) (make-map-seq (get % 1))) data)))

(def hogehoge-map (merge-to-map sample-data))

(defn return-map []
  hogehoge-map)
```
**ここはただの例です。本題からちょっと脱線します！！**
最終的にマップを返せば良い。
ここでは、
1. 多次元配列のsample-dataを、
2. make-map-seqでマップ配列に変換して、
3. merge-to-mapで最終的な形式のマップに統合している
（この辺、もっとスマートなコードにしたいので、親切な方アドバイスいただけないでしょうか…）
最終的には、

```clj
{ :hoge [{:age "1/1/1" :power 10} {:age "2/2/2" :power 20}]
  :piyo [{:age "3/3/3" :power 30} {:age "4/4/4" :power 40}]
}
```
といった形のマップになる

### ④静的リソースの設置
②のルーティングに従って、resources/public下にhtml,js,css等を設置する
d3データ表示用のHTMLは、resources/public/demo下に設置する

### ⑤d3にJSONを渡し、グラフを描画する
グラフを描画…と言っても、d3の解説まで行うのは面倒なので、d3によるグラフ描画の詳細はソースをご参照ください。
https://github.com/circularuins/circularuins/blob/master/resources/public/demo/sentou_d3.html
ここでは、肝心のd3にレスポンスのJSONを渡すところだけ解説します。
…といっても超簡単です。
以下のルーティングで設定した、JSONを取得するクエリ、

```clj:core.clj
(defroutes api-routes
  (GET "/demo/d3/hogehoge" []
       (json-response (hogehoge/return-map)))
```
この"/demo/d3/hogehoge"を、d3.jsonメソッドに渡してやるだけです。

```js:hogehoge.html
<head>
  <link rel="stylesheet" type="text/css" href="../assets/css/hogehoge.css">
  <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>

  <script type="text/javascript">

    d3.json("/demo/d3/hogehoge", function(error, data) {
      // 以下でdataを処理
    }

  </script>

</body>
```
渡されたJSONデータはd3側でdataオブジェクトとして利用可能になります。

## おしまい
以上でおしまいです。
わざわざclojureでやる意味あるの？って聞かれたら……ですが、rubyとかと遜色ない手軽さでWebアプリ書けますよ、ってことと、表示の部分はJSに任せちゃえばいいよね、って話でした。

グラフのデモは、
http://warm-fortress-5777.herokuapp.com/demo/show
こちらになります。

**参考**
http://stefan.arentz.ca/clojure-angularjs-recipes-demo.html