sprockets-railsでNoMethodErrorが発生したので原因の解説と対策を書いておきます。
発生するケース
以下の3つの条件が重なる場合に例外が発生します。
-
app.config.assets.quietがtrue(デフォルト値) -
assetsを参照する - 例えば以下の様に開発環境は独自のloggerを使っており
ActiveSupport::LoggerではなくLoggerクラスのインスタンスを設定している
Rails.application.configure do
config.logger = Logger.new(STDOUT)
end
この状態でassetsを参照しているページにリクエストを行うと以下の様にNoMethodErrorが発生します。
#<NoMethodError: undefined method `silence' for #<Logger:0x0000ffff792b3418 @level=0, @progname=nil, @default_formatter=#<Logger::Formatter:0x0000ffff792b2fb8 @datetime_format=nil>, @formatter=nil, @logdev=#<Logger::LogDevice:0x0000ffff792b29f0 @shift_period_suffix=nil, @shift_size=nil, @shift_age=nil, @filename=nil, @dev=#<IO:<STDOUT>>, @binmode=false, @mon_data=#<Monitor:0x0000ffff792b2950>, @mon_data_owner_object_id=8980>>
::Rails.logger.silence { @app.call(env) }
原因
config.loggerではLoggerクラスのインスタンスを指定できますが、Loggerクラスのインスタンスにはsilenceメソッドが定義されていないためです。
そのためsprockets-railsで例外が発生していました。
def call(env)
if env['PATH_INFO'] =~ @assets_regex
::Rails.logger.silence { @app.call(env) }
else
@app.call(env)
end
end
対応
以下のいずれかの対応することで回避可能です。
1. config.loggerに指定する値はActiveSupport::Loggerのインスタンスに変更する
Rails.application.configure do
- config.logger = Logger.new(STDOUT)
+ config.logger = ActiveSupport::Logger.new(STDOUT)
end
2. app.config.assets.quietをfalseに設定する
Rails.application.configure do
+ config.assets.quiet = false
config.logger = Logger.new(STDOUT)
end
FYI
このケースに遭遇してエラーが発生する場合にNoMethodErrorではなく独自の例外発生とエラーメッセージを出力する対応がされていました。
def raise_logger_silence_error
error = "You have enabled `config.assets.quiet`, but your `Rails.logger`\n"
error << "does not use the `LoggerSilence` module.\n\n"
error << "Please use a compatible logger such as `ActiveSupport::Logger`\n"
error << "to take advantage of quiet asset logging.\n\n"
raise LoggerSilenceError, error
end
当記事の投稿時点ではmasterブランチにはマージされていましたがこちらのコミットが含まれているリリースバージョンはまだリリースされていません。
環境
$ rails -v
Rails 7.0.4.3
$ bundle info sprockets-rails
* sprockets-rails (3.4.2)