pry-rescue 0.5 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
data/bin/rescue CHANGED
@@ -23,7 +23,7 @@ if script = ARGV.shift
23
23
  begin; require 'pry-stack_explorer'; rescue LoadError; end
24
24
 
25
25
  $0 = File.expand_path script
26
- Pry.rescue { load script }
26
+ PryRescue.load script
27
27
  else
28
28
  $stderr.puts "Error: #{script.inspect} not found."
29
29
  end
@@ -18,28 +18,85 @@ class PryRescue
18
18
  # @param [Array<Binding>] bindings The call stack.
19
19
  def enter_exception_context(raised)
20
20
 
21
- exception, bindings = raised.last
22
- prune_call_stack!(bindings)
23
-
24
- if bindings.size > 1 && internal_binding?(bindings.first)
25
- raised.pop
26
- exception, bindings = raised.last
27
- prune_call_stack!(bindings)
21
+ raised = raised.map do |e, bs|
22
+ [e, without_bindings_below_raise(bs)]
28
23
  end
29
24
 
25
+ raised.pop if phantom_load_raise?(*raised.last)
26
+ exception, bindings = raised.last
27
+
30
28
  if defined?(PryStackExplorer)
31
- pry :call_stack => bindings, :hooks => pry_hooks(exception, raised)
29
+ pry :call_stack => without_duplicates(bindings),
30
+ :hooks => pry_hooks(exception, raised),
31
+ :initial_frame => initial_frame(bindings)
32
32
  else
33
- bindings.first.pry :hooks => pry_hooks(exception, raised)
33
+ Pry.start bindings.first, :hooks => pry_hooks(exception, raised)
34
34
  end
35
35
  end
36
36
 
37
+ # Load a script wrapped in Pry::rescue{ }
38
+ # @param [String] The name of the script
39
+ def load(script)
40
+ Pry::rescue{ Kernel.load script }
41
+ end
42
+
37
43
  private
38
44
 
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__))
45
+ # Did this raise happen within pry-rescue?
46
+ #
47
+ # This is designed to remove the extra raise that is caused by PryRescue.load.
48
+ # TODO: we should figure out why it happens...
49
+ # @param [Array<Binding>]
50
+ def phantom_load_raise?(e, bindings)
51
+ bindings.any? && bindings.first.eval("__FILE__") == __FILE__
52
+ end
53
+
54
+ # When using pry-stack-explorer we want to start the rescue session outside of gems
55
+ # and the standard library, as that is most helpful for users.
56
+ #
57
+ # @param [Array<Bindings>] All bindings
58
+ # @return [Fixnum] The offset of the first binding of user code
59
+ def initial_frame(bindings)
60
+ bindings.each_with_index do |binding, i|
61
+ return i if user_path?(binding.eval("__FILE__"))
62
+ end
63
+
64
+ 0
65
+ end
66
+
67
+ # Is this path likely to be code the user is working with right now?
68
+ #
69
+ # @param [String] the absolute path
70
+ # @return [Boolean]
71
+ def user_path?(file)
72
+ !file.start_with?(RbConfig::CONFIG['libdir']) &&
73
+ !Gem::Specification.any?{ |gem| file.start_with?(gem.full_gem_path) }
74
+ end
75
+
76
+ # Remove bindings that are part of Interception/Pry.rescue's internal
77
+ # event handling that happens as part of the exception hooking process.
78
+ #
79
+ # @param [Array<Binding>] bindings The call stack.
80
+ def without_bindings_below_raise(bindings)
81
+ return bindings if bindings.size <= 1
82
+ bindings.drop_while do |b|
83
+ b.eval("__FILE__") == File.expand_path("../pry-rescue/core_ext.rb", __FILE__)
84
+ end.drop_while do |b|
85
+ b.eval("self") == Interception
86
+ end
87
+ end
88
+
89
+ # Remove multiple bindings for the same function.
90
+ #
91
+ # @param [Array<Bindings>]
92
+ # @return [Array<Bindings>]
93
+ def without_duplicates(bindings)
94
+ bindings.zip([nil] + bindings).reject do |b, c|
95
+ # The eval('__method__') is there as a shortcut as loading a method
96
+ # from a binding is very slow.
97
+ c && (b.eval("__method__") == c.eval("__method__")) &&
98
+ Pry::Method.from_binding(b) == Pry::Method.from_binding(c)
99
+ end.map(&:first)
43
100
  end
44
101
 
45
102
  # Define the :before_session hook for the Pry instance.
@@ -56,11 +113,5 @@ class PryRescue
56
113
 
57
114
  hooks
58
115
  end
59
-
60
- # Sanitize the call stack.
61
- # @param [Array<Binding>] bindings The call stack.
62
- def prune_call_stack!(bindings)
63
- bindings.delete_if { |b| [Pry, Interception].include?(b.eval("self")) }
64
- end
65
116
  end
66
117
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'pry-rescue'
3
- s.version = '0.5'
3
+ s.version = '0.6'
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,3 @@
1
+ require 'coderay'
2
+
3
+ CodeRay.scan(123,123).tokens
@@ -0,0 +1 @@
1
+ raise "fixtures/simple"
@@ -0,0 +1,2 @@
1
+ require 'uri'
2
+ URI.parse(0)
@@ -0,0 +1,70 @@
1
+ require File.expand_path('../../lib/pry-rescue.rb', __FILE__)
2
+ require 'uri'
3
+
4
+ describe "PryRescue.load" do
5
+ if defined?(PryStackExplorer)
6
+ it "should open at the correct point" do
7
+ PryRescue.should_receive(:pry).once{ |opts|
8
+ opts[:call_stack].first.eval("__FILE__").should end_with('spec/fixtures/simple.rb')
9
+ }
10
+ lambda{
11
+ PryRescue.load("spec/fixtures/simple.rb")
12
+ }.should raise_error(/fixtures.simple/)
13
+ end
14
+
15
+ it "should open above the standard library" do
16
+ PryRescue.should_receive(:pry).once do |opts|
17
+ opts[:call_stack][opts[:initial_frame]].eval("__FILE__").should end_with('spec/fixtures/uri.rb')
18
+ end
19
+ lambda{
20
+ PryRescue.load("spec/fixtures/uri.rb")
21
+ }.should raise_error(URI::InvalidURIError)
22
+ end
23
+
24
+ it "should keep the standard library on the binding stack" do
25
+ PryRescue.should_receive(:pry).once do |opts|
26
+ opts[:call_stack].first.eval("__FILE__").should start_with(RbConfig::CONFIG['libdir'])
27
+ end
28
+ lambda{
29
+ PryRescue.load("spec/fixtures/uri.rb")
30
+ }.should raise_error(URI::InvalidURIError)
31
+ end
32
+
33
+ it "should open above gems" do
34
+ PryRescue.should_receive(:pry).once do |opts|
35
+ opts[:call_stack][opts[:initial_frame]].eval("__FILE__").should end_with('spec/fixtures/coderay.rb')
36
+ end
37
+ lambda{
38
+ PryRescue.load("spec/fixtures/coderay.rb")
39
+ }.should raise_error(ArgumentError)
40
+ end
41
+
42
+ it "should open above gems" do
43
+ PryRescue.should_receive(:pry).once do |opts|
44
+ opts[:call_stack].first.eval("__FILE__").should start_with(Gem::Specification.detect{|x| x.name == 'coderay' }.full_gem_path)
45
+ end
46
+ lambda{
47
+ PryRescue.load("spec/fixtures/coderay.rb")
48
+ }.should raise_error(ArgumentError)
49
+ end
50
+
51
+ it "should filter out duplicate stack frames" do
52
+ PryRescue.should_receive(:pry).once do |opts|
53
+ opts[:call_stack][0].eval("__LINE__").should == 4
54
+ opts[:call_stack][1].eval("__LINE__").should == 12
55
+ end
56
+ lambda{
57
+ PryRescue.load("spec/fixtures/super.rb")
58
+ }.should raise_error(/fixtures.super/)
59
+ end
60
+ else
61
+ it "should open at the correct point" do
62
+ Pry.should_receive(:start).once{ |binding, h|
63
+ binding.eval("__FILE__").should end_with('spec/fixtures/simple.rb')
64
+ }
65
+ lambda{
66
+ PryRescue.load("spec/fixtures/simple.rb")
67
+ }.should raise_error(/fixtures.simple/)
68
+ end
69
+ end
70
+ end
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.5'
4
+ version: '0.6'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -70,6 +70,10 @@ files:
70
70
  - lib/pry/rescue.rb
71
71
  - pry-rescue.gemspec
72
72
  - spec/core_ext_spec.rb
73
+ - spec/fixtures/coderay.rb
74
+ - spec/fixtures/simple.rb
75
+ - spec/fixtures/uri.rb
76
+ - spec/pry_rescue_spec.rb
73
77
  homepage: https://github.com/ConradIrwin/pry-rescue
74
78
  licenses: []
75
79
  post_install_message: