├── test ├── tmp │ └── .keep ├── views │ ├── part │ │ └── _first.html.erb │ └── layouts │ │ ├── a_few_error_pages.html.erb │ │ ├── app_helpers.html.erb │ │ ├── assigns_vars.html.erb │ │ ├── error_page.html.erb │ │ ├── url_helpers.html.erb │ │ ├── partials.html.erb │ │ ├── given_layout.html.erb │ │ └── custom_helpers.html.erb ├── gemfiles │ ├── Gemfile.rails-5.2.x │ ├── Gemfile.rails-4.2.x │ └── Gemfile.rails-5.0.x ├── test_helper.rb └── errgent_renderer_test.rb ├── Gemfile ├── lib ├── errgent.rb ├── tasks │ └── errgent.rake └── errgent │ ├── engine.rb │ ├── capistrano.rb │ ├── capistrano │ ├── v2.rb │ └── v3.rb │ ├── mina.rb │ └── renderer.rb ├── Rakefile ├── .gitignore ├── .travis.yml ├── errgent.gemspec ├── LICENSE.txt └── README.md /test/tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/views/part/_first.html.erb: -------------------------------------------------------------------------------- 1 | First -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /test/views/layouts/a_few_error_pages.html.erb: -------------------------------------------------------------------------------- 1 | <%= @code %> -------------------------------------------------------------------------------- /test/views/layouts/app_helpers.html.erb: -------------------------------------------------------------------------------- 1 | <%= hi_there %> -------------------------------------------------------------------------------- /test/views/layouts/assigns_vars.html.erb: -------------------------------------------------------------------------------- 1 | <%= @object %> -------------------------------------------------------------------------------- /test/views/layouts/error_page.html.erb: -------------------------------------------------------------------------------- 1 | <%= @code %> error! -------------------------------------------------------------------------------- /test/views/layouts/url_helpers.html.erb: -------------------------------------------------------------------------------- 1 | <%= root_path %> -------------------------------------------------------------------------------- /test/views/layouts/partials.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'part/first' %> -------------------------------------------------------------------------------- /test/views/layouts/given_layout.html.erb: -------------------------------------------------------------------------------- 1 | Hi, <%= 'Dmi' + 'try' %>! -------------------------------------------------------------------------------- /test/views/layouts/custom_helpers.html.erb: -------------------------------------------------------------------------------- 1 | <%= current_user %> = <%= role %> -------------------------------------------------------------------------------- /lib/errgent.rb: -------------------------------------------------------------------------------- 1 | module Errgent 2 | require 'errgent/engine' if defined?(Rails) 3 | end 4 | -------------------------------------------------------------------------------- /lib/tasks/errgent.rake: -------------------------------------------------------------------------------- 1 | desc 'Generate error pages' 2 | task errgent: :environment do 3 | Errgent::Renderer.new.render 4 | end 5 | -------------------------------------------------------------------------------- /lib/errgent/engine.rb: -------------------------------------------------------------------------------- 1 | require 'errgent/renderer' 2 | 3 | class Errgent::Engine < ::Rails::Engine 4 | rake_tasks { load 'tasks/errgent.rake' } 5 | end 6 | -------------------------------------------------------------------------------- /test/gemfiles/Gemfile.rails-5.2.x: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec :path => './../..' 3 | 4 | gem 'railties', github: 'rails/rails', branch: '5-2-stable' 5 | -------------------------------------------------------------------------------- /test/gemfiles/Gemfile.rails-4.2.x: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec :path => './../..' 3 | 4 | gem 'railties', github: 'rails/rails', branch: '4-2-stable' 5 | gem 'minitest', '5.1.0' 6 | -------------------------------------------------------------------------------- /test/gemfiles/Gemfile.rails-5.0.x: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec :path => './../..' 3 | 4 | gem 'railties', github: 'rails/rails', branch: '5-0-stable' 5 | gem 'minitest', '5.1.0' 6 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rake/testtask' 3 | 4 | Rake::TestTask.new do |t| 5 | t.libs << 'test' 6 | t.pattern = 'test/*_test.rb' 7 | t.warning = false 8 | end 9 | 10 | task default: :test 11 | -------------------------------------------------------------------------------- /.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/version_tmp 16 | log 17 | -------------------------------------------------------------------------------- /lib/errgent/capistrano.rb: -------------------------------------------------------------------------------- 1 | if defined?(Capistrano::VERSION) 2 | if Gem::Version.new(Capistrano::VERSION).release >= Gem::Version.new('3.0.0') 3 | version = 3 4 | end 5 | end 6 | 7 | version ||= 2 8 | require_relative "capistrano/v#{version}" 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | rvm: 4 | - '2.3.8' 5 | - '2.5.3' 6 | 7 | gemfile: 8 | - test/gemfiles/Gemfile.rails-4.2.x 9 | - test/gemfiles/Gemfile.rails-5.0.x 10 | - test/gemfiles/Gemfile.rails-5.2.x 11 | 12 | notifications: 13 | email: false 14 | -------------------------------------------------------------------------------- /lib/errgent/capistrano/v2.rb: -------------------------------------------------------------------------------- 1 | Capistrano::Configuration.instance.load do 2 | desc 'Generate error pages' 3 | task :errgent do 4 | run "cd #{latest_release} && RAILS_ENV=#{rails_env} bundle exec rake errgent" 5 | end 6 | 7 | after 'deploy:assets:precompile', 'errgent' 8 | end 9 | -------------------------------------------------------------------------------- /lib/errgent/mina.rb: -------------------------------------------------------------------------------- 1 | namespace :errgent do 2 | desc 'regenerate error pages' 3 | task :generate => :environment do 4 | queue! %( 5 | echo '-----> regenerating error pages' 6 | RAILS_ENV=#{fetch(:rails_env)} bundle exec rake errgent 7 | ) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/errgent/capistrano/v3.rb: -------------------------------------------------------------------------------- 1 | desc 'Generate error pages' 2 | task :errgent do 3 | on roles(:app) do 4 | within release_path do 5 | with rails_env: fetch(:rails_env) do 6 | execute :rake, :errgent 7 | end 8 | end 9 | end 10 | end 11 | 12 | after 'deploy:assets:precompile', 'errgent' 13 | -------------------------------------------------------------------------------- /errgent.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |spec| 2 | spec.name = 'errgent' 3 | spec.version = '2.0.0' 4 | spec.authors = 'Dmitry Vorotilin' 5 | spec.email = 'd.vorotilin@gmail.com' 6 | spec.summary = 'Error pages generator' 7 | spec.description = 'Generate error pages for Rails applications' 8 | spec.homepage = 'https://github.com/route/errgent' 9 | spec.license = 'MIT' 10 | 11 | spec.files = `git ls-files`.split($/) 12 | spec.test_files = spec.files.grep(%r{^test/}) 13 | spec.require_paths = ['lib'] 14 | 15 | spec.add_runtime_dependency 'railties', '>= 3.0' 16 | 17 | spec.add_development_dependency 'bundler', '~> 1.3' 18 | spec.add_development_dependency 'rake' 19 | spec.add_development_dependency 'tzinfo' 20 | end 21 | -------------------------------------------------------------------------------- /lib/errgent/renderer.rb: -------------------------------------------------------------------------------- 1 | class Errgent::Renderer 2 | def initialize(options = {}) 3 | @vars = options[:vars] || {} 4 | @codes = options[:codes] || [403, 404, 422, 500] 5 | @layout = options[:layout] || 'layouts/error_page' 6 | @output = options[:output] || 'public/%s.html' 7 | end 8 | 9 | def render 10 | @codes.each do |code| 11 | File.open(Rails.root.join(@output % code), 'w') do |io| 12 | action_view.assign(@vars.merge(code: code)) 13 | io.write action_view.render(template: @layout) 14 | end 15 | end 16 | end 17 | 18 | def helpers(&block) 19 | action_view.class_eval(&block) 20 | end 21 | 22 | private 23 | 24 | def action_view 25 | @action_view ||= begin 26 | paths = ::ActionController::Base.view_paths 27 | ::ActionView::Base.new(paths).tap do |view| 28 | view.class_eval do 29 | include Rails.application.helpers 30 | include Rails.application.routes.url_helpers 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Dmitry Vorotilin 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require 'rails' 4 | require 'action_view' 5 | require 'action_controller' 6 | require 'active_support/testing/isolation' 7 | 8 | require 'errgent' 9 | 10 | class ActiveSupport::TestCase 11 | VIEW_PATH = File.expand_path('../views', __FILE__) 12 | TMP_PATH = File.expand_path('../tmp', __FILE__) 13 | 14 | def app 15 | Class.new(Rails::Application).tap do |app| 16 | app.configure do 17 | config.active_support.deprecation = :notify 18 | config.secret_token = 'a966a729a228e5d3edf00997e7b7eab7' 19 | config.eager_load = false 20 | 21 | routes { 22 | root to: 'home#index' 23 | } 24 | 25 | helpers_module = Module.new do 26 | def hi_there 27 | 'hi_there!' 28 | end 29 | end 30 | 31 | helpers.send(:include, helpers_module) 32 | end 33 | end 34 | end 35 | 36 | def setup 37 | app.initialize! 38 | end 39 | 40 | def teardown 41 | files = [@output] 42 | @codes.each { |c| files << @output % c } if @codes 43 | files.each { |f| File.delete(f) if File.exist?(f) } 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Errgent 2 | 3 | Gently generate error pages using layout, your favorite markup language, basic 4 | and url helpers or the other stuff you ever used in your templates. Out of the 5 | box Rails has default error pages, but they're hardly maintained, because if you 6 | do a little change in one, you'll have to fix the others. With `Errgent` you can 7 | make changes and re-generate all pages at once. 8 | 9 | ## Installation 10 | 11 | ```ruby 12 | gem 'errgent' 13 | ``` 14 | 15 | ## Usage 16 | 17 | `Errgent` ships with rake task that you can run locally or on your server using 18 | `Capistrano`. In order to generate pages simply run `rake errgent`. By default 19 | `layouts/error_page` is used as a layout. But you can customize this behaviour 20 | as many other things creating your own task in `lib/tasks`. Make sure you didn't 21 | name your task `errgent` as rake will run both tasks yours and original one making 22 | your overrides not working. Name it as `generate_error_pages` for e.g.: 23 | 24 | ```ruby 25 | desc 'Generate error pages' 26 | task generate_error_pages: :environment do 27 | renderer = Errgent::Renderer.new( 28 | codes: [403, 404, 422, 500], # error codes by default, @code variable is available in layout 29 | vars: { name: 'value' }, # variable @name will be available in layout 30 | layout: 'layouts/error_page', # where to find your layout by default 31 | output: 'public/%s.html' # where to put generated pages by default, %s is the replacement for @code 32 | ) 33 | 34 | # You can override or define new helpers: 35 | renderer.helpers do 36 | def current_user 37 | nil 38 | end 39 | 40 | def logged_in? 41 | false 42 | end 43 | end 44 | 45 | renderer.render 46 | end 47 | ``` 48 | 49 | ## Mina 50 | 51 | ```ruby 52 | 53 | require 'errgent/mina' 54 | 55 | task deploy: :environment do 56 | deploy do 57 | ... 58 | invoke :'errgent:generate' 59 | 60 | ``` 61 | 62 | ## Capistrano 63 | 64 | Since almost all applications use `Asset Pipeline`, the links to stylesheets, 65 | js or images must be represented with digest in their names. It's barely possible 66 | to do locally unless you generate assets locally either. That's why `Errgent` 67 | ships with `Capistrano` task. Just put this to `deploy.rb` or `Capfile` (`~> 2.0` 68 | and `~> 3.0` supported) and all the pages will be generated after 69 | `assets:precompile` task on your server during deployment: 70 | 71 | ```ruby 72 | require 'errgent/capistrano' 73 | ``` 74 | 75 | In other words for development, generate pages locally see how it looks, fix it 76 | if it needs. Then re-generate them on the server, because development version 77 | isn't workable in production. 78 | 79 | ## License 80 | 81 | Errgent is released under the [MIT License](http://www.opensource.org/licenses/MIT). 82 | -------------------------------------------------------------------------------- /test/errgent_renderer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Errgent::RendererTest < ActiveSupport::TestCase 4 | include ::ActiveSupport::Testing::Isolation 5 | 6 | def test_renders_given_layout 7 | @layout = 'layouts/given_layout' 8 | @output = File.join(TMP_PATH, 'given_layout.html') 9 | 10 | renderer.render 11 | 12 | assert File.exist?(@output) 13 | assert_equal 'Hi, Dmitry!', File.read(@output) 14 | end 15 | 16 | def test_assigns_vars 17 | @vars = { object: 'you' } 18 | @layout = 'layouts/assigns_vars' 19 | @output = File.join(TMP_PATH, 'assigns_vars.html') 20 | 21 | renderer.render 22 | 23 | assert File.exist?(@output) 24 | assert_equal 'you', File.read(@output) 25 | end 26 | 27 | def test_url_helpers 28 | @layout = 'layouts/url_helpers' 29 | @output = File.join(TMP_PATH, 'url_helpers.html') 30 | 31 | renderer.render 32 | 33 | assert File.exist?(@output) 34 | assert_equal '/', File.read(@output) 35 | end 36 | 37 | def test_app_helpers 38 | @layout = 'layouts/app_helpers' 39 | @output = File.join(TMP_PATH, 'app_helpers.html') 40 | 41 | renderer.render 42 | 43 | assert File.exist?(@output) 44 | assert_equal 'hi_there!', File.read(@output) 45 | end 46 | 47 | def test_custom_helpers 48 | @layout = 'layouts/custom_helpers' 49 | @output = File.join(TMP_PATH, 'custom_helpers.html') 50 | 51 | renderer.helpers do 52 | def current_user 53 | 'Dmitry' 54 | end 55 | 56 | def role 57 | 'admin' 58 | end 59 | end 60 | 61 | renderer.render 62 | 63 | assert File.exist?(@output) 64 | assert_equal 'Dmitry = admin', File.read(@output) 65 | end 66 | 67 | def test_partials 68 | @layout = 'layouts/partials' 69 | @output = File.join(TMP_PATH, 'partials.html') 70 | 71 | renderer.render 72 | 73 | assert File.exist?(@output) 74 | assert_equal 'First', File.read(@output) 75 | end 76 | 77 | def test_a_few_error_pages 78 | @codes = [404, 500] 79 | @layout = 'layouts/a_few_error_pages' 80 | @output = File.join(TMP_PATH, '%s.html') 81 | 82 | renderer.render 83 | 84 | assert File.exist?(@output % 404) 85 | assert_equal '404', File.read(@output % 404) 86 | 87 | assert File.exist?(@output % 500) 88 | assert_equal '500', File.read(@output % 500) 89 | end 90 | 91 | private 92 | 93 | def renderer 94 | @renderer ||= begin 95 | options = { codes: @codes || [404], layout: @layout, output: @output, vars: @vars } 96 | Errgent::Renderer.new(options).tap do |renderer| 97 | renderer.send(:action_view).lookup_context.view_paths = VIEW_PATH 98 | end 99 | end 100 | end 101 | end 102 | --------------------------------------------------------------------------------