Squeelの列名を動的に指定する
Squeelを使うと、Rubyの枠内で複雑なクエリもすいすい書けるようになりますが、さらに凝ったことをしようとするとひと工夫が必要になります。
Squeelって?
Railsでちょっと複雑なSQLのクエリを書こうとすれば、一部を文字列にするか、Arelを使うかということになりますが、どちらにも使いづらい点があります。
- 文字列で書いてしまうと、あとあとの再利用が不便になる
- Arelは内部向けのAPIであって、使いにくい&仕様が安定していない
ここで登場するのがSqueelです。SqueelはArelをラップして、Ruby的なDSLでSQLを書けるようにしてくれます。使い方については他のQiita記事やタイムインターメディアさんの記事に詳しいです。
動作を考える
たとえば、「身長が160cm以上、体重が70kg以下」というような条件で検索をかけるとなると、こんな感じになります。
Person.where{(height >= 160) & (weight <= 70)}
さて、height
やweight
なんて定義した覚えはないのですが、いったいどうなっているのでしょうか。where
の引数がブロックとなっていることからも予想できるかもしれませんが、じつはこのブロック全体がinstance_eval
で評価されます。そして、存在しない名前についてはmethod_missing
が拾っていって、評価式を組み立てるためのオブジェクトに変換してしまう、という仕組みになっています。
動的に指定…できる?
さて、ちょっと凝ったことをしたくなったので、Squeelで条件とする列名をハードコードするのではなく、シンボルなどで外から与える必要が出てきました。ただ、呼ぶべきものは明示的なメソッドがあるわけではない、method_missing
上の機能なので、一体どうすればいいのでしょうか。
と思って調べてみると、send
でメソッドを呼んだ場合にも、当該メソッドがなければmethod_missing
に回ることが判明しました。ということで、DSL内部でsend
を使ってみることにしました。
column = :height
Person.where{send(column) >= 160}
とりあえず実行時にエラーは起きなかったのですが、これでDBを参照してみるとSQL段階でエラーとなってしまいました。原因を調べるために.to_sql
としてみると、SELECT (中略) WHERE send(height) >= 160
というように、send
がDB関数だと解釈されてしまっていました。
非常用メソッド
ということで、この環境ではsend
すら削除されてmethod_missing
に流れるようになってしまっていました。ただし、send
にはもう1つの名前である__send__
というのがあって、これはBasicObject
にすら用意されているものです。こちらでしてみると、どうなるでしょうか。
column = :height
Person.where{__send__(column) >= 160}
こうすることで、きちんと本来のメソッドまでコードが回るようになりました。
Author And Source
この問題について(Squeelの列名を動的に指定する), 我々は、より多くの情報をここで見つけました https://qiita.com/jkr_2255/items/72bb14ae8aaba2d8defd著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .