Rails: blank page - no errors or stack trace - ruby-on-rails

I've been trying to fix a bug in the Rails app I'm developing, and I keep getting a blank screen with no errors. I haven't found anything helpful in development.log, either (though it does show queries being run and such).
Finally, I started to wonder if it's somehow set not to show errors anymore. I tried commenting out a necessary route, and sure enough, I got a blank page instead of the error and stack trace I expected.
What might cause this?
(I wondered if maybe I'm accidentally running production mode and errors aren't supposed to show then, but development.log is being appended, and if I open script/console and echo ENV['RAILS_ENV'], it says development.)

How I broke it, how I fixed it, and how I learned my lesson
I figured out the problem while lying in bed this morning, and it boils down to "I did something stupid." (Doesn't it always?)
The smart part
First, the smart part: I followed some neat advice about setting up access control and permissions. It gives you some great a great syntax for saying what's allowed, like this:
<%= link_to 'Delete', #photo, :confirm => "Really delete this photo?", :method => :delete if current_user.can_delete?(#photo)%>
(The current_user bit comes from the Restful-authentication plugin.)
The author also shows how to handle the case where a user tries to type in a URL for which you haven't given them a link. It involves setting up a special exception class, which subclasses StandardError, and handling it with something like a 401.html - access denied.
The stupid part
What I did that was stupid was I followed his example blindly. He shows this:
def rescue_action(e)
case e
when SecurityTransgression
head :forbidden
end
end
...which handles the SecurityTransgression fine, but breaks the default error handling in Rails. (I'm sure the author knows this and dealt with it, but he didn't discuss it.)
The solution
The solution was to add two lines:
def rescue_action(e)
case e
when SecurityTransgression
head :forbidden
else
super
end
end
The "else super" part says "if I haven't specified anything here, let the inherited rescue_action method handle it."
Now I'm getting the correct stack trace for my original problem, and will proceed to troubleshoot it.
The lesson: be careful when you mess with exception handling, and make sure the default case still works!

Check your default route and make sure the view isn't just empty.

Related

Rails: Model.find() or Model.find_by_id() to avoid RecordNotFound

I just realized I had a very hard to find bug on my website. I frequently use Model.find to retrieve data from my database.
A year ago I merged three websites causing a lot of redirections that needed to be handled. To do I created a "catch all"-functionality in my application controller as this:
around_filter :catch_not_found
def catch_not_found
yield
rescue ActiveRecord::RecordNotFound
require 'functions/redirections'
handle_redirection(request.path)
end
in addition I have this at the bottom of my routes.rb:
match '*not_found_path', :to => 'redirections#not_found_catcher', via: :get, as: :redirect_catcher, :constraints => lambda{|req| req.path !~ /\.(png|gif|jpg|txt|js|css)$/ }
Redirection-controller has:
def not_found_catcher
handle_redirection(request.path)
end
I am not sure these things are relevant in this question but I guess it is better to tell.
My actual problem
I frequently use Model.find to retrieve data from my database. Let's say I have a Product-model with a controller like this:
def show
#product = Product.find(params[:id])
#product.country = Country.find(...some id that does not exist...)
end
# View
<%= #product.country.name %>
This is something I use in some 700+ places in my application. What I realized today was that even though the Product model will be found. Calling the Country.find() and NOT find something causes a RecordNotFound, which in turn causes a 404 error.
I have made my app around the expectation that #product.country = nil if it couldn't find that Country in the .find-search. I know now that is not the case - it will create a RecordNotFound. Basically, if I load the Product#show I will get a 404-page where I would expect to get a 500-error (since #product.country = nil and nil.name should not work).
My question
My big question now. Am I doing things wrong in my app, should I always use Model.find_by_id for queries like my Country.find(...some id...)? What is the best practise here?
Or, does the problem lie within my catch all in the Application Controller?
To answer your questions:
should I always use Model.find_by_id
If you want to find by an id, use Country.find(...some id...). If you want to find be something else, use eg. Country.find_by(name: 'Australia'). The find_by_name syntax is no longer favoured in Rails 4.
But that's an aside, and is not your problem.
Or, does the problem lie within my catch all in the Application Controller?
Yeah, that sounds like a recipe for pain to me. I'm not sure what specifically you're doing or what the nature of your redirections is, but based on the vague sense I get of what you're trying to do, here's how I'd approach it:
Your Rails app shouldn't be responsible for redirecting routes from your previous websites / applications. That should be the responsibility of your webserver (eg nginx or apache or whatever).
Essentially you want to make a big fat list of all the URLs you want to redirect FROM, and where you want to redirect them TO, and then format them in the way your webserver expects, and configure your webserver to do the redirects for you. Search for eg "301 redirect nginx" or "301 redirect apache" to find out info on how to set that up.
If you've got a lot of URLs to redirect, you'll likely want to generate the list with code (most of the logic should already be there in your handle_redirection(request.path) method).
Once you've run that code and generated the list, you can throw that code away, your webserver will be handling the redirects form the old sites, and your rails app can happily go on with no knowledge of the previous sites / URLs, and no dangerous catch-all logic in your application controller.
That is a very interesting way to handle exceptions...
In Rails you use rescue_from to handle exceptions on the controller layer:
class ApplicationController < ActionController::Base
rescue_from SomeError, with: :oh_noes
private def oh_noes
render text: 'Oh no.'
end
end
However Rails already handles some exceptions by serving static html pages (among them ActiveRecord::RecordNotFound). Which you can override with dynamic handlers.
However as #joshua.paling already pointed out you should be handling the redirects on the server level instead of in your application.

Rails best practices - Controller or model?

I want to use this piece of code to retrieve a user's list of credit cards on file with Stripe to show on his profile (/users/:id)
#stripe_cards = Stripe::Customer.retreive(self.stripe_customer_id).cards.all
Thing is, I'm not exactly sure where (in terms of Rails best practices) it fits. My first tought is to put it in the show method of the User controller since it's not really business logic and doesn't fit in the model. I've also looked at helper methods but they seem (from my understanding) to be used strictly when toying around with HTML.
Can any of you Rails experts chime in?
Thanks!
Francis
Good question. Whenever you see an instance variable in rails (starting with a #), it usually is a view/controller bit of code.
#stripe_cards = Stripe::Customer.retreive(self.stripe_customer_id).cards.all
However looking at the tail end of that
Stripe::Customer.retreive(self.stripe_customer_id).cards.all
This might fit better of in a model, where you can reuse that same line, but have the safety of added error handling and predictable behavior. For example
# user.rb
def stripe_customer_cards
Stripe::Customer.retreive(self.stripe_customer_id).cards.all
rescue Stripe::InvalidRequestError
false # You could use this to render some information in your views, without breaking your app.
end
Also note the use of self. This usually implies use of a Rails model, because calling self in the controller actually refers to the controller, rendering it almost worthless, unless you really know what you are doing.
EDIT
To render an error message, simply write a call to redirect or render, with the alert option.
if #stripe_cards = current_user.stripe_customer_cards
# Your being paid, sweet!
else
# Render alert info :(
render 'my_view', alert: 'This is an alert'
redirect_to other_path, alert: 'Another alert'
end
I also like to make it a point to mention that you should not handle errors just because you can. Don't handle errors you don't expect. If you handle errors you don't expect it will
Confuse users
Make bugs in code harder to fix
Exaggerate the time before an error is recognized
I'd recommend adding a virtual attribute in your User model:
# app/models/user.rb
def cards
Stripe::Customer.retrieve(stripe_customer_id).cards.all # note the spelling of `retrieve`
end
Then, you'd be able to access all a users cards in the following manner:
user = User.first
#=> #<User id:1>
user.cards
#=> [Array of all cards]

Rails refuses to forget code, leaving ssl and initialize method errors

I am noticing a pattern of rails acting as if a line of code is still written once it has been deleted, and I think it may have something to do with changing its defaults too much. I have two examples.
In the first, I set config.force_ssl => true in my config file (a major mistake) and immediately got an error on a page where I was introducing an api via a script tag:
My server gave me an error because the response length of the input wasn't known. I tried enabling streaming in my controller, and it failed. I even tried setting config.force_ssl => false, but this too was useless. So, I deleted the config.force_ssl => true line, but in Firefox, the page with the error continued to route to an "https://" url and then give me the same error. Chromium did not, so I switched to using that, but to this day, I still cannot load the page in Firefox without an error.
Now for the second issue. More recently, I created a model where I wanted to create a custom initialize method with four parameters.
association.rb
def initialize(tag_index, relative_index, type, relevance)
#assigning variables
end
In my controller, I assigned these accordingly.
tags_controller.rb
a = Association.new(id, tag_two.id, type, relevance)
Immediately, I get an error that I have 4 for 2 parameters. Thinking it's just rails being picky, I take away "type" and "relevance." Now, though, I get an error message telling me there is no method 'check_validity!' for class 30:Fixnum. So, I remove the initialize function altogether, and just as before, Rails refuses to recognize that the lines of code have been deleted, giving me errors when I enter parameters for Association.new, and telling me I'm missing parameters when I don't enter any at all.
If anyone can help with the little pieces such as how to fix a response length error with ssl, or how to deal with the 'check_validity!' method, that would be great. Better, though, would be if someone could explain why Rails refuses to let old pieces of code be deleted. This is something that has frustrated me to no end, and I can't find anything on any of these forums about how to fix it.
Thanks so much!

Rails Rspec and capybara - using to perform a, um, sort of test

Check edit at bottom of page
My boss has a sitemap up- it's basically just every route as a link, with a button to click that says "valid?" or "ignore" which will mark it valid or ignore it on the page.
He asked me to manually go through and click each link, test that page isn't a 500 or 404, and then mark it valid if it isn't.
This seems silly to me, as it is basically just a user facing test for working routes.
I could, in the same time, write out routing specs in Rspec for all those, but I guess he wants some sort of documentation that this is happening on the front end for himself and users.
I was thinking a fun way to work around this boring clicking would be to do it with some programming WHILE writing the specs. Makes him happy, and also adds actual value and test to the app that can be reused.
Is there a way to, in a spec, write something like:
links = page.all('a.routing-links)
link.each do |link|
link.click
if page status != 404 || 500
Link.find(id).update_attribute("verified", true)
end
end
I tried putting that in my spec, but when link.click hits an incorrect route, it stops the test (which makes sense, as that route is broken and this is a test.
What I'd like is to be able to take that error and use it to update the attribute of my model.
Am I going about this completely wrong? Any better ideas or inspiration?
Thanks
Edit
I agree with the poster who said this is better left to a script or rake task.
I'm a bit lost on how to write a script that will go to a page, find every link, record its status_code, and then find and update a model. Any suggestions or tips? Ideally it would be run within in the application, so that I could have access to my models and controllers.
Thanks
Personally I wouldn't actually put this in a spec since you're not actually expecting anything to fail.
Instead I'd create a quick script, or even rake task to run through the links as you described.
That being said, this article: http://agileleague.com/2012/12/rails-3-2-custom-error-pages-the-exceptions_app-and-testing-with-capybara/ details how to bypass the normal fail in these circumstances, namely:
In your config/environments/test.rb
config.consider_all_requests_local = false
config.action_dispatch.show_exceptions = true
Though this would affect all tests, which is quite possibly not what you want.
Also, a minor thing that you'd probably figure out in no time when testing this - you'll either need to revisit the list page after clicking the link, or rather relying on link clicks, you could visit the href instead which would be a bit quicker.
links = page.all('a.routing-links')
link.each do |link|
visit link[:href]
if page.status != 404 || 500
Link.find(id).update_attribute("verified", true)
end
end
I haven't tested that, so not sure if it would work like that, but you should be able to get the idea.

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

Resources