penny_wise 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/penny_wise.rb +41 -0
- data/lib/penny_wise/configuration.rb +23 -0
- data/lib/penny_wise/error_handler.rb +42 -0
- data/lib/penny_wise/example_initializer.rb +57 -0
- data/lib/penny_wise/exceptions.rb +21 -0
- data/lib/penny_wise/integration.rb +36 -0
- data/lib/penny_wise/integrations/rails.rb +29 -0
- data/lib/penny_wise/integrations/rails/railtie.rb +12 -0
- data/lib/penny_wise/version.rb +3 -0
- data/penny_wise.gemspec +27 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: be5cb9705fdf79a6b448285a6bcdbb9e9e7b8500
|
4
|
+
data.tar.gz: b80260251a8fc79ab341f3ec813975b4623755cb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6f5a69cc0800a7c017ec00f0414ad7435ee8a8a790d38db08cfe72d1f02cbc0be23d187e330711a45bdad418de4e98455c834b8f63054c7d7b487203b1c55bc1
|
7
|
+
data.tar.gz: 2dcc5a1ec2e722b4205442edccd34b616988e3ae321884579d77cebf22f36a8b714bbca4dec4b6853c934ab02654daf0049679a50c052b9952e143d8525f0e20
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Christopher Keele
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# PennyWise
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'penny_wise'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install penny_wise
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/penny_wise.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'active_support/rescuable'
|
4
|
+
require 'active_support/core_ext/object/try'
|
5
|
+
require 'logger'
|
6
|
+
require "penny_wise/exceptions"
|
7
|
+
|
8
|
+
|
9
|
+
module PennyWise
|
10
|
+
|
11
|
+
class << self
|
12
|
+
alias :configure :initialize
|
13
|
+
attr_accessor :configuration
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configure
|
17
|
+
self.configuration ||= Configuration.new
|
18
|
+
yield(configuration) if block_given?
|
19
|
+
require "penny_wise/integration"
|
20
|
+
require "penny_wise/error_handler"
|
21
|
+
configuration
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.root
|
25
|
+
Pathname.new(File.expand_path("../..", __FILE__))
|
26
|
+
end
|
27
|
+
|
28
|
+
# To be overriden by integrations
|
29
|
+
def self.render_errors?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.logger
|
34
|
+
@logger ||= configuration.logger
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
require "penny_wise/configuration"
|
40
|
+
require "penny_wise/integrations/rails/railtie.rb" if defined?(Rails)
|
41
|
+
require "penny_wise/version"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module PennyWise
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
attr_accessor :require_integration, :errors, :error_messages, :error_layout,
|
5
|
+
:error_template, :failure_message, :unknown_error_message, :logger
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@require_integration = true
|
9
|
+
@errors = {}
|
10
|
+
@error_messages = {}
|
11
|
+
@unknown_error_message = ['Unexpected Error', 'It looks like something went wrong.']
|
12
|
+
@error_layout = 'application'
|
13
|
+
@error_template = '/errors/error'
|
14
|
+
@failure_message = "While handling an error, our error handler encountered an error;" \
|
15
|
+
" which is why we built this second one." \
|
16
|
+
"\nThis issue has been registered as critical." \
|
17
|
+
"\nYou can imagine fixing it is now our top priority," \
|
18
|
+
" thank you for discovering it!"
|
19
|
+
@logger = Logger.new(STDERR)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module PennyWise
|
2
|
+
|
3
|
+
module ErrorHandler
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
extend ActiveSupport::Rescuable
|
6
|
+
|
7
|
+
include PennyWise::Integration
|
8
|
+
|
9
|
+
included do
|
10
|
+
if PennyWise.render_errors?
|
11
|
+
PennyWise.configuration.errors.each do |code, exceptions|
|
12
|
+
rescue_from *exceptions, with: ->(exception){ render_error code, exception }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def error
|
18
|
+
render_error(params[:code], PennyWise::Exceptions::RenderedErrorPageException.new)
|
19
|
+
end
|
20
|
+
|
21
|
+
def error_report(exception)
|
22
|
+
"#{exception.class} raised on #{request.fullpath}:" \
|
23
|
+
"\n#{exception.message}" \
|
24
|
+
"\nBacktrace:" \
|
25
|
+
"\n#{exception.backtrace.join("\n")}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def render_error(code, exception)
|
29
|
+
message, description = PennyWise.configuration.error_messages.fetch(code, PennyWise.configuration.unknown_error_message)
|
30
|
+
error_logger = logger.method(code.to_i < 500 ? :info : :error)
|
31
|
+
error_logger.call "#{code}: #{description}.\n" + error_report(exception)
|
32
|
+
@code = code
|
33
|
+
@message = message
|
34
|
+
@description = description
|
35
|
+
error_renderer(code)
|
36
|
+
rescue Exception => handler_exception
|
37
|
+
logger.fatal error_report(PennyWise::Exceptions::ErrorHandlerException.new(exception, handler_exception))
|
38
|
+
failure_renderer(500)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
PennyWise.configure do |config|
|
2
|
+
|
3
|
+
###
|
4
|
+
# A hash of errors mapped to status codes.
|
5
|
+
# `ActiveSupport::Rescuable#rescue_from` will catch these
|
6
|
+
# and pass them to `PennyWise::ErrorHandler`.
|
7
|
+
# Ensure that your general case exception comes first in the list,
|
8
|
+
# otherwise it will catch everything. Not sure why, I think it's
|
9
|
+
# something about `ActiveSupport::Rescuable#rescue_from`.
|
10
|
+
|
11
|
+
config.errors = {
|
12
|
+
500.to_s => [ Exception ],
|
13
|
+
404.to_s => [
|
14
|
+
ActiveRecord::RecordNotFound,
|
15
|
+
ActionController::UnknownController,
|
16
|
+
AbstractController::ActionNotFound,
|
17
|
+
ActionController::RoutingError,
|
18
|
+
],
|
19
|
+
}
|
20
|
+
|
21
|
+
###
|
22
|
+
# A hash of error reports mapped to status codes.
|
23
|
+
# This should be under I18N and L10N instead of here, really.
|
24
|
+
# Takes an array of an error message and a description.
|
25
|
+
#
|
26
|
+
config.error_messages = {
|
27
|
+
401.to_s => [
|
28
|
+
'Unauthorized',
|
29
|
+
'You should log in before you try that!'
|
30
|
+
],
|
31
|
+
403.to_s => [
|
32
|
+
'Forbidden',
|
33
|
+
'You don\'t have permission to do that.'
|
34
|
+
],
|
35
|
+
404.to_s => [
|
36
|
+
'Not Found',
|
37
|
+
'You may have mistyped the address or the page may have moved.'
|
38
|
+
],
|
39
|
+
405.to_s => [
|
40
|
+
'Method Not Allowed',
|
41
|
+
'You may be trying to access our API with an invalid request method.'
|
42
|
+
],
|
43
|
+
406.to_s => [
|
44
|
+
'Not Acceptable',
|
45
|
+
'You may be trying to access our API with invalid request headers.'
|
46
|
+
],
|
47
|
+
500.to_s => [
|
48
|
+
'Internal Server Error',
|
49
|
+
'It looks like something went wrong. Don\'t worry, we\'re on it!'
|
50
|
+
],
|
51
|
+
503.to_s => [
|
52
|
+
'Service Unavailable',
|
53
|
+
'We\'re down for maintainence right now.'
|
54
|
+
],
|
55
|
+
}
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module PennyWise
|
2
|
+
module Exceptions
|
3
|
+
class PennyWiseError < Exception
|
4
|
+
attr_accessor :message, :backtrace
|
5
|
+
end
|
6
|
+
class RenderedErrorPageException < PennyWiseError
|
7
|
+
def initialize
|
8
|
+
@message = 'An error page was explicitly requested.'
|
9
|
+
@backtrace = ['None. No real error occured.']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
class ErrorHandlerException < PennyWiseError
|
13
|
+
def initialize(original_exception, handler_exception)
|
14
|
+
@message = "`ErrorHandler` encountered exception `#{handler_exception.class.name}`" \
|
15
|
+
" while trying to handle exception `#{original_exception.class.name}`:" \
|
16
|
+
"\n#{handler_exception.message}"
|
17
|
+
@backtrace = handler_exception.backtrace
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
Dir.glob( "#{PennyWise.root}/lib/penny_wise/integrations/*.rb", &method(:require) )
|
2
|
+
module PennyWise
|
3
|
+
|
4
|
+
class << self
|
5
|
+
attr_accessor :integrations
|
6
|
+
end
|
7
|
+
PennyWise.integrations = %w[::Rails ::Padrino]
|
8
|
+
|
9
|
+
class IntegrationNotFoundException < PennyWise::Exceptions::PennyWiseError
|
10
|
+
@message = "PennyWise could not find a framework to automatically integrate with." \
|
11
|
+
"\nTried: #{PennyWise.integrations.join(', ')}"
|
12
|
+
end
|
13
|
+
|
14
|
+
module Integration
|
15
|
+
found = nil
|
16
|
+
PennyWise.integrations.each do |integration|
|
17
|
+
begin
|
18
|
+
if defined?(PennyWise.const_get integration)
|
19
|
+
include "PennyWise::Integration#{integration}".constantize
|
20
|
+
found = integration
|
21
|
+
break
|
22
|
+
end
|
23
|
+
rescue NameError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
unless found
|
27
|
+
if PennyWise.configuration.require_integration
|
28
|
+
raise PennyWise::IntegrationNotFoundException
|
29
|
+
else
|
30
|
+
PennyWise.logger.warn "PennyWise did not find a framework to automatically integrate with." \
|
31
|
+
"\nTried: #{PennyWise.integrations.join(', ')}." \
|
32
|
+
"\nRefer to the README to learn how to use PennyWise without integration." \
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module PennyWise
|
2
|
+
|
3
|
+
def self.render_errors?
|
4
|
+
!Rails.application.config.action_dispatch.show_exceptions or !Rails.application.config.consider_all_requests_local
|
5
|
+
end
|
6
|
+
|
7
|
+
module Integration
|
8
|
+
module Rails
|
9
|
+
|
10
|
+
def error_renderer(code)
|
11
|
+
return render PennyWise.configuration.error_template,
|
12
|
+
status: code,
|
13
|
+
layout: PennyWise.configuration.error_layout
|
14
|
+
end
|
15
|
+
|
16
|
+
def failure_renderer(code)
|
17
|
+
render status: code, text: PennyWise.configuration.failure_message
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def routing_error
|
22
|
+
# Raise traditional Rack-level Rails RoutingError at Application-level instead,
|
23
|
+
# so it `rescue_from` in PennyWise's error handler can catch it instead of Rack's.
|
24
|
+
# Works in conjunction with a catchall route pointed at this method.
|
25
|
+
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module PennyWise
|
2
|
+
module Integration
|
3
|
+
module Rails
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
initializer "penny_wise.error_handler" do
|
6
|
+
# Not defined at initializer runtime
|
7
|
+
# ApplicationController.send(:include, PennyWise::ErrorHandler) if defined?(ApplicationController)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/penny_wise.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'penny_wise/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "penny_wise"
|
8
|
+
spec.version = PennyWise::VERSION
|
9
|
+
spec.authors = ["Christopher Keele"]
|
10
|
+
spec.email = ["dev@chriskeele.com"]
|
11
|
+
spec.description = "Easily render customized error pages."
|
12
|
+
spec.summary = "Don't let pennies on the track derail your user experience. Handle all exceptions, big or small, with your own beautiful customized error pages. Framework agnostic with several integrations, supports i18n."
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activesupport", ">= 3.0.0"
|
22
|
+
spec.add_dependency "i18n", ">= 0.6.0"
|
23
|
+
spec.add_dependency "rake", ">= 0.8.0"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rspec", "~> 2.13"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: penny_wise
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christopher Keele
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-04-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: i18n
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.6.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.6.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.8.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.8.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.13'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.13'
|
83
|
+
description: Easily render customized error pages.
|
84
|
+
email:
|
85
|
+
- dev@chriskeele.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- .gitignore
|
91
|
+
- Gemfile
|
92
|
+
- LICENSE.txt
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- lib/penny_wise.rb
|
96
|
+
- lib/penny_wise/configuration.rb
|
97
|
+
- lib/penny_wise/error_handler.rb
|
98
|
+
- lib/penny_wise/example_initializer.rb
|
99
|
+
- lib/penny_wise/exceptions.rb
|
100
|
+
- lib/penny_wise/integration.rb
|
101
|
+
- lib/penny_wise/integrations/rails.rb
|
102
|
+
- lib/penny_wise/integrations/rails/railtie.rb
|
103
|
+
- lib/penny_wise/version.rb
|
104
|
+
- penny_wise.gemspec
|
105
|
+
homepage: ''
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - '>='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.0.0
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Don't let pennies on the track derail your user experience. Handle all exceptions,
|
129
|
+
big or small, with your own beautiful customized error pages. Framework agnostic
|
130
|
+
with several integrations, supports i18n.
|
131
|
+
test_files: []
|
132
|
+
has_rdoc:
|