TextMate で Clojure のソースを快適にインデント!

Clojure のソースを TextMate で書き綴って、REPL で実行するプロセスはとても快適ではありますが、一点ほど難点があります。それはインデントです。

python でも同様の症状が発生しますが、ruby のようにブロックの終端を表わすブレースなどが行頭に戻って来ないソース形式の場合、TextMate ではインデントが正常に動作しません。

仕方ありませんので、python では、Python Package Index : PythonTidy 1.20 を呼び出して代用しておりましたが、どうも Clojure でも同様の処置を施さねばならないようです。


Google 先生によると、こちらを

(参考) はてなブックマーク - TextMateでのClojureのインデント解決編 - マンジラボ

参考にせよ、との思し召しなのですが、残念ながら、当該サイトは閉鎖、ないしはメンテナンスされていない状態のようです。

ブクマに残されたコメントから推察するに、Emacsclojure-mode を呼び出してコード整形を果たしている様子。その辺から Google 先生にお問い合わせすると....

(参考) All sizes | Tidy up clojure code in TextMate | Flickr - Photo Sharing!

有りましたぁ!なんと、コードの断片を画像で発見してしまいましたよw


この画像を写経することでキレイなインデントを獲得することができました。以下が Command(s) に埋め込むコードです。

#!/usr/bin/env ruby

filename = "/tmp/tmptx_tidyup_code.clj";

File.open(filename, "w") {|f|
  STDIN.each do |line|
    f.print line
  end
}

# path は clojure-mode.el を保存したパスに変更してください
# lisp-indent-offset は好みのインデント幅に変更してください

result = `/usr/bin/emacs -batch \
--eval '(load "/path/clojure-mode.el")' \
#{filename} \
--eval '(setq indent-tabs-mode nil)' \
--eval '(setq lisp-indent-offset 4)' \
--eval '(indent-region (point-min) (point-max) nil)' \
-f save-buffer &> /dev/null`

puts `cat #{filename}`


いやいや。これで閉じ括弧を先頭に持って来るような、無様なことをせずに済みます。

「Clojure と slim3」の夏がやってくる (其の四)

忠実に Junit4 を再現しようとして、いささか躓いてしまいましたが、ヒントは ScalaSlim3 対応にありました。


(参考) Slim3をScalaで動かすためのいろいろ - ゆろよろ日記

package slim3scala.controller.Foo

import org.specs.Specification
import org.specs.runner._
import org.slim3.tester.ControllerTester

object IndexControllerSpec extends org.specs.Specification {

  val tester = new ControllerTester( classOf[IndexController] )

(以下略)

要は、ControllerTester や AppEngineTester さえ生成できれば、Clojure 由来のテスト環境でまったく問題無い、ということになります。
切っ掛けとなった id:yuroyoro さんには本当に感謝です。


そこで、まずは、コントローラーのテストです。

IndexControllerTest.clj

(ns com.example.controller.IndexControllerTest
    (:import
        com.example.controller.IndexController,
        javax.servlet.http.HttpServletResponse,
        org.slim3.tester.ControllerTester)
    (:use clojure.contrib.test-is))


(defn setup-fixtures
    "Docstring for setup-fixtures."
    [test-func]

    (def tester (ControllerTester. (.. (IndexController.) getClass)))
    (.setUp tester)
    (.start tester "/")
    (test-func)
    (.tearDown tester))
(use-fixtures :each setup-fixtures)


(deftest test-sample
    "Docstring for -run."

    (is (= (.. HttpServletResponse SC_OK) (.getStatus (.. tester response)))))


次に、サービスのテストです。

Slim3ServiceTest.clj

(ns com.example.service.Slim3ServiceTest
    (:import
        com.example.service.Slim3Service,
        org.slim3.tester.AppEngineTester)
    (:use clojure.contrib.test-is))


(defn setup-fixtures
    "Docstring for setup-fixtures."
    [test-func]

    (def tester (AppEngineTester.))
    (.setUp tester)
    (test-func)
    (.tearDown tester))
(use-fixtures :each setup-fixtures)


(deftest test-sample
    "Docstring for -run."

    (Slim3Service/newAndPut "abc")
    (is (= 1 (.. (Slim3Service/queryAll) size))))


これを

$ mvn test

で実行してあげると....

(前略)
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
There are no tests to run.

Results :

Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
# ↑ こちらは Java のテスト結果

[INFO] [clojure:test {execution: test-clojure}]

(中略)

Ran 2 tests containing 2 assertions.
0 failures, 0 errors.
# ↑ こちらが Clojure のテスト結果

(以下略)

はい。無事成功となりました。

余談ではありますが、Clojure のテストは俗に言う setUp や tearDown にクロージャーを上手く取り入れていて、大変分かりやすいですね。

さぁ、これで一通り、準備は完了です。

さぁて、ClojureSlim3 で、この夏、何を作ろうかな?

「Clojure と slim3」の夏がやってくる (其の参)

さて、サービスの Clojure 化です。

本当は Pure Clojure で行きたかったところですが、そうすると、せっかくの Hot Reloading を無効にせねばなりません。
そうでなくとも、ゆとり Eclipse 世代w としては手動ビルドさえ面倒な状況ですから、ビルドのたびに開発サーバの再起動なんてやってられません。
なので、止むを得ず Java クラスとして扱うこととしました。

まずは、サービスのコードです。

Slim3Service.clj

(ns com.example.service.Slim3Service
    (:gen-class
        :methods [
                     #^{:static true} [newAndPut [String] void],
                     #^{:static true} [queryAll [] java.util.List]])
    (:import
        com.example.meta.Slim3ModelMeta,
        com.example.model.Slim3Model,
        com.google.appengine.api.datastore.Key,
        java.util.List,
        org.slim3.datastore.Datastore))


(defn -newAndPut
    "Docstring for new-and-put [prop1]."
    [prop1]

    (let [model (Slim3Model.)]
        (.setProp1 model prop1)
        (let [key (Datastore/put model)]
            (.setKey model key))))

(defn -queryAll
    "Docstring for queryAll."
    []

    (.. (Datastore/query (Slim3ModelMeta/get)) asList))


次に、作成したサービスを使用するよう、コントローラーを変更します。

IndexController.clj

(ns com.example.controller.IndexController
    (:gen-class
        :extends org.slim3.controller.Controller
        :exposes {response {:get getResponce, :set setResponce}}
        :exposes-methods {forward forwardS, requestScope requestScopeS})
    (:import
        com.example.meta.Slim3ModelMeta,
        com.example.model.Slim3Model,
        com.example.service.Slim3Service,
        java.util.Date,
        org.slim3.datastore.Datastore))


(defn -run
    "Docstring for -run."
    [this]

    (let [res (.. this getResponce)]
        (.println (.. res getWriter) "Hello, World!")

        (Slim3Service/newAndPut (.. (Date.) toString))

        (doseq [x (Slim3Service/queryAll)]
            (.println (.. res getWriter) (.. x getProp1)))

        (.flushBuffer res)))

Java クラスの扱いの勘所が分かってくると、スイスイ記述できますね。本当に素晴らしいです。

さて、次は、本来であれば先に行なうべきなのですが、TestCase を記述してみたいと思います。

「Clojure と slim3」の夏がやってくる (其の弐)

(其の壱) で

これで、コントローラーの Clojure 化を無事果たすことができました。
次は、サービスの Clojure 化に挑戦してみたいと思います。

と申し上げたのですが、maven が生成する初期プロジェクトのコントローラーとサービスの Clojure 化を目指すことに変更しました。

取りあえず、出来上がった IndexController.clj は下記の通りです。

IndexController.clj

(ns com.example.controller.IndexController
    (:gen-class
        :extends org.slim3.controller.Controller
        :exposes {response {:get getResponce, :set setResponce}}
        :exposes-methods {forward forwardS, requestScope requestScopeS})
    (:import
        com.example.meta.Slim3ModelMeta,
        com.example.model.Slim3Model,
        java.util.Date,
        org.slim3.datastore.Datastore))


(defn -run
    "Docstring for -run."
    [this]

    (let [res (.. this getResponce)]
        (.println (.. res getWriter) "Hello, World!")

        (let [model (Slim3Model.)]
            (.setProp1 model (.. (Date.) toString))
            (Datastore/put model))

        (doseq [x (.. (Datastore/query (Slim3ModelMeta/get)) asList)]
            (.println (.. res getWriter) (.. x getProp1)))

        (.flushBuffer res)))

Java の IndexController 同様、"Hello World!" を出力した後、put した Slim3Model の query 結果から順次 prop1 の内容を出力します。

さて、次は、今度こそ、サービスの Clojure 化に挑戦してみたいと思います。


追伸: しかし、本当に Clojure のソースは美しいですね。もう 、ちょっと Java には戻れませんって。