better_errors 2.4.0 → 2.9.1
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.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +130 -0
- data/.github/workflows/release.yml +64 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +1 -1
- data/Gemfile +6 -0
- data/README.md +40 -8
- data/better_errors.gemspec +13 -2
- data/gemfiles/pry010.gemfile +2 -1
- data/gemfiles/pry011.gemfile +2 -1
- data/gemfiles/pry09.gemfile +2 -1
- data/gemfiles/rack.gemfile +2 -1
- data/gemfiles/rack_boc.gemfile +2 -1
- data/gemfiles/rails42.gemfile +3 -1
- data/gemfiles/rails42_boc.gemfile +3 -1
- data/gemfiles/rails42_haml.gemfile +3 -1
- data/gemfiles/rails50.gemfile +3 -1
- data/gemfiles/rails50_boc.gemfile +3 -1
- data/gemfiles/rails50_haml.gemfile +3 -1
- data/gemfiles/rails51.gemfile +3 -1
- data/gemfiles/rails51_boc.gemfile +3 -1
- data/gemfiles/rails51_haml.gemfile +3 -1
- data/gemfiles/rails52.gemfile +9 -0
- data/gemfiles/rails52_boc.gemfile +10 -0
- data/gemfiles/rails52_haml.gemfile +10 -0
- data/gemfiles/rails60.gemfile +8 -0
- data/gemfiles/rails60_boc.gemfile +9 -0
- data/gemfiles/rails60_haml.gemfile +9 -0
- data/lib/better_errors.rb +23 -32
- data/lib/better_errors/editor.rb +99 -0
- data/lib/better_errors/error_page.rb +37 -24
- data/lib/better_errors/exception_hint.rb +29 -0
- data/lib/better_errors/inspectable_value.rb +45 -0
- data/lib/better_errors/middleware.rb +59 -12
- data/lib/better_errors/raised_exception.rb +25 -4
- data/lib/better_errors/stack_frame.rb +25 -7
- data/lib/better_errors/templates/main.erb +78 -24
- data/lib/better_errors/templates/text.erb +5 -2
- data/lib/better_errors/templates/variable_info.erb +9 -2
- data/lib/better_errors/version.rb +1 -1
- metadata +37 -10
- data/.travis.yml +0 -68
data/gemfiles/rails50.gemfile
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
source "https://rubygems.org"
|
|
2
2
|
|
|
3
3
|
gem "rails", "~> 5.0.0"
|
|
4
|
+
gem 'i18n', '< 1.5.2' if RUBY_VERSION < '2.3'
|
|
4
5
|
gem "binding_of_caller"
|
|
5
6
|
|
|
6
|
-
gem '
|
|
7
|
+
gem 'simplecov', require: false
|
|
8
|
+
gem 'simplecov-lcov', require: false
|
|
7
9
|
|
|
8
10
|
gemspec path: "../"
|
data/gemfiles/rails51.gemfile
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
source "https://rubygems.org"
|
|
2
2
|
|
|
3
3
|
gem "rails", "~> 5.1.0"
|
|
4
|
+
gem 'i18n', '< 1.5.2' if RUBY_VERSION < '2.3'
|
|
4
5
|
gem "binding_of_caller"
|
|
5
6
|
|
|
6
|
-
gem '
|
|
7
|
+
gem 'simplecov', require: false
|
|
8
|
+
gem 'simplecov-lcov', require: false
|
|
7
9
|
|
|
8
10
|
gemspec path: "../"
|
data/lib/better_errors.rb
CHANGED
|
@@ -3,24 +3,17 @@ require "erubi"
|
|
|
3
3
|
require "coderay"
|
|
4
4
|
require "uri"
|
|
5
5
|
|
|
6
|
+
require "better_errors/version"
|
|
6
7
|
require "better_errors/code_formatter"
|
|
8
|
+
require "better_errors/inspectable_value"
|
|
7
9
|
require "better_errors/error_page"
|
|
8
10
|
require "better_errors/middleware"
|
|
9
11
|
require "better_errors/raised_exception"
|
|
10
12
|
require "better_errors/repl"
|
|
11
13
|
require "better_errors/stack_frame"
|
|
12
|
-
require "better_errors/
|
|
14
|
+
require "better_errors/editor"
|
|
13
15
|
|
|
14
16
|
module BetterErrors
|
|
15
|
-
POSSIBLE_EDITOR_PRESETS = [
|
|
16
|
-
{ symbols: [:emacs, :emacsclient], sniff: /emacs/i, url: "emacs://open?url=file://%{file}&line=%{line}" },
|
|
17
|
-
{ symbols: [:macvim, :mvim], sniff: /vim/i, url: proc { |file, line| "mvim://open?url=file://#{file}&line=#{line}" } },
|
|
18
|
-
{ symbols: [:sublime, :subl, :st], sniff: /subl/i, url: "subl://open?url=file://%{file}&line=%{line}" },
|
|
19
|
-
{ symbols: [:textmate, :txmt, :tm], sniff: /mate/i, url: "txmt://open?url=file://%{file}&line=%{line}" },
|
|
20
|
-
{ symbols: [:idea], sniff: /idea/i, url: "idea://open?file=%{file}&line=%{line}" },
|
|
21
|
-
{ symbols: [:rubymine], sniff: /mine/i, url: "x-mine://open?file=%{file}&line=%{line}" },
|
|
22
|
-
]
|
|
23
|
-
|
|
24
17
|
class << self
|
|
25
18
|
# The path to the root of the application. Better Errors uses this property
|
|
26
19
|
# to determine if a file in a backtrace should be considered an application
|
|
@@ -51,21 +44,27 @@ module BetterErrors
|
|
|
51
44
|
# the variable won't be returned.
|
|
52
45
|
# @return int
|
|
53
46
|
attr_accessor :maximum_variable_inspect_size
|
|
47
|
+
|
|
48
|
+
# List of classes that are excluded from inspection.
|
|
49
|
+
# @return [Array]
|
|
50
|
+
attr_accessor :ignored_classes
|
|
54
51
|
end
|
|
55
52
|
@ignored_instance_variables = []
|
|
56
53
|
@maximum_variable_inspect_size = 100_000
|
|
54
|
+
@ignored_classes = ['ActionDispatch::Request', 'ActionDispatch::Response']
|
|
57
55
|
|
|
58
|
-
# Returns
|
|
56
|
+
# Returns an object which responds to #url, which when called with
|
|
57
|
+
# a filename and line number argument,
|
|
59
58
|
# returns a URL to open the filename and line in the selected editor.
|
|
60
59
|
#
|
|
61
60
|
# Generates TextMate URLs by default.
|
|
62
61
|
#
|
|
63
|
-
# BetterErrors.editor
|
|
62
|
+
# BetterErrors.editor.url("/some/file", 123)
|
|
64
63
|
# # => txmt://open?url=file:///some/file&line=123
|
|
65
64
|
#
|
|
66
65
|
# @return [Proc]
|
|
67
66
|
def self.editor
|
|
68
|
-
@editor
|
|
67
|
+
@editor ||= default_editor
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
# Configures how Better Errors generates open-in-editor URLs.
|
|
@@ -76,6 +75,7 @@ module BetterErrors
|
|
|
76
75
|
# * `:textmate`, `:txmt`, `:tm`
|
|
77
76
|
# * `:sublime`, `:subl`, `:st`
|
|
78
77
|
# * `:macvim`
|
|
78
|
+
# * `:atom`
|
|
79
79
|
#
|
|
80
80
|
# @param [Symbol] sym
|
|
81
81
|
#
|
|
@@ -105,27 +105,22 @@ module BetterErrors
|
|
|
105
105
|
# @param [Proc] proc
|
|
106
106
|
#
|
|
107
107
|
def self.editor=(editor)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
self.editor = proc { |file, line| editor % { file: URI.encode_www_form_component(file), line: line } }
|
|
108
|
+
if editor.is_a? Symbol
|
|
109
|
+
@editor = Editor.editor_from_symbol(editor)
|
|
110
|
+
raise(ArgumentError, "Symbol #{editor} is not a symbol in the list of supported errors.") unless editor
|
|
111
|
+
elsif editor.is_a? String
|
|
112
|
+
@editor = Editor.for_formatting_string(editor)
|
|
113
|
+
elsif editor.respond_to? :call
|
|
114
|
+
@editor = Editor.for_proc(editor)
|
|
116
115
|
else
|
|
117
|
-
|
|
118
|
-
@editor = editor
|
|
119
|
-
else
|
|
120
|
-
raise TypeError, "Expected editor to be a valid editor key, a format string or a callable."
|
|
121
|
-
end
|
|
116
|
+
raise ArgumentError, "Expected editor to be a valid editor key, a format string or a callable."
|
|
122
117
|
end
|
|
123
118
|
end
|
|
124
119
|
|
|
125
120
|
# Enables experimental Pry support in the inline REPL
|
|
126
121
|
#
|
|
127
122
|
# If you encounter problems while using Pry, *please* file a bug report at
|
|
128
|
-
# https://github.com/
|
|
123
|
+
# https://github.com/BetterErrors/better_errors/issues
|
|
129
124
|
def self.use_pry!
|
|
130
125
|
REPL::PROVIDERS.unshift const: :Pry, impl: "better_errors/repl/pry"
|
|
131
126
|
end
|
|
@@ -135,12 +130,8 @@ module BetterErrors
|
|
|
135
130
|
#
|
|
136
131
|
# @return [Symbol]
|
|
137
132
|
def self.default_editor
|
|
138
|
-
|
|
139
|
-
ENV["EDITOR"] =~ config[:sniff]
|
|
140
|
-
}[:url] || :textmate
|
|
133
|
+
Editor.default_editor
|
|
141
134
|
end
|
|
142
|
-
|
|
143
|
-
BetterErrors.editor = default_editor
|
|
144
135
|
end
|
|
145
136
|
|
|
146
137
|
begin
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
|
|
3
|
+
module BetterErrors
|
|
4
|
+
class Editor
|
|
5
|
+
KNOWN_EDITORS = [
|
|
6
|
+
{ symbols: [:atom], sniff: /atom/i, url: "atom://core/open/file?filename=%{file}&line=%{line}" },
|
|
7
|
+
{ symbols: [:emacs, :emacsclient], sniff: /emacs/i, url: "emacs://open?url=file://%{file}&line=%{line}" },
|
|
8
|
+
{ symbols: [:idea], sniff: /idea/i, url: "idea://open?file=%{file}&line=%{line}" },
|
|
9
|
+
{ symbols: [:macvim, :mvim], sniff: /vim/i, url: "mvim://open?url=file://%{file_unencoded}&line=%{line}" },
|
|
10
|
+
{ symbols: [:rubymine], sniff: /mine/i, url: "x-mine://open?file=%{file}&line=%{line}" },
|
|
11
|
+
{ symbols: [:sublime, :subl, :st], sniff: /subl/i, url: "subl://open?url=file://%{file}&line=%{line}" },
|
|
12
|
+
{ symbols: [:textmate, :txmt, :tm], sniff: /mate/i, url: "txmt://open?url=file://%{file}&line=%{line}" },
|
|
13
|
+
{ symbols: [:vscode, :code], sniff: /code/i, url: "vscode://file/%{file}:%{line}" },
|
|
14
|
+
{ symbols: [:vscodium, :codium], sniff: /codium/i, url: "vscodium://file/%{file}:%{line}" },
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def self.for_formatting_string(formatting_string)
|
|
18
|
+
new proc { |file, line|
|
|
19
|
+
formatting_string % { file: URI.encode_www_form_component(file), file_unencoded: file, line: line }
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.for_proc(url_proc)
|
|
24
|
+
new url_proc
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Automatically sniffs a default editor preset based on
|
|
28
|
+
# environment variables.
|
|
29
|
+
#
|
|
30
|
+
# @return [Symbol]
|
|
31
|
+
def self.default_editor
|
|
32
|
+
editor_from_environment_formatting_string ||
|
|
33
|
+
editor_from_environment_editor ||
|
|
34
|
+
editor_from_symbol(:textmate)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.editor_from_environment_editor
|
|
38
|
+
if ENV["BETTER_ERRORS_EDITOR"]
|
|
39
|
+
editor = editor_from_command(ENV["BETTER_ERRORS_EDITOR"])
|
|
40
|
+
return editor if editor
|
|
41
|
+
puts "BETTER_ERRORS_EDITOR environment variable is not recognized as a supported Better Errors editor."
|
|
42
|
+
end
|
|
43
|
+
if ENV["EDITOR"]
|
|
44
|
+
editor = editor_from_command(ENV["EDITOR"])
|
|
45
|
+
return editor if editor
|
|
46
|
+
puts "EDITOR environment variable is not recognized as a supported Better Errors editor. Using TextMate by default."
|
|
47
|
+
else
|
|
48
|
+
puts "Since there is no EDITOR or BETTER_ERRORS_EDITOR environment variable, using Textmate by default."
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.editor_from_command(editor_command)
|
|
53
|
+
env_preset = KNOWN_EDITORS.find { |preset| editor_command =~ preset[:sniff] }
|
|
54
|
+
for_formatting_string(env_preset[:url]) if env_preset
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.editor_from_environment_formatting_string
|
|
58
|
+
return unless ENV['BETTER_ERRORS_EDITOR_URL']
|
|
59
|
+
|
|
60
|
+
for_formatting_string(ENV['BETTER_ERRORS_EDITOR_URL'])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.editor_from_symbol(symbol)
|
|
64
|
+
KNOWN_EDITORS.each do |preset|
|
|
65
|
+
return for_formatting_string(preset[:url]) if preset[:symbols].include?(symbol)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def initialize(url_proc)
|
|
70
|
+
@url_proc = url_proc
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def url(raw_path, line)
|
|
74
|
+
if virtual_path && raw_path.start_with?(virtual_path)
|
|
75
|
+
if host_path
|
|
76
|
+
file = raw_path.sub(%r{\A#{virtual_path}}, host_path)
|
|
77
|
+
else
|
|
78
|
+
file = raw_path.sub(%r{\A#{virtual_path}/}, '')
|
|
79
|
+
end
|
|
80
|
+
else
|
|
81
|
+
file = raw_path
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
url_proc.call(file, line)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
attr_reader :url_proc
|
|
90
|
+
|
|
91
|
+
def virtual_path
|
|
92
|
+
@virtual_path ||= ENV['BETTER_ERRORS_VIRTUAL_PATH']
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def host_path
|
|
96
|
+
@host_path ||= ENV['BETTER_ERRORS_HOST_PATH']
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -26,8 +26,13 @@ module BetterErrors
|
|
|
26
26
|
@id ||= SecureRandom.hex(8)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def render(template_name = "main")
|
|
29
|
+
def render(template_name = "main", csrf_token = nil)
|
|
30
30
|
binding.eval(self.class.template(template_name).src)
|
|
31
|
+
rescue => e
|
|
32
|
+
# Fix the backtrace, which doesn't identify the template that failed (within Better Errors).
|
|
33
|
+
# We don't know the line number, so just injecting the template path has to be enough.
|
|
34
|
+
e.backtrace.unshift "#{self.class.template_path(template_name)}:0"
|
|
35
|
+
raise
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
def do_variables(opts)
|
|
@@ -59,7 +64,23 @@ module BetterErrors
|
|
|
59
64
|
end
|
|
60
65
|
|
|
61
66
|
def exception_message
|
|
62
|
-
exception.message.
|
|
67
|
+
exception.message.strip.gsub(/(\r?\n\s*\r?\n)+/, "\n")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def exception_hint
|
|
71
|
+
exception.hint
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def active_support_actions
|
|
75
|
+
return [] unless defined?(ActiveSupport::ActionableError)
|
|
76
|
+
|
|
77
|
+
ActiveSupport::ActionableError.actions(exception.type)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def action_dispatch_action_endpoint
|
|
81
|
+
return unless defined?(ActionDispatch::ActionableExceptions)
|
|
82
|
+
|
|
83
|
+
ActionDispatch::ActionableExceptions.endpoint
|
|
63
84
|
end
|
|
64
85
|
|
|
65
86
|
def application_frames
|
|
@@ -70,9 +91,10 @@ module BetterErrors
|
|
|
70
91
|
application_frames.first || backtrace_frames.first
|
|
71
92
|
end
|
|
72
93
|
|
|
73
|
-
|
|
94
|
+
private
|
|
95
|
+
|
|
74
96
|
def editor_url(frame)
|
|
75
|
-
BetterErrors.editor
|
|
97
|
+
BetterErrors.editor.url(frame.filename, frame.line)
|
|
76
98
|
end
|
|
77
99
|
|
|
78
100
|
def rack_session
|
|
@@ -104,28 +126,19 @@ module BetterErrors
|
|
|
104
126
|
end
|
|
105
127
|
|
|
106
128
|
def inspect_value(obj)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
"<span class='unsupported'>(exception #{CGI.escapeHTML(e.class.to_s)} was raised in inspect)</span>"
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def inspect_raw_value(obj)
|
|
115
|
-
value = CGI.escapeHTML(obj.inspect)
|
|
116
|
-
|
|
117
|
-
if value_small_enough_to_inspect?(value)
|
|
118
|
-
value
|
|
129
|
+
if BetterErrors.ignored_classes.include? obj.class.name
|
|
130
|
+
"<span class='unsupported'>(Instance of ignored class. "\
|
|
131
|
+
"#{obj.class.name ? "Remove #{CGI.escapeHTML(obj.class.name)} from" : "Modify"}"\
|
|
132
|
+
" BetterErrors.ignored_classes if you need to see it.)</span>"
|
|
119
133
|
else
|
|
120
|
-
|
|
121
|
-
"Modify #{CGI.escapeHTML(obj.class.to_s)}#inspect "\
|
|
122
|
-
"or increase BetterErrors.maximum_variable_inspect_size)</span>"
|
|
134
|
+
InspectableValue.new(obj).to_html
|
|
123
135
|
end
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
136
|
+
rescue BetterErrors::ValueLargerThanConfiguredMaximum
|
|
137
|
+
"<span class='unsupported'>(Object too large. "\
|
|
138
|
+
"#{obj.class.name ? "Modify #{CGI.escapeHTML(obj.class.name)}#inspect or a" : "A"}"\
|
|
139
|
+
"djust BetterErrors.maximum_variable_inspect_size if you need to see it.)</span>"
|
|
140
|
+
rescue Exception => e
|
|
141
|
+
"<span class='unsupported'>(exception #{CGI.escapeHTML(e.class.to_s)} was raised in inspect)</span>"
|
|
129
142
|
end
|
|
130
143
|
|
|
131
144
|
def eval_and_respond(index, code)
|