アプリケーション作成時に使用した「pluck」メソッドについて、便利な機能だと思っていましたが、どのように処理をしているのか気になったのでRailsのソースコードを読んでみました。
pluckとは
公式ドキュメントでは以下のように記載されています。
pluckは、1つのモデルで使用されているテーブルからカラム (1つでも複数でも可) を取得するクエリを送信するのに使用できます。引数としてカラム名のリストを与えると、指定したカラムの値の配列を、対応するデータ型で返します。
例えば、Productモデルがあったとして、nameカラムの中身を確認したい場合は、
Product.pluck(:name)
=> ["Ruby on Rails Tote",
"Ruby on Rails Bag",
"Ruby on Rails Baseball Jersey",
"Ruby on Rails Jr. Spaghetti",
"Ruby on Rails Ringer T-Shirt",
"Ruby Baseball Jersey",
"Apache Baseball Jersey",
"Ruby on Rails Mug",
"Ruby on Rails Stein"]
といったようにProductモデル内のnameカラムを一覧表示することができます。
Product.pluck(:id,:name)
=> [[1, "Ruby on Rails Tote"],
[2, "Ruby on Rails Bag"],
[3, "Ruby on Rails Baseball Jersey"],
[4, "Ruby on Rails Jr. Spaghetti"],
[5, "Ruby on Rails Ringer T-Shirt"],
[6, "Ruby Baseball Jersey"],
[7, "Apache Baseball Jersey"],
[8, "Ruby on Rails Mug"],
[9, "Ruby on Rails Stein"]]
このように第二引数を設定することも可能です。
pluckの処理内容と、第二引数を設定した場合の処理がどのように行われているか気になったので、ソースコードを見てみることにしました。
Railsのソースコード
def pluck(*keys)
if keys.many?
map { |element| keys.map { |key| element[key] } }
else
key = keys.first
map { |element| element[key] }
end
end
実際に読んでみる①
まず、引数が一つのケースで考えてみます。
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
# => ["David", "Rafael", "Aaron"]
def pluck(*keys)
if keys.many?
map { |element| keys.map { |key| element[key] } }
else
key = keys.first
map { |element| element[key] }
end
end
一行目
(*keys)に、:nameが配列として格納されます。
pluck(*keys): *をつければ引数を複数個設定できる。(可変長引数) 引数は配列として受け取られる。
二行目
keys.many? → 今回、keysは一つだけなのでスルー
many? → 条件を満たす要素が 2 つ以上ある場合に true。
五行目
keysの配列で、最初の要素(:name)を、keyに格納
六行目
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }]
上記の配列をmapメソッドでそれぞれ、key に関連づけられた値を抽出。その後、配列として出力した結果、
=> ["David", "Rafael", "Aaron"]
nameカラムの要素のみ表示できました。
実際に読んでみる②
次に、引数が2つ以上のケースで考えてみます。
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
# => [[1, "David"], [2, "Rafael"]]
def pluck(*keys)
if keys.many?
map { |element| keys.map { |key| element[key] } }
else
key = keys.first
map { |element| element[key] }
end
end
一行目
(*keys)に、:id, :name が配列として格納されます。
二行目
keysが、2つ以上なのでそのまま進みます。
三行目
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }]
まず、上記の配列からmapメソッドで、keys → (:id, :name) に関連づけられた要素を抽出。
そこからさらに、mapメソッドを使って配列の要素それぞれに、上記の処理を行う。
その後、配列として出力した結果、
=> [[1, "David"], [2, "Rafael"]]
idカラムとnameカラムの要素を表示することができました。
##終わりに
ソースコードのどこを読めばいいのかとか、おろそかにしてきた基礎文法の知識の足りなさが露呈したりと、たったこれを読むだけでもかなり苦戦しました。
ただ、今まで公式ドキュメント読んでも、「...?」だった内容が事細かに書いてある(当たり前)ので、ソースコードで確認することで、どういう仕組みか納得して進められるなと思った次第です。
これからも、誰かのソースコードを読むことは必須になってくるので、気になったメソッドとかがあったらソースコードを見てみる癖を付けておきたいなと思うのでした。