ClojureのSOAPライブラリ
Clojure用のSOAPライブラリを公開。
https://bitbucket.org/taka2ru/clj-soap
準備
Leiningenのproject.cljにこう書くだけで利用可。
project.clj
(defproject someproj "0.0.1-SNAPSHOT" :depenencies [[org.clojure/clojure "1.2.1"] [org.clojure/clojure-contrib "1.2.0"] [clj-soap "0.1.0"]] :aot [someproj.core]) ; src/someproj/core.cljでサービスを定義する場合。
$ lein deps
サーバを書く
サーバを書くには、defserviceを使う。
(ns someproj.core (:require [clj-soap.core :as soap])) (soap/defservice jp.hoge.MyApp ^Integer (multiple [^Integer x ^Integer y] (* x y))) (defn -main [] (soap/serve "jp.hoge.MyApp")) ; 複数クラスがある場合は、並べて書けばいい。
※project.cljでAOTコンパイルを指定するのを忘れずに
$ lein compile $ lein repl user=> (use 'someproj.core :reload-all) user=> (-main)
これで、 http://localhost:6060/axis2/services/ にアクセスしてみると、WSDLが見れる。
クライアント
client-fn にWSDLのURLを渡してやると、関数が帰ってくる。
関数の第1引数にメソッド名をキーワード指定し、以降に引数を並べてやれば呼び出しができる。
(let [cl (client-fn "http://localhost:6060/axis2/services/MyApp?wsdl")] (cl :multiple 10 20))
vimclojureとcakeのインストール
vimclojureとcakeで簡単にslime/swank的なことができる。
Ubuntu 11.04での設定を書いておく。
まあ、ここにも載ってるわけですが・・・
http://ubuntuforums.org/showthread.php?t=1746137
初期設定
まずは必要なパッケージをインストール
$ sudo apt-get install vim openjdk-6-jdk rubygems $ sudo gem install cake
Cakeを起動できるようにbashにエイリアスを設定
パスを通してもいいんだけど、一つだけなのでエイリアス。
$ echo "alias cake='/var/lib/gems/1.8/bin/cake'" >> ~/.bashrc $ exec bash
Vimclojure用のNailgunクライアントをインストール
$ mkdir -p ~/src $ cd ~/src $ wget http://kotka.de/projects/vimclojure/vimclojure-nailgun-client-2.2.0.zip $ unzip vimclojure-nailgun-client-2.2.0.zip $ cd ~/src/vimclojure-nailgun-client $ make $ sudo cp ng /usr/local/bin/
Vimclojureをインストール
$ cd ~/src $ wget -O vimclojure-2.2.0.zip http://www.vim.org/scripts/download_script.php?src_id=13986 $ mkdir -p ~/src/vimclojure-2.2.0 $ cd ~/src/vimclojure-2.2.0 $ mkdir -p ~/.vim $ cp -a autoload doc ftdetect ftplugin indent plugin syntax ~/.vim/
vimのファイルを編集
~/.vimrcを編集
syntax on filetype indent plugin on let g:vimclojure#WantNailgun = 1 let g:vimclojure#NailgunClient = "ng" let g:vimclojure#HighlightBuiltins = 1 let g:vimclojure#ParenRainbow = 1 let g:vimclojure#DynamicHighlighting = 1
プロジェクトごとの作業
Cakeプロジェクトを作成
$ mkdir -p ~/work $ cd ~/work $ cake new hellocake $ cd hellocake
VimclojureのNailgunサーバをプロジェクトに導入
project.cljを編集
(defproject hellocake "0.0.1-SNAPSHOT" :description "TODO: add summary of your project" :dependencies [[org.clojure/clojure "1.2.1"]] :dev-dependencies [[vimclojure/server "2.2.0"]]) ; ←これを追加
依存パッケージの取得
$ cake deps
編集時
Nailgunサーバの起動
一度起動すればプロセスが常駐します。
$ cake ng NGServer started on 127.0.0.1, port 2113. $
Vimclojureコマンド一覧
マニュアル丸写しですが。。。
通常のモードで下記を実行します。
評価
- \et (EvalToplevel)
- カーソル位置のトップレベル式を評価
- \ef (EvalFile)
- ファイル全体を評価
- \eb (EvalBlock)
- ビジュアルモードで選んだブロックを評価
- \el (EvalLine)
- 現在の行を評価
- \ep (EvalParagraph)
- 現在のパラグラフ
ロード
- \rf (RequireFile)
- ファイルをrequire (:reload)
- \rF (RequireFileAll)
- 依存先を含めてファイルをrequire (:reload-all)
- \rt (RunTests)
- ファイルをrequireし、そのファイルをclojure.contrib.test-isを使ってテスト
マクロ展開
- \me (MacroExpand)
- カーソル位置を含むマクロ式を展開
- \m1 (MacroExpand1)
- カーソル位置を含むマクロ式を1段だけ展開
ドキュメント
ソース調査
REPL起動
- \sr (StartRepl)
- REPLを起動する
- \sR (StartLocalRepl)
- 現在のバッファのnamespaceでREPLを起動する
Clozure CL + HunchentootのPOST時の文字化け対策
Clozure CL(Linux amd64)上でHunchentootを使用したときに、少しハマったので、メモしておく。
現象
POSTのパラメータに日本語を含めると、日本語部分だけが丸ごと消えてしまう。
原因
URLデコード処理のせいかもしれないと思って調べたら、やっぱりそうだった。
Clozure CL
? (hunchentoot::url-encode "あ" :utf-8) "%C3%A3%C2%81%C2%82" ; あれ?UTF-8じゃないぞ。 ? (hunchentoot::form-url-encoded-list-to-alist '("abc=%C3%A3%C2%81%C2%82") :utf-8) (("abc" . "あ")) ? (hunchentoot::form-url-encoded-list-to-alist '("abc=%E3%81%82") :utf-8) (("abc" . "")) ; 正しいUTF-8のコードはうまくデコードできない模様。 ? (hunchentoot::form-url-encoded-list-to-alist '("abc=%E3%81%82") :latin-1) (("abc" . "あ")) ; external-formatをlatin-1にするとなぜかうまくいった。
SBCLだと正しく変換される
* (hunchentoot::url-encode "あ" :utf-8) "%E3%81%82" * (hunchentoot::form-url-encoded-list-to-alist '("abc=%E3%81%82") :utf-8) (("abc" . "あ"))
対処
ハンドラの先頭で、external-format=latin-1でリクエストを再計算してやると、うまく取れる模様。
(defun some-handler () #+ccl (progn (hunchentoot:no-cache) (hunchentoot:recompute-request-parameters :external-format :latin-1)) ... ;以下、普通のハンドラの処理。 )
Common LispでXMPPサーバコンポーネントを書くライブラリを作ってみた
https://bitbucket.org/taka2ru/cl-xmpp-ext/src
XMPPというのは、インスタントメッセージ(MSNメッセンジャみたいなやつ)のプロトコル。
Google Talkもこのプロトコルで通信をしており、クライアント次第でボイスチャットやビデオチャットも乗せられるスグレモノ。
自分で拡張も定義できて、チャット以外の用途にも使える。リアルタイムでプッシュしたいものが向いている。HTTPはそういうの苦手*1。
XMPPプロトコル自体にサーバを拡張する仕様があって、今回はそのプログラムを書くためのライブラリを書いた。
何か面白いものを作れないか妄想中。
*1:一応、Cometみたいなのでやれることはやれる。
Windows上のClozure CLでhunchentoot
http://d.hatena.ne.jp/t2ru/20101205/1291537110
のやり方でasdf-installを使えるようになったのは良いが、hunchentootをインストールしようとしたときにCL+SSLでエラーが出てしまう。
O_NONBLOCKというのがないと言われてた。
いいのかどうかは不明だが、下記の方法でとりあえずデフォルトページが動くところまではできた。
変更前
#+clozure-common-lisp (defun install-nonblock-flag (fd) (ccl::fd-set-flags fd (logior (ccl::fd-get-flags fd) #.(read-from-string "#$O_NONBLOCK")))) ;; read-from-string is necessary because ;; CLISP and perhaps other Lisps are confused ;; by #$, signaling"undefined dispatch character $", ;; even though the defun in conditionalized by ;; #+clozure-common-lisp
変更後
#+(and clozure-common-lisp (not win32)) (defun install-nonblock-flag (fd) (ccl::fd-set-flags fd (logior (ccl::fd-get-flags fd) #.(read-from-string "#$O_NONBLOCK")))) ;; read-from-string is necessary because ;; CLISP and perhaps other Lisps are confused ;; by #$, signaling"undefined dispatch character $", ;; even though the defun in conditionalized by ;; #+clozure-common-lisp
Windows上のClozure CLでasdf-install
Windows上のClozure CLでasdf-installを使う方法について、先日のエントリのコメ欄で教えてもらった方法を試した。
http://d.hatena.ne.jp/t2ru/20100807/1281151524#c
意外に悶絶してしまったので、やり方を記録しておく。
~/ccl-init.lisp
;; asdf-installで使っているのに、CCLでは何故か入っていない。 (pushnew :win32 *features*) (pushnew :mswindows *features*) (require 'asdf) (push "ccl:tools;asdf-install;" asdf:*central-registry*) ;; asdf-installでシンボリックリンクを貼れないので、パスをサーチするようにする。 (in-package #:asdf) (defvar *subdir-search-registry* `(,(merge-pathnames ".asdf-install-dir/site/" (user-homedir-pathname)))) (defvar *subdir-search-wildcard* :wild) (defun sysdef-subdir-search (system) (let ((latter-path (make-pathname :name (coerce-name system) :directory (list :relative *subdir-search-wildcard*) :type "asd" :version :newest :case :local))) (dolist (d *subdir-search-registry*) (let* ((wild-path (merge-pathnames latter-path d)) (files (directory wild-path))) (when files (return (first files))))))) (pushnew 'sysdef-subdir-search *system-definition-search-functions*) (in-package #:cl-user)
asdf-install用に必要なパッケージを~/.ccl/startup/に手動で展開
- gzip-stream
- archive
- trivial-gray-streams
- flexi-streams
- salza2
~/.asdf-install
;; asdf-installが使うパッケージは手動でロードできる状態にしておかないといけない。 (defparameter *asdf-startup-sites* (merge-pathnames ".ccl/startup/" (user-homedir-pathname))) (dolist (p '("gzip-stream_0.2.8" "archive_0.8" "trivial-gray-streams-2008-11-02" "flexi-streams-1.0.7" "salza2-2.0.7")) (pushnew (merge-pathnames (concatenate 'string p "/") *asdf-startup-sites*) asdf:*central-registry*)) (asdf:oos 'asdf:load-op :gzip-stream) (asdf:oos 'asdf:load-op :archive) (defun asdf-install-extractor (to-dir tarball) (let ((name nil)) (gzip-stream:with-open-gzip-file (ins tarball) (archive:with-open-archive (archive ins) (let ((*default-pathname-defaults* (pathname to-dir))) (archive:do-archive-entries (entry archive name) (archive:extract-entry archive entry) (unless name (setf name (archive:name entry))))))) ;; we use string instead of namestring because ;; asdf-install searches for /'s and not \'s ;; which will break on windows (string name))) (push 'asdf-install-extractor asdf-install:*tar-extractors*) ;; suppress GPG checking (setq asdf-install:*verify-gpg-signatures* nil)
これでasdf-installが動いた。
チルダクオート
マクロを組むときには変数捕捉に気をつけないといけないが、実は変数捕捉をうまく使う方法もあって、On Lispにそんな例が載っている。
http://www.komaba.utmc.or.jp/~flatline/onlispjhtml/anaphoricMacros.html
一番簡単な例としては、アナフォリックIFがある。
(defmacro aif (test-form then-form &optional else-form) `(let ((it ,test-form)) (if it ,then-form ,else-form))) (aif (big-long-calculation) (foo it))
この例では、比較結果である変数itを捕捉し、aifのthen-form内で使えるようにする。
Clojureでは少し長くなって、
(defmacro aif ([test-form then-form] `(aif ~test-form ~then-form nil)) ([test-form then-form else-form] `(let [~'it ~test-form] (if ~'it ~then-form ~else-form)))) (aif (big-long-calculation) (foo it))
である。*1
アナフォラのitの前にチルダクオート(~')が付いているが、これはClojureのバッククオートがCommon Lispのものとは違う動作をするため。
Common Lispのバッククオートは、アンクオートできるクオートである。Clojureのバッククオートは、これに「名前空間解決&付加」という機能がプラスされている。
素のシンボルが欲しければ、クオートされたシンボルをアンクオート(すなわちチルダクオート(~'))しないといけない。
* `(hoge list ,'list) (HOGE LIST LIST)
user=> `(hoge list ~'list) (user/hoge clojure.core/list list)
この違いは、マクロを使うときの名前衝突への対策のため。ちなみに、各言語での名前衝突への対策はこんな感じ:
- Common Lisp : そもそもLisp-2*2なので変数名と関数名は衝突しないよ派
- #'とかfuncallとかうぜぇ。
- Scheme(R4RS〜) : 名前の衝突を自動回避するならLisp-1*3でも大丈夫だよ派
- アナフォリックマクロみたいな、意図的に安全対策を破る(orサボる)ようなマクロは書けない。
- Gaucheとかはdefine-macroが使えるけど、名前の衝突に関しては自己責任の部分がすごく増える。
- Clojure : バッククオートの定義を変えて、read時に名前空間を付け足してしまえばLisp-1でも大丈夫だよ派
Clojureでは、バッククオートの定義を変えてしまったために、アナフォラを導入するときにチルダクオート(~')が必要になってしまったけど、逆に安全対策を破っていることを示すサインみたいになっていて、かえって良いかも知れない。