RubyのORMはいくつかあるが、Railsで使われているActiveRecordが最もメジャーで、その次くらいにSequelがあると認識している。
最近ふとしたことで両方を使ってみた感想。どちらも、テーブル名を複数形(sつき)にしなきゃいけなかったり、いろいろ面倒だった。勝手に変化させないで欲しいんだよねー。おれ、プログラム上で語尾を変化させるのは好きじゃないのよね。-erと-orの違いとか、sつけるのつけないの、esやiesになるの、受け身だからedだとか、haveかhasかhadとかhavenとか、isかareか、theをつけるかつけないかとか、datumかdataかなんて、考えたくないじゃん。ただのシンボルなんだから。
そういう細かいアレがあるにせよ、どちらも割と使えたんだけど、ActiveRecordはDB側のconstraintを指定できず、例えばカジュアルにuniqueをつけたいと思ってもできない。Ruby側でvalidationを書くかインデックスにuniqueをつけるかしなければならなかったりして、今のところSequelのほうが使用感は良い。ActiveRecordがモデルの記述を重点しているのに対し、Sequelはモデルがなくてもかなりイイトコまで働ける感じ。
例えばJoinの書き方に関してはSequelのほうが直感的で分かりやすい。現代においてSQLを使うというのはすなわちJoinを使うということであるようなのだが、Sequelはモデルに何も書かなくてもキーをjoinの引数に指定するだけでjoinできる。ActiveRecordはモデルにリレーション(belongs_toだの何だの)をつけないとjoinできない。そもそもjoinじゃなくてjoinsなんだよな…三人称単数現在…そこでそうするか。SQLではJOINSなんて書けないよ。んなこと言うならおめーはActiveRecordsじゃねえのかッコラー!!
ただSequelのjoinは同じカラム名のものがあるときに、間違った(と私が思う)結果が返ってきてしまうことに気づいた。意味的に考えてバグっている気がする。
つまり、こうだ。
[pyg language=”ruby”]
#! /usr/bin/ruby
require ‘sequel’
require ‘pp’
DB=Sequel.sqlite
DB.create_table :tbl1s do
primary_key :id
String :value
end
DB.create_table :tbl2s do
primary_key :id
String :value2
Integer :tbl1_id
end
class Tbl1 < Sequel::Model ; end
class Tbl2 < Sequel::Model ; end
if $0==__FILE__
Tbl1.insert(:value=>“tbl1data1″) # tbl1s.id=>1
Tbl1.insert(:value=>”tbl1data2″) # tbl1s.id=>2
Tbl2.insert(:value2=>”tbl2data1″, :tbl1_id=>2) # tbl2s.id=>1
Tbl2.insert(:value2=>”tbl2data2″, :tbl1_id=>1) # tbl2s.id=>2
# SELECT * FROM tbl1s INNER JOIN tbl2s ON (tbl2s.tbl1_id = tbl1s.id)
t=Tbl1.join(Tbl2, :tbl1_id=>:id).first
pp t # Tbl1{:id=>1, :value=>”tbl1data2″, :value2=>”tbl2data1”, :tbl1_id=>2}
end
[/pyg]
Tbl1.join(Tbl2)だと、Tbl1のオブジェクトが返るが、その:idはなんとTbl2のものなので、戻ってきたTbl1オブジェクトをそのまま扱っていると大変な事故が起きる。例えばこれで中身を見てdeleteしたら、思ったのと異なるものが削除されてしまう(No!)。これを避けるには、こんなふうに↓書かなければならなかった。
Tbl1.join(Tbl2, :tbl1_id=>:id).select(*Tbl1.columns.map{|m| Sequel.qualify(:tbl1s, m) })
これって、モデルで関連をちゃんと書けばいいという話なんだろうか? 戻るのはTbl2じゃなくてTbl1のオブジェクトなんだから、selectやモデル定義で小細工しなくてもTbl1側のカラム名が優先されるのが当然に思えるんだけど。