rack-rescue 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Users/dneighman/Projects/rack-rescue/Gemfile +15 -0
- data/Users/dneighman/Projects/rack-rescue/Gemfile.lock +93 -0
- data/Users/dneighman/Projects/rack-rescue/LICENSE +20 -0
- data/Users/dneighman/Projects/rack-rescue/README.textile +80 -0
- data/Users/dneighman/Projects/rack-rescue/Rakefile +26 -0
- data/Users/dneighman/Projects/rack-rescue/TODO.textile +7 -0
- data/Users/dneighman/Projects/rack-rescue/lib/rack-rescue.rb +1 -0
- data/Users/dneighman/Projects/rack-rescue/lib/rack/rescue.rb +93 -0
- data/Users/dneighman/Projects/rack-rescue/lib/rack/rescue/default_exceptions.rb +16 -0
- data/Users/dneighman/Projects/rack-rescue/lib/rack/rescue/exceptions.rb +107 -0
- data/Users/dneighman/Projects/rack-rescue/lib/rack/rescue/handler.rb +124 -0
- data/Users/dneighman/Projects/rack-rescue/lib/rack/rescue/responder.rb +21 -0
- data/Users/dneighman/Projects/rack-rescue/lib/rack/rescue/templates/rack_rescue_templates/error.development.html.erb +19 -0
- data/Users/dneighman/Projects/rack-rescue/lib/rack/rescue/templates/rack_rescue_templates/error.html.erb +15 -0
- data/Users/dneighman/Projects/rack-rescue/lib/rack/rescue/templates/rack_rescue_templates/error.text.erb +14 -0
- data/Users/dneighman/Projects/rack-rescue/lib/rack/rescue/templates/rack_rescue_templates/not_found.text.erb +4 -0
- data/Users/dneighman/Projects/rack-rescue/rack-rescue.gemspec +26 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack-rescue_spec.rb +151 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/exceptions_spec.rb +85 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/alternate_errors/alternate_exceptions.foo_env.html.erb +1 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/alternate_errors/alternate_exceptions.html.erb +1 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/alternate_errors/custom_exception.text.erb +1 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/alternate_errors/error.xml.erb +5 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/alternate_errors/inherit_from_error_backtrace.text.erb +4 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/alternate_errors/inherit_from_error_header.text.erb +4 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/alternate_errors/inherit_from_error_message.text.erb +4 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/alternate_errors/not_found.text.erb +7 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/alternate_errors/not_found.xml.erb +3 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/layouts/custom_error_layout.xml.erb +4 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/fixtures/layouts/error.html.erb +7 -0
- data/Users/dneighman/Projects/rack-rescue/spec/rack/rescue/handler_spec.rb +112 -0
- data/Users/dneighman/Projects/rack-rescue/spec/spec.opts +2 -0
- data/Users/dneighman/Projects/rack-rescue/spec/spec_helper.rb +22 -0
- metadata +213 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
---
|
2
|
+
dependencies:
|
3
|
+
pancake:
|
4
|
+
group:
|
5
|
+
- :default
|
6
|
+
version: ">= 0.2"
|
7
|
+
rake:
|
8
|
+
group:
|
9
|
+
- :default
|
10
|
+
version: ">= 0"
|
11
|
+
any_view:
|
12
|
+
group:
|
13
|
+
- :default
|
14
|
+
version: ">= 0.2"
|
15
|
+
hashie:
|
16
|
+
group:
|
17
|
+
- :default
|
18
|
+
version: ">= 0"
|
19
|
+
rspec:
|
20
|
+
group:
|
21
|
+
- :test
|
22
|
+
version: ">= 0"
|
23
|
+
dirge:
|
24
|
+
group:
|
25
|
+
- :default
|
26
|
+
version: ">= 0"
|
27
|
+
rack:
|
28
|
+
group:
|
29
|
+
- :default
|
30
|
+
version: ">= 0"
|
31
|
+
extlib:
|
32
|
+
group:
|
33
|
+
- :default
|
34
|
+
version: ">= 0"
|
35
|
+
tilt:
|
36
|
+
group:
|
37
|
+
- :default
|
38
|
+
version: ">= 0.10"
|
39
|
+
specs:
|
40
|
+
- rake:
|
41
|
+
version: 0.8.7
|
42
|
+
- builder:
|
43
|
+
version: 2.1.2
|
44
|
+
- i18n:
|
45
|
+
version: 0.3.7
|
46
|
+
- memcache-client:
|
47
|
+
version: 1.8.3
|
48
|
+
- tzinfo:
|
49
|
+
version: 0.3.22
|
50
|
+
- activesupport:
|
51
|
+
version: 3.0.0.beta3
|
52
|
+
- columnize:
|
53
|
+
version: 0.3.1
|
54
|
+
- linecache:
|
55
|
+
version: "0.43"
|
56
|
+
- ruby-debug-base:
|
57
|
+
version: 0.10.3
|
58
|
+
- ruby-debug:
|
59
|
+
version: 0.10.3
|
60
|
+
- tilt:
|
61
|
+
version: "0.10"
|
62
|
+
- any_view:
|
63
|
+
version: 0.2.2
|
64
|
+
- dirge:
|
65
|
+
version: 0.0.4
|
66
|
+
- extlib:
|
67
|
+
version: 0.9.15
|
68
|
+
- fuzzyhash:
|
69
|
+
version: 0.0.11
|
70
|
+
- hashie:
|
71
|
+
version: 0.2.0
|
72
|
+
- json:
|
73
|
+
version: 1.4.3
|
74
|
+
- rack:
|
75
|
+
version: 1.1.0
|
76
|
+
- rack-accept-media-types:
|
77
|
+
version: "0.9"
|
78
|
+
- rack-test:
|
79
|
+
version: 0.5.4
|
80
|
+
- thor:
|
81
|
+
version: 0.13.6
|
82
|
+
- usher:
|
83
|
+
version: 0.7.1
|
84
|
+
- wrapt:
|
85
|
+
version: 0.1.5
|
86
|
+
- pancake:
|
87
|
+
version: 0.2.0
|
88
|
+
- rspec:
|
89
|
+
version: 1.3.0
|
90
|
+
hash: 5cef1217c6aaf844669831ef4a76bb2057a382b7
|
91
|
+
sources:
|
92
|
+
- Rubygems:
|
93
|
+
uri: http://gemcutter.org
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Daniel Neighman
|
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.
|
@@ -0,0 +1,80 @@
|
|
1
|
+
h1. Rack::Rescue
|
2
|
+
|
3
|
+
Rack Rescue is a middleware for handling exceptions in your rack application.
|
4
|
+
|
5
|
+
Rack::Rescue will set the correct status, render a template, and optionally run any number of callbacks when an exception is experienced.
|
6
|
+
|
7
|
+
Just insert the middleware in your stack, and any unhandled exception will trigger the handling. Unknown exceptions trigger a basic error page, with a 500 status.
|
8
|
+
|
9
|
+
Rather than launch into a long-winded chat about what it can do, I'll suffice with a list.
|
10
|
+
|
11
|
+
* Per exception status codes
|
12
|
+
* Per exception templates (with a fallback template)
|
13
|
+
* Content type negotiation (:html, :xml, :js etc)
|
14
|
+
* cascading tempate locations
|
15
|
+
* template naming of the usual <name>.<format>.<(erb|haml|other tilt template)>
|
16
|
+
* template naming including the RACK_ENV
|
17
|
+
** <name>.<RACK_ENV>.<format>.<(tilt template engine ext)>
|
18
|
+
** If the more specific template is not found, it will fall back to just name, format and ext
|
19
|
+
* template inheritance
|
20
|
+
* Arbitrary exception hooks
|
21
|
+
|
22
|
+
h2. Adding Exceptions
|
23
|
+
|
24
|
+
<pre><code>
|
25
|
+
# Add to the default list
|
26
|
+
Rack::Rescue::Exceptions.add_defaults("MyException", "AnotherException", :status => 404)
|
27
|
+
Rack::Rescue::Exceptions.add_defaults("DifferetOne", :status => 455, :template => "different")
|
28
|
+
|
29
|
+
# Add to a specific Rack::Rescue
|
30
|
+
use Rack::Rescue do |rr|
|
31
|
+
rr.add("SomeException", :status => 478, :template => "something")
|
32
|
+
end
|
33
|
+
</code></pre>
|
34
|
+
|
35
|
+
h2. Add a template location
|
36
|
+
|
37
|
+
By default templates should be located in
|
38
|
+
|
39
|
+
<handler root>/rack_rescue_templates/**/*
|
40
|
+
|
41
|
+
To add a new handler root
|
42
|
+
|
43
|
+
<pre><code>
|
44
|
+
Rack::Rescue::Handler.roots << File.join(Dir.pwd, "views")
|
45
|
+
</pre></code>
|
46
|
+
|
47
|
+
This will then look in "./views/rack_rescue_template" directory for you templates. If it doesn't find them in there it will fall back to the templates located in any other roots, or finall to the default templates it ships with.
|
48
|
+
|
49
|
+
h2. Template Naming
|
50
|
+
|
51
|
+
Templates are named with the following form, in the following preference:
|
52
|
+
|
53
|
+
1. name.env.format.ext # error.development.html.erb
|
54
|
+
2. name.format.ext # error.html.erb
|
55
|
+
|
56
|
+
If the first is not matched, the second will be also checked
|
57
|
+
|
58
|
+
h2. Arbitrary Exception hooks
|
59
|
+
|
60
|
+
When an exception is detected, any exception hooks that have been declared are executed before the template is rendered and the response returned to the client.
|
61
|
+
|
62
|
+
<pre><code>
|
63
|
+
Rack::Rescue.add_handler do |exception, env, options|
|
64
|
+
# email me the exception!
|
65
|
+
end
|
66
|
+
</code></pre>
|
67
|
+
|
68
|
+
h1. Note on Patches/Pull Requests
|
69
|
+
|
70
|
+
* Fork the project.
|
71
|
+
* Make your feature addition or bug fix.
|
72
|
+
* Add tests for it. This is important so I don't break it in a
|
73
|
+
future version unintentionally.
|
74
|
+
* Commit, do not mess with rakefile, version, or history.
|
75
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
76
|
+
* Send me a pull request. Bonus points for topic branches.
|
77
|
+
|
78
|
+
h2. Copyright
|
79
|
+
|
80
|
+
Copyright (c) 2010 Daniel Neighman. See LICENSE for details.
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
6
|
+
spec.libs << 'lib' << 'spec'
|
7
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
11
|
+
spec.libs << 'lib' << 'spec'
|
12
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
13
|
+
spec.rcov = true
|
14
|
+
end
|
15
|
+
|
16
|
+
task :default => :spec
|
17
|
+
|
18
|
+
require 'rake/rdoctask'
|
19
|
+
Rake::RDocTask.new do |rdoc|
|
20
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
21
|
+
|
22
|
+
rdoc.rdoc_dir = 'rdoc'
|
23
|
+
rdoc.title = "rack-rescue #{version}"
|
24
|
+
rdoc.rdoc_files.include('README*')
|
25
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
26
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
h1. TODO
|
2
|
+
|
3
|
+
* Add simple helpers for the view
|
4
|
+
* Add default templates for differnt formats (content negotiation)
|
5
|
+
* Make the default handler inheritance safe (DEFAULT_HANLDERS)
|
6
|
+
* Provide a format check to use in preference to the normal content_negotiation
|
7
|
+
* Make it layout aware
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'rack/rescue'
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'pancake'
|
2
|
+
module Rack
|
3
|
+
# Rack Middleware for rescuing exceptions and responding to the client
|
4
|
+
# With a page that is descriptive
|
5
|
+
class Rescue
|
6
|
+
autoload :Exceptions, 'rack/rescue/default_exceptions'
|
7
|
+
autoload :Handler, 'rack/rescue/handler'
|
8
|
+
autoload :Responder, 'rack/rescue/responder'
|
9
|
+
|
10
|
+
inheritable_inner_classes "Exceptions", "Handler", "Responder"
|
11
|
+
class_inheritable_accessor :error_handlers
|
12
|
+
self.error_handlers = []
|
13
|
+
|
14
|
+
# Make sure there are a minimum set of formats available
|
15
|
+
[:html, :xml, :text, :js, :json].each do |f|
|
16
|
+
::Pancake::MimeTypes.group(f)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.default_formats
|
20
|
+
return @default_formats if @default_formats
|
21
|
+
|
22
|
+
if ENV['RACK_ENV'] == 'production'
|
23
|
+
@default_formats = Pancake::MimeTypes.groups.keys.map(&:to_sym)
|
24
|
+
else
|
25
|
+
Pancake::MimeTypes.groups.keys.map(&:to_sym)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.add_handler(&blk)
|
30
|
+
error_handlers << blk
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(app, options = {})
|
34
|
+
@app = app
|
35
|
+
load_defaults = options.fetch(:load_default_exceptions, true)
|
36
|
+
@exceptions_map = Exceptions.new(load_defaults)
|
37
|
+
@formats = options[:formats]
|
38
|
+
yield @exceptions_map if block_given?
|
39
|
+
end
|
40
|
+
|
41
|
+
def formats
|
42
|
+
@formats || self.class.default_formats
|
43
|
+
end
|
44
|
+
|
45
|
+
def formats=(fmts)
|
46
|
+
@formats = fmts
|
47
|
+
end
|
48
|
+
|
49
|
+
def call(env)
|
50
|
+
@app.call(env)
|
51
|
+
rescue Exception => e
|
52
|
+
handle_exception(env, e)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
# Apply the layout if it exists
|
57
|
+
# @api private
|
58
|
+
def apply_layout(env, content, opts)
|
59
|
+
if layout = env['layout']
|
60
|
+
layout.format = opts[:format]
|
61
|
+
layout.content = content
|
62
|
+
layout.template_name = opts[:layout] if layout.template_name?(opts[:layout], opts)
|
63
|
+
layout
|
64
|
+
else
|
65
|
+
content
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_exception(env, e)
|
70
|
+
# negotiate the content
|
71
|
+
request = Rack::Request.new(env)
|
72
|
+
responder = Responder.new(env, self)
|
73
|
+
opts = {}
|
74
|
+
if request.path =~ /\.(.\w)$/
|
75
|
+
opts[:format] = $1
|
76
|
+
end
|
77
|
+
responder.negotiate! opts
|
78
|
+
|
79
|
+
opts[:format] = responder.content_type
|
80
|
+
opts[:layout] ||= 'error'
|
81
|
+
|
82
|
+
self.class.error_handlers.each{|blk| blk.call(e, env, opts)}
|
83
|
+
|
84
|
+
handler = @exceptions_map[e].nil? ? @exceptions_map[RuntimeError] : @exceptions_map[e]
|
85
|
+
|
86
|
+
resp, status = handler.render_error(e, opts), handler.status
|
87
|
+
|
88
|
+
response = apply_layout(env, resp, opts)
|
89
|
+
|
90
|
+
Rack::Response.new(response, status, responder.headers).finish
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rack/rescue/exceptions'
|
2
|
+
|
3
|
+
class Rack::Rescue::Exceptions
|
4
|
+
DEFAULT_HANDLERS = [
|
5
|
+
["RuntimeError", {:status => 500}],
|
6
|
+
["DataMapper::ObjectNotFoundError", {:status => 404}],
|
7
|
+
["ActiveRecord::RecordNotFound", {:status => 404}],
|
8
|
+
["Pancake::Errors::NotFound", {:status => 404, :template => :not_found}],
|
9
|
+
["Pancake::Errors::UnknownRouter", {:status => 500}],
|
10
|
+
["Pancake::Errors::UnknownConfiguration", {:status => 500}],
|
11
|
+
["Pancake::Errors::Unauthorized", {:status => 401}],
|
12
|
+
["Pancake::Errors::Forbidden", {:status => 403}],
|
13
|
+
["Pancake::Errors::Server", {:status => 500}],
|
14
|
+
["Pancake::Errors::NotAcceptable", {:status => 406}]
|
15
|
+
].map{|(name, opts)| Rack::Rescue::Handler.new(name, opts)}
|
16
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Rack
|
2
|
+
class Rescue
|
3
|
+
class Exceptions
|
4
|
+
|
5
|
+
# Add default exceptions to handle.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# Rack::Rescue::Exceptions.add_defaults("MyException", "AnotherException", :status => 404)
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
# @see Rack::Rescue::Exceptions::Handler
|
12
|
+
# @see Rack::Rescue::Exceptions#add
|
13
|
+
def self.add_defaults(*exceptions)
|
14
|
+
opts = Hash === exceptions.last ? exceptions.pop : {}
|
15
|
+
exceptions.each do |e|
|
16
|
+
DEFAULT_HANDLERS << Handler.new(e, opts)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Remove a deafult exception from the list
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# Rack::Rescue::Exceptions.remove_defaults("MyException", "AnotherException")
|
24
|
+
# Rack::Rescue::Exceptions.remove_defaults(/MyKindOfException/)
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
# @see Rack::Rescue::Exceptions.add_defaults
|
28
|
+
def self.remove_defaults(*exceptions)
|
29
|
+
removed = []
|
30
|
+
exceptions.each do |e|
|
31
|
+
DEFAULT_HANDLERS.each do |h|
|
32
|
+
match = case e
|
33
|
+
when String
|
34
|
+
h.name == e
|
35
|
+
when Regexp
|
36
|
+
h.name =~ e
|
37
|
+
end
|
38
|
+
removed << h if match
|
39
|
+
end
|
40
|
+
end
|
41
|
+
removed.each{|r| DEFAULT_HANDLERS.delete(r) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param load_defaults Load the default list of exceptions into this instance
|
45
|
+
def initialize(load_defaults = true)
|
46
|
+
@exception_handlers = {}
|
47
|
+
load_defaults! if load_defaults
|
48
|
+
end
|
49
|
+
|
50
|
+
# Loads the default list of exceptions defined in
|
51
|
+
# Rack::Rescue::Exceptions::DEFAULT_HANDLERS
|
52
|
+
# @api public
|
53
|
+
def load_defaults!
|
54
|
+
DEFAULT_HANDLERS.each do |handler|
|
55
|
+
exception_handlers[handler.name] = handler
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Add an exception handler to Rack::Rescue.
|
60
|
+
# Whenever Rack::Rescue rescues an exception, it will check it's list to see what to do with it.
|
61
|
+
#
|
62
|
+
# @param exceptions a list of exceptions with an optional options hash on the end
|
63
|
+
#
|
64
|
+
# @option :status [Integer] An integer representing the http response code that should be associated with this exception
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
# @see Rack::Rescue::Exceptions.add_defaults
|
68
|
+
# @see Rack::Rescue::Exceptions::Handler
|
69
|
+
def add(*exceptions)
|
70
|
+
opts = Hash === exceptions.last ? exceptions.pop : {}
|
71
|
+
exceptions.each do |e|
|
72
|
+
name = exception_name(e)
|
73
|
+
exception_handlers[name] = Handler.new(e, opts)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Remove an exception handler from this instance of Rack::Rescue::Exceptions
|
78
|
+
# This will not remove a default handler
|
79
|
+
def delete(e)
|
80
|
+
exception_handlers.delete(exception_name(e))
|
81
|
+
end
|
82
|
+
|
83
|
+
def exception_handlers
|
84
|
+
@exception_handlers
|
85
|
+
end
|
86
|
+
|
87
|
+
def [](exception)
|
88
|
+
exception_handlers[exception_name(exception)]
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.exception_name(e)
|
92
|
+
case e
|
93
|
+
when String
|
94
|
+
e
|
95
|
+
when Class
|
96
|
+
e.name
|
97
|
+
else
|
98
|
+
e.class.name
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def exception_name(e)
|
103
|
+
self.class.exception_name(e)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|