例
標準出力に出力するための Logger オブジェクトを作りたい
require 'logger'
logger = Logger.new(STDOUT)
logger.formatter = Logger::Formatter.new
logger.datetime_format = '%Y/%m/%d %H:%M:%S '
logger.info('BONFIRE LIT')
logger.error('YOU DIED')
I, [2019-07-09T21:40:12.663123 #18929] INFO -- : BONFIRE LIT
E, [2019-07-09T21:40:13.036144 #18929] ERROR -- : YOU DIED
このような形で毎回 logger オブジェクトを作るのは大変だから、クラス化しよう
require 'forwardable'
require 'logger'
class StdoutLogger
extend Forwardable
def_delegators :logger, :debug, :info, :warn, :error, :fatal
private
def logger
@logger ||=
Logger.new(STDOUT).tap do |logger|
logger.formatter = Logger::Formatter.new
logger.datetime_format = '%Y/%m/%d %H:%M:%S '
end
end
end
logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')
以前はこのように「Logger オブジェクトである logger
を所有し、info
や error
などのメソッドを logger
に委譲する」というクラスを実装していました。しかし、この方法では委譲したいメソッドが増えた場合、例えば Logger#unknown も委譲したい場合などに def_delegators
に追記しなくてはいけません
そこで便利なのが Kernel#DelegateClass です。
require 'delegate'
require 'logger'
class StdoutLogger < DelegateClass(Logger)
def initialize
super(logger)
end
private
def logger
@logger ||=
Logger.new(STDOUT).tap do |logger|
logger.formatter = Logger::Formatter.new
logger.datetime_format = '%Y/%m/%d %H:%M:%S '
end
end
end
logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')
DelegateClass(Logger)
を継承することで、Logger のインスタンスメソッドはすべて logger
に委譲されます。
ちなみに Logger
クラスを継承する方法では
最後に
DelegateClass
は非常に便利で、例えば Array のラッパークラスを定義したい場合にも使えます。これについては以前書いた
という記事を参照してください。
Logger 関連でよろしければこちらの記事もご覧ください。
追記: 通常の継承の方がいいような……
この場合 Logger クラスを継承する方法でもいいような……。
require 'logger'
class StdoutLogger < Logger
def initialize
super(STDOUT, formatter: Formatter.new, datetime_format: '%Y/%m/%d %H:%M:%S ')
end
end
logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')
違いのひとつとして、Logger クラスのサブクラスかどうかが異なる。
# DelegateClass(Logger) を継承する場合
logger.is_a?(Logger)
#=> false
# Logger を継承する場合
logger.is_a?(Logger)
#=> true
この場合、むしろ Logger オブジェクトであると見なしたほうが適切かもしれない。