みなさん、Rubyのtap, というメソッドをご存知だろうか。
tapは、Objectのメッソドで、ブロックを引数にとり、ブロックの引数にselfを渡すメソッドだ。
たとえば、以下のように動作する。
"aiueo".tap{|s| puts s} => "aiueo"
ドキュメントを見たとき、多くの人が「は?tapとかwwwこれがどうやって約にたつの?wwww」
と馬鹿にしてスルーするだろう。
だが、馬鹿はお前らだ。tapほどすばらしいメソッドはない。
tapの利用シーン1
以下のようなコードがある。
u = User.new
u.name = 'taro'
u.age = 12
tapを使うと次のようになる。
u = User.new.tap {|u|
u.name = 'taro'
u.age = 12
}
この処理のすばらしさは、ユーザーのプロパティーの初期化のスコープをtapのブロックに閉じる事で、どこまでが一体の処理かを明確にすることができる。
このケースのtapの本質は、tapされるobjectのコンテクストを意識させるスコープを作り出す事にある。
いまそこのrubyのコードに、あるオブジェクトを中心とした処理をひとまとめにしたメソッドを作ろうとしていないだろうか。
例えば、以下のようなケースだ。
class User {
def set_properties(name, age)
this.name = name
this.age = age
end
}
u = new User()
u.set_properties(name, age)
これは、ユーザーのプロパティーをセットするという一連の処理にまとまりの感覚をもたせるためのメソッドだ。
しかし、そのような目的のメソッドなら、必ずしもメソッドをつくらなくとも、tap一つで見かけ上のscopeのまとまりを作れてしまう。
もう一度見てみよう。美しくはなかろうか。
u = User.new.tap {|u|
u.name = 'taro'
u.age = 12
}
ユースケース2 変数をスコープ内に埋め込んでグローバルから消し去る
どういうことか。
例えば、以下のように、ユーザーに紐づく写真を削除する処理を考えてみよう
u = User.find_by(name: 'taro')
id = u.id
pictures = Picture.where(user_id: id)
pictures.each {|picture| picture.destroy }
しかし、この場合目的の処理はpictureの削除だけだ。そのためにグローバルな空間に3つも変数を持つ事になっている。ここでtapを使うと、変数をscopeの内側に入れこみグローバルから消し去ることができる。
User.find_by(name: 'taro').tap {|u|
id = u.id
pictures = Picture.where(user_id: id)
pictures.each {|picture| picture.destroy }
}
どうだろうか。グローバルの変数が全て消えた。エレガントだ。
他にも、変数をスコープに閉じることで、変数名をシンプルにすることができる。
user_id = user.id
Picture.save({parent_id: user_id})
place_id = place.id
Picture.save({parent_id: suer_id})
Tapを用いると、user_idとplace_idなど呼ばず、どちらもidで処理が出来るようになる。
user.id.tap {|id|
Picture.save({parent_id: id})
}
place.id.tap { |id|
Picture.save({parent_id: id})
}
以上がtapの使い道
上で説明したことをまとめると、tapの使い道は
1. 変数をscopeの中にとじる
2. 対象のオブジェクトのコンテクストを意識したブロックを作り処理のまとまりを表現する
とても重要な役割を持つ。
tapが何もしない愚か者に見えるのは、お前らが愚鈍だからということが分かっただろう。
これからtapに敬意を払っていきよ。