GaucheでS式でリモートでク
エリでevalでAjaxで
KPF #x08
@cryks
注釈 (2013/08)
● これは2013年2月に行われた第8回KPF
(Kumamoto Programming Freaks)の発表資料
です
● 何故今更アップロードしたのか?
→ すっかり忘れていました
● デスマ真っ最中に急遽仕上げた資料なので、結
構いい加減です
● この資料に登場したライブラリをリファインして
Github等で公開すると当時発言した気がします
が、手を付けていません
Internet
Local Network
ターゲット
RS-232C
Global
HTTP Server
Private Configuration
HTTP Server
Database
IP over RS232
Local PC
Browser
作りました (まだ作ってます)
● dbexecutor
● ajax-router
● reploxy
dbexecutor
● dbiのラッパー
● dbiって汎用化されてるけど使いづらい
(and-let* ([query (dbi-prepare conn "SELECT * FROM users")]
[result (dbi-execute query)]
[accessor (relation-accessor result)])
(begin0 (map
(^[row]
`([id . ,(x->integer (accessor row "id"))]
[name . ,(accessor row "name")]))
result)
(dbi-close result)))
=> (((id . 1) (name . "Alice"))
((id . 2) (name . "Bob"))
...)
こんなふうに書けます
(dbe-select ([id :: int]
[name])
"users")
=> (((id . 1) (name . "Alice"))
((id . 2) (name . "Bob"))
...)
ジェネレータ版(遅延評価)な dbe-gselect もあります
JSON化も簡単
(construct-json
(list->vector
(dbe-select ...)))
=> [{"id":1,"name":"Alice"},...]
● ちなみに Gauche 0.9.3 以降ならこう書ける
($ construct-json
$ list->vector
$ dbe-select ...)
(dbe-select ([hoge.id as id :: int]
[fuga.fuga_id as fid :: int]
[fuga.key as key])
"hoge"
:joins `([LEFT "fuga" USING "fuga_id"])
:where `(= fuga.key ,key))
WHEREとかJOINは?
S式クエリ
こんなクエリが発行できます
`(and (= id 10) (= name "hoge"))
=> ("id" = 10) AND ("name" = 'hoge')
`(= name "fu'ga")
=> "name" = 'fu''ga'
`(= user_key user_name "foo")
=> "user_key" = "user_name" AND "user_name" = 'foo'
`(not (= id 15))
=> "id" <> 15
(let1 name "a'b'c"
(construct-where `(= name ,name))) => "name" = 'a''b''c'
いらんかったんや!
プレースホルダなんて
※ 同じSQLをパラメータのみ変更して何度も実行する場合を除く
ajax-router
● CGI呼び出しを自動化
● HTTPサーバとして起動
nginxやApacheからリバースプロキシ経由で使う
● 指定したディレクトリにファイルを置けば、それがURLにな
る
hoge.scm => http://localhost:8808/hoge
● 読んだソースはキャッシュされるが、ソースを更新すると自
動的に読み込み直す
● 1ソースにつき1つの無名環境
● shiro さんの makiki の上に構築
● コア部分は router モジュールとして分離
● makiki + router ≒ ajax-router
CGI 呼び出し
(define (cgimain)
#("JSONable" (("key" . "value"))))
=> ["JSONable",{"key":"value"}]
http://.../hoge?a=1&b=2
(define (cgimain :key a b)
`#(,a ,b))
=> ["1", "2"]
クロージャ
(define (cgimain :key name)
`#(,(register-cont (lambda (:key name2)
`#(,name ,name2)))))
http://.../hoge?name=hogehoge
=> ["...hash..."]
http://.../cont/...hash...?name2=fugafuga
=> ["hogehoge","fugafuga"]
もういちど dbexecutor
● dbexecutor には with-database というDB接続
マクロがある
(with-database <dsn>
(dbe-select ...))
● dbexecutor のサンプルに connection が一切
なかったのはそのため
● with-database は、本当にDB接続が必要に
なったときに初めてDBに接続しにいく
● with-database ブロック内で dbe-* 系が呼ばれ
なければDB接続は発生しない
● ブロックを抜ければDBは切断
ajax-router + dbexecutor
● ajax-router は with-database ブロック内で
cgimain を呼んでいる
● cgimain 内で with-database する必要はない
● cgimain でデータベースを使用しなかった場合
に、不要なDBコネクションが発生することもない
(use dbexecutor)
(define (cgimain)
(list->vector
(dbe-select ([id :: int] [name]) "users")))
=> [{"id":1,"name":"Alice"},{"id":2,"name":"Bob"},...]
reploxy
● REPL + Proxy = reploxy
● server と client のセットモジュール
reploxy server
● REPL サーバ
● read して eval して print (write) してるだけ
● 文字通りのREPL
● eval中に例外が上がった場合も、clientにその
例外を伝えられる
● 多値もばっちり返すことが出来る
● write したときに #<...> になるようなオブジェクト
はさすがに返せない
reploxy client
● remote-eval 手続き
(remote-eval "localhost" 12345 `(begin ...))
OR
(with-remote-host "localhost" 12345
(remote-eval `(begin ...)))
● 引数のS式をリモートで評価して結果を得る
● リモートで例外が上がった場合、ローカル側に
同じ例外が上がる
(remote-eval `(error "message"))
=> ERROR: message
● 多値も受け取れる
($ values->list $ remote-eval `(values 1 2 3))
=> (1 2 3)
● remote-eval のたびにコネクション発生
import-remote-variables
● reploxy server が動作している環境の、エクス
ポートされている束縛をローカルに持ってくる乱
暴な手続き(いまのところ手続きだけ)
;; server
(export get-hello-world my+)
(define (get-hello-world)
"Hello reploxy world!")
(define (my+ a b)
(+ a b 1))
(reploxy-run-server ...)
;; client
(import-remote-variables)
=> get-hello-world, my+ が生えてくる!
(print (get-hello-world))
(my+ 1 2) => 4
reploxy + dbexecutor
● reploxy server はコネクションが張られるときに
callback を指定できる
● ということは
(reploxy-run-server 12345
(^[]
(with-database *dbi-connect-settings*
(reploxy-repl (current-module)))))
(remote-eval `(dbe-select ([id :: int] [name]) "users"))
=> (((id . 1) (name . "Alice"))
((id . 2) (name . "Bob"))
...)
ajax-router + reploxy + dbexecutor
● ajax-router は cgimain を呼び出すときに
with-remote-host も行っている
(use reploxy)
(define (cgimain :key id)
(list->vector
(remote-eval
`(dbe-select ([id :: int] [name])
"users"
:where `(= id ,,id)))))
http://.../hoge?id=2
=> [{"id":2,"name":"Bob"}]
import-remote-variables (2)
;; server
(define (update-db)
...)
(export update-db)
;; client
(use reploxy)
(import-remote-variables)
(define (cgimain)
(update-db))
組み合わせることで
● ajax-router で JavaScript から ajax リクエスト
を受け付け
● reploxy でシリアルの向こうにあるマシンに問い
合わせ
● dbexecutor でフレキシブルなDBアクセス
● 共通化した部分は import-remote-variables
で呼び出し
Internet
Local Network
ターゲット
RS-232C
Global
HTTP Server
Private Configuration
HTTP Server
Database
IP over RS232
Local PC
Browser
Internet
Local Network
ターゲット
RS-232C
Global
HTTP Server
Private Configuration
HTTP Server
Database
IP over RS232
Local PC
Browser
ajax-router
ajax-router
reploxy-server
reploxy over RS232
reploxy(local)
dbexecutor
おわり

第8回KPF発表資料