tap
は『selfを引数としてブロックを評価する』『返す値はブロックの評価値ではなくself』という特徴をもつメソッドです。1
今回はtapの利用例について紹介をします。
メソッドチェインの途中結果を確認する
tap
内のブロックの評価値は捨てられるため、メソッドチェインの途中でtap
を利用しても結果に影響を与えません。この特徴を利用すると、メソッドチェインの途中結果をtap
で確認できます。
def tap_example
"hoge".tap {|string| puts "original: #{string}" }
.upcase.tap {|string| puts "upcase: #{string}" }
.reverse.tap {|string| puts "reverse: #{string}" }
end
実行結果
$ rails c
> tap_example
original: hoge
upcase: HOGE
reverse: EGOH
=> "EGOH"
インスタンスの生成と初期設定をまとめて実行し、評価値として返す
new
に対してtap
を利用することで、ブロック内でインスタンスのメソッド実行やプロパティのセットができます。そして、tap
はブロック内で実行された内容が適用されたインスタンスを返します。
たとえば以下のような『インスタンスの作成、初期値のセット、インスタンスの返却』を行うメソッドがあったとします。
class User < ApplicationRecord
def self.build_user(name)
user = User.new
user.name = name
user
end
end
実行結果
$ rails c
> user = User.build_user("Bob")
> user.name
=> "Bob"
上記のコードをtap
で書き換えると以下のようになります。
class User < ApplicationRecord
def self.build_user(name)
User.new.tap do |user|
user.name = name
end
end
end
実行結果
$ rails c
> user = User.build_user("Bob")
> user.name
=> "Bob"
この特性を利用した使用例でよくみかけるのが『Loggerの初期設定をするメソッド』です。
『Loggerの初期設定をするメソッド』をtap
を利用しないで実装した場合、以下のようになります。
class MyLogger
def self.logger
@logger = ActiveSupport::Logger.new(STDOUT)
@logger.formatter = Logger::Formatter.new
@logger.formatter.datetime_format = '%Y-%m-%d %H:%M:%S'
@logger
end
end
実行結果
$ rails c
> require 'my_logger'
> MyLogger.logger.formatter.datetime_format
=> "%Y-%m-%d %H:%M:%S"
上記のコードをtap
で書き換えると以下のようになります。||=
は自己代入演算子です。
class MyLogger
def logger
@logger ||= ActiveSupport::Logger.new(STDOUT).tap do |logger|
logger.formatter = Logger::Formatter.new
logger.formatter.datetime_format = '%Y-%m-%d %H:%M:%S'
end
end
end
実行結果
$ rails c
> require 'my_logger'
> MyLogger.logger.formatter.datetime_format
=> "%Y-%m-%d %H:%M:%S"
オブジェクトを返しつつ、オブジェクトの参照を外す
Rubyのtapはメソッドチェーンだけのものじゃない!では『オブジェクトがセットされたインスタンス変数の参照をはずしつつ、オブジェクトを返す』というメソッドをtap
で実装する方法について紹介しています。
たとえば以下のようなメソッドがあったとします。
# リセット対象のnameを返しつつ、変数@nameを初期化する
def reset_name
name = @name
@name = nil
name
end
実行結果
$ rails c
> @name = 'Bob'
> reset_name
=> "Bob"
> @name
=> nil
tap
はブロックの実行をしますが、self
(tap
のレシーバ)を返します。ですので、上記のコードをtap
で書き換えると以下のようになります。
# self(ここでいう@name.tapの@name)を返しつつ、変数@nameを初期化する
def reset_name
@name.tap { @name = nil }
end
実行結果
$ rails c
> @name = 'Bob'
> reset_name
=> "Bob"
> @name
=> nil
さいごに
Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!