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) endend
class TwitterManager def initialize(logger) @logger = logger endend
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 endend
Import = Dry::AutoInject(SampleContainer)
class Sample include Import['file_logger', 'twitter_manager']end
これで、Sample
に SampleContainer
で登録されているインスタンスを注入できました。次のようにメソッド形式の呼び出しでインスタンスが取得できます。
sample = Sample.newpp sample.file_loggerpp sample.twitter_managerpp 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>