better_errors 1.0.1 → 2.0.0
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 +1 -3
- data/CHANGELOG.md +3 -0
- data/Gemfile +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +3 -1
- data/Rakefile +12 -3
- data/better_errors.gemspec +3 -3
- data/lib/better_errors/error_page.rb +22 -33
- 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/pry.rb +2 -2
- data/lib/better_errors/stack_frame.rb +19 -67
- data/lib/better_errors/templates/main.erb +63 -49
- data/lib/better_errors/templates/text.erb +2 -2
- data/lib/better_errors/version.rb +1 -1
- data/lib/better_errors.rb +5 -5
- data/spec/better_errors/middleware_spec.rb +42 -0
- data/spec/better_errors/raised_exception_spec.rb +52 -0
- data/spec/better_errors/repl/pry_spec.rb +5 -1
- data/spec/better_errors/stack_frame_spec.rb +26 -30
- data/spec/spec_helper.rb +2 -4
- data/spec/without_binding_of_caller.rb +9 -0
- metadata +34 -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: bc36d3113c7041816a7697fbd1e50333f5976003
|
|
4
|
+
data.tar.gz: 834f3f4a4903a45f150d83adde3f25452d2fd86b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 130d61a4ce2e5509eb1585dfa0e3c620bf54387ac6a0931d1a534296460a0e06601a6d71ce41b3d3ffbd15951626ddfd7e753bb1bc911c35cf76cfa52cfcd771
|
|
7
|
+
data.tar.gz: 5906697a352187036b92cdefe124fea459405d7e3cc80f3bcc6f07c7a2952f328367f452e88272947edef464a4cc75fbe3d276c24580b9b633b6f9e60c99a60d
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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
|
|
|
@@ -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.**
|
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"
|
|
@@ -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
|
|
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
|
|
@@ -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"
|
|
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
|
|
@@ -36,8 +36,8 @@ module BetterErrors
|
|
|
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
|
|
@@ -4,67 +4,26 @@ 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
|
|
7
|
+
RaisedException.new(exception).backtrace
|
|
28
8
|
end
|
|
29
9
|
|
|
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?
|
|
50
|
-
end
|
|
51
|
-
|
|
52
10
|
attr_reader :filename, :line, :name, :frame_binding
|
|
53
|
-
|
|
11
|
+
|
|
54
12
|
def initialize(filename, line, name, frame_binding = nil)
|
|
55
13
|
@filename = filename
|
|
56
14
|
@line = line
|
|
57
15
|
@name = name
|
|
58
16
|
@frame_binding = frame_binding
|
|
59
|
-
|
|
17
|
+
|
|
60
18
|
set_pretty_method_name if frame_binding
|
|
61
19
|
end
|
|
62
|
-
|
|
20
|
+
|
|
63
21
|
def application?
|
|
64
|
-
root = BetterErrors.application_root
|
|
65
|
-
|
|
22
|
+
if root = BetterErrors.application_root
|
|
23
|
+
filename.index(root) == 0 && filename.index("#{root}/vendor") != 0
|
|
24
|
+
end
|
|
66
25
|
end
|
|
67
|
-
|
|
26
|
+
|
|
68
27
|
def application_path
|
|
69
28
|
filename[(BetterErrors.application_root.length+1)..-1]
|
|
70
29
|
end
|
|
@@ -72,7 +31,7 @@ module BetterErrors
|
|
|
72
31
|
def gem?
|
|
73
32
|
Gem.path.any? { |path| filename.index(path) == 0 }
|
|
74
33
|
end
|
|
75
|
-
|
|
34
|
+
|
|
76
35
|
def gem_path
|
|
77
36
|
if path = Gem.path.detect { |path| filename.index(path) == 0 }
|
|
78
37
|
gem_name_and_version, path = filename.sub("#{path}/gems/", "").split("/", 2)
|
|
@@ -88,7 +47,7 @@ module BetterErrors
|
|
|
88
47
|
def method_name
|
|
89
48
|
@method_name || @name
|
|
90
49
|
end
|
|
91
|
-
|
|
50
|
+
|
|
92
51
|
def context
|
|
93
52
|
if gem?
|
|
94
53
|
:gem
|
|
@@ -98,7 +57,7 @@ module BetterErrors
|
|
|
98
57
|
:dunno
|
|
99
58
|
end
|
|
100
59
|
end
|
|
101
|
-
|
|
60
|
+
|
|
102
61
|
def pretty_path
|
|
103
62
|
case context
|
|
104
63
|
when :application; application_path
|
|
@@ -106,23 +65,18 @@ module BetterErrors
|
|
|
106
65
|
else filename
|
|
107
66
|
end
|
|
108
67
|
end
|
|
109
|
-
|
|
68
|
+
|
|
110
69
|
def local_variables
|
|
111
70
|
return {} unless frame_binding
|
|
112
71
|
frame_binding.eval("local_variables").each_with_object({}) do |name, hash|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
hash[name] = frame_binding.eval(name.to_s)
|
|
118
|
-
end
|
|
119
|
-
rescue NameError => e
|
|
120
|
-
# local_variables sometimes returns broken variables.
|
|
121
|
-
# 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)
|
|
122
76
|
end
|
|
123
77
|
end
|
|
124
78
|
end
|
|
125
|
-
|
|
79
|
+
|
|
126
80
|
def instance_variables
|
|
127
81
|
return {} unless frame_binding
|
|
128
82
|
Hash[visible_instance_variables.map { |x|
|
|
@@ -137,17 +91,15 @@ module BetterErrors
|
|
|
137
91
|
def to_s
|
|
138
92
|
"#{pretty_path}:#{line}:in `#{name}'"
|
|
139
93
|
end
|
|
140
|
-
|
|
94
|
+
|
|
141
95
|
private
|
|
142
96
|
def set_pretty_method_name
|
|
143
|
-
return if RUBY_VERSION < "2.0.0"
|
|
144
|
-
|
|
145
97
|
name =~ /\A(block (\([^)]+\) )?in )?/
|
|
146
98
|
recv = frame_binding.eval("self")
|
|
147
99
|
|
|
148
100
|
return unless method_name = frame_binding.eval("::Kernel.__method__")
|
|
149
101
|
|
|
150
|
-
if
|
|
102
|
+
if Module === recv
|
|
151
103
|
@class_name = "#{$1}#{recv}"
|
|
152
104
|
@method_name = ".#{method_name}"
|
|
153
105
|
else
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
|
-
<title><%= exception.
|
|
4
|
+
<title><%= exception.type %> at <%= request_path %></title>
|
|
5
5
|
</head>
|
|
6
6
|
<body>
|
|
7
7
|
<%# Stylesheets are placed in the <body> for Turbolinks compatibility. %>
|
|
@@ -180,6 +180,7 @@
|
|
|
180
180
|
padding-right: 20px;
|
|
181
181
|
overflow-y: auto;
|
|
182
182
|
word-wrap: break-word;
|
|
183
|
+
white-space: pre-wrap;
|
|
183
184
|
height: auto;
|
|
184
185
|
max-height: 7em;
|
|
185
186
|
}
|
|
@@ -443,13 +444,13 @@
|
|
|
443
444
|
font-weight: bold;
|
|
444
445
|
font-size: 10pt;
|
|
445
446
|
}
|
|
446
|
-
|
|
447
|
+
|
|
447
448
|
.trace_info .title .location a {
|
|
448
449
|
color:inherit;
|
|
449
450
|
text-decoration:none;
|
|
450
451
|
border-bottom:1px solid #aaaaaa;
|
|
451
452
|
}
|
|
452
|
-
|
|
453
|
+
|
|
453
454
|
.trace_info .title .location a:hover {
|
|
454
455
|
border-color:#666666;
|
|
455
456
|
}
|
|
@@ -472,7 +473,7 @@
|
|
|
472
473
|
padding-bottom:9px;
|
|
473
474
|
float:left;
|
|
474
475
|
}
|
|
475
|
-
|
|
476
|
+
|
|
476
477
|
.code_linenums span{
|
|
477
478
|
display:block;
|
|
478
479
|
padding:0 12px;
|
|
@@ -730,8 +731,8 @@
|
|
|
730
731
|
|
|
731
732
|
<div class='top'>
|
|
732
733
|
<header class="exception">
|
|
733
|
-
<h2><strong><%= exception.
|
|
734
|
-
<p><%=
|
|
734
|
+
<h2><strong><%= exception.type %></strong> <span>at <%= request_path %></span></h2>
|
|
735
|
+
<p><%= exception.message %></p>
|
|
735
736
|
</header>
|
|
736
737
|
</div>
|
|
737
738
|
|
|
@@ -767,8 +768,8 @@
|
|
|
767
768
|
<script>
|
|
768
769
|
(function() {
|
|
769
770
|
|
|
770
|
-
var OID =
|
|
771
|
-
|
|
771
|
+
var OID = "<%= id %>";
|
|
772
|
+
|
|
772
773
|
var previousFrame = null;
|
|
773
774
|
var previousFrameInfo = null;
|
|
774
775
|
var allFrames = document.querySelectorAll("ul.frames li");
|
|
@@ -786,68 +787,73 @@
|
|
|
786
787
|
}
|
|
787
788
|
};
|
|
788
789
|
}
|
|
789
|
-
|
|
790
|
+
|
|
790
791
|
function escapeHTML(html) {
|
|
791
792
|
return html.replace(/&/, "&").replace(/</g, "<");
|
|
792
793
|
}
|
|
793
|
-
|
|
794
|
+
|
|
794
795
|
function REPL(index) {
|
|
795
796
|
this.index = index;
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
797
|
+
|
|
798
|
+
var previousCommands = JSON.parse(localStorage.getItem("better_errors_previous_commands"));
|
|
799
|
+
if(previousCommands === null) {
|
|
800
|
+
localStorage.setItem("better_errors_previous_commands", JSON.stringify([]));
|
|
801
|
+
previousCommands = [];
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
this.previousCommandOffset = previousCommands.length;
|
|
799
805
|
}
|
|
800
|
-
|
|
806
|
+
|
|
801
807
|
REPL.all = [];
|
|
802
|
-
|
|
808
|
+
|
|
803
809
|
REPL.prototype.install = function(containerElement) {
|
|
804
810
|
this.container = containerElement;
|
|
805
|
-
|
|
811
|
+
|
|
806
812
|
this.promptElement = this.container.querySelector(".prompt span");
|
|
807
813
|
this.inputElement = this.container.querySelector("input");
|
|
808
814
|
this.outputElement = this.container.querySelector("pre");
|
|
809
|
-
|
|
815
|
+
|
|
810
816
|
var self = this;
|
|
811
817
|
this.inputElement.onkeydown = function(ev) {
|
|
812
818
|
self.onKeyDown(ev);
|
|
813
819
|
};
|
|
814
|
-
|
|
820
|
+
|
|
815
821
|
this.setPrompt(">>");
|
|
816
|
-
|
|
822
|
+
|
|
817
823
|
REPL.all[this.index] = this;
|
|
818
824
|
}
|
|
819
|
-
|
|
825
|
+
|
|
820
826
|
REPL.prototype.focus = function() {
|
|
821
827
|
this.inputElement.focus();
|
|
822
828
|
};
|
|
823
|
-
|
|
829
|
+
|
|
824
830
|
REPL.prototype.setPrompt = function(prompt) {
|
|
825
831
|
this._prompt = prompt;
|
|
826
832
|
this.promptElement.innerHTML = escapeHTML(prompt);
|
|
827
833
|
};
|
|
828
|
-
|
|
834
|
+
|
|
829
835
|
REPL.prototype.getInput = function() {
|
|
830
836
|
return this.inputElement.value;
|
|
831
837
|
};
|
|
832
|
-
|
|
838
|
+
|
|
833
839
|
REPL.prototype.setInput = function(text) {
|
|
834
840
|
this.inputElement.value = text;
|
|
835
|
-
|
|
841
|
+
|
|
836
842
|
if(this.inputElement.setSelectionRange) {
|
|
837
843
|
// set cursor to end of input
|
|
838
844
|
this.inputElement.setSelectionRange(text.length, text.length);
|
|
839
845
|
}
|
|
840
846
|
};
|
|
841
|
-
|
|
847
|
+
|
|
842
848
|
REPL.prototype.writeRawOutput = function(output) {
|
|
843
849
|
this.outputElement.innerHTML += output;
|
|
844
850
|
this.outputElement.scrollTop = this.outputElement.scrollHeight;
|
|
845
851
|
};
|
|
846
|
-
|
|
852
|
+
|
|
847
853
|
REPL.prototype.writeOutput = function(output) {
|
|
848
854
|
this.writeRawOutput(escapeHTML(output));
|
|
849
855
|
};
|
|
850
|
-
|
|
856
|
+
|
|
851
857
|
REPL.prototype.sendInput = function(line) {
|
|
852
858
|
var self = this;
|
|
853
859
|
apiCall("eval", { "index": this.index, source: line }, function(response) {
|
|
@@ -861,48 +867,56 @@
|
|
|
861
867
|
self.setInput(response.prefilled_input);
|
|
862
868
|
});
|
|
863
869
|
};
|
|
864
|
-
|
|
870
|
+
|
|
865
871
|
REPL.prototype.onEnterKey = function() {
|
|
866
872
|
var text = this.getInput();
|
|
867
873
|
if(text != "" && text !== undefined) {
|
|
868
|
-
|
|
874
|
+
var previousCommands = JSON.parse(localStorage.getItem("better_errors_previous_commands"));
|
|
875
|
+
this.previousCommandOffset = previousCommands.push(text);
|
|
876
|
+
if(previousCommands.length > 100) {
|
|
877
|
+
previousCommands.splice(0, 1);
|
|
878
|
+
}
|
|
879
|
+
localStorage.setItem("better_errors_previous_commands", JSON.stringify(previousCommands));
|
|
869
880
|
}
|
|
870
881
|
this.setInput("");
|
|
871
882
|
this.sendInput(text);
|
|
872
883
|
};
|
|
873
|
-
|
|
884
|
+
|
|
874
885
|
REPL.prototype.onNavigateHistory = function(direction) {
|
|
875
886
|
this.previousCommandOffset += direction;
|
|
876
|
-
|
|
887
|
+
var previousCommands = JSON.parse(localStorage.getItem("better_errors_previous_commands"));
|
|
888
|
+
|
|
877
889
|
if(this.previousCommandOffset < 0) {
|
|
878
890
|
this.previousCommandOffset = -1;
|
|
879
891
|
this.setInput("");
|
|
880
892
|
return;
|
|
881
893
|
}
|
|
882
|
-
|
|
883
|
-
if(this.previousCommandOffset >=
|
|
884
|
-
this.previousCommandOffset =
|
|
894
|
+
|
|
895
|
+
if(this.previousCommandOffset >= previousCommands.length) {
|
|
896
|
+
this.previousCommandOffset = previousCommands.length;
|
|
885
897
|
this.setInput("");
|
|
886
898
|
return;
|
|
887
899
|
}
|
|
888
|
-
|
|
889
|
-
this.setInput(
|
|
900
|
+
|
|
901
|
+
this.setInput(previousCommands[this.previousCommandOffset]);
|
|
890
902
|
};
|
|
891
|
-
|
|
903
|
+
|
|
892
904
|
REPL.prototype.onKeyDown = function(ev) {
|
|
893
905
|
if(ev.keyCode == 13) {
|
|
894
906
|
this.onEnterKey();
|
|
895
|
-
} else if(ev.keyCode == 38) {
|
|
896
|
-
// the user pressed the up arrow
|
|
907
|
+
} else if(ev.keyCode == 38 || (ev.ctrlKey && ev.keyCode == 80)) {
|
|
908
|
+
// the user pressed the up arrow or Ctrl-P
|
|
897
909
|
this.onNavigateHistory(-1);
|
|
910
|
+
ev.preventDefault();
|
|
898
911
|
return false;
|
|
899
|
-
} else if(ev.keyCode == 40) {
|
|
900
|
-
// the user pressed the down arrow
|
|
912
|
+
} else if(ev.keyCode == 40 || (ev.ctrlKey && ev.keyCode == 78)) {
|
|
913
|
+
// the user pressed the down arrow or Ctrl-N
|
|
901
914
|
this.onNavigateHistory(1);
|
|
915
|
+
ev.preventDefault();
|
|
902
916
|
return false;
|
|
903
917
|
}
|
|
904
918
|
};
|
|
905
|
-
|
|
919
|
+
|
|
906
920
|
function switchTo(el) {
|
|
907
921
|
if(previousFrameInfo) previousFrameInfo.style.display = "none";
|
|
908
922
|
previousFrameInfo = el;
|
|
@@ -937,7 +951,7 @@
|
|
|
937
951
|
});
|
|
938
952
|
}
|
|
939
953
|
}
|
|
940
|
-
|
|
954
|
+
|
|
941
955
|
for(var i = 0; i < allFrames.length; i++) {
|
|
942
956
|
(function(i, el) {
|
|
943
957
|
var el = allFrames[i];
|
|
@@ -947,12 +961,12 @@
|
|
|
947
961
|
}
|
|
948
962
|
el.className = "selected";
|
|
949
963
|
previousFrame = el;
|
|
950
|
-
|
|
964
|
+
|
|
951
965
|
selectFrameInfo(el.attributes["data-index"].value);
|
|
952
966
|
};
|
|
953
967
|
})(i);
|
|
954
968
|
}
|
|
955
|
-
|
|
969
|
+
|
|
956
970
|
// Click the first application frame
|
|
957
971
|
(
|
|
958
972
|
document.querySelector(".frames li.application") ||
|
|
@@ -968,7 +982,7 @@
|
|
|
968
982
|
var applicationFramesButtonIsInstalled = false;
|
|
969
983
|
var applicationFramesButton = document.getElementById("application_frames");
|
|
970
984
|
var allFramesButton = document.getElementById("all_frames");
|
|
971
|
-
|
|
985
|
+
|
|
972
986
|
// The application frames button only needs to be bound if
|
|
973
987
|
// there are actually any application frames to look at.
|
|
974
988
|
var installApplicationFramesButton = function() {
|
|
@@ -984,10 +998,10 @@
|
|
|
984
998
|
}
|
|
985
999
|
return false;
|
|
986
1000
|
};
|
|
987
|
-
|
|
1001
|
+
|
|
988
1002
|
applicationFramesButtonIsInstalled = true;
|
|
989
1003
|
}
|
|
990
|
-
|
|
1004
|
+
|
|
991
1005
|
allFramesButton.onclick = function() {
|
|
992
1006
|
if(applicationFramesButtonIsInstalled) {
|
|
993
1007
|
applicationFramesButton.className = "";
|
|
@@ -999,7 +1013,7 @@
|
|
|
999
1013
|
}
|
|
1000
1014
|
return false;
|
|
1001
1015
|
};
|
|
1002
|
-
|
|
1016
|
+
|
|
1003
1017
|
// If there are no application frames, select the 'All Frames'
|
|
1004
1018
|
// tab by default.
|
|
1005
1019
|
if(applicationFramesCount > 0) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
<%== text_heading("=", "%s at %s" % [exception.
|
|
1
|
+
<%== text_heading("=", "%s at %s" % [exception.type, request_path]) %>
|
|
2
2
|
|
|
3
|
-
> <%==
|
|
3
|
+
> <%== exception.message %>
|
|
4
4
|
<% if backtrace_frames.any? %>
|
|
5
5
|
|
|
6
6
|
<%== text_heading("-", "%s, line %i" % [first_frame.pretty_path, first_frame.line]) %>
|
data/lib/better_errors.rb
CHANGED
|
@@ -3,12 +3,13 @@ require "erubis"
|
|
|
3
3
|
require "coderay"
|
|
4
4
|
require "uri"
|
|
5
5
|
|
|
6
|
-
require "better_errors/
|
|
6
|
+
require "better_errors/code_formatter"
|
|
7
7
|
require "better_errors/error_page"
|
|
8
|
-
require "better_errors/stack_frame"
|
|
9
8
|
require "better_errors/middleware"
|
|
10
|
-
require "better_errors/
|
|
9
|
+
require "better_errors/raised_exception"
|
|
11
10
|
require "better_errors/repl"
|
|
11
|
+
require "better_errors/stack_frame"
|
|
12
|
+
require "better_errors/version"
|
|
12
13
|
|
|
13
14
|
module BetterErrors
|
|
14
15
|
POSSIBLE_EDITOR_PRESETS = [
|
|
@@ -136,11 +137,10 @@ end
|
|
|
136
137
|
|
|
137
138
|
begin
|
|
138
139
|
require "binding_of_caller"
|
|
140
|
+
require "better_errors/exception_extension"
|
|
139
141
|
BetterErrors.binding_of_caller_available = true
|
|
140
142
|
rescue LoadError => e
|
|
141
143
|
BetterErrors.binding_of_caller_available = false
|
|
142
144
|
end
|
|
143
145
|
|
|
144
|
-
require "better_errors/core_ext/exception"
|
|
145
|
-
|
|
146
146
|
require "better_errors/rails" if defined? Rails::Railtie
|
|
@@ -40,6 +40,14 @@ module BetterErrors
|
|
|
40
40
|
app.call("REMOTE_ADDR" => "77.55.33.11")
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
it "respects the X-Forwarded-For header" do
|
|
44
|
+
app.should_not_receive :better_errors_call
|
|
45
|
+
app.call(
|
|
46
|
+
"REMOTE_ADDR" => "127.0.0.1",
|
|
47
|
+
"HTTP_X_FORWARDED_FOR" => "1.2.3.4",
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
43
51
|
it "doesn't blow up when given a blank REMOTE_ADDR" do
|
|
44
52
|
expect { app.call("REMOTE_ADDR" => " ") }.to_not raise_error
|
|
45
53
|
end
|
|
@@ -71,6 +79,40 @@ module BetterErrors
|
|
|
71
79
|
status.should == 500
|
|
72
80
|
end
|
|
73
81
|
|
|
82
|
+
context "original_exception" do
|
|
83
|
+
class OriginalExceptionException < Exception
|
|
84
|
+
attr_reader :original_exception
|
|
85
|
+
|
|
86
|
+
def initialize(message, original_exception = nil)
|
|
87
|
+
super(message)
|
|
88
|
+
@original_exception = original_exception
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "shows Original Exception if it responds_to and has an original_exception" do
|
|
93
|
+
app = Middleware.new(->env {
|
|
94
|
+
raise OriginalExceptionException.new("Other Exception", Exception.new("Original Exception"))
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
status, _, body = app.call({})
|
|
98
|
+
|
|
99
|
+
status.should == 500
|
|
100
|
+
body.join.should_not match(/Other Exception/)
|
|
101
|
+
body.join.should match(/Original Exception/)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "won't crash if the exception responds_to but doesn't have an original_exception" do
|
|
105
|
+
app = Middleware.new(->env {
|
|
106
|
+
raise OriginalExceptionException.new("Other Exception")
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
status, _, body = app.call({})
|
|
110
|
+
|
|
111
|
+
status.should == 500
|
|
112
|
+
body.join.should match(/Other Exception/)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
74
116
|
it "returns ExceptionWrapper's status_code" do
|
|
75
117
|
ad_ew = double("ActionDispatch::ExceptionWrapper")
|
|
76
118
|
ad_ew.stub('new').with({}, exception ){ double("ExceptionWrapper", status_code: 404) }
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
module BetterErrors
|
|
4
|
+
describe RaisedException do
|
|
5
|
+
let(:exception) { RuntimeError.new("whoops") }
|
|
6
|
+
subject { RaisedException.new(exception) }
|
|
7
|
+
|
|
8
|
+
its(:exception) { should == exception }
|
|
9
|
+
its(:message) { should == "whoops" }
|
|
10
|
+
its(:type) { should == RuntimeError }
|
|
11
|
+
|
|
12
|
+
context "when the exception wraps another exception" do
|
|
13
|
+
let(:original_exception) { RuntimeError.new("something went wrong!") }
|
|
14
|
+
let(:exception) { double(:original_exception => original_exception) }
|
|
15
|
+
|
|
16
|
+
its(:exception) { should == original_exception }
|
|
17
|
+
its(:message) { should == "something went wrong!" }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context "when the exception is a syntax error" do
|
|
21
|
+
let(:exception) { SyntaxError.new("foo.rb:123: you made a typo!") }
|
|
22
|
+
|
|
23
|
+
its(:message) { should == "you made a typo!" }
|
|
24
|
+
its(:type) { should == SyntaxError }
|
|
25
|
+
|
|
26
|
+
it "has the right filename and line number in the backtrace" do
|
|
27
|
+
subject.backtrace.first.filename.should == "foo.rb"
|
|
28
|
+
subject.backtrace.first.line.should == 123
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context "when the exception is a HAML syntax error" do
|
|
33
|
+
before do
|
|
34
|
+
stub_const("Haml::SyntaxError", Class.new(SyntaxError))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
let(:exception) {
|
|
38
|
+
Haml::SyntaxError.new("you made a typo!").tap do |ex|
|
|
39
|
+
ex.set_backtrace(["foo.rb:123", "haml/internals/blah.rb:123456"])
|
|
40
|
+
end
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
its(:message) { should == "you made a typo!" }
|
|
44
|
+
its(:type) { should == Haml::SyntaxError }
|
|
45
|
+
|
|
46
|
+
it "has the right filename and line number in the backtrace" do
|
|
47
|
+
subject.backtrace.first.filename.should == "foo.rb"
|
|
48
|
+
subject.backtrace.first.line.should == 123
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -25,7 +25,11 @@ module BetterErrors
|
|
|
25
25
|
filled.should == " "
|
|
26
26
|
|
|
27
27
|
output, prompt, filled = repl.send_input "end"
|
|
28
|
-
|
|
28
|
+
if RUBY_VERSION >= "2.1.0"
|
|
29
|
+
output.should == "=> :f\n"
|
|
30
|
+
else
|
|
31
|
+
output.should == "=> nil\n"
|
|
32
|
+
end
|
|
29
33
|
prompt.should == ">>"
|
|
30
34
|
filled.should == ""
|
|
31
35
|
end
|
|
@@ -6,77 +6,77 @@ module BetterErrors
|
|
|
6
6
|
it "is true for application filenames" do
|
|
7
7
|
BetterErrors.stub(:application_root).and_return("/abc/xyz")
|
|
8
8
|
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
frame.application?.should be_true
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
it "is false for everything else" do
|
|
14
14
|
BetterErrors.stub(:application_root).and_return("/abc/xyz")
|
|
15
15
|
frame = StackFrame.new("/abc/nope", 123, "foo")
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
frame.application?.should be_false
|
|
18
18
|
end
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
it "doesn't care if no application_root is set" do
|
|
21
21
|
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
frame.application?.should be_false
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
context "#gem?" do
|
|
28
28
|
it "is true for gem filenames" do
|
|
29
29
|
Gem.stub(:path).and_return(["/abc/xyz"])
|
|
30
30
|
frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
frame.gem?.should be_true
|
|
33
33
|
end
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
it "is false for everything else" do
|
|
36
36
|
Gem.stub(:path).and_return(["/abc/xyz"])
|
|
37
37
|
frame = StackFrame.new("/abc/nope", 123, "foo")
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
frame.gem?.should be_false
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
context "#application_path" do
|
|
44
44
|
it "chops off the application root" do
|
|
45
45
|
BetterErrors.stub(:application_root).and_return("/abc/xyz")
|
|
46
46
|
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
frame.application_path.should == "app/controllers/crap_controller.rb"
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
context "#gem_path" do
|
|
53
53
|
it "chops of the gem path and stick (gem) there" do
|
|
54
54
|
Gem.stub(:path).and_return(["/abc/xyz"])
|
|
55
55
|
frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
frame.gem_path.should == "whatever (1.2.3) lib/whatever.rb"
|
|
58
58
|
end
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
it "prioritizes gem path over application path" do
|
|
61
61
|
BetterErrors.stub(:application_root).and_return("/abc/xyz")
|
|
62
62
|
Gem.stub(:path).and_return(["/abc/xyz/vendor"])
|
|
63
63
|
frame = StackFrame.new("/abc/xyz/vendor/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
frame.gem_path.should == "whatever (1.2.3) lib/whatever.rb"
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
context "#pretty_path" do
|
|
70
70
|
it "returns #application_path for application paths" do
|
|
71
71
|
BetterErrors.stub(:application_root).and_return("/abc/xyz")
|
|
72
72
|
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
|
|
73
73
|
frame.pretty_path.should == frame.application_path
|
|
74
74
|
end
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
it "returns #gem_path for gem paths" do
|
|
77
77
|
Gem.stub(:path).and_return(["/abc/xyz"])
|
|
78
78
|
frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
frame.pretty_path.should == frame.gem_path
|
|
81
81
|
end
|
|
82
82
|
end
|
|
@@ -90,28 +90,28 @@ module BetterErrors
|
|
|
90
90
|
frames.first.filename.should == "my_file.rb"
|
|
91
91
|
frames.first.line.should == 123
|
|
92
92
|
end
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
it "doesn't blow up if no method name is given" do
|
|
95
95
|
error = StandardError.allocate
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
error.stub(:backtrace).and_return(["foo.rb:123"])
|
|
98
98
|
frames = StackFrame.from_exception(error)
|
|
99
99
|
frames.first.filename.should == "foo.rb"
|
|
100
100
|
frames.first.line.should == 123
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
error.stub(:backtrace).and_return(["foo.rb:123: this is an error message"])
|
|
103
103
|
frames = StackFrame.from_exception(error)
|
|
104
104
|
frames.first.filename.should == "foo.rb"
|
|
105
105
|
frames.first.line.should == 123
|
|
106
106
|
end
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
it "ignores a backtrace line if its format doesn't make any sense at all" do
|
|
109
109
|
error = StandardError.allocate
|
|
110
110
|
error.stub(:backtrace).and_return(["foo.rb:123:in `foo'", "C:in `find'", "bar.rb:123:in `bar'"])
|
|
111
111
|
frames = StackFrame.from_exception(error)
|
|
112
112
|
frames.count.should == 2
|
|
113
113
|
end
|
|
114
|
-
|
|
114
|
+
|
|
115
115
|
it "doesn't blow up if a filename contains a colon" do
|
|
116
116
|
error = StandardError.allocate
|
|
117
117
|
error.stub(:backtrace).and_return(["crap:filename.rb:123"])
|
|
@@ -125,11 +125,7 @@ module BetterErrors
|
|
|
125
125
|
::Kernel.binding
|
|
126
126
|
end
|
|
127
127
|
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index", obj.my_binding)
|
|
128
|
-
|
|
129
|
-
frame.class_name.should == 'BasicObject'
|
|
130
|
-
else
|
|
131
|
-
frame.class_name.should be_nil
|
|
132
|
-
end
|
|
128
|
+
frame.class_name.should == 'BasicObject'
|
|
133
129
|
end
|
|
134
130
|
|
|
135
131
|
it "sets method names properly" do
|
|
@@ -143,12 +139,12 @@ module BetterErrors
|
|
|
143
139
|
end
|
|
144
140
|
|
|
145
141
|
frame = StackFrame.from_exception(obj.my_method).first
|
|
146
|
-
if
|
|
142
|
+
if BetterErrors.binding_of_caller_available?
|
|
147
143
|
frame.method_name.should == "#my_method"
|
|
148
144
|
frame.class_name.should == "String"
|
|
149
145
|
else
|
|
150
146
|
frame.method_name.should == "my_method"
|
|
151
|
-
frame.class_name.should
|
|
147
|
+
frame.class_name.should == nil
|
|
152
148
|
end
|
|
153
149
|
end
|
|
154
150
|
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,43 +1,57 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: better_errors
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Charlie Somerville
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2014-08-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: erubis
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: 2.6.6
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: 2.6.6
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: coderay
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- -
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: 1.0.0
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- -
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: 1.0.0
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rack
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 0.9.0
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 0.9.0
|
|
41
55
|
description: Provides a better error page for Rails and other Rack apps. Includes
|
|
42
56
|
source code inspection, a live REPL and local/instance variable inspection for all
|
|
43
57
|
stack frames.
|
|
@@ -47,9 +61,10 @@ executables: []
|
|
|
47
61
|
extensions: []
|
|
48
62
|
extra_rdoc_files: []
|
|
49
63
|
files:
|
|
50
|
-
- .gitignore
|
|
51
|
-
- .travis.yml
|
|
52
|
-
- .yardopts
|
|
64
|
+
- ".gitignore"
|
|
65
|
+
- ".travis.yml"
|
|
66
|
+
- ".yardopts"
|
|
67
|
+
- CHANGELOG.md
|
|
53
68
|
- Gemfile
|
|
54
69
|
- LICENSE.txt
|
|
55
70
|
- README.md
|
|
@@ -59,10 +74,11 @@ files:
|
|
|
59
74
|
- lib/better_errors/code_formatter.rb
|
|
60
75
|
- lib/better_errors/code_formatter/html.rb
|
|
61
76
|
- lib/better_errors/code_formatter/text.rb
|
|
62
|
-
- lib/better_errors/core_ext/exception.rb
|
|
63
77
|
- lib/better_errors/error_page.rb
|
|
78
|
+
- lib/better_errors/exception_extension.rb
|
|
64
79
|
- lib/better_errors/middleware.rb
|
|
65
80
|
- lib/better_errors/rails.rb
|
|
81
|
+
- lib/better_errors/raised_exception.rb
|
|
66
82
|
- lib/better_errors/repl.rb
|
|
67
83
|
- lib/better_errors/repl/basic.rb
|
|
68
84
|
- lib/better_errors/repl/pry.rb
|
|
@@ -74,6 +90,7 @@ files:
|
|
|
74
90
|
- spec/better_errors/code_formatter_spec.rb
|
|
75
91
|
- spec/better_errors/error_page_spec.rb
|
|
76
92
|
- spec/better_errors/middleware_spec.rb
|
|
93
|
+
- spec/better_errors/raised_exception_spec.rb
|
|
77
94
|
- spec/better_errors/repl/basic_spec.rb
|
|
78
95
|
- spec/better_errors/repl/pry_spec.rb
|
|
79
96
|
- spec/better_errors/repl/shared_examples.rb
|
|
@@ -81,6 +98,7 @@ files:
|
|
|
81
98
|
- spec/better_errors/support/my_source.rb
|
|
82
99
|
- spec/better_errors_spec.rb
|
|
83
100
|
- spec/spec_helper.rb
|
|
101
|
+
- spec/without_binding_of_caller.rb
|
|
84
102
|
homepage: https://github.com/charliesome/better_errors
|
|
85
103
|
licenses:
|
|
86
104
|
- MIT
|
|
@@ -91,17 +109,17 @@ require_paths:
|
|
|
91
109
|
- lib
|
|
92
110
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
111
|
requirements:
|
|
94
|
-
- -
|
|
112
|
+
- - ">="
|
|
95
113
|
- !ruby/object:Gem::Version
|
|
96
|
-
version:
|
|
114
|
+
version: 2.0.0
|
|
97
115
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
116
|
requirements:
|
|
99
|
-
- -
|
|
117
|
+
- - ">="
|
|
100
118
|
- !ruby/object:Gem::Version
|
|
101
119
|
version: '0'
|
|
102
120
|
requirements: []
|
|
103
121
|
rubyforge_project:
|
|
104
|
-
rubygems_version: 2.
|
|
122
|
+
rubygems_version: 2.2.2
|
|
105
123
|
signing_key:
|
|
106
124
|
specification_version: 4
|
|
107
125
|
summary: Better error page for Rails and other Rack apps
|
|
@@ -109,6 +127,7 @@ test_files:
|
|
|
109
127
|
- spec/better_errors/code_formatter_spec.rb
|
|
110
128
|
- spec/better_errors/error_page_spec.rb
|
|
111
129
|
- spec/better_errors/middleware_spec.rb
|
|
130
|
+
- spec/better_errors/raised_exception_spec.rb
|
|
112
131
|
- spec/better_errors/repl/basic_spec.rb
|
|
113
132
|
- spec/better_errors/repl/pry_spec.rb
|
|
114
133
|
- spec/better_errors/repl/shared_examples.rb
|
|
@@ -116,4 +135,4 @@ test_files:
|
|
|
116
135
|
- spec/better_errors/support/my_source.rb
|
|
117
136
|
- spec/better_errors_spec.rb
|
|
118
137
|
- spec/spec_helper.rb
|
|
119
|
-
|
|
138
|
+
- spec/without_binding_of_caller.rb
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
class Exception
|
|
2
|
-
original_set_backtrace = instance_method(:set_backtrace)
|
|
3
|
-
|
|
4
|
-
if BetterErrors.binding_of_caller_available?
|
|
5
|
-
define_method :set_backtrace do |*args|
|
|
6
|
-
unless Thread.current[:__better_errors_exception_lock]
|
|
7
|
-
Thread.current[:__better_errors_exception_lock] = true
|
|
8
|
-
begin
|
|
9
|
-
@__better_errors_bindings_stack = binding.callers.drop(1)
|
|
10
|
-
ensure
|
|
11
|
-
Thread.current[:__better_errors_exception_lock] = false
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
original_set_backtrace.bind(self).call(*args)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def __better_errors_bindings_stack
|
|
19
|
-
@__better_errors_bindings_stack || []
|
|
20
|
-
end
|
|
21
|
-
end
|