2011-06-17 (Fri)
TextMate で Clojure のソースを快適にインデント!
Clojure のソースを TextMate で書き綴って、REPL で実行するプロセスはとても快適ではありますが、一点ほど難点があります。それはインデントです。
python でも同様の症状が発生しますが、ruby のようにブロックの終端を表わすブレースなどが行頭に戻って来ないソース形式の場合、TextMate ではインデントが正常に動作しません。
仕方ありませんので、python では、Python Package Index : PythonTidy 1.20 を呼び出して代用しておりましたが、どうも Clojure でも同様の処置を施さねばならないようです。
(参考) はてなブックマーク - TextMateでのClojureのインデント解決編 - マンジラボ
参考にせよ、との思し召しなのですが、残念ながら、当該サイトは閉鎖、ないしはメンテナンスされていない状態のようです。
ブクマに残されたコメントから推察するに、Emacs の clojure-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}`
いやいや。これで閉じ括弧を先頭に持って来るような、無様なことをせずに済みます。
2011-06-16 (Thu)
「Clojure と slim3」の夏がやってくる (其の四)
忠実に Junit4 を再現しようとして、いささか躓いてしまいましたが、ヒントは Scala の Slim3 対応にありました。
(参考) 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 にクロージャーを上手く取り入れていて、大変分かりやすいですね。
さぁ、これで一通り、準備は完了です。
さぁて、Clojure と Slim3 で、この夏、何を作ろうかな?
2011-06-14 (Tue)
「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 には戻れませんって。
「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 を記述してみたいと思います。
2011-06-12 (Sun)
「Clojure と slim3」の夏がやってくる (其の壱)
最近、ようやく AppEngine にというか、KVS の扱いに慣れてきたように思います。
Rails や Django では常に DRY であること要求されますが、その無言の圧力に従っていれば、自然とアプリ全体が見通しの良い構造になっていきます。
ある意味、考え無しでも、そこそこのモノができてしまいますので、実力が上がったように思いがちですが、実際はフレームワークの力によるもの、ということが往々にしてあります。
反面、GAE では製作者の技量が試されるような気がします。
ここは、規定時間内に処理が終了さえすれば、何も問われない世界です。だからと言って、何も考えずにアプローチしていては、この GAE というコースは攻略できません。
DRY ばかりを推し進めてもウマく行きません。さりとて、正規化を外しまくると管理が大変になってしまいます。とにかくバランスがとても大切で、この感覚を定着させるまでが一苦労でした。
「各種 API へのアクセスを可能な限り減らして、インスタンス内で高速にデータ処理する」というサイクルを、製作するアプリの性質ごとに最適化していくプロセスは、ちょっとしたパズルのようで、個人的には一番楽しい時間でもあります (ちなみに、一番キライなのはページ作りw)
ただ、先日発表になった新料金プランによって敷居が更に上がったような気がします。これからは、もっと妥協無く上記サイクルを徹底しないと利用料金を低く抑えられません (面倒になってしまって、手綱を緩めてしまうこと多々<涙>)
Rails のような FW とは異なり、AppEngine では、データとコードが相当なレベルで分離されていません。KVS からリスト構造を取得して、そのリストがコードに変化してユーザに届くような感じで、この感覚は正に Lisp であります。
そういう意味で、リストが扱いづらい Java への不満が募り始めたところでありました (ちなみに、Java の方がテスト環境の構築が容易であることを考慮すると、pytyon という選択肢は残念ながら有り得ません)
そこで、JVM 上の Lisp と言えば Clojure。Slim3 を Clojure から操れないか、試してみました。
まずは、プロジェクトの作成から。Jenkins から扱いやすいよう maven で生成します。
$ mvn archetype:generate -DarchetypeCatalog=http://slim3.googlecode.com/svn/trunk/repository (中略) Define value for property 'groupId': : com.example Define value for property 'artifactId': : CljTest (以下略)
次に、pom.xml を修正して Clojure 関連ツールの導入準備をします。
(参考) Clojureでの開発環境 - in a Stream
次に、プロジェクトを maven で Eclipse 化して Eclipse へインポートします。
$ mvn eclipse:eclipse
これで、Eclipse で編集できるようになりました。試してみたところ、Hot Reloading は有効のようです。
ただ、Eclipse の自動ビルドではコンパイルしてくれないので、手動でコンパイルする必要があります。
$ mvn compile (含む Java ファイル) or $ mvn clojure:compile (Clojure ファイルのみ)
また、せっかくコンパイルした Clojure 由来のクラスファイルを消されてしまいますので、Eclipse の「Project メニュ → Build Automatically」のチェックを外しておきます。
さぁ、これで準備完了です。
手始めに maven が生成した IndexController を置き換えてみたいと思います。
まずは、src/main/java/com/example/controller/IndexController.java を削除します。
次に、src/main/clojure/com/example/controller/IndexController.clj と war/index.jsp を作成します。
IndexController.clj
(ns com.example.controller.IndexController (:gen-class :extends org.slim3.controller.Controller :exposes-methods {forward forwardSuper, requestScope requestScopeSuper})) (defn -run "Docstring for -run." [this] (. this requestScopeSuper "msg", "OK?") (. this forwardSuper "index.jsp")) ;; 余談ではありますが、Java のソースと比べると圧到的に美しいですねぇ (主観 100%)
index.jsp
<!DOCTYPE HTML> <html> <body> <h1>Hello, World!! ${msg}</h1> </body> </html>
最後に、Eclipse の「Run メニュ → Run As → Web Application」を実行した後、
$ mvn clojure:compile
を実行して http://localhost:8888/ にアクセスしてみましょう。
Hello, World!! OK?
と表示されれば、成功です。
これで、コントローラーの Clojure 化を無事果たすことができました。
次は、サービスの Clojure 化に挑戦してみたいと思います。
2011-03-08 (Tue)
生産性が求められる「今」だからこそ、Eclipse を使おう!
eclipse | |
![]()
私と Eclipse の不幸な出会い
今から 7年ほど前まで遡りますが、そのころの私は Java ベースの WebObjects で開発を行なっておりました。開発環境は当然ながら、Xcode。
しかし、私のスキルの問題も多分にあったと思うのですが、なかなか効率が上がらない WebObjects のスタイルに嫌気が差していた私は、そこから離れて PHP に鞍替えすることにしました。
問題は開発環境です。IDE に慣れていたので、PHP でもやはり同様なモノが欲しいと思っていたところ、遭遇したのが TruStudio。これが形を変えた Eclipse との出会いでした。
しかし...
Carbon 版 Eclipse は遅かった。とてつもなく遅かった
素の Eclipse に Plugin として TruStudio をインストールしても状況は改善されず、必然的に Emacs から Textmate へとテキストエディタを中心に開発環境は変遷し、Eclipse のコトは記憶の彼方に忘れ去ってしまいました。
再会もやはり不幸だった
3年ほど前に愛機を PowerBook G4 から ThinkPad (T23 → R40e → X200 → T500) に変えました。
Snow Leopard の発表を機に OSX に幻滅した私は、以前から憧れを感じていた「タイルウインドウマネージャを駆使する Linux」に移行したのです。ディストリビューションは、もちろん(笑) Gentoo/Linux。
そんな中、GAE/J のデビューもあって Eclipse と二度目の邂逅を果たします。さすがに、GTK+ ベースであれば、Carbon 版のような不幸は起きないだろう、と高を括っておりました。確かに最初は幸福でした。
しかし...
Gentoo 使いとしての私のスキルの低さも影響したと思うのですが、「何とまぁ。こうまで相性が良くないのか」と苦笑いする他ありませんでした。
三度目の出会いや如何に?
2011年も明けた早々、2年ほど前に書かれた下記記事を発見しました。
Eclipse をまともに使いこなしたコトが無い私から見ると、恥ずかしながら、とても魅力的な内容でした。
丁度、MacBookAir Late2010 の魅力に勝てず、Gentoo/Linux からの移行を終えたばかりのころでしたが、Cocoa 版 Eclipse が存在していることを思い出した私は、チュートリアルがてら上記記事を試してみたのです。
すると...
何ということでしょう!まるで、テキストエディタのごとく軽快に動作するではありませんか!
上記記事の内容もさることながら、Cocoa 版 Eclipse 素晴らしさに感銘を受けました。以来、常用ツールとして、とても重宝しています。
静的な型付け言語こそが生産性向上のカギ?
私の少ない経験ではありますが、PHP ⇒ Ruby ⇒ Python と、LL言語を変遷してきた中で感じたのは「LL言語は、世間が言うほど簡単ではない」ということでしょうか。特に オブジェクト指向の皮を被った Lisp であるところの Ruby は、私にとっては難しい部類の言語でした。
確かに、LL言語の場合は、学習開始前までの敷居が低いことは事実だと思います。しかし、いつまでも初心者ではいられません。チュートリアルとは比べものにならないほど規模が大きくなる開発で活用していくためには、言語仕様の学習もさることながら、言語実装以外の周辺環境が重要になってきます。
1. デバッガ
まず、一番悩まされるのは変数の扱いでしょうか。LL言語であっても、デバッグの際は変数をトレースすることになります。しかし、その割に良いデバッガには、なかなかに巡り合いませんね。
変数の中身が見えてくると実装に内在する問題がググっと浮き上がってくるというのは、プログラミングに共通の出来事でしょう。Django のバックトレース画面には随分と助けられましたが、ああいう機能を標準で備えている方が例外です。
余談ですが、Erlang の変数には衝撃を覚えましたが、今はあのスタイルが一番良いのではないか、と思っています。
2. 補完機能
最近の LL言語であれば、ライブラリについてはまったく問題無いでしょう。むしろ重量級が多く、必要な API を覚えるだけでも一苦労です。
そこで、補完機能を備えた支援システムがクローズアップされることになるのですが、LL言語ではこの時点で一定の限界に到達するように思います。
インストール方法が複雑かつ、それでいて動作も完全に保証されている訳ではありませんので、時間を掛けてセットアップした割に動かなかった、ということは日常茶飯事です。趣味ならばともかく、仕事で使う場合は「えっ...」と絶句する瞬間ですね (苦笑
わずか二つの事例だけで優劣を論じるのは少し無理があるようにも思われますが、Eclipse などの導入しやすい IDE 文化が中心にある java には一日の長があるのでしょう。良くも悪くも、エンタープライズ市場で鍛えられてきた環境は伊達ではないのでしょう。
静的言語が一方的に効率が悪いとされた時代は過ぎたのではないか
最近は、空気を読んでくれる Eclipse と相まって、java を見直す日々であります。
いよいよ言語仕様も古くなってきて、「いつまで使えるか? Oracle で大丈夫なのか?」などなど、その将来に多くの人が疑問符を持つ状況になっているようにも思いますが、さりとて、その代替となると、これまでの資産の継承も含めて完全にリプレース可能なキラー言語に淘汰されるのは、もうちょっと先でしょう。
個人的には、Scala をもうちょっとスリムにした、Eclipse 上で安定したプラグインが供給された、静的な型付けの関数型言語が登場しれくれれば、一番嬉しいです。
そんな夢のような日まで、java とお付き合いする日々が、どうやら続きそうです。
