I have a rails controller with a rescue_from block in which I call render.
class SomeController < ApplicationController
rescue_from Some::Error, :some_error
private
def some_error error
@error = error
render 'error'
end
end
The strange thing is that even if I have an error.js.erb view, rails will always use error.html.erb, even if the request is a JS:
Started GET /some/1
Processing by SomeController#show as JS
...
Rendered some/error.html.erb
Not how in the shortened log above it says it's rendering as JS, but it still uses the HTML file. The .js.erb is at the right location, and rendering JS views when there's no rescue_from involved works just fine.
What's going on here?
Update 1: I have created a test repository to demonstrate the problem
Update 2 I've found a solution (see below). Can anyone come up with a more generic solution like the ones below, or can you tell my why this would be impossible or a really bad idea? The bounty is still open.
self.formats in ActionController::Rescue.process_action or
Solution
I did some debugging and digging around in the rails sources and found a solution myself:
def error
@error = error
self.formats = request.formats.map(&:ref).compact
render 'error'
end
Explanation
Calling the rescue_from block happens in ActionController::Rescue.process_action. If there is an error, the block will be called. If there is no error, eventually ActionController::Rendering.process_action will get called which just sets self.formats:
def process_action(*) #:nodoc:
self.formats = request.formats.map(&:ref).compact
super
end
Here is a full stacktrace between ActionController::Rescue.process_action and the actual controller action.
#0 TestController.index at /tmp/rescue_from/app/controllers/test_controller.rb:11
#1 ActionController::ImplicitRender.send_action(method#String, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/implicit_render.rb:4
#2 AbstractController::Base.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/abstract_controller/base.rb:198
#3 ActionController::Rendering.process_action(action, *args) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/rendering.rb:10
#4 block in AbstractController::Callbacks.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/abstract_controller/callbacks.rb:20
ͱ-- #5 Proc.call(*args) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:115
#6 ActiveSupport::Callbacks::Filters::End.call(env#ActiveSupport::Callbacks::Filters::Environment) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:115
#7 block (2 levels) in ActiveSupport::Callbacks::CallbackChain.compile at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:553
ͱ-- #8 Proc.call(*args) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:503
#9 ActiveSupport::Callbacks::CallbackSequence.call(*args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:503
#10 ActiveSupport::Callbacks.run_callbacks(kind#Symbol, &block#Proc) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:88
#11 AbstractController::Callbacks.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/abstract_controller/callbacks.rb:19
#12 ActionController::Rescue.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/rescue.rb:29
Here is a full stacktrace when there is an error (thrown in a before_action):
--> #0 TestController.standard_error(error#RuntimeError) at /Users/timou/tmp/rescue_from/app/controllers/test_controller.rb:20
ͱ-- #1 Method.call(*args) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/rescuable.rb:80
#2 ActiveSupport::Rescuable.rescue_with_handler(exception#RuntimeError) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/rescuable.rb:80
#3 ActionController::Rescue.rescue_with_handler(exception#RuntimeError) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/rescue.rb:15
#4 rescue in ActionController::Rescue.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/rescue.rb:32
#5 ActionController::Rescue.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/rescue.rb:29
So my problem was actually that the error is raised in a before_action, which is handled by AbstractController::Callbacks.process_action and happens before ActionController::Rendering.process_action can set self.formats.
If the error is raised in the action itself, self.formats would already be set and the correct view would be rendered without settings self.formats in the rescue_from block.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With