better_errors 0.3.2 → 0.5.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.
Potentially problematic release.
This version of better_errors might be problematic. Click here for more details.
- data/README.md +4 -2
- data/better_errors.gemspec +3 -1
- data/lib/better_errors.rb +9 -2
- data/lib/better_errors/code_formatter.rb +13 -11
- data/lib/better_errors/code_formatter/html.rb +19 -0
- data/lib/better_errors/code_formatter/text.rb +14 -0
- data/lib/better_errors/error_page.rb +18 -2
- data/lib/better_errors/middleware.rb +44 -11
- data/lib/better_errors/rails.rb +1 -1
- data/lib/better_errors/repl/pry.rb +0 -4
- data/lib/better_errors/stack_frame.rb +13 -13
- data/lib/better_errors/templates/main.erb +22 -6
- data/lib/better_errors/templates/text.erb +21 -0
- data/lib/better_errors/templates/variable_info.erb +2 -2
- data/lib/better_errors/version.rb +1 -1
- data/spec/better_errors/code_formatter_spec.rb +55 -14
- data/spec/better_errors/middleware_spec.rb +19 -1
- data/spec/better_errors/repl/basic_spec.rb +3 -17
- data/spec/better_errors/repl/pry_spec.rb +33 -0
- data/spec/better_errors/repl/shared_examples.rb +22 -0
- data/spec/better_errors/stack_frame_spec.rb +5 -6
- metadata +49 -4
data/README.md
CHANGED
@@ -43,8 +43,10 @@ Here's an example using Sinatra:
|
|
43
43
|
require "sinatra"
|
44
44
|
require "better_errors"
|
45
45
|
|
46
|
-
|
47
|
-
BetterErrors
|
46
|
+
configure :development do
|
47
|
+
use BetterErrors::Middleware
|
48
|
+
BetterErrors.application_root = File.expand_path("..", __FILE__)
|
49
|
+
end
|
48
50
|
|
49
51
|
get "/" do
|
50
52
|
raise "oops"
|
data/better_errors.gemspec
CHANGED
@@ -20,10 +20,12 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.add_development_dependency "rake"
|
21
21
|
s.add_development_dependency "rspec", "~> 2.12.0"
|
22
22
|
s.add_development_dependency "binding_of_caller"
|
23
|
+
s.add_development_dependency "pry"
|
23
24
|
s.add_development_dependency "simplecov"
|
24
25
|
s.add_development_dependency "yard"
|
26
|
+
s.add_development_dependency "redcarpet"
|
25
27
|
|
26
|
-
s.add_dependency "erubis", ">= 2.
|
28
|
+
s.add_dependency "erubis", ">= 2.6.6"
|
27
29
|
s.add_dependency "coderay", ">= 1.0.0"
|
28
30
|
|
29
31
|
# optional dependencies:
|
data/lib/better_errors.rb
CHANGED
@@ -90,7 +90,7 @@ module BetterErrors
|
|
90
90
|
when :sublime, :subl, :st
|
91
91
|
self.editor = "subl://open?url=file://%{file}&line=%{line}"
|
92
92
|
when :macvim, :mvim
|
93
|
-
self.editor = "mvim://open?url=file
|
93
|
+
self.editor = proc { |file, line| "mvim://open?url=file://#{file}&line=#{line}" }
|
94
94
|
when String
|
95
95
|
self.editor = proc { |file, line| editor % { file: URI.encode_www_form_component(file), line: line } }
|
96
96
|
else
|
@@ -101,12 +101,19 @@ module BetterErrors
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
104
|
+
|
105
|
+
# Enables experimental Pry support in the inline REPL
|
106
|
+
#
|
107
|
+
# If you encounter problems while using Pry, *please* file a bug report at
|
108
|
+
# https://github.com/charliesome/better_errors/issues
|
109
|
+
def self.use_pry!
|
110
|
+
REPL::PROVIDERS.unshift const: :Pry, impl: "better_errors/repl/pry"
|
111
|
+
end
|
104
112
|
|
105
113
|
BetterErrors.editor = :textmate
|
106
114
|
end
|
107
115
|
|
108
116
|
begin
|
109
|
-
$:.unshift "/Users/charlie/code/binding_of_caller/lib"
|
110
117
|
require "binding_of_caller"
|
111
118
|
BetterErrors.binding_of_caller_available = true
|
112
119
|
rescue LoadError => e
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module BetterErrors
|
2
2
|
# @private
|
3
3
|
class CodeFormatter
|
4
|
+
require "better_errors/code_formatter/html"
|
5
|
+
require "better_errors/code_formatter/text"
|
6
|
+
|
4
7
|
FILE_TYPES = {
|
5
8
|
".rb" => :ruby,
|
6
9
|
"" => :ruby,
|
@@ -16,26 +19,25 @@ module BetterErrors
|
|
16
19
|
@line = line
|
17
20
|
@context = context
|
18
21
|
end
|
19
|
-
|
20
|
-
def
|
21
|
-
|
22
|
+
|
23
|
+
def output
|
24
|
+
formatted_code
|
22
25
|
rescue Errno::ENOENT, Errno::EINVAL
|
23
26
|
source_unavailable
|
24
27
|
end
|
25
|
-
|
26
|
-
def
|
27
|
-
|
28
|
+
|
29
|
+
def formatted_code
|
30
|
+
formatted_lines.join
|
28
31
|
end
|
29
32
|
|
30
33
|
def coderay_scanner
|
31
34
|
ext = File.extname(filename)
|
32
35
|
FILE_TYPES[ext] || :text
|
33
36
|
end
|
34
|
-
|
35
|
-
def
|
36
|
-
line_range.zip(
|
37
|
-
|
38
|
-
sprintf '<pre class="%s">%5d %s</pre>', class_name, current_line, str
|
37
|
+
|
38
|
+
def each_line_of(lines, &blk)
|
39
|
+
line_range.zip(lines).map do |current_line, str|
|
40
|
+
yield (current_line == line), current_line, str
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module BetterErrors
|
2
|
+
# @private
|
3
|
+
class CodeFormatter::HTML < CodeFormatter
|
4
|
+
def source_unavailable
|
5
|
+
"<p class='unavailable'>Source is not available</p>"
|
6
|
+
end
|
7
|
+
|
8
|
+
def formatted_lines
|
9
|
+
each_line_of highlighted_lines do |highlight, current_line, str|
|
10
|
+
class_name = highlight ? "highlight" : ""
|
11
|
+
sprintf '<pre class="%s">%5d %s</pre>', class_name, current_line, str
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def formatted_code
|
16
|
+
%{<div class="code">#{super}</div>}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module BetterErrors
|
2
|
+
# @private
|
3
|
+
class CodeFormatter::Text < CodeFormatter
|
4
|
+
def source_unavailable
|
5
|
+
"# Source is not available"
|
6
|
+
end
|
7
|
+
|
8
|
+
def formatted_lines
|
9
|
+
each_line_of context_lines do |highlight, current_line, str|
|
10
|
+
sprintf '%s %3d %s', (highlight ? '>' : ' '), current_line, str
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -51,6 +51,14 @@ module BetterErrors
|
|
51
51
|
def backtrace_frames
|
52
52
|
@backtrace_frames ||= StackFrame.from_exception(exception)
|
53
53
|
end
|
54
|
+
|
55
|
+
def application_frames
|
56
|
+
backtrace_frames.select { |frame| frame.context == :application }
|
57
|
+
end
|
58
|
+
|
59
|
+
def first_frame
|
60
|
+
backtrace_frames.detect { |frame| frame.context == :application } || backtrace_frames.first
|
61
|
+
end
|
54
62
|
|
55
63
|
private
|
56
64
|
def editor_url(frame)
|
@@ -89,8 +97,16 @@ module BetterErrors
|
|
89
97
|
env["PATH_INFO"]
|
90
98
|
end
|
91
99
|
|
92
|
-
def
|
93
|
-
CodeFormatter.new(frame.filename, frame.line).
|
100
|
+
def html_formatted_code_block(frame)
|
101
|
+
CodeFormatter::HTML.new(frame.filename, frame.line).output
|
102
|
+
end
|
103
|
+
|
104
|
+
def text_formatted_code_block(frame)
|
105
|
+
CodeFormatter::Text.new(frame.filename, frame.line).output
|
106
|
+
end
|
107
|
+
|
108
|
+
def text_heading(char, str)
|
109
|
+
str + "\n" + char*str.size
|
94
110
|
end
|
95
111
|
|
96
112
|
def inspect_value(obj)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "json"
|
2
|
+
require "ipaddr"
|
2
3
|
|
3
4
|
module BetterErrors
|
4
5
|
# Better Errors' error handling middleware. Including this in your middleware
|
@@ -36,36 +37,62 @@ module BetterErrors
|
|
36
37
|
# @param [Hash] env
|
37
38
|
# @return [Array]
|
38
39
|
def call(env)
|
40
|
+
if local_request? env
|
41
|
+
better_errors_call env
|
42
|
+
else
|
43
|
+
@app.call env
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
IPV4_LOCAL = IPAddr.new("127.0.0.0/8")
|
49
|
+
IPV6_LOCAL = IPAddr.new("::1/128")
|
50
|
+
|
51
|
+
def local_request?(env)
|
52
|
+
# REMOTE_ADDR is not in the rack spec, so some application servers do
|
53
|
+
# not provide it.
|
54
|
+
return true unless env["REMOTE_ADDR"]
|
55
|
+
ip = IPAddr.new env["REMOTE_ADDR"]
|
56
|
+
IPV4_LOCAL.include? ip or IPV6_LOCAL.include? ip
|
57
|
+
end
|
58
|
+
|
59
|
+
def better_errors_call(env)
|
39
60
|
case env["PATH_INFO"]
|
40
61
|
when %r{\A/__better_errors/(?<oid>-?\d+)/(?<method>\w+)\z}
|
41
62
|
internal_call env, $~
|
42
63
|
when %r{\A/__better_errors/?\z}
|
43
64
|
show_error_page env
|
44
65
|
else
|
45
|
-
|
66
|
+
protected_app_call env
|
46
67
|
end
|
47
68
|
end
|
48
|
-
|
49
|
-
|
50
|
-
def app_call(env)
|
69
|
+
|
70
|
+
def protected_app_call(env)
|
51
71
|
@app.call env
|
52
72
|
rescue Exception => ex
|
53
73
|
@error_page = @handler.new ex, env
|
54
74
|
log_exception
|
55
75
|
show_error_page(env)
|
56
76
|
end
|
57
|
-
|
77
|
+
|
58
78
|
def show_error_page(env)
|
59
|
-
content = if @error_page
|
60
|
-
|
79
|
+
type, content = if @error_page
|
80
|
+
if text?(env)
|
81
|
+
[ 'plain', @error_page.render('text') ]
|
82
|
+
else
|
83
|
+
[ 'html', @error_page.render ]
|
84
|
+
end
|
61
85
|
else
|
62
|
-
|
63
|
-
"<code>Better Errors v#{BetterErrors::VERSION}</code>"
|
86
|
+
[ 'html', no_errors_page ]
|
64
87
|
end
|
65
88
|
|
66
|
-
[500, { "Content-Type" => "text
|
89
|
+
[500, { "Content-Type" => "text/#{type}; charset=utf-8" }, [content]]
|
67
90
|
end
|
68
91
|
|
92
|
+
def text?(env)
|
93
|
+
env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" ||
|
94
|
+
!env["HTTP_ACCEPT"].to_s.include?('html')
|
95
|
+
end
|
69
96
|
|
70
97
|
def log_exception
|
71
98
|
return unless BetterErrors.logger
|
@@ -82,9 +109,15 @@ module BetterErrors
|
|
82
109
|
if opts[:oid].to_i != @error_page.object_id
|
83
110
|
return [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(error: "Session expired")]]
|
84
111
|
end
|
85
|
-
|
112
|
+
|
113
|
+
env["rack.input"].rewind
|
86
114
|
response = @error_page.send("do_#{opts[:method]}", JSON.parse(env["rack.input"].read))
|
87
115
|
[200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(response)]]
|
88
116
|
end
|
117
|
+
|
118
|
+
def no_errors_page
|
119
|
+
"<h1>No errors</h1><p>No errors have been recorded yet.</p><hr>" +
|
120
|
+
"<code>Better Errors v#{BetterErrors::VERSION}</code>"
|
121
|
+
end
|
89
122
|
end
|
90
123
|
end
|
data/lib/better_errors/rails.rb
CHANGED
@@ -3,7 +3,7 @@ module BetterErrors
|
|
3
3
|
class Railtie < Rails::Railtie
|
4
4
|
initializer "better_errors.configure_rails_initialization" do
|
5
5
|
unless Rails.env.production?
|
6
|
-
Rails.application.middleware.
|
6
|
+
Rails.application.middleware.insert_after ActionDispatch::DebugExceptions, BetterErrors::Middleware
|
7
7
|
BetterErrors.logger = Rails.logger
|
8
8
|
BetterErrors.application_root = Rails.root.to_s
|
9
9
|
end
|
@@ -2,19 +2,19 @@ module BetterErrors
|
|
2
2
|
# @private
|
3
3
|
class StackFrame
|
4
4
|
def self.from_exception(exception)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
5
|
+
if exception.__better_errors_bindings_stack.any?
|
6
|
+
list = exception.__better_errors_bindings_stack.map { |binding|
|
7
|
+
file = binding.eval "__FILE__"
|
8
|
+
line = binding.eval "__LINE__"
|
9
|
+
name = binding.frame_description
|
10
|
+
StackFrame.new(file, line, name, binding)
|
11
|
+
}
|
12
|
+
else
|
13
|
+
list = (exception.backtrace || []).map { |frame|
|
14
|
+
next unless md = /\A(?<file>.*?):(?<line>\d+)(:in `(?<name>.*)')?/.match(frame)
|
15
|
+
StackFrame.new(md[:file], md[:line].to_i, md[:name])
|
16
|
+
}.compact
|
17
|
+
end
|
18
18
|
|
19
19
|
if exception.is_a?(SyntaxError) && exception.to_s =~ /\A(.*):(\d*):/
|
20
20
|
list.unshift StackFrame.new($1, $2.to_i, "")
|
@@ -2,6 +2,10 @@
|
|
2
2
|
<html>
|
3
3
|
<head>
|
4
4
|
<title><%= exception.class %> at <%= request_path %></title>
|
5
|
+
</head>
|
6
|
+
<body>
|
7
|
+
|
8
|
+
<%# Stylesheets are placed in the <body> for Turbolinks compatibility. %>
|
5
9
|
<style>
|
6
10
|
/* Basic reset */
|
7
11
|
* {
|
@@ -143,7 +147,6 @@
|
|
143
147
|
header.exception h2,
|
144
148
|
header.exception p {
|
145
149
|
line-height: 1.4em;
|
146
|
-
height: 1.4em;
|
147
150
|
overflow: hidden;
|
148
151
|
white-space: pre;
|
149
152
|
text-overflow: ellipsis;
|
@@ -660,11 +663,17 @@
|
|
660
663
|
.code:hover::-webkit-scrollbar-thumb {
|
661
664
|
background: #888;
|
662
665
|
}
|
666
|
+
</style>
|
663
667
|
|
668
|
+
<%#
|
669
|
+
If Rails's Turbolinks is used, the Better Errors page is probably
|
670
|
+
rendered in the host app's layout. Let's empty out the styles of the
|
671
|
+
host app.
|
672
|
+
%>
|
673
|
+
<script>
|
674
|
+
if (window.Turbolinks) document.head.innerHTML = "";
|
675
|
+
</script>
|
664
676
|
|
665
|
-
</style>
|
666
|
-
</head>
|
667
|
-
<body>
|
668
677
|
<div class='top'>
|
669
678
|
<header class="exception">
|
670
679
|
<h2><strong><%= exception.class %></strong> <span>at <%= request_path %></span></h2>
|
@@ -680,7 +689,7 @@
|
|
680
689
|
</nav>
|
681
690
|
<ul class="frames">
|
682
691
|
<% backtrace_frames.each_with_index do |frame, index| %>
|
683
|
-
<li class="<%= frame.context %>"
|
692
|
+
<li class="<%= frame.context %>" data-context="<%= frame.context %>" data-index="<%= index %>">
|
684
693
|
<span class='stroke'></span>
|
685
694
|
<i class="icon <%= frame.context %>"></i>
|
686
695
|
<div class="info">
|
@@ -703,6 +712,7 @@
|
|
703
712
|
</body>
|
704
713
|
<script>
|
705
714
|
(function() {
|
715
|
+
|
706
716
|
var OID = <%== object_id.to_s.inspect %>;
|
707
717
|
|
708
718
|
var previousFrame = null;
|
@@ -890,7 +900,7 @@
|
|
890
900
|
document.querySelector(".frames li.application") ||
|
891
901
|
document.querySelector(".frames li")
|
892
902
|
).onclick();
|
893
|
-
|
903
|
+
|
894
904
|
var applicationFramesButton = document.getElementById("application_frames");
|
895
905
|
var allFramesButton = document.getElementById("all_frames");
|
896
906
|
|
@@ -920,4 +930,10 @@
|
|
920
930
|
})();
|
921
931
|
</script>
|
922
932
|
</html>
|
933
|
+
|
934
|
+
<!--
|
935
|
+
|
936
|
+
<%== render('text') %>
|
937
|
+
|
938
|
+
-->
|
923
939
|
<!-- generated by Better Errors in <%= Time.now.to_f - @start_time %> seconds -->
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<%== text_heading("=", "%s at %s" % [exception.class, request_path]) %>
|
2
|
+
|
3
|
+
> <%== exception_message %>
|
4
|
+
<% if backtrace_frames.any? %>
|
5
|
+
|
6
|
+
<%== text_heading("-", "%s, line %i" % [first_frame.pretty_path, first_frame.line]) %>
|
7
|
+
|
8
|
+
``` ruby
|
9
|
+
<%== text_formatted_code_block(first_frame) %>```
|
10
|
+
|
11
|
+
App backtrace
|
12
|
+
-------------
|
13
|
+
|
14
|
+
<%== application_frames.map { |s| " - #{s}" }.join("\n") %>
|
15
|
+
|
16
|
+
Full backtrace
|
17
|
+
--------------
|
18
|
+
|
19
|
+
<%== backtrace_frames.map { |s| " - #{s}" }.join("\n") %>
|
20
|
+
|
21
|
+
<% end %>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<div class="location"><span class="filename"><a href="<%= editor_url(@frame) %>"><%= @frame.pretty_path %></a></span></div>
|
5
5
|
</div>
|
6
6
|
|
7
|
-
<%==
|
7
|
+
<%== html_formatted_code_block @frame %>
|
8
8
|
|
9
9
|
<% if BetterErrors.binding_of_caller_available? && @frame.frame_binding %>
|
10
10
|
<div class="repl">
|
@@ -18,7 +18,7 @@
|
|
18
18
|
|
19
19
|
<% if BetterErrors.binding_of_caller_available? && @frame.frame_binding %>
|
20
20
|
<div class="hint">
|
21
|
-
This a live shell. Type in here.
|
21
|
+
This is a live shell. Type in here.
|
22
22
|
</div>
|
23
23
|
|
24
24
|
<div class="variable_info"></div>
|
@@ -27,25 +27,66 @@ module BetterErrors
|
|
27
27
|
"thirteen\n"
|
28
28
|
]
|
29
29
|
end
|
30
|
-
|
31
|
-
it "should highlight the erroring line" do
|
32
|
-
formatter.html.should =~ /highlight.*eight/
|
33
|
-
end
|
34
|
-
|
30
|
+
|
35
31
|
it "should work when the line is right on the edge" do
|
36
32
|
formatter = CodeFormatter.new(filename, 20)
|
37
33
|
formatter.line_range.should == (15..20)
|
38
|
-
formatter.html.should_not == formatter.source_unavailable
|
39
34
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
35
|
+
|
36
|
+
describe CodeFormatter::HTML do
|
37
|
+
it "should highlight the erroring line" do
|
38
|
+
formatter = CodeFormatter::HTML.new(filename, 8)
|
39
|
+
formatter.output.should =~ /highlight.*eight/
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should work when the line is right on the edge" do
|
43
|
+
formatter = CodeFormatter::HTML.new(filename, 20)
|
44
|
+
formatter.output.should_not == formatter.source_unavailable
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should not barf when the lines don't make any sense" do
|
48
|
+
formatter = CodeFormatter::HTML.new(filename, 999)
|
49
|
+
formatter.output.should == formatter.source_unavailable
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not barf when the file doesn't exist" do
|
53
|
+
formatter = CodeFormatter::HTML.new("fkdguhskd7e l", 1)
|
54
|
+
formatter.output.should == formatter.source_unavailable
|
55
|
+
end
|
44
56
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
57
|
+
|
58
|
+
describe CodeFormatter::Text do
|
59
|
+
it "should highlight the erroring line" do
|
60
|
+
formatter = CodeFormatter::Text.new(filename, 8)
|
61
|
+
formatter.output.should == <<-TEXT.gsub(/^ /, "")
|
62
|
+
3 three
|
63
|
+
4 four
|
64
|
+
5 five
|
65
|
+
6 six
|
66
|
+
7 seven
|
67
|
+
> 8 eight
|
68
|
+
9 nine
|
69
|
+
10 ten
|
70
|
+
11 eleven
|
71
|
+
12 twelve
|
72
|
+
13 thirteen
|
73
|
+
TEXT
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should work when the line is right on the edge" do
|
77
|
+
formatter = CodeFormatter::Text.new(filename, 20)
|
78
|
+
formatter.output.should_not == formatter.source_unavailable
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should not barf when the lines don't make any sense" do
|
82
|
+
formatter = CodeFormatter::Text.new(filename, 999)
|
83
|
+
formatter.output.should == formatter.source_unavailable
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should not barf when the file doesn't exist" do
|
87
|
+
formatter = CodeFormatter::Text.new("fkdguhskd7e l", 1)
|
88
|
+
formatter.output.should == formatter.source_unavailable
|
89
|
+
end
|
49
90
|
end
|
50
91
|
end
|
51
92
|
end
|
@@ -18,6 +18,11 @@ module BetterErrors
|
|
18
18
|
app.call("PATH_INFO" => "/__better_errors/")
|
19
19
|
end
|
20
20
|
|
21
|
+
it "should not show the error page to a non-local address" do
|
22
|
+
app.should_not_receive :better_errors_call
|
23
|
+
app.call("REMOTE_ADDR" => "1.2.3.4")
|
24
|
+
end
|
25
|
+
|
21
26
|
context "when requesting the /__better_errors manually" do
|
22
27
|
let(:app) { Middleware.new(->env { ":)" }) }
|
23
28
|
|
@@ -39,7 +44,20 @@ module BetterErrors
|
|
39
44
|
it "should return UTF-8 error pages" do
|
40
45
|
status, headers, body = app.call({})
|
41
46
|
|
42
|
-
headers["Content-Type"].should
|
47
|
+
headers["Content-Type"].should match /charset=utf-8/
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should return text pages by default" do
|
51
|
+
status, headers, body = app.call({})
|
52
|
+
|
53
|
+
headers["Content-Type"].should match /text\/plain/
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should return HTML pages by default" do
|
57
|
+
# Chrome's 'Accept' header looks similar this.
|
58
|
+
status, headers, body = app.call("HTTP_ACCEPT" => "text/html,application/xhtml+xml;q=0.9,*/*")
|
59
|
+
|
60
|
+
headers["Content-Type"].should match /text\/html/
|
43
61
|
end
|
44
62
|
|
45
63
|
it "should log the exception" do
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "better_errors/repl/basic"
|
3
|
+
require "better_errors/repl/shared_examples"
|
3
4
|
|
4
5
|
module BetterErrors
|
5
6
|
module REPL
|
@@ -10,23 +11,8 @@ module BetterErrors
|
|
10
11
|
}
|
11
12
|
|
12
13
|
let(:repl) { Basic.new fresh_binding }
|
13
|
-
|
14
|
-
|
15
|
-
repl.send_input("local_a = 456")
|
16
|
-
fresh_binding.eval("local_a").should == 456
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should return a tuple of output and the new prompt" do
|
20
|
-
output, prompt = repl.send_input("1 + 2")
|
21
|
-
output.should == "=> 3\n"
|
22
|
-
prompt.should == ">>"
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should not barf if the code throws an exception" do
|
26
|
-
output, prompt = repl.send_input("raise Exception")
|
27
|
-
output.should == "!! #<Exception: Exception>\n"
|
28
|
-
prompt.should == ">>"
|
29
|
-
end
|
14
|
+
|
15
|
+
it_behaves_like "a good repl should"
|
30
16
|
end
|
31
17
|
end
|
32
18
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "pry"
|
3
|
+
require "better_errors/repl/pry"
|
4
|
+
require "better_errors/repl/shared_examples"
|
5
|
+
|
6
|
+
module BetterErrors
|
7
|
+
module REPL
|
8
|
+
describe Pry do
|
9
|
+
let(:fresh_binding) {
|
10
|
+
local_a = 123
|
11
|
+
binding
|
12
|
+
}
|
13
|
+
|
14
|
+
let(:repl) { Pry.new fresh_binding }
|
15
|
+
|
16
|
+
it_behaves_like "a good repl should"
|
17
|
+
|
18
|
+
it "should do line continuation" do
|
19
|
+
output, prompt = repl.send_input ""
|
20
|
+
output.should == "=> nil\n"
|
21
|
+
prompt.should == ">>"
|
22
|
+
|
23
|
+
output, prompt = repl.send_input "def f(x)"
|
24
|
+
output.should == ""
|
25
|
+
prompt.should == ".."
|
26
|
+
|
27
|
+
output, prompt = repl.send_input "end"
|
28
|
+
output.should == "=> nil\n"
|
29
|
+
prompt.should == ">>"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module BetterErrors
|
2
|
+
module REPL
|
3
|
+
shared_examples "a good repl should" do
|
4
|
+
it "should evaluate ruby code in a given context" do
|
5
|
+
repl.send_input("local_a = 456")
|
6
|
+
fresh_binding.eval("local_a").should == 456
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return a tuple of output and the new prompt" do
|
10
|
+
output, prompt = repl.send_input("1 + 2")
|
11
|
+
output.should == "=> 3\n"
|
12
|
+
prompt.should == ">>"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should not barf if the code throws an exception" do
|
16
|
+
output, prompt = repl.send_input("raise Exception")
|
17
|
+
output.should include "Exception: Exception"
|
18
|
+
prompt.should == ">>"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -82,16 +82,15 @@ module BetterErrors
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it "should special case SyntaxErrors" do
|
85
|
-
syntax_error = SyntaxError.
|
86
|
-
|
85
|
+
syntax_error = SyntaxError.allocate
|
86
|
+
Exception.instance_method(:initialize).bind(syntax_error).call("my_file.rb:123: you wrote bad ruby!")
|
87
87
|
frames = StackFrame.from_exception(syntax_error)
|
88
|
-
frames.count.should == 1
|
89
88
|
frames.first.filename.should == "my_file.rb"
|
90
89
|
frames.first.line.should == 123
|
91
90
|
end
|
92
91
|
|
93
92
|
it "should not blow up if no method name is given" do
|
94
|
-
error = StandardError.
|
93
|
+
error = StandardError.allocate
|
95
94
|
|
96
95
|
error.stub!(:backtrace).and_return(["foo.rb:123"])
|
97
96
|
frames = StackFrame.from_exception(error)
|
@@ -105,14 +104,14 @@ module BetterErrors
|
|
105
104
|
end
|
106
105
|
|
107
106
|
it "should ignore a backtrace line if its format doesn't make any sense at all" do
|
108
|
-
error = StandardError.
|
107
|
+
error = StandardError.allocate
|
109
108
|
error.stub!(:backtrace).and_return(["foo.rb:123:in `foo'", "C:in `find'", "bar.rb:123:in `bar'"])
|
110
109
|
frames = StackFrame.from_exception(error)
|
111
110
|
frames.count.should == 2
|
112
111
|
end
|
113
112
|
|
114
113
|
it "should not blow up if a filename contains a colon" do
|
115
|
-
error = StandardError.
|
114
|
+
error = StandardError.allocate
|
116
115
|
error.stub!(:backtrace).and_return(["crap:filename.rb:123"])
|
117
116
|
frames = StackFrame.from_exception(error)
|
118
117
|
frames.first.filename.should == "crap:filename.rb"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: better_errors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-02-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -59,6 +59,22 @@ dependencies:
|
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: pry
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
62
78
|
- !ruby/object:Gem::Dependency
|
63
79
|
name: simplecov
|
64
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -91,6 +107,22 @@ dependencies:
|
|
91
107
|
- - ! '>='
|
92
108
|
- !ruby/object:Gem::Version
|
93
109
|
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: redcarpet
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
94
126
|
- !ruby/object:Gem::Dependency
|
95
127
|
name: erubis
|
96
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -98,7 +130,7 @@ dependencies:
|
|
98
130
|
requirements:
|
99
131
|
- - ! '>='
|
100
132
|
- !ruby/object:Gem::Version
|
101
|
-
version: 2.
|
133
|
+
version: 2.6.6
|
102
134
|
type: :runtime
|
103
135
|
prerelease: false
|
104
136
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -106,7 +138,7 @@ dependencies:
|
|
106
138
|
requirements:
|
107
139
|
- - ! '>='
|
108
140
|
- !ruby/object:Gem::Version
|
109
|
-
version: 2.
|
141
|
+
version: 2.6.6
|
110
142
|
- !ruby/object:Gem::Dependency
|
111
143
|
name: coderay
|
112
144
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,6 +174,8 @@ files:
|
|
142
174
|
- better_errors.gemspec
|
143
175
|
- lib/better_errors.rb
|
144
176
|
- lib/better_errors/code_formatter.rb
|
177
|
+
- lib/better_errors/code_formatter/html.rb
|
178
|
+
- lib/better_errors/code_formatter/text.rb
|
145
179
|
- lib/better_errors/core_ext/exception.rb
|
146
180
|
- lib/better_errors/error_page.rb
|
147
181
|
- lib/better_errors/middleware.rb
|
@@ -151,12 +185,15 @@ files:
|
|
151
185
|
- lib/better_errors/repl/pry.rb
|
152
186
|
- lib/better_errors/stack_frame.rb
|
153
187
|
- lib/better_errors/templates/main.erb
|
188
|
+
- lib/better_errors/templates/text.erb
|
154
189
|
- lib/better_errors/templates/variable_info.erb
|
155
190
|
- lib/better_errors/version.rb
|
156
191
|
- spec/better_errors/code_formatter_spec.rb
|
157
192
|
- spec/better_errors/error_page_spec.rb
|
158
193
|
- spec/better_errors/middleware_spec.rb
|
159
194
|
- spec/better_errors/repl/basic_spec.rb
|
195
|
+
- spec/better_errors/repl/pry_spec.rb
|
196
|
+
- spec/better_errors/repl/shared_examples.rb
|
160
197
|
- spec/better_errors/stack_frame_spec.rb
|
161
198
|
- spec/better_errors/support/my_source.rb
|
162
199
|
- spec/better_errors_spec.rb
|
@@ -174,12 +211,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
174
211
|
- - ! '>='
|
175
212
|
- !ruby/object:Gem::Version
|
176
213
|
version: '0'
|
214
|
+
segments:
|
215
|
+
- 0
|
216
|
+
hash: -1514096288284811368
|
177
217
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
178
218
|
none: false
|
179
219
|
requirements:
|
180
220
|
- - ! '>='
|
181
221
|
- !ruby/object:Gem::Version
|
182
222
|
version: '0'
|
223
|
+
segments:
|
224
|
+
- 0
|
225
|
+
hash: -1514096288284811368
|
183
226
|
requirements: []
|
184
227
|
rubyforge_project:
|
185
228
|
rubygems_version: 1.8.24
|
@@ -191,6 +234,8 @@ test_files:
|
|
191
234
|
- spec/better_errors/error_page_spec.rb
|
192
235
|
- spec/better_errors/middleware_spec.rb
|
193
236
|
- spec/better_errors/repl/basic_spec.rb
|
237
|
+
- spec/better_errors/repl/pry_spec.rb
|
238
|
+
- spec/better_errors/repl/shared_examples.rb
|
194
239
|
- spec/better_errors/stack_frame_spec.rb
|
195
240
|
- spec/better_errors/support/my_source.rb
|
196
241
|
- spec/better_errors_spec.rb
|