├── .coveralls.yml
├── .gitignore
├── .rspec
├── .rspec_status
├── .slugignore
├── .travis.yml
├── Gemfile
├── README.md
├── Rakefile
├── app
├── assets
│ ├── images
│ │ └── exception_handler
│ │ │ ├── alert.jpg
│ │ │ ├── alert.png
│ │ │ ├── connect
│ │ │ ├── facebook.png
│ │ │ ├── fusion.png
│ │ │ ├── linkedin.png
│ │ │ ├── twitter.png
│ │ │ └── youtube.png
│ │ │ ├── favicon.ico
│ │ │ ├── icon.png
│ │ │ ├── noise.png
│ │ │ └── overlay.png
│ └── stylesheets
│ │ ├── exception_handler.css.erb
│ │ └── styles
│ │ ├── _base.css.erb
│ │ ├── _exception.css.erb
│ │ ├── _footer.css.erb
│ │ └── _responsive.css
├── controllers
│ └── exception_handler
│ │ └── exceptions_controller.rb
├── mailers
│ └── exception_handler
│ │ └── exception_mailer.rb
├── models
│ └── exception_handler
│ │ └── exception.rb
└── views
│ ├── exception_handler
│ ├── exceptions
│ │ └── show.html.erb
│ └── mailers
│ │ ├── layout.haml
│ │ ├── layout.text.erb
│ │ └── new_exception.erb
│ └── layouts
│ ├── exception.html.erb
│ ├── mailer.html.erb
│ └── mailer.text.erb
├── config
├── locales
│ └── exception_handler.en.yml
└── routes.rb
├── db
└── migrate
│ └── 000000_create_errors.rb
├── exception_handler.gemspec
├── lib
├── exception_handler.rb
├── exception_handler
│ ├── config.rb
│ ├── engine.rb
│ └── version.rb
└── generators
│ └── exception_handler
│ └── views_generator.rb
├── readme
├── HTTP.png
├── banner.jpg
├── custom_exceptions.png
├── default.png
├── dev.png
├── fl.jpg
├── layouts.jpg
├── local_requests.jpg
├── medium.png
├── middleware.jpg
├── routes.jpg
├── show_exceptions.png
└── title.jpg
└── spec
├── controllers
└── controller_spec.rb
├── dummy
├── .rspec
├── .ruby-version
├── Rakefile
├── app
│ ├── assets
│ │ ├── config
│ │ │ └── manifest.js
│ │ ├── images
│ │ │ └── .keep
│ │ ├── javascripts
│ │ │ ├── application.js
│ │ │ ├── cable.js
│ │ │ └── channels
│ │ │ │ └── .keep
│ │ └── stylesheets
│ │ │ └── application.css
│ ├── channels
│ │ └── application_cable
│ │ │ ├── channel.rb
│ │ │ └── connection.rb
│ ├── controllers
│ │ ├── application_controller.rb
│ │ └── concerns
│ │ │ └── .keep
│ ├── helpers
│ │ └── application_helper.rb
│ ├── jobs
│ │ └── application_job.rb
│ ├── mailers
│ │ └── application_mailer.rb
│ ├── models
│ │ ├── application_record.rb
│ │ └── concerns
│ │ │ └── .keep
│ └── views
│ │ └── layouts
│ │ ├── application.html.erb
│ │ ├── mailer.html.erb
│ │ └── mailer.text.erb
├── bin
│ ├── bundle
│ ├── rails
│ ├── rake
│ ├── setup
│ ├── update
│ └── yarn
├── config.ru
├── config
│ ├── application.rb
│ ├── boot.rb
│ ├── cable.yml
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── development.rb
│ │ ├── production.rb
│ │ └── test.rb
│ ├── initializers
│ │ ├── application_controller_renderer.rb
│ │ ├── assets.rb
│ │ ├── backtrace_silencers.rb
│ │ ├── content_security_policy.rb
│ │ ├── cookies_serializer.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── inflections.rb
│ │ ├── mime_types.rb
│ │ └── wrap_parameters.rb
│ ├── locales
│ │ └── en.yml
│ ├── puma.rb
│ ├── routes.rb
│ └── storage.yml
├── lib
│ └── assets
│ │ └── .keep
├── log
│ └── .keep
├── package.json
├── public
│ ├── 404.html
│ ├── 422.html
│ ├── 500.html
│ ├── apple-touch-icon-precomposed.png
│ ├── apple-touch-icon.png
│ └── favicon.ico
├── spec
│ ├── rails_helper.rb
│ └── spec_helper.rb
└── storage
│ └── .keep
├── features
├── asset_spec.rb
├── config_spec.rb
└── engine_spec.rb
├── mailers
└── mailer_spec.rb
├── models
└── model_spec.rb
├── routing
└── routing_spec.rb
├── spec_helper.rb
└── views
└── views_spec.rb
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | service_name: travis-ci
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | InstalledFiles
8 | _yardoc
9 | coverage
10 | doc/
11 | lib/bundler/man
12 | pkg
13 | rdoc
14 | spec/reports
15 | test/tmp
16 | test/version_tmp
17 | tmp
18 | *.bundle
19 | *.so
20 | *.o
21 | *.a
22 | mkmf.log
23 | *.tmp
24 | .rspec_status
25 | /coverage/
26 |
27 | ##########################################################
28 | ##########################################################
29 |
30 | # http://stackoverflow.com/questions/8865848/comments-in-gitignore
31 |
32 | ##########################################################
33 | ##########################################################
34 |
35 | # Desktop INI (Windows)
36 | desktop.ini
37 |
38 | # Readme Source (PSD's)
39 | readme/source
40 |
41 | # Git BFG
42 | .git.bfg-report
43 | .git.bfg-report/2017-06-29/07-53-00
44 |
45 | # Specs
46 | log/*.log
47 | pkg/
48 | spec/dummy/db/*.sqlite3
49 | spec/dummy/log/*.log
50 | spec/dummy/tmp/
51 | spec/dummy/.sass-cache
52 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format progress
2 | --color
3 | --require spec_helper
4 |
--------------------------------------------------------------------------------
/.rspec_status:
--------------------------------------------------------------------------------
1 | example_id | status | run_time |
2 | ---------------------------------------------- | ------ | --------------- |
3 | ./spec/controllers/controller_spec.rb[1:1:1] | passed | 0.00033 seconds |
4 | ./spec/controllers/controller_spec.rb[1:3:1:1] | passed | 0.00291 seconds |
5 | ./spec/controllers/controller_spec.rb[1:4:1:1] | passed | 0.00057 seconds |
6 | ./spec/features/asset_spec.rb[1:1:1] | passed | 0.00023 seconds |
7 | ./spec/features/asset_spec.rb[1:2:1] | passed | 0.00015 seconds |
8 | ./spec/features/config_spec.rb[1:1:1] | passed | 0.00184 seconds |
9 | ./spec/features/config_spec.rb[1:1:2] | passed | 0.00016 seconds |
10 | ./spec/features/config_spec.rb[1:1:3] | passed | 0.00015 seconds |
11 | ./spec/features/config_spec.rb[1:1:4] | passed | 0.00019 seconds |
12 | ./spec/features/config_spec.rb[1:1:5] | passed | 0.00015 seconds |
13 | ./spec/features/config_spec.rb[1:1:6] | passed | 0.00017 seconds |
14 | ./spec/features/config_spec.rb[1:1:7] | passed | 0.00022 seconds |
15 | ./spec/features/config_spec.rb[1:1:8] | passed | 0.00242 seconds |
16 | ./spec/features/engine_spec.rb[1:1:1:1] | passed | 0.00021 seconds |
17 | ./spec/features/engine_spec.rb[1:1:1:2] | passed | 0.00199 seconds |
18 | ./spec/features/engine_spec.rb[1:1:2:1] | passed | 0.0003 seconds |
19 | ./spec/features/engine_spec.rb[1:1:2:2] | passed | 0.00014 seconds |
20 | ./spec/features/engine_spec.rb[1:2:1:1] | passed | 0.00229 seconds |
21 | ./spec/features/engine_spec.rb[1:2:1:2] | passed | 0.00028 seconds |
22 | ./spec/features/engine_spec.rb[1:2:1:3] | passed | 0.00022 seconds |
23 | ./spec/features/engine_spec.rb[1:2:1:4] | passed | 0.00014 seconds |
24 | ./spec/features/engine_spec.rb[1:2:1:5] | passed | 0.00028 seconds |
25 | ./spec/features/engine_spec.rb[1:2:1:6] | passed | 0.00017 seconds |
26 | ./spec/features/engine_spec.rb[1:2:3:1:1] | passed | 0.00028 seconds |
27 | ./spec/features/engine_spec.rb[1:2:3:2:1] | passed | 0.00206 seconds |
28 | ./spec/features/engine_spec.rb[1:2:4:1] | passed | 0.00024 seconds |
29 | ./spec/routing/routing_spec.rb[1:1:1:1] | passed | 0.01336 seconds |
30 | ./spec/routing/routing_spec.rb[1:1:1:2] | passed | 0.03184 seconds |
31 | ./spec/routing/routing_spec.rb[1:1:2:1] | passed | 0.00636 seconds |
32 | ./spec/routing/routing_spec.rb[1:1:2:2] | passed | 0.00883 seconds |
33 | ./spec/routing/routing_spec.rb[1:2:1:1] | passed | 0.01326 seconds |
34 | ./spec/routing/routing_spec.rb[1:2:1:2] | passed | 0.01605 seconds |
35 | ./spec/routing/routing_spec.rb[1:2:2:1] | passed | 0.00447 seconds |
36 | ./spec/routing/routing_spec.rb[1:2:2:2] | passed | 0.00699 seconds |
37 |
--------------------------------------------------------------------------------
/.slugignore:
--------------------------------------------------------------------------------
1 | # Ignore art files
2 | # https://devcenter.heroku.com/articles/slug-compiler#ignoring-files-with-slugignore
3 | *.psd
4 | *.pdf
5 | /test
6 | /spec
7 | /readme
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 2.2.4
4 | - 2.3.7
5 | - 2.4.4
6 | - 2.5.1
7 | - ruby-head
8 | notifications:
9 | - false
10 | scripts:
11 | - bundle exec rspec
12 | after_script:
13 | - bundle exec rake coveralls:push
14 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in custom_error_pages.gemspec
4 | gemspec
5 |
6 | ###########################################
7 |
8 | #For Testing
9 | group :test do
10 | gem 'coveralls', require: false
11 | end
12 |
13 | # => Required for Windows
14 | gem 'tzinfo-data' if Gem.win_platform? # => TZInfo For Windows
15 |
16 | ###########################################
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 |
5 |
6 |
7 | With 290,000+ downloads, it is the *only* gem to provide custom 400/500 exception pages for Rails 5 & 6
8 |
9 |

10 |
Current 0.8.0.0 (August 2018)
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
36 |
37 |
38 |
39 |
📝 Introduction
40 |
41 |
42 | ---
43 |
44 |
ExceptionHandler
replaces Rails' default error pages with dynamic views.
45 |
It does this by injecting config.exceptions_app
with our controller - allowing us to populate erroneous responses with our own HTML. To understand how this works, you need to appreciate how Rails handles errors:
46 |

47 |
Rails uses ActionDispatch::ShowExceptions
(above) to generate error responses.
48 |
Because web browsers (Rails is a web framework) can only interpret HTTP responses, Ruby/Rails exceptions have to be translated into something a browser can read. This is done by calling the above middleware.
49 |
--
50 |
As highlighted, an HTTP response is built independent of the Rails stack. This includes assigning an HTTP status code and HTML response body. It's the response body which ExceptionHandler
is designed to override.
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
⚠️ Installation
61 |
62 |
63 | ---
64 |
65 |
66 |
67 | 💎 RubyGems (Code) |
68 | 💻 Medium (Tutorial)
69 |
70 |
# Gemfile
71 | gem 'exception_handler', '~> 0.8.0.0'
72 |
Because ExceptionHandler is built around a Rails engine, there is nothing to be done to get it working in production. Installing the Gem should translate your production 4xx/5xx error pages into dynamic views.
73 |
Environments other than production (development/staging) required the dev
variable to be true
.
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
🔧 Configuration
84 |
85 |
86 | ---
87 |
88 |
89 | 📁 Config 💻 Dev 💾 Database ✉️ Email 👓 Views 💬 Locales 📋 Layouts ⛔️ Custom Exceptions
90 |
91 |
92 | ---
93 |
94 |
95 |
96 |
97 | The **ONLY** thing you need to manage `ExceptionHandler` is its [`config`](https://github.com/richpeck/exception_handler/blob/master/lib/exception_handler/config.rb) settings.
98 |
99 | Whilst the gem **works out of the box** (without any configuration), if you want to manage the [`layouts`](#layouts), [`email`](#email), [`dev`](#dev) or the [`database`](#db), you'll need to set the appropriate values in the config hash.
100 |
101 | This is done in `config/application.rb` or `config/environments/[env].rb` ↴
102 |
103 | ```rb
104 | # config/application.rb
105 |
106 | module YourApp
107 | class Application < Rails::Application
108 |
109 | # => This is an example of ALL available config options
110 | # => You're able to see exactly how it works here:
111 | # => https://github.com/richpeck/exception_handler/blob/master/lib/exception_handler/config.rb
112 |
113 | # => Config hash (no initializer required)
114 | config.exception_handler = {
115 | dev: nil, # allows you to turn ExceptionHandler "on" in development
116 | db: nil, # allocates a "table name" into which exceptions are saved (defaults to nil)
117 | email: nil, # sends exception emails to a listed email (string // "you@email.com")
118 |
119 | # Custom Exceptions
120 | custom_exceptions: {
121 | #'ActionController::RoutingError' => :not_found # => example
122 | },
123 |
124 | # On default 5xx error page, social media links included
125 | social: {
126 | facebook: nil, # Facebook page name
127 | twitter: nil, # Twitter handle
128 | youtube: nil, # Youtube channel name / ID
129 | linkedin: nil, # LinkedIn name
130 | fusion: nil # FL Fusion handle
131 | },
132 |
133 | # This is an entirely NEW structure for the "layouts" area
134 | # You're able to define layouts, notifications etc ↴
135 |
136 | # All keys interpolated as strings, so you can use symbols, strings or integers where necessary
137 | exceptions: {
138 |
139 | :all => {
140 | layout: "exception", # define layout
141 | notification: true, # (false by default)
142 | deliver: #something here to control the type of response
143 | },
144 | :4xx => {
145 | layout: nil, # define layout
146 | notification: true, # (false by default)
147 | deliver: #something here to control the type of response
148 | },
149 | :5xx => {
150 | layout: "exception", # define layout
151 | notification: true, # (false by default)
152 | deliver: #something here to control the type of response
153 | },
154 | 500 => {
155 | layout: "exception", # define layout
156 | notification: true, # (false by default)
157 | deliver: #something here to control the type of response
158 | },
159 |
160 | # This is the old structure
161 | # Still works but will be deprecated in future versions
162 |
163 | 501 => "exception",
164 | 502 => "exception",
165 | 503 => "exception",
166 | 504 => "exception",
167 | 505 => "exception",
168 | 507 => "exception",
169 | 510 => "exception"
170 |
171 | }
172 | }
173 | end
174 | end
175 | ```
176 |
177 | For a full retinue of the available options, you'll be best looking at the [`config`](https://github.com/richpeck/exception_handler/blob/master/lib/exception_handler/config.rb) file itself.
178 |
179 | --
180 |
181 | If using an [`engine`](http://guides.rubyonrails.org/engines.html), you **DON'T need an `initializer`**:
182 | ```rb
183 | # lib/engine.rb
184 | module YourModule
185 | class Engine < Rails::Engine
186 |
187 | # => ExceptionHandler
188 | # => Works in and out of an initializer
189 | config.exception_handler = {
190 | dev: nil, # => this will not load the gem in development
191 | db: true # => this will use the :errors table to store exceptions
192 | }
193 | end
194 | end
195 | ```
196 |
197 | The best thing about using a `config` options block is that you are able to only define the options that you require.
198 |
199 | 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...
200 |
201 | ---
202 |
203 |
204 |
205 |
💻 Dev
206 |
207 |
208 | As explained, `ExceptionHandler` does *not* work in `development` by default.
209 |
210 | This is because it overrides the `exceptions_app` middleware hook - which is *only* invoked in `production` or `staging`.
211 |
212 |
213 |
214 |
215 |
216 | To get it working in `development`, you need to override the [`config.consider_all_requests_local`](http://guides.rubyonrails.org/configuring.html#rails-general-configuration) setting (a standard component of Rails) - setting it to "false" ↴
217 |
218 |
219 |
220 |
221 |
222 | 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...
223 |
224 | ```rb
225 | # config/application.rb
226 | config.exception_handler = { dev: true }
227 | ```
228 |
229 | This disables [`config.consider_all_requests_local`](http://guides.rubyonrails.org/configuring.html#rails-general-configuration), making Rails behave as it would in production.
230 |
231 | Whilst simple, it's not recommended for extended use. Very good for testing new ideas etc.
232 |
233 | ---
234 |
235 |
236 |
237 |
💾 DB
238 |
239 |
240 | To save exceptions to your database, you're able to set the `db` option.
241 |
242 | Because we use a `controller` to manage the underlying way the system works, we're able to invoke the likes of a [`model`](https://github.com/richpeck/exception_handler/blob/master/app/models/exception_handler/exception.rb) with other functionality.
243 |
244 | Ths is done automatically with the latest version of `ExceptionHandler`.
245 |
246 | To do this, once you've populated the option with either `true` or a `string`, run `rails db:migrate` from your console.
247 |
248 | Our new [`migration system`](https://github.com/richpeck/exception_handler/tree/readme#migrations) will automatically run the migration.
249 |
250 | ```rb
251 | # config/application.rb
252 | config.exception_handler = { db: true }
253 | ```
254 |
255 | This enables `ActiveRecord::Base` on the [`Exception`](app/models/exception_handler/exception.rb) class, allowing us to save to the database.
256 |
257 | In order for this to work, your db needs the correct table.
258 |
259 | ---
260 |
261 |
262 |
263 |
✉️ Email
264 |
265 |
266 | `ExceptionHandler` also sends **email notifications**.
267 |
268 | If you want to receive emails whenever your application raises an error, you can do so by adding your email to the config:
269 |
270 | ```rb
271 | # config/application.rb
272 | config.exception_handler = {
273 | email: "your@email.com"
274 | }
275 | ```
276 |
277 | > **Please Note** this requires [`ActionMailer`](http://guides.rubyonrails.org/action_mailer_basics.html). If you don't have any outbound SMTP server, [`SendGrid`](http://sendgrid.com) is free.
278 |
279 | From version [`0.8.0.0`](https://github.com/richpeck/exception_handler/releases/tag/v0.8.0.0), you're able to define whether email notifications are sent on a per-error basis:
280 |
281 | ```rb
282 | # config/application.rb
283 | config.exception_handlder = {
284 |
285 | # This has to be present for any "notification" declarations to work
286 | # Defaults to 'false'
287 | email: "test@test.com",
288 |
289 | # Each status code in the new "exceptions" block allows us to define whether email notifications are sent
290 | exceptions: {
291 | :all => { notification: true },
292 | :50x => { notification: false },
293 | 500 => { notification: false }
294 | }
295 | }
296 | ```
297 | ---
298 |
299 |
300 |
301 |
👓 Views
302 |
303 |
304 | What *most* people want out of the view is to change the way it ***looks***. This can be done without changing the "view" itself.
305 |
306 | To better explain, if [`ExceptionsController`](https://github.com/richpeck/exception_handler/blob/master/app/controllers/exception_handler/exceptions_controller.rb) is invoked (by `exceptions_app`), it has **ONE** method ([`show`](https://github.com/richpeck/exception_handler/blob/master/app/controllers/exception_handler/exceptions_controller.rb#L42)).
307 |
308 | This method calls the [`show` view](https://github.com/richpeck/exception_handler/blob/master/app/views/exception_handler/exceptions/show.html.erb), which is *entirely* dependent on the locales for content & the layout for the look.
309 |
310 | This means that if you wish to change how the view "looks" - you're *either* going to want to change your [layout][layouts] or the [*locales*](#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.
311 |
312 | --
313 |
314 | We've also included a number of routes which shows in [`dev`](dev) mode (allowing you to test):
315 |
316 |
317 |
318 |
319 |
320 | ---
321 |
322 |
323 |
324 |
💬 Locales
325 |
326 |
327 | [Locales](https://github.com/richpeck/exception_handler/blob/Readme/config/locales/exception_handler.en.yml) are used to create interchangeable text (translations/internationalization).
328 |
329 | --
330 |
331 | In `ExceptionHandler`, it provides the wording for each type of error code.
332 |
333 | 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"](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L492) within your locales file:
334 |
335 | ```yml
336 | # config/locales/en.yml
337 | en:
338 | exception_handler:
339 | not_found: "Your message here" # -> 404 page
340 | unauthorized: "You need to login to continue"
341 | internal_server_error: "This is a test to show the %{status} of the error"
342 | ```
343 |
344 | You get access to [`%{message}` and `%{status}`](https://github.com/richpeck/exception_handler/blob/master/app/views/exception_handler/exceptions/show.html.erb#L1), both inferring from an [`@exception`](https://github.com/richpeck/exception_handler/blob/master/app/controllers/exception_handler/exceptions_controller.rb#L20) object we invoke in the controller...
345 |
346 | - `%{message}` is the error's actual message ("XYZ file could not be shown")
347 | - `%{status}` is the error's status code ("Internal Server Error")
348 |
349 | --
350 |
351 | By default, only `internal_server_error` is customized by the gem:
352 |
353 | ```yml
354 | # config/locales/en.yml
355 | en:
356 | exception_handler:
357 | internal_server_error: "%{status} Error %{message}"
358 | ```
359 | ---
360 |
361 |
362 |
363 |
📋 Layouts
364 |
365 |
366 | The most attractive feature of `ExceptionHandler` (for most) is its ability to manage [`layouts`](https://guides.rubyonrails.org/layouts_and_rendering.html#structuring-layouts) for HTTP status.
367 |
368 | --
369 |
370 | The reason for this 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, it will render the "view" HTML and nothing else.
371 |
372 | This means 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.
373 |
374 | To this end, `ExceptionHandler` has been designed around providing a [SINGLE VIEW](app/controllers/exception_handler/exceptions_controller.rb#L44) for exceptions. This view does not need to change (although you're welcome to use a [`generator`][generators] to do so) - the key is the `layout` that's assigned...
375 |
376 | - [`4xx`](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_errors) errors are given a `nil` layout (by default) (inherits from `ApplicationController` in your main app)
377 | - [`5xx`](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_Server_errors) errors are assigned our own [`exception`](app/views/layouts/exception.html.erb) layout:
378 |
379 | ```rb
380 | # config/application.rb
381 | config.exception_handler = {
382 |
383 | # The new syntax allows us to assign different values to each HTTP status code
384 | # At the moment, only 'layout' & 'notification' are supported
385 | # We plan to include several more in the future...
386 |
387 | exceptions: {
388 | all: { layout: nil } # -> this will inherit from ApplicationController's layout
389 | }
390 | }
391 | ```
392 |
393 | The `layout` system has changed between [`0.7.7.0`](releases/tag/v0.7.7.0) and [`0.8.0.0`](releases/tag/v0.8.0.0).
394 |
395 | 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:
396 |
397 | ```rb
398 | # config/application.rb
399 | config.exception_handler = {
400 |
401 | # Old (still works)
402 | # No "all" / "4xx"/"5xx" options
403 | layouts: {
404 | 500 => 'exception',
405 | 501 => 'exception'
406 | },
407 |
408 | # New
409 | exceptions: {
410 | :all => { layout: 'exception' },
411 | :4xx => { layout: 'exception' },
412 | :5xx => { layout: 'exception' }, # -> this overrides the :all declaration
413 | 500 => { layout: nil } # -> this overrides the 5xx declaration
414 | }
415 | }
416 | ```
417 |
418 | We've bundled the [`exception`](app/views/layouts/exception.html.erb) 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.
419 |
420 | ---
421 |
422 |
423 |
424 |
⛔️ Custom Exceptions
425 |
426 |
427 | As mentioned, Rails' primary role is to convert Ruby exceptions into HTTP errors.
428 |
429 | Part of this process involves mapping Ruby/Rails exceptions to the equivalent HTTP status code.
430 |
431 | This is done with [`config.action_dispatch.rescue_responses`](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L492).
432 |
433 |
434 |
435 |
436 |
437 | 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`).
438 |
439 | If you wanted to keep this functionality inside `ExceptionHandler`, you're able to do it as follows:
440 |
441 | ```rb
442 | # config/application.rb
443 | config.exception_handler = {
444 | custom_exceptions: {
445 | 'CustomClass::Exception' => :not_found
446 | }
447 | }
448 | ```
449 |
450 | Alternatively, you're able to still do it with the default Rails behaviour:
451 |
452 | ```rb
453 | # config/application.rb
454 | config.action_dispatch.rescue_responses = { 'CustomClass::Exception' => :not_found }
455 | ```
456 | ---
457 |
458 |
459 |
460 |
💼 Generators
461 |
462 |
463 | If you want to edit the `controller`, `views`, `model` or `assets`, you're able to invoke them in your own application.
464 |
465 | This is done - as with other gems - with a single [`generator`](https://github.com/richpeck/exception_handler/blob/master/lib/generators/exception_handler/views_generator.rb) which takes a series of arguments:
466 |
467 | rails g exception_handler:views
468 | rails g exception_handler:views -v views
469 | rails g exception_handler:views -v controllers
470 | rails g exception_handler:views -v models
471 | rails g exception_handler:views -v assets
472 | rails g exception_handler:views -v views controllers models assets
473 |
474 | If you don't include any switches, this will copy **all** `ExceptionHandler`'s folders put into your app.
475 |
476 | Each switch defines which folders you want (EG `-v views` will only copy `views` dir).
477 |
478 | ---
479 |
480 |
481 |
482 |
✔️ Migrations
483 |
484 |
485 | You **DON'T** need to generate a migration anymore.
486 |
487 | From [`0.7.5`](https://github.com/richpeck/exception_handler/releases/tag/0.7.5), the `migration` generator has been removed in favour of our own [migration system](lib/exception_handler/engine.rb#L58).
488 |
489 | 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...
490 |
491 | > If you set the [`db`][db] option in config, run `rails db:migrate` and the migration will be run.
492 |
493 | To rollback, use the following:
494 |
495 | rails db:migrate:down VERSION=000000
496 |
497 | The drawback to this is that if you remove `ExceptionHandler` before you rollback the migration, it won't exist anymore.
498 |
499 | You can **only** fire the `rollback` when you have `ExceptionHandler` installed.
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
☎️ Support
509 |
510 |
511 | ---
512 |
513 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
⭐ Changelog
528 |
529 |
530 | ---
531 |
532 | [**1.0.0.0**](https://github.com/richpeck/exception_handler/releases/tag/v1.0.0.0)
533 | - [ ] TBA
534 |
535 | [**0.8.0.0**](https://github.com/richpeck/exception_handler/releases/tag/v0.8.0.0)
536 | - [x] [README](https://github.com/richpeck/exception_handler/issues/52) (focus on utility)
537 | - [x] Introduction of `4xx`,`5xx`,`:all` for layouts config
538 | - [x] Changed `layouts` to `exceptions` in config
539 | - [x] Email improvement
540 | - [x] Streamlined migration
541 | - [x] Updated model
542 |
543 | [**0.7.7.0**](https://github.com/richpeck/exception_handler/releases/tag/v0.7.7.0)
544 | - [x] [HTTP status layouts](#layouts)
545 |
546 | **0.7.0.0**
547 | - [x] Wildcard mime types
548 | - [x] [Custom exceptions](#custom_exceptions)
549 | - [x] Test suite integration
550 | - [x] [Model backend](#database)
551 | - [x] Sprockets 4+
552 | - [x] New layout
553 | - [x] Readme / wiki overhaul
554 |
555 | **0.6.5.0**
556 | - [x] Streamlined interface
557 | - [x] ActiveRecord / Middleware overhaul
558 | - [x] Supports Sprockets 4+ ([`manifest.js`](http://eileencodes.com/posts/the-sprockets-4-manifest/))
559 | - [x] Email integration
560 | - [x] Asset overhaul & improvement
561 | - [x] Removed dependencies
562 |
563 | **0.5.0.0**
564 | - [x] Locales
565 | - [x] Email notifications
566 | - [x] Full test suite
567 | - [x] Rails 4.2 & Rails 5.0 native ([`request.env`](https://github.com/rails/rails/commit/05934d24aff62d66fc62621aa38dae6456e276be) fix)
568 | - [x] Controller fixed
569 | - [x] `DB` fixed
570 | - [x] Legacy initializer support ([more](https://github.com/richpeck/exception_handler/wiki/1-Setup))
571 | - [x] Rails asset management improvement
572 | - [x] Reduced gem file size
573 |
574 | **0.4.7.0**
575 | - [x] New config system
576 | - [x] Fixed controller layout issues
577 | - [x] Streamlined middleware
578 | - [x] New layout & interface
579 |
580 |
581 |
582 |
583 |
584 |
585 | [![404 + 500 Errors][banner]][rubygems]
586 |
587 |
588 | ExceptionHandler
provides custom error pages gem for Rails 5+
589 |
590 | No other gem is as simple or effective at providing branded exception pages in production
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 | ➡️ Download & Info ⬅️
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 | :copyright:
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 | [banner]:readme/banner.jpg
622 |
623 |
624 | [rubygems]: http://rubygems.org/gems/exception_handler
625 |
626 | [10x]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#1xx_Informational_responses
627 | [20x]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#2xx_Success
628 | [30x]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
629 | [40x]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_errors
630 | [50x]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_Server_errors
631 |
632 |
633 | [db]: #db
634 | [email]: #email
635 | [dev]: #dev
636 | [layouts]: #layouts
637 | [locales]: #locales
638 | [configuration]: #configuration
639 | [generators]: #generators
640 | [custom-exceptions]: #custom-exceptions
641 |
642 |
643 |
644 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rspec/core/rake_task"
3 |
4 | ###########################################
5 |
6 | RSpec::Core::RakeTask.new(:spec)
7 |
8 | task default: :spec
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/alert.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/alert.jpg
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/alert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/alert.png
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/connect/facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/connect/facebook.png
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/connect/fusion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/connect/fusion.png
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/connect/linkedin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/connect/linkedin.png
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/connect/twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/connect/twitter.png
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/connect/youtube.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/connect/youtube.png
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/favicon.ico
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/icon.png
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/noise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/noise.png
--------------------------------------------------------------------------------
/app/assets/images/exception_handler/overlay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/app/assets/images/exception_handler/overlay.png
--------------------------------------------------------------------------------
/app/assets/stylesheets/exception_handler.css.erb:
--------------------------------------------------------------------------------
1 | /* ---------------------------------------------------- */
2 | /* ---------------------------------------------------- */
3 | /* _____ _____ _____ */
4 | /* / __ \/ ___/ ___| */
5 | /* | / \/\ `--.\ `--. */
6 | /* | | `--. \`--. \ */
7 | /* | \__/\/\__/ /\__/ / */
8 | /* \____/\____/\_____/ */
9 | /* */
10 | /* ---------------------------------------------------- */
11 | /* ---------------------------------------------------- */
12 | /*
13 | *= require_self
14 | *= require_tree ./styles
15 | */
16 | /* ---------------------------------------------------- */
17 | /* ---------------------------------------------------- */
18 | /*
19 | *= link_tree ../images
20 | */
21 | /* ---------------------------------------------------- */
22 | /* ---------------------------------------------------- */
23 | /* ---------------------------------------------------- */
24 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/styles/_base.css.erb:
--------------------------------------------------------------------------------
1 | /* ---------------------------------------------------- */
2 | /* ---------------------------------------------------- */
3 | /* Dependencies */
4 | /* ---------------------------------------------------- */
5 | /* ---------------------------------------------------- */
6 |
7 | * { margin: 0; }
8 | html, body { height: 100%; }
9 | html {
10 | height: 100%;
11 | color: #fff;
12 | background: #121212;
13 | background-size: 100% 100%;
14 | box-sizing: border-box;
15 | }
16 | body {
17 | position: relative;
18 | font-family: Helvetica, Arial, Sans-Serif;
19 | font-size: 12px;
20 | text-transform: capitalize;
21 | box-sizing: border-box;
22 | overflow-x: hidden;
23 | }
24 | a { color: #fff; text-decoration: none; }
25 | a:hover { text-decoration: underline; }
26 |
27 | /* ---------------------------------------------------- */
28 | /* ---------------------------------------------------- */
29 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/styles/_exception.css.erb:
--------------------------------------------------------------------------------
1 | /* ---------------------------------------------------- */
2 | /* ---------------------------------------------------- */
3 |
4 | .exception {
5 | display: block;
6 | color: rgba(0,0,0,1);
7 | position: relative;
8 | width: 20%;
9 | top: 48%;
10 | margin: auto;
11 | border: 1px solid rgba(0,0,0,1);
12 | max-width: 400px;
13 | min-width: 350px;
14 | border-radius: 5px;
15 | box-sizing: border-box;
16 | box-shadow: 0px 0px 2px 0px rgba(0,0,0,0.5);
17 | background: rgba(16,0,1,1);
18 | background-image: linear-gradient(to bottom, rgba(16,0,1,1) 70%,rgba(0,0,0,1) 100%);
19 | background-clip: content-box;
20 | overflow-x: hidden;
21 | overflow-y: hidden;
22 | transition: background 0.1s ease-in-out;
23 | transform: translateY(-50%);
24 | z-index: 10;
25 | }
26 | .exception::before {
27 | content: url(<%= asset_url("exception_handler/icon.png") %>) " " attr(data-response);
28 | display: block;
29 | color: rgba(255,255,255,1);
30 | font-size: 1.25em;
31 | font-weight: bold;
32 | text-transform: capitalize;
33 | padding: 1em;
34 | line-height: 10px;
35 | text-shadow: 0 1px 0 rgba(0,0,0,1);
36 | border-bottom: 1px solid rgba(122,11,11,1);
37 | vertical-align: middle;
38 | background-color: rgba(227,11,11,1);
39 | background-image: url(<%= asset_url("exception_handler/noise.png") %>), linear-gradient(to bottom, rgba(227,11,11,0) 0%,rgba(0,0,0,0.55) 100%); /* W3C */
40 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#007a07ed', endColorstr='#000000',GradientType=0 ); /* IE6-9 */
41 | }
42 | .exception::after {
43 | content: attr(data-rails) " \203A\203A Our developers have been notified. Click here to go home.";
44 | display: block;
45 | padding: 1em;
46 | font-size: 0.8em;
47 | line-height: 11px;
48 | color: rgba(255,255,255,1);
49 | text-align: center;
50 | box-sizing: border-box;
51 |
52 | border-width: 0 1px 1px;
53 | border-color: rgba(255,255,255,0.09);
54 | border-style: solid
55 | }
56 | .exception:hover { cursor: pointer; }
57 | .exception:hover:after { text-decoration: underline; }
58 |
59 | .exception span:before {
60 | display: block;
61 | content: url(<%= asset_url("exception_handler/alert.png") %>);
62 | padding: 3.5em 0 1em;
63 | text-align: center;
64 | }
65 | .exception span {
66 | position: relative;
67 | display: block;
68 | min-height: 288px;
69 | color: rgba(255,255,255,1);
70 | padding: 0 2.5em 3em;
71 | box-sizing: border-box;
72 | background-clip: border-box;
73 | text-align: center;
74 | background: url(<%= asset_url("exception_handler/alert.jpg") %>) top center no-repeat;
75 |
76 | border-top: 1px solid rgba(0,0,0,0.05);
77 | border-left: 1px solid rgba(255,255,255,0.08);
78 | border-right: 1px solid rgba(255,255,255,0.08);
79 | }
80 | .exception span:after {
81 | content: "";
82 | display: block;
83 | position: absolute;
84 | left: 0;
85 | top: 0;
86 | width: 100%;
87 | height: 2px;
88 |
89 | background: rgba(0,0,0,0.35);
90 | background: -moz-linear-gradient(top, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0) 100%);
91 | background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(0,0,0,0.35)), color-stop(100%, rgba(0,0,0,0)));
92 | background: -webkit-linear-gradient(top, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0) 100%);
93 | background: -o-linear-gradient(top, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0) 100%);
94 | background: -ms-linear-gradient(top, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0) 100%);
95 | background: linear-gradient(to bottom, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0) 100%);
96 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#000000', endColorstr='#000000', GradientType=0 );
97 | }
98 | .exception span strong {
99 | display: block;
100 | font-size: 2em;
101 | margin: 1em 0 0.25em;
102 | text-transform: uppercase;
103 | }
104 | .exception span p { display: block; margin-top: 1.5em; font-size: 0.85em; text-decoration: underline; }
105 | /* ---------------------------------------------------- */
106 | /* ---------------------------------------------------- */
107 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/styles/_footer.css.erb:
--------------------------------------------------------------------------------
1 | /* ---------------------------------------------------- */
2 | /* ---------------------------------------------------- */
3 |
4 | footer {
5 | display: block;
6 | position: absolute;
7 | bottom: 0;
8 | width: 100%;
9 | font-size: 2em;
10 | text-align: center;
11 | vertical-align: middle;
12 | }
13 | footer a {
14 | display: inline-block;
15 | margin-right: -4px;
16 | padding: 1.5em 0.3em;
17 | vertical-align: middle;
18 | transition: opacity 0.15s ease-in-out;
19 | }
20 | footer a:hover { text-decoration: none; }
21 | footer a img { width: 30px; vertical-align: middle; }
22 |
23 | /* ---------------------------------------------------- */
24 | /* ---------------------------------------------------- */
25 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/styles/_responsive.css:
--------------------------------------------------------------------------------
1 | /* Responsive */
2 | @media screen and (max-width: 950px) {
3 | html { background-size: cover !important; }
4 | .container .error { width: 55% !important; }
5 | }
6 |
--------------------------------------------------------------------------------
/app/controllers/exception_handler/exceptions_controller.rb:
--------------------------------------------------------------------------------
1 | module ExceptionHandler
2 | class ExceptionsController < ApplicationController
3 |
4 | # => Response
5 | # => http://www.justinweiss.com/articles/respond-to-without-all-the-pain/
6 | respond_to :html, :js, :json, :xml
7 |
8 | # => CSRF
9 | protect_from_forgery
10 |
11 | # => Devise
12 | # => http://stackoverflow.com/a/38531245/1143732
13 | skip_before_action :authenticate_user!, raise: false
14 |
15 | ##################################
16 | ##################################
17 |
18 | # => Definitions
19 | # => Exception model (tied to DB)
20 | before_action { |e| @exception = ExceptionHandler::Exception.new request: e.request }
21 | before_action { @exception.save if @exception.valid? && ExceptionHandler.config.try(:db) }
22 |
23 | # => Response format (required for non-standard formats (.css / .gz etc))
24 | # => request.format required until responders updates with wildcard / failsafe (:all)
25 | before_action { |e| e.request.format = :html unless self.class.respond_to.include? e.request.format }
26 |
27 | # => Routes
28 | # => Removes need for "main_app" prefix in routes
29 | # => http://stackoverflow.com/a/40251516/1143732
30 | helper Rails.application.routes.url_helpers
31 |
32 | # => Layout
33 | # => Layouts only 400 / 500 because they are the only error responses (300 is redirect)
34 | # => http://guides.rubyonrails.org/layouts_and_rendering.html#the-status-option
35 | # => Layout proc kills "nil" inheritance, needs to be method for now
36 | layout :layout
37 |
38 | ####################
39 | # Actions #
40 | ####################
41 |
42 | # => General Show Functionality
43 | # => Introduced new "action" config option in 0.8.0.0
44 | def show
45 | respond_with @exception, status: @exception.status
46 | end
47 |
48 | ##################################
49 | ##################################
50 |
51 | private
52 |
53 | # => Pulls from Exception class
54 | # => Spanner in the works is nil
55 | # => .present? validates against empty strings (IE a string is present)
56 | # => .nil? validates to see if the returned data is "nil"
57 | # => nil required to facilitate inheritance of the layout w/ ApplicationController
58 | def layout option = ExceptionHandler.config.options(@exception.status, :layout)
59 | (option.present? || option.nil?) ? option : 'exception'
60 | end
61 |
62 | ##################################
63 | ##################################
64 |
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/app/mailers/exception_handler/exception_mailer.rb:
--------------------------------------------------------------------------------
1 | module ExceptionHandler
2 | class ExceptionMailer < ActionMailer::Base
3 |
4 | # Layout
5 | layout "mailer"
6 |
7 | # Defaults
8 | default from: ExceptionHandler.config.email
9 | default template_path: "exception_handler/mailers" # => http://stackoverflow.com/a/18579046/1143732
10 |
11 | def new_exception e
12 | @exception = e
13 | mail to: ExceptionHandler.config.email
14 | Rails.logger.info "Exception Sent To → #{ExceptionHandler.config.email}"
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/app/models/exception_handler/exception.rb:
--------------------------------------------------------------------------------
1 | module ExceptionHandler
2 |
3 | ############################################################
4 | ############################################################
5 |
6 | # => Search Bots
7 | # => Used in "Exception" class
8 | BOTS = %w(Baidu Gigabot Googlebot libwww-per lwp-trivial msnbot SiteUptime Slurp Wordpress ZIBB ZyBorg Yandex Jyxobot Huaweisymantecspider ApptusBot)
9 |
10 | # => Attributes
11 | # => Determine schema etc
12 | ATTRS = %i(class_name status message trace target referrer params user_agent)
13 |
14 | ############################################################
15 | ############################################################
16 |
17 | # => Class (inheritance dependent on whether db option is available)
18 | self::Exception =
19 | Class.new( (ExceptionHandler.config.try(:db) && defined?(ActiveRecord)) ? ActiveRecord::Base : Object ) do
20 |
21 | # => Include individual elements
22 | # => Only required if no db present (no ActiveRecord)
23 | if ExceptionHandler.config.try(:db)
24 |
25 | # => Set Attrs
26 | def initialize attributes={}
27 | super
28 | ATTRS.each do |type|
29 | self[type] = send(type)
30 | end
31 | end
32 |
33 | else
34 |
35 | # => ActiveModel
36 | include ActiveModel::Model
37 | include ActiveModel::Validations
38 |
39 | # => Callback Extension
40 | extend ActiveModel::Callbacks
41 | define_model_callbacks :initialize, only: :after
42 |
43 | # => Initialize
44 | # => http://api.rubyonrails.org/classes/ActiveModel/Callbacks.html
45 | # => http://stackoverflow.com/a/17682228/1143732
46 | def initialize attributes={}
47 | super
48 | run_callbacks :initialize do
49 | # => Needed for after_initialize
50 | end
51 | end
52 |
53 | end
54 |
55 | ##################################
56 | ##################################
57 |
58 | ####################
59 | # Table #
60 | ####################
61 |
62 | # Schema
63 | ###################
64 | # class_name @exception.class.name
65 | # status ActionDispatch::ExceptionWrapper.new(@request.env, @exception).status_code
66 | # message @exception.message
67 | # trace @exception.backtrace.join("\n")
68 | # target @request.url
69 | # referer @request.referer
70 | # params @request.params.inspect
71 | # user_agent @request.user_agent
72 | # created_at
73 | # updated_at
74 |
75 | # => Table is called "errors"
76 | # => Dev needs to use migration to create db
77 | if ExceptionHandler.config.try(:db)
78 | def self.table_name
79 | ExceptionHandler.config.db
80 | end
81 | end
82 |
83 | ##################################
84 | ##################################
85 |
86 | ####################
87 | # Options #
88 | ####################
89 |
90 | # => Email
91 | # => after_initialize invoked after .new method called
92 | # => Should have been after_create but user may not save
93 | after_initialize -> (e) { ExceptionHandler::ExceptionMailer.new_exception(e).deliver }, if: :email? # => see bottom of file
94 |
95 | # => Attributes
96 | attr_accessor :request, :klass, :exception, :description
97 | attr_accessor *ATTRS unless ExceptionHandler.config.try(:db)
98 |
99 | # => Validations
100 | validates :user_agent, format: { without: Regexp.new( BOTS.join("|"), Regexp::IGNORECASE ) }
101 |
102 | ##################################
103 | ##################################
104 |
105 | ####################################
106 | # Virtual
107 | ####################################
108 |
109 | # => Exception (virtual)
110 | # => Basis on which all the class is built
111 | def exception
112 | request.env['action_dispatch.exception']
113 | end
114 |
115 | # => Klass
116 | # => Used for validation (needs to be cleaned up in 0.7.0)
117 | def klass
118 | exception.class
119 | end
120 |
121 | # => Description
122 | def description
123 | I18n.with_options scope: [:exception_handler], message: message, status: status do |i18n|
124 | i18n.t response, default: Rack::Utils::HTTP_STATUS_CODES[status]
125 | end
126 | end
127 |
128 | ####################################
129 | # Exception
130 | ####################################
131 |
132 | # => Class Name
133 | def class_name
134 | exception.class.name
135 | end
136 |
137 | # => Message
138 | def message
139 | exception ? exception.message : Rack::Utils::HTTP_STATUS_CODES[status]
140 | end
141 |
142 | # => Trace
143 | def trace
144 | exception.backtrace.join("\n")
145 | end
146 |
147 | ####################################
148 | # Request
149 | ####################################
150 |
151 | # => Target URL
152 | def target
153 | request.url
154 | end
155 |
156 | # => Referrer URL
157 | def referer
158 | request.referer
159 | end
160 |
161 | # => Params
162 | def params
163 | request.params.inspect
164 | end
165 |
166 | # => User Agent
167 | def user_agent
168 | request.user_agent
169 | end
170 |
171 | ####################################
172 | # Other
173 | ####################################
174 |
175 | # => Status code (404, 500 etc)
176 | def status
177 | exception ? ActionDispatch::ExceptionWrapper.new(request.env, exception).try(:status_code) : request.env["PATH_INFO"][1..-1].to_i
178 | end
179 |
180 | # => Server Response ("Not Found" etc)
181 | def response
182 | ActionDispatch::ExceptionWrapper.rescue_responses[class_name]
183 | end
184 |
185 | ##################################
186 | ##################################
187 |
188 | private
189 |
190 | # => Email
191 | # => should be on the same line as after_initialize but too long
192 | def email?
193 | ExceptionHandler.config.try(:email).try(:is_a?, String) && ExceptionHandler.config.options(status, :notification) != false
194 | end
195 |
196 | end
197 | end
198 |
199 | ############################################################
200 | ############################################################
201 |
--------------------------------------------------------------------------------
/app/views/exception_handler/exceptions/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= content_tag :div, class: "exception", data: { status: @exception.status, response: @exception.response.to_s.humanize, rails: Rails.version }, onclick: ("location.href=\"#{root_url}\";" if @exception.status.to_s.first == "5" && Rails.application.routes.recognize_path("/")) do %>
2 | <%= content_tag :span, @exception.description.html_safe %>
3 | <% end %>
4 |
--------------------------------------------------------------------------------
/app/views/exception_handler/mailers/layout.haml:
--------------------------------------------------------------------------------
1 | !!!
2 | %html
3 | %head
4 | %meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"}/
5 | :css
6 | /* Email styles need to be inline */
7 | %body
8 | = yield
9 |
--------------------------------------------------------------------------------
/app/views/exception_handler/mailers/layout.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/app/views/exception_handler/mailers/new_exception.erb:
--------------------------------------------------------------------------------
1 | Rails Error (<%= Rails.env.titleize %>)
2 |
3 | <%= @exception.response %> (<%= @exception.status %>)
4 | <%= @exception.message %>
5 |
--------------------------------------------------------------------------------
/app/views/layouts/exception.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= content_tag :title, "Error - #{@exception.status} (#{@exception.response.to_s.gsub("_", " ").titleize})" %>
7 |
8 |
9 | <%= stylesheet_link_tag :exception_handler %>
10 | <%= favicon_link_tag "exception_handler/favicon.ico" %>
11 |
12 | <% if x = ExceptionHandler.config.options(@exception.status) %>
13 |
14 | <% end %>
15 |
16 |
17 | <%= csrf_meta_tags %>
18 |
19 |
20 |
21 | <%= content_tag :body, yield %>
22 |
23 |
24 | <% if ExceptionHandler.config.try(:social) %>
25 | <%= content_tag :footer do %>
26 | <% ExceptionHandler.config.social.each do |k,v| %>
27 | <%= link_to image_tag("exception_handler/connect/#{k}.png"), ExceptionHandler::Config::SOCIAL[k] + "/" + v, id: k, title: "#{k.to_s.titleize} Feed", target: :blank if v %>
28 | <% end %>
29 | <% end %>
30 | <% end %>
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= yield %>
5 |
6 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/config/locales/exception_handler.en.yml:
--------------------------------------------------------------------------------
1 | #######################################################################################
2 | #######################################################################################
3 | ## _____ _ _ _ _ _ _ ##
4 | ## | ___| | | (_) | | | | | | | ##
5 | ## | |____ _____ ___ _ __ | |_ _ ___ _ __ | |_| | __ _ _ __ __| | | ___ _ __ ##
6 | ## | __\ \/ / __/ _ \ '_ \| __| |/ _ \| '_ \ | _ |/ _` | '_ \ / _` | |/ _ \ '__| ##
7 | ## | |___> < (_| __/ |_) | |_| | (_) | | | | | | | | (_| | | | | (_| | | __/ | ##
8 | ## \____/_/\_\___\___| .__/ \__|_|\___/|_| |_| \_| |_/\__,_|_| |_|\__,_|_|\___|_| ##
9 | ## | | ##
10 | ## |_| ##
11 | #######################################################################################
12 | #######################################################################################
13 |
14 | # Have to use strings rather than integers
15 | # https://github.com/rack/rack/blob/1.5.2/lib/rack/utils.rb#L544
16 |
17 | # By default, exceptions will show their "response" code (404 = "Not Found")
18 | # If you want to create custom messages, you have to assign the values as [response]: "message"
19 |
20 | #######################################################################################
21 | #######################################################################################
22 |
23 | en:
24 | exception_handler:
25 | internal_server_error: "%{status} Error %{message}"
26 |
27 | #######################################################################################
28 | #######################################################################################
29 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | ########################################
2 | ########################################
3 | ## _____ _ ##
4 | ## | ___ \ | | ##
5 | ## | |_/ /___ _ _| |_ ___ ___ ##
6 | ## | // _ \| | | | __/ _ \/ __| ##
7 | ## | |\ \ (_) | |_| | || __/\__ \ ##
8 | ## \_| \_\___/ \__,_|\__\___||___/ ##
9 | ## ##
10 | ########################################
11 | ########################################
12 |
13 | ## Good resource
14 | ## https://gist.github.com/maxivak/5d428ade54828836e6b6#merge-engine-and-app-routes
15 |
16 | ########################################
17 | ########################################
18 |
19 | ## Routes ##
20 | Rails.application.routes.draw do
21 |
22 | ########################################
23 | ########################################
24 |
25 | # => ExceptionHandler
26 | # => Used to provide error page examples in "dev" mode
27 | if Object.const_defined?('ExceptionHandler') && ExceptionHandler.config.try(:dev)
28 |
29 | # => Items
30 | Rack::Utils::SYMBOL_TO_STATUS_CODE.select{ |key, value| value.to_s.match('\b(?:4[0-9]{2}|5[0-9]{2}|599)\b') }.each do |code, status|
31 | get status.to_s, to: 'exception_handler/exceptions#show', as: code, code: code
32 | end
33 |
34 | end
35 |
36 | ########################################
37 | ########################################
38 |
39 | end
40 |
41 | ########################################
42 | ########################################
43 |
--------------------------------------------------------------------------------
/db/migrate/000000_create_errors.rb:
--------------------------------------------------------------------------------
1 | class CreateErrors < ActiveRecord::Migration[5.0]
2 |
3 | # => ATTRS
4 | require_relative "../../app/models/exception_handler/exception.rb"
5 |
6 | #########################################
7 | #########################################
8 |
9 | # => Defs
10 | @@table = ExceptionHandler.config.try(:db)
11 |
12 | #########################################
13 |
14 | # Up
15 | def up
16 | create_table @@table do |t|
17 | ExceptionHandler::ATTRS.each do |attr|
18 | t.text attr
19 | end
20 | t.timestamps
21 | end
22 | end
23 |
24 | #########################################
25 |
26 | # Down
27 | def down
28 | drop_table @@table, if_exists: true
29 | end
30 |
31 | #########################################
32 | #########################################
33 |
34 | end
35 |
--------------------------------------------------------------------------------
/exception_handler.gemspec:
--------------------------------------------------------------------------------
1 | ########################################################################################
2 | ########################################################################################
3 |
4 | # => Version
5 | require_relative 'lib/exception_handler/version'
6 |
7 | ##############################################################
8 | ##############################################################
9 |
10 | ## Specs ##
11 | Gem::Specification.new do |s|
12 |
13 | ## General ##
14 | s.name = "exception_handler"
15 | s.authors = ["R.Peck"]
16 | s.email = ["rpeck@fl.co.uk"]
17 | s.version = ExceptionHandler::VERSION::STRING
18 | s.platform = Gem::Platform::RUBY
19 |
20 | ## Details ##
21 | s.summary = %q{Rails gem to show custom error pages in production. Also logs errors in db & sends notification emails}
22 | s.description = %q{Rails gem to create custom error pages. Captures exceptions using "exception_app" callback, routing to "Exception" controller, rendering the view as required.}
23 | s.homepage = "https://github.com/richpeck/exception_handler"
24 |
25 | ## License ##
26 | s.license = "MIT"
27 |
28 | ##############################################################
29 | ##############################################################
30 |
31 | ## Files ##
32 | s.files = `git ls-files -z`.split("\x0")
33 | s.files.reject! { |fn| fn.include? "readme" } #-> https://github.com/gauntlt/gauntlt/blob/master/gauntlt.gemspec#L16
34 |
35 | s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
36 | s.test_files = s.files.grep(%r{^(test|spec|features)/}) unless RUBY_VERSION >= "2.2.0" #-> deprecated in Ruby 2.2.0
37 | s.require_paths = ["lib"]
38 |
39 | ##############################################################
40 | ##############################################################
41 |
42 | ## Ruby ##
43 | s.required_ruby_version = ">= 2.1.0"
44 |
45 | ## Runtime
46 | s.add_dependency "bundler"
47 | s.add_dependency "rails", ">= 4.2.0"
48 | s.add_dependency "responders"
49 |
50 | ## Extras ##
51 | s.add_development_dependency "autoprefixer-rails"
52 |
53 | ## Dev ##
54 | s.add_development_dependency "rake"
55 | s.add_development_dependency "rspec"
56 | s.add_development_dependency "rspec-rails"
57 | s.add_development_dependency "coveralls"
58 | s.add_development_dependency "sqlite3", ">= 1.3.10"
59 |
60 | ##############################################################
61 | ##############################################################
62 |
63 | end
64 |
--------------------------------------------------------------------------------
/lib/exception_handler.rb:
--------------------------------------------------------------------------------
1 | ########################################################################################
2 | ########################################################################################
3 | ## _____ _ _ _ _ _ _ ##
4 | ## | ___| | | (_) | | | | | | | ##
5 | ## | |____ _____ ___ _ __ | |_ _ ___ _ __ | |_| | __ _ _ __ __| | | ___ _ __ ##
6 | ## | __\ \/ / __/ _ \ '_ \| __| |/ _ \| '_ \ | _ |/ _` | '_ \ / _` | |/ _ \ '__| ##
7 | ## | |___> < (_| __/ |_) | |_| | (_) | | | | | | | | (_| | | | | (_| | | __/ | ##
8 | ## \____/_/\_\___\___| .__/ \__|_|\___/|_| |_| \_| |_/\__,_|_| |_|\__,_|_|\___|_| ##
9 | ## | | ##
10 | ## |_| ##
11 | ########################################################################################
12 | ########################################################################################
13 |
14 | module ExceptionHandler
15 |
16 | ##############################
17 | ##############################
18 |
19 | # => Table Prefix
20 | # => Keeps Rails Engine from generating all table prefixes with the engines name
21 | # => http://stackoverflow.com/questions/19435214/rails-mountable-engine-with-isolate-namespace-but-without-prefixed-namespace-on
22 | def self.table_name_prefix
23 | # => No prefix
24 | end
25 |
26 | # => Config
27 | # => Invoke instance of config (ExceptionHandler.config)
28 | mattr_accessor :config
29 |
30 | ##############################
31 | ##############################
32 |
33 | # => Exceptions
34 | # => https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/errors.rb
35 | class Error < StandardError; end
36 |
37 | ##############################
38 | ##############################
39 |
40 |
41 | end
42 |
43 | #########################################################
44 | #########################################################
45 |
46 | # => Libs
47 | # => http://stackoverflow.com/a/4528011/1143732
48 | # => http://stackoverflow.com/a/21693468/1143732
49 | # => https://github.com/jekyll/jekyll/blob/master/lib/jekyll.rb#L8
50 | Dir.glob(File.join(File.dirname(__FILE__), 'exception_handler', '**/*.rb'), &method(:require))
51 |
52 | # => External Dependencies
53 | require 'responders'
54 |
55 | #########################################################
56 | #########################################################
57 |
--------------------------------------------------------------------------------
/lib/exception_handler/config.rb:
--------------------------------------------------------------------------------
1 | ###########################################
2 | ###########################################
3 | ## _____ __ _ ##
4 | ## / __ \ / _(_) ##
5 | ## | / \/ ___ _ __ | |_ _ __ _ ##
6 | ## | | / _ \| '_ \| _| |/ _` | ##
7 | ## | \__/\ (_) | | | | | | | (_| | ##
8 | ## \____/\___/|_| |_|_| |_|\__, | ##
9 | ## __/ | ##
10 | ## |___/ ##
11 | ###########################################
12 | ###########################################
13 |
14 | # Refs
15 | # http://stackoverflow.com/questions/10584638/setting-up-configuration-settings-when-writing-a-gem
16 | # http://robots.thoughtbot.com/mygem-configure-block
17 |
18 | ###########################################
19 | ###########################################
20 |
21 | module ExceptionHandler
22 | class Config
23 |
24 | # => Instace Objects
25 | # => ExceptionHandler.config.dev
26 | # => ExceptionHandler.config.db
27 | # => ExceptionHandler.config.email
28 | # => ExceptionHandler.config.social
29 | # => ExceptionHandler.config.layouts -> will need to be deprecated
30 | # => ExceptionHandler.config.exceptions
31 | # => ExceptionHandler.config.custom_exceptions
32 | attr_accessor :dev, :db, :email, :social, :layouts, :exceptions, :custom_exceptions
33 |
34 | ###########################################
35 | ###########################################
36 | ###########################################
37 | ###########################################
38 |
39 | # => Table Name
40 | # => Has to be "errors" because "exceptions" is a reserved word
41 | TABLE = :errors
42 |
43 | ###########################################
44 | ###########################################
45 |
46 | # => Social URLs
47 | # => Extracted from "social" block
48 | SOCIAL =
49 | ActiveSupport::HashWithIndifferentAccess.new({
50 | facebook: "https://www.facebook.com",
51 | twitter: "https://www.twitter.com",
52 | youtube: "https://www.youtube.com/user",
53 | linkedin: "https://www.linkedin.com/company",
54 | fusion: "https://www.frontlinefusion.com"
55 | })
56 |
57 | ###########################################
58 | ###########################################
59 |
60 | # => Defaults
61 | # => http://stackoverflow.com/a/8917301/1143732
62 | DEFAULTS =
63 | ActiveSupport::HashWithIndifferentAccess.new({
64 |
65 | # => General options
66 | dev: nil, # => defaults to "false" for dev mode
67 | db: nil, # => defaults to :errors if true, else use "table_name" / :table_name
68 | email: nil, # => requires string email and ActionMailer
69 |
70 | # => Used in "exception" layout
71 | social: {
72 | facebook: nil,
73 | twitter: nil,
74 | youtube: nil,
75 | linkedin: nil,
76 | fusion: nil,
77 | },
78 |
79 | # => Defaults for exceptions. Override with specific status codes
80 | # => Please note these are all STRINGS
81 | exceptions: {
82 |
83 | # => 4xx/5xx base standard
84 | # => :all provide block customization (overrides 4xx/5xx)
85 | # => specific provides individual (overrides all)
86 |
87 | # => 4xx Errors (resource not found)
88 | # => https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_errors
89 | '4xx' => {
90 | layout: nil
91 | # notification: true #(this is for emails - it's true by default - only if you have email inputted)
92 | # action: ____, (this is general)
93 | # background: (can define custom background for exceptions layout if required)
94 | },
95 |
96 | # => 5xx Errors (server error)
97 | # => https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_Server_errors
98 | '5xx' => {
99 | layout: 'exception'
100 | # notification: true (this is for emails - it's true by default - only if you have email inputted)
101 | # action: _____, (this is general)
102 | # background: (can define custom background for exceptions layout if required)
103 | }
104 | },
105 |
106 | # Deprecated
107 | #layouts: {
108 | # => nil inherits from ApplicationController
109 | # => 4xx errors should be nil
110 | # => 5xx errors should be "exception" but can be nil if explicitly defined
111 | #500 => 'exception',
112 | #501 => 'exception',
113 | #502 => 'exception',
114 | #503 => 'exception',
115 | #504 => 'exception',
116 | #505 => 'exception',
117 | #507 => 'exception',
118 | #510 => 'exception'
119 | #},
120 |
121 | # => If you want to map your own classes to HTTP errors
122 | # => use this...
123 | custom_exceptions: {
124 | #'ActionController::RoutingError' => :not_found # => example
125 | }
126 |
127 | })
128 |
129 | ###########################################
130 | ###########################################
131 |
132 | # => Constructor
133 | # => Merges DEFAULTS to values, creates instances vars (for attr_accessor)
134 | def initialize values
135 |
136 | # => Vars
137 | DEFAULTS.deep_merge!(values || {}).each do |k,v|
138 | instance_variable_set("@#{k}",v)
139 | end
140 |
141 | # => Validation
142 | raise ExceptionHandler::Error, "Email Not Valid" if @email && !@email.nil? && !@email.is_a?(String)
143 | raise ExceptionHandler::Error, "Migration Required → \"#{db}\" doesn't exist" if @db && !ActiveRecord::Base.connection.table_exists?(db) && (File.basename($0) != "rake" && !ARGV.include?("db:migrate"))
144 |
145 | end
146 |
147 | ###########################################
148 | ###########################################
149 |
150 | # => DB
151 | # => If config db = "true", use TABLE constant
152 | def db
153 | @db == true ? TABLE : @db.try(:parameterize, separator: "_")
154 | end
155 |
156 | ###########################################
157 | ###########################################
158 |
159 | # => Options
160 | # => Requires argument
161 | def options status, pluck=nil
162 |
163 | # => Structure from old + new setup
164 | # => 1. layouts => [500, '500']
165 | # => 2. exceptions => [500, '500' 'all', '4xx'/'5xx']
166 | { layouts: [status, status.to_s], # old + new
167 | exceptions: [status, status.to_s, 'all', status.to_s.first + 'xx'] }.each do |key,array|
168 |
169 | # => Array
170 | # => https://stackoverflow.com/a/26877095/1143732
171 | array.each do |specific|
172 | item = self.send(key).try(:[], specific)
173 | return (item.is_a?(Hash) ? ActiveSupport::HashWithIndifferentAccess.new(item)[pluck.try(:to_sym)] : item) if item.present? || (self.send(key).try(:has_key?, specific) && item.nil?) #if result exists and it has a value (including nil)
174 | end
175 |
176 | end
177 | end
178 |
179 | ###########################################
180 | ###########################################
181 | ###########################################
182 | ###########################################
183 |
184 | end
185 | end
186 |
--------------------------------------------------------------------------------
/lib/exception_handler/engine.rb:
--------------------------------------------------------------------------------
1 | ###########################################
2 | ###########################################
3 | ## _____ _ ##
4 | ## | ___| (_) ##
5 | ## | |__ _ __ __ _ _ _ __ ___ ##
6 | ## | __| '_ \ / _` | | '_ \ / _ \ ##
7 | ## | |__| | | | (_| | | | | | __/ ##
8 | ## \____/_| |_|\__, |_|_| |_|\___| ##
9 | ## __/ | ##
10 | ## |___/ ##
11 | ###########################################
12 | ###########################################
13 |
14 | module ExceptionHandler
15 | class Engine < Rails::Engine
16 |
17 | # => Rails default MIME types:
18 | # => http://apidock.com/rails/ActionController/MimeResponds/InstanceMethods/respond_to#14-Rails-defined-Mime-Types
19 |
20 | #########################################################
21 | #########################################################
22 |
23 | # => Wraps helpers in ExceptionHandler module
24 | # => http://guides.rubyonrails.org/engines.html#inside-an-engine
25 | # => http://stackoverflow.com/questions/31877839/accessing-helpers-from-the-parent-app-in-an-isolated-rails-engine
26 | isolate_namespace ExceptionHandler
27 |
28 | # => Tests
29 | config.generators do |g|
30 | g.test_framework :rspec
31 | end
32 |
33 | # => Assets
34 | # => For Sprockets 4, had to include link_tree in exception_handler.css
35 | config.assets.precompile << "exception_handler.css"
36 |
37 | #########################################################
38 | #########################################################
39 |
40 | # => Config
41 | # => Builds lib/exception_handler/config.rb
42 | # => config.before_initialize do |app| => Needs to be fixed for ActiveRecord::Base
43 | # => to support later version of config, "with_indifferent_access" used in config.rb
44 | initializer :exception_handler_config, before: "better_errors.configure_rails_initialization" do |app|
45 | ExceptionHandler.config ||= ExceptionHandler::Config.new config.try(:exception_handler)
46 | end
47 |
48 | #########################################################
49 | #########################################################
50 |
51 | # => Middleware
52 | # => This should be config.before_initialize but because ActiveRecord is not initialized, cannot check for table
53 | initializer :exception_handler, before: "better_errors.configure_rails_initialization" do |app|
54 | app.config.exceptions_app = ->(env) { ExceptionHandler::ExceptionsController.action(:show).call(env) }
55 | app.config.consider_all_requests_local = !ExceptionHandler.config.try(:dev) if Rails.env.development? || Rails.env.test?
56 | end
57 |
58 | # => Custom Exceptions
59 | # => This just mimicks standard Rails behaviour
60 | # => Look for "config.action_dispatch.rescue_responses" for more info)
61 | initializer :exception_handler_custom_exceptions do |app|
62 | app.config.action_dispatch.rescue_responses.merge! ExceptionHandler.config.custom_exceptions if ExceptionHandler.config.custom_exceptions
63 | end
64 |
65 | # => Migrations
66 | # => This has to be kept in an initializer (to access app)
67 | # => https://blog.pivotal.io/labs/labs/leave-your-migrations-in-your-rails-engines
68 | initializer :migration_paths do |app|
69 | config.paths["db/migrate"].expanded.each do |expanded_path|
70 | app.config.paths["db/migrate"] << expanded_path if ExceptionHandler.config.try(:db)
71 | end
72 | end
73 |
74 | #########################################################
75 | #########################################################
76 |
77 | end
78 | end
79 |
--------------------------------------------------------------------------------
/lib/exception_handler/version.rb:
--------------------------------------------------------------------------------
1 | ###########################################
2 | ###########################################
3 | ## _ _ _ ##
4 | ## | | | | (_) ##
5 | ## | | | | ___ _ __ ___ _ ___ _ __ ##
6 | ## | | | |/ _ \ '__/ __| |/ _ \| '_ \ ##
7 | ## \ \_/ / __/ | \__ \ | (_) | | | | ##
8 | ## \___/ \___|_| |___/_|\___/|_| |_| ##
9 | ## ##
10 | ###########################################
11 | ###########################################
12 |
13 | module ExceptionHandler
14 | module VERSION
15 | MAJOR = 0
16 | MINOR = 8
17 | TINY = 0
18 | PRE = 1 # "alpha"
19 |
20 | STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
21 | end
22 | end
23 |
24 | ###########################################
25 | ###########################################
26 |
--------------------------------------------------------------------------------
/lib/generators/exception_handler/views_generator.rb:
--------------------------------------------------------------------------------
1 | module ExceptionHandler
2 | class ViewsGenerator < Rails::Generators::Base
3 |
4 | ###########################################
5 | ###########################################
6 | ###########################################
7 |
8 | #Views
9 | VIEWS = %w(views controllers models assets)
10 |
11 | #Options
12 | class_option :files, aliases: "-v", default: VIEWS, type: :array, desc: "Select file types (views, models, controllers, assets)"
13 |
14 | #Needed to reference files
15 | source_root File.expand_path("../../../../app", __FILE__)
16 |
17 | ###########################################
18 | ###########################################
19 | ###########################################
20 |
21 | #Files
22 | def create_files
23 | options.files.each do |arg|
24 | directory arg, "app/#{arg}"
25 | end
26 | end
27 |
28 | ###########################################
29 | ###########################################
30 | ###########################################
31 |
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/readme/HTTP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/HTTP.png
--------------------------------------------------------------------------------
/readme/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/banner.jpg
--------------------------------------------------------------------------------
/readme/custom_exceptions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/custom_exceptions.png
--------------------------------------------------------------------------------
/readme/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/default.png
--------------------------------------------------------------------------------
/readme/dev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/dev.png
--------------------------------------------------------------------------------
/readme/fl.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/fl.jpg
--------------------------------------------------------------------------------
/readme/layouts.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/layouts.jpg
--------------------------------------------------------------------------------
/readme/local_requests.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/local_requests.jpg
--------------------------------------------------------------------------------
/readme/medium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/medium.png
--------------------------------------------------------------------------------
/readme/middleware.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/middleware.jpg
--------------------------------------------------------------------------------
/readme/routes.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/routes.jpg
--------------------------------------------------------------------------------
/readme/show_exceptions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/show_exceptions.png
--------------------------------------------------------------------------------
/readme/title.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/readme/title.jpg
--------------------------------------------------------------------------------
/spec/controllers/controller_spec.rb:
--------------------------------------------------------------------------------
1 | #####################################################
2 | #####################################################
3 | ## _____ _ _ _ ##
4 | ## / __ \ | | | | | ##
5 | ## | / \/ ___ _ __ | |_ _ __ ___ | | | ___ _ __ ##
6 | ## | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__| ##
7 | ## | \__/\ (_) | | | | |_| | | (_) | | | __/ | ##
8 | ## \____/\___/|_| |_|\__|_| \___/|_|_|\___|_| ##
9 | ## ##
10 | #####################################################
11 | #####################################################
12 |
13 | require 'spec_helper'
14 |
15 | ###############################################
16 | ###############################################
17 |
18 | # => ExceptionHandler (Controller)
19 | # => Test erroneous requests under different circumstances
20 | # => Expect the return of 404, 500 (etc) Error Pages
21 | # => Test layout, show action, @exception object and different config options
22 | RSpec.describe ExceptionHandler::ExceptionsController do
23 |
24 | # => General
25 | # => Used to describe the actual Controller class
26 | describe "class" do
27 | subject { controller }
28 | it { should respond_to :show }
29 | end
30 |
31 | #################################
32 | #################################
33 |
34 | # => Layout
35 | # => Expected results & overall views
36 | describe "layout" do
37 | #subject { response.layout }
38 | #it { should be_a String }
39 | end
40 |
41 | #################################
42 | #################################
43 |
44 | # => Response
45 | # => Should deliver 404 / 500 error responses
46 | # => Should deliver appropriate headers, layout etc
47 | describe "response" do
48 |
49 | # => Before
50 | before(:each) { }
51 |
52 | # => 404
53 | context "404" do
54 | subject { response }
55 | it { should have_http_status :ok }
56 |
57 | end
58 |
59 | # => 500
60 | context "500" do
61 | end
62 |
63 | end
64 |
65 | #################################
66 | #################################
67 |
68 | # => Dev Routes
69 | # => Should deliver 404 / 500 error responses
70 | # => Should deliver appropriate headers, layout etc
71 | describe "dev routes" do
72 |
73 | # => Item
74 | before(:context) { ExceptionHandler.config.dev = true }
75 | before(:context) { Rails.application.reload_routes! }
76 |
77 | # => Items
78 | let(:dev) { ExceptionHandler.config.dev }
79 |
80 | # => Config
81 |
82 | # => Pages
83 | # => These are shown when
84 | context "pages" do
85 |
86 | # => Dev Mode
87 | # => Only works with dev enabled
88 | it "has Dev mode enabled" do
89 | expect(dev).to eq(true)
90 | end
91 |
92 | # => The error pages need to return the correct status code
93 | #Rack::Utils::SYMBOL_TO_STATUS_CODE.select{ |key, value| value.to_s.match('\b(?:4[0-9]{2}|5[0-9]{2}|599)\b') }.each do |status,code|
94 | # it "shows #{code.to_s} page" do
95 | # get :show, params: { code: status.to_sym }
96 | # expect(response).to have_http_status status.to_sym
97 | # expect(response.body).to match /404/
98 | # end
99 | #end
100 |
101 | end
102 | end
103 |
104 | end
105 |
106 | ###############################################
107 | ###############################################
108 |
--------------------------------------------------------------------------------
/spec/dummy/.rspec:
--------------------------------------------------------------------------------
1 | --require spec_helper
2 |
--------------------------------------------------------------------------------
/spec/dummy/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.5.1
--------------------------------------------------------------------------------
/spec/dummy/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative 'config/application'
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/spec/dummy/app/assets/images/.keep
--------------------------------------------------------------------------------
/spec/dummy/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file. JavaScript code in this file should be added after the last require_* statement.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require rails-ujs
14 | //= require activestorage
15 | //= require_tree .
16 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/javascripts/cable.js:
--------------------------------------------------------------------------------
1 | // Action Cable provides the framework to deal with WebSockets in Rails.
2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3 | //
4 | //= require action_cable
5 | //= require_self
6 | //= require_tree ./channels
7 |
8 | (function() {
9 | this.App || (this.App = {});
10 |
11 | App.cable = ActionCable.createConsumer();
12 |
13 | }).call(this);
14 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/javascripts/channels/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/spec/dummy/app/assets/javascripts/channels/.keep
--------------------------------------------------------------------------------
/spec/dummy/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/spec/dummy/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/spec/dummy/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | end
3 |
--------------------------------------------------------------------------------
/spec/dummy/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/spec/dummy/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/spec/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/spec/dummy/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | end
3 |
--------------------------------------------------------------------------------
/spec/dummy/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/spec/dummy/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/spec/dummy/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/spec/dummy/app/models/concerns/.keep
--------------------------------------------------------------------------------
/spec/dummy/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dummy
5 | <%= csrf_meta_tags %>
6 | <%= csp_meta_tag %>
7 |
8 | <%= stylesheet_link_tag 'application', media: 'all' %>
9 | <%= javascript_include_tag 'application' %>
10 |
11 |
12 |
13 | <%= yield %>
14 |
15 |
16 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/spec/dummy/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/spec/dummy/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby.exe
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/spec/dummy/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby.exe
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/spec/dummy/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby.exe
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/spec/dummy/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby.exe
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a starting point to setup your application.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | # puts "\n== Copying sample files =="
24 | # unless File.exist?('config/database.yml')
25 | # cp 'config/database.yml.sample', 'config/database.yml'
26 | # end
27 |
28 | puts "\n== Preparing database =="
29 | system! 'bin/rails db:setup'
30 |
31 | puts "\n== Removing old logs and tempfiles =="
32 | system! 'bin/rails log:clear tmp:clear'
33 |
34 | puts "\n== Restarting application server =="
35 | system! 'bin/rails restart'
36 | end
37 |
--------------------------------------------------------------------------------
/spec/dummy/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby.exe
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | puts "\n== Updating database =="
24 | system! 'bin/rails db:migrate'
25 |
26 | puts "\n== Removing old logs and tempfiles =="
27 | system! 'bin/rails log:clear tmp:clear'
28 |
29 | puts "\n== Restarting application server =="
30 | system! 'bin/rails restart'
31 | end
32 |
--------------------------------------------------------------------------------
/spec/dummy/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby.exe
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | begin
5 | exec "yarnpkg", *ARGV
6 | rescue Errno::ENOENT
7 | $stderr.puts "Yarn executable was not detected in the system."
8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9 | exit 1
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/dummy/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative 'config/environment'
4 |
5 | run Rails.application
6 |
--------------------------------------------------------------------------------
/spec/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative 'boot'
2 |
3 | require 'rails/all'
4 |
5 | Bundler.require(*Rails.groups)
6 | require 'exception_handler'
7 |
8 | module Dummy
9 | class Application < Rails::Application
10 | # Initialize configuration defaults for originally generated Rails version.
11 | config.load_defaults 5.2
12 |
13 | # Settings in config/environments/* take precedence over those specified here.
14 | # Application configuration can go into files in config/initializers
15 | # -- all .rb files in that directory are automatically loaded after loading
16 | # the framework and any gems in your application.
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/dummy/config/boot.rb:
--------------------------------------------------------------------------------
1 | # Set up gems listed in the Gemfile.
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__)
3 |
4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5 | $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
6 |
--------------------------------------------------------------------------------
/spec/dummy/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: async
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: dummy_production
11 |
--------------------------------------------------------------------------------
/spec/dummy/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/spec/dummy/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | # Run rails dev:cache to toggle caching.
17 | if Rails.root.join('tmp', 'caching-dev.txt').exist?
18 | config.action_controller.perform_caching = true
19 |
20 | config.cache_store = :memory_store
21 | config.public_file_server.headers = {
22 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
23 | }
24 | else
25 | config.action_controller.perform_caching = false
26 |
27 | config.cache_store = :null_store
28 | end
29 |
30 | # Store uploaded files on the local file system (see config/storage.yml for options)
31 | config.active_storage.service = :local
32 |
33 | # Don't care if the mailer can't send.
34 | config.action_mailer.raise_delivery_errors = false
35 |
36 | config.action_mailer.perform_caching = false
37 |
38 | # Print deprecation notices to the Rails logger.
39 | config.active_support.deprecation = :log
40 |
41 | # Raise an error on page load if there are pending migrations.
42 | config.active_record.migration_error = :page_load
43 |
44 | # Highlight code that triggered database queries in logs.
45 | config.active_record.verbose_query_logs = true
46 |
47 | # Debug mode disables concatenation and preprocessing of assets.
48 | # This option may cause significant delays in view rendering with a large
49 | # number of complex assets.
50 | config.assets.debug = true
51 |
52 | # Suppress logger output for asset requests.
53 | config.assets.quiet = true
54 |
55 | # Raises error for missing translations
56 | # config.action_view.raise_on_missing_translations = true
57 |
58 | # Use an evented file watcher to asynchronously detect changes in source code,
59 | # routes, locales, etc. This feature depends on the listen gem.
60 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
61 | end
62 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
19 | # config.require_master_key = true
20 |
21 | # Disable serving static files from the `/public` folder by default since
22 | # Apache or NGINX already handles this.
23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
24 |
25 | # Compress JavaScripts and CSS.
26 | config.assets.js_compressor = :uglifier
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
33 |
34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
35 | # config.action_controller.asset_host = 'http://assets.example.com'
36 |
37 | # Specifies the header that your server uses for sending files.
38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
40 |
41 | # Store uploaded files on the local file system (see config/storage.yml for options)
42 | config.active_storage.service = :local
43 |
44 | # Mount Action Cable outside main process or domain
45 | # config.action_cable.mount_path = nil
46 | # config.action_cable.url = 'wss://example.com/cable'
47 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
48 |
49 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
50 | # config.force_ssl = true
51 |
52 | # Use the lowest log level to ensure availability of diagnostic information
53 | # when problems arise.
54 | config.log_level = :debug
55 |
56 | # Prepend all log lines with the following tags.
57 | config.log_tags = [ :request_id ]
58 |
59 | # Use a different cache store in production.
60 | # config.cache_store = :mem_cache_store
61 |
62 | # Use a real queuing backend for Active Job (and separate queues per environment)
63 | # config.active_job.queue_adapter = :resque
64 | # config.active_job.queue_name_prefix = "dummy_#{Rails.env}"
65 |
66 | config.action_mailer.perform_caching = false
67 |
68 | # Ignore bad email addresses and do not raise email delivery errors.
69 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
70 | # config.action_mailer.raise_delivery_errors = false
71 |
72 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
73 | # the I18n.default_locale when a translation cannot be found).
74 | config.i18n.fallbacks = true
75 |
76 | # Send deprecation notices to registered listeners.
77 | config.active_support.deprecation = :notify
78 |
79 | # Use default logging formatter so that PID and timestamp are not suppressed.
80 | config.log_formatter = ::Logger::Formatter.new
81 |
82 | # Use a different logger for distributed setups.
83 | # require 'syslog/logger'
84 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
85 |
86 | if ENV["RAILS_LOG_TO_STDOUT"].present?
87 | logger = ActiveSupport::Logger.new(STDOUT)
88 | logger.formatter = config.log_formatter
89 | config.logger = ActiveSupport::TaggedLogging.new(logger)
90 | end
91 |
92 | # Do not dump schema after migrations.
93 | config.active_record.dump_schema_after_migration = false
94 | end
95 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure public file server for tests with Cache-Control for performance.
16 | config.public_file_server.enabled = true
17 | config.public_file_server.headers = {
18 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
19 | }
20 |
21 | # Show full error reports and disable caching.
22 | config.consider_all_requests_local = true
23 | config.action_controller.perform_caching = false
24 |
25 | # Raise exceptions instead of rendering exception templates.
26 | config.action_dispatch.show_exceptions = false
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 |
31 | # Store uploaded files on the local file system in a temporary directory
32 | config.active_storage.service = :test
33 |
34 | config.action_mailer.perform_caching = false
35 |
36 | # Tell Action Mailer not to deliver emails to the real world.
37 | # The :test delivery method accumulates sent emails in the
38 | # ActionMailer::Base.deliveries array.
39 | config.action_mailer.delivery_method = :test
40 |
41 | # Print deprecation notices to the stderr.
42 | config.active_support.deprecation = :stderr
43 |
44 | # Raises error for missing translations
45 | # config.action_view.raise_on_missing_translations = true
46 | end
47 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 | # Add Yarn node_modules folder to the asset load path.
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 |
15 | # # Specify URI for violation reports
16 | # # policy.report_uri "/csp-violation-report-endpoint"
17 | # end
18 |
19 | # If you are using UJS then enable automatic nonce generation
20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
21 |
22 | # Report CSP violations to a specified URI
23 | # For further information see the following documentation:
24 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
25 | # Rails.application.config.content_security_policy_report_only = true
26 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/spec/dummy/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/spec/dummy/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at http://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/spec/dummy/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8 | threads threads_count, threads_count
9 |
10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
11 | #
12 | port ENV.fetch("PORT") { 3000 }
13 |
14 | # Specifies the `environment` that Puma will run in.
15 | #
16 | environment ENV.fetch("RAILS_ENV") { "development" }
17 |
18 | # Specifies the number of `workers` to boot in clustered mode.
19 | # Workers are forked webserver processes. If using threads and workers together
20 | # the concurrency of the application would be max `threads` * `workers`.
21 | # Workers do not work on JRuby or Windows (both of which do not support
22 | # processes).
23 | #
24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
25 |
26 | # Use the `preload_app!` method when specifying a `workers` number.
27 | # This directive tells Puma to first boot the application and load code
28 | # before forking the application. This takes advantage of Copy On Write
29 | # process behavior so workers use less memory.
30 | #
31 | # preload_app!
32 |
33 | # Allow puma to be restarted by `rails restart` command.
34 | plugin :tmp_restart
35 |
--------------------------------------------------------------------------------
/spec/dummy/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
3 | end
4 |
--------------------------------------------------------------------------------
/spec/dummy/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/spec/dummy/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/spec/dummy/lib/assets/.keep
--------------------------------------------------------------------------------
/spec/dummy/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/spec/dummy/log/.keep
--------------------------------------------------------------------------------
/spec/dummy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dummy",
3 | "private": true,
4 | "dependencies": {}
5 | }
6 |
--------------------------------------------------------------------------------
/spec/dummy/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/spec/dummy/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/spec/dummy/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/spec/dummy/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/spec/dummy/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/spec/dummy/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/spec/dummy/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/spec/dummy/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/spec/dummy/public/favicon.ico
--------------------------------------------------------------------------------
/spec/dummy/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | # This file is copied to spec/ when you run 'rails generate rspec:install'
2 | require 'spec_helper'
3 | ENV['RAILS_ENV'] ||= 'test'
4 | require File.expand_path('../../config/environment', __FILE__)
5 | # Prevent database truncation if the environment is production
6 | abort("The Rails environment is running in production mode!") if Rails.env.production?
7 | require 'rspec/rails'
8 | # Add additional requires below this line. Rails is not loaded until this point!
9 |
10 | # Requires supporting ruby files with custom matchers and macros, etc, in
11 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
12 | # run as spec files by default. This means that files in spec/support that end
13 | # in _spec.rb will both be required and run as specs, causing the specs to be
14 | # run twice. It is recommended that you do not name files matching this glob to
15 | # end with _spec.rb. You can configure this pattern with the --pattern
16 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
17 | #
18 | # The following line is provided for convenience purposes. It has the downside
19 | # of increasing the boot-up time by auto-requiring all files in the support
20 | # directory. Alternatively, in the individual `*_spec.rb` files, manually
21 | # require only the support files necessary.
22 | #
23 | # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
24 |
25 | # Checks for pending migrations and applies them before tests are run.
26 | # If you are not using ActiveRecord, you can remove this line.
27 | ActiveRecord::Migration.maintain_test_schema!
28 |
29 | RSpec.configure do |config|
30 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
31 | config.fixture_path = "#{::Rails.root}/spec/fixtures"
32 |
33 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
34 | # examples within a transaction, remove the following line or assign false
35 | # instead of true.
36 | config.use_transactional_fixtures = true
37 |
38 | # RSpec Rails can automatically mix in different behaviours to your tests
39 | # based on their file location, for example enabling you to call `get` and
40 | # `post` in specs under `spec/controllers`.
41 | #
42 | # You can disable this behaviour by removing the line below, and instead
43 | # explicitly tag your specs with their type, e.g.:
44 | #
45 | # RSpec.describe UsersController, :type => :controller do
46 | # # ...
47 | # end
48 | #
49 | # The different available types are documented in the features, such as in
50 | # https://relishapp.com/rspec/rspec-rails/docs
51 | config.infer_spec_type_from_file_location!
52 |
53 | # Filter lines from Rails gems in backtraces.
54 | config.filter_rails_from_backtrace!
55 | # arbitrary gems may also be filtered via:
56 | # config.filter_gems_from_backtrace("gem name")
57 | end
58 |
--------------------------------------------------------------------------------
/spec/dummy/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all
2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3 | # The generated `.rspec` file contains `--require spec_helper` which will cause
4 | # this file to always be loaded, without a need to explicitly require it in any
5 | # files.
6 | #
7 | # Given that it is always loaded, you are encouraged to keep this file as
8 | # light-weight as possible. Requiring heavyweight dependencies from this file
9 | # will add to the boot time of your test suite on EVERY test run, even for an
10 | # individual file that may not need all of that loaded. Instead, consider making
11 | # a separate helper file that requires the additional dependencies and performs
12 | # the additional setup, and require it from the spec files that actually need
13 | # it.
14 | #
15 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16 | RSpec.configure do |config|
17 | # rspec-expectations config goes here. You can use an alternate
18 | # assertion/expectation library such as wrong or the stdlib/minitest
19 | # assertions if you prefer.
20 | config.expect_with :rspec do |expectations|
21 | # This option will default to `true` in RSpec 4. It makes the `description`
22 | # and `failure_message` of custom matchers include text for helper methods
23 | # defined using `chain`, e.g.:
24 | # be_bigger_than(2).and_smaller_than(4).description
25 | # # => "be bigger than 2 and smaller than 4"
26 | # ...rather than:
27 | # # => "be bigger than 2"
28 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29 | end
30 |
31 | # rspec-mocks config goes here. You can use an alternate test double
32 | # library (such as bogus or mocha) by changing the `mock_with` option here.
33 | config.mock_with :rspec do |mocks|
34 | # Prevents you from mocking or stubbing a method that does not exist on
35 | # a real object. This is generally recommended, and will default to
36 | # `true` in RSpec 4.
37 | mocks.verify_partial_doubles = true
38 | end
39 |
40 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41 | # have no way to turn it off -- the option exists only for backwards
42 | # compatibility in RSpec 3). It causes shared context metadata to be
43 | # inherited by the metadata hash of host groups and examples, rather than
44 | # triggering implicit auto-inclusion in groups with matching metadata.
45 | config.shared_context_metadata_behavior = :apply_to_host_groups
46 |
47 | # The settings below are suggested to provide a good initial experience
48 | # with RSpec, but feel free to customize to your heart's content.
49 | =begin
50 | # This allows you to limit a spec run to individual examples or groups
51 | # you care about by tagging them with `:focus` metadata. When nothing
52 | # is tagged with `:focus`, all examples get run. RSpec also provides
53 | # aliases for `it`, `describe`, and `context` that include `:focus`
54 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55 | config.filter_run_when_matching :focus
56 |
57 | # Allows RSpec to persist some state between runs in order to support
58 | # the `--only-failures` and `--next-failure` CLI options. We recommend
59 | # you configure your source control system to ignore this file.
60 | config.example_status_persistence_file_path = "spec/examples.txt"
61 |
62 | # Limits the available syntax to the non-monkey patched syntax that is
63 | # recommended. For more details, see:
64 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
65 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
67 | config.disable_monkey_patching!
68 |
69 | # This setting enables warnings. It's recommended, but in some cases may
70 | # be too noisy due to issues in dependencies.
71 | config.warnings = true
72 |
73 | # Many RSpec users commonly either run the entire suite or an individual
74 | # file, and it's useful to allow more verbose output when running an
75 | # individual spec file.
76 | if config.files_to_run.one?
77 | # Use the documentation formatter for detailed output,
78 | # unless a formatter has already been configured
79 | # (e.g. via a command-line flag).
80 | config.default_formatter = "doc"
81 | end
82 |
83 | # Print the 10 slowest examples and example groups at the
84 | # end of the spec run, to help surface which specs are running
85 | # particularly slow.
86 | config.profile_examples = 10
87 |
88 | # Run specs in random order to surface order dependencies. If you find an
89 | # order dependency and want to debug it, you can fix the order by providing
90 | # the seed, which is printed after each run.
91 | # --seed 1234
92 | config.order = :random
93 |
94 | # Seed global randomization in this process using the `--seed` CLI option.
95 | # Setting this allows you to use `--seed` to deterministically reproduce
96 | # test failures related to randomization by passing the same `--seed` value
97 | # as the one that triggered the failure.
98 | Kernel.srand config.seed
99 | =end
100 | end
101 |
--------------------------------------------------------------------------------
/spec/dummy/storage/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richpeck/exception_handler/e13d5df2a49e1dc7d96af7e921e013857c0700d8/spec/dummy/storage/.keep
--------------------------------------------------------------------------------
/spec/features/asset_spec.rb:
--------------------------------------------------------------------------------
1 | ###############################################
2 | ###############################################
3 | ## ___ _ ##
4 | ## / _ \ | | ##
5 | ## / /_\ \___ ___ ___| |_ ___ ##
6 | ## | _ / __/ __|/ _ \ __/ __| ##
7 | ## | | | \__ \__ \ __/ |_\__ \ ##
8 | ## \_| |_/___/___/\___|\__|___/ ##
9 | ## ##
10 | ###############################################
11 | ###############################################
12 |
13 | require 'spec_helper'
14 |
15 | ###############################################
16 | ###############################################
17 |
18 | # => ExceptionHandler (Assets)
19 | # => Needs to test if exception_handler.css added to precompile queue
20 | # => Needs to test if exception_handler.css adds /images folder
21 | # => Needsd to check if all files are precompiled
22 | RSpec.describe "ExceptionHandler Assets" do
23 |
24 | # => Defs
25 | let(:assets) { Rails.configuration.assets }
26 | let(:sprockets) { Rails.application.assets }
27 |
28 | # => Precompilation
29 | # => Expects exception_handler.css
30 | # => Expects contents of /images to be included by file
31 | describe "precompile" do
32 | subject { assets.precompile }
33 | it { should include('exception_handler.css') }
34 | end
35 |
36 | # => Images
37 | # => Should be present in Rails.application.assets
38 | # => ref: https://stackoverflow.com/a/11005361/1143732
39 | describe "images" do
40 | it "includes exception_handler/images/*" do
41 | #Dir.foreach('../../../app/assets/images') do |item|
42 | # next if item == '.' or item == '..'
43 | # puts item
44 | # expect(sprockets.each_file).to include(item)
45 | #end
46 | end
47 | end
48 |
49 | # => Assets
50 | # => Expects exception_handler.css
51 | # => Expects /images
52 | # => Expects /images/[specific]
53 | describe "assets" do
54 | #it { expect(Rails.application.assets.find_asset(path)).to include('exception_handler.css') }
55 | end
56 |
57 |
58 | end
59 |
60 | ###############################################
61 | ###############################################
62 |
--------------------------------------------------------------------------------
/spec/features/config_spec.rb:
--------------------------------------------------------------------------------
1 | ###############################################
2 | ###############################################
3 | ## _____ __ _ ##
4 | ## / __ \ / _(_) ##
5 | ## | / \/ ___ _ __ | |_ _ __ _ ##
6 | ## | | / _ \| '_ \| _| |/ _` | ##
7 | ## | \__/\ (_) | | | | | | | (_| | ##
8 | ## \____/\___/|_| |_|_| |_|\__, | ##
9 | ## __/ | ##
10 | ## |___/ ##
11 | ###############################################
12 | ###############################################
13 |
14 | require 'spec_helper'
15 |
16 | ###############################################
17 | ###############################################
18 |
19 | # => ExceptionHandler (Config)
20 | # => Should explore config has been created/initialized
21 | # => Needs to identify available options (dev/db/email/social/layouts/exceptions/custom_exceptions)
22 | # => Needs to return specific results per option (dev = true/false, email = string etc)
23 | RSpec.describe ExceptionHandler.config do
24 |
25 | # => Config
26 | let(:config) { ExceptionHandler.config }
27 |
28 | # => Class
29 | # => Initialized?
30 | # => Responds to methods?
31 | describe "class" do
32 | subject { config }
33 | it { should be_a ExceptionHandler::Config }
34 | %i(dev db email social layouts exceptions custom_exceptions).each do |method|
35 | it { should respond_to(method) }
36 | end
37 | end
38 |
39 | # => Dev
40 | # => true/false
41 | describe "dev" do
42 | subject { ExceptionHandler.config.dev }
43 | #it { should be_boolean }
44 | end
45 |
46 | # => DB
47 |
48 | end
49 |
50 | ###############################################
51 | ###############################################
52 |
--------------------------------------------------------------------------------
/spec/features/engine_spec.rb:
--------------------------------------------------------------------------------
1 | ###############################################
2 | ###############################################
3 | ## _____ _____ ##
4 | ## | ___ \/ ___| ##
5 | ## | |_/ /\ `--. _ __ ___ ___ ##
6 | ## | / `--. \ '_ \ / _ \/ __| ##
7 | ## | |\ \ /\__/ / |_) | __/ (__ ##
8 | ## \_| \_|\____/| .__/ \___|\___| ##
9 | ## | | ##
10 | ## |_| ##
11 | ###############################################
12 | ###############################################
13 |
14 | require 'spec_helper'
15 |
16 | ###############################################
17 | ###############################################
18 |
19 | # => ExceptionHandler (base)
20 | # => Test underlying engine (loading, initializers, etc)
21 | # => Ensure that all elements are correctly integrated into Rails core
22 | RSpec.describe ExceptionHandler::Engine do
23 |
24 | #############################################
25 | #############################################
26 |
27 | # => Ensure Gem has value, loaded etc
28 | describe "Gem" do
29 |
30 | # => Options
31 | let(:version) { ExceptionHandler::VERSION::STRING }
32 | let(:engine) { ExceptionHandler::Engine }
33 |
34 | # => Version needs to exist
35 | # => Present Version
36 | describe "version" do
37 | subject { version }
38 | it { is_expected.not_to be_empty }
39 | it { is_expected.to eq('0.8.0.0') }
40 | end
41 |
42 | # => Loaded?
43 | # => Accessible by Rails?
44 | describe "class" do
45 | subject { engine }
46 | it { should be_const_defined('ExceptionHandler') }
47 | it { }
48 | end
49 |
50 | end
51 |
52 | #############################################
53 | #############################################
54 |
55 | # => Ensure Gem's features are loaded into Rails
56 | describe "Setup" do
57 |
58 | #########################
59 | #########################
60 |
61 | # => Options
62 | let(:config) { ExceptionHandler.config }
63 | let(:rails) { Rails.configuration }
64 |
65 | # => Before
66 | before(:context) { ExceptionHandler.config.dev = false }
67 |
68 | #########################
69 | #########################
70 |
71 | # => Config
72 | # => Exists? Accessible? Right Values?
73 | describe "config" do
74 | subject { config }
75 |
76 | # => Initialized
77 | it "has an initializer" do
78 | expect(Rails.application.initializers.map(&:name)).to include(:exception_handler_config)
79 | end
80 |
81 | # => Initialization Queue
82 | it "is initialized before better_errors" do
83 | end
84 |
85 | # => Accessible?
86 | # => Can we access the instance of the class and see which items are available?
87 | it { should be_a ExceptionHandler::Config }
88 | it "is accessible" do
89 |
90 | end
91 |
92 | # => Values
93 | # => The returned values need to be tested in the config class
94 | it "responds to methods" do
95 | %i(dev db email social layouts exceptions custom_exceptions).each do |method|
96 | expect(config).to respond_to(method)
97 | end
98 | end
99 |
100 | # => Basic values
101 | it "has basic values" do
102 | expect(config.dev).to be_boolean
103 | end
104 | end
105 |
106 | #########################
107 | #########################
108 |
109 | # => Middleware
110 | # => Check if it's correctly overwritten @exceptions_app
111 | # => http://guides.rubyonrails.org/configuring.html#rails-general-configuration
112 | describe "middleware" do
113 | subject { rails.exceptions_app }
114 | #it { should eq(ExceptionHandler) }
115 | # => accessible?
116 | end
117 |
118 | #########################
119 | #########################
120 |
121 | # => Dev Mode
122 | # => Changes "consider_all_requests_local" to opposite of config
123 | describe "dev" do
124 |
125 | # => Access dev mode
126 | # => Ensure the config is present & accessible
127 | context "config" do
128 | subject { config.dev }
129 | it { should_not be true }
130 | end
131 |
132 | # => Local Requests
133 | # => Should be the opposite of the dev option
134 | context "local requests" do
135 | subject { rails.consider_all_requests_local }
136 | it { should_not be ExceptionHandler.config.dev }
137 | end
138 |
139 | end
140 |
141 | #########################
142 | #########################
143 |
144 | # => Custom Exceptions
145 | # => Ensure custom exceptions added to Rails
146 | describe "custom exceptions" do
147 |
148 | # => Rescue Response
149 | subject { rails.action_dispatch.rescue_responses }
150 |
151 | # => Let
152 | let(:config) { ExceptionHandler.config }
153 |
154 | # => Removes
155 | it "has an initializer" do
156 | expect(Rails.application.initializers.map(&:name)).to include(:exception_handler_custom_exceptions)
157 | end
158 |
159 | # => Check if present
160 | context "present" do
161 | end
162 |
163 | # => Check if can be accessed
164 | context "accessiblity" do
165 | before { config.custom_exceptions.merge! 'ActionController::RoutingError' => :not_found }
166 |
167 | #it { should include config.custom_exceptions }
168 | end
169 |
170 | end
171 |
172 | end
173 |
174 | #############################################
175 | #############################################
176 |
177 | end
178 |
179 | ###############################################
180 | ###############################################
181 |
--------------------------------------------------------------------------------
/spec/mailers/mailer_spec.rb:
--------------------------------------------------------------------------------
1 | #####################################################
2 | #####################################################
3 | ## __ __ _ _ ##
4 | ## | \/ | (_) | ##
5 | ## | . . | __ _ _| | ___ _ __ ##
6 | ## | |\/| |/ _` | | |/ _ \ '__| ##
7 | ## | | | | (_| | | | __/ | ##
8 | ## \_| |_/\__,_|_|_|\___|_| ##
9 | ## ##
10 | #####################################################
11 | #####################################################
12 |
13 | require 'spec_helper'
14 |
15 | ###############################################
16 | ###############################################
17 |
18 | # => ExceptionHandler (Mailer)
19 | # => Check veracity of methods / returned results
20 | # =>
21 | RSpec.describe ExceptionHandler::ExceptionMailer do
22 |
23 | end
24 |
25 | ###############################################
26 | ###############################################
27 |
--------------------------------------------------------------------------------
/spec/models/model_spec.rb:
--------------------------------------------------------------------------------
1 | #####################################################
2 | #####################################################
3 | ## __ __ _ _ ##
4 | ## | \/ | | | | | ##
5 | ## | . . | ___ __| | ___| | ##
6 | ## | |\/| |/ _ \ / _` |/ _ \ | ##
7 | ## | | | | (_) | (_| | __/ | ##
8 | ## \_| |_/\___/ \__,_|\___|_| ##
9 | ## ##
10 | #####################################################
11 | #####################################################
12 |
13 | require 'spec_helper'
14 |
15 | ###############################################
16 | ###############################################
17 |
18 | # => ExceptionHandler (Model)
19 | # => Check veracity of methods / returned results
20 | # =>
21 | RSpec.describe ExceptionHandler::Exception do
22 |
23 | end
24 |
25 | ###############################################
26 | ###############################################
27 |
--------------------------------------------------------------------------------
/spec/routing/routing_spec.rb:
--------------------------------------------------------------------------------
1 | ###############################################
2 | ###############################################
3 | ## _____ _ ##
4 | ## | ___ \ | | ##
5 | ## | |_/ /___ _ _| |_ ___ ___ ##
6 | ## | // _ \| | | | __/ _ \/ __| ##
7 | ## | |\ \ (_) | |_| | || __/\__ \ ##
8 | ## \_| \_\___/ \__,_|\__\___||___/ ##
9 | ## ##
10 | ###############################################
11 | ###############################################
12 |
13 | require 'spec_helper'
14 |
15 | ###############################################
16 | ###############################################
17 |
18 | # => Routes (base)
19 | # => Test underlying routes + engine routes
20 | RSpec.describe 'ExceptionHandler::Engine.routes' do
21 |
22 | #############################################
23 | #############################################
24 |
25 | # => Let (config)
26 | let(:dev) { ExceptionHandler.config.dev }
27 |
28 | #############################################
29 | #############################################
30 |
31 | # => Rails application Routes
32 | # => Should be routable WITH dev option
33 | # => Should not be routable WITHOUT dev option
34 | ['Rails.application.routes', 'ExceptionHandler::Engine.routes'].each do |item|
35 |
36 | # => Different types of route
37 | # => Shouldn't have any routes for engine
38 | context item do
39 |
40 | # => Routes
41 | routes { eval item }
42 |
43 | # => Dev mode
44 | context "✔️ dev mode" do
45 | subject { dev }
46 | before { ExceptionHandler.config.dev = true }
47 | before { Rails.application.reload_routes! }
48 |
49 | it { should eq(true) }
50 | it "has exception routes" do
51 | Rack::Utils::SYMBOL_TO_STATUS_CODE.select{ |key, value| value.to_s.match('\b(?:4[0-9]{2}|5[0-9]{2}|599)\b') }.each do |status,code|
52 | if item == 'Rails.application.routes'
53 | expect(:get => code.to_s).to route_to(:controller => "exception_handler/exceptions", :action => "show", :code => status.to_sym)
54 | else
55 | expect(:get => code.to_s).not_to be_routable
56 | end
57 | end
58 | end
59 |
60 | end
61 |
62 | # => Non Dev mode
63 | context "❌ dev mode" do
64 | subject { dev }
65 | before { ExceptionHandler.config.dev = false }
66 | before { Rails.application.reload_routes! }
67 |
68 | it { should_not eq(true) }
69 | it "does not have exception routes" do
70 | Rack::Utils::SYMBOL_TO_STATUS_CODE.select{ |key, value| value.to_s.match('\b(?:4[0-9]{2}|5[0-9]{2}|599)\b') }.each do |status,code|
71 | expect(:get => code.to_s).not_to be_routable
72 | end
73 | end
74 |
75 | end
76 | end
77 | end
78 |
79 | #############################################
80 | #############################################
81 |
82 | end
83 |
84 | ###############################################
85 | ###############################################
86 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | ###############################################
2 | ###############################################
3 | ## _____ _____ ##
4 | ## | ___ \/ ___| ##
5 | ## | |_/ /\ `--. _ __ ___ ___ ##
6 | ## | / `--. \ '_ \ / _ \/ __| ##
7 | ## | |\ \ /\__/ / |_) | __/ (__ ##
8 | ## \_| \_|\____/| .__/ \___|\___| ##
9 | ## | | ##
10 | ## |_| ##
11 | ###############################################
12 | ###############################################
13 |
14 | # => Coveralls
15 | # => https://docs.coveralls.io/
16 | require 'coveralls'
17 | Coveralls.wear!('rails')
18 |
19 | ###############################################
20 | ###############################################
21 |
22 | # => Helper
23 | require 'spec_helper'
24 |
25 | # => ENV
26 | ENV["RAILS_ENV"] ||= "test"
27 |
28 | # => Rails
29 | require_relative './dummy/config/environment'
30 | abort("The Rails environment is running in production mode!") if Rails.env.production?
31 |
32 | # => RSpec
33 | require 'rspec/rails'
34 |
35 | # => Environment
36 | ActiveRecord::Migrator.migrations_paths = [File.expand_path("./dummy/db/migrate", __dir__)]
37 |
38 | # => Gem (to test)
39 | require 'exception_handler'
40 |
41 | ###############################################
42 | ###############################################
43 |
44 | # => ActiveRecord
45 | ActiveRecord::Migration.maintain_test_schema!
46 |
47 | # Load fixtures from the engine
48 | if ActiveSupport::TestCase.respond_to?(:fixture_path=)
49 | ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__)
50 | ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
51 | ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
52 | ActiveSupport::TestCase.fixtures :all
53 | end
54 |
55 | ###############################################
56 | ###############################################
57 |
58 | #=> Custom Matchers
59 | RSpec::Matchers.define :be_boolean do
60 | match do |value|
61 | [true, false].include? value
62 | end
63 | end
64 |
65 | ###############################################
66 | ###############################################
67 |
68 | # => http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
69 | RSpec.configure do |config|
70 |
71 | # rspec-expectations config goes here. You can use an alternate
72 | # assertion/expectation library such as wrong or the stdlib/minitest
73 | # assertions if you prefer.
74 | config.expect_with :rspec do |expectations|
75 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true
76 | expectations.syntax = :expect
77 | end
78 |
79 | # Enable flags like --only-failures and --next-failure
80 | config.example_status_persistence_file_path = ".rspec_status"
81 |
82 | # Disable RSpec exposing methods globally on `Module` and `main`
83 | config.disable_monkey_patching!
84 |
85 | # => Rails (Fixtures etc)
86 | config.infer_spec_type_from_file_location!
87 | config.filter_rails_from_backtrace!
88 | config.order = :random
89 |
90 | # => Asset Precompile
91 | config.before(:suite) do
92 | Rails.application.load_tasks
93 | Rake::Task["assets:precompile"].invoke
94 | end
95 |
96 | config.after(:suite) do
97 | Rails.application.load_tasks
98 | Rake::Task["assets:clobber"].invoke
99 | end
100 |
101 | end
102 |
--------------------------------------------------------------------------------
/spec/views/views_spec.rb:
--------------------------------------------------------------------------------
1 | ###############################################
2 | ###############################################
3 | ## _ _ _ ##
4 | ## | | | (_) ##
5 | ## | | | |_ _____ _____ ##
6 | ## | | | | |/ _ \ \ /\ / / __| ##
7 | ## \ \_/ / | __/\ V V /\__ \ ##
8 | ## \___/|_|\___| \_/\_/ |___/ ##
9 | ## ##
10 | ###############################################
11 | ###############################################
12 |
13 | require 'spec_helper'
14 |
15 | ###############################################
16 | ###############################################
17 |
18 | # => ExceptionHandler (Views)
19 | # => Determine views / layout for different exceptions
20 | # => Needs to explain exactly what users is doing to see (request.status, message etc)
21 | # => Needs to include 404/500 dev routes
22 | RSpec.describe "ExceptionHandler Views" do
23 |
24 |
25 |
26 | end
27 |
28 | ###############################################
29 | ###############################################
30 |
--------------------------------------------------------------------------------