Rails: Reset rendering/redirecting state - ruby-on-rails

I have an exception handler (registered with rescue_from) which sometimes causes Double Render errors because the real action has already rendered/redirected before the exception is thrown.
To prevent this exception, I'm looking for the modern equivalent of erase_results. It cleared any rendering/redirecting activity in the current request. Not sure why it was deprecated as it seems useful. Anyway, I've tried to reconstruct it by digging into the original source, but some of the detail has changed too, so it would be a hack and I'd rather do it cleanly.

Rails uses controller#response_body to decide if the request is already rendered or redirected. Just set response_body to nil to avoid this error. Note: arguably better way is to prevent multiple rendering or redirecting at first place.
See: #render

Related

Gracefully handle DoubleRenderError / is it possible to "throw out" previous renders?

My rails app has a global rescue method in application_controller which catches any errors and displays a nice looking 500 page.
This works for most exceptions, but I can't find a way gracefully handle a DoubleRenderError. It does not seem possible to render the nice 500 page because attempting to do so will throw a new DoubleRenderError from within the global rescue method.
Does anybody know of a way to "throw out" previous renders so I can render the 500 page?
You should be able to structure your code so that this type of error is impossible - how are you managing to get 2 renders in the same action?

When to use render vs redirect_to when handling error validations

I have a fairly complex view that has multiple forms, lots of validations on those forms, paginations, and other features. When validations fail, I like to use render because then you can be more specific about what errors occurred in the forms. However, when I use render different compiler errors crop up such as "undefined method `total_pages' for []:Array" and "undefined model_name". Is this a situation when I have to use redirect_to or is it feasible to somehow work around the errors that are coming up when the view is being rendered. Thanks a bunch!
You should grasp things in their perspective.
Why is render used instead of redirect:
when you use render, you pass the instantiated object
this object, newly created or updated, received some params
when attempting to save the object, validation was triggered and, if unsuccessful, added errors to the current instance
so your object in memory contains validation errors.
But when you use redirect, you restart with a fresh stack which doesn't know anything about the former object in memory, there could not be any magic:
either the object is saved and you can get the persisted data from database
or if it's not saved, you can have some information you previously stored in session
To answer your question a bit closer: before you use render, you have to instantiate all objects needed by the page.
It's just logic the view fails if the expected instance variables are missing.
First, these are not compiler error - its run-time errors.
Second, you should either check your data in the controller to make sure its being served properly for rendering OR do some conditional blocks in the view in order to cope with this different data structures.
Lastly, redirect_to is just a technique of moving the user around, it could be used here but you still need to handle those errors, even in the redirected-to page...
HTH

before_filter not cancelling action

I'm having trouble getting before filters to work in a Rails app I have recently upgraded from 1.9(?) to 2.3.11. To try and debug it, I have put a before_filter in a controller:
before_filter :false_filter
and the following in application_controller.rb:
def false_filter
puts "false filter running"
false
end
I then call the method from either cucumber/webrat or a browser, and while the filter is getting called (I can see the puts outputting the message), the filter chain isn't getting terminated.
I'm wondering if there's some boilerplate code that hasn't been generated. Can anyone suggest where to look?
Nothing pays any attention to a before-filter's return value. If you want to stop processing, you have to render something from your filter or redirect to somewhere else, from the fine guide:
If a before filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter they are also cancelled.
The same text appears in the 5.2.0 guide.
This behavior does make sense, if the filter chain doesn't complete (i.e. stops filtering part way through) then you'd end up calling controller methods with things not set up the way they were expecting them to be and that would just cause pain, suffering, and confusion and that wouldn't be at all friendly or fun.

What is best strategy to handle exceptions & errors in Rails?

I was wondering if people would share their best practices / strategies on handling exceptions & errors. Now I'm not asking when to throw an exception ( it has been throroughly answered here: SO: When to throw an Exception) . And I'm not using this for my application flow - but there are legitimate exceptions that happen all the time. For example the most popular one would be ActiveRecord::RecordNotFound. What would be the best way to handle it? The DRY way?
Right now I'm doing a lot of checking within my controller so if Post.find(5) returns Nil - I check for that and throw a flash message. However while this is very granular - it's a bit cumbersome in a sense that I need to check for exceptions like that in every controller, while most of them are essentially the same and have to do with record not found or related records not found - such as either Post.find(5) not found or if you are trying to display comments related to post that doesn't exist, that would throw an exception (something like Post.find(5).comments[0].created_at)
I know you can do something like this in ApplicationController and overwrite it later in a particular controller/method to get more granular support, however would that be a proper way to do it?
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordInvalid do |exception|
render :action => (exception.record.new_record? ? :new : :edit)
end
end
Also this would work in case Post.find(5) not found, but what about Post.find(5).comments[0].created_at - I meant I can't throw a full blown exception if the post exists but has not comments, right?
To summarize so far I was doing a lot of manual checking using if/else/unless or case/when ( and I confess occasionally begin/rescue) and checking for nil? or empty?, etc. , but there's got to be a better way it seems.
REPLIES:
#Milan:
Hi Milan
Thanks for a reply - I agree with what you said, and I think I misused the word exception. What I meant is that right now I do a lot of things like:
if Post.exists?(params[:post_id])
#p = Post.find(params[:post_id])
else
flash[:error] = " Can't find Blog Post"
end
And I do a lot of this kind of "exception handling", I try to avoid using begin/rescue. But it seems to me that this is a common enough result/verification/situation that there should be a DRYer way to do this, don't you?
How would you do this kind of check?
Also how would handle it in this case?
Let's say you want to display comment created date in your view:
Last comment for this post at : <%= #post.comments[0].created_at %>
And this post doesn't have any comments.
You can do
Last comment for this post at : <%= #post.comments.last.created_at unless #post.comments.empty? %>
You could do a check in controller. Etc. There are several ways to do it. But what is the "best" way to handle this?
The fact that you do a lot of manual checking for exceptions suggests that you are just not using them right. In fact, none of your examples is exceptional.
As for the non-existing post - you should expect your API users (eg. a user using your web via browser) to ask for non-existing posts.
Your second example(Post.find(5).comments[0].created_at) is not exceptional either. Some posts just don't have comments and you know it up front. So why should that throw an exception?
The same is the case with the ActiveRecord::RecordInvalid example. There's just no reason to handle this case by means of an exception. That a user enters some invalid data into a form is a pretty usual thing and there is nothing exceptional about it.
Using the exception mechanism for these kinds of situations might be very convenient in some situations, but it's incorrect for the reasons mentioned above.
With that said, it doesn't mean you can't DRY the code which encapsulates these situations. There's a pretty big chance that you can do it at least to some extent since these are pretty common situations.
So, what about the exceptions? Well, the first rule really is: use them as sparsely as possible.
If you really need to use them there are two kinds of exceptions in general (as I see it):
exceptions that don't break the user's general workflow inside your app (imagine an exception inside your profile picture thumbnail generation routine) and you can either hide them from the user or you just notify him about the problem and its consequences when neccessary
exceptions that preclude the user from using the app at all. These are the last resort and should be handled via the 500 internal server error in web applications.
I tend to use the rescue_from method in the ApplicationController only for the latter, since there are more appropriate places for the first kind and the ApplicationController as the topmost of the controller classes seems to be the right place to fall back to in such circumstances (although nowadays some kind of Rack middleware might be even more appropriate place to put such a thing).
-- EDIT --
The constructive part:
As for the first thing, my advice would be to start using find_by_id instead of find, since it it doesn't throw an exception but returns nil if unsuccessful. Your code would look something like this then:
unless #p = Post.find_by_id(params[:id])
flash[:error] = "Can't find Blog Post"
end
which is far less chatty.
Another common idiom for DRYing this kind of situations is to use the controller before_filters to set the often used variables (like #p in this case). After that, your controller might look as follows
controller PostsController
before_filter :set_post, :only => [:create, :show, :destroy, :update]
def show
flash[:error] = "Can't find Blog Post" unless #p
end
private
def set_post
#p = Post.find_by_id(params[:id])
end
end
As for the second situation (non-existing last comment), one obvious solution to this problem is to move the whole thing into a helper:
# This is just your way of finding out the time of the last comment moved into a
# helper. I'm not saying it's the best one ;)
def last_comment_datetime(post)
comments = post.comments
if comments.empty?
"No comments, yet."
else
"Last comment for this post at: #{comments.last.created_at}"
end
end
Then, in your views, you'd just call
<%= last_comment_datetime(post) %>
In this way the edge case (post without any comments) will be handled in it's own place and it won't clutter the view.
I know, none of these suggests any pattern for handling errors in Rails, but maybe with some refactorings such as these you'll find that a great deal of the need for some kind of strategy for exception/error handling just disappears.
Exceptions are for exceptional circumstances. Bad user input is typically not exceptional; if anything, it's quite common. When you do have an exceptional circumstance, you want to give yourself as much information as possible. In my experience, the best way to do that is to religiously improve your exception handling based on debugging experience. When you bump into an exception, the very first thing you should do is write a unit test for it. The second thing you should do is determine if there is more information that can be added to the exception. More information in this case usually takes the form of catching the exception higher up the stack and either handling it or throwing a new, more informative exception that has the benefit of additional context. My personal rule is that I don't like catching exceptions from much more than three levels up the stack. If an exception has to travel any further than that, you need to be catching it earlier.
As for exposing errors in the UI, if/case statements are totally OK as long as you don't nest them too deeply. That's when this kind of code gets hard to maintain. You can abstract this if it becomes a problem.
For instance:
def flash_assert(conditional, message)
return true if conditional
flash[:error] = message
return false
end
flash_assert(Post.exists?(params[:post_id]), "Can't find Blog Post") or return

Rails resource_controller with interruption?

I want to use the before action to optionally interrupt the processing if it fails to comply certain criteria (e.g. the object is not owned by such user etc)
I need to use this because I have a more complicated scenario that I
need the object to be loaded before the access rights could be
determined, so I would have situations where I want to interrupt the
action if it is invalid access, anyway I could achieve this?
OK, this is something I was thinking about myself when working with RC.
the usual RC action is something like:
def show
load_object
before :show
response_for :show
rescue ActiveRecord::RecordNotFound
response_for :show_fails
end
So suppose you want to interrupt the show just after load_object if some conditions fail.
The best way to do it that I could think of (except for modifying RC :) is:
use before_filter to check the condition
in the the before_filter use the object or collection helpers (according to the action). this way the load_object/load_collection in the RC action implementation will use the same value cached from your usage of the helper so no extra queries will be made.
Assuming you are referring to before_filter:
Any render or redirect call should abort the filter chain and the execution of the action. So simply put your access control filter after the one to load 'stuff', then render an error message (with an appropriate status code for good web karma and to prevent a w3c beat down).
It's just another area of rails that works fine as long as you don't think too hard.
Any help or am I missing the point?
Vitaly's approach is probably correct, but I have a interesting over-engineered approach too, so to post as a reference:
Use around_filter
At the before hook, throw AccessDeniedException
Capture the exception in the around filter
That would do the job as well.
If you find yourself writing before_filters for precondition checks very often, you might find Aegis useful. It allows you to define access rules in a single file so you can see at a glance who may access what.
It was also built for easy integration with resource_controller.

Resources