通好みのメソッドpluck
Railsでアプリケーションを組むと、ActiveRecordのインスタンス生成コストが勿体ないな、と感じることが多いと思います。
そんな時の強い味方がpluck
メソッドで、愛用される方も多いと思います。
Rails4からは複数のカラムも指定できて、使い勝手が向上しました。
複数カラムを指定すると、値がArrayのArrayで返って来ます。
例を示すと、例えばこんなデータがあるとすると
- employees
id | name | created_at |
---|---|---|
1 | Taro | 2014-08-01 01:00:05 |
2 | Jiro | 2014-08-01 02:00:06 |
3 | Saburo | 2014-08-01 03:00:07 |
Emplyee.pluck :id, :name
=> [[1, "Taro"],
[2, "Jiro"],
[3, "Saburo"]]
のように返ります。
これはこれで良いのですが、この結果をJSONで返したい時等は、できればHashのArray、つまり
Emplyee.hash_pluck :id, :name
=> [{:id=>1, :name=>"Taro"},
{:id=>2, :name=>"Jiro"},
{:id=>3, :name=>"Saburo"}]
のように返って欲しいです。
これを実現できるように、pluck
メソッドに手を入れました。
ついでに、tableの物理column名と論理column名が異なることもあるだろうと、key名も指定できるようにしています。
前提条件
Rails4.1で動作を確認しました。
できあがったものがこちら
コードの細かい説明は省きます。短時間で書いた、と言い訳を先にしておきます。
module ActiveRecord::Calculations
def pluck_with_keys *column_names
unless (options = column_names.pop).is_a? Hash
return pluck_without_keys *(column_names << options)
end
_pluck = pluck_without_keys *column_names
case keys = options[:keys]
when TrueClass
_pluck.map{|obj| Hash[*column_names.zip(obj).flatten]}
when Array
_pluck.map{|obj| Hash[*keys.zip(obj).flatten]}
else
_pluck
end
end
alias_method_chain :pluck, :keys
end
これをRAILS_ROOT/lib/extensions/calculations.rb
等に保存して、RAILS_ROOT/lib
をautoload_pathに追加した上でinitializer辺りでこのファイルをrequire
します。
使ってみる
既存のシステムにも適用し易いように、元のpluck
メソッドの挙動になるべく影響無いようにしています。
元々pluck
は可変長引数を取りますが、最後の引数がオリジナルのpluck
が取らないHashが来たら動きが変わります。
最後の引数にkeys: true
とすると、指定したcolumn名をkeyにしてHashが帰り、keys: [:employee_id, :employee_name]
のようにkey名の配列を渡すと、そのように良い感じにしてくれます。
実際の動作はこのような感じです。
Employee.pluck :id, :name
=> [[1, "Taro"],
[2, "Jiro"],
[3, "Saburo"]]
Employee.pluck :id, :name, keys: true
=> [{:id=>1, :name=>"Taro"},
{:id=>2, :name=>"Jiro"},
{:id=>3, :name=>"Saburo"}]
Employee.pluck :id, :name, keys: [:employee_id, :employee_name]
=> [{:employee_id =>1, :employee_name=>"Taro"},
{:employee_id =>2, :employee_name=>"Jiro"},
{:employee_id =>3, :employee_name=>"Saburo"}]
key名が足りないと、あるものしか返しません。
Employee.pluck :id, :name, keys: [:employee_id]
=> [{:employee_id =>1},
{:employee_id =>2},
{:employee_id =>3}]
狙った通りに出来ました。
gemにしようかな。