dry-containerとdry-auto_injectでDIコンテナを作る
dry-rbファミリーのdry-containerとdry-auto_injectを使うと、POROの組み合わせでDIが実現できます。
DIについてハイパーざっくり理解を得るには次の記事を読めばよいです。

上の記事の中のDIコンテナを適用したコードをdry-containerとdry-auto_injectを使って書き直してみました。
require 'dry-auto_inject'
require 'logger'
class FileLogger
def initialize(filename)
@logger = Logger.new(filename)
end
end
class TwitterManager
def initialize(logger)
@logger = logger
end
end
class DatabaseUserAuthenticator; end
class SampleContainer
extend Dry::Container::Mixin
register 'file_logger' do
FileLogger.new('example.log')
end
register 'twitter_manager' do
TwitterManager.new(FileLogger.new('twitter.log'))
end
register 'database_authenticator' do
DatabaseUserAuthenticator.new
end
end
Import = Dry::AutoInject(SampleContainer)
class Sample
include Import['file_logger', 'twitter_manager']
end
これで、Sample
に SampleContainer
で登録されているインスタンスを注入できました。次のようにメソッド形式の呼び出しでインスタンスが取得できます。
sample = Sample.new
pp sample.file_logger
pp sample.twitter_manager
pp sample.database_authenticator
pp
で中身を確認するとインスタンスが取得できていることがわかります。また、database_authenticator
は Sample
に注入しなかったので取得できずエラーになります。
#<FileLogger:0x00007f84780cb3c8
@logger=
#<Logger:0x00007f84780cb378
@default_formatter=
#<Logger::Formatter:0x00007f84780cb328 @datetime_format=nil>,
@formatter=nil,
@level=0,
@logdev=
#<Logger::LogDevice:0x00007f84780cb2d8
@dev=#<File:example.log>,
@filename="example.log",
@mon_count=0,
@mon_mutex=#<Thread::Mutex:0x00007f84780cb1c0>,
@mon_owner=nil,
@shift_age=0,
@shift_period_suffix="%Y%m%d",
@shift_size=1048576>,
@progname=nil>>
#<TwitterManager:0x00007f84780cab08
@logger=
#<FileLogger:0x00007f84780cadd8
@logger=
#<Logger:0x00007f84780cadb0
@default_formatter=
#<Logger::Formatter:0x00007f84780cad60 @datetime_format=nil>,
@formatter=nil,
@level=0,
@logdev=
#<Logger::LogDevice:0x00007f84780cad10
@dev=#<File:twitter.log>,
@filename="twitter.log",
@mon_count=0,
@mon_mutex=#<Thread::Mutex:0x00007f84780cacc0>,
@mon_owner=nil,
@shift_age=0,
@shift_period_suffix="%Y%m%d",
@shift_size=1048576>,
@progname=nil>>>
Traceback (most recent call last):
container.rb:43:in `<main>': undefined method `database_authenticator' for #<Sample:0x00007fd8149da578> (NoMethodError)
また、別のインスタンスに差し替えることもできます。テストのときはモックに差し替える、というような用途で便利です。
sample = Sample.new(file_logger: Logger.new(STDOUT))
pp sample.file_logger
#<Logger:0x00007f84788cfdd0
@default_formatter=
#<Logger::Formatter:0x00007f84788cfce0 @datetime_format=nil>,
@formatter=nil,
@level=0,
@logdev=
#<Logger::LogDevice:0x00007f84788cfbc8
@dev=#<IO:<STDOUT>>,
@filename=nil,
@mon_count=0,
@mon_mutex=#<Thread::Mutex:0x00007f84788cf010>,
@mon_owner=nil,
@shift_age=nil,
@shift_period_suffix=nil,
@shift_size=nil>,
@progname=nil>