interception 0.1.pre.1 → 0.1.pre.2
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.
- data/.gitignore +3 -0
- data/.travis.yml +10 -0
- data/Gemfile +2 -0
- data/Rakefile +32 -0
- data/examples/example.rb +21 -0
- data/examples/example2.rb +20 -0
- data/ext/extconf.rb +2 -1
- data/ext/interception.c +22 -3
- data/interception.gemspec +2 -1
- data/lib/cross_platform.rb +67 -0
- data/lib/interception.rb +84 -48
- data/lib/pryception.rb +66 -0
- data/pryly.rb +41 -0
- data/spec/interception_spec.rb +141 -0
- data/spec/spec_helpers.rb +1 -0
- metadata +22 -1
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
task :clean do
|
3
|
+
sh 'rm -f ext/*.o ext/*.so ext/*.dylib'
|
4
|
+
sh 'rm -f ext/org/pryrepl/*.class'
|
5
|
+
end
|
6
|
+
|
7
|
+
desc "Compile *.c files"
|
8
|
+
task :compile => [:clean] do
|
9
|
+
cd 'ext/' do
|
10
|
+
sh 'ruby extconf.rb'
|
11
|
+
sh 'make'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Run example"
|
16
|
+
task :example do
|
17
|
+
sh "ruby -I./lib/ ./examples/example.rb "
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Run example 2"
|
21
|
+
task :example2 do
|
22
|
+
sh "ruby -I./lib/ ./examples/example2.rb "
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Run tests"
|
26
|
+
task :test do
|
27
|
+
sh 'rspec spec -r ./spec/spec_helpers.rb'
|
28
|
+
end
|
29
|
+
|
30
|
+
task :default => [:compile, :test]
|
31
|
+
|
32
|
+
|
data/examples/example.rb
ADDED
data/ext/extconf.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'rbconfig'
|
2
2
|
|
3
|
+
|
3
4
|
if RbConfig::CONFIG['ruby_install_name'] == 'jruby'
|
4
5
|
|
5
6
|
File.open("Makefile", "w") do |f|
|
6
7
|
f.write "install:\n\tjrubyc --javac org/pryrepl/InterceptionEventHook.java\n"
|
7
8
|
end
|
8
9
|
|
9
|
-
elsif RbConfig::CONFIG['ruby_install_name']
|
10
|
+
elsif RbConfig::CONFIG['ruby_install_name'] =~ /^ruby/
|
10
11
|
|
11
12
|
require 'mkmf'
|
12
13
|
$CFLAGS += " -DRUBY_19" if RUBY_VERSION =~ /^1.9/
|
data/ext/interception.c
CHANGED
@@ -2,12 +2,26 @@
|
|
2
2
|
|
3
3
|
static VALUE rb_mInterception;
|
4
4
|
|
5
|
+
extern struct FRAME {
|
6
|
+
VALUE self;
|
7
|
+
int argc;
|
8
|
+
ID last_func;
|
9
|
+
ID orig_func;
|
10
|
+
VALUE last_class;
|
11
|
+
struct FRAME *prev;
|
12
|
+
struct FRAME *tmp;
|
13
|
+
struct RNode *node;
|
14
|
+
int iter;
|
15
|
+
int flags;
|
16
|
+
unsigned long uniq;
|
17
|
+
} *ruby_frame;
|
18
|
+
|
5
19
|
#ifdef RUBY_19
|
6
20
|
|
7
21
|
void
|
8
22
|
interception_hook(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
|
9
23
|
{
|
10
|
-
VALUE binding = rb_funcall(
|
24
|
+
VALUE binding = rb_funcall(self, rb_intern("binding"), 0, NULL);
|
11
25
|
rb_funcall(rb_mInterception, rb_intern("rescue"), 2, rb_errinfo(), binding);
|
12
26
|
}
|
13
27
|
|
@@ -24,7 +38,12 @@ interception_start(VALUE self)
|
|
24
38
|
void
|
25
39
|
interception_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
26
40
|
{
|
27
|
-
VALUE
|
41
|
+
VALUE bself = ruby_frame->prev->self;
|
42
|
+
if (node == ruby_frame->node) {
|
43
|
+
bself = ruby_frame->prev->prev->self;
|
44
|
+
}
|
45
|
+
|
46
|
+
VALUE binding = rb_funcall(bself, rb_intern("binding"), 0, NULL);
|
28
47
|
rb_funcall(rb_mInterception, rb_intern("rescue"), 2, ruby_errinfo, binding);
|
29
48
|
}
|
30
49
|
|
@@ -48,5 +67,5 @@ Init_interception()
|
|
48
67
|
{
|
49
68
|
rb_mInterception = rb_define_module("Interception");
|
50
69
|
rb_define_singleton_method(rb_mInterception, "start", interception_start, 0);
|
51
|
-
rb_define_singleton_method(rb_mInterception, "stop",
|
70
|
+
rb_define_singleton_method(rb_mInterception, "stop", interception_stop, 0);
|
52
71
|
}
|
data/interception.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "interception"
|
3
|
-
s.version = "0.1.pre.
|
3
|
+
s.version = "0.1.pre.2"
|
4
4
|
s.author = "Conrad Irwin"
|
5
5
|
s.email = "conrad.irwin@gmail.com"
|
6
6
|
s.homepage = "http://github.com/ConradIrwin/interception"
|
@@ -12,4 +12,5 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.require_path = "lib"
|
13
13
|
|
14
14
|
s.add_development_dependency 'rake'
|
15
|
+
s.add_development_dependency 'rspec'
|
15
16
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Platform specific implementations of Interception.start and Interception.stop
|
2
|
+
class << Interception
|
3
|
+
private
|
4
|
+
|
5
|
+
# For Rubinius we just monkeypatch Kernel#raise_exception,
|
6
|
+
#
|
7
|
+
# This is normally a thin wrapper around raising an exception on the VM
|
8
|
+
# (so the layer of abstraction below Kernel#raise).
|
9
|
+
if defined? Rubinius
|
10
|
+
|
11
|
+
def start
|
12
|
+
class << Rubinius
|
13
|
+
|
14
|
+
alias raise_with_no_interception raise_exception
|
15
|
+
|
16
|
+
def raise_exception(exc)
|
17
|
+
bt = Rubinius::VM.backtrace(1, true).drop_while do |x|
|
18
|
+
x.variables.method.file.to_s.start_with?("kernel/")
|
19
|
+
end.first
|
20
|
+
b = Binding.setup(bt.variables, bt.variables.method, bt.constant_scope, bt.variables.self, bt)
|
21
|
+
|
22
|
+
Interception.rescue(exc, b)
|
23
|
+
raise_with_no_interception(exc)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def stop
|
29
|
+
class << Rubinius
|
30
|
+
alias raise_exception raise_with_no_interception
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# For JRuby we use the underlying hooks mechanism.
|
35
|
+
#
|
36
|
+
# It seems to work even if I don't pass --debug, but it still
|
37
|
+
# warns about it. So disable the warnings and install the hook.
|
38
|
+
elsif defined?(JRuby)
|
39
|
+
|
40
|
+
require 'java'
|
41
|
+
$CLASSPATH << File.expand_path('../../ext/', __FILE__)
|
42
|
+
java_import org.pryrepl.InterceptionEventHook
|
43
|
+
|
44
|
+
def start
|
45
|
+
old_verbose = $VERBOSE
|
46
|
+
$VERBOSE = nil
|
47
|
+
JRuby.runtime.add_event_hook(hook)
|
48
|
+
ensure
|
49
|
+
$VERBOSE = old_verbose
|
50
|
+
end
|
51
|
+
|
52
|
+
def stop
|
53
|
+
JRuby.runtime.remove_event_hook(hook)
|
54
|
+
end
|
55
|
+
|
56
|
+
def hook
|
57
|
+
@hook ||= InterceptionEventHook.new(proc do |e, b|
|
58
|
+
self.rescue(e, b)
|
59
|
+
end)
|
60
|
+
end
|
61
|
+
|
62
|
+
else #MRI
|
63
|
+
|
64
|
+
require File.expand_path('../../ext/interception', __FILE__)
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
data/lib/interception.rb
CHANGED
@@ -1,19 +1,69 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
|
+
|
4
|
+
# Provides global facility for monitoring exceptions raised in your application.
|
3
5
|
module Interception
|
4
6
|
|
5
7
|
class << self
|
6
|
-
attr_accessor :mutex, :listeners
|
8
|
+
attr_accessor :mutex, :listeners, :rescueing
|
7
9
|
end
|
8
|
-
|
9
10
|
self.mutex = Mutex.new
|
10
11
|
self.listeners = []
|
12
|
+
self.rescueing = false
|
11
13
|
|
14
|
+
# Listen for any exceptions raised.
|
15
|
+
#
|
16
|
+
# The listener block that you pass in will be executed as though inside Kernel#raise,
|
17
|
+
# so your entire program is still actively running. If you have a gem like
|
18
|
+
# pry-stack_explorer you can access the stack frames that lead to the exception
|
19
|
+
# occurring.
|
20
|
+
#
|
21
|
+
# NOTE: Be careful when writing a listener, if your listener raises an
|
22
|
+
# exception it will mask the original exception (though it will not recursively
|
23
|
+
# call your listener).
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
#
|
27
|
+
# # To report exceptions for the entire run of the program:
|
28
|
+
# Interception.listen do |exception, binding|
|
29
|
+
# Emailer.spam!('on-duty@startup.com', exception, binding.eval('self.class.name'))
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
#
|
34
|
+
# # To log exceptions for the duration of a given block.
|
35
|
+
# def log_exceptions(&block)
|
36
|
+
# Interception.listen(block) do |exception, binding|
|
37
|
+
# puts "#{binding.eval("self.inspect")} raised #{exception.inspect}"
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
#
|
43
|
+
# # You can also turn listeners on and off manually
|
44
|
+
#
|
45
|
+
# listener = Proc.new{ |exception, binding|
|
46
|
+
# binding.pry
|
47
|
+
# }
|
48
|
+
# Interception.listen(listener)
|
49
|
+
# Async::Redis.get("foo") do
|
50
|
+
# Interception.unlisten(listener)
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# @param [Proc] for_block (nil) If you pass for_block in, then you will only
|
54
|
+
# intercept exceptions raised while that block
|
55
|
+
# is running.
|
56
|
+
# @param [Proc] listen_block The block to call when an exception occurs,
|
57
|
+
# takes two arguments, the exception and the
|
58
|
+
# binding
|
59
|
+
# @return [Object] The return value of the for_block (if present)
|
60
|
+
# @yield [exception, binding]
|
61
|
+
# @see .unlisten
|
12
62
|
def self.listen(for_block=nil, &listen_block)
|
13
|
-
raise "no block given" unless listen_block || for_block
|
63
|
+
raise ArgumentError, "no block given" unless listen_block || for_block
|
14
64
|
mutex.synchronize{
|
15
|
-
|
16
|
-
|
65
|
+
start if listeners.empty?
|
66
|
+
listeners << (listen_block || for_block)
|
17
67
|
}
|
18
68
|
|
19
69
|
if listen_block && for_block
|
@@ -22,9 +72,15 @@ module Interception
|
|
22
72
|
ensure
|
23
73
|
unlisten listen_block
|
24
74
|
end
|
75
|
+
else
|
76
|
+
listen_block
|
25
77
|
end
|
26
78
|
end
|
27
79
|
|
80
|
+
# Disable a previously added listener
|
81
|
+
#
|
82
|
+
# @param [Proc] listen_block The listen block you wish to remove.
|
83
|
+
# @see .listen
|
28
84
|
def self.unlisten(listen_block)
|
29
85
|
mutex.synchronize{
|
30
86
|
listeners.delete listen_block
|
@@ -32,53 +88,33 @@ module Interception
|
|
32
88
|
}
|
33
89
|
end
|
34
90
|
|
35
|
-
|
91
|
+
# Called by platform-specific implementations whenever an exception is raised.
|
92
|
+
#
|
93
|
+
# The arguments will be forwarded on to all listeners added via {listen} that
|
94
|
+
# haven't been removed via {unlisten}.
|
95
|
+
#
|
96
|
+
# For efficiency, this block will never be called unless there are active
|
97
|
+
# listeners.
|
98
|
+
#
|
99
|
+
# @param [Exception] exception The exception that was raised
|
100
|
+
# @param [Binding] binding The binding from which it was raised
|
101
|
+
def self.rescue(exception, binding)
|
102
|
+
return if rescueing
|
103
|
+
self.rescueing = true
|
36
104
|
listeners.each do |l|
|
37
|
-
l.call(
|
105
|
+
l.call(exception, binding)
|
38
106
|
end
|
107
|
+
ensure
|
108
|
+
self.rescueing = false
|
39
109
|
end
|
40
110
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
alias raise_with_no_interception raise_exception
|
45
|
-
|
46
|
-
def raise_exception(exc)
|
47
|
-
bt = Rubinius::VM.backtrace(1, true).drop_while do |x|
|
48
|
-
x.variables.method.file.to_s.start_with?("kernel/")
|
49
|
-
end.first
|
50
|
-
b = Binding.setup(bt.variables, bt.variables.method, bt.constant_scope, bt.variables.self, bt)
|
51
|
-
|
52
|
-
Interception.rescue(exc, b)
|
53
|
-
raise_with_no_interception(exc)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
111
|
+
# Start sending events to rescue.
|
112
|
+
# Implemented per-platform
|
113
|
+
def self.start; raise NotImplementedError end
|
57
114
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
62
|
-
end
|
63
|
-
elsif defined?(JRuby)
|
64
|
-
$CLASSPATH << File.expand_path('../../ext/', __FILE__)
|
65
|
-
java_import org.pryrepl.InterceptionEventHook
|
115
|
+
# Stop sending events to rescue.
|
116
|
+
# Implemented per-platform
|
117
|
+
def self.stop; raise NotImplementedError end
|
66
118
|
|
67
|
-
|
68
|
-
JRuby.runtime.add_event_hook(hook)
|
69
|
-
end
|
70
|
-
|
71
|
-
def self.stop
|
72
|
-
JRuby.runtime.remove_event_hook(hook)
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.hook
|
76
|
-
@hook ||= InterceptionEventHook.new(proc do |e, b|
|
77
|
-
self.rescue(e, b)
|
78
|
-
end)
|
79
|
-
end
|
80
|
-
|
81
|
-
else
|
82
|
-
require File.expand_path('../../ext/interception.so', __FILE__)
|
83
|
-
end
|
119
|
+
require File.expand_path('../cross_platform.rb', __FILE__)
|
84
120
|
end
|
data/lib/pryception.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'interception'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'pry-stack_explorer'
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
|
10
|
+
module Interception
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
# Intercept all exceptions that arise in the block and start a Pry session
|
15
|
+
# at the fail site.
|
16
|
+
def prycept(&block)
|
17
|
+
raised = []
|
18
|
+
|
19
|
+
Interception.listen(block) do |exception, binding|
|
20
|
+
if defined?(PryStackExplorer)
|
21
|
+
raised << [exception, binding.callers]
|
22
|
+
else
|
23
|
+
raised << [exception, Array(binding)]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ensure
|
28
|
+
if raised.any?
|
29
|
+
exception, bindings = raised.last
|
30
|
+
enter_exception_context(exception, bindings)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Sanitize the call stack.
|
37
|
+
# @param [Array<Binding>] bindings The call stack.
|
38
|
+
def prune_call_stack!(bindings)
|
39
|
+
bindings.delete_if { |b| b.eval("self") == self || b.eval("__method__") == :prycept }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Start a Pry session in the context of the exception.
|
43
|
+
# @param [Exception] exception The exception.
|
44
|
+
# @param [Array<Binding>] bindings The call stack.
|
45
|
+
def enter_exception_context(exception, bindings)
|
46
|
+
inject_local("_ex_", exception, bindings.first)
|
47
|
+
inject_local("_raised_", [exception, bindings.first], bindings.first)
|
48
|
+
|
49
|
+
prune_call_stack!(bindings)
|
50
|
+
if defined?(PryStackExplorer)
|
51
|
+
pry :call_stack => bindings
|
52
|
+
else
|
53
|
+
bindings.first.pry
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Inject a local variable into a binding.
|
58
|
+
def inject_local(var, object, binding)
|
59
|
+
Thread.current[:__intercept_var__] = object
|
60
|
+
binding.eval("#{var} = Thread.current[:__intercept_var__]")
|
61
|
+
ensure
|
62
|
+
Thread.current[:__intercept_var__] = nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
data/pryly.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'interception'
|
3
|
+
require 'pry'
|
4
|
+
require 'pry-stack_explorer'
|
5
|
+
def pryly(&block)
|
6
|
+
raised = []
|
7
|
+
|
8
|
+
Interception.listen(block) do |exception, binding|
|
9
|
+
raised << [exception, binding.callers]
|
10
|
+
end
|
11
|
+
|
12
|
+
ensure
|
13
|
+
if raised.last
|
14
|
+
e, bindings = raised.last
|
15
|
+
$foo = e
|
16
|
+
$bar = raised
|
17
|
+
bindings.first.eval("_ex_ = $foo")
|
18
|
+
bindings.first.eval("_raised_ = $bar")
|
19
|
+
bindings = bindings.drop_while { |b| b.eval("self") == Interception || b.eval("__method__") == :pryly }
|
20
|
+
pry :call_stack => bindings
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
pryly do
|
25
|
+
|
26
|
+
def a
|
27
|
+
begin
|
28
|
+
begin
|
29
|
+
raise "foo"
|
30
|
+
|
31
|
+
rescue => e
|
32
|
+
raise "bar"
|
33
|
+
end
|
34
|
+
|
35
|
+
rescue => e
|
36
|
+
1 / 0
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
a
|
41
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
Interception.listen(proc {
|
2
|
+
begin; raise "fooo"; rescue; end
|
3
|
+
}) do |e, b|
|
4
|
+
$initial_eb = [e,b]
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Interception do
|
8
|
+
|
9
|
+
before do
|
10
|
+
@exceptions = []
|
11
|
+
Interception.listen do |e, b|
|
12
|
+
@exceptions << [e, b]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
after do
|
17
|
+
Interception.listeners.each do |l|
|
18
|
+
Interception.unlisten l
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should allow keeping a log of all exceptions raised" do
|
23
|
+
begin
|
24
|
+
raise "foo"
|
25
|
+
rescue => e
|
26
|
+
#
|
27
|
+
end
|
28
|
+
|
29
|
+
@exceptions.map(&:first).should == [e]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should catch the correct binding" do
|
33
|
+
shoulder = :bucket
|
34
|
+
begin
|
35
|
+
raise "foo"
|
36
|
+
rescue => e
|
37
|
+
#
|
38
|
+
end
|
39
|
+
|
40
|
+
@exceptions.map{ |e, b| b.eval('shoulder') }.should == [:bucket]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should catch the binding on the correct line" do
|
44
|
+
shoulder = :bucket
|
45
|
+
|
46
|
+
line = nil
|
47
|
+
begin
|
48
|
+
line = __LINE__; raise "foo"
|
49
|
+
rescue => e
|
50
|
+
#
|
51
|
+
end
|
52
|
+
|
53
|
+
@exceptions.map{ |e, b| b.eval('__LINE__') }.should == [line]
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should catch all nested exceptions" do
|
57
|
+
|
58
|
+
begin
|
59
|
+
begin
|
60
|
+
raise "foo"
|
61
|
+
rescue => e1
|
62
|
+
raise "bar"
|
63
|
+
end
|
64
|
+
rescue => e2
|
65
|
+
#
|
66
|
+
end
|
67
|
+
|
68
|
+
@exceptions.map(&:first).should == [e1, e2]
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should be able to listen for the duration of a block" do
|
72
|
+
|
73
|
+
e1, e2 = nil
|
74
|
+
block = proc{
|
75
|
+
begin
|
76
|
+
line = __LINE__; raise "foo"
|
77
|
+
rescue => e1
|
78
|
+
#
|
79
|
+
end
|
80
|
+
}
|
81
|
+
Interception.listen(block) do |e, b|
|
82
|
+
e2 = e
|
83
|
+
end
|
84
|
+
|
85
|
+
e1.should == e2
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should allow nested calls to listen with a block" do
|
89
|
+
e1, e2 = nil
|
90
|
+
b1, b2 = nil
|
91
|
+
block = proc{
|
92
|
+
begin
|
93
|
+
raise "foo"
|
94
|
+
rescue => e1
|
95
|
+
#
|
96
|
+
end
|
97
|
+
}
|
98
|
+
block2 = proc{
|
99
|
+
Interception.listen(block) do |e, b|
|
100
|
+
e2 = e
|
101
|
+
b1 = b
|
102
|
+
end
|
103
|
+
}
|
104
|
+
Interception.listen(block2) do |e, b|
|
105
|
+
b2 = b
|
106
|
+
end
|
107
|
+
|
108
|
+
e1.should == e2
|
109
|
+
b1.should == b2
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should be able to handle NoMethodErrors" do
|
113
|
+
shoulder = :bucket
|
114
|
+
|
115
|
+
begin
|
116
|
+
line = __LINE__; "snorkle".desnrok
|
117
|
+
rescue => e1
|
118
|
+
#
|
119
|
+
end
|
120
|
+
|
121
|
+
@exceptions.map{ |e, b| [e] + b.eval('[__LINE__, shoulder, self]') }.should == [[e1, line, :bucket, self]]
|
122
|
+
e1.message.should =~ /desnrok/
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should be able to handle division by 0 errors" do
|
126
|
+
shoulder = :bucket
|
127
|
+
|
128
|
+
begin
|
129
|
+
line = __LINE__; 1 / 0
|
130
|
+
rescue => e1
|
131
|
+
#
|
132
|
+
end
|
133
|
+
|
134
|
+
@exceptions.map{ |e, b| [e] + b.eval('[__LINE__, shoulder, self]') }.should == [[e1, line, :bucket, self]]
|
135
|
+
ZeroDivisionError.should === e1
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should have the right exception and binding at the top level" do
|
139
|
+
$initial_eb.last.eval("self").should == TOPLEVEL_BINDING.eval("self")
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path('../../lib/interception', __FILE__)
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: interception
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: 4
|
5
|
-
version: 0.1.pre.
|
5
|
+
version: 0.1.pre.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Conrad Irwin
|
@@ -22,6 +22,17 @@ dependencies:
|
|
22
22
|
requirement: *2056
|
23
23
|
prerelease: false
|
24
24
|
type: :development
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
version_requirements: &2074 !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ! '>='
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0'
|
32
|
+
none: false
|
33
|
+
requirement: *2074
|
34
|
+
prerelease: false
|
35
|
+
type: :development
|
25
36
|
description: Provides a cross-platform ability to intercept all exceptions as they are raised.
|
26
37
|
email: conrad.irwin@gmail.com
|
27
38
|
executables: []
|
@@ -30,11 +41,21 @@ extensions:
|
|
30
41
|
extra_rdoc_files: []
|
31
42
|
files:
|
32
43
|
- .gitignore
|
44
|
+
- .travis.yml
|
45
|
+
- Gemfile
|
46
|
+
- Rakefile
|
47
|
+
- examples/example.rb
|
48
|
+
- examples/example2.rb
|
33
49
|
- ext/extconf.rb
|
34
50
|
- ext/interception.c
|
35
51
|
- ext/org/pryrepl/InterceptionEventHook.java
|
36
52
|
- interception.gemspec
|
53
|
+
- lib/cross_platform.rb
|
37
54
|
- lib/interception.rb
|
55
|
+
- lib/pryception.rb
|
56
|
+
- pryly.rb
|
57
|
+
- spec/interception_spec.rb
|
58
|
+
- spec/spec_helpers.rb
|
38
59
|
homepage: http://github.com/ConradIrwin/interception
|
39
60
|
licenses: []
|
40
61
|
post_install_message:
|