Common Lisp / Caveman2でDB接続

このエントリーをはてなブックマークに追加
はてなブックマーク - Common Lisp / Caveman2でDB接続
Facebook にシェア
[`yahoo` not found]
[`livedoor` not found]
[`friendfeed` not found]
[`tweetmeme` not found]

src/config.lips にDB設定がある。標準だとsqlite3を使う設定になっているのでmysqlに変えて使ってみよう。PostgreSQLにダイブしてみようとも思ったけど、それはまた今度。

設定

$ vim src/config.lisp
;(defconfig |default|
;  `(:databases ((:maindb :sqlite3 :database-name ":memory:"))))
(defconfig |default|
  `(:databases ((:maindb :mysql :database-name "dbname" :username "username" :password "password"))))

テーブル作成

「db/schema.sql」っていう中身空のファイルがあるんだけどここにスキーマ書いていくといいのかなー。なんか勝手にやってくれたりするんだろうか。
今回試した限りでは特段使われている感じは無かったので保存だけしておいて、手動でテーブル作成。
あ、データベースは適当に作ってある前提ですいません。。。

mysql> CREATE TABLE person
    -> (
    ->   id INT(11) NOT NULL AUTO_INCREMENT,
    ->   name VARCHAR(255),
    ->   age INTEGER,
    ->   PRIMARY KEY (id)
    -> );
Query OK, 0 rows affected (0.21 sec)

mysql> show columns from person;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(255) | YES  |     | NULL    |                |
| age   | int(11)      | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

エラーログ

DBに限った話ではないのだけど、Caveman2ではエラーログ出力もサポートしている。
ちょうどDBいじっているときに使ってみたので紹介。
設定は簡単。src/config.lisp に下記の設定をすれば書き出してくれる。

$ vim src/config.lisp
(defconfig |default|
  `(:error-log #P"/path/to/log/myapp_error.log"
    :databases ((:maindb :mysql :database-name "dbname" :username "username" :password "password"))))

出力されるログは下記の通り。
スタックトレースとリクエスト情報が生成される。書式が処理しにくそうなので本番で使うには考えとかないといけないかも?
今のところこれだけ情報があればデバッグには困らないですね。

Date/time: 2014-03-22-06:04An unhandled error condition has been signalled:
                              The function :NAME is undefined.


0: (SB-DEBUG::MAP-BACKTRACE
    #
    :START
    0
    :COUNT
    4611686018427387903)
1: (BACKTRACE 4611686018427387903 #)
2: (TRIVIAL-BACKTRACE:PRINT-BACKTRACE-TO-STREAM
    #)
3: (TRIVIAL-BACKTRACE:PRINT-BACKTRACE
    #
    :OUTPUT
    NIL
    :IF-EXISTS
    :APPEND
    :VERBOSE
    NIL)
(略)

Request:
    REQUEST-METHOD: :GET
    SCRIPT-NAME: ""
    PATH-INFO: "/users"
    SERVER-NAME: "servername"
    SERVER-PORT: 8080
    SERVER-PROTOCOL: :HTTP/1.1
    REQUEST-URI: "/users"
    URL-SCHEME: :HTTP
    REMOTE-ADDR: "10.0.3.1"
    REMOTE-PORT: 39378
    QUERY-STRING: ""
    RAW-BODY: #<FLEXI-STREAMS:FLEXI-IO-STREAM {1008659EF3}>
    CONTENT-LENGTH: NIL
    CONTENT-TYPE: NIL
    HTTP-HOST: ":8080"
    HTTP-USER-AGENT: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:27.0) Gecko/20100101 Firefox/27.0"
    HTTP-ACCEPT-LANGUAGE: "ja,en-us;q=0.7,en;q=0.3"
    HTTP-ACCEPT-ENCODING: "gzip, deflate"
    HTTP-COOKIE: "CLACK.SESSION=session"
    HTTP-VIA: "1.1 localhost (squid/3.1.19)"
    HTTP-X-FORWARDED-FOR: "ipaddr"
    HTTP-CACHE-CONTROL: "max-age=0"
    RAW-BODY-BUFFER: #()

任意のログを書き出したい気がしてきたけど今日は忘れて次へ進もう。

DBからデータ取得してテンプレートで表示

ここからが本番!

コントローラ側に20歳以上の人を取得する search-adults 関数と、 persons ルートを定義。
DBアクセス部分は高機能なCommon LispのO/Rマッパー「Integral」を作りました – 八発白中を使いたいけどまだ理解が追いついていない。

$ vim src/web.lisp
(defun search-adults ()
  (let ((db (connect-db :maindb)))
    (select-all db :*
      (from :person)
      (where (:>= :age 20)))))

(defroute "/persons" ()
  (with-layout (:title "List | Persons"
                :users (search-adults))
    (print (search-adults))
    (print (search-seventeen))
    (render #P"users.tmpl")))

テンプレート側

$ vim templates/layouts/default.tmpl
<ul>
  <% @loop users %>
  <li><% @var name %>(<% @var age %>)</li>
  <% @endloop %>
</ul>

本当はusers.tmplで表示させたかったんだけどコントローラ側のusersをusers.tmplに渡す方法がわからなかったので
default.tmplで今回は我慢。

データはこんなのを用意。

mysql> select * from person;
+----+---------+------+
| id | name    | age  |
+----+---------+------+
|  1 | yosiwo  |   35 |
|  2 | yukarin |   17 |
+----+---------+------+
2 rows in set (0.00 sec)

yukarinは17歳なのでsearch-adultsで取得されないため、表示されるのは「yosiwo(35)」のみのはず。
http://localhost:5000/persons
にアクセスして確認。うまくいった!

printしている部分はブラウザ側ではなくコンソール側に出てきます。REPLで起動していればREPLのコンソールに下記のように表示されます。

(print (search-adults)) -> ((:|id| 1 :|name| "yosiwo" :|age| 35))
(print (search-seventeen)) -> ((:|id| 2 :|name| "yukarin" :|age| 17))

さてさて、追加されるとどうなるかな。

mysql> INSERT INTO person(name, age) VALUES('yukarin_', 38);
Query OK, 1 row affected (0.03 sec)

mysql> select * from person;
+----+----------+------+
| id | name     | age  |
+----+----------+------+
|  1 | yosiwo   |   35 |
|  2 | yukarin  |   17 |
|  3 | yukarin_ |   38 |
+----+----------+------+
3 rows in set (0.00 sec)

謎のyukarin_を38歳でINSERTしてみる。もう一回アクセすると!
当たり前ですが、「yosiwo(35)」「yukarin_(38)」が表示されます。なにか恐怖を覚えますね。

print部分リスト丸ごとダンプしてくれてらくちん。量が多いとこんな事やってられないけどまだデバッグ方法を
理解していないのでとりあえずこれで眺めています。

(print (search-adults)) -> ((:|id| 1 :|name| "yosiwo" :|age| 35) (:|id| 3 :|name| "yukarin_" :|age| 38))
(print (search-seventeen)) -> ((:|id| 2 :|name| "yukarin" :|age| 17))

ひとまずこんな感じですかねー。
ブログチュートリアルを書く!という予定は全く達成できていませんが、、、基本的な使い方はわかってきた感じがします。
Common Lisp自体の理解ももっと進めないとしんどいので引き続き時間を作って進めていきます。

とりあえず気になっているのはDBに日本語入れると「????」になって悲しい。
解決しときたいんですが思ったよりほかで時間を食ってしまったので今後の課題にします。

  • Common LispのO/Rマッパー「Integral」はぜひともつかいたい。
  • ログ周りも勉強段階では優先順位的にはそんなでもないけど運用していくときには欲しい
  • テンプレートのCL-EMBがまだ気持ちよくなるに至っていないので理解を深めたい。FormHelper的なのとかあるのかな。
  • このままだと src/web.lisp が太るの気持ちよく分離する方法
  • scaffoldほしーなー。「Caveman2 is no more "micro" web application framework. 」って書いてあるしー

この情報も参考になりそうなので後で読む。全部が深町さん。。。どんなひとなのでしょう。気になります!
Common LispのWebアプリケーションを社内運用してみた - 八発白中

ほかにもCavemanのv1のときの情報ですが参考になったのでリンク張っておきます。
Common LispでWebアプリケーション#1 - hiyosi's blog
Common LispでWebアプリケーション#2 - hiyosi's blog
Common LispでWebアプリケーション #3 - hiyosi's blog
hiyosi/caveman-city-api-sample

試行錯誤しながら書いているので間違いあったら指摘してもらえるとうれしいです。
typoやここ理解してねーだろおまえ、的なものがあったらぜひ!

Popular Posts: