Reputation: 7110
I have an update method which is using save and delete methods having their own render calls. Below is the code:
Update Method:
def update_book
self.delete_book
params[:Name] = params[:NewName]
self.save_book
end
Delete Method:
def delete_book
# Do something
rescue BookDoesNotExist => exception
render status: 404
return
end
head 200
end
Save Method:
def save_book
# Do something
rescue BookDoesNotExist => exception
render status: 404
return
end
rescue BookAlreadyExist => exception
render status: 409
return
end
head 200
end
When I run this with some input the system throws "AbstractController::DoubleRenderError
" error along with some message "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return
"
I understand it is because the is throwing exception in both delete and save method with the input I am passing. How do I handle this scenario?
Doing some research I learnt I need to use redirect. So I understand that when my delete method throws exception/renders I should return back and should not call the save method.
Here is what I tried with redirect_to:
Update Method:
def update_book
redirect_to action: self.delete_book and return if status=404 end
params[:Name] = params[:NewName]
self.save_book
end
Am I doing it right or is there any other best way to handle redirect?
Upvotes: 1
Views: 1427
Reputation: 13181
When your delete_book
or save_book
methods receive an exception they will render something and then return, this is correct.
But the return
will give execution back to the caller method, which is update_book
. This caller method have no idea of the exception that occurred and therefore will do its job and render what it has to render.
To fix, your delete_book
or save_book
methods need to return a success status to the caller, like this:
def delete_book
# ... Do something
rescue BookDoesNotExist => exception
render status: 404
return false
end
head 200
true
end
Then your caller method shall check for this status:
def update_book
return unless self.delete_book
params[:Name] = params[:NewName]
return unless self.save_book
end
An alternative that will keep your code clear from those heavy exception testing would be to use your controller'srescue_from
.
In your ApplicationController
:
class ApplicationController < ActionController::Base
rescue_from BookDoesNotExist, with: :redirect_book_not_existing
private
def redirect_book_not_existing
render status: 404
end
end
From now on, very time your controllers receive a BookDoesNotExist
exception, the execution will stop and the render status: 404
line will be executed. Off course you must not rescue
the exceptions in the other method, like this:
def delete_book
# Do something
head 200
end
Your code will be much cleaner
Upvotes: 3