pry-rescue 0.3
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/LICENSE.MIT +19 -0
- data/README.md +133 -0
- data/Rakefile +22 -0
- data/bin/rescue +32 -0
- data/examples/example.rb +21 -0
- data/examples/example2.rb +20 -0
- data/lib/pry-rescue.rb +54 -0
- data/lib/pry-rescue/cli.rb +1 -0
- data/lib/pry-rescue/commands.rb +65 -0
- data/lib/pry-rescue/core_ext.rb +30 -0
- data/pry-rescue.gemspec +14 -0
- metadata +79 -0
data/LICENSE.MIT
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 Conrad Irwin <conrad.irwin@gmail.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,133 @@
|
|
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
|
+
```
|
16
|
+
|
17
|
+
Usage
|
18
|
+
=====
|
19
|
+
|
20
|
+
There are *two ways* to use pry-rescue:
|
21
|
+
|
22
|
+
Wrap an entire script
|
23
|
+
---------------------
|
24
|
+
|
25
|
+
Use the launcher script:
|
26
|
+
|
27
|
+
```
|
28
|
+
rescue <script.rb> [arguments..]
|
29
|
+
```
|
30
|
+
|
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.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
require 'pry-rescue'
|
39
|
+
|
40
|
+
def test
|
41
|
+
raise "foo"
|
42
|
+
rescue => e
|
43
|
+
raise "bar"
|
44
|
+
end
|
45
|
+
|
46
|
+
Pry.rescue do
|
47
|
+
test
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
This will land you in a pry-session:
|
52
|
+
|
53
|
+
```
|
54
|
+
From: examples/example.rb @ line 4 Object#test:
|
55
|
+
|
56
|
+
4: def test
|
57
|
+
5: raise "foo"
|
58
|
+
6: rescue => e
|
59
|
+
=> 7: raise "bar"
|
60
|
+
8: end
|
61
|
+
|
62
|
+
RuntimeError: bar
|
63
|
+
from examples/example.rb:7:in `rescue in test'
|
64
|
+
[1] pry(main)>
|
65
|
+
```
|
66
|
+
|
67
|
+
cd-cause
|
68
|
+
========
|
69
|
+
|
70
|
+
If you need to find the reason that the exception happened, you can use the `cd-cause`
|
71
|
+
command:
|
72
|
+
|
73
|
+
```
|
74
|
+
[1] pry(main)> cd-cause
|
75
|
+
From: examples/example.rb @ line 4 Object#test:
|
76
|
+
|
77
|
+
4: def test
|
78
|
+
=> 5: raise "foo"
|
79
|
+
6: rescue => e
|
80
|
+
7: raise "bar"
|
81
|
+
8: end
|
82
|
+
|
83
|
+
RuntimeError: foo
|
84
|
+
from examples/example.rb:5:in `test'
|
85
|
+
[1] pry(main)>
|
86
|
+
```
|
87
|
+
|
88
|
+
To get back from `cd-cause` you can either type `<ctrl+d>` or `cd ..`.
|
89
|
+
|
90
|
+
try-again
|
91
|
+
=========
|
92
|
+
|
93
|
+
Once you've used Pry's `edit` or `edit-method` commands to fix your code, you can issue a
|
94
|
+
`try-again` command to re-run your code. (Either from the start in the case of using the
|
95
|
+
`rescue` script, or from the block if you're using that API).
|
96
|
+
|
97
|
+
```
|
98
|
+
[1] pry(main)> edit-method
|
99
|
+
[2] pry(main)> whereami
|
100
|
+
From: examples/example.rb @ line 4 Object#test:
|
101
|
+
|
102
|
+
4: def test
|
103
|
+
=> 5: puts "foo"
|
104
|
+
6: rescue => e
|
105
|
+
7: raise "bar"
|
106
|
+
8: end
|
107
|
+
[3] pry(main)> try-again
|
108
|
+
foo
|
109
|
+
```
|
110
|
+
|
111
|
+
pry-stack explorer
|
112
|
+
==================
|
113
|
+
|
114
|
+
If you're running rubinius, or ruby-1.9, then you can use `pry-rescue` alongside
|
115
|
+
`pry-stack_explorer`. This gives you the ability to move `up` or `down` the stack so that
|
116
|
+
you can get a better idea of why your function ended up in a bad state. Run
|
117
|
+
[example2.rb](https://github.com/ConradIrwin/pry-rescue/blob/master/examples/example2.rb) to get a feel for what this is like.
|
118
|
+
|
119
|
+
Known bugs
|
120
|
+
==========
|
121
|
+
|
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.
|
125
|
+
|
126
|
+
On rbx we are unable to intercept some exceptions thrown from inside the C++ VM, for
|
127
|
+
example the ZeroDivisionError in `1 / 0`.
|
128
|
+
|
129
|
+
Meta-fu
|
130
|
+
=======
|
131
|
+
|
132
|
+
Released under the MIT license, see LICENSE.MIT for details. Contributions and bug-reports
|
133
|
+
are welcome.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
desc "Run example"
|
2
|
+
task :example do
|
3
|
+
sh "ruby -I./lib/ ./examples/example.rb "
|
4
|
+
end
|
5
|
+
|
6
|
+
desc "Run example 2"
|
7
|
+
task :example2 do
|
8
|
+
sh "ruby -I./lib/ ./examples/example2.rb "
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Run tests"
|
12
|
+
task :test do
|
13
|
+
sh 'rspec spec -r ./spec/spec_helpers.rb'
|
14
|
+
end
|
15
|
+
|
16
|
+
task :build do
|
17
|
+
sh 'gem build *.gemspec'
|
18
|
+
end
|
19
|
+
|
20
|
+
task :install => :build do
|
21
|
+
sh 'gem install *.gem'
|
22
|
+
end
|
data/bin/rescue
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
USAGE = %{
|
4
|
+
rescue (pry-rescue wrapper)
|
5
|
+
|
6
|
+
Usage:
|
7
|
+
rescue <script.rb> [arguments...]
|
8
|
+
|
9
|
+
What it does:
|
10
|
+
Runs <script.rb>, and if an uncaught exception is raised,
|
11
|
+
pry will come to the rescue, giving you a pry prompt in the
|
12
|
+
context where the exception was raised.
|
13
|
+
|
14
|
+
You can then poke around to figure out why your code broke!
|
15
|
+
|
16
|
+
(Not yet available: fixing the problem and continuing your program.)
|
17
|
+
|
18
|
+
}
|
19
|
+
|
20
|
+
if script = ARGV.shift
|
21
|
+
if File.exists? script
|
22
|
+
require 'pry-rescue'
|
23
|
+
begin; require 'pry-stack_explorer'; rescue LoadError; end
|
24
|
+
|
25
|
+
$0 = File.expand_path script
|
26
|
+
Pry.rescue { load script }
|
27
|
+
else
|
28
|
+
$stderr.puts "Error: #{script.inspect} not found."
|
29
|
+
end
|
30
|
+
else
|
31
|
+
puts USAGE
|
32
|
+
end
|
data/examples/example.rb
ADDED
data/lib/pry-rescue.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'interception'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
require File.expand_path('../pry-rescue/core_ext', __FILE__)
|
6
|
+
require File.expand_path('../pry-rescue/commands', __FILE__)
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'pry-stack_explorer'
|
10
|
+
rescue LoadError
|
11
|
+
end
|
12
|
+
|
13
|
+
class PryRescue
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Start a Pry session in the context of the exception.
|
17
|
+
# @param [Exception] exception The exception.
|
18
|
+
# @param [Array<Binding>] bindings The call stack.
|
19
|
+
def enter_exception_context(raised)
|
20
|
+
exception, bindings = raised.last
|
21
|
+
|
22
|
+
prune_call_stack!(bindings)
|
23
|
+
|
24
|
+
if defined?(PryStackExplorer)
|
25
|
+
pry :call_stack => bindings, :hooks => pry_hooks(exception, raised)
|
26
|
+
else
|
27
|
+
bindings.first.pry :hooks => pry_hooks(exception, raised)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Define the :before_session hook for the Pry instance.
|
34
|
+
# This ensures that the `_ex_` and `_raised_` sticky locals are
|
35
|
+
# properly set.
|
36
|
+
def pry_hooks(ex, raised)
|
37
|
+
hooks = Pry.config.hooks.dup
|
38
|
+
hooks.add_hook(:before_session, :save_captured_exception) do |_, _, _pry_|
|
39
|
+
_pry_.last_exception = ex
|
40
|
+
_pry_.backtrace = ex.backtrace
|
41
|
+
_pry_.sticky_locals.merge!({ :_raised_ => raised })
|
42
|
+
_pry_.exception_handler.call(_pry_.output, ex, _pry_)
|
43
|
+
end
|
44
|
+
|
45
|
+
hooks
|
46
|
+
end
|
47
|
+
|
48
|
+
# Sanitize the call stack.
|
49
|
+
# @param [Array<Binding>] bindings The call stack.
|
50
|
+
def prune_call_stack!(bindings)
|
51
|
+
bindings.delete_if { |b| [Pry, Interception].include?(b.eval("self")) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'pry-rescue'
|
@@ -0,0 +1,65 @@
|
|
1
|
+
Pry::Commands.create_command "cd-cause", "Move to the previously raised exception" do
|
2
|
+
|
3
|
+
banner <<-BANNER
|
4
|
+
Usage: cd-cause
|
5
|
+
|
6
|
+
Starts a new pry session at the previously raised exception.
|
7
|
+
|
8
|
+
This is useful if you've caught one exception, and raised another,
|
9
|
+
if you need to find out why the original was raised.
|
10
|
+
|
11
|
+
@example
|
12
|
+
5. def foo
|
13
|
+
6. raise "one"
|
14
|
+
7. rescue
|
15
|
+
8. => raise "two"
|
16
|
+
9. end
|
17
|
+
|
18
|
+
pry> cd-cause
|
19
|
+
|
20
|
+
5. def foo
|
21
|
+
6. => raise "one"
|
22
|
+
7. rescue
|
23
|
+
8. raise "two"
|
24
|
+
9. end
|
25
|
+
|
26
|
+
Once you have finished with the internal exception type <ctrl+d> or cd .. to
|
27
|
+
return to where you were.
|
28
|
+
|
29
|
+
If you have many layers of exceptions that are rescued and then re-raised, you
|
30
|
+
can repeat cd-cause as many times as you need.
|
31
|
+
BANNER
|
32
|
+
|
33
|
+
def process
|
34
|
+
raised = target.eval("_raised_ rescue nil")
|
35
|
+
raise Pry::CommandError, "cd-cause only works in a pry session created by Pry::rescue{}" unless raised
|
36
|
+
raised.pop
|
37
|
+
|
38
|
+
if raised.any?
|
39
|
+
PryRescue.enter_exception_context(raised)
|
40
|
+
else
|
41
|
+
raise Pry::CommandError, "No previous exception detected"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
Pry::Commands.create_command "try-again", "Re-try the code that caused this exception" do
|
47
|
+
|
48
|
+
banner <<-BANNER
|
49
|
+
Usage: try-again
|
50
|
+
|
51
|
+
Runs the code wrapped by Pry::rescue{ } again.
|
52
|
+
|
53
|
+
This is useful if you've used `edit` or `edit-method` to fix the problem
|
54
|
+
that caused this exception to be raised and you want a quick way to test
|
55
|
+
your changes.
|
56
|
+
|
57
|
+
NOTE: try-again may cause confusing results if the code that's run have
|
58
|
+
side-effects (like deleting rows from a database) as it will try to do that
|
59
|
+
again, which may not work.
|
60
|
+
BANNER
|
61
|
+
|
62
|
+
def process
|
63
|
+
run "exit", ":try_again"
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Pry
|
2
|
+
|
3
|
+
# Start a pry session on any unhandled exceptions within this block.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# Pry::rescue do
|
7
|
+
# raise "foo"
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# @return [Object] The return value of the block
|
11
|
+
def self.rescue(&block)
|
12
|
+
raised = []
|
13
|
+
|
14
|
+
Interception.listen(block) do |exception, binding|
|
15
|
+
if defined?(PryStackExplorer)
|
16
|
+
raised << [exception, binding.callers]
|
17
|
+
else
|
18
|
+
raised << [exception, Array(binding)]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
rescue Exception => e
|
23
|
+
case PryRescue.enter_exception_context(raised)
|
24
|
+
when :try_again
|
25
|
+
retry
|
26
|
+
else
|
27
|
+
raise
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/pry-rescue.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'pry-rescue'
|
3
|
+
s.version = '0.3'
|
4
|
+
s.summary = 'Open a pry session on any unhandled exceptions'
|
5
|
+
s.description = 'Allows you to wrap code in Pry::rescue{ } to open a pry session at any unhandled exceptions'
|
6
|
+
s.homepage = 'https://github.com/ConradIrwin/pry-rescue'
|
7
|
+
s.email = ['conrad.irwin@gmail.com', 'jrmair@gmail.com', 'chris@ill-logic.com']
|
8
|
+
s.authors = ['Conrad Irwin', 'banisterfiend', 'epitron']
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.require_paths = ['lib']
|
11
|
+
s.executables = ['rescue']
|
12
|
+
|
13
|
+
s.add_dependency 'interception'
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pry-rescue
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.3'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Conrad Irwin
|
9
|
+
- banisterfiend
|
10
|
+
- epitron
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2012-08-15 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: interception
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ! '>='
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '0'
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ! '>='
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0'
|
32
|
+
description: Allows you to wrap code in Pry::rescue{ } to open a pry session at any
|
33
|
+
unhandled exceptions
|
34
|
+
email:
|
35
|
+
- conrad.irwin@gmail.com
|
36
|
+
- jrmair@gmail.com
|
37
|
+
- chris@ill-logic.com
|
38
|
+
executables:
|
39
|
+
- rescue
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- LICENSE.MIT
|
44
|
+
- README.md
|
45
|
+
- Rakefile
|
46
|
+
- bin/rescue
|
47
|
+
- examples/example.rb
|
48
|
+
- examples/example2.rb
|
49
|
+
- lib/pry-rescue.rb
|
50
|
+
- lib/pry-rescue/cli.rb
|
51
|
+
- lib/pry-rescue/commands.rb
|
52
|
+
- lib/pry-rescue/core_ext.rb
|
53
|
+
- pry-rescue.gemspec
|
54
|
+
homepage: https://github.com/ConradIrwin/pry-rescue
|
55
|
+
licenses: []
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.8.24
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Open a pry session on any unhandled exceptions
|
78
|
+
test_files: []
|
79
|
+
has_rdoc:
|