├── .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 | ![Exception Handler](readme/title.jpg "Exception Handler Logo") 2 | 3 | 4 |
5 |

ExceptionHandler is presently the MOST POPULAR exceptions gem for CUSTOM Rails error pages.

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 | Coverage Status 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 |
514 |

You're welcome to contact me directly at rpeck@frontlineutilities.co.uk.

515 |

Alternatively, you may wish to post on our GitHub Issues, or StackOverflow.

516 |

--

517 |

Medium

518 |
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 | Coverage Status 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 | --------------------------------------------------------------------------------