better_errors 0.0.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +9 -0
- data/README.md +32 -8
- data/better_errors.gemspec +11 -5
- data/lib/better_errors/code_formatter.rb +61 -0
- data/lib/better_errors/core_ext/exception.rb +14 -13
- data/lib/better_errors/disable_logging_middleware.rb +16 -0
- data/lib/better_errors/error_page.rb +72 -48
- data/lib/better_errors/middleware.rb +77 -2
- data/lib/better_errors/rails.rb +7 -4
- data/lib/better_errors/repl/basic.rb +20 -0
- data/lib/better_errors/repl/pry.rb +77 -0
- data/lib/better_errors/repl.rb +30 -0
- data/lib/better_errors/stack_frame.rb +121 -0
- data/lib/better_errors/templates/main.erb +923 -0
- data/lib/better_errors/templates/variable_info.erb +69 -0
- data/lib/better_errors/version.rb +1 -1
- data/lib/better_errors.rb +99 -11
- data/spec/better_errors/code_formatter_spec.rb +51 -0
- data/spec/better_errors/error_page_spec.rb +33 -18
- data/spec/better_errors/middleware_spec.rb +29 -1
- data/spec/better_errors/repl/basic_spec.rb +32 -0
- data/spec/better_errors/stack_frame_spec.rb +121 -0
- data/spec/better_errors_spec.rb +13 -0
- data/spec/spec_helper.rb +3 -0
- metadata +92 -12
- data/lib/better_errors/error_frame.rb +0 -86
- data/lib/better_errors/error_page.erb +0 -234
- data/spec/better_errors/error_frame_spec.rb +0 -61
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
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
# Better Errors
|
|
2
2
|
|
|
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.
|
|
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 in any Rack app as Rack middleware.
|
|
4
4
|
|
|
5
|
-

|
|
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
|
+
* Live REPL on every stack frame
|
|
12
13
|
|
|
13
14
|
## Installation
|
|
14
15
|
|
|
15
|
-
Add this
|
|
16
|
+
Add this to your Gemfile:
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
```ruby
|
|
19
|
+
group :development do
|
|
20
|
+
gem "better_errors"
|
|
21
|
+
end
|
|
22
|
+
```
|
|
18
23
|
|
|
19
|
-
|
|
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.**
|
|
20
25
|
|
|
21
|
-
|
|
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:
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
```ruby
|
|
29
|
+
gem "binding_of_caller"
|
|
30
|
+
```
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
This is an optional dependency however, and Better Errors will work without it.
|
|
26
33
|
|
|
27
34
|
## Usage
|
|
28
35
|
|
|
@@ -44,6 +51,23 @@ get "/" do
|
|
|
44
51
|
end
|
|
45
52
|
```
|
|
46
53
|
|
|
54
|
+
## Compatibility
|
|
55
|
+
|
|
56
|
+
* **Supported**
|
|
57
|
+
* MRI 1.9.2, 1.9.3
|
|
58
|
+
* JRuby (1.9 mode) - *advanced features unsupported*
|
|
59
|
+
* Rubinius (1.9 mode) - *advanced features unsupported*
|
|
60
|
+
* **Coming soon**
|
|
61
|
+
* MRI 2.0.0 - the official API for grabbing caller bindings is slated for MRI 2.0.0, but it has not been implemented yet
|
|
62
|
+
|
|
63
|
+
## Known issues
|
|
64
|
+
|
|
65
|
+
* Calling `yield` from the REPL segfaults MRI 1.9.x.
|
|
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
|
+
|
|
47
71
|
## Contributing
|
|
48
72
|
|
|
49
73
|
1. Fork it
|
data/better_errors.gemspec
CHANGED
|
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
|
|
|
8
8
|
s.version = BetterErrors::VERSION
|
|
9
9
|
s.authors = ["Charlie Somerville"]
|
|
10
10
|
s.email = ["charlie@charliesomerville.com"]
|
|
11
|
-
s.description = %q{
|
|
12
|
-
s.summary = %q{Better
|
|
11
|
+
s.description = %q{Provides a better error page for Rails and other Rack apps. Includes source code inspection, a live REPL and local/instance variable inspection for all stack frames.}
|
|
12
|
+
s.summary = %q{Better error page for Rails and other Rack apps}
|
|
13
13
|
s.homepage = "https://github.com/charliesome/better_errors"
|
|
14
14
|
s.license = "MIT"
|
|
15
15
|
|
|
@@ -18,9 +18,15 @@ 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
|
-
s.add_dependency "erubis"
|
|
23
|
-
s.add_dependency "coderay"
|
|
24
|
-
|
|
26
|
+
s.add_dependency "erubis", ">= 2.7.0"
|
|
27
|
+
s.add_dependency "coderay", ">= 1.0.0"
|
|
28
|
+
|
|
29
|
+
# optional dependencies:
|
|
25
30
|
# s.add_dependency "binding_of_caller"
|
|
31
|
+
# s.add_dependency "pry"
|
|
26
32
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module BetterErrors
|
|
2
|
+
# @private
|
|
3
|
+
class CodeFormatter
|
|
4
|
+
FILE_TYPES = {
|
|
5
|
+
".rb" => :ruby,
|
|
6
|
+
"" => :ruby,
|
|
7
|
+
".html" => :html,
|
|
8
|
+
".erb" => :erb,
|
|
9
|
+
".haml" => :haml
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
attr_reader :filename, :line, :context
|
|
13
|
+
|
|
14
|
+
def initialize(filename, line, context = 5)
|
|
15
|
+
@filename = filename
|
|
16
|
+
@line = line
|
|
17
|
+
@context = context
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def html
|
|
21
|
+
%{<div class="code">#{formatted_lines.join}</div>}
|
|
22
|
+
rescue Errno::ENOENT, Errno::EINVAL
|
|
23
|
+
source_unavailable
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def source_unavailable
|
|
27
|
+
"<p class='unavailable'>Source unavailable</p>"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def coderay_scanner
|
|
31
|
+
ext = File.extname(filename)
|
|
32
|
+
FILE_TYPES[ext] || :text
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def formatted_lines
|
|
36
|
+
line_range.zip(highlighted_lines).map do |current_line, str|
|
|
37
|
+
class_name = current_line == line ? "highlight" : ""
|
|
38
|
+
sprintf '<pre class="%s">%5d %s</pre>', class_name, current_line, str
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def highlighted_lines
|
|
43
|
+
CodeRay.scan(context_lines.join, coderay_scanner).div(wrap: nil).lines
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def context_lines
|
|
47
|
+
range = line_range
|
|
48
|
+
source_lines[(range.begin - 1)..(range.end - 1)] or raise Errno::EINVAL
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def source_lines
|
|
52
|
+
@source_lines ||= File.readlines(filename)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def line_range
|
|
56
|
+
min = [line - context, 1].max
|
|
57
|
+
max = [line + context, source_lines.count].min
|
|
58
|
+
min..max
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
class Exception
|
|
2
|
-
attr_reader :__better_errors_bindings_stack
|
|
3
|
-
|
|
4
2
|
original_initialize = instance_method(:initialize)
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Thread.current[:__better_errors_exception_lock]
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
if BetterErrors.binding_of_caller_available?
|
|
5
|
+
define_method :initialize do |*args|
|
|
6
|
+
unless Thread.current[:__better_errors_exception_lock]
|
|
7
|
+
Thread.current[:__better_errors_exception_lock] = true
|
|
8
|
+
begin
|
|
9
|
+
@__better_errors_bindings_stack = binding.callers.drop(1)
|
|
10
|
+
ensure
|
|
11
|
+
Thread.current[:__better_errors_exception_lock] = false
|
|
13
12
|
end
|
|
14
|
-
ensure
|
|
15
|
-
Thread.current[:__better_errors_exception_lock] = false
|
|
16
13
|
end
|
|
14
|
+
original_initialize.bind(self).call(*args)
|
|
17
15
|
end
|
|
18
|
-
original_initialize.bind(self).call(*args)
|
|
19
16
|
end
|
|
20
|
-
|
|
17
|
+
|
|
18
|
+
def __better_errors_bindings_stack
|
|
19
|
+
@__better_errors_bindings_stack || []
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -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,27 +1,82 @@
|
|
|
1
|
+
require "cgi"
|
|
1
2
|
require "json"
|
|
2
3
|
|
|
3
4
|
module BetterErrors
|
|
5
|
+
# @private
|
|
4
6
|
class ErrorPage
|
|
5
|
-
def self.template_path
|
|
6
|
-
|
|
7
|
+
def self.template_path(template_name)
|
|
8
|
+
File.expand_path("../templates/#{template_name}.erb", __FILE__)
|
|
7
9
|
end
|
|
8
10
|
|
|
9
|
-
def self.template
|
|
10
|
-
Erubis::EscapedEruby.new(File.read(template_path))
|
|
11
|
+
def self.template(template_name)
|
|
12
|
+
Erubis::EscapedEruby.new(File.read(template_path(template_name)))
|
|
11
13
|
end
|
|
12
14
|
|
|
13
|
-
attr_reader :exception, :env
|
|
15
|
+
attr_reader :exception, :env, :repls
|
|
14
16
|
|
|
15
17
|
def initialize(exception, env)
|
|
16
18
|
@exception = real_exception(exception)
|
|
17
19
|
@env = env
|
|
20
|
+
@start_time = Time.now.to_f
|
|
21
|
+
@repls = []
|
|
18
22
|
end
|
|
19
23
|
|
|
20
|
-
def render
|
|
21
|
-
self.class.template.result binding
|
|
24
|
+
def render(template_name = "main")
|
|
25
|
+
self.class.template(template_name).result binding
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def do_variables(opts)
|
|
29
|
+
index = opts["index"].to_i
|
|
30
|
+
@frame = backtrace_frames[index]
|
|
31
|
+
@var_start_time = Time.now.to_f
|
|
32
|
+
{ html: render("variable_info") }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def do_eval(opts)
|
|
36
|
+
index = opts["index"].to_i
|
|
37
|
+
code = opts["source"]
|
|
38
|
+
|
|
39
|
+
unless binding = backtrace_frames[index].frame_binding
|
|
40
|
+
return { error: "REPL unavailable in this stack frame" }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
result, prompt =
|
|
44
|
+
(@repls[index] ||= REPL.provider.new(binding)).send_input(code)
|
|
45
|
+
|
|
46
|
+
{ result: result,
|
|
47
|
+
prompt: prompt,
|
|
48
|
+
highlighted_input: CodeRay.scan(code, :ruby).div(wrap: nil) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def backtrace_frames
|
|
52
|
+
@backtrace_frames ||= StackFrame.from_exception(exception)
|
|
22
53
|
end
|
|
23
54
|
|
|
24
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
|
+
|
|
72
|
+
def exception_message
|
|
73
|
+
if exception.is_a?(SyntaxError) && exception.message =~ /\A.*:\d*: (.*)$/
|
|
74
|
+
$1
|
|
75
|
+
else
|
|
76
|
+
exception.message
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
25
80
|
def real_exception(exception)
|
|
26
81
|
if exception.respond_to? :original_exception
|
|
27
82
|
exception.original_exception
|
|
@@ -31,50 +86,19 @@ module BetterErrors
|
|
|
31
86
|
end
|
|
32
87
|
|
|
33
88
|
def request_path
|
|
34
|
-
env["
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def backtrace_frames
|
|
38
|
-
@backtrace_frames ||= ErrorFrame.from_exception(exception)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def coderay_scanner_for_ext(ext)
|
|
42
|
-
case ext
|
|
43
|
-
when "rb"; :ruby
|
|
44
|
-
when "html"; :html
|
|
45
|
-
when "erb"; :erb
|
|
46
|
-
when "haml"; :haml
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def file_extension(filename)
|
|
51
|
-
filename.split(".").last
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def code_extract(frame, lines_of_context = 5)
|
|
55
|
-
lines = File.readlines(frame.filename)
|
|
56
|
-
min_line = [1, frame.line - lines_of_context].max - 1
|
|
57
|
-
max_line = [frame.line + lines_of_context, lines.count + 1].min - 1
|
|
58
|
-
[min_line, max_line, lines[min_line..max_line].join]
|
|
89
|
+
env["PATH_INFO"]
|
|
59
90
|
end
|
|
60
91
|
|
|
61
92
|
def highlighted_code_block(frame)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
else
|
|
72
|
-
html << "<pre>"
|
|
73
|
-
end
|
|
74
|
-
html << sprintf("%5d", min_line + index + 1) << " " << str << "</pre>"
|
|
75
|
-
end
|
|
76
|
-
html << "</div>"
|
|
77
|
-
end
|
|
93
|
+
CodeFormatter.new(frame.filename, frame.line).html
|
|
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>"
|
|
78
102
|
end
|
|
79
103
|
end
|
|
80
104
|
end
|
|
@@ -1,15 +1,90 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
1
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
|
+
#
|
|
2
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.
|
|
3
29
|
def initialize(app, handler = ErrorPage)
|
|
4
30
|
@app = app
|
|
5
31
|
@handler = handler
|
|
6
32
|
end
|
|
7
33
|
|
|
34
|
+
# Calls the Better Errors middleware
|
|
35
|
+
#
|
|
36
|
+
# @param [Hash] env
|
|
37
|
+
# @return [Array]
|
|
8
38
|
def call(env)
|
|
39
|
+
case env["PATH_INFO"]
|
|
40
|
+
when %r{\A/__better_errors/(?<oid>-?\d+)/(?<method>\w+)\z}
|
|
41
|
+
internal_call env, $~
|
|
42
|
+
when %r{\A/__better_errors/?\z}
|
|
43
|
+
show_error_page env
|
|
44
|
+
else
|
|
45
|
+
app_call env
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
def app_call(env)
|
|
9
51
|
@app.call env
|
|
10
52
|
rescue Exception => ex
|
|
11
|
-
error_page = @handler.new ex, env
|
|
12
|
-
|
|
53
|
+
@error_page = @handler.new ex, env
|
|
54
|
+
log_exception
|
|
55
|
+
show_error_page(env)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def show_error_page(env)
|
|
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]]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def log_exception
|
|
71
|
+
return unless BetterErrors.logger
|
|
72
|
+
|
|
73
|
+
message = "\n#{@error_page.exception.class} - #{@error_page.exception.message}:\n"
|
|
74
|
+
@error_page.backtrace_frames.each do |frame|
|
|
75
|
+
message << " #{frame}\n"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
BetterErrors.logger.fatal message
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def internal_call(env, opts)
|
|
82
|
+
if opts[:oid].to_i != @error_page.object_id
|
|
83
|
+
return [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(error: "Session expired")]]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
response = @error_page.send("do_#{opts[:method]}", JSON.parse(env["rack.input"].read))
|
|
87
|
+
[200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(response)]]
|
|
13
88
|
end
|
|
14
89
|
end
|
|
15
90
|
end
|
data/lib/better_errors/rails.rb
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
module BetterErrors
|
|
2
|
+
# @private
|
|
2
3
|
class Railtie < Rails::Railtie
|
|
3
4
|
initializer "better_errors.configure_rails_initialization" do
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
unless Rails.env.production?
|
|
6
|
+
Rails.application.middleware.use BetterErrors::Middleware
|
|
7
|
+
Rails.application.middleware.insert_before Rails::Rack::Logger, BetterErrors::DisableLoggingMiddleware
|
|
8
|
+
BetterErrors.logger = Rails.logger
|
|
9
|
+
BetterErrors.application_root = Rails.root.to_s
|
|
10
|
+
end
|
|
8
11
|
end
|
|
9
12
|
end
|
|
10
13
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module BetterErrors
|
|
2
|
+
module REPL
|
|
3
|
+
class Basic
|
|
4
|
+
def initialize(binding)
|
|
5
|
+
@binding = binding
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def send_input(str)
|
|
9
|
+
[execute(str), ">>"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
def execute(str)
|
|
14
|
+
"=> #{@binding.eval(str).inspect}\n"
|
|
15
|
+
rescue Exception => e
|
|
16
|
+
"!! #{e.inspect rescue e.class.to_s rescue "Exception"}\n"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require "fiber"
|
|
2
|
+
require "pry"
|
|
3
|
+
|
|
4
|
+
module BetterErrors
|
|
5
|
+
module REPL
|
|
6
|
+
class Pry
|
|
7
|
+
class Input
|
|
8
|
+
def readline
|
|
9
|
+
Fiber.yield
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Output
|
|
14
|
+
def initialize
|
|
15
|
+
@buffer = ""
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def puts(*args)
|
|
19
|
+
args.each do |arg|
|
|
20
|
+
@buffer << "#{arg.chomp}\n"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def tty?
|
|
25
|
+
false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def read_buffer
|
|
29
|
+
@buffer
|
|
30
|
+
ensure
|
|
31
|
+
@buffer = ""
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def initialize(binding)
|
|
36
|
+
@fiber = Fiber.new do
|
|
37
|
+
@pry.repl binding
|
|
38
|
+
end
|
|
39
|
+
@input = Input.new
|
|
40
|
+
@output = Output.new
|
|
41
|
+
@pry = ::Pry.new input: @input, output: @output
|
|
42
|
+
@pry.hooks.clear_all
|
|
43
|
+
@continued_expression = false
|
|
44
|
+
@pry.hooks.add_hook :after_read, "better_errors hacky hook" do
|
|
45
|
+
@continued_expression = false
|
|
46
|
+
end
|
|
47
|
+
@fiber.resume
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def pry_indent
|
|
51
|
+
@pry.instance_variable_get(:@indent)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def send_input(str)
|
|
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
|
|
70
|
+
ensure
|
|
71
|
+
old_attrs.each do |k, v|
|
|
72
|
+
obj.send "#{k}=", v
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module BetterErrors
|
|
2
|
+
# @private
|
|
3
|
+
module REPL
|
|
4
|
+
PROVIDERS = [
|
|
5
|
+
{ impl: "better_errors/repl/basic",
|
|
6
|
+
const: :Basic },
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
def self.provider
|
|
10
|
+
@provider ||= const_get detect[:const]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.provider=(prov)
|
|
14
|
+
@provider = prov
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.detect
|
|
18
|
+
PROVIDERS.find do |prov|
|
|
19
|
+
test_provider prov
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.test_provider(provider)
|
|
24
|
+
require provider[:impl]
|
|
25
|
+
true
|
|
26
|
+
rescue LoadError
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|