前回のバリデーションに引き続き、今回はActiveRecord 3を使った際のwhere
メソッド内での比較演算が簡単になるgemのMetaWhereについて調べたのでメモ。ぶっちゃけ今回はSinatraはあんまり関係なくて、ActiveRecord 3の使い方が便利になる話。
お題
updated_atカラムの中で、今から1時間前よりも古い日時のレコードだけ抽出せよ
このお題をクリアするためのActiveRecord 3のwhereメソッドの書き方がよくわからなかった。というか、今もわかってない。たぶんSQL文を直接書いて「updated_at < 1時間前の数値」的な感じで書くんだろうけど…と調べているときに、タイトルにもある通りの比較演算が簡単になるMetaWhereの存在を知ってしまった。ので、whereでの書き方がよく分からないまま、このgemを使って解決した。
比較演算が簡単になるgem
上述のお題について検索していたら、この記事を発見して、かなり便利そうだったので入れて使ってみた。実際便利だった。
まず、Gemfileにmeta_where
を追加する。今回使ったバージョンは1.0.4だった。
gem 'meta_where'
んで$ bundle install
して準備完了。使い方は、Sinatraの場合は本体となるapp.rb
で主に使うわけだけど、MetaWhereのgemがrequireされていれば、ActiveRecord 3のメソッドやら引数に自動で機能が拡張されるみたいなので、gemのインストールとrequire以外は特に準備するものはない。それで、前述のお題をクリアするためのコードで、実際にMetaWhereを使ってみるとこんな感じ。
get '/meta_where1' do
c = Count.where(:updated_at.lt => 1.hour.ago)
end
この:updated_at
シンボルにlt
がくっついている部分がMetaWhereによって拡張された機能で、「:update_atが1時間前(1.hour.ago)より小さい(lt)」(時間が経過するほど値が大きくなるので、小さい=昔)というような指定ができるようになる。もちろん、大なりのgt
も用意されている。これはなかなか便利。それ以外にも、以上を表すgte
、以下を表すlte
が用意されていて、まとめると以下のようになる。
||| |-|-| |gt|より大きい| ex. a > 3 | |lt|より小さい| ex. a < 3 | |gte|以上| ex. a <= 3 | |lte|以下| ex. a >= 3 |
ちなみに、Sinatraでよく使われているO/RマッパーのDataMapperは、これらの機能はデフォルトで用意されており、上記以外にもnotやlikeなどが使える。
大なりと小なり、gtとlt
いきなり話が飛ぶけど、大なりと小なり、gtとltってどっちがどっちかすぐ分からなくなる。ちょっとググッてみたら、同じような人がいた。
あぁ、説明不足だったんですが、僕はだいなり・しょうなりってどっちがどっちだかわからないバカなので (同じように東と西もわからなかった)、gt/lt が何の略か知っていてもそれが記号に一致しないんです。でも l は left の l とバカでもわかるように覚えておけば、左手に近いほうの Shift+, をおせばいいというのが直感的に理解できるんです。バカのバカなりの考えかたです。
いちおう自分は、*小なりの「<」は大文字の「L」を少し右回転させた形と似てるので「LTは<」*と覚えてたけど、この人の覚え方の(キーボードの並びで)LeftのLもいいね。両方覚えておけば、よっぽどド忘れしないかぎりは思い出せそう。
MetaWhere.operator_overload!
話が脱線してしまったけど、MetaWhereがさらにすごいのは、オペレーターオーバーロードというクラスメソッドを実行すると、gtだのltだのを覚える必要すらないということ!(さっきの覚え方の話はなんだったのだというツッコミはなしで) このオーバーロードする1文MetaWhere.operator_overload!
を使う前に書き加えるだけで、超絶便利になる。普通のRubyのように直感的にそのまま書ける。
get '/meta_where2' do
MetaWhere.operator_overload! # これを書くと
c = Count.where(:updated_at < 1.hour.ago) # こんなに直感的に書ける
end
これはすばらしー。このオーバーロードをすることでどんな弊害があるのかはよくわからんけど、便利なのでとりあえずオーバーロードする方向で使っちゃおう。ちなみに、大なり小なり以外の記号では、ドキュメントを見ると下記の記号を使えるみたい。
||| |-|-| |>>| (equal)| |^| (not equal)| |+| (in array/range)| |-| (not in array/range)| |=~| (matching – not a regexp but a string for SQL LIKE)| |!~| (not matching, only available under Ruby 1.9)| |>| (greater than)| |>=| (greater than or equal to)| |<| (less than)| |<=| (less than or equal to)| |[]| Function|
orderメソッドにも追加されてる
オマケってわけじゃないけど、order
メソッドにも降順にするdesc
や昇順にするasc
という機能が追加されているみたい。
c = Count.order(:updated_at.desc) #=> 降順
c = Count.order(:updated_at.asc) #=> 昇順
というわけで、今作ってるWebサービスで時間を比較する必要があったので調べてたら、たまたま見かけたgemがとても便利だったのでMetaWhereについて書いてみた。同じシリーズで検索できるっぽい機能拡張MetaSearchというのもあるみたいだけど、今のところ使う予定がない。あと、このSinatraからActiveRecord 3を使うシリーズも、とりあえず今回で目的が達成できたので一旦終了。今後何か調べたりしたら続ける予定。
ASCIIcasts - “Episode 251 - MetaWhereとMetaSearch” MetaWhere – metautonomo.us