RailsでURLのクエリ文字列を組み立てるときはHash#to_queryを使うと捗る

はじめに

Railsでコードを書いていて、ControllerやhelperでURLのクエリ文字列を組み立てる場面ってありますよね。
こうゆうとき、僕は今まで

query = { hoge: 1, fuga: 2 }
query.map{ |k,v| "#{k}=#{v}" }.join("&")

とかやってたんですけど、これだと値が配列の場合に対応してないんですよね。。。
で、もっといいやり方ないかなって探してみたら見つけたので紹介します。

Hash#to_query

Hash#to_queryActiveSupportのHash拡張で、レシーバーのHashインスタンスから、クエリ文字列を組み立てて返してくれます。
Hash#to_paramも同様の挙動です。より正確に言うと、Hash#to_queryHash#to_paramエイリアスです。

実行例

require 'active_support/core_ext'

{ hoge: 1, fuga: 2 }.to_query
# => "fuga=2&hoge=1"
# 何気にアルファベット順になっているのもポイント。

{ hoge: [0,1], fuga: { piyo: 2 } }.to_query
# => "fuga%5Bpiyo%5D=2&hoge%5B%5D=0&hoge%5B%5D=1"
# 配列や入れ子のハッシュにも対応。エスケープもしてくれる。

{ id: 1, name: "hoge" }.to_query("user")
# => "user%5Bid%5D=1&user%5Bname%5D=hoge"
# 引数に文字列(Symbolでも可)を渡すと、名前空間付きになる。

元コード読むのも勉強になる

「たぶん再帰的にto_queryを呼び出しているんだろうな」という予測はつくと思うんですが、実際はもう少し工夫されています。
なかなか勉強になったので、よかったらどう実装されているかチェックしてみてください。

個人的には、Hash#to_param内のcollect do |key, value| ~ end.sort! * '&'という記述にびっくりしました。*1

おわりに

Railsは、こうゆうのないかな〜って思って探してみるとだいたいあるので便利。

*1:do ~ endブロックに対してさらにメソッドチェーンするんだ・・・と。