pry-rescue 0.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,38 +1,18 @@
1
1
 
2
- **pry-rescue** helps you quickly figure out why your code broke; when an exception is raised that would normally kill your program, Pry comes to the rescue, opening a Pry session in the context of code that raised the exception.
3
-
4
- Installation
5
- ============
6
-
7
- Either `gem install pry-rescue`, or add it to the development section of your Gemfile:
8
-
9
- ```ruby
10
- source :rubygems
11
- group :development do
12
- gem 'pry-rescue'
13
- gem 'pry-stack_explorer' # if you're using MRI 1.9 and you want it to be awesome.
14
- end
15
- ```
2
+ **pry-rescue** super-fast, painless, debugging for the (ruby) masses. (See [Pry to the rescue!](https://cirw.in/blog/pry-to-the-rescue))
16
3
 
17
4
  Usage
18
5
  =====
19
6
 
20
- There are *two ways* to use pry-rescue:
21
-
22
- Wrap an entire script
23
- ---------------------
24
-
25
- Use the launcher script:
7
+ First `gem install pry-rescue pry-stack_explorer`. Then run your program with `rescue`
8
+ instead of `ruby`:
26
9
 
27
10
  ```
28
11
  rescue <script.rb> [arguments..]
29
12
  ```
30
13
 
31
- Wrap a block in your code
32
- -------------------------
33
- In development, wrap your code in `Pry::rescue{ }`; then any exceptions that are raised
34
- but not rescued will open a pry session. This is particularly useful for debugging
35
- servers and other long-running processes.
14
+ If you want more fine-grained control over which parts of your code are rescued, you can
15
+ also use the block form:
36
16
 
37
17
  ```ruby
38
18
  require 'pry-rescue'
@@ -47,7 +27,6 @@ Pry.rescue do
47
27
  test
48
28
  end
49
29
  ```
50
-
51
30
  This will land you in a pry-session:
52
31
 
53
32
  ```
@@ -119,9 +98,8 @@ you can get a better idea of why your function ended up in a bad state. Run
119
98
  Known bugs
120
99
  ==========
121
100
 
122
- Occasionally, when using ruby-1.8 or jruby, the value for `self` will be incorrect. You
123
- will still be able to access local variables, but calling methods will not work as you
124
- expect.
101
+ Occasionally, when using ruby-1.8 the value for `self` will be incorrect. You will still
102
+ be able to access local variables, but calling methods will not work as you expect.
125
103
 
126
104
  On rbx we are unable to intercept some exceptions thrown from inside the C++ VM, for
127
105
  example the ZeroDivisionError in `1 / 0`.
@@ -31,7 +31,7 @@ Pry::Commands.create_command "cd-cause", "Move to the previously raised exceptio
31
31
  BANNER
32
32
 
33
33
  def process
34
- raised = target.eval("_raised_ rescue nil")
34
+ raised = target.eval("_raised_.dup rescue nil")
35
35
  raise Pry::CommandError, "cd-cause only works in a pry session created by Pry::rescue{}" unless raised
36
36
  raised.pop
37
37
 
@@ -60,6 +60,6 @@ Pry::Commands.create_command "try-again", "Re-try the code that caused this exce
60
60
  BANNER
61
61
 
62
62
  def process
63
- run "exit", ":try_again"
63
+ throw :try_again
64
64
  end
65
65
  end
@@ -10,21 +10,47 @@ class Pry
10
10
  # @return [Object] The return value of the block
11
11
  def self.rescue(&block)
12
12
  raised = []
13
+ (@raised_stack ||= []) << raised
13
14
 
14
- Interception.listen(block) do |exception, binding|
15
- if defined?(PryStackExplorer)
16
- raised << [exception, binding.callers]
17
- else
18
- raised << [exception, Array(binding)]
15
+ loop do
16
+ catch(:try_again) do
17
+ raised.clear
18
+ begin
19
+ return 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
+ rescue Exception => e
27
+ PryRescue.enter_exception_context(raised)
28
+ raise e
29
+ end
19
30
  end
20
31
  end
32
+ ensure
33
+ @raised_stack.pop
34
+ end
21
35
 
22
- rescue Exception => e
23
- case PryRescue.enter_exception_context(raised)
24
- when :try_again
25
- retry
26
- else
27
- raise
28
- end
36
+ # Start a pry session on an exception that you rescued within a Pry::rescue{ }.
37
+ #
38
+ # @example
39
+ # Pry::rescue do
40
+ # begin
41
+ # raise "foo"
42
+ # rescue => e
43
+ # Pry::rescued(e)
44
+ # end
45
+ # end
46
+ #
47
+ def self.rescued(e=$!)
48
+ raise "Tried to inspect rescued exception outside Pry::rescue{ } block" unless @raised_stack.any?
49
+
50
+ raised = @raised_stack.last.dup
51
+ raised.pop until raised.empty? || raised.last.first == e
52
+ raise "Tried to inspect an exception that was not raised in this Pry::rescue{ } block" unless raised.any?
53
+
54
+ PryRescue.enter_exception_context(raised)
29
55
  end
30
56
  end
data/lib/pry-rescue.rb CHANGED
@@ -17,10 +17,16 @@ class PryRescue
17
17
  # @param [Exception] exception The exception.
18
18
  # @param [Array<Binding>] bindings The call stack.
19
19
  def enter_exception_context(raised)
20
- exception, bindings = raised.last
21
20
 
21
+ exception, bindings = raised.last
22
22
  prune_call_stack!(bindings)
23
23
 
24
+ if bindings.size > 1 && internal_binding?(bindings.first)
25
+ raised.pop
26
+ exception, bindings = raised.last
27
+ prune_call_stack!(bindings)
28
+ end
29
+
24
30
  if defined?(PryStackExplorer)
25
31
  pry :call_stack => bindings, :hooks => pry_hooks(exception, raised)
26
32
  else
@@ -30,6 +36,12 @@ class PryRescue
30
36
 
31
37
  private
32
38
 
39
+ # Is this binding within pry-rescue?
40
+ def internal_binding?(binding)
41
+ binding.eval("__FILE__").start_with?(File.expand_path('../../lib', __FILE__)) ||
42
+ binding.eval("__FILE__").start_with?(File.expand_path('../../bin', __FILE__))
43
+ end
44
+
33
45
  # Define the :before_session hook for the Pry instance.
34
46
  # This ensures that the `_ex_` and `_raised_` sticky locals are
35
47
  # properly set.
data/pry-rescue.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'pry-rescue'
3
- s.version = '0.3'
3
+ s.version = '0.4'
4
4
  s.summary = 'Open a pry session on any unhandled exceptions'
5
5
  s.description = 'Allows you to wrap code in Pry::rescue{ } to open a pry session at any unhandled exceptions'
6
6
  s.homepage = 'https://github.com/ConradIrwin/pry-rescue'
@@ -0,0 +1,96 @@
1
+ require File.expand_path('../../lib/pry-rescue.rb', __FILE__)
2
+
3
+ describe 'Pry.rescue' do
4
+ it 'should call PryRescue.enter_exception_context' do
5
+ lambda{
6
+ PryRescue.should_receive(:enter_exception_context).once
7
+ Pry::rescue{ raise "foobar" }
8
+ }.should raise_error(/foobar/)
9
+ end
10
+
11
+ it "should retry on try-again" do
12
+ @called = 0
13
+ PryRescue.should_receive(:enter_exception_context).once{ throw :try_again }
14
+ Pry::rescue do
15
+ @called += 1
16
+ raise "foobar" if @called == 1
17
+ end
18
+ @called.should == 2
19
+ end
20
+
21
+ it "should try-again from innermost block" do
22
+ @outer = @inner = 0
23
+ PryRescue.should_receive(:enter_exception_context).once{ throw :try_again }
24
+ Pry::rescue do
25
+ @outer += 1
26
+ Pry::rescue do
27
+ @inner += 1
28
+ raise "oops" if @inner == 1
29
+ end
30
+ end
31
+
32
+ @outer.should == 1
33
+ @inner.should == 2
34
+ end
35
+
36
+ it "should clear out exceptions between retrys at the same level" do
37
+ @outer = @inner = 0
38
+ PryRescue.should_receive(:enter_exception_context).once{ |raised| raised.size.should == 1; throw :try_again }
39
+ PryRescue.should_receive(:enter_exception_context).once{ |raised| raised.size.should == 1; throw :try_again }
40
+ Pry::rescue do
41
+ @outer += 1
42
+ Pry::rescue do
43
+ @inner += 1
44
+ raise "oops" if @inner <= 2
45
+ end
46
+ end
47
+ end
48
+
49
+ it "should preserve exceptions between retrys at a higher level" do
50
+ @outer = @inner = 0
51
+ PryRescue.should_receive(:enter_exception_context).once{ |raised| raised.size.should == 1; throw :try_again }
52
+ PryRescue.should_receive(:enter_exception_context).once{ |raised| raised.size.should == 1; throw :try_again }
53
+ PryRescue.should_receive(:enter_exception_context).once{ |raised| raised.size.should == 3; throw :try_again }
54
+ Pry::rescue do
55
+ @outer += 1
56
+ Pry::rescue do
57
+ @inner += 1
58
+ raise "oops" if @inner <= 2
59
+ end
60
+ raise "foops" if @outer == 1
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "Pry.rescued" do
66
+
67
+ it "should raise an error if used outwith Pry::rescue" do
68
+ begin
69
+ raise "foo"
70
+ rescue => e
71
+ lambda{
72
+ Pry.rescued(e)
73
+ }.should raise_error(/outside Pry::rescue/)
74
+ end
75
+ end
76
+
77
+ it "should raise an error if used on an exception not raised" do
78
+ Pry::rescue do
79
+ lambda{
80
+ Pry.rescued(RuntimeError.new("foo").exception)
81
+ }.should raise_error(/not raised/)
82
+ end
83
+ end
84
+
85
+ it "should call Pry.enter_exception_context" do
86
+ Pry::rescue do
87
+ begin
88
+ raise "foo"
89
+ rescue => e
90
+ PryRescue.should_receive(:enter_exception_context).once
91
+ Pry::rescued(e)
92
+ end
93
+ end
94
+ end
95
+ end
96
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pry-rescue
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.3'
4
+ version: '0.4'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-08-15 00:00:00.000000000 Z
14
+ date: 2012-08-26 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: interception
@@ -51,6 +51,7 @@ files:
51
51
  - lib/pry-rescue/commands.rb
52
52
  - lib/pry-rescue/core_ext.rb
53
53
  - pry-rescue.gemspec
54
+ - spec/core_ext_spec.rb
54
55
  homepage: https://github.com/ConradIrwin/pry-rescue
55
56
  licenses: []
56
57
  post_install_message: