better_errors 0.3.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
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
|