Tcl版ActiveRecordのその後

あれから8ヶ月が過ぎたんですな。早いものです。

世の中にTcl版ActiveRecordが出てくる兆しはなかったので、結局自分で作ってみました。オリジナルのTclはオブジェクト指向言語ではないんですが、ActiveRecordRubyと同じように作るには、少なくともクラスメソッドとインスタンスメソッドを使えるオブジェクト指向拡張が必要だろうと思ったので、限りなくRubyに近いことが出来るTclの拡張であるXOTclを使うことにし、データベースへの接続も、XOSql拡張というのを使うことにした。

Railsは膨大なソースからなっていて、ActiveRecordだけでも途方にくれるので、実際にはほとんど使わないような機能は無視することにした。とはいえ、method_missingを利用したfind_all_by_group_id_and_category_idみたいな、デバッグのとき意外ほとんど使わないと思うんだけど、TclだってRubyみたいなことはできるんだぞってことを証明するために、一応作ってみた。

SELECTの結果をオブジェクトのプロパティにマッピングする機能
execute_sql {sql}
find_by_sql {sql}
Rubyに負けたくなくてつくった機能
method_missingを利用したfind_by*, find_all_by*
belongs_to, has_one, has_many
Group.usersなどの関連したレコードを取得するメソッドが使えるようになる機能。
save(INSERT, UPDATEの自動選択), delete
TclのArrayを使って、find_by_sqlSQL文をばらばらに作って後で組み立てる機能。

set sql(select) *
set sql(from) users
set sql(where) "name = 'Taro' and mail = 'taro@abc.jp'"

XOTclはfake namespaceでプロパティの参照・代入が直接できる。

set user [ActiveRecord::User find_by_name "太郎"]
$user set property(mail) "taro@abc.jp"; # varchar型のカラムなので、自動でクオートする。
$user array set property {
  mail "taro@abc.jp"
  age   27
}
$user save
$user destroy; # GCないから。

XOTclより前から存在してる有名な拡張、[incr Tcl]では、パブリック変数を使ってもプロパティを普通の変数と同じように代入、参照することができない。動的にメソッドを追加できないので、アクセサというのも難しい。データベースのカラムごとにアクセサを定義するなんて面倒臭すぎる。

そして、実際に今まで自分が作ってきたプログラムの「データソースをCSVファイルからデータベースに移行しようプロジェクト」に使ってみた。

とりあえず今のところMySQLとSQLite3でためしてみた。SQLite3ならBLOBを含めて必要な機能は使えるということが分かった。XOSql自体はPostgreSQL(pgtcl)とかにも対応してるので、他のものでもいけるんじゃなかろうか。MySQLはどうも日本語の処理がうまくいかない。

セキュリティとか全く考慮してないので(もしかするとライブラリが勝手にやってくれてるのかもしれんけど。)少なくともRailsのようにWebで使うのは危険だと思う。

ちなみに当初はBLOBが必要になる予定だった。それで対応したんだけどえらい大変だった。SQLite3のライブラリは出来が良くて、生のバイナリデータを直接送る方法が提供されていた。通常Tclはバイナリデータを文字列として展開する際に、たとえば「;」とかが混じってると、行の終わりだと思ってしまい、エラーになる。tclsqliteはCレベルでTclの変数を見ていて、$fileとかの変数を展開しないで送ってくれるらしい(あまりよく理解してないので適当な説明です)生のバイナリがダメでもBase64で文字列にしてしまえば同じことができるらしい。