はじめに
Warden::Proxyまわりのエラーを調べてたら、日本語の記事ではRSpecでの記事しか見つからなかったので投稿。
また、この記事は以下のサイトを参考にしております。ちなみに、こちらのサイトではDeviseだけでなく、Clearanceについても言及されています。
Using Rails 5 new renderer with Clearance and Devise
#ActionView::Template::Errorエラー
ApplicationController.renderer.renderを呼び出した際に下記のエラーが発生。
ActionView::Template::Error (Devise could not find the
Warden::Proxy
instance on your request environment.Make sure that your application is loading Devise and Warden as expected and that theWarden::Manager
middleware is present in your middleware stack.
If you are seeing this on one of your tests, ensure that your tests are either executing the Rails middleware stack or that your tests are using theDevise::Test::ControllerHelpers
module to inject therequest.env['warden']
object for you.):
##原因
Rails5のActionController::Rendererを使用して独自のプロキシを定義せずに通常のコントローラーとは別フローで、テンプレートをレンダリングするようです。
そのため、Deviseでの認証に必要なコントローラー環境全体とミドルウェアにアクセスできず、エラーが発生します。
Rendering partials with the new Rails 5 ActionController::Renderer provides a nice shortcut to render templates outside the normal controller flow without defining own proxy classes. This is useful for a variety of use cases, like pushing partials down a MessageBus pipe to frontend or mobile, or PDF generation with WicketPDF.
But to access authentication stuff, like Clearance/Devise (current_user) or CanCan (current_ability) is not obvious, as the whole controller environment and middlewares are missing.
Using Rails 5 new renderer with Clearance and Devise
##対策
ApplicationControllerに、renderer.renderの代替メソッドを用意します。
def self.render_with_signed_in_user(user, *args)
ActionController::Renderer::RACK_KEY_TRANSLATION['warden'] ||= 'warden'
proxy = Warden::Proxy.new({}, Warden::Manager.new({})).tap{|i| i.set_user(user, scope: :user) }
renderer = self.renderer.new('warden' => proxy)
renderer.render(*args)
end
呼び出す際は、ApplicationController.renderer.render(変更前)の代わりにApplicationController.render_with_signed_in_user(変更後)を呼び出します。
# 変更前
ApplicationController.renderer.render('messages/message', locals: { post: @post })
# 変更後
ApplicationController.render_with_signed_in_user(user, 'posts/post', locals: { post: @post })
##参考サイト
今回の記事に載せた対策について
Using Rails 5 new renderer with Clearance and Devise
Renderについて
Render views outside of action