nested_exceptions 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +75 -0
- data/examples/example.rb +66 -0
- data/lib/nested_exceptions.rb +41 -10
- data/lib/nested_exceptions/define.rb +2 -2
- data/lib/nested_exceptions/version.rb +1 -1
- metadata +5 -3
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# NestedExceptions
|
2
|
+
|
3
|
+
This simple library adds support for nested exceptions in Ruby.
|
4
|
+
|
5
|
+
## Why?
|
6
|
+
|
7
|
+
It can be really painful to debug a situation where a library rescues an
|
8
|
+
exception and throws a different one, or when it has a bug in its
|
9
|
+
exception handling logic. This gem goes a diving catch and recovers the
|
10
|
+
play!
|
11
|
+
|
12
|
+
The best way to learn more about this topic is to investigate the great
|
13
|
+
source of information at the [Exceptional Ruby](http://exceptionalruby.com/) site.
|
14
|
+
|
15
|
+
## How do I use it?
|
16
|
+
|
17
|
+
The best way to use it is to simply add the following line of code to
|
18
|
+
your project:
|
19
|
+
|
20
|
+
require 'nested_exceptions/global'
|
21
|
+
|
22
|
+
That includes the NestedExceptions module in all of Ruby's base
|
23
|
+
exception classes (except the Exception root class), magically enabling
|
24
|
+
you to debug the trickiest, buggiest libraries.
|
25
|
+
|
26
|
+
### I'm not sure I want to include it globally
|
27
|
+
|
28
|
+
If you want to try it out without extending Ruby's exception classes,
|
29
|
+
you can also do the following:
|
30
|
+
|
31
|
+
require 'nested_exceptions'
|
32
|
+
|
33
|
+
class MyException < StandardError
|
34
|
+
include NestedExceptions
|
35
|
+
end
|
36
|
+
|
37
|
+
That will only extend your class and leave the rest of the exception
|
38
|
+
classes untouched.
|
39
|
+
|
40
|
+
## Does it change anything?
|
41
|
+
|
42
|
+
Yes. A bit of extra information is added to your backtraces. In addition
|
43
|
+
to the standard backtrace, you'll get a little bit of nesting
|
44
|
+
information.
|
45
|
+
|
46
|
+
ruby examples/example.rb original
|
47
|
+
|
48
|
+
examples/example.rb:32:in `rescue in double_bug': oops (RuntimeError)
|
49
|
+
from examples/example.rb:30:in `double_bug'
|
50
|
+
from examples/example.rb:42:in `<main>'
|
51
|
+
|
52
|
+
|
53
|
+
ruby examples/example.rb nested
|
54
|
+
|
55
|
+
examples/example.rb:30:in `rescue in double_bug': oops (RuntimeError)
|
56
|
+
from --- cause: ErrorSpec::InternalError: problem
|
57
|
+
from examples/example.rb:20:in `rescue in problem'
|
58
|
+
from --- cause: RuntimeError: bug
|
59
|
+
from examples/example.rb:10:in `bug'
|
60
|
+
from examples/example.rb:14:in `nested_bug'
|
61
|
+
from examples/example.rb:18:in `problem'
|
62
|
+
from examples/example.rb:24:in `nested_problem'
|
63
|
+
from examples/example.rb:28:in `double_bug'
|
64
|
+
from examples/example.rb:35:in `<main>'
|
65
|
+
|
66
|
+
It also adds two methods to the exception:
|
67
|
+
|
68
|
+
* #root_cause: allows you to easily get the first exception
|
69
|
+
* #cause: get the exception (if any) that may have caused this one
|
70
|
+
|
71
|
+
## Gotchas
|
72
|
+
|
73
|
+
Do not include the NestedExceptions module in a subclass before adding
|
74
|
+
it to its superclass. That will cause the nested exceptions feature to
|
75
|
+
deactivate itself to prevent a stack overflow.
|
data/examples/example.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
$: << './lib'
|
4
|
+
require 'nested_exceptions'
|
5
|
+
|
6
|
+
module ErrorSpec
|
7
|
+
|
8
|
+
NestedExceptions.define_standard_exception_classes_in ErrorSpec
|
9
|
+
|
10
|
+
class Example
|
11
|
+
def bug
|
12
|
+
raise 'bug'
|
13
|
+
end
|
14
|
+
|
15
|
+
def nested_bug
|
16
|
+
bug
|
17
|
+
end
|
18
|
+
|
19
|
+
def problem
|
20
|
+
nested_bug
|
21
|
+
rescue
|
22
|
+
raise InternalError, 'problem'
|
23
|
+
end
|
24
|
+
|
25
|
+
def nested_problem
|
26
|
+
problem
|
27
|
+
end
|
28
|
+
|
29
|
+
def double_bug
|
30
|
+
nested_problem
|
31
|
+
rescue
|
32
|
+
raise 'oops'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
example = ErrorSpec::Example.new
|
38
|
+
|
39
|
+
if ARGV.first == 'original'
|
40
|
+
|
41
|
+
puts "Original exception:"
|
42
|
+
example.double_bug
|
43
|
+
|
44
|
+
elsif ARGV.first == 'nested'
|
45
|
+
|
46
|
+
require 'nested_exceptions/global'
|
47
|
+
begin
|
48
|
+
example.double_bug
|
49
|
+
rescue StandardError => e
|
50
|
+
if Object.const_defined? :RUBY_ENGINE and (RUBY_ENGINE == 'jruby' or RUBY_ENGINE == 'rbx')
|
51
|
+
puts "Note that JRuby and Rubinius do not display the modified backtrace:"
|
52
|
+
pp e.backtrace
|
53
|
+
puts
|
54
|
+
end
|
55
|
+
puts "Nested exception:"
|
56
|
+
raise
|
57
|
+
end
|
58
|
+
|
59
|
+
else
|
60
|
+
|
61
|
+
puts "Usage:"
|
62
|
+
puts
|
63
|
+
puts "ruby #{ $0 } [original|nested]"
|
64
|
+
|
65
|
+
end
|
66
|
+
|
data/lib/nested_exceptions.rb
CHANGED
@@ -6,18 +6,33 @@ module NestedExceptions
|
|
6
6
|
attr_reader :cause
|
7
7
|
|
8
8
|
def initialize(message = nil, cause = nil)
|
9
|
-
@cause
|
10
|
-
|
9
|
+
# @cause could be defined if this module is included multiple
|
10
|
+
# times in a class heirarchy.
|
11
|
+
@illegal_nesting = defined? @cause
|
12
|
+
@recursing = false
|
13
|
+
if @illegal_nesting
|
14
|
+
warn "WARNING: NestedExceptions is included in the class heirarchy of #{ self.class } more than once."
|
15
|
+
warn "- Ensure if you require 'nested_exceptions/global' or manually add"
|
16
|
+
warn " NestedExceptions to a built-in Ruby exception class, you must do it at"
|
17
|
+
warn " the beginning of the program."
|
18
|
+
else
|
19
|
+
@cause = cause || $!
|
20
|
+
super(message)
|
21
|
+
end
|
11
22
|
end
|
12
23
|
|
13
|
-
if Object.const_defined? :RUBY_ENGINE and RUBY_ENGINE == 'jruby' or RUBY_ENGINE == 'rbx'
|
24
|
+
if Object.const_defined? :RUBY_ENGINE and (RUBY_ENGINE == 'jruby' or RUBY_ENGINE == 'rbx')
|
14
25
|
def backtrace
|
15
|
-
|
16
|
-
|
26
|
+
prevent_recursion do
|
27
|
+
return @processed_backtrace if defined? @processed_backtrace and @processed_backtrace
|
28
|
+
@processed_backtrace = process_backtrace(super)
|
29
|
+
end
|
17
30
|
end
|
18
31
|
else
|
19
32
|
def set_backtrace(bt)
|
20
|
-
|
33
|
+
prevent_recursion do
|
34
|
+
super process_backtrace(bt)
|
35
|
+
end
|
21
36
|
end
|
22
37
|
end
|
23
38
|
|
@@ -33,18 +48,34 @@ module NestedExceptions
|
|
33
48
|
|
34
49
|
protected
|
35
50
|
|
51
|
+
# This shouldn't be necessary anymore
|
52
|
+
def prevent_recursion
|
53
|
+
if not @recursing and not @illegal_nesting
|
54
|
+
begin
|
55
|
+
@recursing = true
|
56
|
+
yield
|
57
|
+
ensure
|
58
|
+
@recursing = false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
36
63
|
def process_backtrace(bt)
|
37
|
-
|
38
|
-
|
64
|
+
bt ||= []
|
65
|
+
if @cause and not @illegal_nesting
|
66
|
+
cause_backtrace = @cause.backtrace || []
|
67
|
+
cause_backtrace.reverse.each do |line|
|
39
68
|
if bt.last == line
|
40
69
|
bt.pop
|
41
70
|
else
|
42
71
|
break
|
43
72
|
end
|
44
73
|
end
|
45
|
-
bt << "--- cause: #{cause.class
|
46
|
-
bt.concat
|
74
|
+
bt << "--- cause: #{@cause.class}: #{@cause}"
|
75
|
+
bt.concat cause_backtrace
|
47
76
|
end
|
48
77
|
bt
|
78
|
+
rescue Exception => e
|
79
|
+
warn "exception processing backtrace: #{ e.class }: #{ e.message }\n #{ caller(0).join("\n ") }"
|
49
80
|
end
|
50
81
|
end
|
@@ -5,9 +5,9 @@ module NestedExceptions
|
|
5
5
|
#
|
6
6
|
# I actually recommend that you just define these manually, but this may be
|
7
7
|
# a handy shortcut in smaller projects.
|
8
|
-
def define_standard_exception_classes_in(target_module)
|
8
|
+
def define_standard_exception_classes_in(target_module, add_module = false)
|
9
9
|
definitions = %{
|
10
|
-
class Error < StandardError; include NestedExceptions; end
|
10
|
+
class Error < StandardError; #{ add_module ? 'include NestedExceptions;' : '' } end
|
11
11
|
class UserError < Error; end
|
12
12
|
class LogicError < Error; end
|
13
13
|
class ClientError < LogicError; end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nested_exceptions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3005118107932468525
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 1
|
10
|
+
version: 1.0.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Darrick Wiebe
|
@@ -45,7 +45,9 @@ files:
|
|
45
45
|
- .gitignore
|
46
46
|
- .rspec
|
47
47
|
- Gemfile
|
48
|
+
- README.md
|
48
49
|
- Rakefile
|
50
|
+
- examples/example.rb
|
49
51
|
- lib/nested_exceptions.rb
|
50
52
|
- lib/nested_exceptions/define.rb
|
51
53
|
- lib/nested_exceptions/global.rb
|