Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

raise ActiveRecord::RecordNotFound (or any 404 status) for invalid date

In my controller controller, I have use Date.new to create a date object to be passed in my ActiveRecord.

end_range = Date.new(params[:year].to_i, params[:month].to_i, params[:day].to_i).end_of_day.to_formatted_s(:db)

The problem with this above is that if a user tried to change the parameters in the URL manually, such as entering 40 for the day param, the Date.new fails (as expected). However, I would rather not have a 500 error if a user typed in something like that, but instead a 404 error (because you will never actually be able a record with a day of 40).

I tried various conditionals (if and unless statements) to raise ActiveRecord::RecordNotFound if that fails, but it returns the 500 error before running the conditional (and therefor never returning a 404).

Does anybody know a better way to handle that, or a way to allow the Date.new to fail more gracefully so that the conditional statement can run?

like image 716
James Pierce Avatar asked Aug 04 '10 17:08

James Pierce


2 Answers

In this case you might rescue one exception and raise another if you'd like to re-map exceptions that aren't handled into the kind that are:

def show
  begin
    end_range = Date.new(...)
  rescue ArgumentError
    # Invalid date
    raise ActiveRecord::RecordNotFound
  end
rescue ActiveRecord::RecordNotFound
  render(:partial => 'not_found', :layout => 'application', :status => :not_found)
end

It might be more efficient to simply render and fail right away though.

def show
  begin
    end_range = Date.new(...)
  rescue ArgumentError
    return render(:partial => 'date_not_found', :layout => 'application', :status => :not_found)
  end
end

You can also do this in a more sweeping sense using the rescue_from method of ApplicationController:

class ApplicationController < ActionController::Base
  rescue_from 'ArgumentError do
    render(:partial => 'exceptions/argument_error', :layout => 'application', :status => :not_found)
  end
end
like image 105
tadman Avatar answered Nov 11 '22 13:11

tadman


I think it makes more sense to rescue an invalid date error with a useful message. This is a common problem when people select 30 February for example. I would rather send them back to the form in question and tell them why they are getting sent back. A 404 would be wrong in most cases, since the error is invalid user input, not an invalid URL.

Further, if you rescue all ArgumentErrors then you won't get notified of other code-related bugs. So if it's not a date error then I'm re-raising the error. Unfortunately we have to change the exception type to avoid a loop.

I've put this in the application_controller:

rescue_from ArgumentError do |exception|
  if exception.message == 'invalid date'
    flash[:error] = exception.message
    redirect_to request.referer ? :back : root_url
  else
    raise StandardError, exception.message, exception.backtrace
  end
end
like image 45
ice cream Avatar answered Nov 11 '22 12:11

ice cream