better_errors 0.0.2 → 0.0.3
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 +2 -1
- data/lib/better_errors/core_ext/exception.rb +1 -4
- data/lib/better_errors/error_frame.rb +11 -2
- data/lib/better_errors/error_page.rb +24 -6
- data/lib/better_errors/middleware.rb +22 -2
- data/lib/better_errors/{error_page.erb → templates/main.erb} +89 -14
- data/lib/better_errors/templates/variable_info.erb +13 -0
- data/lib/better_errors/version.rb +1 -1
- metadata +4 -3
data/README.md
CHANGED
@@ -2,13 +2,14 @@
|
|
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.
|
4
4
|
|
5
|
-
![image](http://i.imgur.com/
|
5
|
+
![image](http://i.imgur.com/quHUZ.png)
|
6
6
|
|
7
7
|
## Features
|
8
8
|
|
9
9
|
* Full stack trace
|
10
10
|
* Source code inspection for all stack frames (with highlighting)
|
11
11
|
* Local and instance variable inspection
|
12
|
+
* Ruby console on every stack frame
|
12
13
|
|
13
14
|
## Installation
|
14
15
|
|
@@ -7,10 +7,7 @@ class Exception
|
|
7
7
|
unless Thread.current[:__better_errors_exception_lock]
|
8
8
|
Thread.current[:__better_errors_exception_lock] = true
|
9
9
|
begin
|
10
|
-
@__better_errors_bindings_stack =
|
11
|
-
2.upto(caller.size) do |index|
|
12
|
-
@__better_errors_bindings_stack << binding.of_caller(index) rescue break
|
13
|
-
end
|
10
|
+
@__better_errors_bindings_stack = binding.callers.drop(1)
|
14
11
|
ensure
|
15
12
|
Thread.current[:__better_errors_exception_lock] = false
|
16
13
|
end
|
@@ -3,7 +3,9 @@ module BetterErrors
|
|
3
3
|
def self.from_exception(exception)
|
4
4
|
exception.backtrace.each_with_index.map { |frame, idx|
|
5
5
|
next unless frame =~ /\A(.*):(\d*):in `(.*)'\z/
|
6
|
-
|
6
|
+
if BetterErrors.binding_of_caller_available?
|
7
|
+
b = exception.__better_errors_bindings_stack[idx]
|
8
|
+
end
|
7
9
|
ErrorFrame.new($1, $2.to_i, $3, b)
|
8
10
|
}.compact
|
9
11
|
end
|
@@ -59,7 +61,14 @@ module BetterErrors
|
|
59
61
|
|
60
62
|
def local_variables
|
61
63
|
return {} unless frame_binding
|
62
|
-
|
64
|
+
frame_binding.eval("local_variables").each_with_object({}) do |name, hash|
|
65
|
+
begin
|
66
|
+
hash[name] = frame_binding.eval(name.to_s)
|
67
|
+
rescue NameError => e
|
68
|
+
# local_variables sometimes returns broken variables.
|
69
|
+
# https://bugs.ruby-lang.org/issues/7536
|
70
|
+
end
|
71
|
+
end
|
63
72
|
end
|
64
73
|
|
65
74
|
def instance_variables
|
@@ -2,12 +2,12 @@ require "json"
|
|
2
2
|
|
3
3
|
module BetterErrors
|
4
4
|
class ErrorPage
|
5
|
-
def self.template_path
|
6
|
-
|
5
|
+
def self.template_path(template_name)
|
6
|
+
File.expand_path("../templates/#{template_name}.erb", __FILE__)
|
7
7
|
end
|
8
8
|
|
9
|
-
def self.template
|
10
|
-
Erubis::EscapedEruby.new(File.read(template_path))
|
9
|
+
def self.template(template_name)
|
10
|
+
Erubis::EscapedEruby.new(File.read(template_path(template_name)))
|
11
11
|
end
|
12
12
|
|
13
13
|
attr_reader :exception, :env
|
@@ -15,10 +15,28 @@ module BetterErrors
|
|
15
15
|
def initialize(exception, env)
|
16
16
|
@exception = real_exception(exception)
|
17
17
|
@env = env
|
18
|
+
@start_time = Time.now.to_f
|
18
19
|
end
|
19
20
|
|
20
|
-
def render
|
21
|
-
self.class.template.result binding
|
21
|
+
def render(template_name = "main")
|
22
|
+
self.class.template(template_name).result binding
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_variables(opts)
|
26
|
+
index = opts["index"].to_i
|
27
|
+
@frame = backtrace_frames[index]
|
28
|
+
{ html: render("variable_info") }
|
29
|
+
end
|
30
|
+
|
31
|
+
def do_eval(opts)
|
32
|
+
index = opts["index"].to_i
|
33
|
+
response = begin
|
34
|
+
result = backtrace_frames[index].frame_binding.eval(opts["source"])
|
35
|
+
{ result: result.inspect }
|
36
|
+
rescue Exception => e
|
37
|
+
{ error: (e.inspect rescue e.class.name rescue "Exception") }
|
38
|
+
end
|
39
|
+
response.merge(highlighted_input: CodeRay.scan(opts["source"], :ruby).div(wrap: nil))
|
22
40
|
end
|
23
41
|
|
24
42
|
private
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "json"
|
2
|
+
|
1
3
|
module BetterErrors
|
2
4
|
class Middleware
|
3
5
|
def initialize(app, handler = ErrorPage)
|
@@ -6,10 +8,28 @@ module BetterErrors
|
|
6
8
|
end
|
7
9
|
|
8
10
|
def call(env)
|
11
|
+
if env["REQUEST_PATH"] =~ %r{\A/__better_errors/(?<oid>\d+)/(?<method>\w+)\z}
|
12
|
+
internal_call env, $~
|
13
|
+
else
|
14
|
+
app_call env
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def app_call(env)
|
9
20
|
@app.call env
|
10
21
|
rescue Exception => ex
|
11
|
-
error_page = @handler.new ex, env
|
12
|
-
[500, { "Content-Type" => "text/html; charset=utf-8" }, [error_page.render]]
|
22
|
+
@error_page = @handler.new ex, env
|
23
|
+
[500, { "Content-Type" => "text/html; charset=utf-8" }, [@error_page.render]]
|
24
|
+
end
|
25
|
+
|
26
|
+
def internal_call(env, opts)
|
27
|
+
if opts[:oid].to_i != @error_page.object_id
|
28
|
+
return [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(error: "Session expired")]]
|
29
|
+
end
|
30
|
+
|
31
|
+
response = @error_page.send("do_#{opts[:method]}", JSON.parse(env["rack.input"].read))
|
32
|
+
[200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(response)]]
|
13
33
|
end
|
14
34
|
end
|
15
35
|
end
|
@@ -120,6 +120,7 @@
|
|
120
120
|
font-weight:normal;
|
121
121
|
border-top:1px solid #cccccc;
|
122
122
|
padding-top:16px;
|
123
|
+
margin-bottom:16px;
|
123
124
|
}
|
124
125
|
.var_table {
|
125
126
|
border-collapse:collapse;
|
@@ -133,6 +134,33 @@
|
|
133
134
|
.var_table tr {
|
134
135
|
border-bottom:1px solid #cccccc;
|
135
136
|
}
|
137
|
+
.repl .console {
|
138
|
+
background-color:#ffffff;
|
139
|
+
padding:2px 4px;
|
140
|
+
border:1px solid #d0d0d0;
|
141
|
+
margin-bottom:8px;
|
142
|
+
font-family:monospace;
|
143
|
+
}
|
144
|
+
.repl pre {
|
145
|
+
font-size:12px;
|
146
|
+
white-space:pre-wrap;
|
147
|
+
max-height:400px;
|
148
|
+
overflow:auto;
|
149
|
+
}
|
150
|
+
.repl .prompt {
|
151
|
+
font-size:12px;
|
152
|
+
position:relative;
|
153
|
+
}
|
154
|
+
.repl input {
|
155
|
+
font-size:12px;
|
156
|
+
border:none;
|
157
|
+
font-family:monospace;
|
158
|
+
outline:none;
|
159
|
+
padding:0px;
|
160
|
+
position:absolute;
|
161
|
+
left:20px;
|
162
|
+
right:0px;
|
163
|
+
}
|
136
164
|
</style>
|
137
165
|
</head>
|
138
166
|
<body>
|
@@ -177,19 +205,15 @@
|
|
177
205
|
<div class="location"><span class="filename"><%= frame.pretty_path %></span>, line <span class="line"><%= frame.line %></span></div>
|
178
206
|
<%== highlighted_code_block frame %>
|
179
207
|
|
180
|
-
<
|
181
|
-
|
182
|
-
|
183
|
-
<
|
184
|
-
|
185
|
-
|
208
|
+
<div class="repl">
|
209
|
+
<h3>REPL</h3>
|
210
|
+
<div class="console">
|
211
|
+
<pre></pre>
|
212
|
+
<div class="prompt">>> <input/></div>
|
213
|
+
</div>
|
214
|
+
</div>
|
186
215
|
|
187
|
-
<
|
188
|
-
<table class="var_table">
|
189
|
-
<% frame.instance_variables.each do |name, value| %>
|
190
|
-
<tr><td class="name"><%= name %></td><td><pre><%= value.inspect %></pre></td></tr>
|
191
|
-
<% end %>
|
192
|
-
</table>
|
216
|
+
<div class="variable_info"></div>
|
193
217
|
</div>
|
194
218
|
<% end %>
|
195
219
|
<div style="clear:both"></div>
|
@@ -197,25 +221,58 @@
|
|
197
221
|
</body>
|
198
222
|
<script>
|
199
223
|
(function() {
|
224
|
+
var oid = <%== object_id.to_s.inspect %>;
|
225
|
+
|
200
226
|
var previous = null;
|
201
227
|
var previousFrameInfo = null;
|
202
228
|
var frames = document.querySelectorAll("ul.frames li");
|
229
|
+
var frameInfos = document.querySelectorAll(".frame_info");
|
203
230
|
|
204
231
|
var header = document.querySelector("header");
|
205
232
|
var headerHeight = header.offsetHeight;
|
206
233
|
|
234
|
+
apiCall = function(method, opts, cb) {
|
235
|
+
var req = new XMLHttpRequest();
|
236
|
+
req.open("POST", "/__better_errors/" + oid + "/" + method, true);
|
237
|
+
req.setRequestHeader("Content-Type", "application/json");
|
238
|
+
req.send(JSON.stringify(opts));
|
239
|
+
req.onreadystatechange = function() {
|
240
|
+
if(req.readyState == 4) {
|
241
|
+
var res = JSON.parse(req.responseText);
|
242
|
+
cb(res);
|
243
|
+
}
|
244
|
+
};
|
245
|
+
}
|
246
|
+
|
247
|
+
function escapeHTML(html) {
|
248
|
+
return html.replace(/&/, "&").replace(/</g, "<");
|
249
|
+
}
|
250
|
+
|
207
251
|
function selectFrameInfo(index) {
|
208
252
|
var el = document.getElementById("frame_info_" + index);
|
209
253
|
|
254
|
+
var varInfo = el.querySelector(".variable_info");
|
255
|
+
if(varInfo.innerHTML == "") {
|
256
|
+
apiCall("variables", { "index": index }, function(response) {
|
257
|
+
if(response.error) {
|
258
|
+
varInfo.innerHTML = "<span class='error'>" + escapeHTML(response.error) + "</span>";
|
259
|
+
} else {
|
260
|
+
varInfo.innerHTML = response.html;
|
261
|
+
}
|
262
|
+
});
|
263
|
+
}
|
264
|
+
|
210
265
|
if(previousFrameInfo) {
|
211
266
|
previousFrameInfo.style.display = "none";
|
212
267
|
}
|
213
268
|
previousFrameInfo = el;
|
214
269
|
previousFrameInfo.style.display = "block";
|
270
|
+
|
271
|
+
el.querySelector(".repl input").focus();
|
215
272
|
}
|
216
273
|
|
217
274
|
for(var i = 0; i < frames.length; i++) {
|
218
|
-
(function(el) {
|
275
|
+
(function(index, el, frameInfo) {
|
219
276
|
el.onclick = function() {
|
220
277
|
if(previous) {
|
221
278
|
previous.className = "";
|
@@ -225,10 +282,28 @@
|
|
225
282
|
|
226
283
|
selectFrameInfo(el.attributes["data-index"].value);
|
227
284
|
};
|
228
|
-
|
285
|
+
var replPre = frameInfo.querySelector(".repl pre");
|
286
|
+
var replInput = frameInfo.querySelector(".repl input");
|
287
|
+
replInput.onkeydown = function(ev) {
|
288
|
+
if(ev.keyCode == 13) {
|
289
|
+
var text = replInput.value;
|
290
|
+
replInput.value = "";
|
291
|
+
apiCall("eval", { "index": index, source: text }, function(response) {
|
292
|
+
replPre.innerHTML += ">> " + response.highlighted_input + "\n";
|
293
|
+
if(response.error) {
|
294
|
+
replPre.innerHTML += "!! " + escapeHTML(response.error) + "\n";
|
295
|
+
} else {
|
296
|
+
replPre.innerHTML += "=> " + escapeHTML(response.result) + "\n";
|
297
|
+
}
|
298
|
+
replPre.scrollTop = replPre.offsetHeight;
|
299
|
+
});
|
300
|
+
}
|
301
|
+
};
|
302
|
+
})(i, frames[i], frameInfos[i]);
|
229
303
|
}
|
230
304
|
|
231
305
|
document.querySelector(".frames li:first-child").click();
|
232
306
|
})();
|
233
307
|
</script>
|
234
308
|
</html>
|
309
|
+
<!-- generated by Better Errors in <%= Time.now.to_f - @start_time %> seconds -->
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<h3>Local Variables</h3>
|
2
|
+
<table class="var_table">
|
3
|
+
<% @frame.local_variables.each do |name, value| %>
|
4
|
+
<tr><td class="name"><%= name %></td><td><pre><%= value.inspect %></pre></td></tr>
|
5
|
+
<% end %>
|
6
|
+
</table>
|
7
|
+
|
8
|
+
<h3>Instance Variables</h3>
|
9
|
+
<table class="var_table">
|
10
|
+
<% @frame.instance_variables.each do |name, value| %>
|
11
|
+
<tr><td class="name"><%= name %></td><td><pre><%= value.inspect %></pre></td></tr>
|
12
|
+
<% end %>
|
13
|
+
</table>
|
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.0.
|
4
|
+
version: 0.0.3
|
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: 2012-12-
|
12
|
+
date: 2012-12-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -75,10 +75,11 @@ files:
|
|
75
75
|
- lib/better_errors.rb
|
76
76
|
- lib/better_errors/core_ext/exception.rb
|
77
77
|
- lib/better_errors/error_frame.rb
|
78
|
-
- lib/better_errors/error_page.erb
|
79
78
|
- lib/better_errors/error_page.rb
|
80
79
|
- lib/better_errors/middleware.rb
|
81
80
|
- lib/better_errors/rails.rb
|
81
|
+
- lib/better_errors/templates/main.erb
|
82
|
+
- lib/better_errors/templates/variable_info.erb
|
82
83
|
- lib/better_errors/version.rb
|
83
84
|
- spec/better_errors/error_frame_spec.rb
|
84
85
|
- spec/better_errors/error_page_spec.rb
|