表題のものがこちらにあります https://github.com/sonots/ruby-sql-maker
細かい使い方はドキュメント書いている途中なので少々お待ちを> <
=> 追記:用意しました
動機
SQL を文字列として自力で組み立てているコードがあって、 そこを綺麗にしたかったんだけど、 Ruby でクエリビルダーというと Arel ぐらいしかなくて、 しかし、Arel で書くと非常につらぽよな感じだったので作った。
ActiveRecord (以下、AR) とか Sequel とかの ORM に丸っと置き換えても良かったんだけど、 そのアプリにとってはちょっと重すぎるというのと、 クエリ生成コードを綺麗にしたい&セキュアにしたいだけだったので、 理想的には使いやすいクエリビルダがあれば良いのになぁという感じだった。 で、理想を求めた。
使い方 - Arel との比較
Arel がどんなかんじに辛いのかは、 Arelで色んなSQLを組み立ててみる - @ryopekoのなにか の記事を見てもらうと想像つくと思うんだけど、 一番シンプルなやつでもこんなかんじで、ちょっと「うっ」と来る。覚えられる気がしない。AR は内部で Arel を使っているわけだけれど、如何に AR がいい感じにしてくれているのかが良くわかる実例。
SQL::Maker を使うとこうなる。メソッドチェーンで呼び出すだけになって直感的。
ライセンスについて
SQL::Maker のライセンスは「perlと同じ」と書いてあるので、 GPL と Artistic License のデュアルライセンスになっているのだけど、 移植の場合にこれを果たして MIT ライセンスにしてもいいのだろうか、とか思ったので、 社内の有識者に質問したりしてた。
cf. perl のライブラリ(ライセンスはperlと同じ)をrubyに移植した場合、GPL汚染を受けるのか?
で、まぁ色々悩んだわけですが、「MIT ライセンス」にしましたので、 あとは @zigorou さんによろしくおねがいしたいと思います。╭( ・ㅂ・)و ̑̑
※ ちなみに SQL::QueryMaker は MIT ライセンスでOK
perl 版との違い
perl 版わかる人にしか伝わらないかもだけど、書いておく。
スカラーリファレンスの扱い
perl の SQL::Maker は内部的に文字列と、文字列リファレンスで分岐していて、
- 文字列ならクォートする
- 文字列リファレンスならクォートしない
というような扱いをしているんだけど、ruby だと全部リファレンスなので、 この区別ができない。なので、ruby 版では、クォートされたくない場合は、 sql_raw
メソッド(SQL::QueryMaker由来)を使って、オブジェクトを渡してもらうことにした。
perl 版
ruby 版
SQL::Maker::SelectSet の Export 関数perl 版では、
みたいに use すると、そのスコープに union
や intersect
関数が生える。
ruby 版では、これを SQL::Maker::Helper というモジュールにして
として使ってもらうようにした。 ただし、名前が sql_union
, sql_intersect
のように変更してある。
perl では qw(union)
のようにして取り込む関数を絞ることができるからまだいいんだけど、 ruby の include だとそういうことができないので、重複の可能性が減る方向にもっていった。
SQL::QueryMaker の Export 関数
今回 SQL::Maker に kazuho さんの SQL::QueryMaker も取り込んだ。 SQL::QueryMaker 由来の関数も
で生えるようにした。sql_raw
とか sql_eq
とか。
オートバインド
悲しいことに ruby の代表的な Mysql library である mysql2 には、 prepared statement の機能がない。
そこで、:auto_bind => true オプションを渡すと bind して SQL を返すようにした。
:auto_bind => false
(デフォルト)
:auto_bind => true
内部的には escape して ? を置換している。ActiveRecord と全く同じことをやっている。
今後
DBI 由来の sql_type とか、プラグインとかまだ移植してないのでボチボチやっていく。 sql_type はどうしようかな〜
おわりに
Ruby には missing だった使いやすい SQL ビルダーができたので、 ORM 使うほどじゃないなぁって場合は、試してもらえるといいかも。
ActiveRecord を使っている場合でも、 1部直接 SQL を自力で組み立てている人とかけっこういると思うので、 そういうところで使っても便利かもしれないですね。Enjoy!