better_errors 1.1.0 → 2.1.1
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.
- checksums.yaml +4 -4
- data/.gitignore +7 -5
- data/.travis.yml +3 -3
- data/Gemfile +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +10 -3
- data/Rakefile +12 -3
- data/better_errors.gemspec +3 -3
- data/lib/better_errors/code_formatter/html.rb +1 -1
- data/lib/better_errors/code_formatter.rb +7 -7
- data/lib/better_errors/error_page.rb +23 -34
- data/lib/better_errors/exception_extension.rb +17 -0
- data/lib/better_errors/middleware.rb +7 -7
- data/lib/better_errors/raised_exception.rb +66 -0
- data/lib/better_errors/repl/basic.rb +2 -2
- data/lib/better_errors/repl/pry.rb +9 -9
- data/lib/better_errors/repl.rb +3 -3
- data/lib/better_errors/stack_frame.rb +7 -56
- data/lib/better_errors/templates/main.erb +6 -5
- data/lib/better_errors/templates/text.erb +2 -2
- data/lib/better_errors/version.rb +1 -1
- data/lib/better_errors.rb +6 -6
- data/spec/better_errors/code_formatter_spec.rb +7 -7
- data/spec/better_errors/error_page_spec.rb +12 -12
- data/spec/better_errors/middleware_spec.rb +8 -0
- data/spec/better_errors/raised_exception_spec.rb +72 -0
- data/spec/better_errors/repl/basic_spec.rb +1 -1
- data/spec/better_errors/repl/shared_examples.rb +2 -2
- data/spec/better_errors/stack_frame_spec.rb +31 -35
- data/spec/spec_helper.rb +2 -4
- data/spec/without_binding_of_caller.rb +9 -0
- metadata +33 -15
- data/lib/better_errors/core_ext/exception.rb +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5ae01bfc76b45f6af1df26c7268229088f8de251
|
|
4
|
+
data.tar.gz: f95f617f84e01f1641bf193d735d45d85360b768
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4881b5a147d85c9c7ed116a8838e45745bd95958df71dea8934e0b5601fd32cde2a016bbc419a855f9e29dd8e6bab322fcae8d676a78ede120d134809dac29b7
|
|
7
|
+
data.tar.gz: 6fb75468216fc264250e8f6726d47689025352441701cb83b2c125b541ac389c491da6ba8b3eeded2759b99be47f5c0cd103b54315122d7a33905261d02d4cca
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# Better Errors [](https://rubygems.org/gems/better_errors) [](https://travis-ci.org/charliesome/better_errors) [](https://codeclimate.com/github/charliesome/better_errors)
|
|
2
2
|
|
|
3
3
|
Better Errors replaces the standard Rails error page with a much better and more useful error page. It is also usable outside of Rails in any Rack app as Rack middleware.
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -21,7 +21,7 @@ group :development do
|
|
|
21
21
|
end
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
If you would like to use Better Errors' **advanced features** (REPL, local/instance variable inspection, pretty stack frame names), you need to add the [`binding_of_caller`](https://github.com/banister/binding_of_caller) gem by [@banisterfiend](
|
|
24
|
+
If you would like to use Better Errors' **advanced features** (REPL, local/instance variable inspection, pretty stack frame names), you need to add the [`binding_of_caller`](https://github.com/banister/binding_of_caller) gem by [@banisterfiend](https://twitter.com/banisterfiend) to your Gemfile:
|
|
25
25
|
|
|
26
26
|
```ruby
|
|
27
27
|
gem "binding_of_caller"
|
|
@@ -29,6 +29,8 @@ gem "binding_of_caller"
|
|
|
29
29
|
|
|
30
30
|
This is an optional dependency however, and Better Errors will work without it.
|
|
31
31
|
|
|
32
|
+
_Note: If you discover that Better Errors isn't working - particularly after upgrading from version 0.5.0 or less - be sure to set `config.consider_all_requests_local = true` in `config/environments/development.rb`._
|
|
33
|
+
|
|
32
34
|
## Security
|
|
33
35
|
|
|
34
36
|
**NOTE:** It is *critical* you put better\_errors in the **development** section. **Do NOT run better_errors in production, or on Internet facing hosts.**
|
|
@@ -75,6 +77,11 @@ get "/" do
|
|
|
75
77
|
end
|
|
76
78
|
```
|
|
77
79
|
|
|
80
|
+
### Plain text
|
|
81
|
+
|
|
82
|
+
Better Errors will render a plain text error page when the request is an
|
|
83
|
+
`XMLHttpRequest` or when the `Accept` header does *not* include 'html'.
|
|
84
|
+
|
|
78
85
|
### Unicorn, Puma, and other multi-worker servers
|
|
79
86
|
|
|
80
87
|
Better Errors works by leaving a lot of context in server process memory. If
|
data/Rakefile
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
require "bundler/gem_tasks"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
require "rspec/core/rake_task"
|
|
3
|
+
|
|
4
|
+
namespace :test do
|
|
5
|
+
RSpec::Core::RakeTask.new(:with_binding_of_caller)
|
|
6
|
+
|
|
7
|
+
without_task = RSpec::Core::RakeTask.new(:without_binding_of_caller)
|
|
8
|
+
without_task.ruby_opts = "-I spec -r without_binding_of_caller"
|
|
9
|
+
|
|
10
|
+
task :all => [:with_binding_of_caller, :without_binding_of_caller]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
task :default => "test:all"
|
data/better_errors.gemspec
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
1
|
lib = File.expand_path('../lib', __FILE__)
|
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
3
|
require 'better_errors/version'
|
|
@@ -17,10 +16,11 @@ Gem::Specification.new do |s|
|
|
|
17
16
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
|
18
17
|
s.require_paths = ["lib"]
|
|
19
18
|
|
|
20
|
-
s.required_ruby_version = ">=
|
|
21
|
-
|
|
19
|
+
s.required_ruby_version = ">= 2.0.0"
|
|
20
|
+
|
|
22
21
|
s.add_dependency "erubis", ">= 2.6.6"
|
|
23
22
|
s.add_dependency "coderay", ">= 1.0.0"
|
|
23
|
+
s.add_dependency "rack", ">= 0.9.0"
|
|
24
24
|
|
|
25
25
|
# optional dependencies:
|
|
26
26
|
# s.add_dependency "binding_of_caller"
|
|
@@ -11,9 +11,9 @@ module BetterErrors
|
|
|
11
11
|
".erb" => :erb,
|
|
12
12
|
".haml" => :haml
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
attr_reader :filename, :line, :context
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
def initialize(filename, line, context = 5)
|
|
18
18
|
@filename = filename
|
|
19
19
|
@line = line
|
|
@@ -29,7 +29,7 @@ module BetterErrors
|
|
|
29
29
|
def formatted_code
|
|
30
30
|
formatted_lines.join
|
|
31
31
|
end
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
def coderay_scanner
|
|
34
34
|
ext = File.extname(filename)
|
|
35
35
|
FILE_TYPES[ext] || :text
|
|
@@ -40,20 +40,20 @@ module BetterErrors
|
|
|
40
40
|
yield (current_line == line), current_line, str
|
|
41
41
|
}
|
|
42
42
|
end
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
def highlighted_lines
|
|
45
45
|
CodeRay.scan(context_lines.join, coderay_scanner).div(wrap: nil).lines
|
|
46
46
|
end
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
def context_lines
|
|
49
49
|
range = line_range
|
|
50
50
|
source_lines[(range.begin - 1)..(range.end - 1)] or raise Errno::EINVAL
|
|
51
51
|
end
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
def source_lines
|
|
54
54
|
@source_lines ||= File.readlines(filename)
|
|
55
55
|
end
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
def line_range
|
|
58
58
|
min = [line - context, 1].max
|
|
59
59
|
max = [line + context, source_lines.count].min
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "cgi"
|
|
2
2
|
require "json"
|
|
3
|
+
require "securerandom"
|
|
3
4
|
|
|
4
5
|
module BetterErrors
|
|
5
6
|
# @private
|
|
@@ -7,42 +8,46 @@ module BetterErrors
|
|
|
7
8
|
def self.template_path(template_name)
|
|
8
9
|
File.expand_path("../templates/#{template_name}.erb", __FILE__)
|
|
9
10
|
end
|
|
10
|
-
|
|
11
|
+
|
|
11
12
|
def self.template(template_name)
|
|
12
13
|
Erubis::EscapedEruby.new(File.read(template_path(template_name)))
|
|
13
14
|
end
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
attr_reader :exception, :env, :repls
|
|
16
|
-
|
|
17
|
+
|
|
17
18
|
def initialize(exception, env)
|
|
18
|
-
@exception =
|
|
19
|
+
@exception = RaisedException.new(exception)
|
|
19
20
|
@env = env
|
|
20
21
|
@start_time = Time.now.to_f
|
|
21
22
|
@repls = []
|
|
22
23
|
end
|
|
23
|
-
|
|
24
|
+
|
|
25
|
+
def id
|
|
26
|
+
@id ||= SecureRandom.hex(8)
|
|
27
|
+
end
|
|
28
|
+
|
|
24
29
|
def render(template_name = "main")
|
|
25
30
|
self.class.template(template_name).result binding
|
|
26
31
|
end
|
|
27
|
-
|
|
32
|
+
|
|
28
33
|
def do_variables(opts)
|
|
29
34
|
index = opts["index"].to_i
|
|
30
35
|
@frame = backtrace_frames[index]
|
|
31
36
|
@var_start_time = Time.now.to_f
|
|
32
37
|
{ html: render("variable_info") }
|
|
33
38
|
end
|
|
34
|
-
|
|
39
|
+
|
|
35
40
|
def do_eval(opts)
|
|
36
41
|
index = opts["index"].to_i
|
|
37
42
|
code = opts["source"]
|
|
38
|
-
|
|
43
|
+
|
|
39
44
|
unless binding = backtrace_frames[index].frame_binding
|
|
40
45
|
return { error: "REPL unavailable in this stack frame" }
|
|
41
46
|
end
|
|
42
|
-
|
|
47
|
+
|
|
43
48
|
result, prompt, prefilled_input =
|
|
44
49
|
(@repls[index] ||= REPL.provider.new(binding)).send_input(code)
|
|
45
|
-
|
|
50
|
+
|
|
46
51
|
{ result: result,
|
|
47
52
|
prompt: prompt,
|
|
48
53
|
prefilled_input: prefilled_input,
|
|
@@ -50,22 +55,22 @@ module BetterErrors
|
|
|
50
55
|
end
|
|
51
56
|
|
|
52
57
|
def backtrace_frames
|
|
53
|
-
|
|
58
|
+
exception.backtrace
|
|
54
59
|
end
|
|
55
60
|
|
|
56
61
|
def application_frames
|
|
57
|
-
backtrace_frames.select
|
|
62
|
+
backtrace_frames.select(&:application?)
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
def first_frame
|
|
61
|
-
|
|
66
|
+
application_frames.first || backtrace_frames.first
|
|
62
67
|
end
|
|
63
|
-
|
|
68
|
+
|
|
64
69
|
private
|
|
65
70
|
def editor_url(frame)
|
|
66
71
|
BetterErrors.editor[frame.filename, frame.line]
|
|
67
72
|
end
|
|
68
|
-
|
|
73
|
+
|
|
69
74
|
def rack_session
|
|
70
75
|
env['rack.session']
|
|
71
76
|
end
|
|
@@ -77,31 +82,15 @@ module BetterErrors
|
|
|
77
82
|
def uri_prefix
|
|
78
83
|
env["SCRIPT_NAME"] || ""
|
|
79
84
|
end
|
|
80
|
-
|
|
81
|
-
def exception_message
|
|
82
|
-
if exception.is_a?(SyntaxError) && exception.message =~ /\A.*:\d*: (.*)$/
|
|
83
|
-
$1
|
|
84
|
-
else
|
|
85
|
-
exception.message
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
85
|
|
|
89
|
-
def real_exception(exception)
|
|
90
|
-
if exception.respond_to?(:original_exception) && exception.original_exception.is_a?(Exception)
|
|
91
|
-
exception.original_exception
|
|
92
|
-
else
|
|
93
|
-
exception
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
86
|
def request_path
|
|
98
87
|
env["PATH_INFO"]
|
|
99
88
|
end
|
|
100
|
-
|
|
89
|
+
|
|
101
90
|
def html_formatted_code_block(frame)
|
|
102
91
|
CodeFormatter::HTML.new(frame.filename, frame.line).output
|
|
103
92
|
end
|
|
104
|
-
|
|
93
|
+
|
|
105
94
|
def text_formatted_code_block(frame)
|
|
106
95
|
CodeFormatter::Text.new(frame.filename, frame.line).output
|
|
107
96
|
end
|
|
@@ -114,7 +103,7 @@ module BetterErrors
|
|
|
114
103
|
CGI.escapeHTML(obj.inspect)
|
|
115
104
|
rescue NoMethodError
|
|
116
105
|
"<span class='unsupported'>(object doesn't support inspect)</span>"
|
|
117
|
-
rescue Exception
|
|
106
|
+
rescue Exception
|
|
118
107
|
"<span class='unsupported'>(exception was raised in inspect)</span>"
|
|
119
108
|
end
|
|
120
109
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module BetterErrors
|
|
2
|
+
module ExceptionExtension
|
|
3
|
+
prepend_features Exception
|
|
4
|
+
|
|
5
|
+
def set_backtrace(*)
|
|
6
|
+
if caller_locations.none? { |loc| loc.path == __FILE__ }
|
|
7
|
+
@__better_errors_bindings_stack = ::Kernel.binding.callers.drop(1)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def __better_errors_bindings_stack
|
|
14
|
+
@__better_errors_bindings_stack || []
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
require "ipaddr"
|
|
3
3
|
require "set"
|
|
4
|
+
require "rack"
|
|
4
5
|
|
|
5
6
|
module BetterErrors
|
|
6
7
|
# Better Errors' error handling middleware. Including this in your middleware
|
|
@@ -62,16 +63,15 @@ module BetterErrors
|
|
|
62
63
|
private
|
|
63
64
|
|
|
64
65
|
def allow_ip?(env)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
ip = IPAddr.new env["REMOTE_ADDR"].split("%").first
|
|
66
|
+
request = Rack::Request.new(env)
|
|
67
|
+
return true unless request.ip and !request.ip.strip.empty?
|
|
68
|
+
ip = IPAddr.new request.ip.split("%").first
|
|
69
69
|
ALLOWED_IPS.any? { |subnet| subnet.include? ip }
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def better_errors_call(env)
|
|
73
73
|
case env["PATH_INFO"]
|
|
74
|
-
when %r{/__better_errors/(?<
|
|
74
|
+
when %r{/__better_errors/(?<id>.+?)/(?<method>\w+)\z}
|
|
75
75
|
internal_call env, $~
|
|
76
76
|
when %r{/__better_errors/?\z}
|
|
77
77
|
show_error_page env
|
|
@@ -115,7 +115,7 @@ module BetterErrors
|
|
|
115
115
|
def log_exception
|
|
116
116
|
return unless BetterErrors.logger
|
|
117
117
|
|
|
118
|
-
message = "\n#{@error_page.exception.
|
|
118
|
+
message = "\n#{@error_page.exception.type} - #{@error_page.exception.message}:\n"
|
|
119
119
|
@error_page.backtrace_frames.each do |frame|
|
|
120
120
|
message << " #{frame}\n"
|
|
121
121
|
end
|
|
@@ -124,7 +124,7 @@ module BetterErrors
|
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
def internal_call(env, opts)
|
|
127
|
-
if opts[:
|
|
127
|
+
if opts[:id] != @error_page.id
|
|
128
128
|
return [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(error: "Session expired")]]
|
|
129
129
|
end
|
|
130
130
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @private
|
|
2
|
+
module BetterErrors
|
|
3
|
+
class RaisedException
|
|
4
|
+
attr_reader :exception, :message, :backtrace
|
|
5
|
+
|
|
6
|
+
def initialize(exception)
|
|
7
|
+
if exception.respond_to?(:original_exception) && exception.original_exception
|
|
8
|
+
exception = exception.original_exception
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
@exception = exception
|
|
12
|
+
@message = exception.message
|
|
13
|
+
|
|
14
|
+
setup_backtrace
|
|
15
|
+
massage_syntax_error
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def type
|
|
19
|
+
exception.class
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
def has_bindings?
|
|
24
|
+
exception.respond_to?(:__better_errors_bindings_stack) && exception.__better_errors_bindings_stack.any?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def setup_backtrace
|
|
28
|
+
if has_bindings?
|
|
29
|
+
setup_backtrace_from_bindings
|
|
30
|
+
else
|
|
31
|
+
setup_backtrace_from_backtrace
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def setup_backtrace_from_bindings
|
|
36
|
+
@backtrace = exception.__better_errors_bindings_stack.map { |binding|
|
|
37
|
+
file = binding.eval "__FILE__"
|
|
38
|
+
line = binding.eval "__LINE__"
|
|
39
|
+
name = binding.frame_description
|
|
40
|
+
StackFrame.new(file, line, name, binding)
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def setup_backtrace_from_backtrace
|
|
45
|
+
@backtrace = (exception.backtrace || []).map { |frame|
|
|
46
|
+
if /\A(?<file>.*?):(?<line>\d+)(:in `(?<name>.*)')?/ =~ frame
|
|
47
|
+
StackFrame.new(file, line.to_i, name)
|
|
48
|
+
end
|
|
49
|
+
}.compact
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def massage_syntax_error
|
|
53
|
+
case exception.class.to_s
|
|
54
|
+
when "Haml::SyntaxError", "Sprockets::Coffeelint::Error"
|
|
55
|
+
if /\A(.+?):(\d+)/ =~ exception.backtrace.first
|
|
56
|
+
backtrace.unshift(StackFrame.new($1, $2.to_i, ""))
|
|
57
|
+
end
|
|
58
|
+
when "SyntaxError"
|
|
59
|
+
if /\A(.+?):(\d+): (.*)/m =~ exception.message
|
|
60
|
+
backtrace.unshift(StackFrame.new($1, $2.to_i, ""))
|
|
61
|
+
@message = $3
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -9,40 +9,40 @@ module BetterErrors
|
|
|
9
9
|
Fiber.yield
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
class Output
|
|
14
14
|
def initialize
|
|
15
15
|
@buffer = ""
|
|
16
16
|
end
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
def puts(*args)
|
|
19
19
|
args.each do |arg|
|
|
20
20
|
@buffer << "#{arg.chomp}\n"
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
def tty?
|
|
25
25
|
false
|
|
26
26
|
end
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
def read_buffer
|
|
29
29
|
@buffer
|
|
30
30
|
ensure
|
|
31
31
|
@buffer = ""
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
def initialize(binding)
|
|
36
36
|
@fiber = Fiber.new do
|
|
37
37
|
@pry.repl binding
|
|
38
38
|
end
|
|
39
|
-
@input = Input.new
|
|
40
|
-
@output = Output.new
|
|
39
|
+
@input = BetterErrors::REPL::Pry::Input.new
|
|
40
|
+
@output = BetterErrors::REPL::Pry::Output.new
|
|
41
41
|
@pry = ::Pry.new input: @input, output: @output
|
|
42
42
|
@pry.hooks.clear_all if defined?(@pry.hooks.clear_all)
|
|
43
43
|
@fiber.resume
|
|
44
44
|
end
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
def send_input(str)
|
|
47
47
|
local ::Pry.config, color: false, pager: false do
|
|
48
48
|
@fiber.resume "#{str}\n"
|
|
@@ -59,7 +59,7 @@ module BetterErrors
|
|
|
59
59
|
rescue
|
|
60
60
|
[">>", ""]
|
|
61
61
|
end
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
private
|
|
64
64
|
def local(obj, attrs)
|
|
65
65
|
old_attrs = {}
|
data/lib/better_errors/repl.rb
CHANGED
|
@@ -9,17 +9,17 @@ module BetterErrors
|
|
|
9
9
|
def self.provider
|
|
10
10
|
@provider ||= const_get detect[:const]
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
def self.provider=(prov)
|
|
14
14
|
@provider = prov
|
|
15
15
|
end
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
def self.detect
|
|
18
18
|
PROVIDERS.find { |prov|
|
|
19
19
|
test_provider prov
|
|
20
20
|
}
|
|
21
21
|
end
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
def self.test_provider(provider)
|
|
24
24
|
require provider[:impl]
|
|
25
25
|
true
|
|
@@ -4,49 +4,7 @@ module BetterErrors
|
|
|
4
4
|
# @private
|
|
5
5
|
class StackFrame
|
|
6
6
|
def self.from_exception(exception)
|
|
7
|
-
|
|
8
|
-
list = exception.__better_errors_bindings_stack.map { |binding|
|
|
9
|
-
file = binding.eval "__FILE__"
|
|
10
|
-
line = binding.eval "__LINE__"
|
|
11
|
-
name = binding.frame_description
|
|
12
|
-
StackFrame.new(file, line, name, binding)
|
|
13
|
-
}
|
|
14
|
-
else
|
|
15
|
-
list = (exception.backtrace || []).map { |frame|
|
|
16
|
-
next unless md = /\A(?<file>.*?):(?<line>\d+)(:in `(?<name>.*)')?/.match(frame)
|
|
17
|
-
StackFrame.new(md[:file], md[:line].to_i, md[:name])
|
|
18
|
-
}.compact
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
if syntax_error?(exception)
|
|
22
|
-
if trace = exception.backtrace and trace.first =~ /\A(.*):(\d+)/
|
|
23
|
-
list.unshift StackFrame.new($1, $2.to_i, "")
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
list
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def self.syntax_error_classes
|
|
31
|
-
# Better Errors may be loaded before some of the gems that provide these
|
|
32
|
-
# classes, so we lazily set up the set of syntax error classes at runtime
|
|
33
|
-
# after everything has hopefully had a chance to load.
|
|
34
|
-
#
|
|
35
|
-
@syntax_error_classes ||= begin
|
|
36
|
-
class_names = %w[
|
|
37
|
-
Haml::SyntaxError
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
Set.new(class_names.map { |klass| eval(klass) rescue nil }.compact)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def self.syntax_error?(exception)
|
|
45
|
-
exception.is_a?(SyntaxError) || syntax_error_classes.include?(exception.class)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def self.has_binding_stack?(exception)
|
|
49
|
-
exception.respond_to?(:__better_errors_bindings_stack) && exception.__better_errors_bindings_stack.any?
|
|
7
|
+
RaisedException.new(exception).backtrace
|
|
50
8
|
end
|
|
51
9
|
|
|
52
10
|
attr_reader :filename, :line, :name, :frame_binding
|
|
@@ -75,7 +33,7 @@ module BetterErrors
|
|
|
75
33
|
end
|
|
76
34
|
|
|
77
35
|
def gem_path
|
|
78
|
-
if path = Gem.path.detect { |
|
|
36
|
+
if path = Gem.path.detect { |p| filename.index(p) == 0 }
|
|
79
37
|
gem_name_and_version, path = filename.sub("#{path}/gems/", "").split("/", 2)
|
|
80
38
|
/(?<gem_name>.+)-(?<gem_version>[\w.]+)/ =~ gem_name_and_version
|
|
81
39
|
"#{gem_name} (#{gem_version}) #{path}"
|
|
@@ -111,15 +69,10 @@ module BetterErrors
|
|
|
111
69
|
def local_variables
|
|
112
70
|
return {} unless frame_binding
|
|
113
71
|
frame_binding.eval("local_variables").each_with_object({}) do |name, hash|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
hash[name] = frame_binding.eval(name.to_s)
|
|
119
|
-
end
|
|
120
|
-
rescue NameError => e
|
|
121
|
-
# local_variables sometimes returns broken variables.
|
|
122
|
-
# https://bugs.ruby-lang.org/issues/7536
|
|
72
|
+
if defined?(frame_binding.local_variable_get)
|
|
73
|
+
hash[name] = frame_binding.local_variable_get(name)
|
|
74
|
+
else
|
|
75
|
+
hash[name] = frame_binding.eval(name.to_s)
|
|
123
76
|
end
|
|
124
77
|
end
|
|
125
78
|
end
|
|
@@ -141,14 +94,12 @@ module BetterErrors
|
|
|
141
94
|
|
|
142
95
|
private
|
|
143
96
|
def set_pretty_method_name
|
|
144
|
-
return if RUBY_VERSION < "2.0.0"
|
|
145
|
-
|
|
146
97
|
name =~ /\A(block (\([^)]+\) )?in )?/
|
|
147
98
|
recv = frame_binding.eval("self")
|
|
148
99
|
|
|
149
100
|
return unless method_name = frame_binding.eval("::Kernel.__method__")
|
|
150
101
|
|
|
151
|
-
if
|
|
102
|
+
if Module === recv
|
|
152
103
|
@class_name = "#{$1}#{recv}"
|
|
153
104
|
@method_name = ".#{method_name}"
|
|
154
105
|
else
|