better_errors 0.0.1 → 0.0.4
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.
- data/README.md +15 -2
- data/lib/better_errors/core_ext/exception.rb +1 -4
- data/lib/better_errors/error_frame.rb +26 -3
- data/lib/better_errors/error_page.rb +28 -12
- data/lib/better_errors/middleware.rb +22 -2
- data/lib/better_errors/{error_page.erb → templates/main.erb} +155 -25
- 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,11 +2,18 @@
|
|
|
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
|
-

|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
* Full stack trace
|
|
10
|
+
* Source code inspection for all stack frames (with highlighting)
|
|
11
|
+
* Local and instance variable inspection
|
|
12
|
+
* Ruby console on every stack frame
|
|
6
13
|
|
|
7
14
|
## Installation
|
|
8
15
|
|
|
9
|
-
Add this line to your application's Gemfile:
|
|
16
|
+
Add this line to your application's Gemfile (under the **development** group):
|
|
10
17
|
|
|
11
18
|
gem 'better_errors'
|
|
12
19
|
|
|
@@ -18,6 +25,12 @@ Or install it yourself as:
|
|
|
18
25
|
|
|
19
26
|
$ gem install better_errors
|
|
20
27
|
|
|
28
|
+
If you would like to use Better Errors' **advanced features**, you need to install the `binding_of_caller` gem:
|
|
29
|
+
|
|
30
|
+
$ gem install binding_of_caller
|
|
31
|
+
|
|
32
|
+
This is an optional dependency however, and Better Errors will work without it.
|
|
33
|
+
|
|
21
34
|
## Usage
|
|
22
35
|
|
|
23
36
|
If you're using Rails, there's nothing else you need to do.
|
|
@@ -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,17 +3,22 @@ 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
|
|
9
|
+
ErrorFrame.new($1, $2.to_i, $3, b)
|
|
7
10
|
}.compact
|
|
8
11
|
end
|
|
9
12
|
|
|
10
13
|
attr_reader :filename, :line, :name, :frame_binding
|
|
11
14
|
|
|
12
|
-
def initialize(filename, line, name, frame_binding)
|
|
15
|
+
def initialize(filename, line, name, frame_binding = nil)
|
|
13
16
|
@filename = filename
|
|
14
17
|
@line = line
|
|
15
18
|
@name = name
|
|
16
19
|
@frame_binding = frame_binding
|
|
20
|
+
|
|
21
|
+
set_pretty_method_name if frame_binding
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
def application?
|
|
@@ -56,7 +61,14 @@ module BetterErrors
|
|
|
56
61
|
|
|
57
62
|
def local_variables
|
|
58
63
|
return {} unless frame_binding
|
|
59
|
-
|
|
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
|
|
60
72
|
end
|
|
61
73
|
|
|
62
74
|
def instance_variables
|
|
@@ -65,6 +77,17 @@ module BetterErrors
|
|
|
65
77
|
end
|
|
66
78
|
|
|
67
79
|
private
|
|
80
|
+
def set_pretty_method_name
|
|
81
|
+
name =~ /\A(block (\([^)]+\) )?in )?/
|
|
82
|
+
recv = frame_binding.eval("self")
|
|
83
|
+
method = frame_binding.eval("__method__")
|
|
84
|
+
@name = if recv.is_a? Module
|
|
85
|
+
"#{$1}#{recv}.#{method}"
|
|
86
|
+
else
|
|
87
|
+
"#{$1}#{recv.class}##{method}"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
68
91
|
def starts_with?(haystack, needle)
|
|
69
92
|
haystack[0, needle.length] == needle
|
|
70
93
|
end
|
|
@@ -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,20 +15,36 @@ 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
|
|
25
43
|
def real_exception(exception)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return exception
|
|
31
|
-
end
|
|
44
|
+
if exception.respond_to? :original_exception
|
|
45
|
+
exception.original_exception
|
|
46
|
+
else
|
|
47
|
+
exception
|
|
32
48
|
end
|
|
33
49
|
end
|
|
34
50
|
|
|
@@ -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
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
}
|
|
15
15
|
header {
|
|
16
16
|
padding:32px 32px;
|
|
17
|
-
border-bottom:1px solid #
|
|
17
|
+
border-bottom:1px solid #e64c38;
|
|
18
18
|
background-color: #ffe5e7;
|
|
19
19
|
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffe5e7), to(#ffb2b8)); /* Safari 4+, Chrome */
|
|
20
20
|
background-image: -webkit-linear-gradient(top, #ffe5e7, #ffb2b8); /* Chrome 10+, Safari 5.1+, iOS 5+ */
|
|
@@ -41,6 +41,40 @@
|
|
|
41
41
|
header table th, header table td {
|
|
42
42
|
padding:4px 6px;
|
|
43
43
|
}
|
|
44
|
+
nav {
|
|
45
|
+
display:block;
|
|
46
|
+
padding:12px 12px;
|
|
47
|
+
border-bottom:1px solid #000000;
|
|
48
|
+
background-color: #444444;
|
|
49
|
+
background-image: -webkit-gradient(linear, left top, left bottom, from(#444444), to(#222222)); /* Safari 4+, Chrome */
|
|
50
|
+
background-image: -webkit-linear-gradient(top, #444444, #222222); /* Chrome 10+, Safari 5.1+, iOS 5+ */
|
|
51
|
+
background-image: -moz-linear-gradient(top, #444444, #222222); /* Firefox 3.6-15 */
|
|
52
|
+
background-image: -o-linear-gradient(top, #444444, #222222); /* Opera 11.10-12.00 */
|
|
53
|
+
background-image: linear-gradient(to bottom, #444444, #222222); /* Firefox 16+, IE10, Opera 12.50+ */
|
|
54
|
+
}
|
|
55
|
+
nav a {
|
|
56
|
+
color:#ffffff;
|
|
57
|
+
text-decoration:none;
|
|
58
|
+
padding:4px 16px;
|
|
59
|
+
border-radius:16px;
|
|
60
|
+
}
|
|
61
|
+
nav a:hover {
|
|
62
|
+
background-color: #666666;
|
|
63
|
+
background-image: -webkit-gradient(linear, left top, left bottom, from(#666666), to(#444444)); /* Safari 4+, Chrome */
|
|
64
|
+
background-image: -webkit-linear-gradient(top, #666666, #444444); /* Chrome 10+, Safari 5.1+, iOS 5+ */
|
|
65
|
+
background-image: -moz-linear-gradient(top, #666666, #444444); /* Firefox 3.6-15 */
|
|
66
|
+
background-image: -o-linear-gradient(top, #666666, #444444); /* Opera 11.10-12.00 */
|
|
67
|
+
background-image: linear-gradient(to bottom, #666666, #444444); /* Firefox 16+, IE10, Opera 12.50+ */
|
|
68
|
+
}
|
|
69
|
+
nav a.selected {
|
|
70
|
+
color:#000000;
|
|
71
|
+
background-color: #efefef;
|
|
72
|
+
background-image: -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#cfcfcf)); /* Safari 4+, Chrome */
|
|
73
|
+
background-image: -webkit-linear-gradient(top, #efefef, #cfcfcf); /* Chrome 10+, Safari 5.1+, iOS 5+ */
|
|
74
|
+
background-image: -moz-linear-gradient(top, #efefef, #cfcfcf); /* Firefox 3.6-15 */
|
|
75
|
+
background-image: -o-linear-gradient(top, #efefef, #cfcfcf); /* Opera 11.10-12.00 */
|
|
76
|
+
background-image: linear-gradient(to bottom, #efefef, #cfcfcf); /* Firefox 16+, IE10, Opera 12.50+ */
|
|
77
|
+
}
|
|
44
78
|
.frames {
|
|
45
79
|
width:50%;
|
|
46
80
|
float:left;
|
|
@@ -120,6 +154,7 @@
|
|
|
120
154
|
font-weight:normal;
|
|
121
155
|
border-top:1px solid #cccccc;
|
|
122
156
|
padding-top:16px;
|
|
157
|
+
margin-bottom:16px;
|
|
123
158
|
}
|
|
124
159
|
.var_table {
|
|
125
160
|
border-collapse:collapse;
|
|
@@ -133,6 +168,33 @@
|
|
|
133
168
|
.var_table tr {
|
|
134
169
|
border-bottom:1px solid #cccccc;
|
|
135
170
|
}
|
|
171
|
+
.repl .console {
|
|
172
|
+
background-color:#ffffff;
|
|
173
|
+
padding:2px 4px;
|
|
174
|
+
border:1px solid #d0d0d0;
|
|
175
|
+
margin-bottom:8px;
|
|
176
|
+
font-family:monospace;
|
|
177
|
+
}
|
|
178
|
+
.repl pre {
|
|
179
|
+
font-size:12px;
|
|
180
|
+
white-space:pre-wrap;
|
|
181
|
+
max-height:400px;
|
|
182
|
+
overflow:auto;
|
|
183
|
+
}
|
|
184
|
+
.repl .prompt {
|
|
185
|
+
font-size:12px;
|
|
186
|
+
position:relative;
|
|
187
|
+
}
|
|
188
|
+
.repl input {
|
|
189
|
+
font-size:12px;
|
|
190
|
+
border:none;
|
|
191
|
+
font-family:monospace;
|
|
192
|
+
outline:none;
|
|
193
|
+
padding:0px;
|
|
194
|
+
position:absolute;
|
|
195
|
+
left:20px;
|
|
196
|
+
right:0px;
|
|
197
|
+
}
|
|
136
198
|
</style>
|
|
137
199
|
</head>
|
|
138
200
|
<body>
|
|
@@ -155,6 +217,10 @@
|
|
|
155
217
|
<% end %>
|
|
156
218
|
</table>
|
|
157
219
|
</header>
|
|
220
|
+
<nav>
|
|
221
|
+
<a href="#" id="application_frames">Application Frames</a>
|
|
222
|
+
<a href="#" id="all_frames">All Frames</a>
|
|
223
|
+
</nav>
|
|
158
224
|
<section class="backtrace">
|
|
159
225
|
<ul class="frames">
|
|
160
226
|
<% backtrace_frames.each_with_index do |frame, index| %>
|
|
@@ -177,19 +243,15 @@
|
|
|
177
243
|
<div class="location"><span class="filename"><%= frame.pretty_path %></span>, line <span class="line"><%= frame.line %></span></div>
|
|
178
244
|
<%== highlighted_code_block frame %>
|
|
179
245
|
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
246
|
+
<div class="repl">
|
|
247
|
+
<h3>REPL</h3>
|
|
248
|
+
<div class="console">
|
|
249
|
+
<pre></pre>
|
|
250
|
+
<div class="prompt">>> <input/></div>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
186
253
|
|
|
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>
|
|
254
|
+
<div class="variable_info"></div>
|
|
193
255
|
</div>
|
|
194
256
|
<% end %>
|
|
195
257
|
<div style="clear:both"></div>
|
|
@@ -197,34 +259,58 @@
|
|
|
197
259
|
</body>
|
|
198
260
|
<script>
|
|
199
261
|
(function() {
|
|
262
|
+
var oid = <%== object_id.to_s.inspect %>;
|
|
263
|
+
|
|
200
264
|
var previous = null;
|
|
201
265
|
var previousFrameInfo = null;
|
|
202
266
|
var frames = document.querySelectorAll("ul.frames li");
|
|
267
|
+
var frameInfos = document.querySelectorAll(".frame_info");
|
|
203
268
|
|
|
204
269
|
var header = document.querySelector("header");
|
|
205
270
|
var headerHeight = header.offsetHeight;
|
|
206
271
|
|
|
272
|
+
apiCall = function(method, opts, cb) {
|
|
273
|
+
var req = new XMLHttpRequest();
|
|
274
|
+
req.open("POST", "/__better_errors/" + oid + "/" + method, true);
|
|
275
|
+
req.setRequestHeader("Content-Type", "application/json");
|
|
276
|
+
req.send(JSON.stringify(opts));
|
|
277
|
+
req.onreadystatechange = function() {
|
|
278
|
+
if(req.readyState == 4) {
|
|
279
|
+
var res = JSON.parse(req.responseText);
|
|
280
|
+
cb(res);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function escapeHTML(html) {
|
|
286
|
+
return html.replace(/&/, "&").replace(/</g, "<");
|
|
287
|
+
}
|
|
288
|
+
|
|
207
289
|
function selectFrameInfo(index) {
|
|
208
290
|
var el = document.getElementById("frame_info_" + index);
|
|
209
291
|
|
|
292
|
+
var varInfo = el.querySelector(".variable_info");
|
|
293
|
+
if(varInfo.innerHTML == "") {
|
|
294
|
+
apiCall("variables", { "index": index }, function(response) {
|
|
295
|
+
if(response.error) {
|
|
296
|
+
varInfo.innerHTML = "<span class='error'>" + escapeHTML(response.error) + "</span>";
|
|
297
|
+
} else {
|
|
298
|
+
varInfo.innerHTML = response.html;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
210
303
|
if(previousFrameInfo) {
|
|
211
304
|
previousFrameInfo.style.display = "none";
|
|
212
305
|
}
|
|
213
306
|
previousFrameInfo = el;
|
|
214
307
|
previousFrameInfo.style.display = "block";
|
|
308
|
+
|
|
309
|
+
el.querySelector(".repl input").focus();
|
|
215
310
|
}
|
|
216
311
|
|
|
217
|
-
function updateMarginTop() {
|
|
218
|
-
return;
|
|
219
|
-
if(previousFrameInfo) {
|
|
220
|
-
previousFrameInfo.style.marginTop = Math.max(window.scrollY - headerHeight, 0) + "px";
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
window.onscroll = updateMarginTop;
|
|
225
|
-
|
|
226
312
|
for(var i = 0; i < frames.length; i++) {
|
|
227
|
-
(function(el) {
|
|
313
|
+
(function(index, el, frameInfo) {
|
|
228
314
|
el.onclick = function() {
|
|
229
315
|
if(previous) {
|
|
230
316
|
previous.className = "";
|
|
@@ -233,12 +319,56 @@
|
|
|
233
319
|
previous = el;
|
|
234
320
|
|
|
235
321
|
selectFrameInfo(el.attributes["data-index"].value);
|
|
236
|
-
updateMarginTop();
|
|
237
322
|
};
|
|
238
|
-
|
|
323
|
+
var replPre = frameInfo.querySelector(".repl pre");
|
|
324
|
+
var replInput = frameInfo.querySelector(".repl input");
|
|
325
|
+
replInput.onkeydown = function(ev) {
|
|
326
|
+
if(ev.keyCode == 13) {
|
|
327
|
+
var text = replInput.value;
|
|
328
|
+
replInput.value = "";
|
|
329
|
+
apiCall("eval", { "index": index, source: text }, function(response) {
|
|
330
|
+
replPre.innerHTML += ">> " + response.highlighted_input + "\n";
|
|
331
|
+
if(response.error) {
|
|
332
|
+
replPre.innerHTML += "!! " + escapeHTML(response.error) + "\n";
|
|
333
|
+
} else {
|
|
334
|
+
replPre.innerHTML += "=> " + escapeHTML(response.result) + "\n";
|
|
335
|
+
}
|
|
336
|
+
replPre.scrollTop = replPre.offsetHeight;
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
})(i, frames[i], frameInfos[i]);
|
|
239
341
|
}
|
|
240
342
|
|
|
241
343
|
document.querySelector(".frames li:first-child").click();
|
|
344
|
+
|
|
345
|
+
var applicationFrames = document.getElementById("application_frames");
|
|
346
|
+
var allFrames = document.getElementById("all_frames");
|
|
347
|
+
|
|
348
|
+
applicationFrames.onclick = function() {
|
|
349
|
+
allFrames.className = "";
|
|
350
|
+
applicationFrames.className = "selected";
|
|
351
|
+
for(var i = 0; i < frames.length; i++) {
|
|
352
|
+
if(frames[i].attributes["data-context"].value == "application") {
|
|
353
|
+
frames[i].style.display = "block";
|
|
354
|
+
} else {
|
|
355
|
+
frames[i].style.display = "none";
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return false;
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
allFrames.onclick = function() {
|
|
362
|
+
applicationFrames.className = "";
|
|
363
|
+
allFrames.className = "selected";
|
|
364
|
+
for(var i = 0; i < frames.length; i++) {
|
|
365
|
+
frames[i].style.display = "block";
|
|
366
|
+
}
|
|
367
|
+
return false;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
applicationFrames.click();
|
|
242
371
|
})();
|
|
243
372
|
</script>
|
|
244
373
|
</html>
|
|
374
|
+
<!-- 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.4
|
|
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
|