better_errors 0.2.0 → 0.3.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/.gitignore +3 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +9 -0
- data/README.md +8 -2
- data/better_errors.gemspec +4 -0
- data/lib/better_errors.rb +95 -9
- data/lib/better_errors/code_formatter.rb +1 -0
- data/lib/better_errors/disable_logging_middleware.rb +16 -0
- data/lib/better_errors/error_page.rb +28 -1
- data/lib/better_errors/middleware.rb +38 -2
- data/lib/better_errors/rails.rb +2 -0
- data/lib/better_errors/repl.rb +1 -0
- data/lib/better_errors/repl/pry.rb +18 -8
- data/lib/better_errors/stack_frame.rb +9 -12
- data/lib/better_errors/templates/main.erb +50 -71
- data/lib/better_errors/templates/variable_info.erb +50 -2
- data/lib/better_errors/version.rb +1 -1
- data/spec/better_errors/error_page_spec.rb +2 -2
- data/spec/better_errors/middleware_spec.rb +21 -1
- data/spec/better_errors/stack_frame_spec.rb +37 -0
- data/spec/better_errors_spec.rb +13 -0
- data/spec/spec_helper.rb +3 -0
- metadata +71 -2
data/.gitignore
CHANGED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown --no-private
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Contributing to Better Errors
|
2
|
+
|
3
|
+
Please make sure to follow these guidelines when contributing code to Better Errors. They will improve the chances of your pull request being merged and they will make my life easier.
|
4
|
+
|
5
|
+
* If you are contributing a large-ish change, please split your changes up into small, logical commits. This is so I have the option of merging in some but not all of your commits. If you just give me one huge commit and there are things that I don't want to merge in, I won't.
|
6
|
+
|
7
|
+
* Whitespace at the end of lines of code is not OK, but empty lines with indentation is fine. Please do not remove these when sending in changes.
|
8
|
+
|
9
|
+
* Don't change things that are unrelated to your main changes unnecessarily. Send another pull request for these changes - don't try to sneak them in.
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ Better Errors replaces the standard Rails error page with a much better and more
|
|
13
13
|
|
14
14
|
## Installation
|
15
15
|
|
16
|
-
Add this
|
16
|
+
Add this to your Gemfile:
|
17
17
|
|
18
18
|
```ruby
|
19
19
|
group :development do
|
@@ -21,7 +21,9 @@ group :development do
|
|
21
21
|
end
|
22
22
|
```
|
23
23
|
|
24
|
-
|
24
|
+
**NOTE:** It is *critical* you put better\_errors in the **development** section. **Do NOT run better_errors in production, or on Internet facing hosts.**
|
25
|
+
|
26
|
+
If you would like to use Better Errors' **advanced features** (REPL, local/instance variable inspection, pretty stack frame names), you need to add the [`binding_of_caller`](https://github.com/banister/binding_of_caller) gem by [@banisterfiend](http://twitter.com/banisterfiend) to your Gemfile:
|
25
27
|
|
26
28
|
```ruby
|
27
29
|
gem "binding_of_caller"
|
@@ -62,6 +64,10 @@ end
|
|
62
64
|
|
63
65
|
* Calling `yield` from the REPL segfaults MRI 1.9.x.
|
64
66
|
|
67
|
+
## Get in touch!
|
68
|
+
|
69
|
+
If you're using better_errors, I'd love to hear from you. Drop me a line and tell me what you think!
|
70
|
+
|
65
71
|
## Contributing
|
66
72
|
|
67
73
|
1. Fork it
|
data/better_errors.gemspec
CHANGED
@@ -18,6 +18,10 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
20
|
s.add_development_dependency "rake"
|
21
|
+
s.add_development_dependency "rspec", "~> 2.12.0"
|
22
|
+
s.add_development_dependency "binding_of_caller"
|
23
|
+
s.add_development_dependency "simplecov"
|
24
|
+
s.add_development_dependency "yard"
|
21
25
|
|
22
26
|
s.add_dependency "erubis", ">= 2.7.0"
|
23
27
|
s.add_dependency "coderay", ">= 1.0.0"
|
data/lib/better_errors.rb
CHANGED
@@ -1,33 +1,119 @@
|
|
1
1
|
require "pp"
|
2
2
|
require "erubis"
|
3
3
|
require "coderay"
|
4
|
+
require "uri"
|
4
5
|
|
5
6
|
require "better_errors/version"
|
6
7
|
require "better_errors/error_page"
|
7
8
|
require "better_errors/stack_frame"
|
8
9
|
require "better_errors/middleware"
|
10
|
+
require "better_errors/disable_logging_middleware"
|
9
11
|
require "better_errors/code_formatter"
|
10
12
|
require "better_errors/repl"
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
+
module BetterErrors
|
15
|
+
class << self
|
16
|
+
# The path to the root of the application. Better Errors uses this property
|
17
|
+
# to determine if a file in a backtrace should be considered an application
|
18
|
+
# frame. If you are using Better Errors with Rails, you do not need to set
|
19
|
+
# this attribute manually.
|
20
|
+
#
|
21
|
+
# @return [String]
|
22
|
+
attr_accessor :application_root
|
23
|
+
|
24
|
+
# The logger to use when logging exception details and backtraces. If you
|
25
|
+
# are using Better Errors with Rails, you do not need to set this attribute
|
26
|
+
# manually. If this attribute is `nil`, nothing will be logged.
|
27
|
+
#
|
28
|
+
# @return [Logger, nil]
|
29
|
+
attr_accessor :logger
|
30
|
+
|
31
|
+
# @private
|
32
|
+
attr_accessor :binding_of_caller_available
|
33
|
+
|
34
|
+
# @private
|
35
|
+
alias_method :binding_of_caller_available?, :binding_of_caller_available
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns a proc, which when called with a filename and line number argument,
|
39
|
+
# returns a URL to open the filename and line in the selected editor.
|
40
|
+
#
|
41
|
+
# Generates TextMate URLs by default.
|
42
|
+
#
|
43
|
+
# BetterErrors.editor["/some/file", 123]
|
44
|
+
# # => txmt://open?url=file:///some/file&line=123
|
45
|
+
#
|
46
|
+
# @return [Proc]
|
47
|
+
def self.editor
|
48
|
+
@editor
|
49
|
+
end
|
50
|
+
|
51
|
+
# Configures how Better Errors generates open-in-editor URLs.
|
52
|
+
#
|
53
|
+
# @overload BetterErrors.editor=(sym)
|
54
|
+
# Uses one of the preset editor configurations. Valid symbols are:
|
55
|
+
#
|
56
|
+
# * `:textmate`, `:txmt`, `:tm`
|
57
|
+
# * `:sublime`, `:subl`, `:st`
|
58
|
+
# * `:macvim`
|
59
|
+
#
|
60
|
+
# @param [Symbol] sym
|
61
|
+
#
|
62
|
+
# @overload BetterErrors.editor=(str)
|
63
|
+
# Uses `str` as the format string for generating open-in-editor URLs.
|
64
|
+
#
|
65
|
+
# Use `%{file}` and `%{line}` as placeholders for the actual values.
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# BetterErrors.editor = "my-editor://open?url=%{file}&line=%{line}"
|
69
|
+
#
|
70
|
+
# @param [String] str
|
71
|
+
#
|
72
|
+
# @overload BetterErrors.editor=(proc)
|
73
|
+
# Uses `proc` to generate open-in-editor URLs. The proc will be called
|
74
|
+
# with `file` and `line` parameters when a URL needs to be generated.
|
75
|
+
#
|
76
|
+
# Your proc should take care to escape `file` appropriately with
|
77
|
+
# `URI.encode_www_form_component` (please note that `URI.escape` is **not**
|
78
|
+
# a suitable substitute.)
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# BetterErrors.editor = proc { |file, line|
|
82
|
+
# "my-editor://open?url=#{URI.encode_www_form_component file}&line=#{line}"
|
83
|
+
# }
|
84
|
+
#
|
85
|
+
# @param [Proc] proc
|
86
|
+
#
|
87
|
+
def self.editor=(editor)
|
88
|
+
case editor
|
89
|
+
when :textmate, :txmt, :tm
|
90
|
+
self.editor = "txmt://open?url=file://%{file}&line=%{line}"
|
91
|
+
when :sublime, :subl, :st
|
92
|
+
self.editor = "subl://open?url=file://%{file}&line=%{line}"
|
93
|
+
when :macvim, :mvim
|
94
|
+
self.editor = "mvim://open?url=file://%{file}&line=%{line}"
|
95
|
+
when String
|
96
|
+
self.editor = proc { |file, line| editor % { file: URI.encode_www_form_component(file), line: line } }
|
97
|
+
else
|
98
|
+
if editor.respond_to? :call
|
99
|
+
@editor = editor
|
100
|
+
else
|
101
|
+
raise TypeError, "Expected editor to be a valid editor key, a format string or a callable."
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
14
105
|
|
15
|
-
|
106
|
+
BetterErrors.editor = :textmate
|
16
107
|
end
|
17
108
|
|
18
109
|
begin
|
110
|
+
$:.unshift "/Users/charlie/code/binding_of_caller/lib"
|
19
111
|
require "binding_of_caller"
|
20
112
|
BetterErrors.binding_of_caller_available = true
|
21
113
|
rescue LoadError => e
|
22
114
|
BetterErrors.binding_of_caller_available = false
|
23
115
|
end
|
24
116
|
|
25
|
-
unless BetterErrors.binding_of_caller_available?
|
26
|
-
warn "BetterErrors: binding_of_caller gem unavailable, cannot display local variables on error pages."
|
27
|
-
warn "Add 'binding_of_caller' to your Gemfile to make this warning go away."
|
28
|
-
warn ""
|
29
|
-
end
|
30
|
-
|
31
117
|
require "better_errors/core_ext/exception"
|
32
118
|
|
33
119
|
require "better_errors/rails" if defined? Rails::Railtie
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module BetterErrors
|
2
|
+
# @private
|
3
|
+
class DisableLoggingMiddleware
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
original_level = BetterErrors.logger.level
|
10
|
+
BetterErrors.logger.level = Logger::ERROR if env['PATH_INFO'].index("/__better_errors") == 0
|
11
|
+
@app.call(env)
|
12
|
+
ensure
|
13
|
+
BetterErrors.logger.level = original_level
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
|
+
require "cgi"
|
1
2
|
require "json"
|
2
3
|
|
3
4
|
module BetterErrors
|
5
|
+
# @private
|
4
6
|
class ErrorPage
|
5
7
|
def self.template_path(template_name)
|
6
8
|
File.expand_path("../templates/#{template_name}.erb", __FILE__)
|
@@ -26,6 +28,7 @@ module BetterErrors
|
|
26
28
|
def do_variables(opts)
|
27
29
|
index = opts["index"].to_i
|
28
30
|
@frame = backtrace_frames[index]
|
31
|
+
@var_start_time = Time.now.to_f
|
29
32
|
{ html: render("variable_info") }
|
30
33
|
end
|
31
34
|
|
@@ -50,6 +53,22 @@ module BetterErrors
|
|
50
53
|
end
|
51
54
|
|
52
55
|
private
|
56
|
+
def editor_url(frame)
|
57
|
+
BetterErrors.editor[frame.filename, frame.line]
|
58
|
+
end
|
59
|
+
|
60
|
+
def rack_session
|
61
|
+
env['rack.session']
|
62
|
+
end
|
63
|
+
|
64
|
+
def rails_params
|
65
|
+
env['action_dispatch.request.parameters']
|
66
|
+
end
|
67
|
+
|
68
|
+
def uri_prefix
|
69
|
+
env["SCRIPT_NAME"] || ""
|
70
|
+
end
|
71
|
+
|
53
72
|
def exception_message
|
54
73
|
if exception.is_a?(SyntaxError) && exception.message =~ /\A.*:\d*: (.*)$/
|
55
74
|
$1
|
@@ -67,11 +86,19 @@ module BetterErrors
|
|
67
86
|
end
|
68
87
|
|
69
88
|
def request_path
|
70
|
-
env["
|
89
|
+
env["PATH_INFO"]
|
71
90
|
end
|
72
91
|
|
73
92
|
def highlighted_code_block(frame)
|
74
93
|
CodeFormatter.new(frame.filename, frame.line).html
|
75
94
|
end
|
95
|
+
|
96
|
+
def inspect_value(obj)
|
97
|
+
CGI.escapeHTML(obj.inspect)
|
98
|
+
rescue NoMethodError
|
99
|
+
"<span class='unsupported'>(object doesn't support inspect)</span>"
|
100
|
+
rescue Exception => e
|
101
|
+
"<span class='unsupported'>(exception was raised in inspect)</span>"
|
102
|
+
end
|
76
103
|
end
|
77
104
|
end
|
@@ -1,15 +1,43 @@
|
|
1
1
|
require "json"
|
2
2
|
|
3
3
|
module BetterErrors
|
4
|
+
# Better Errors' error handling middleware. Including this in your middleware
|
5
|
+
# stack will show a Better Errors error page for exceptions raised below this
|
6
|
+
# middleware.
|
7
|
+
#
|
8
|
+
# If you are using Ruby on Rails, you do not need to manually insert this
|
9
|
+
# middleware into your middleware stack.
|
10
|
+
#
|
11
|
+
# @example Sinatra
|
12
|
+
# require "better_errors"
|
13
|
+
#
|
14
|
+
# if development?
|
15
|
+
# use BetterErrors::Middleware
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# @example Rack
|
19
|
+
# require "better_errors"
|
20
|
+
# if ENV["RACK_ENV"] == "development"
|
21
|
+
# use BetterErrors::Middleware
|
22
|
+
# end
|
23
|
+
#
|
4
24
|
class Middleware
|
25
|
+
# A new instance of BetterErrors::Middleware
|
26
|
+
#
|
27
|
+
# @param app The Rack app/middleware to wrap with Better Errors
|
28
|
+
# @param handler The error handler to use.
|
5
29
|
def initialize(app, handler = ErrorPage)
|
6
30
|
@app = app
|
7
31
|
@handler = handler
|
8
32
|
end
|
9
33
|
|
34
|
+
# Calls the Better Errors middleware
|
35
|
+
#
|
36
|
+
# @param [Hash] env
|
37
|
+
# @return [Array]
|
10
38
|
def call(env)
|
11
39
|
case env["PATH_INFO"]
|
12
|
-
when %r{\A/__better_errors/(?<oid
|
40
|
+
when %r{\A/__better_errors/(?<oid>-?\d+)/(?<method>\w+)\z}
|
13
41
|
internal_call env, $~
|
14
42
|
when %r{\A/__better_errors/?\z}
|
15
43
|
show_error_page env
|
@@ -28,8 +56,16 @@ module BetterErrors
|
|
28
56
|
end
|
29
57
|
|
30
58
|
def show_error_page(env)
|
31
|
-
|
59
|
+
content = if @error_page
|
60
|
+
@error_page.render
|
61
|
+
else
|
62
|
+
"<h1>No errors</h1><p>No errors have been recorded yet.</p><hr>" +
|
63
|
+
"<code>Better Errors v#{BetterErrors::VERSION}</code>"
|
64
|
+
end
|
65
|
+
|
66
|
+
[500, { "Content-Type" => "text/html; charset=utf-8" }, [content]]
|
32
67
|
end
|
68
|
+
|
33
69
|
|
34
70
|
def log_exception
|
35
71
|
return unless BetterErrors.logger
|
data/lib/better_errors/rails.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
module BetterErrors
|
2
|
+
# @private
|
2
3
|
class Railtie < Rails::Railtie
|
3
4
|
initializer "better_errors.configure_rails_initialization" do
|
4
5
|
unless Rails.env.production?
|
5
6
|
Rails.application.middleware.use BetterErrors::Middleware
|
7
|
+
Rails.application.middleware.insert_before Rails::Rack::Logger, BetterErrors::DisableLoggingMiddleware
|
6
8
|
BetterErrors.logger = Rails.logger
|
7
9
|
BetterErrors.application_root = Rails.root.to_s
|
8
10
|
end
|
data/lib/better_errors/repl.rb
CHANGED
@@ -52,15 +52,25 @@ module BetterErrors
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def send_input(str)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
55
|
+
local ::Pry.config, color: false, pager: false do
|
56
|
+
@continued_expression = true
|
57
|
+
@fiber.resume "#{str}\n"
|
58
|
+
[@output.read_buffer, @continued_expression ? ".." : ">>"]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def local(obj, attrs)
|
64
|
+
old_attrs = {}
|
65
|
+
attrs.each do |k, v|
|
66
|
+
old_attrs[k] = obj.send k
|
67
|
+
obj.send "#{k}=", v
|
68
|
+
end
|
69
|
+
yield
|
62
70
|
ensure
|
63
|
-
|
71
|
+
old_attrs.each do |k, v|
|
72
|
+
obj.send "#{k}=", v
|
73
|
+
end
|
64
74
|
end
|
65
75
|
end
|
66
76
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module BetterErrors
|
2
|
+
# @private
|
2
3
|
class StackFrame
|
3
4
|
def self.from_exception(exception)
|
4
5
|
idx_offset = 0
|
5
6
|
list = exception.backtrace.each_with_index.map do |frame, idx|
|
6
7
|
frame_binding = exception.__better_errors_bindings_stack[idx - idx_offset]
|
7
|
-
md = /\A(?<file
|
8
|
+
next unless md = /\A(?<file>.*?):(?<line>\d+)(:in `(?<name>.*)')?/.match(frame)
|
8
9
|
|
9
10
|
# prevent mismatching frames in the backtrace with the binding stack
|
10
11
|
if frame_binding and frame_binding.eval("__FILE__") != md[:file]
|
@@ -13,7 +14,7 @@ module BetterErrors
|
|
13
14
|
end
|
14
15
|
|
15
16
|
StackFrame.new(md[:file], md[:line].to_i, md[:name], frame_binding)
|
16
|
-
end
|
17
|
+
end.compact
|
17
18
|
|
18
19
|
if exception.is_a?(SyntaxError) && exception.to_s =~ /\A(.*):(\d*):/
|
19
20
|
list.unshift StackFrame.new($1, $2.to_i, "")
|
@@ -35,7 +36,7 @@ module BetterErrors
|
|
35
36
|
|
36
37
|
def application?
|
37
38
|
root = BetterErrors.application_root
|
38
|
-
|
39
|
+
filename.index(root) == 0 if root
|
39
40
|
end
|
40
41
|
|
41
42
|
def application_path
|
@@ -43,12 +44,12 @@ module BetterErrors
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def gem?
|
46
|
-
Gem.path.any? { |path|
|
47
|
+
Gem.path.any? { |path| filename.index(path) == 0 }
|
47
48
|
end
|
48
49
|
|
49
50
|
def gem_path
|
50
51
|
Gem.path.each do |path|
|
51
|
-
if
|
52
|
+
if filename.index(path) == 0
|
52
53
|
return filename.gsub("#{path}/gems/", "(gem) ")
|
53
54
|
end
|
54
55
|
end
|
@@ -63,10 +64,10 @@ module BetterErrors
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def context
|
66
|
-
if
|
67
|
-
:application
|
68
|
-
elsif gem?
|
67
|
+
if gem?
|
69
68
|
:gem
|
69
|
+
elsif application?
|
70
|
+
:application
|
70
71
|
else
|
71
72
|
:dunno
|
72
73
|
end
|
@@ -116,9 +117,5 @@ module BetterErrors
|
|
116
117
|
@method_name = "##{method_name}"
|
117
118
|
end
|
118
119
|
end
|
119
|
-
|
120
|
-
def starts_with?(haystack, needle)
|
121
|
-
haystack[0, needle.length] == needle
|
122
|
-
end
|
123
120
|
end
|
124
121
|
end
|
@@ -422,6 +422,16 @@
|
|
422
422
|
font-weight: bold;
|
423
423
|
font-size: 10pt;
|
424
424
|
}
|
425
|
+
|
426
|
+
.trace_info .title .location a {
|
427
|
+
color:inherit;
|
428
|
+
text-decoration:none;
|
429
|
+
border-bottom:1px solid #aaaaaa;
|
430
|
+
}
|
431
|
+
|
432
|
+
.trace_info .title .location a:hover {
|
433
|
+
border-color:#666666;
|
434
|
+
}
|
425
435
|
|
426
436
|
.trace_info .title .name {
|
427
437
|
float: right;
|
@@ -493,7 +503,7 @@
|
|
493
503
|
|
494
504
|
.console pre {
|
495
505
|
padding: 10px 10px 0 10px;
|
496
|
-
max-height:
|
506
|
+
max-height: 400px;
|
497
507
|
overflow-x: none;
|
498
508
|
overflow-y: auto;
|
499
509
|
margin-bottom: -3px;
|
@@ -594,6 +604,8 @@
|
|
594
604
|
font-weight: bold;
|
595
605
|
font-size: 0.8em;
|
596
606
|
padding-right: 20px;
|
607
|
+
|
608
|
+
word-wrap: break-word;
|
597
609
|
}
|
598
610
|
|
599
611
|
.sub table td pre {
|
@@ -608,6 +620,12 @@
|
|
608
620
|
white-space: normal;
|
609
621
|
}
|
610
622
|
|
623
|
+
/* "(object doesn't support inspect)" */
|
624
|
+
.sub .unsupported {
|
625
|
+
font-family: sans-serif;
|
626
|
+
color: #777;
|
627
|
+
}
|
628
|
+
|
611
629
|
/* ---------------------------------------------------------------------
|
612
630
|
* Scrollbar
|
613
631
|
* --------------------------------------------------------------------- */
|
@@ -662,16 +680,7 @@
|
|
662
680
|
</nav>
|
663
681
|
<ul class="frames">
|
664
682
|
<% backtrace_frames.each_with_index do |frame, index| %>
|
665
|
-
<li
|
666
|
-
class="<%= frame.context %>"
|
667
|
-
style="display: none"
|
668
|
-
data-context="<%= frame.context %>"
|
669
|
-
data-full-filename="<%= frame.filename %>"
|
670
|
-
data-filename="<%= frame.pretty_path %>"
|
671
|
-
data-line="<%= frame.line %>"
|
672
|
-
data-name="<%= frame.name %>"
|
673
|
-
data-index="<%= index %>"
|
674
|
-
>
|
683
|
+
<li class="<%= frame.context %>" style="display: none" data-context="<%= frame.context %>" data-index="<%= index %>">
|
675
684
|
<span class='stroke'></span>
|
676
685
|
<i class="icon <%= frame.context %>"></i>
|
677
686
|
<div class="info">
|
@@ -688,41 +697,8 @@
|
|
688
697
|
</nav>
|
689
698
|
|
690
699
|
<% backtrace_frames.each_with_index do |frame, index| %>
|
691
|
-
<div class="frame_info" id="frame_info_<%= index %>" style="display:none;">
|
692
|
-
<header class="trace_info">
|
693
|
-
<div class="title">
|
694
|
-
<h2 class="name"><%= frame.name %></h2>
|
695
|
-
<div class="location"><span class="filename"><%= frame.pretty_path %></span></div>
|
696
|
-
</div>
|
697
|
-
|
698
|
-
<%== highlighted_code_block frame %>
|
699
|
-
|
700
|
-
<% if BetterErrors.binding_of_caller_available? && frame.frame_binding %>
|
701
|
-
<div class="repl">
|
702
|
-
<div class="console">
|
703
|
-
<pre></pre>
|
704
|
-
<div class="prompt"><span>>></span> <input/></div>
|
705
|
-
</div>
|
706
|
-
</div>
|
707
|
-
<% end %>
|
708
|
-
</header>
|
709
|
-
|
710
|
-
<% if BetterErrors.binding_of_caller_available? && frame.frame_binding %>
|
711
|
-
<div class="hint">
|
712
|
-
This a live shell. Type in here.
|
713
|
-
</div>
|
714
|
-
|
715
|
-
<div class="variable_info"></div>
|
716
|
-
<% end %>
|
717
|
-
|
718
|
-
<% unless BetterErrors.binding_of_caller_available? %>
|
719
|
-
<div class="hint">
|
720
|
-
<strong>Tip:</strong> add <code>gem "binding_of_caller"</code> to your Gemfile to enable the REPL and local/instance variable inspection.
|
721
|
-
</div>
|
722
|
-
<% end %>
|
723
|
-
</div>
|
700
|
+
<div class="frame_info" id="frame_info_<%= index %>" style="display:none;"></div>
|
724
701
|
<% end %>
|
725
|
-
<div style="clear:both"></div>
|
726
702
|
</section>
|
727
703
|
</body>
|
728
704
|
<script>
|
@@ -736,7 +712,7 @@
|
|
736
712
|
|
737
713
|
function apiCall(method, opts, cb) {
|
738
714
|
var req = new XMLHttpRequest();
|
739
|
-
req.open("POST", "/__better_errors/" + OID + "/" + method, true);
|
715
|
+
req.open("POST", <%== uri_prefix.gsub("<", "<").inspect %> + "/__better_errors/" + OID + "/" + method, true);
|
740
716
|
req.setRequestHeader("Content-Type", "application/json");
|
741
717
|
req.send(JSON.stringify(opts));
|
742
718
|
req.onreadystatechange = function() {
|
@@ -859,34 +835,41 @@
|
|
859
835
|
}
|
860
836
|
};
|
861
837
|
|
862
|
-
function
|
838
|
+
function switchTo(el) {
|
839
|
+
if(previousFrameInfo) previousFrameInfo.style.display = "none";
|
840
|
+
previousFrameInfo = el;
|
841
|
+
|
842
|
+
el.style.display = "block";
|
843
|
+
|
844
|
+
var replInput = el.querySelector('.console input');
|
845
|
+
if (replInput) replInput.focus();
|
846
|
+
}
|
847
|
+
|
848
|
+
function selectFrameInfo(index) {
|
863
849
|
var el = allFrameInfos[index];
|
864
|
-
|
865
|
-
|
850
|
+
if(el) {
|
851
|
+
if (el.loaded) {
|
852
|
+
return switchTo(el);
|
853
|
+
}
|
854
|
+
|
866
855
|
apiCall("variables", { "index": index }, function(response) {
|
856
|
+
el.loaded = true;
|
867
857
|
if(response.error) {
|
868
|
-
|
858
|
+
el.innerHTML = "<span class='error'>" + escapeHTML(response.error) + "</span>";
|
869
859
|
} else {
|
870
|
-
|
860
|
+
el.innerHTML = response.html;
|
861
|
+
|
862
|
+
var repl = el.querySelector(".repl .console");
|
863
|
+
if(repl) {
|
864
|
+
new REPL(index).install(repl);
|
865
|
+
}
|
866
|
+
|
867
|
+
switchTo(el);
|
871
868
|
}
|
872
869
|
});
|
873
870
|
}
|
874
871
|
}
|
875
872
|
|
876
|
-
function selectFrameInfo(index) {
|
877
|
-
populateVariableInfo(index);
|
878
|
-
|
879
|
-
if(previousFrameInfo) {
|
880
|
-
previousFrameInfo.style.display = "none";
|
881
|
-
}
|
882
|
-
previousFrameInfo = allFrameInfos[index];
|
883
|
-
previousFrameInfo.style.display = "block";
|
884
|
-
|
885
|
-
if(REPL.all[index]) {
|
886
|
-
REPL.all[index].focus();
|
887
|
-
}
|
888
|
-
}
|
889
|
-
|
890
873
|
for(var i = 0; i < allFrames.length; i++) {
|
891
874
|
(function(i, el) {
|
892
875
|
var el = allFrames[i];
|
@@ -899,10 +882,6 @@
|
|
899
882
|
|
900
883
|
selectFrameInfo(el.attributes["data-index"].value);
|
901
884
|
};
|
902
|
-
var repl = allFrameInfos[i].querySelector(".repl .console");
|
903
|
-
if(repl) {
|
904
|
-
new REPL(i).install(repl);
|
905
|
-
}
|
906
885
|
})(i);
|
907
886
|
}
|
908
887
|
|
@@ -910,7 +889,7 @@
|
|
910
889
|
(
|
911
890
|
document.querySelector(".frames li.application") ||
|
912
891
|
document.querySelector(".frames li")
|
913
|
-
).
|
892
|
+
).onclick();
|
914
893
|
|
915
894
|
var applicationFramesButton = document.getElementById("application_frames");
|
916
895
|
var allFramesButton = document.getElementById("all_frames");
|
@@ -937,7 +916,7 @@
|
|
937
916
|
return false;
|
938
917
|
};
|
939
918
|
|
940
|
-
applicationFramesButton.
|
919
|
+
applicationFramesButton.onclick();
|
941
920
|
})();
|
942
921
|
</script>
|
943
922
|
</html>
|
@@ -1,9 +1,55 @@
|
|
1
|
+
<header class="trace_info">
|
2
|
+
<div class="title">
|
3
|
+
<h2 class="name"><%= @frame.name %></h2>
|
4
|
+
<div class="location"><span class="filename"><a href="<%= editor_url(@frame) %>"><%= @frame.pretty_path %></a></span></div>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<%== highlighted_code_block @frame %>
|
8
|
+
|
9
|
+
<% if BetterErrors.binding_of_caller_available? && @frame.frame_binding %>
|
10
|
+
<div class="repl">
|
11
|
+
<div class="console">
|
12
|
+
<pre></pre>
|
13
|
+
<div class="prompt"><span>>></span> <input/></div>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
</header>
|
18
|
+
|
19
|
+
<% if BetterErrors.binding_of_caller_available? && @frame.frame_binding %>
|
20
|
+
<div class="hint">
|
21
|
+
This a live shell. Type in here.
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div class="variable_info"></div>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<% unless BetterErrors.binding_of_caller_available? %>
|
28
|
+
<div class="hint">
|
29
|
+
<strong>Tip:</strong> add <code>gem "binding_of_caller"</code> to your Gemfile to enable the REPL and local/instance variable inspection.
|
30
|
+
</div>
|
31
|
+
<% end %>
|
32
|
+
|
33
|
+
<div class="sub">
|
34
|
+
<h3>Request info</h3>
|
35
|
+
<div class='inset variables'>
|
36
|
+
<table class="var_table">
|
37
|
+
<% if rails_params %>
|
38
|
+
<tr><td class="name">Request parameters</td><td><pre><%== inspect_value rails_params %></pre></td></tr>
|
39
|
+
<% end %>
|
40
|
+
<% if rack_session %>
|
41
|
+
<tr><td class="name">Rack session</td><td><pre><%== inspect_value rack_session %></pre></td></tr>
|
42
|
+
<% end %>
|
43
|
+
</table>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
|
1
47
|
<div class="sub">
|
2
48
|
<h3>Local Variables</h3>
|
3
49
|
<div class='inset variables'>
|
4
50
|
<table class="var_table">
|
5
51
|
<% @frame.local_variables.each do |name, value| %>
|
6
|
-
<tr><td class="name"><%= name %></td><td><pre
|
52
|
+
<tr><td class="name"><%= name %></td><td><pre><%== inspect_value value %></pre></td></tr>
|
7
53
|
<% end %>
|
8
54
|
</table>
|
9
55
|
</div>
|
@@ -14,8 +60,10 @@
|
|
14
60
|
<div class="inset variables">
|
15
61
|
<table class="var_table">
|
16
62
|
<% @frame.instance_variables.each do |name, value| %>
|
17
|
-
<tr><td class="name"><%= name %></td><td><pre
|
63
|
+
<tr><td class="name"><%= name %></td><td><pre><%== inspect_value value %></pre></td></tr>
|
18
64
|
<% end %>
|
19
65
|
</table>
|
20
66
|
</div>
|
21
67
|
</div>
|
68
|
+
|
69
|
+
<!-- <%= Time.now.to_f - @var_start_time %> seconds -->
|
@@ -2,9 +2,9 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
module BetterErrors
|
4
4
|
describe ErrorPage do
|
5
|
-
let(:exception) { raise ZeroDivisionError, "you divided by zero you silly goose!" rescue $! }
|
5
|
+
let!(:exception) { raise ZeroDivisionError, "you divided by zero you silly goose!" rescue $! }
|
6
6
|
|
7
|
-
let(:error_page) { ErrorPage.new exception, { "
|
7
|
+
let(:error_page) { ErrorPage.new exception, { "PATH_INFO" => "/some/path" } }
|
8
8
|
|
9
9
|
let(:response) { error_page.render }
|
10
10
|
|
@@ -2,10 +2,30 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
module BetterErrors
|
4
4
|
describe Middleware do
|
5
|
+
let(:app) { Middleware.new(->env { ":)" }) }
|
6
|
+
|
5
7
|
it "should pass non-error responses through" do
|
6
|
-
app = Middleware.new(->env { ":)" })
|
7
8
|
app.call({}).should == ":)"
|
8
9
|
end
|
10
|
+
|
11
|
+
it "should call the internal methods" do
|
12
|
+
app.should_receive :internal_call
|
13
|
+
app.call("PATH_INFO" => "/__better_errors/1/preform_awesomness")
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should show the error page" do
|
17
|
+
app.should_receive :show_error_page
|
18
|
+
app.call("PATH_INFO" => "/__better_errors/")
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when requesting the /__better_errors manually" do
|
22
|
+
let(:app) { Middleware.new(->env { ":)" }) }
|
23
|
+
|
24
|
+
it "should show that no errors have been recorded" do
|
25
|
+
status, headers, body = app.call("PATH_INFO" => "/__better_errors")
|
26
|
+
body.join.should match /No errors have been recorded yet./
|
27
|
+
end
|
28
|
+
end
|
9
29
|
|
10
30
|
context "when handling an error" do
|
11
31
|
let(:app) { Middleware.new(->env { raise "oh no :(" }) }
|
@@ -56,6 +56,29 @@ module BetterErrors
|
|
56
56
|
|
57
57
|
frame.gem_path.should == "(gem) whatever-1.2.3/lib/whatever.rb"
|
58
58
|
end
|
59
|
+
|
60
|
+
it "should prioritize gem path over application path" do
|
61
|
+
BetterErrors.stub!(:application_root).and_return("/abc/xyz")
|
62
|
+
Gem.stub!(:path).and_return(["/abc/xyz/vendor"])
|
63
|
+
frame = StackFrame.new("/abc/xyz/vendor/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
|
64
|
+
|
65
|
+
frame.gem_path.should == "(gem) whatever-1.2.3/lib/whatever.rb"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "#pretty_path" do
|
70
|
+
it "should return #application_path for application paths" do
|
71
|
+
BetterErrors.stub!(:application_root).and_return("/abc/xyz")
|
72
|
+
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
|
73
|
+
frame.pretty_path.should == frame.application_path
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should return #gem_path for gem paths" do
|
77
|
+
Gem.stub!(:path).and_return(["/abc/xyz"])
|
78
|
+
frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
|
79
|
+
|
80
|
+
frame.pretty_path.should == frame.gem_path
|
81
|
+
end
|
59
82
|
end
|
60
83
|
|
61
84
|
it "should special case SyntaxErrors" do
|
@@ -80,5 +103,19 @@ module BetterErrors
|
|
80
103
|
frames.first.filename.should == "foo.rb"
|
81
104
|
frames.first.line.should == 123
|
82
105
|
end
|
106
|
+
|
107
|
+
it "should ignore a backtrace line if its format doesn't make any sense at all" do
|
108
|
+
error = StandardError.new
|
109
|
+
error.stub!(:backtrace).and_return(["foo.rb:123:in `foo'", "C:in `find'", "bar.rb:123:in `bar'"])
|
110
|
+
frames = StackFrame.from_exception(error)
|
111
|
+
frames.count.should == 2
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should not blow up if a filename contains a colon" do
|
115
|
+
error = StandardError.new
|
116
|
+
error.stub!(:backtrace).and_return(["crap:filename.rb:123"])
|
117
|
+
frames = StackFrame.from_exception(error)
|
118
|
+
frames.first.filename.should == "crap:filename.rb"
|
119
|
+
end
|
83
120
|
end
|
84
121
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe BetterErrors do
|
4
|
+
context ".editor" do
|
5
|
+
it "should default to textmate" do
|
6
|
+
subject.editor["foo.rb", 123].should == "txmt://open?url=file://foo.rb&line=123"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should url escape the filename" do
|
10
|
+
subject.editor["&.rb", 0].should == "txmt://open?url=file://%26.rb&line=0"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/spec/spec_helper.rb
CHANGED
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.3.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: 2012-12-
|
12
|
+
date: 2012-12-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -27,6 +27,70 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.12.0
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.12.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: binding_of_caller
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: simplecov
|
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'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: yard
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
30
94
|
- !ruby/object:Gem::Dependency
|
31
95
|
name: erubis
|
32
96
|
requirement: !ruby/object:Gem::Requirement
|
@@ -69,6 +133,8 @@ extensions: []
|
|
69
133
|
extra_rdoc_files: []
|
70
134
|
files:
|
71
135
|
- .gitignore
|
136
|
+
- .yardopts
|
137
|
+
- CONTRIBUTING.md
|
72
138
|
- Gemfile
|
73
139
|
- LICENSE.txt
|
74
140
|
- README.md
|
@@ -77,6 +143,7 @@ files:
|
|
77
143
|
- lib/better_errors.rb
|
78
144
|
- lib/better_errors/code_formatter.rb
|
79
145
|
- lib/better_errors/core_ext/exception.rb
|
146
|
+
- lib/better_errors/disable_logging_middleware.rb
|
80
147
|
- lib/better_errors/error_page.rb
|
81
148
|
- lib/better_errors/middleware.rb
|
82
149
|
- lib/better_errors/rails.rb
|
@@ -93,6 +160,7 @@ files:
|
|
93
160
|
- spec/better_errors/repl/basic_spec.rb
|
94
161
|
- spec/better_errors/stack_frame_spec.rb
|
95
162
|
- spec/better_errors/support/my_source.rb
|
163
|
+
- spec/better_errors_spec.rb
|
96
164
|
- spec/spec_helper.rb
|
97
165
|
homepage: https://github.com/charliesome/better_errors
|
98
166
|
licenses:
|
@@ -126,5 +194,6 @@ test_files:
|
|
126
194
|
- spec/better_errors/repl/basic_spec.rb
|
127
195
|
- spec/better_errors/stack_frame_spec.rb
|
128
196
|
- spec/better_errors/support/my_source.rb
|
197
|
+
- spec/better_errors_spec.rb
|
129
198
|
- spec/spec_helper.rb
|
130
199
|
has_rdoc:
|