├── .gitignore ├── .rspec ├── .travis.yml ├── Gemfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── lib ├── rexception.rb └── rexception │ ├── config.rb │ ├── exceptions_controller.rb │ ├── railtie.rb │ └── version.rb ├── rexception.gemspec └── spec ├── dummy ├── app │ └── views │ │ ├── errors_dir │ │ ├── application.html.erb │ │ ├── forbidden.html.erb │ │ └── not_found.html.erb │ │ └── layouts │ │ └── layout.html.erb ├── application.rb ├── bin │ └── rails └── config.ru ├── requests └── error_pages_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | pkg/ 3 | spec/dummy/log/ 4 | spec/dummy/tmp/ 5 | Gemfile.lock 6 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 kami 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rexception 2 | 3 | [![Build Status](https://travis-ci.org/kami-zh/rexception.svg)](https://travis-ci.org/kami-zh/rexception) 4 | [![Gem Version](https://badge.fury.io/rb/rexception.svg)](http://badge.fury.io/rb/rexception) 5 | 6 | Rexception is a Rails plugin that renders error pages dynamically, such as 403, 404, 500, and so on. 7 | 8 | ## Installation 9 | 10 | Add this line to your application's Gemfile: 11 | 12 | ```ruby 13 | gem 'rexception' 14 | ``` 15 | 16 | And then execute: 17 | 18 | ``` 19 | $ bundle 20 | ``` 21 | 22 | ## Usage 23 | 24 | The simplest way, just add `app/views/errors/application.html.erb`. 25 | That's it, and Rails application render it when raise any errors. 26 | 27 | If you placed specific views, such as `forbidden.html.erb` or `not_found.html.erb`, Rexception prefers these views to `application.html.erb`. 28 | (File name is see also: [ActionDispatch::ExceptionWrapper.rescue_responses](https://github.com/rails/rails/blob/083f657c0f1990e980d33f89f44d8943a9270475/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb#L9-L19)) 29 | 30 | And you can specify layout file name, directory name to place views, and custom exceptions. 31 | Create `config/initializers/rexception.rb` and add following lines: 32 | 33 | ```ruby 34 | Rexception.configure do |config| 35 | # Layout file name to use for rendering error page. 36 | config.layout = 'application' 37 | 38 | # Directory name where you place error pages. 39 | config.errors_dir = 'errors_dir' 40 | 41 | # Pairs of custom exceptions and statuses. 42 | config.rescue_responses = { 43 | 'CustomException' => :not_found 44 | } 45 | end 46 | 47 | ``` 48 | 49 | In `development` mode, you should add this line to `config/environments/development.rb` to render error pages: 50 | 51 | ```ruby 52 | config.consider_all_requests_local = false 53 | ``` 54 | 55 | ## Contributing 56 | 57 | 1. Fork it ( https://github.com/kami-zh/rexception/fork ) 58 | 2. Create your feature branch (git checkout -b my-new-feature) 59 | 3. Commit your changes (git commit -am 'Add some feature') 60 | 4. Push to the branch (git push origin my-new-feature) 61 | 5. Create a new Pull Request 62 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | require 'rspec/core/rake_task' 3 | 4 | Bundler::GemHelper.install_tasks 5 | 6 | RSpec::Core::RakeTask.new(:spec) 7 | task default: :spec 8 | -------------------------------------------------------------------------------- /lib/rexception.rb: -------------------------------------------------------------------------------- 1 | require 'rexception/version' 2 | require 'rexception/railtie' if defined?(Rails) 3 | 4 | module Rexception 5 | autoload :ExceptionsController, 'rexception/exceptions_controller' 6 | 7 | class << self 8 | # Layout file name to use for rendering error page. 9 | # 10 | # @return [String] 11 | attr_accessor :layout 12 | 13 | # Directory name where you place error pages. 14 | # 15 | # @return [String] 16 | attr_accessor :errors_dir 17 | 18 | # Pairs of custom exceptions and statuses. 19 | # 20 | # @return [Hash{String => Symbol}] 21 | def rescue_responses=(rescue_responses) 22 | ActionDispatch::ExceptionWrapper.rescue_responses.merge!(rescue_responses) 23 | end 24 | 25 | # Configuring module attributes by initializer. 26 | # 27 | # @yield Rexception 28 | def configure 29 | yield self 30 | end 31 | end 32 | end 33 | 34 | require 'rexception/config' 35 | -------------------------------------------------------------------------------- /lib/rexception/config.rb: -------------------------------------------------------------------------------- 1 | Rexception.configure do |config| 2 | # Layout file name to use for rendering error page. 3 | config.layout = 'application' 4 | 5 | # Directory name where you place error pages. 6 | config.errors_dir = 'errors_dir' 7 | 8 | # Pairs of custom exceptions and statuses. 9 | config.rescue_responses = { 10 | 'CustomException' => :not_found 11 | } 12 | end 13 | -------------------------------------------------------------------------------- /lib/rexception/exceptions_controller.rb: -------------------------------------------------------------------------------- 1 | module Rexception 2 | class ExceptionsController < ActionController::Base 3 | STATUSES = Rack::Utils::SYMBOL_TO_STATUS_CODE.select { |_, code| code >= 400 } 4 | 5 | STATUSES.each do |status, code| 6 | eval <<-RUBY 7 | def #{status} 8 | render template_exists?(view) ? view : view(:application), status: #{code} 9 | end 10 | RUBY 11 | end 12 | 13 | layout -> { layout } 14 | 15 | private 16 | 17 | def view(status = status()) 18 | "#{errors_dir}/#{status}" 19 | end 20 | 21 | def status 22 | self.class.status(env) 23 | end 24 | 25 | def layout 26 | Rexception.layout 27 | end 28 | 29 | def errors_dir 30 | Rexception.errors_dir 31 | end 32 | 33 | class << self 34 | def call(env) 35 | action(status(env)).call(env) 36 | end 37 | 38 | def status(env) 39 | ActionDispatch::ExceptionWrapper.rescue_responses[exception(env).class.name] 40 | end 41 | 42 | def exception(env) 43 | env['action_dispatch.exception'] 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/rexception/railtie.rb: -------------------------------------------------------------------------------- 1 | module Rexception 2 | class Railtie < Rails::Railtie 3 | initializer 'rexception' do |app| 4 | app.config.exceptions_app = Rexception::ExceptionsController 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/rexception/version.rb: -------------------------------------------------------------------------------- 1 | module Rexception 2 | VERSION = '0.1.0' 3 | end 4 | -------------------------------------------------------------------------------- /rexception.gemspec: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path('../lib', __FILE__) 2 | 3 | require 'rexception/version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'rexception' 7 | s.version = Rexception::VERSION 8 | s.authors = 'kami' 9 | s.email = 'hiroki.zenigami@gmail.com' 10 | s.homepage = 'https://github.com/kami-zh/rexception' 11 | s.summary = 'Rendering error pages for Rails application.' 12 | s.description = 'Rendering error pages for Rails application.' 13 | s.license = 'MIT' 14 | 15 | s.files = `git ls-files -z`.split("\x0") 16 | 17 | s.add_dependency 'rails' 18 | 19 | s.add_development_dependency 'rspec-rails' 20 | s.add_development_dependency 'capybara' 21 | end 22 | -------------------------------------------------------------------------------- /spec/dummy/app/views/errors_dir/application.html.erb: -------------------------------------------------------------------------------- 1 | Application Error 2 | -------------------------------------------------------------------------------- /spec/dummy/app/views/errors_dir/forbidden.html.erb: -------------------------------------------------------------------------------- 1 | Forbidden 2 | -------------------------------------------------------------------------------- /spec/dummy/app/views/errors_dir/not_found.html.erb: -------------------------------------------------------------------------------- 1 | Not Found 2 | -------------------------------------------------------------------------------- /spec/dummy/app/views/layouts/layout.html.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /spec/dummy/application.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path('../../../lib', __FILE__) 2 | 3 | require 'action_controller/railtie' 4 | require 'action_view/railtie' 5 | require 'rexception' 6 | 7 | module Dummy 8 | class Application < Rails::Application 9 | config.secret_token = 'abcdefghijklmnopqrstuvwxyz0123456789' 10 | config.session_store :cookie_store, key: '_dummy_session' 11 | config.eager_load = false 12 | config.active_support.deprecation = :log 13 | end 14 | end 15 | 16 | Dummy::Application.initialize! 17 | 18 | Dummy::Application.routes.draw do 19 | get '/application_error', to: 'errors#application_error' 20 | get '/forbidden', to: 'errors#forbidden' 21 | end 22 | 23 | class CustomException < StandardError 24 | end 25 | 26 | class ApplicationController < ActionController::Base 27 | end 28 | 29 | class ErrorsController < ApplicationController 30 | def application_error 31 | raise Exception 32 | end 33 | 34 | def forbidden 35 | raise CustomException 36 | end 37 | end 38 | 39 | Rexception.configure do |config| 40 | config.layout = 'layout' 41 | 42 | config.errors_dir = 'errors_dir' 43 | 44 | config.rescue_responses = { 45 | 'CustomException' => :forbidden 46 | } 47 | end 48 | -------------------------------------------------------------------------------- /spec/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | APP_PATH = File.expand_path('../../application', __FILE__) 4 | 5 | require 'rails/commands' 6 | -------------------------------------------------------------------------------- /spec/dummy/config.ru: -------------------------------------------------------------------------------- 1 | run Rails.application 2 | -------------------------------------------------------------------------------- /spec/requests/error_pages_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'Error pages' do 4 | shared_examples_for 'error page' do |path, code, content| 5 | it "should return #{code}" do 6 | visit path 7 | 8 | expect(page.status_code).to eq code 9 | expect(page).to have_content content 10 | end 11 | end 12 | 13 | context 'access to 403 page' do 14 | it_behaves_like 'error page', '/forbidden', 403, 'Forbidden' 15 | end 16 | 17 | context 'access to 404 page' do 18 | it_behaves_like 'error page', '/not_found', 404, 'Not Found' 19 | end 20 | 21 | context 'access to 500 page' do 22 | it_behaves_like 'error page', '/application_error', 500, 'Application Error' 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'dummy/application' 2 | require 'rspec/rails' 3 | require 'capybara/rails' 4 | require 'capybara/rspec' 5 | 6 | RSpec.configure do |config| 7 | config.include Capybara::DSL 8 | end 9 | --------------------------------------------------------------------------------