Setting status code in Sinatra's custom error block - ruby

I have a custom error message for 400 status code:
get '/do' do
raise ParamXMissingError unless params['x']
end
error ParamXMissingError do
haml :custom_error_page
end
I want ParamXMissingError to be 400, but when I run the above code, and check through Firefox Network tools, it seems Sinatra is actually returning 500, not 400. How do I make it display the custom_error_page.haml and return 400?
Preferably, it'd be nice to have the status code and the page handled from inside the error block, not something that I would sprinkle around when raising ParamXMissingError. For example, this would be repetitive and not a good solution:halt 400, haml(:custom_error_page)

Doh. All I need is to set status 400:
error ParamXMissingError do
status 400
haml :custom_error_page
end

Related

Rescuing error does nothing?

I'm trying to rescue an exception with this method:
def template_deleted
mailchimp_client.templates.info(mailchimp_id)
rescue Mailchimp::InvalidTemplateError => error
puts "Template deleted in Mailchimp: #{error}"
return true
else
return false
end
And no matter what I use to output the message, whether it's STDERR, STDOUT, log.error, p, puts, or print, nothing gets out to the environment's log. This should definitely be returning an error, because the template definitely doesn't exist in Mailchimp.
When I try the same code in the console I can read the error just fine, so either there's something wrong with the rescuing itself (i.e., my method is returning false which it shouldn't), or there's something wrong with the way I'm outputting it.
To output something in the log file of the current environment, use the Rails logger like this:
logger.debug "Template deleted in Mailchimp: #{error}"
You can replace the debug method call with any logging level name, that are briefly described in the link above. Also don't forget to make sure you're running in correct environment!

Sinatra Catching Exceptions for Custom Error Pages

I am trying to handle errors in a Modular Sinatra App. We raise various error through out the app and I wrote some thing alike to catch errors thinking it will happen hierarchically.
My the file I use to error handle looks like the following.
#!/usr/bin/env ruby
# #class class RApp
# #brief Sinatra main routes overloading for App class
class RApp < Sinatra::Base
# #fn not_found do {{{
# #brief Not found error page
not_found do
render_error 404, _('Not Found'), env['sinatra.error'].message
end # }}}
# #fn error ServiceNotAvailableError do {{{
# #brief Handle ServiceNotFoundException, commonly associated with communication
# errors with external required services like Ambrosia, Saba
error ServiceNotAvailableError do
render_error 503, _('Service Not Available'), env['sinatra.error'].message
end # }}}
# #fn error Exception do {{{
# #brief Handle general internal errors
error Exception do
render_error 500, _('Internal Server Error'), env['sinatra.error'].message
end # }}}
error DBC::InvalidUUIDError do
"Invalid UUID Error"
end
# #fn def show_error code, title, message, view = nil {{{
# #brief Displays the proper message (html or text) based on if the request is XHR or otherwise
def render_error code, title, message, view = nil
view = code.to_s if view.nil?
if request.xhr?
halt code, message
else
halt code, slim(:"error_pages/#{view}", locals: {title: title, message: message})
end
end # }}}
# Just for testing
get '/errors/:type' do |type|
raise Object.const_get(type)
end
end # of class RApp < Sinatra::Base }}}
# vim:ts=2:tw=100:wm=100
I was thinking it will try in the order in the file which seem to be true.
How ever the issue is Exception doesn't catch all exceptions.
For example I have DBC::InvalidUUIDError which is as such
DBC::InvalidUUIDErrror < DBC::Error
DBC::Error < RuntimeError
which I understand as in Ruby RuntimeError < Exception
But it error Exception doesn't catch all Exception as I thought.
Am I doing some thing wrong? Or is it not possible to catch all exceptions generally?
Note: Additionally to the answers provided (both works), I had set :raise_errors, true. You need to not set it in development and production. By default it is set for 'test' env.
Nature of the matter was some exceptions been handled and some not.
Add this to prevent Sinatra's own error page from interfering with your custom error handlers:
set :show_exceptions, false
By default, the setting is true in development mode, and false in production.
Note that if you follow the Sinatra readme's advice about setting set :show_exceptions, :after_handler, that will enable your error handlers to run even in development mode (at least for some exception classes), but it will also enable the built-in error page in production for uncaught exceptions. And it's unclear to me which error handlers it will respect, and which ones it will ignore in favor of the built-in debug page.
Edit: I realised you also asked about the order the error handlers are defined in. It doesn't matter; Sinatra looks for exact matches to the exception class in the app class first, and then in its superclasses. If it doesn't find any, it repeats the search for the exception's superclass, etc. So the handler for Exception will only get called for exceptions for which there isn't a closer match.
Here is the relevant info from the Sinatra docs:
Sinatra installs special not_found and error handlers when running under the development environment to display nice stack traces and additional debugging information in your browser.
What this means, is that when you are running in development, Sinatra has a creates a default "catch-all" error handler which has a higher precedence than your error Exception do .. end block.
Once you enter production mode, (or disable in dev via disable :show_exceptions
), your error Exception do .. end block should catch all of your exceptions.
Note that the order of these error blocks defined is irrelevant.

Sinatra - Why does 404 error block never trigger?

Here is a simple app:
class App < Sinatra::Base
set :show_exceptions, false
not_found do
slim :err_404
end
post "/doit" do
user ||= User.find(params["userid"]) || halt(404)
end
end
When given an invalid userid, the 404 error block should trigger and then render the 404 page. Instead, Sinatra shows "Internal Server Error" on the page, and this stacktrace is printed to console:
Problem:
Document(s) not found for class User with id(s) 53d06a8ca7b7d52d11300003.
Summary:
....
I'm guessing the halt(404) isn't being called. I was following this blog post about error handling, so why wouldn't the same thing work here?
Assuming you are using ActiveRecord, User.find(params["userid"]) will raise an ActiveRecord::RecordNotFound exception if there is no matching record. This exception is thrown before the 404 handler gets to run and ends the route handling, resulting in the internal server error.
To fix it you could check for the exception, and call the 404 handler if it is raised:
post "/doit" do
begin
user ||= User.find(params["userid"]
rescue ActiveRecord::RecordNotFound
halt(404)
end
end
A better solution might be to use find_by_id instead, which avoids using exceptions for flow control:
post "/doit" do
user ||= User.find_by_id(params["userid"]) || halt(404)
end

Error handling using aspects in Ruby

I see myself handling similar exceptions in a rather similar fashion repeatedly and would like to use aspects to keep this error handling code outside of the core business logic. A quick search online pulled up a couple of ruby gems (aquarium, aspector, etc) but I don't see a whole lot of downloads for those gems in rubygems. Given that, I want to believe there are probably other nicer ways to deal with this in Ruby.
get '/products/:id' do
begin
product = find_product params[:id]
rescue Mongoid::Errors::DocumentNotFound
status 404
end
end
get '/users/:id' do
begin
user = find_user params[:id]
rescue Mongoid::Errors::DocumentNotFound
status 404
end
end
In the above example, there are 2 Sinatra routes that look for a requested object by ID in MongoDB and throw a 404 if the object were not to be found. Clearly, the code is repetitive and I am looking to find a Ruby way to make it DRY.
You can see answer in this guide.
You code example:
error Mongoid::Errors::DocumentNotFound do
status 404
end

abort in rails loop function

I've got a function in my rails controller (I know, not the rails way, but I find it easier to write in the controller when I have something big like this and then move to the model).
I have an error in a array that I'm looping through, unfortunately, the error is being added somewhere in the loop. It is a big array with lots of properties, and I'm trying to figure out where the error is being caused.
I think I can isolate which object in the array is causing the error, but I can't get it to print.
Aparently ruby has an abort('message') function, but that returns an error in rails.
return render isn't working, it gives me an error that render and/or redirect is being called multiple times. How can I do a php type die in this situation?
This SO Post makes an excellent suggestion.
raise RuntimeError, 'Message goes here'
In the 'Message goes here' section you could even add in the array element:
array.each do |array_element|
<logic>
raise RuntimeError, "#{array_element.inspect}; Message goes here"
end

Resources