💣 README Currently Being Updated 💣 - Gem Incorporated into 💀 VPSDeploy.com - VPS Deployment Solution 💀 -
Custom Programming Tutorials + Deployment Infrastructure For Cloud VPS Providers...


Exception Handler


➡️ ExceptionHandler is a Custom Error Pages Gem For Ruby on Rails ⬅️ - With 160,000+ downloads, it is one of the most
popular, robust & extensible exceptions management gems for Ruby on Rails 4 & 5+...

Coverage Status

Version 🏹 0.8.0.0 🏹 has now introduced a number of ⭐️ KEY UPGRADES ⭐️ including...
🚧 New Config Settings 🚧 ▪️ 💻 Custom Views Options 💻 ▪️ 💬 4xx/5xx Locales 💬...

⚠️ Tutorial shows how it works ⚠️
✉️ Support Email ✉️

  Responsive     Branded Error Pages     Middleware Exception Handling     Fully Customizable  

⌚️ Introduction ⌚️

➡️ ExceptionHandler ⬅️ was designed to replace Rails' error pages (400.html, 422.html, 500.html) with dynamic views...

The gem inserts a custom controller into exceptions_app, allowing us to render custom HTML for erroneous requests.

The controller uses a single method/view to build a response to errors. This view remains the same for every exception; the ONLY change is the layout - depending on the HTTP response being returned (4xx/5xx).

The beauty lies in the simplicity through which this is achieved → rather than having many different elements, its SOLE focus is to provide different HTML responses via differing layouts. ExceptionHandler does this within the scope of ActionView, allowing for the use of views, helpers and data from the database.

Gem works 100% out of the box in production, and has the option to be called in dev if necessary.

--

📑 HTTP

The most important thing to understand is that it doesn't matter which errors Ruby/Rails raises - they all need to be wrapped in a valid HTTP response. Due to the nature of HTTP, you only need to facilitate responses for 4xx - 5xx.

This means that all you're really doing is taking "Ruby" errors and giving them an appropriate HTTP status code & message body (HTML). Rails handles the process for you - the only thing we need to worry about is how the HTML is generated.

What confuses most is the way in which Rails does this. The process is handled by ActionDispatch::ShowExceptions - which builds a new response out of the one passed to it by the exception generator. Through this process, it calls whichever class is present in exceptions_app...

# show_exceptions.rb
def render_exception(request, exception)
  backtrace_cleaner = request.get_header "action_dispatch.backtrace_cleaner"
  wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
  status  = wrapper.status_code
  request.set_header "action_dispatch.exception", wrapper.exception
  request.set_header "action_dispatch.original_path", request.path_info
  request.path_info = "/#{status}"
  response = @exceptions_app.call(request.env) #-> this is where the HTML is generated
  response[1]["X-Cascade"] == "pass" ? pass_response(status) : response
rescue Exception => failsafe_error
  $stderr.puts "Error during failsafe response: #{failsafe_error}\n  #{failsafe_error.backtrace * "\n  "}"
  FAILSAFE_RESPONSE
end

In other words, what a user sees has very little to do with the fact Rails experienced an error. ExceptionHandler doesn't change this behaviour - it simply adds our own controller/views setup to provide the HTML...

To better explain, there are 5️⃣ types of HTTP status code - [10x][10x], [20x][20x], [30x][30x], [40x][40x], & [50x][50x].

Each does its own thing, but what's important is they ALL describe "responses" that your web browser will receive for HTTP requests. The only erroneous status codes are 4xx (client error) or 5xx (server error)...

The point is that when you're dealing with "errors" online, you're actually dealing with erroneous HTTP STATUS CODES. The response delivered with these codes is ALWAYS going to remain the same; difference lying in how they're built (on the server).

By default, NGinx + Apache use "static" HTML pages to show the errors - if we're using Rails, we have the ability to create our own pages. This is exactly what our gem has been designed to do.

ExceptionHandler provides Rails with the ability to serve dynamic exception pages, built with your *own* layouts/views. By overriding the exceptions_app hook, it provides a custom controller, model and views to display custom error pages.

The system is 100% compatible with Rails 4 & 5 and has already been downloaded 180,000+ times...

🔨 Setup 🔨

--
The most important thing to appreciate about the gem is that it's designed to be completely unobtrusive.

This means that if you're looking at using it,

Configuration Options Config    Dev Dev    Database Database    Email Email    Views Views    Locales Locales    Custom Exceptions Custom Exceptions    Generators Generators

🚧 Config 🚧

The ONLY thing you need to configure ExceptionHandler is the config.

Whilst the gem works out of the box (without any configuration), if you want to manage the layouts, email, dev or the database, you'll need to set the appropriate values in the config hash (invoked at init).

This can be done in config/application.rb or config/environments/[env].rb

# config/application.rb

module YourApp
  class Application < Rails::Application

    # => This is an example of ALL available config options
    # => You're able to see exactly how it works here:
    # => https://github.com/richpeck/exception_handler/blob/master/lib/exception_handler/config.rb

    # => Config hash (no initializer required)
    config.exception_handler = {
      dev:        nil, # allows you to turn ExceptionHandler "on" in development
      db:         nil, # allocates a "table name" into which exceptions are saved (defaults to nil)
      email:      nil, # sends exception emails to a listed email (string // "[email protected]")

      # On default 5xx error page, social media links included
      social: {        
        facebook: nil, # Facebook page name   
        twitter:  nil, # Twitter handle  
        youtube:  nil, # Youtube channel name / ID
        linkedin: nil, # LinkedIn name
        fusion:   nil  # FL Fusion handle
      },  

      # This is an entirely NEW structure for the "layouts" area
      # You're able to define layouts, notifications etc ↴

      # All keys interpolated as strings, so you can use symbols, strings or integers where necessary
      exceptions: {

        :all => {
          layout: "exception", # define layout
          notification: true, # (false by default)
          deliver: #something here to control the type of response
        },
        :4xx => {
          layout: nil, # define layout
          notification: true, # (false by default)
          deliver: #something here to control the type of response    
        },    
        :5xx => {
          layout: "exception", # define layout
          notification: true, # (false by default)
          deliver: #something here to control the type of response    
        },
        500 => {
          layout: "exception", # define layout
          notification: true, # (false by default)
          deliver: #something here to control the type of response    
        },

        # This is the old structure
        # Still works but will be deprecated in future versions

        501 => "exception",
        502 => "exception",
        503 => "exception",
        504 => "exception",
        505 => "exception",
        507 => "exception",
        510 => "exception"

      }
    }

  end
end

For a full retinue of the available options, you'll be best looking at the config file itself.

--

If using an engine, DON'T need an initializer:

# lib/engine.rb
module YourModule
  class Engine < Rails::Engine

    # => ExceptionHandler
    # => Works in and out of an initializer
    config.exception_handler = {
      dev: nil, # => this will not load the gem in development
      db:  true # => this will use the :errors table to store exceptions
    }
  end

end

The best thing about using a config options block is that you are able to only define the options that you require. This means that if you have particular options you only wish to run in staging, or have single options for production etc...


💻 Dev 💻

As explained, ExceptionHandler does not work in development mode by default. This is because it overrides the exceptions_app middleware hook - which is only invoked in production or staging...

To get it working in development, you need to override the config.consider_all_requests_local setting (a standard component of Rails) - setting it to "false" ↴

This is normally done by changing the setting in your Rails config files. However, to make the process simpler for ExceptionHandler- we've added a dev option which allows you to override the hook through the context of the gem...

# config/application.rb
config.exception_handler = { dev: true }

This disables config.consider_all_requests_local, making Rails behave as it would in production:

![Dev][dev_img]

Obviously, this has other connotations including the likes of making your requests go through your production server etc. For this reason, it's STRONGLY recommended you only use the dev option to test your layouts etc.


💾 Database 💾

If you want to save exceptions to your database, you will need to migrate a new

# config/application.rb
config.exception_handler = { db: true }

This enables ActiveRecord::Base on the Exception class, allowing us to save to the database.

In order for this to work, your db needs the correct table.

To do this, once you've enabled the option, run rails db:migrate from your console. Our new migration system will automatically run the migration.


✉️ Email ✉️

ExceptionHandler also now sends email notifications.

If you want to receive emails whenever your application raises an error, you can do so by adding your email to the config:

# config/application.rb
config.exception_handler = {
  email: "[email protected]"
}

Please Note this requires ActionMailer. If you don't have any outbound SMTP server, SendGrid is free.

Full tutorial here


👓 Views 👓

The views system in ExceptionHandler is modular.

What most people want out of the view is to change the way it looks. This can be done without changing the exception "view" itself...

To better explain, if ExceptionsController is invoked (by exceptions_app), it has ONE method (show). This method calls the show view, which is entirely dependent on the locales for content & the layout for the look.

This means that if you wish to change how the view "looks" - you're either going to want to change your layouts or the locales. There is NO reason to change the show view itself - it's succinct and entirely modular. Whilst you're definitely at liberty to change it, you'll just be making the issue more complicated than it needs to be.

-

If you wish to change the "layout" / "look", there are two options...

  • Firstly, you can create your own layout. This is done by changing the

  • Secondly,


💬 Locales 💬

Locales are used to denote interchangeable text (for different languages).

We've used it for a different purpose - to provide text for our "show" view. The beauty of this is that 1) It's entirely modular & 2) It's extensible (we are able to use as many locales as required)...

[[ locales ]]

The ExceptionHandler view is populated by @exception.description, which pulls from the locales.

If you want custom messages, you need the following. The key is defined by the HTTP status_code

# config/locales/en.yml
en:
  exception_handler:
    not_found: "Your message here"
    unauthorized: "You need to login to continue"
    internal_server_error: "This is a test to show the %{status} of the error"

You get access to %{message} and %{status}, both inferring from @exception.


📋 Layouts 📋

![Layout][layout_img]

If you want to change the various layouts, you need to use the config to set them.

![Layout][layouts_img]

By default, 5xx errors are shown with our [exception layout][layout] - this can be overridden by changing the config to use a layout of your choice. If you want to inherit the ApplicationController layout, assign the codes to nil.


⛔️ Custom Exceptions ⛔️

Custom Exceptions also supported in 0.7.5

Rails handles this for us - [config.action_dispatch.rescue_responses][rescue_responses] ↴

![ActionDispatch][config.action_dispatch.rescue_responses]

You need to add to the rescue_responses hash in your app's config (mapped to status codes):

# config/application.rb
config.action_dispatch.rescue_responses["ActionController::YourError"] = :bad_request

Because HTTP can only process 4xx / 5xx errors, if Rails raises an exception, it needs to assign one of the error status codes. Default is internal_server_error - if you'd prefer your app to just return 500 errors for your custom exception, you don't need to explicitly declare them.


💼 Generators 💼

You can generate ExceptionHandler into your own application.

[[ Generator ]]

The following commands will copy the directories...

rails g exception_handler:views
rails g exception_handler:views -v views
rails g exception_handler:views -v controllers
rails g exception_handler:views -v models
rails g exception_handler:views -v assets
rails g exception_handler:views -v views controllers models assets

If you don't include any switches, this will copy all the folders put into your app.

Each switch defines which folders you want (EG -v views will only copy views dir).


✔️ Migrations (deprecated) ✔️

You DON'T need to generate a migration any more.

From 0.7.5, the migration generator has been removed in favour of our own migration system.

The reason we did this was so not to pollute your migrations folder with a worthless file. Our migration doesn't need to be changed - we only have to get it into the database and the gem takes care of the rest...

If you set the db option in config, run rails db:migrate and the migration will be run.

To rollback, use the following:

rails db:migrate:down VERSION=000000

The drawback to this is that if you remove ExceptionHandler before you rollback the migration, it won't exist anymore. You can only fire the rollback when you have ExceptionHandler installed.

☎️ Support ☎️

🚨 Obviously, if you've taken the time to use the gem, it makes sense to support it 🚨!

The fastest way to get a direct response is via email.

You're also welcome to access our Issues page to contact us directly. You could also use StackOverflow...

⭐ Changelog ⭐

The most important thing to appreciate is...

➡️ ExceptionHandler is designed to provide custom error pages for Ruby on Rails. ⬅️

If you're looking at adding extra functionality (such as a debugger), you'll probably be better looking at the likes of better_errors or gaffe. Whilst we'll certainly look at adding - or integrating - other features (if they're requested), our core intention has always been to provide an exception handling stack that was both simple and customizable.

--

What we've built so far...

👻 1.0.0.0

  • [ ] TBA

🏹 0.8.0.0

  • [x] README (focus on utility)
  • [x] Introduction of 4xx,5xx,:all for layouts config
  • [x] Changed layouts to exceptions in config
  • [x] Email improvement
  • [x] Streamlined migration
  • [x] Updated model

👽 0.7.7.0

⚡ 0.7.0.0

Ⓜ️ 0.6.5.0

  • [x] Streamlined interface
  • [x] ActiveRecord / Middleware overhaul
  • [x] Supports Sprockets 4+ (manifest.js)
  • [x] Email integration
  • [x] Asset overhaul & improvement
  • [x] Removed dependencies

✔️ 0.5.0.0

  • [x] Locales
  • [x] Email notifications
  • [x] Full test suite
  • [x] Rails 4.2 & Rails 5.0 native (request.env fix)
  • [x] Controller fixed
  • [x] DB fixed
  • [x] Legacy initializer support (more)
  • [x] Rails asset management improvement
  • [x] Reduced gem file size

⭕ 0.4.7.0

  • [x] New config system
  • [x] Fixed controller layout issues
  • [x] Streamlined middleware
  • [x] New layout & interface

[![404 + 500 Errors][banner]][rubygems]

ExceptionHandler is now the • LEADING • custom error pages gem for Rails 4 & 5...
No other gem is as simple or effective at providing branded exception pages in production...

Coverage Status

➡️ Download & Info ⬅️

:copyright:

[dev_mode]: readme/dev_mode.jpg [dev_img]: readme/dev.png [layouts_img]: readme/layouts.jpg [layout_img]: readme/layout.png [view_img]: readme/view.jpg [http_codes]: readme/http_codes.png [config]: readme/config.jpg [config.action_dispatch.rescue_responses]: readme/config.action_dispatch.rescue_responses.jpg [banner]: readme/banner.jpg [gem]: readme/gem.jpg [gemfile]: readme/gemfile.jpg [middleware]: readme/middleware.jpg [exceptions_app]: readme/exceptions_app.jpg [view]: readme/titles/view.jpg [dev]: readme/titles/dev.jpg [db]: readme/titles/db.png [support]: readme/titles/support.png "Support" [changelog]: readme/titles/changelog.png "Changelog" [contribution]: readme/titles/contributions.png "Contributions" [fl]: readme/fl.jpg "Frontline Utilities LTD" [profile]: https://avatars0.githubusercontent.com/u/1104431 "R Peck"

[better_errors]: https://github.com/charliesome/better_errors [layout]: app/views/layouts/exception.html.erb [status_codes]: http://guides.rubyonrails.org/layouts_and_rendering.html#the-status-option [stackoverflow]: http://stackoverflow.com/questions/ask?tags=ruby-on-rails+exception-handler [rescue_responses]: http://guides.rubyonrails.org/configuring.html#configuring-action-dispatch [latest]: https://github.com/richpeck/exception_handler/releases/latest [show_exception]: https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_dispatch/middleware/show_exceptions.rb [exception_app]: http://guides.rubyonrails.org/configuring.html#rails-general-configuration [rubygems]: http://rubygems.org/gems/exception_handler [frontlineutilities.co.uk]: http://www.frontlineutilities.co.uk [stackoverflow.com]: http://stackoverflow.com/users/1143732/richard-peck?tab=profile [fork]: #fork-destination-box [pull]: http://github.com/richpeck/exception_handler/pulls [issues]: http://github.com/richpeck/exception_handler/issues

[10x]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#1xx_Informational_responses [20x]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_Success [30x]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection [40x]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_errors [50x]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_Server_errors