ExceptionHandler
is presently the MOST POPULAR exceptions gem for CUSTOM Rails error pages...
With 180,000+ downloads, it is the *only* gem to provide custom 400/500 exception pages for Rails 4 + 5...
Following explains how it works.
If you need further support, please feel free to email rpeck@frontlineutilities.co.uk...
π Introduction
ExceptionHandler
replaces Rails' default error pages with dynamic views...
Works by injecting our own controller into the exceptions_app
middlware hook.
--
Rails' default error pages are static HTML files.
Whilst most don't mind this, it bugged the hell out of me - culminating in the development of this gem.
Over the past 4 years, we've ensured the system is able to operate with the latest version of Rails.
You're now welcome to enjoy the fruits of our labour - with one of the most popular, robust and versatile exception management gems for the Rails framework.
To understand how it works, you need to appreciate how HTTP errors are handled...
π HTTP Error Management
The most important thing to note is that it doesn't matter which errors Ruby/Rails raises - they all need to be wrapped in a valid HTTP response. All transactions online are handled through HTTP, and as such, it pays to understand how it works.
HTTP is a protocol built on top of TCP/IP. It was introduced as a means to manage access to "public" Internet-connected computers - the implication being that certain connected systems did not want to be publicly accessible.
Due to the stateless nature of HTTP, each transaction is treated independently to the others. This means that each time you send a request over the protocol, the recipient system will compile a fresh response each time.
Rails already has an HTTP wrapper to turn exceptions into HTTP responses.
only two are used to denote errors β 4xx
+ 5xx
:
All Rails is really doing is taking "Ruby" errors and giving them an appropriate HTTP status code & message body (HTML).
What confuses most is the way in which Rails does this.
The process is handled by ActionDispatch::ShowExceptions
- middleware which builds a new response out of the erroneous one passed to it by Rails. Through this process, it calls whichever class is present in exceptions_app
...
In other words, what a user sees (in the browser) has very little to do with the error Ruby/Rails experienced.
ExceptionHandler
doesn't change this behaviour - it simply adds our own controller/view to provide the necessary HTML...
βοΈ Middleware-Powered Exceptions
The key with ExceptionHandler
is its integration with the Rack middleware stack.
Most other "exception" gems hack the core Rails system; ours works with Rails to provide a valid set of HTML (using ActionView
and the asset pipeline) without having to compromise the efficiency of the system...
This is important, because the biggest issue for most "exception management" gems is they are simply unable to interface with Rails' view system (and hence cannot show truly custom error pages).
By tapping into the middleware level, the ExceptionHandler
gem negates the need for manual controller/view management.
The following shows how...
β οΈ Installation
# Gem
gem install exception_handler
# Gemfile
gem 'exception_handler', '~> 0.8.0.0'
The gem is available on RubyGems and is fully compatible with Rails 4 + 5.
Installation works by overriding the exceptions_app
hook - which means that it is always present in production.
You have to do NOTHING to get it working in production. Just install the gem and it will provide custom 400/500 exception pages. The 4xx
pages will use your app's standard layout, 5xx
has its own custom layout. Both can be customized.
To get it working in development, we've included a dev
mode, which overrides the consider_all_requests_local
option inside Rails. This is consequential; the core of the gem is robust and works on 1,000's of apps which have graciously chosen to run it.
βοΈ Configuration
π Config π» Dev πΎ Database βοΈ Email π Views π¬ Locales π Layouts βοΈ Custom Exceptions
π Config
The ONLY thing you need to configure ExceptionHandler
is its config
settings.
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.
This is 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 // "you@email.com")
# Custom Exceptions
custom_exceptions: {
#'ActionController::RoutingError' => :not_found # => example
},
# 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
, you 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.
If you have particular options you only wish to run in staging
, or have single options for production
etc, this setup gives you the ability to manage it properly...
π» Dev
As explained, ExceptionHandler
does not work in development
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.
Whilst simple, it's not recommended for extended use. Very good for testing new ideas etc.
πΎ DB
To save exceptions to your database, you're able to set the db
option...
[[ image ]]
Because we use a controller
to manage the underlying way the system works, we're able to invoke the likes of a model
with other functionality.
Ths is done automatically with the latest version of ExceptionHandler
.
To do this, once you've populated the option with either true
or a string
, run rails db:migrate
from your console.
Our new migration system
will automatically run the migration.
# 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.
βοΈ Email
ExceptionHandler
also 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: "your@email.com"
}
Please Note this requires
ActionMailer
. If you don't have any outbound SMTP server,SendGrid
is free.
From version 0.8.0.0
, you're able to define whether email notifications are sent on a per-error basis:
# config/application.rb
config.exception_handlder = {
# This has to be present for any "notification" declarations to work
# Defaults to 'false'
email: "test@test.com",
# Each status code in the new "exceptions" block allows us to define whether email notifications are sent
exceptions: {
:all => { notification: true },
:50x => { notification: false },
500 => { notification: false }
}
}
π 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...
[[ image ]]
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 layout 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.
--
We've also included a number of routes which shows in dev
mode:
π¬ Locales
Locales are used to create interchangeable text (translations/internationalization)...
[[ image ]]
In ExceptionHandler
, we use it to provide the wording for each error which may be shown to users.
By default, the English name of the error is used ("404"
will appear as "Not Found"
) - if you want to create custom messages, you're able to do so by referencing the error's "status_code" within your locales file:
# config/locales/en.yml
en:
exception_handler:
not_found: "Your message here" # -> 404 page
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 an @exception
object we invoke in the controller...
%{message}
is the error's actual message ("XYZ file could not be shown")%{status}
is the error's status code ("Internal Server Error")
--
By default, only internal_server_error
is customized by the gem:
# config/locales/en.yml
en:
exception_handler:
internal_server_error: "<strong>%{status} Error</strong> %{message}"
π Layouts
The most attractive feature of ExceptionHandler
(for most) is its ability to manage layouts
dependent on HTTP status...
[[ image ]]
The reason this is important is due to the way in which Rails works β the "layout" is a "wrapper" for the returned HTML (the "styling" of a page). If you have no layout, you'll end up with the "view" HTML and nothing else.
This means that if you want to change the "look" of a Rails action, you simply have to be able to change the layout
. You should not change the view at all.
To this end, ExceptionHandler
has been designed around providing a SINGLE VIEW for exceptions. This view does not need to change (although you're welcome to use a generator
to do so) - the key is the layout
that's assigned...
4xx
errors are given anil
layout (by default) (inherits fromApplicationController
in your main app)5xx
errors are assigned our ownexception
layout:
# config/application.rb
config.exception_handler = {
# The new syntax allows us to assign different values to each HTTP status code
# At the moment, only 'layout' & 'notification' are supported
# We plan to include several more in the future...
exceptions: {
all: { layout: nil } # -> this will inherit from ApplicationController's layout
}
}
The layout
system has changed between 0.7.7.0
and 0.8.0.0
.
Building on the former's adoption of HTTP status-centric layouts, it is now the case that we have the all
, 5xx
and 4xx
options - allowing us to manage the layouts for blocks of HTTP errors respectively:
# config/application.rb
config.exception_handler = {
# Old (still works)
# No "all" / "4xx"/"5xx" options
layouts: {
500 => 'exception',
501 => 'exception'
},
# New
exceptions: {
:all => { layout: 'exception' },
:4xx => { layout: 'exception' },
:5xx => { layout: 'exception' }, # -> this overrides the :all declaration
500 => { layout: nil } # -> this overrides the 5xx declaration
}
}
We've bundled the exception
layout for 5xx
errors because since these denote internal server errors, it's best to isolate the view system as much as possible. Whilst you're at liberty to change it, we've found it sufficient for most use-cases.
βοΈ Custom Exceptions
As mentioned, Rails' primary role is to convert Ruby exceptions into HTTP errors.
Part of this process involves mapping Ruby/Rails exceptions to the equivalent HTTP status code.
This is done with config.action_dispatch.rescue_responses
...
Whilst this works well, it may be the case that you want to map your own classes to an HTTP status code (default is Internal Server Error
).
If you wanted to keep this functionality inside ExceptionHandler
, you're able to do it as follows:
# config/application.rb
config.exception_handler = {
custom_exceptions: {
'CustomClass::Exception' => :not_found
}
}
Alternatively, you're able to still do it with the default Rails behaviour:
# config/application.rb
config.action_dispatch.rescue_responses = { 'CustomClass::Exception' => :not_found }
πΌ Generators
If you want to edit the controller
, views
, model
or assets
, you're able to invoke them in your own application.
This is done - as with other gems - with a single generator
which takes a series of arguments:
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 ExceptionHandler
's folders put into your app.
Each switch defines which folders you want (EG -v views
will only copy views
dir).
βοΈ Migrations
You DON'T need to generate a migration anymore.
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, runrails 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
You're welcome to contact me directly at rpeck@frontlineutilities.co.uk.
Alternatively, you may wish to post on our GitHub Issues, or StackOverflow.
--
β Changelog
- TBA
- README (focus on utility)
- Introduction of
4xx
,5xx
,:all
for layouts config - Changed
layouts
toexceptions
in config - Email improvement
- Streamlined migration
- Updated model
0.7.0.0
- Wildcard mime types
- Custom exceptions
- Test suite integration
- Model backend
- Sprockets 4+
- New layout
- Readme / wiki overhaul
0.6.5.0
- Streamlined interface
- ActiveRecord / Middleware overhaul
- Supports Sprockets 4+ (
manifest.js
) - Email integration
- Asset overhaul & improvement
- Removed dependencies
0.5.0.0
- Locales
- Email notifications
- Full test suite
- Rails 4.2 & Rails 5.0 native (
request.env
fix) - Controller fixed
-
DB
fixed - Legacy initializer support (more)
- Rails asset management improvement
- Reduced gem file size
0.4.7.0
- New config system
- Fixed controller layout issues
- Streamlined middleware
- New layout & interface
ExceptionHandler
provides custom error pages gem for Rails 4 & 5...
No other gem is as simple or effective at providing branded exception pages in production
β‘οΈ Download & Info β¬ οΈ