better_errors 2.5.1 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 949ae4930972ea12a576bf6036ab19ef6f9d876c03a640062f76a0871648181f
4
- data.tar.gz: 36dcea704937546ac60e4d0c9503222fbcd0cb0aa62e6b2b2e35d92b8cbbbc2c
3
+ metadata.gz: 59e37a4996e35b2775a359cbe11302a8ded7213d7e940905a92cfe39d872e2e9
4
+ data.tar.gz: f162b4cbb8d5fc3c285c466ecbfb449626337e724cd0c8964fcbc7fb3784288b
5
5
  SHA512:
6
- metadata.gz: 5bc9b0ea98c35285ad19a774394b26459c80cda362b3fd940c490c4342e8190550990615c04bad077b5f71adb6e609caf890027baf8832c8de287713dcb0ca90
7
- data.tar.gz: e0f31fa9e3dfa676e35a6838547280df6da65573a14d55da125d0d20dc62edaef8485cd30e2580f50bff26d82d6533167453ee3e941cf7697ac3b6e423994b40
6
+ metadata.gz: 36a80fb99501720b6dda49b61dac22deefa30b9bfcc46b7c00c7ec293dc0de4400dcdb4ac3b35d46d3d6722d8bf79a5e7ace2481c3c91ad1b0310900135aa802
7
+ data.tar.gz: 3d3c7f3eea41454551f3fa21dd8721da779eaa676f97520946fcb8294e20c3bdddb88fd613bfd43cdf0edaf434583c8c93ddc3ad099fddc8e8179598c70b9e92
@@ -1,16 +1,5 @@
1
- sudo: false
2
1
  language: ruby
3
2
  cache: bundler
4
- before_install:
5
- # Since Rails 4.2 only supports rubygems < 2, we must install an older version for it.
6
- - >
7
- if [[ "$BUNDLE_GEMFILE" =~ "rails42" ]]; then
8
- rvm @global do gem install rubygems-update -v '<2'
9
- update_rubygems
10
- rvm @global do gem uninstall bundler --force --executables
11
- rvm @global do gem install bundler -v "~> 1.3"
12
- fi
13
-
14
3
  notifications:
15
4
  webhooks:
16
5
  # With COVERALLS_PARALLEL, coverage information sent to coveralls will not be processed until
@@ -19,23 +8,29 @@ notifications:
19
8
  - secure: "YnHYbTq51ySistjvOxsuNhyg4GLuUffEJstTYeGYXiBF7HG5h43IVYo8KNuLzwkgsOYBcNo+YMdQX7qCqJffSbhsr1FZRSzBmjFFxcyD4hu+ukM2theZ4mePVAZiePscYvQPRNY4hIb4d3egStJEytkalDhB3sOebF57tIaCssg="
20
9
  rvm:
21
10
  - 2.2.10
22
- - 2.3.7
23
- - 2.4.4
24
- - 2.5.1
11
+ - 2.3.8
12
+ - 2.4.9
13
+ - 2.5.7
14
+ - 2.6.5
15
+ - 2.7.0
25
16
  - ruby-head
17
+ - truffleruby-head
26
18
  gemfile:
27
19
  - gemfiles/rails42.gemfile
28
20
  - gemfiles/rails50.gemfile
29
21
  - gemfiles/rails51.gemfile
30
22
  - gemfiles/rails52.gemfile
23
+ - gemfiles/rails60.gemfile
31
24
  - gemfiles/rails42_haml.gemfile
32
25
  - gemfiles/rails50_haml.gemfile
33
26
  - gemfiles/rails51_haml.gemfile
34
27
  - gemfiles/rails52_haml.gemfile
28
+ - gemfiles/rails60_haml.gemfile
35
29
  - gemfiles/rails42_boc.gemfile
36
30
  - gemfiles/rails50_boc.gemfile
37
31
  - gemfiles/rails51_boc.gemfile
38
32
  - gemfiles/rails52_boc.gemfile
33
+ - gemfiles/rails60_boc.gemfile
39
34
  - gemfiles/rack.gemfile
40
35
  - gemfiles/rack_boc.gemfile
41
36
  - gemfiles/pry09.gemfile
@@ -48,15 +43,69 @@ matrix:
48
43
  - gemfile: gemfiles/pry010.gemfile
49
44
  - gemfile: gemfiles/pry011.gemfile
50
45
  exclude:
51
- - rvm: 2.4.4
46
+ - rvm: 2.2.10
47
+ gemfile: gemfiles/rails60.gemfile
48
+ - rvm: 2.2.10
49
+ gemfile: gemfiles/rails60_boc.gemfile
50
+ - rvm: 2.2.10
51
+ gemfile: gemfiles/rails60_haml.gemfile
52
+ - rvm: 2.3.8
52
53
  gemfile: gemfiles/rails42.gemfile
53
- - rvm: 2.4.4
54
+ - rvm: 2.3.8
54
55
  gemfile: gemfiles/rails42_boc.gemfile
55
- - rvm: 2.4.4
56
+ - rvm: 2.3.8
56
57
  gemfile: gemfiles/rails42_haml.gemfile
57
- - rvm: 2.5.1
58
+ - rvm: 2.3.8
59
+ gemfile: gemfiles/rails60.gemfile
60
+ - rvm: 2.3.8
61
+ gemfile: gemfiles/rails60_boc.gemfile
62
+ - rvm: 2.3.8
63
+ gemfile: gemfiles/rails60_haml.gemfile
64
+ - rvm: 2.4.9
58
65
  gemfile: gemfiles/rails42.gemfile
59
- - rvm: 2.5.1
66
+ - rvm: 2.4.9
60
67
  gemfile: gemfiles/rails42_boc.gemfile
61
- - rvm: 2.5.1
68
+ - rvm: 2.4.9
62
69
  gemfile: gemfiles/rails42_haml.gemfile
70
+ - rvm: 2.4.9
71
+ gemfile: gemfiles/rails60.gemfile
72
+ - rvm: 2.4.9
73
+ gemfile: gemfiles/rails60_boc.gemfile
74
+ - rvm: 2.4.9
75
+ gemfile: gemfiles/rails60_haml.gemfile
76
+ - rvm: 2.5.7
77
+ gemfile: gemfiles/rails42.gemfile
78
+ - rvm: 2.5.7
79
+ gemfile: gemfiles/rails42_boc.gemfile
80
+ - rvm: 2.5.7
81
+ gemfile: gemfiles/rails42_haml.gemfile
82
+ - rvm: 2.6.5
83
+ gemfile: gemfiles/rails42.gemfile
84
+ - rvm: 2.6.5
85
+ gemfile: gemfiles/rails42_boc.gemfile
86
+ - rvm: 2.6.5
87
+ gemfile: gemfiles/rails42_haml.gemfile
88
+ - rvm: 2.7.0
89
+ gemfile: gemfiles/rails42.gemfile
90
+ - rvm: 2.7.0
91
+ gemfile: gemfiles/rails42_boc.gemfile
92
+ - rvm: 2.7.0
93
+ gemfile: gemfiles/rails42_haml.gemfile
94
+ - rvm: ruby-head
95
+ gemfile: gemfiles/rails42.gemfile
96
+ - rvm: ruby-head
97
+ gemfile: gemfiles/rails42_boc.gemfile
98
+ - rvm: ruby-head
99
+ gemfile: gemfiles/rails42_haml.gemfile
100
+ - rvm: truffleruby-head
101
+ gemfile: gemfiles/rails42_boc.gemfile
102
+ - rvm: truffleruby-head
103
+ gemfile: gemfiles/rails50_boc.gemfile
104
+ - rvm: truffleruby-head
105
+ gemfile: gemfiles/rails51_boc.gemfile
106
+ - rvm: truffleruby-head
107
+ gemfile: gemfiles/rails52_boc.gemfile
108
+ - rvm: truffleruby-head
109
+ gemfile: gemfiles/rails60_boc.gemfile
110
+ - rvm: truffleruby-head
111
+ gemfile: gemfiles/rack_boc.gemfile
data/README.md CHANGED
@@ -35,6 +35,28 @@ end
35
35
 
36
36
  _Note: If you discover that Better Errors isn't working - particularly after upgrading from version 0.5.0 or less - be sure to set `config.consider_all_requests_local = true` in `config/environments/development.rb`._
37
37
 
38
+ ### Optional: Set `EDITOR`
39
+
40
+ For many reasons outside of Better Errors, you should have the `EDITOR` environment variable set to your preferred
41
+ editor.
42
+ Better Errors, like many other tools, will use that environment variable to show a link that opens your
43
+ editor to the file and line from the console.
44
+
45
+ By default the links will open TextMate-protocol links.
46
+
47
+ To see if your editor is supported or to set up a different editor, see [the wiki](https://github.com/BetterErrors/better_errors/wiki/Link-to-your-editor).
48
+
49
+ ### Optional: Set `BETTER_ERRORS_INSIDE_FRAME`
50
+
51
+ If your application is running inside of an iframe, or if you have a Content Security Policy that disallows links
52
+ to other protocols, the editor links will not work.
53
+
54
+ To work around this set `BETTER_ERRORS_INSIDE_FRAME=1` in the environment, and the links will include `target=_blank`,
55
+ allowing the link to open regardless of the policy.
56
+
57
+ _This works because it opens the editor from a new browser tab, escaping from the restrictions of your site._
58
+ _Unfortunately it leaves behind an empty tab each time, so only use this if needed._
59
+
38
60
  ## Security
39
61
 
40
62
  **NOTE:** It is *critical* you put better\_errors only in the **development** section of your Gemfile.
@@ -93,6 +115,16 @@ See [the wiki for instructions on configuring the editor](https://github.com/Bet
93
115
  BetterErrors.maximum_variable_inspect_size = 100_000
94
116
  ```
95
117
 
118
+ ## Ignore inspection of variables with certain classes.
119
+
120
+ ```ruby
121
+ # e.g. in config/initializers/better_errors.rb
122
+ # This will stop BetterErrors from trying to inspect objects of these classes, which can cause
123
+ # slow loading times and unneccessary database queries. Does not check inheritance chain, use
124
+ # strings not contants.
125
+ # default value: ['ActionDispatch::Request', 'ActionDispatch::Response']
126
+ BetterErrors.ignored_classes = ['ActionDispatch::Request', 'ActionDispatch::Response']
127
+ ```
96
128
 
97
129
  ## Get in touch!
98
130
 
@@ -22,9 +22,11 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.add_development_dependency "rake", "~> 10.0"
24
24
  s.add_development_dependency "rspec", "~> 3.5"
25
+ s.add_development_dependency "rspec-html-matchers"
25
26
  s.add_development_dependency "rspec-its"
26
27
  s.add_development_dependency "yard"
27
- s.add_development_dependency "kramdown"
28
+ # kramdown 2.1 requires Ruby 2.3+
29
+ s.add_development_dependency "kramdown", (RUBY_VERSION < '2.3' ? '< 2.0.0' : '> 2.0.0')
28
30
  # simplecov and coveralls must not be included here. See the Gemfiles instead.
29
31
 
30
32
  s.add_dependency "erubi", ">= 1.0.0"
@@ -2,6 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gem "rails", "~> 4.2.0"
4
4
  gem 'nokogiri', RUBY_VERSION < '2.1' ? '~> 1.6.0' : '>= 1.7'
5
+ gem 'i18n', '< 1.5.2' if RUBY_VERSION < '2.3'
5
6
 
6
7
  gem 'coveralls', require: false
7
8
 
@@ -2,6 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gem "rails", "~> 4.2.0"
4
4
  gem 'nokogiri', RUBY_VERSION < '2.1' ? '~> 1.6.0' : '>= 1.7'
5
+ gem 'i18n', '< 1.5.2' if RUBY_VERSION < '2.3'
5
6
  gem "binding_of_caller"
6
7
 
7
8
  gem 'coveralls', require: false
@@ -2,6 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gem "rails", "~> 4.2.0"
4
4
  gem 'nokogiri', RUBY_VERSION < '2.1' ? '~> 1.6.0' : '>= 1.7'
5
+ gem 'i18n', '< 1.5.2' if RUBY_VERSION < '2.3'
5
6
  gem "haml"
6
7
 
7
8
  gem 'coveralls', require: false
@@ -1,6 +1,7 @@
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
 
5
6
  gem 'coveralls', require: false
6
7
 
@@ -1,6 +1,7 @@
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
7
  gem 'coveralls', require: false
@@ -1,6 +1,7 @@
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 "haml"
5
6
 
6
7
  gem 'coveralls', require: false
@@ -1,6 +1,7 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gem "rails", "~> 5.1.0"
4
+ gem 'i18n', '< 1.5.2', require: false if RUBY_VERSION < '2.3'
4
5
 
5
6
  gem 'coveralls', require: false
6
7
 
@@ -1,6 +1,7 @@
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
7
  gem 'coveralls', require: false
@@ -1,6 +1,7 @@
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 "haml"
5
6
 
6
7
  gem 'coveralls', require: false
@@ -1,6 +1,7 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gem "rails", "~> 5.2.0"
4
+ gem 'i18n', '< 1.5.2' if RUBY_VERSION < '2.3'
4
5
 
5
6
  gem 'coveralls', require: false
6
7
 
@@ -1,6 +1,7 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gem "rails", "~> 5.2.0"
4
+ gem 'i18n', '< 1.5.2' if RUBY_VERSION < '2.3'
4
5
  gem "binding_of_caller"
5
6
 
6
7
  gem 'coveralls', require: false
@@ -1,6 +1,7 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gem "rails", "~> 5.2.0"
4
+ gem 'i18n', '< 1.5.2' if RUBY_VERSION < '2.3'
4
5
  gem "haml"
5
6
 
6
7
  gem 'coveralls', require: false
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rails", "~> 6.0.0"
4
+
5
+ gem 'coveralls', require: false
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rails", "~> 6.0.0"
4
+ gem "binding_of_caller"
5
+
6
+ gem 'coveralls', require: false
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rails", "~> 6.0.0"
4
+ gem "haml"
5
+
6
+ gem 'coveralls', require: false
7
+
8
+ gemspec path: "../"
@@ -21,6 +21,7 @@ module BetterErrors
21
21
  { symbols: [:idea], sniff: /idea/i, url: "idea://open?file=%{file}&line=%{line}" },
22
22
  { symbols: [:rubymine], sniff: /mine/i, url: "x-mine://open?file=%{file}&line=%{line}" },
23
23
  { symbols: [:vscode, :code], sniff: /code/i, url: "vscode://file/%{file}:%{line}" },
24
+ { symbols: [:vscodium, :codium], sniff: /codium/i, url: "vscodium://file/%{file}:%{line}" },
24
25
  { symbols: [:atom], sniff: /atom/i, url: "atom://core/open/file?filename=%{file}&line=%{line}" },
25
26
  ]
26
27
 
@@ -54,9 +55,14 @@ module BetterErrors
54
55
  # the variable won't be returned.
55
56
  # @return int
56
57
  attr_accessor :maximum_variable_inspect_size
58
+
59
+ # List of classes that are excluded from inspection.
60
+ # @return [Array]
61
+ attr_accessor :ignored_classes
57
62
  end
58
63
  @ignored_instance_variables = []
59
64
  @maximum_variable_inspect_size = 100_000
65
+ @ignored_classes = ['ActionDispatch::Request', 'ActionDispatch::Response']
60
66
 
61
67
  # Returns a proc, which when called with a filename and line number argument,
62
68
  # returns a URL to open the filename and line in the selected editor.
@@ -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,19 @@ module BetterErrors
59
64
  end
60
65
 
61
66
  def exception_message
62
- exception.message.lstrip
67
+ exception.message.strip.gsub(/(\r?\n\s*\r?\n)+/, "\n")
68
+ end
69
+
70
+ def active_support_actions
71
+ return [] unless defined?(ActiveSupport::ActionableError)
72
+
73
+ ActiveSupport::ActionableError.actions(exception.type)
74
+ end
75
+
76
+ def action_dispatch_action_endpoint
77
+ return unless defined?(ActionDispatch::ActionableExceptions)
78
+
79
+ ActionDispatch::ActionableExceptions.endpoint
63
80
  end
64
81
 
65
82
  def application_frames
@@ -105,7 +122,13 @@ module BetterErrors
105
122
  end
106
123
 
107
124
  def inspect_value(obj)
108
- InspectableValue.new(obj).to_html
125
+ if BetterErrors.ignored_classes.include? obj.class.name
126
+ "<span class='unsupported'>(Instance of ignored class. "\
127
+ "#{obj.class.name ? "Remove #{CGI.escapeHTML(obj.class.name)} from" : "Modify"}"\
128
+ " BetterErrors.ignored_classes if you need to see it.)</span>"
129
+ else
130
+ InspectableValue.new(obj).to_html
131
+ end
109
132
  rescue BetterErrors::ValueLargerThanConfiguredMaximum
110
133
  "<span class='unsupported'>(Object too large. "\
111
134
  "#{obj.class.name ? "Modify #{CGI.escapeHTML(obj.class.name)}#inspect or a" : "A"}"\
@@ -1,5 +1,6 @@
1
1
  require "json"
2
2
  require "ipaddr"
3
+ require "securerandom"
3
4
  require "set"
4
5
  require "rack"
5
6
 
@@ -33,12 +34,14 @@ module BetterErrors
33
34
  # Adds an address to the set of IP addresses allowed to access Better
34
35
  # Errors.
35
36
  def self.allow_ip!(addr)
36
- ALLOWED_IPS << IPAddr.new(addr)
37
+ ALLOWED_IPS << (addr.is_a?(IPAddr) ? addr : IPAddr.new(addr))
37
38
  end
38
39
 
39
40
  allow_ip! "127.0.0.0/8"
40
41
  allow_ip! "::1/128" rescue nil # windows ruby doesn't have ipv6 support
41
42
 
43
+ CSRF_TOKEN_COOKIE_NAME = 'BetterErrors-CSRF-Token'
44
+
42
45
  # A new instance of BetterErrors::Middleware
43
46
  #
44
47
  # @param app The Rack app/middleware to wrap with Better Errors
@@ -72,7 +75,7 @@ module BetterErrors
72
75
  def better_errors_call(env)
73
76
  case env["PATH_INFO"]
74
77
  when %r{/__better_errors/(?<id>.+?)/(?<method>\w+)\z}
75
- internal_call env, $~
78
+ internal_call(env, $~[:id], $~[:method])
76
79
  when %r{/__better_errors/?\z}
77
80
  show_error_page env
78
81
  else
@@ -89,11 +92,14 @@ module BetterErrors
89
92
  end
90
93
 
91
94
  def show_error_page(env, exception=nil)
95
+ request = Rack::Request.new(env)
96
+ csrf_token = request.cookies[CSRF_TOKEN_COOKIE_NAME] || SecureRandom.uuid
97
+
92
98
  type, content = if @error_page
93
99
  if text?(env)
94
100
  [ 'plain', @error_page.render('text') ]
95
101
  else
96
- [ 'html', @error_page.render ]
102
+ [ 'html', @error_page.render('main', csrf_token) ]
97
103
  end
98
104
  else
99
105
  [ 'html', no_errors_page ]
@@ -104,12 +110,22 @@ module BetterErrors
104
110
  status_code = ActionDispatch::ExceptionWrapper.new(env, exception).status_code
105
111
  end
106
112
 
107
- [status_code, { "Content-Type" => "text/#{type}; charset=utf-8" }, [content]]
113
+ response = Rack::Response.new(content, status_code, { "Content-Type" => "text/#{type}; charset=utf-8" })
114
+
115
+ unless request.cookies[CSRF_TOKEN_COOKIE_NAME]
116
+ response.set_cookie(CSRF_TOKEN_COOKIE_NAME, value: csrf_token, httponly: true, same_site: :strict)
117
+ end
118
+
119
+ # In older versions of Rack, the body returned here is actually a Rack::BodyProxy which seems to be a bug.
120
+ # (It contains status, headers and body and does not act like an array of strings.)
121
+ # Since we already have status code and body here, there's no need to use the ones in the Rack::Response.
122
+ (_status_code, headers, _body) = response.finish
123
+ [status_code, headers, [content]]
108
124
  end
109
125
 
110
126
  def text?(env)
111
127
  env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" ||
112
- !env["HTTP_ACCEPT"].to_s.include?('html')
128
+ !env["HTTP_ACCEPT"].to_s.include?('html')
113
129
  end
114
130
 
115
131
  def log_exception
@@ -129,13 +145,22 @@ module BetterErrors
129
145
  end
130
146
  end
131
147
 
132
- def internal_call(env, opts)
148
+ def internal_call(env, id, method)
149
+ return not_found_json_response unless %w[variables eval].include?(method)
133
150
  return no_errors_json_response unless @error_page
134
- return invalid_error_json_response if opts[:id] != @error_page.id
151
+ return invalid_error_json_response if id != @error_page.id
152
+
153
+ request = Rack::Request.new(env)
154
+ return invalid_csrf_token_json_response unless request.cookies[CSRF_TOKEN_COOKIE_NAME]
155
+
156
+ request.body.rewind
157
+ body = JSON.parse(request.body.read)
158
+ return invalid_csrf_token_json_response unless request.cookies[CSRF_TOKEN_COOKIE_NAME] == body['csrfToken']
159
+
160
+ return not_acceptable_json_response unless request.content_type == 'application/json'
135
161
 
136
- env["rack.input"].rewind
137
- response = @error_page.send("do_#{opts[:method]}", JSON.parse(env["rack.input"].read))
138
- [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(response)]]
162
+ response = @error_page.send("do_#{method}", body)
163
+ [200, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.dump(response)]]
139
164
  end
140
165
 
141
166
  def no_errors_page
@@ -157,18 +182,40 @@ module BetterErrors
157
182
  "The application has been restarted since this page loaded, " +
158
183
  "or the framework is reloading all gems before each request "
159
184
  end
160
- [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(
185
+ [200, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.dump(
161
186
  error: 'No exception information available',
162
187
  explanation: explanation,
163
188
  )]]
164
189
  end
165
190
 
166
191
  def invalid_error_json_response
167
- [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(
192
+ [200, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.dump(
168
193
  error: "Session expired",
169
194
  explanation: "This page was likely opened from a previous exception, " +
170
195
  "and the exception is no longer available in memory.",
171
196
  )]]
172
197
  end
198
+
199
+ def invalid_csrf_token_json_response
200
+ [200, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.dump(
201
+ error: "Invalid CSRF Token",
202
+ explanation: "The browser session might have been cleared, " +
203
+ "or something went wrong.",
204
+ )]]
205
+ end
206
+
207
+ def not_found_json_response
208
+ [404, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.dump(
209
+ error: "Not found",
210
+ explanation: "Not a recognized internal call.",
211
+ )]]
212
+ end
213
+
214
+ def not_acceptable_json_response
215
+ [406, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.dump(
216
+ error: "Request not acceptable",
217
+ explanation: "The internal request did not match an acceptable content type.",
218
+ )]]
219
+ end
173
220
  end
174
221
  end
@@ -4,9 +4,18 @@ module BetterErrors
4
4
  attr_reader :exception, :message, :backtrace
5
5
 
6
6
  def initialize(exception)
7
- if exception.respond_to?(:cause)
7
+ if exception.class.name == "ActionView::Template::Error" && exception.respond_to?(:cause)
8
+ # Rails 6+ exceptions of this type wrap the "real" exception, and the real exception
9
+ # is actually more useful than the ActionView-provided wrapper. Once Better Errors
10
+ # supports showing all exceptions in the cause stack, this should go away. Or perhaps
11
+ # this can be changed to provide guidance by showing the second error in the cause stack
12
+ # under this condition.
8
13
  exception = exception.cause if exception.cause
9
14
  elsif exception.respond_to?(:original_exception) && exception.original_exception
15
+ # This supports some specific Rails exceptions, and this is not intended to act the same as
16
+ # the Ruby's {Exception#cause}.
17
+ # It's possible this should only support ActionView::Template::Error, but by not changing
18
+ # this we're preserving longstanding behavior of Better Errors with Rails < 6.
10
19
  exception = exception.original_exception
11
20
  end
12
21
 
@@ -36,8 +45,13 @@ module BetterErrors
36
45
 
37
46
  def setup_backtrace_from_bindings
38
47
  @backtrace = exception.__better_errors_bindings_stack.map { |binding|
39
- file = binding.eval "__FILE__"
40
- line = binding.eval "__LINE__"
48
+ if binding.respond_to?(:source_location) # Ruby >= 2.6
49
+ file = binding.source_location[0]
50
+ line = binding.source_location[1]
51
+ else
52
+ file = binding.eval "__FILE__"
53
+ line = binding.eval "__LINE__"
54
+ end
41
55
  name = binding.frame_description
42
56
  StackFrame.new(file, line, name, binding)
43
57
  }
@@ -69,7 +69,10 @@ module BetterErrors
69
69
  def local_variables
70
70
  return {} unless frame_binding
71
71
 
72
- frame_binding.eval("local_variables").each_with_object({}) do |name, hash|
72
+ lv = frame_binding.eval("local_variables")
73
+ return {} unless lv
74
+
75
+ lv.each_with_object({}) do |name, hash|
73
76
  # Ruby 2.2's local_variables will include the hidden #$! variable if
74
77
  # called from within a rescue context. This is not a valid variable name,
75
78
  # so the local_variable_get method complains. This should probably be
@@ -94,7 +97,10 @@ module BetterErrors
94
97
  end
95
98
 
96
99
  def visible_instance_variables
97
- frame_binding.eval("instance_variables") - BetterErrors.ignored_instance_variables
100
+ iv = frame_binding.eval("instance_variables")
101
+ return {} unless iv
102
+
103
+ iv - BetterErrors.ignored_instance_variables
98
104
  end
99
105
 
100
106
  def to_s
@@ -146,6 +146,14 @@
146
146
  }
147
147
 
148
148
  /* Heading */
149
+ header.exception .fix-actions {
150
+ margin-top: .5em;
151
+ }
152
+
153
+ header.exception .fix-actions input[type=submit] {
154
+ font-weight: bold;
155
+ }
156
+
149
157
  header.exception h2 {
150
158
  font-weight: 200;
151
159
  font-size: 11pt;
@@ -153,7 +161,7 @@
153
161
 
154
162
  header.exception h2,
155
163
  header.exception p {
156
- line-height: 1.4em;
164
+ line-height: 1.5em;
157
165
  overflow: hidden;
158
166
  white-space: pre;
159
167
  text-overflow: ellipsis;
@@ -166,7 +174,7 @@
166
174
 
167
175
  header.exception p {
168
176
  font-weight: 200;
169
- font-size: 20pt;
177
+ font-size: 17pt;
170
178
  color: white;
171
179
  }
172
180
 
@@ -744,6 +752,18 @@
744
752
  <header class="exception">
745
753
  <h2><strong><%= exception_type %></strong> <span>at <%= request_path %></span></h2>
746
754
  <p><%= exception_message %></p>
755
+ <% unless active_support_actions.empty? %>
756
+ <div class='fix-actions'>
757
+ <% active_support_actions.each do |action, _| %>
758
+ <form class="button_to" method="post" action="<%= action_dispatch_action_endpoint %>">
759
+ <input type="submit" value="<%= action %>">
760
+ <input type="hidden" name="action" value="<%= action %>">
761
+ <input type="hidden" name="error" value="<%= exception_type %>">
762
+ <input type="hidden" name="location" value="<%= request_path %>">
763
+ </form>
764
+ <% end %>
765
+ </div>
766
+ <% end %>
747
767
  </header>
748
768
  </div>
749
769
 
@@ -780,6 +800,7 @@
780
800
  (function() {
781
801
 
782
802
  var OID = "<%= id %>";
803
+ var csrfToken = "<%= csrf_token %>";
783
804
 
784
805
  var previousFrame = null;
785
806
  var previousFrameInfo = null;
@@ -790,6 +811,7 @@
790
811
  var req = new XMLHttpRequest();
791
812
  req.open("POST", "//" + window.location.host + <%== uri_prefix.gsub("<", "&lt;").inspect %> + "/__better_errors/" + OID + "/" + method, true);
792
813
  req.setRequestHeader("Content-Type", "application/json");
814
+ opts.csrfToken = csrfToken;
793
815
  req.send(JSON.stringify(opts));
794
816
  req.onreadystatechange = function() {
795
817
  if(req.readyState == 4) {
@@ -1,7 +1,14 @@
1
1
  <header class="trace_info clearfix">
2
2
  <div class="title">
3
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>
4
+ <div class="location">
5
+ <span class="filename">
6
+ <a
7
+ href="<%= editor_url(@frame) %>"
8
+ <%= ENV.key?('BETTER_ERRORS_INSIDE_FRAME') ? "target=_blank" : '' %>
9
+ ><%= @frame.pretty_path %></a>
10
+ </span>
11
+ </div>
5
12
  </div>
6
13
  <div class="code_block clearfix">
7
14
  <%== html_formatted_code_block @frame %>
@@ -1,3 +1,3 @@
1
1
  module BetterErrors
2
- VERSION = "2.5.1"
2
+ VERSION = "2.8.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_errors
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.1
4
+ version: 2.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charlie Somerville
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-14 00:00:00.000000000 Z
11
+ date: 2020-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.5'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec-its
42
+ name: rspec-html-matchers
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: yard
56
+ name: rspec-its
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: kramdown
70
+ name: yard
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: kramdown
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">"
88
+ - !ruby/object:Gem::Version
89
+ version: 2.0.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">"
95
+ - !ruby/object:Gem::Version
96
+ version: 2.0.0
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: erubi
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -157,6 +171,9 @@ files:
157
171
  - gemfiles/rails52.gemfile
158
172
  - gemfiles/rails52_boc.gemfile
159
173
  - gemfiles/rails52_haml.gemfile
174
+ - gemfiles/rails60.gemfile
175
+ - gemfiles/rails60_boc.gemfile
176
+ - gemfiles/rails60_haml.gemfile
160
177
  - lib/better_errors.rb
161
178
  - lib/better_errors/code_formatter.rb
162
179
  - lib/better_errors/code_formatter/html.rb
@@ -197,7 +214,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
214
  - !ruby/object:Gem::Version
198
215
  version: '0'
199
216
  requirements: []
200
- rubygems_version: 3.0.1
217
+ rubygems_version: 3.1.2
201
218
  signing_key:
202
219
  specification_version: 4
203
220
  summary: Better error page for Rails and other Rack apps