LuminusでClojureScriptを使う方法

以下の公式ドキュメントを和訳しました。
[Clojure Luminus - ClojureScript](http://www.luminusweb.net/docs/clojurescript.md)


ClojureScriptはクライアントサイドのアプリケーションロジックを記述するための、JavaScripの代わりとなる素晴らしい言語だ。

ClojureScriptを使うことで以下のような利点が得られる。

* クライアントとサーバーの両方で同じ言語を使うことができる。
* フロントエンドとバックエンドで共通するコードを共有できる。
* より簡潔で構造化された言語を使用できる。
* Leiningenによる依存関係の管理
* 不変なデータ構造
* 強力な標準ライブラリ

# ClojureScriptサポートの追加
ClojureScriptのサポートを追加する上で最も簡単な方法は新しいプロジェクトを作成する際に+cljsフラグを使用することだ。
しかしながら、すでに存在するプロジェクトに追加することも同じくらいにきわめて簡単だ。
`project.clj`ファイルに以下のセクションを追加するだけだ。

```clj
:plugins [... [lein-cljsbuild "1.0.3"]]  
:hooks [... leiningen.cljsbuild]

:cljsbuild
{:builds
     [{:id "dev"
       :source-paths ["src-cljs"]
       :compiler
        {:optimizations :none
         :output-to "resources/public/js/app.js"
         :output-dir "resources/public/js/"
         :pretty-print true
         :source-map true}}
      {:id "release"
       :source-paths ["src-cljs"]
       :compiler
        {:output-to "resources/public/js/app.js"
         :optimizations :advanced
         :pretty-print false
         :output-wrapper false
         :closure-warnings {:non-standard-jsdoc :off}}}]}
```

上記の設定は[lein-cjsbuild](https://github.com/emezeske/lein-cljsbuild)プラグインを追加し、フックするようにビルドの設定をする。

すべてのClojureScriptの名前空間はプロジェクトのルートディレクトリ以下の`src-cljs`ディレクトリ内に配置する必要がある。
ClojureScriptのファイルは拡張子が.cljsになっている必要がある。もし、拡張子が.cljだったとしてもコンパイルされるが、jsの名前空間にアクセスすることできなくなる。

# ライブラリを使う
ClojureScriptを使う利点の一つはクライアントサイドのライブラリをLeiningenを使用して管理することができることである。ClojureScriptのライブラリは他のライブラリと同じように`project.clj`の依存関係に含まれる。

# コンパイラを走らせる
ClojureScriptのアプリケーションを開発するもっとも簡単な方法はコンパイラをautoモードで動かすことである。
この方法では名前空間内で変更が加えられると自動的に再コンパイルされ、Webページに対して即座に反映される。
コンパイラをこのモードで起動するには単純に以下コマンドを実行する。

```bash
lein cljsbuild auto dev
```

プロダクション用にアプリケーションをコンパイルするにはonceオプションを使用する。これはプロジェクトに含まれるすべてのスクリプトをコンパイルして単一のJSファイルを生成する。

```bash
lein cljsbuild once release
```

# 高度なコンパイルとエクスポート
高度なコンパイルをする際、変数名はよりコードを短くするために、コンパイラによって書き換えられます。
JavaScriptの関数の名前を保護したい場合があるかもしれない。これは`^:export`アノテーションを使用することで可能となる。

```clj
(ns main)

(defn ^:export init []
  (js/alert "hello world"))
```

こうして他のページからこの関数を他のものと同じように呼び出すことができる。

```js
<script>
main.init();
</script>
```

もし、何かしらのJSのライブラリを使用する場合、使用したいすべての関数名を保護しなくてはいけない。例えば、[AlbumColors](https://github.com/chengyin/albumcolors)というライブラリを使用したい場合、以下のように書くことができる。

```clj
(defn ^:export init []  
  (.getColors (js/AlbumColors. "/img/foo.jpg") 
    (fn [[background]]
     (.log js/console background))))
```

しかしながら、スクリプトが:advancedフラグを使用してコンパイルされた場合、AlbumColorsとgetColorsは書き換えられてしまうだろう。
これらを保護するためにはこれらに対する参照を行うJSファイルを作る必要がある。

```js
var AlbumColors = {};
AlbumColors.getColors = function() {};
```

もし、上記のコードをresourcesディレクトリ内の`externs.js`というファイルに記述した場合、cljsbuildセクションで以下のようにして参照することができるだろう。

```clj
{:id "release"
 :source-paths ["src-cljs"]
 :compiler
 {:output-to "resources/public/js/app.js"
  :optimizations :advanced
  :pretty-print false
  :output-wrapper false
  ;;specify the externs file to protect function names
  :externs ["resources/externs.js"]
  :closure-warnings {:non-standard-jsdoc :off}}}
```

# JavaScriptとの相互作用
すべてのJavaScriptのグローバル関数とグローバル変数は`js`名前空間を通して使用可能である。

## メソッドの呼び出し

```clj
(.method object params)

(.log js/console "hello world!")
```

## プロパティに対するアクセス

```clj
(.-property object)

(.-style div)
```

## プロパティの設定

```clj
(set! (.-property object))

(set! (.-color (.-style div) "#234567"))
```

ClojureScriptにおけるJavaScriptの標準的な操作の例は[Himera documentation](http://himera.herokuapp.com/synonym.html)を見ることで分かる。

# Reagentを使う
[Reagent](http://holmsand.github.io/reagent/)はLuminusによってClojureScriptアプリケーションを構築する上で推奨されるアプローチである。
Luminusにおいて+cljsプロファイルを使用することは、アプリケーションをそのように設定された状態で生成することになる。
ReagentはDOM表記のための[Hiccup](https://github.com/weavejester/hiccup)スタイルの構文を使用してUIコンポーネントを定義するための標準的な方法を提供する。
それぞれのUIコンポーネントは特定のDOM要素を表すデータ構造になっている。
UIのDOMを中心としたビューを採用することで、ReagentはUIコンポーネントの構成をシンプルかつ直感的に書くことができるようにする。

# クライアントサイドのルーティング
[Secretary](https://github.com/gf3/secretary)はおすすめでできるClojureScriptのルーティングライブラリである。これはルーティング定義についてCompojureに影響を受けた構文を使用している。 このライブラリを使用するためには、プロジェクトに依存関係を追加する。+cljsテンプレートを使用してプロジェクトを作成した場合は、それはデフォルトで含まれる。

```clj
[secretary "1.2.0"]
```

次はライブラリを使うためにClojureScriptの名前空間で参照できるようにする。

```clj
(ns app
  (:require [secretary.core :as secretary
             :include-macros true
             :refer [defroute]]
            [goog.events :as events])
  (:import goog.History
           goog.history.EventType))
```

ライブラリがインポートできたら、トリガされたら指定されたDOM要素の内容を設定するルーティング定義をつくることができる。

```clj
(defn home []
  [:div [:h1 "Home"]])

(defn info []
  [:div [:h1 "About this app"]])

(defn not-found []
  [:div [:h1 "404: Page doesn't exist"]])

(defn page [page-component]
  (reagent/render-component
    [page-component]
    (.getElementById js/document "appContainer")))
    
(defroute home-path "/" [] (page home))
(defroute home-path "/about" [] (page info))
(defroute "*" [] (page not-found))
```

より詳しい内容は[公式ドキュメント](https://github.com/gf3/secretary)を参照しなさい。

# DOMを直接操作する
### 注意
Reagentは、仮想DOMを使用して必要に応じてコンポーネントをレンダリングするので、DOMを直接操作しないことを強く推奨する。Reagentコンポーネントの外部のDOM要素を更新した場合、予測できない動作が発生することがある。

DOM要素に対してアクセスし、変更することを可能にするライブラリはいくつかある。具体的には[Domina](https://github.com/levand/domina)や[Dommy](https://github.com/Prismatic/dommy)といったものを見てみるかもしれない。DominaはDOM要素を選択して操作するだけでなく、イベントハンドリングも行う軽量ライブラリである。DommyはHiccupに似たテンプレートライブラリである。

# Ajax
LuminusはAjax操作をハンドリングするために[cljs-ajax](https://github.com/yogthos/cljs-ajax)を使用する。このライブラリはajax-request、GET、POST関数を使用して簡単にAjaxリクエストをサーバーに送信するための方法を提供する。

## ajax-request
ajax-requestは以下のようなパラメータを受け入れる基本的なリクエスト関数である。

* uri - リクエストのURI
* method - PUT、DELETEなどのようなHTTPリクエストの種類を表す文字列
* format - レスポンスのフォーマットを指定するキーワードで`:json`、`:edn`のどちらかを指定できる。デフォルトは`:edn`。
* handler - 成功時のハンドラ。レスポンスを単一の引数で受け取る関数。
* error-handler - エラー時のハンドラ。エラーとキーを表すマップを受け取る関数。
* params - サーバーに送信するパラメータのマップ。

## GET/POSTヘルパー
GETとPOSTのヘルパーはURIに続いてオプションのマップを受け取る。

* `:handler` - 成功時のハンドラ。デシリアライズされた単一のパラメータを受け取る関数。
* `:error-handler` - エラー時のハンドラ。`:status`、 `:status-text`のキーを含むマップを受け取る関数。
* `:format` - レスポンスのフォーマットを指定するキーワードで`:json`、`:edn`のどちらかを指定できる。デフォルトは`:edn`。
* `:params` - リクエストとともに送信されるパラメータのマップ。

```clj
(ns foo
  (:require [ajax.core :refer [GET POST]]))

(defn handler [response]
  (.log js/console (str response)))

(defn error-handler [{:keys [status status-text]}]
  (.log js/console 
    (str "something bad happened: " status " " status-text)))

(GET "/hello")

(GET "/hello" {:handler handler})

(POST "/hello")

(POST "/send-message" 
        {:params {:message "Hello World"
                  :user    "Bob"}
         :handler handler
         :error-handler error-handler})
```