ラベル Clojure の投稿を表示しています。 すべての投稿を表示
ラベル Clojure の投稿を表示しています。 すべての投稿を表示

2010/03/21

Clojure Leiningen @ Shibuya.lisp テクニカルトーク #5

Clojure の Leiningen で以前挫折をした。いやむしろ SLIME がちゃんと動かなくて嫌になった。

@making さんの LT "Clojure+Leiningenで3分Hadoop" で Clojure の Leiningen がちゃんと動いくことの確信を得たのでリトライする。

目標は

  • Leiningen でプロジェクトを作る。
  • Leiningen で swank server を起動して、SLIMEでインタラクティブ開発。
  • 単体実行 jar の作成。

シェルで次を実行。a はプロジェクト名。

lein new a

./a/project.clj を編集

  • lein swank で swank server を起動するために :dev-dependencies を追加
  • lein uberja で作る単体実行 jar のエントリポイントを指定する :main を追加
(defproject a "1.0.0-SNAPSHOT"
:description "FIXME: write"
:dependencies [[org.clojure/clojure "1.1.0"]
[org.clojure/clojure-contrib "1.1.0"]]
:dev-dependencies [[leiningen/lein-swank "1.1.0"]]
:main a.core)

シェルで次を実行し依存ライブラリの取得後、swank server を起動する。

lein deps
lein swank

Emacs から M-x slime-connect とすると、ちゃんと SLIME 動いた。補完もちゃんと動いた。別途、Emacs + SLIME まわりの設定は必要なので、それは後述する。

./a/src/a/core.clj を編集

  • :gen-class を追加
  • -main を追加
(ns a.core
(:gen-class))

(defn -main [& args]
(. javax.swing.JOptionPane (showMessageDialog nil "Hello World")))

シェルで

lein uberjar
chmod +x ./a-standalone.jar
./a-standalone.jar

以上、目標達成。

後述予定の Emacs + SLIME まわり。

github から swank-clojure と clojure-mode を git clone しておく。 ~/.emacs の設定は SBCL の最新 SLIME とかぶらないように無理をしている。うまく両立する方法はないものだろうか?おまけに Google App Engine を SLIME から起動しようとしたときの残骸も残ってる。

(defun clojure ()
(interactive)
(add-path "~/letter/clojure/lib/clojure-mode")
(add-path "~/letter/clojure/lib/slime")
(add-path "~/letter/clojure/lib/slime/contrib")
(eval-after-load "slime"
'(progn
(slime-setup '(slime-repl
slime-fuzzy
slime-banner))
(setq slime-complete-symbol*-fancy t)
(setq slime-complete-symbol-function 'slime-fuzzy-complete-symbol)
(global-set-key [(control ?\;)] 'slime-selector)
(loop for (key command) in
'(([(control ?c) ?\;] slime-insert-balanced-comments)
([(control ?u) (control ?c) ?\;] slime-remove-balanced-comments)
([(control ?c) ?\;] slime-insert-balanced-comments)
("\C-j" slime-eval-print-last-expression)
("\C-m" newline-and-indent)
("\C-i" slime-indent-and-complete-symbol))
do (define-key slime-mode-map key command))))
(setq swank-clojure-classpath
'("~/letter/java/eclipse-workspace/moeopa-clj/src"
"~/letter/java/eclipse-workspace/moeopa-clj/war/WEB-INF/classes"
"~/letter/clojure/lib/swank-clojure/src"
"~/letter/clojure/lib/clojure/clojure.jar"
"~/letter/clojure/lib/clojure-contrib/clojure-contrib.jar"
"~/letter/clojure/lib/appengine-clj/appengine-clj.jar"
"~/letter/clojure/lib/compojure/compojure.jar"
"~/letter/clojure/lib/compojure/deps/commons-codec-1.3.jar"
"~/letter/clojure/lib/compojure/deps/commons-fileupload-1.2.1.jar"
"~/letter/clojure/lib/compojure/deps/commons-io-1.4.jar"
"~/letter/clojure/lib/compojure/deps/jetty-6.1.15.jar"
"~/letter/clojure/lib/compojure/deps/jetty-util-6.1.15.jar"
"~/letter/clojure/lib/compojure/deps/servlet-api-2.5-20081211.jar"
"~/local/opt/appengine-java-sdk/lib/appengine-tools-api.jar"
"~/local/opt/appengine-java-sdk/lib/shared/appengine-local-runtime-shared.jar"
"~/local/opt/appengine-java-sdk/lib/shared/geronimo-el_1.0_spec-1.0.1.jar"
"~/local/opt/appengine-java-sdk/lib/shared/geronimo-jsp_2.1_spec-1.0.1.jar"
"~/local/opt/appengine-java-sdk/lib/shared/geronimo-servlet_2.5_spec-1.2.jar"
"~/local/opt/appengine-java-sdk/lib/shared/jsp/repackaged-appengine-ant-1.6.5.jar"
"~/local/opt/appengine-java-sdk/lib/shared/jsp/repackaged-appengine-ant-launcher-1.6.5.jar"
"~/local/opt/appengine-java-sdk/lib/shared/jsp/repackaged-appengine-commons-el-1.0.jar"
"~/local/opt/appengine-java-sdk/lib/shared/jsp/repackaged-appengine-commons-logging-1.1.1.jar"
"~/local/opt/appengine-java-sdk/lib/shared/jsp/repackaged-appengine-jasper-compiler-5.0.28.jar"
"~/local/opt/appengine-java-sdk/lib/shared/jsp/repackaged-appengine-jasper-runtime-5.0.28.jar"
"~/local/opt/appengine-java-sdk/lib/impl/appengine-api-labs.jar"
"~/local/opt/appengine-java-sdk/lib/impl/appengine-api-stubs.jar"
"~/local/opt/appengine-java-sdk/lib/impl/appengine-api.jar"
"~/local/opt/appengine-java-sdk/lib/impl/appengine-local-runtime.jar"
"~/local/opt/appengine-java-sdk/lib/impl/agent/appengine-agentruntime.jar"
"~/letter/clojure/lib/programming-clojure/lib/ant.jar"
"~/letter/clojure/lib/programming-clojure/lib/ant-launcher.jar"))
(setq slime-lisp-implementations
`((clojure
("java"
"-classpath"
,(concat ".:classes:"
(mapconcat 'identity
(mapcar 'expand-file-name
swank-clojure-classpath)
":"))
"clojure.main"
"--repl")
:init swank-clojure-init)))
(load
(expand-file-name "~/.emacs.d/elpa/package.el"))
(package-initialize)
(require 'clojure-mode)
(require 'slime-autoloads)
(slime)
)

Leiningen からコマンド一発で Google App Engine の雛形プロジェクトが作成できたらいいのにな。

ということで making さんありがとうございました。

今回の Shibuya.lisp もよかったです。みなさんありがとうございました。

Common Lisp 的にはちょっとさびしかったな。

2009/12/21

Clojure で Google App Engine するためのメモページ

Clojure で GAE(Google App Engine)するためのメモ。

逐次追記する予定。

SLIME

git clone git://github.com/technomancy/swank-clojure.git 現在かなり不安定っぽい。 -> @hchbaw さんに教えてもらって幸せになりました。http://twitter.com/hchbaw/statuses/6895804697

hackers-with-attitude: Interactive Programming with Clojure, Compojure, Google App Engine and Emacs のとおりにすれば、SLIME 上からインタラクティブに開発できる。さいこー!! Lisp たる必須条件だよね。

Compojure

weavejester's compojure at master - GitHub Compojure - Wikibooks, collection of open-content textbooks

メソッドマクロ

  • GET
  • POST
  • PUT
  • DELETE
  • HEADE
  • ANY どれでも ok

URL に対するバインドは : で、取得は :route-params で行う。複数のパラメータの場合は * を使う。ドメイン名でもルーティングできるらしい。

(GET "/posts/:id" ...)
(-> request :route-params :id)

(GET "/foo/*" ...)
(-> request :route-params :*)

静的ファイルをレスポンスするには serve-file を使う。ファイルは public に置く。 404 を返すには page-not-found を使い、public/404.html を用意しておく。

(defroutes main-routes
...
(GET "/*"
(or (serve-file (params :*)) :next))
(ANY "*"
(page-not-found)))

でも Google App Engine ではスタティックファイルが存在するとそちらが優先されること、サーブレットの呼び出しでは MIME タイプを web.xml で指定する必要があることから次のようにしておくのがよいと思う。

(defroutes main-routes
...
(GET "/*"
(or (serve-file "./" (params :*)) :next))
(ANY "*"
(page-not-found "404.html")))
war/
css/
style.css
img/
image.png
404.html

request が持っているもの。

  • params → (:params request)
  • cookies → (:cookies request)
  • session → (:session request)
  • flash → (:flash request)

HTML はベクタで書く。属性はマップ。 id と クラスはタグにくっつけられる。

[:h1#title "こんにちは"]
[:form#main.list
[:div.foo.bar "..."]
[:input {:type "text" :name "baz"}]]

appengine-clj

duelinmarkers's appengine-clj at master - GitHub

DataStore, UserService の簡単なライブラリ。これだけじゃ足りないので、自分で実装を追加するか、Java の API をコールすることになる。

Datastore 低レベル API

Key は他のエンティティの参照としてプロパティに保持可能。

KeyFactory.keyToString と KeyFactory.stringToKey によって Key と Web セーフな String を相互に変換できる。ポストパラメータ等には keyToString した Key を使う。

2009/10/29

Clojure で Google app engine

基本的に Clojure on Google AppEngine - El Humidor のとおりやれば動いた。

ちょっとひっかかったところがいくつか

  • clojure.jar, clojure-contrib.jar は Compojure の ant deps のものではなく、ちゃんとそれぞれの github から持ってきたものを使う。
  • commons-codec-1.3.jar, commons-fileupload-1.2.1.jar, commons-io-1.4.jar も war/WEB-INF/lib にコピー(or ln -s)する。
  • (GET "/" だと何故がうまく動かなかったので (GET "/foo" とかした。

2009/09/07

Clojure-contrib のインストールとビルド

http://richhickey.github.com/clojure-contrib/

Clojure 1.0 を使う場合は clojure-1.0-compatible ブランチにしなければいけない。ブランチのかえかたはこれでいいのか? git 難しいな。

git clone git://github.com/richhickey/clojure-contrib.git
cd clojure-contrib
git checkout origin/clojure-1.0-compatible
git checkout -b clojure-1.0-compatible
ant -Dclojure.jar=/usr/share/java/clojure.jar

~/.emacs で swank-clojure-extra-classpaths を指定する。

(setq swank-clojure-extra-classpaths
(list "~/letter/clojure/lib/clojure-contrib/clojure-contrib.jar"))

2009/09/05

Clojure のセットアップ

Clojure をセットアップする。 Debian にはパッケージがあったので apt-get する。 Emacs + SLIME のために git clone を2つ。

sudo apt-get install clojure
cd ~/letter/clojure/lib
git clone git://github.com/jochu/clojure-mode.git
git clone git://github.com/jochu/swank-clojure.git

~/.emacs に clojure-mode と swank-clojure の設定を追加。

(setq slime-net-coding-system 'utf-8-unix)
;;;; Clojure
;; clojure-mode
(add-path "~/letter/clojure/lib/clojure-mode")
(require 'clojure-mode)
;; swank-clojure
(add-path "~/letter/clojure/lib/swank-clojure/src/emacs")
(setq swank-clojure-jar-path "~/letter/clojure/git/clojure/clojure.jar")
(require 'swank-clojure-autoload)
(setq swank-clojure-extra-classpaths
(list "~/letter/clojure/git/clojure-contrib/clojure-contrib.jar"))

SLIME 自体の設定は省略、と思ったけどついでにのせておこう。

~/.emacs

;;;;;SLIME
(setq common-lisp-hyperspec-root "file:/usr/share/doc/hyperspec/")
(add-path "~/letter/lisp/clbuild/source/slime")
(add-path "~/letter/lisp/clbuild/source/slime/contrib")
;;(setq slime-communication-style :fd-handler)
;;(setq slime-communication-style :spawn)
;;(setq slime-communication-style :sigio)
(setq slime-net-coding-system 'utf-8-unix)
(setq slime-lisp-implementations
`((sbcl ("sbcl") :coding-system utf-8-unix)
(ccl ("/home/ancient/local/opt/ccl/lx86cl64")
:coding-system utf-8-unix)
;; (ccl ("/home/ancient/letter/lisp/clbuild/clbuild"
;; "--implementation" "ccl" "lisp")
;; :coding-system utf-8-unix)
(deb-sbcl ("/usr/bin/sbcl") :coding-system utf-8-unix)
(clisp ("/home/ancient/letter/lisp/clbuild/clbuild"
"--implementation" "clisp" "lisp")
:coding-system utf-8-unix)
(abcl ("/home/ancient/letter/lisp/abcl/abcl")
:coding-system utf-8-unix)
(acl ("/home/ancient/local/opt/acl81_express/alisp")
:coding-system utf-8-unix)
(cmucl ("lisp"))))
(require 'slime-autoloads)
;;(add-hook 'lisp-mode-hook
;; (lambda ()
;; (cond ((not (featurep 'slime))
;; (require 'slime)
;; (normal-mode)))))
(setq slime-truncate-lines nil)
(setq slime-enable-evaluate-in-emacs t)
(defun my-slime-complete-form ()
(interactive)
(condition-case nil
(slime-complete-form)
(error
(save-excursion
(insert
(multiple-value-bind (ops arg-indices points)
(slime-enclosing-form-specs)
(run-hook-with-args 'slime-autodoc-hook ops arg-indices points)
(multiple-value-bind (cache-key retrieve-form)
(slime-compute-autodoc-rpc-form ops arg-indices points)
(slime-eval retrieve-form))))))))
(add-hook
'slime-mode-hook
(lambda ()
(global-set-key [(control ?\;)] 'slime-selector)
(define-key slime-mode-map "\C-c\C-s" 'my-slime-complete-form)
(define-key slime-repl-mode-map "\C-c\C-s" 'my-slime-complete-form)
(loop for (key command) in
'(([(control ?c) ?\;] slime-insert-balanced-comments)
([(control ?u) (control ?c) ?\;] slime-remove-balanced-comments)
([(control ?c) ?\;] slime-insert-balanced-comments)
("\C-m" newline-and-indent)
("\C-i" slime-indent-and-complete-symbol))
do (define-key slime-mode-map key command))))
(add-to-list 'auto-mode-alist '("\\.asd$" . common-lisp-mode))
(eval-after-load "slime"
'(progn
(slime-setup '(slime-repl
slime-asdf
slime-fancy
slime-indentation
slime-references
slime-tramp
slime-banner))
(setq slime-complete-symbol*-fancy t)
(setq slime-complete-symbol-function 'slime-fuzzy-complete-symbol)
(setq lisp-indent-function 'cl-indent:function)
;; g000001 さん作
(progn
(defun gcode-lookup ()
"カーソル位置のシンボルをGoogle Codeで検索(lisp決め打ち)"
(interactive)
(browse-url
(format "http://www.google.com/codesearch?q=%s+lang:%s+file:\\.%s$&hl=ja&num=20"
(thing-at-point 'symbol) "lisp" "lisp")))
(define-key slime-mode-map
[(control ?c) (control ?d) ?g] 'gcode-lookup))))

C-- M-x slime clojure