better_errors 1.0.1 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 29ae99a691ade2e90114703be9bc09dfccf220b4
4
- data.tar.gz: 07dc30996fea81069a374862faf29989c843a06e
3
+ metadata.gz: bc36d3113c7041816a7697fbd1e50333f5976003
4
+ data.tar.gz: 834f3f4a4903a45f150d83adde3f25452d2fd86b
5
5
  SHA512:
6
- metadata.gz: 9aa2006d43787d6931c28baddd137fbeebeae07efac5aab3b9ed052e8c3ab3d610b15c196f7a69c109606c423f6b9e25b84b7975aeb2baec5fb0c484208f2723
7
- data.tar.gz: f17296d9da8e87080839aabd3a1e113b6768885c3e2a4ed48dd3d73734d33c9c31b242fbfe0bfd475b350d1f99cccc2cd0efea08d9ee621db64b5f557c8645ea
6
+ metadata.gz: 130d61a4ce2e5509eb1585dfa0e3c620bf54387ac6a0931d1a534296460a0e06601a6d71ce41b3d3ffbd15951626ddfd7e753bb1bc911c35cf76cfa52cfcd771
7
+ data.tar.gz: 5906697a352187036b92cdefe124fea459405d7e3cc80f3bcc6f07c7a2952f328367f452e88272947edef464a4cc75fbe3d276c24580b9b633b6f9e60c99a60d
data/.gitignore CHANGED
@@ -1,6 +1,8 @@
1
1
  *.gem
2
- tmp
3
- Gemfile.lock
4
- coverage
5
- .yardoc
6
- doc
2
+ /tmp
3
+ /Gemfile.lock
4
+ /coverage
5
+ /.yardoc
6
+ /doc
7
+ /.bundle
8
+ /vendor/gems
data/.travis.yml CHANGED
@@ -1,6 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.1.0
3
4
  - 2.0.0
4
- - 1.9.3
5
- - 1.9.2
6
- - jruby-1.7.4
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # Changelog
2
+
3
+ See https://github.com/charliesome/better_errors/releases
data/Gemfile CHANGED
@@ -3,9 +3,9 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  gem "rake"
6
+ gem "rack"
6
7
  gem "rspec", "2.14.1"
7
8
  gem "binding_of_caller", platforms: :ruby
8
9
  gem "pry", "0.9.12"
9
- gem "simplecov"
10
10
  gem "yard"
11
11
  gem "kramdown"
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Charlie Somerville
1
+ Copyright (c) 2014 Charlie Somerville
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Better Errors [![Build Status](https://travis-ci.org/charliesome/better_errors.png)](https://travis-ci.org/charliesome/better_errors) [![Code Climate](https://codeclimate.com/github/charliesome/better_errors.png)](https://codeclimate.com/github/charliesome/better_errors)
1
+ # Better Errors [![Gem Version](http://img.shields.io/gem/v/better_errors.svg)](https://rubygems.org/gems/better_errors) [![Build Status](https://travis-ci.org/charliesome/better_errors.svg)](https://travis-ci.org/charliesome/better_errors) [![Code Climate](http://img.shields.io/codeclimate/github/charliesome/better_errors.svg)](https://codeclimate.com/github/charliesome/better_errors)
2
2
 
3
3
  Better Errors replaces the standard Rails error page with a much better and more useful error page. It is also usable outside of Rails in any Rack app as Rack middleware.
4
4
 
@@ -29,6 +29,8 @@ gem "binding_of_caller"
29
29
 
30
30
  This is an optional dependency however, and Better Errors will work without it.
31
31
 
32
+ _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`._
33
+
32
34
  ## Security
33
35
 
34
36
  **NOTE:** It is *critical* you put better\_errors in the **development** section. **Do NOT run better_errors in production, or on Internet facing hosts.**
data/Rakefile CHANGED
@@ -1,4 +1,13 @@
1
1
  require "bundler/gem_tasks"
2
- task :default => :spec
3
- task :test => :spec
4
- task :spec do sh 'bundle exec rspec' end
2
+ require "rspec/core/rake_task"
3
+
4
+ namespace :test do
5
+ RSpec::Core::RakeTask.new(:with_binding_of_caller)
6
+
7
+ without_task = RSpec::Core::RakeTask.new(:without_binding_of_caller)
8
+ without_task.ruby_opts = "-I spec -r without_binding_of_caller"
9
+
10
+ task :all => [:with_binding_of_caller, :without_binding_of_caller]
11
+ end
12
+
13
+ task :default => "test:all"
@@ -1,4 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
1
  lib = File.expand_path('../lib', __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'better_errors/version'
@@ -17,10 +16,11 @@ Gem::Specification.new do |s|
17
16
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
17
  s.require_paths = ["lib"]
19
18
 
20
- s.required_ruby_version = ">= 1.9.2"
21
-
19
+ s.required_ruby_version = ">= 2.0.0"
20
+
22
21
  s.add_dependency "erubis", ">= 2.6.6"
23
22
  s.add_dependency "coderay", ">= 1.0.0"
23
+ s.add_dependency "rack", ">= 0.9.0"
24
24
 
25
25
  # optional dependencies:
26
26
  # s.add_dependency "binding_of_caller"
@@ -1,5 +1,6 @@
1
1
  require "cgi"
2
2
  require "json"
3
+ require "securerandom"
3
4
 
4
5
  module BetterErrors
5
6
  # @private
@@ -7,42 +8,46 @@ module BetterErrors
7
8
  def self.template_path(template_name)
8
9
  File.expand_path("../templates/#{template_name}.erb", __FILE__)
9
10
  end
10
-
11
+
11
12
  def self.template(template_name)
12
13
  Erubis::EscapedEruby.new(File.read(template_path(template_name)))
13
14
  end
14
-
15
+
15
16
  attr_reader :exception, :env, :repls
16
-
17
+
17
18
  def initialize(exception, env)
18
- @exception = real_exception(exception)
19
+ @exception = RaisedException.new(exception)
19
20
  @env = env
20
21
  @start_time = Time.now.to_f
21
22
  @repls = []
22
23
  end
23
-
24
+
25
+ def id
26
+ @id ||= SecureRandom.hex(8)
27
+ end
28
+
24
29
  def render(template_name = "main")
25
30
  self.class.template(template_name).result binding
26
31
  end
27
-
32
+
28
33
  def do_variables(opts)
29
34
  index = opts["index"].to_i
30
35
  @frame = backtrace_frames[index]
31
36
  @var_start_time = Time.now.to_f
32
37
  { html: render("variable_info") }
33
38
  end
34
-
39
+
35
40
  def do_eval(opts)
36
41
  index = opts["index"].to_i
37
42
  code = opts["source"]
38
-
43
+
39
44
  unless binding = backtrace_frames[index].frame_binding
40
45
  return { error: "REPL unavailable in this stack frame" }
41
46
  end
42
-
47
+
43
48
  result, prompt, prefilled_input =
44
49
  (@repls[index] ||= REPL.provider.new(binding)).send_input(code)
45
-
50
+
46
51
  { result: result,
47
52
  prompt: prompt,
48
53
  prefilled_input: prefilled_input,
@@ -50,22 +55,22 @@ module BetterErrors
50
55
  end
51
56
 
52
57
  def backtrace_frames
53
- @backtrace_frames ||= StackFrame.from_exception(exception)
58
+ exception.backtrace
54
59
  end
55
60
 
56
61
  def application_frames
57
- backtrace_frames.select { |frame| frame.context == :application }
62
+ backtrace_frames.select(&:application?)
58
63
  end
59
64
 
60
65
  def first_frame
61
- backtrace_frames.detect { |frame| frame.context == :application } || backtrace_frames.first
66
+ application_frames.first || backtrace_frames.first
62
67
  end
63
-
68
+
64
69
  private
65
70
  def editor_url(frame)
66
71
  BetterErrors.editor[frame.filename, frame.line]
67
72
  end
68
-
73
+
69
74
  def rack_session
70
75
  env['rack.session']
71
76
  end
@@ -77,31 +82,15 @@ module BetterErrors
77
82
  def uri_prefix
78
83
  env["SCRIPT_NAME"] || ""
79
84
  end
80
-
81
- def exception_message
82
- if exception.is_a?(SyntaxError) && exception.message =~ /\A.*:\d*: (.*)$/
83
- $1
84
- else
85
- exception.message
86
- end
87
- end
88
85
 
89
- def real_exception(exception)
90
- if exception.respond_to? :original_exception
91
- exception.original_exception
92
- else
93
- exception
94
- end
95
- end
96
-
97
86
  def request_path
98
87
  env["PATH_INFO"]
99
88
  end
100
-
89
+
101
90
  def html_formatted_code_block(frame)
102
91
  CodeFormatter::HTML.new(frame.filename, frame.line).output
103
92
  end
104
-
93
+
105
94
  def text_formatted_code_block(frame)
106
95
  CodeFormatter::Text.new(frame.filename, frame.line).output
107
96
  end
@@ -0,0 +1,17 @@
1
+ module BetterErrors
2
+ module ExceptionExtension
3
+ prepend_features Exception
4
+
5
+ def set_backtrace(*)
6
+ if caller_locations.none? { |loc| loc.path == __FILE__ }
7
+ @__better_errors_bindings_stack = ::Kernel.binding.callers.drop(1)
8
+ end
9
+
10
+ super
11
+ end
12
+
13
+ def __better_errors_bindings_stack
14
+ @__better_errors_bindings_stack || []
15
+ end
16
+ end
17
+ end
@@ -1,6 +1,7 @@
1
1
  require "json"
2
2
  require "ipaddr"
3
3
  require "set"
4
+ require "rack"
4
5
 
5
6
  module BetterErrors
6
7
  # Better Errors' error handling middleware. Including this in your middleware
@@ -62,16 +63,15 @@ module BetterErrors
62
63
  private
63
64
 
64
65
  def allow_ip?(env)
65
- # REMOTE_ADDR is not in the rack spec, so some application servers do
66
- # not provide it.
67
- return true unless env["REMOTE_ADDR"] and !env["REMOTE_ADDR"].strip.empty?
68
- ip = IPAddr.new env["REMOTE_ADDR"].split("%").first
66
+ request = Rack::Request.new(env)
67
+ return true unless request.ip and !request.ip.strip.empty?
68
+ ip = IPAddr.new request.ip.split("%").first
69
69
  ALLOWED_IPS.any? { |subnet| subnet.include? ip }
70
70
  end
71
71
 
72
72
  def better_errors_call(env)
73
73
  case env["PATH_INFO"]
74
- when %r{/__better_errors/(?<oid>-?\d+)/(?<method>\w+)\z}
74
+ when %r{/__better_errors/(?<id>.+?)/(?<method>\w+)\z}
75
75
  internal_call env, $~
76
76
  when %r{/__better_errors/?\z}
77
77
  show_error_page env
@@ -115,7 +115,7 @@ module BetterErrors
115
115
  def log_exception
116
116
  return unless BetterErrors.logger
117
117
 
118
- message = "\n#{@error_page.exception.class} - #{@error_page.exception.message}:\n"
118
+ message = "\n#{@error_page.exception.type} - #{@error_page.exception.message}:\n"
119
119
  @error_page.backtrace_frames.each do |frame|
120
120
  message << " #{frame}\n"
121
121
  end
@@ -124,7 +124,7 @@ module BetterErrors
124
124
  end
125
125
 
126
126
  def internal_call(env, opts)
127
- if opts[:oid].to_i != @error_page.object_id
127
+ if opts[:id] != @error_page.id
128
128
  return [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(error: "Session expired")]]
129
129
  end
130
130
 
@@ -0,0 +1,66 @@
1
+ # @private
2
+ module BetterErrors
3
+ class RaisedException
4
+ attr_reader :exception, :message, :backtrace
5
+
6
+ def initialize(exception)
7
+ if exception.respond_to?(:original_exception) && exception.original_exception
8
+ exception = exception.original_exception
9
+ end
10
+
11
+ @exception = exception
12
+ @message = exception.message
13
+
14
+ setup_backtrace
15
+ massage_syntax_error
16
+ end
17
+
18
+ def type
19
+ exception.class
20
+ end
21
+
22
+ private
23
+ def has_bindings?
24
+ exception.respond_to?(:__better_errors_bindings_stack) && exception.__better_errors_bindings_stack.any?
25
+ end
26
+
27
+ def setup_backtrace
28
+ if has_bindings?
29
+ setup_backtrace_from_bindings
30
+ else
31
+ setup_backtrace_from_backtrace
32
+ end
33
+ end
34
+
35
+ def setup_backtrace_from_bindings
36
+ @backtrace = exception.__better_errors_bindings_stack.map { |binding|
37
+ file = binding.eval "__FILE__"
38
+ line = binding.eval "__LINE__"
39
+ name = binding.frame_description
40
+ StackFrame.new(file, line, name, binding)
41
+ }
42
+ end
43
+
44
+ def setup_backtrace_from_backtrace
45
+ @backtrace = (exception.backtrace || []).map { |frame|
46
+ if /\A(?<file>.*?):(?<line>\d+)(:in `(?<name>.*)')?/ =~ frame
47
+ StackFrame.new(file, line.to_i, name)
48
+ end
49
+ }.compact
50
+ end
51
+
52
+ def massage_syntax_error
53
+ case exception.class.to_s
54
+ when "Haml::SyntaxError"
55
+ if /\A(.+?):(\d+)/ =~ exception.backtrace.first
56
+ backtrace.unshift(StackFrame.new($1, $2.to_i, ""))
57
+ end
58
+ when "SyntaxError"
59
+ if /\A(.+?):(\d+): (.*)/m =~ exception.message
60
+ backtrace.unshift(StackFrame.new($1, $2.to_i, ""))
61
+ @message = $3
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -36,8 +36,8 @@ module BetterErrors
36
36
  @fiber = Fiber.new do
37
37
  @pry.repl binding
38
38
  end
39
- @input = Input.new
40
- @output = Output.new
39
+ @input = BetterErrors::REPL::Pry::Input.new
40
+ @output = BetterErrors::REPL::Pry::Output.new
41
41
  @pry = ::Pry.new input: @input, output: @output
42
42
  @pry.hooks.clear_all if defined?(@pry.hooks.clear_all)
43
43
  @fiber.resume
@@ -4,67 +4,26 @@ module BetterErrors
4
4
  # @private
5
5
  class StackFrame
6
6
  def self.from_exception(exception)
7
- if has_binding_stack?(exception)
8
- list = exception.__better_errors_bindings_stack.map { |binding|
9
- file = binding.eval "__FILE__"
10
- line = binding.eval "__LINE__"
11
- name = binding.frame_description
12
- StackFrame.new(file, line, name, binding)
13
- }
14
- else
15
- list = (exception.backtrace || []).map { |frame|
16
- next unless md = /\A(?<file>.*?):(?<line>\d+)(:in `(?<name>.*)')?/.match(frame)
17
- StackFrame.new(md[:file], md[:line].to_i, md[:name])
18
- }.compact
19
- end
20
-
21
- if syntax_error?(exception)
22
- if trace = exception.backtrace and trace.first =~ /\A(.*):(\d+)/
23
- list.unshift StackFrame.new($1, $2.to_i, "")
24
- end
25
- end
26
-
27
- list
7
+ RaisedException.new(exception).backtrace
28
8
  end
29
9
 
30
- def self.syntax_error_classes
31
- # Better Errors may be loaded before some of the gems that provide these
32
- # classes, so we lazily set up the set of syntax error classes at runtime
33
- # after everything has hopefully had a chance to load.
34
- #
35
- @syntax_error_classes ||= begin
36
- class_names = %w[
37
- Haml::SyntaxError
38
- ]
39
-
40
- Set.new(class_names.map { |klass| eval(klass) rescue nil }.compact)
41
- end
42
- end
43
-
44
- def self.syntax_error?(exception)
45
- exception.is_a?(SyntaxError) || syntax_error_classes.include?(exception.class)
46
- end
47
-
48
- def self.has_binding_stack?(exception)
49
- exception.respond_to?(:__better_errors_bindings_stack) && exception.__better_errors_bindings_stack.any?
50
- end
51
-
52
10
  attr_reader :filename, :line, :name, :frame_binding
53
-
11
+
54
12
  def initialize(filename, line, name, frame_binding = nil)
55
13
  @filename = filename
56
14
  @line = line
57
15
  @name = name
58
16
  @frame_binding = frame_binding
59
-
17
+
60
18
  set_pretty_method_name if frame_binding
61
19
  end
62
-
20
+
63
21
  def application?
64
- root = BetterErrors.application_root
65
- filename.index(root) == 0 if root
22
+ if root = BetterErrors.application_root
23
+ filename.index(root) == 0 && filename.index("#{root}/vendor") != 0
24
+ end
66
25
  end
67
-
26
+
68
27
  def application_path
69
28
  filename[(BetterErrors.application_root.length+1)..-1]
70
29
  end
@@ -72,7 +31,7 @@ module BetterErrors
72
31
  def gem?
73
32
  Gem.path.any? { |path| filename.index(path) == 0 }
74
33
  end
75
-
34
+
76
35
  def gem_path
77
36
  if path = Gem.path.detect { |path| filename.index(path) == 0 }
78
37
  gem_name_and_version, path = filename.sub("#{path}/gems/", "").split("/", 2)
@@ -88,7 +47,7 @@ module BetterErrors
88
47
  def method_name
89
48
  @method_name || @name
90
49
  end
91
-
50
+
92
51
  def context
93
52
  if gem?
94
53
  :gem
@@ -98,7 +57,7 @@ module BetterErrors
98
57
  :dunno
99
58
  end
100
59
  end
101
-
60
+
102
61
  def pretty_path
103
62
  case context
104
63
  when :application; application_path
@@ -106,23 +65,18 @@ module BetterErrors
106
65
  else filename
107
66
  end
108
67
  end
109
-
68
+
110
69
  def local_variables
111
70
  return {} unless frame_binding
112
71
  frame_binding.eval("local_variables").each_with_object({}) do |name, hash|
113
- begin
114
- if defined?(frame_binding.local_variable_get)
115
- hash[name] = frame_binding.local_variable_get(name)
116
- else
117
- hash[name] = frame_binding.eval(name.to_s)
118
- end
119
- rescue NameError => e
120
- # local_variables sometimes returns broken variables.
121
- # https://bugs.ruby-lang.org/issues/7536
72
+ if defined?(frame_binding.local_variable_get)
73
+ hash[name] = frame_binding.local_variable_get(name)
74
+ else
75
+ hash[name] = frame_binding.eval(name.to_s)
122
76
  end
123
77
  end
124
78
  end
125
-
79
+
126
80
  def instance_variables
127
81
  return {} unless frame_binding
128
82
  Hash[visible_instance_variables.map { |x|
@@ -137,17 +91,15 @@ module BetterErrors
137
91
  def to_s
138
92
  "#{pretty_path}:#{line}:in `#{name}'"
139
93
  end
140
-
94
+
141
95
  private
142
96
  def set_pretty_method_name
143
- return if RUBY_VERSION < "2.0.0"
144
-
145
97
  name =~ /\A(block (\([^)]+\) )?in )?/
146
98
  recv = frame_binding.eval("self")
147
99
 
148
100
  return unless method_name = frame_binding.eval("::Kernel.__method__")
149
101
 
150
- if Kernel.instance_method(:is_a?).bind(recv).call Module
102
+ if Module === recv
151
103
  @class_name = "#{$1}#{recv}"
152
104
  @method_name = ".#{method_name}"
153
105
  else
@@ -1,7 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <title><%= exception.class %> at <%= request_path %></title>
4
+ <title><%= exception.type %> at <%= request_path %></title>
5
5
  </head>
6
6
  <body>
7
7
  <%# Stylesheets are placed in the <body> for Turbolinks compatibility. %>
@@ -180,6 +180,7 @@
180
180
  padding-right: 20px;
181
181
  overflow-y: auto;
182
182
  word-wrap: break-word;
183
+ white-space: pre-wrap;
183
184
  height: auto;
184
185
  max-height: 7em;
185
186
  }
@@ -443,13 +444,13 @@
443
444
  font-weight: bold;
444
445
  font-size: 10pt;
445
446
  }
446
-
447
+
447
448
  .trace_info .title .location a {
448
449
  color:inherit;
449
450
  text-decoration:none;
450
451
  border-bottom:1px solid #aaaaaa;
451
452
  }
452
-
453
+
453
454
  .trace_info .title .location a:hover {
454
455
  border-color:#666666;
455
456
  }
@@ -472,7 +473,7 @@
472
473
  padding-bottom:9px;
473
474
  float:left;
474
475
  }
475
-
476
+
476
477
  .code_linenums span{
477
478
  display:block;
478
479
  padding:0 12px;
@@ -730,8 +731,8 @@
730
731
 
731
732
  <div class='top'>
732
733
  <header class="exception">
733
- <h2><strong><%= exception.class %></strong> <span>at <%= request_path %></span></h2>
734
- <p><%= exception_message %></p>
734
+ <h2><strong><%= exception.type %></strong> <span>at <%= request_path %></span></h2>
735
+ <p><%= exception.message %></p>
735
736
  </header>
736
737
  </div>
737
738
 
@@ -767,8 +768,8 @@
767
768
  <script>
768
769
  (function() {
769
770
 
770
- var OID = <%== object_id.to_s.inspect %>;
771
-
771
+ var OID = "<%= id %>";
772
+
772
773
  var previousFrame = null;
773
774
  var previousFrameInfo = null;
774
775
  var allFrames = document.querySelectorAll("ul.frames li");
@@ -786,68 +787,73 @@
786
787
  }
787
788
  };
788
789
  }
789
-
790
+
790
791
  function escapeHTML(html) {
791
792
  return html.replace(/&/, "&amp;").replace(/</g, "&lt;");
792
793
  }
793
-
794
+
794
795
  function REPL(index) {
795
796
  this.index = index;
796
-
797
- this.previousCommands = [];
798
- this.previousCommandOffset = 0;
797
+
798
+ var previousCommands = JSON.parse(localStorage.getItem("better_errors_previous_commands"));
799
+ if(previousCommands === null) {
800
+ localStorage.setItem("better_errors_previous_commands", JSON.stringify([]));
801
+ previousCommands = [];
802
+ }
803
+
804
+ this.previousCommandOffset = previousCommands.length;
799
805
  }
800
-
806
+
801
807
  REPL.all = [];
802
-
808
+
803
809
  REPL.prototype.install = function(containerElement) {
804
810
  this.container = containerElement;
805
-
811
+
806
812
  this.promptElement = this.container.querySelector(".prompt span");
807
813
  this.inputElement = this.container.querySelector("input");
808
814
  this.outputElement = this.container.querySelector("pre");
809
-
815
+
810
816
  var self = this;
811
817
  this.inputElement.onkeydown = function(ev) {
812
818
  self.onKeyDown(ev);
813
819
  };
814
-
820
+
815
821
  this.setPrompt(">>");
816
-
822
+
817
823
  REPL.all[this.index] = this;
818
824
  }
819
-
825
+
820
826
  REPL.prototype.focus = function() {
821
827
  this.inputElement.focus();
822
828
  };
823
-
829
+
824
830
  REPL.prototype.setPrompt = function(prompt) {
825
831
  this._prompt = prompt;
826
832
  this.promptElement.innerHTML = escapeHTML(prompt);
827
833
  };
828
-
834
+
829
835
  REPL.prototype.getInput = function() {
830
836
  return this.inputElement.value;
831
837
  };
832
-
838
+
833
839
  REPL.prototype.setInput = function(text) {
834
840
  this.inputElement.value = text;
835
-
841
+
836
842
  if(this.inputElement.setSelectionRange) {
837
843
  // set cursor to end of input
838
844
  this.inputElement.setSelectionRange(text.length, text.length);
839
845
  }
840
846
  };
841
-
847
+
842
848
  REPL.prototype.writeRawOutput = function(output) {
843
849
  this.outputElement.innerHTML += output;
844
850
  this.outputElement.scrollTop = this.outputElement.scrollHeight;
845
851
  };
846
-
852
+
847
853
  REPL.prototype.writeOutput = function(output) {
848
854
  this.writeRawOutput(escapeHTML(output));
849
855
  };
850
-
856
+
851
857
  REPL.prototype.sendInput = function(line) {
852
858
  var self = this;
853
859
  apiCall("eval", { "index": this.index, source: line }, function(response) {
@@ -861,48 +867,56 @@
861
867
  self.setInput(response.prefilled_input);
862
868
  });
863
869
  };
864
-
870
+
865
871
  REPL.prototype.onEnterKey = function() {
866
872
  var text = this.getInput();
867
873
  if(text != "" && text !== undefined) {
868
- this.previousCommandOffset = this.previousCommands.push(text);
874
+ var previousCommands = JSON.parse(localStorage.getItem("better_errors_previous_commands"));
875
+ this.previousCommandOffset = previousCommands.push(text);
876
+ if(previousCommands.length > 100) {
877
+ previousCommands.splice(0, 1);
878
+ }
879
+ localStorage.setItem("better_errors_previous_commands", JSON.stringify(previousCommands));
869
880
  }
870
881
  this.setInput("");
871
882
  this.sendInput(text);
872
883
  };
873
-
884
+
874
885
  REPL.prototype.onNavigateHistory = function(direction) {
875
886
  this.previousCommandOffset += direction;
876
-
887
+ var previousCommands = JSON.parse(localStorage.getItem("better_errors_previous_commands"));
888
+
877
889
  if(this.previousCommandOffset < 0) {
878
890
  this.previousCommandOffset = -1;
879
891
  this.setInput("");
880
892
  return;
881
893
  }
882
-
883
- if(this.previousCommandOffset >= this.previousCommands.length) {
884
- this.previousCommandOffset = this.previousCommands.length;
894
+
895
+ if(this.previousCommandOffset >= previousCommands.length) {
896
+ this.previousCommandOffset = previousCommands.length;
885
897
  this.setInput("");
886
898
  return;
887
899
  }
888
-
889
- this.setInput(this.previousCommands[this.previousCommandOffset]);
900
+
901
+ this.setInput(previousCommands[this.previousCommandOffset]);
890
902
  };
891
-
903
+
892
904
  REPL.prototype.onKeyDown = function(ev) {
893
905
  if(ev.keyCode == 13) {
894
906
  this.onEnterKey();
895
- } else if(ev.keyCode == 38) {
896
- // the user pressed the up arrow.
907
+ } else if(ev.keyCode == 38 || (ev.ctrlKey && ev.keyCode == 80)) {
908
+ // the user pressed the up arrow or Ctrl-P
897
909
  this.onNavigateHistory(-1);
910
+ ev.preventDefault();
898
911
  return false;
899
- } else if(ev.keyCode == 40) {
900
- // the user pressed the down arrow.
912
+ } else if(ev.keyCode == 40 || (ev.ctrlKey && ev.keyCode == 78)) {
913
+ // the user pressed the down arrow or Ctrl-N
901
914
  this.onNavigateHistory(1);
915
+ ev.preventDefault();
902
916
  return false;
903
917
  }
904
918
  };
905
-
919
+
906
920
  function switchTo(el) {
907
921
  if(previousFrameInfo) previousFrameInfo.style.display = "none";
908
922
  previousFrameInfo = el;
@@ -937,7 +951,7 @@
937
951
  });
938
952
  }
939
953
  }
940
-
954
+
941
955
  for(var i = 0; i < allFrames.length; i++) {
942
956
  (function(i, el) {
943
957
  var el = allFrames[i];
@@ -947,12 +961,12 @@
947
961
  }
948
962
  el.className = "selected";
949
963
  previousFrame = el;
950
-
964
+
951
965
  selectFrameInfo(el.attributes["data-index"].value);
952
966
  };
953
967
  })(i);
954
968
  }
955
-
969
+
956
970
  // Click the first application frame
957
971
  (
958
972
  document.querySelector(".frames li.application") ||
@@ -968,7 +982,7 @@
968
982
  var applicationFramesButtonIsInstalled = false;
969
983
  var applicationFramesButton = document.getElementById("application_frames");
970
984
  var allFramesButton = document.getElementById("all_frames");
971
-
985
+
972
986
  // The application frames button only needs to be bound if
973
987
  // there are actually any application frames to look at.
974
988
  var installApplicationFramesButton = function() {
@@ -984,10 +998,10 @@
984
998
  }
985
999
  return false;
986
1000
  };
987
-
1001
+
988
1002
  applicationFramesButtonIsInstalled = true;
989
1003
  }
990
-
1004
+
991
1005
  allFramesButton.onclick = function() {
992
1006
  if(applicationFramesButtonIsInstalled) {
993
1007
  applicationFramesButton.className = "";
@@ -999,7 +1013,7 @@
999
1013
  }
1000
1014
  return false;
1001
1015
  };
1002
-
1016
+
1003
1017
  // If there are no application frames, select the 'All Frames'
1004
1018
  // tab by default.
1005
1019
  if(applicationFramesCount > 0) {
@@ -1,6 +1,6 @@
1
- <%== text_heading("=", "%s at %s" % [exception.class, request_path]) %>
1
+ <%== text_heading("=", "%s at %s" % [exception.type, request_path]) %>
2
2
 
3
- > <%== exception_message %>
3
+ > <%== exception.message %>
4
4
  <% if backtrace_frames.any? %>
5
5
 
6
6
  <%== text_heading("-", "%s, line %i" % [first_frame.pretty_path, first_frame.line]) %>
@@ -1,3 +1,3 @@
1
1
  module BetterErrors
2
- VERSION = "1.0.1"
2
+ VERSION = "2.0.0"
3
3
  end
data/lib/better_errors.rb CHANGED
@@ -3,12 +3,13 @@ require "erubis"
3
3
  require "coderay"
4
4
  require "uri"
5
5
 
6
- require "better_errors/version"
6
+ require "better_errors/code_formatter"
7
7
  require "better_errors/error_page"
8
- require "better_errors/stack_frame"
9
8
  require "better_errors/middleware"
10
- require "better_errors/code_formatter"
9
+ require "better_errors/raised_exception"
11
10
  require "better_errors/repl"
11
+ require "better_errors/stack_frame"
12
+ require "better_errors/version"
12
13
 
13
14
  module BetterErrors
14
15
  POSSIBLE_EDITOR_PRESETS = [
@@ -136,11 +137,10 @@ end
136
137
 
137
138
  begin
138
139
  require "binding_of_caller"
140
+ require "better_errors/exception_extension"
139
141
  BetterErrors.binding_of_caller_available = true
140
142
  rescue LoadError => e
141
143
  BetterErrors.binding_of_caller_available = false
142
144
  end
143
145
 
144
- require "better_errors/core_ext/exception"
145
-
146
146
  require "better_errors/rails" if defined? Rails::Railtie
@@ -40,6 +40,14 @@ module BetterErrors
40
40
  app.call("REMOTE_ADDR" => "77.55.33.11")
41
41
  end
42
42
 
43
+ it "respects the X-Forwarded-For header" do
44
+ app.should_not_receive :better_errors_call
45
+ app.call(
46
+ "REMOTE_ADDR" => "127.0.0.1",
47
+ "HTTP_X_FORWARDED_FOR" => "1.2.3.4",
48
+ )
49
+ end
50
+
43
51
  it "doesn't blow up when given a blank REMOTE_ADDR" do
44
52
  expect { app.call("REMOTE_ADDR" => " ") }.to_not raise_error
45
53
  end
@@ -71,6 +79,40 @@ module BetterErrors
71
79
  status.should == 500
72
80
  end
73
81
 
82
+ context "original_exception" do
83
+ class OriginalExceptionException < Exception
84
+ attr_reader :original_exception
85
+
86
+ def initialize(message, original_exception = nil)
87
+ super(message)
88
+ @original_exception = original_exception
89
+ end
90
+ end
91
+
92
+ it "shows Original Exception if it responds_to and has an original_exception" do
93
+ app = Middleware.new(->env {
94
+ raise OriginalExceptionException.new("Other Exception", Exception.new("Original Exception"))
95
+ })
96
+
97
+ status, _, body = app.call({})
98
+
99
+ status.should == 500
100
+ body.join.should_not match(/Other Exception/)
101
+ body.join.should match(/Original Exception/)
102
+ end
103
+
104
+ it "won't crash if the exception responds_to but doesn't have an original_exception" do
105
+ app = Middleware.new(->env {
106
+ raise OriginalExceptionException.new("Other Exception")
107
+ })
108
+
109
+ status, _, body = app.call({})
110
+
111
+ status.should == 500
112
+ body.join.should match(/Other Exception/)
113
+ end
114
+ end
115
+
74
116
  it "returns ExceptionWrapper's status_code" do
75
117
  ad_ew = double("ActionDispatch::ExceptionWrapper")
76
118
  ad_ew.stub('new').with({}, exception ){ double("ExceptionWrapper", status_code: 404) }
@@ -0,0 +1,52 @@
1
+ require "spec_helper"
2
+
3
+ module BetterErrors
4
+ describe RaisedException do
5
+ let(:exception) { RuntimeError.new("whoops") }
6
+ subject { RaisedException.new(exception) }
7
+
8
+ its(:exception) { should == exception }
9
+ its(:message) { should == "whoops" }
10
+ its(:type) { should == RuntimeError }
11
+
12
+ context "when the exception wraps another exception" do
13
+ let(:original_exception) { RuntimeError.new("something went wrong!") }
14
+ let(:exception) { double(:original_exception => original_exception) }
15
+
16
+ its(:exception) { should == original_exception }
17
+ its(:message) { should == "something went wrong!" }
18
+ end
19
+
20
+ context "when the exception is a syntax error" do
21
+ let(:exception) { SyntaxError.new("foo.rb:123: you made a typo!") }
22
+
23
+ its(:message) { should == "you made a typo!" }
24
+ its(:type) { should == SyntaxError }
25
+
26
+ it "has the right filename and line number in the backtrace" do
27
+ subject.backtrace.first.filename.should == "foo.rb"
28
+ subject.backtrace.first.line.should == 123
29
+ end
30
+ end
31
+
32
+ context "when the exception is a HAML syntax error" do
33
+ before do
34
+ stub_const("Haml::SyntaxError", Class.new(SyntaxError))
35
+ end
36
+
37
+ let(:exception) {
38
+ Haml::SyntaxError.new("you made a typo!").tap do |ex|
39
+ ex.set_backtrace(["foo.rb:123", "haml/internals/blah.rb:123456"])
40
+ end
41
+ }
42
+
43
+ its(:message) { should == "you made a typo!" }
44
+ its(:type) { should == Haml::SyntaxError }
45
+
46
+ it "has the right filename and line number in the backtrace" do
47
+ subject.backtrace.first.filename.should == "foo.rb"
48
+ subject.backtrace.first.line.should == 123
49
+ end
50
+ end
51
+ end
52
+ end
@@ -25,7 +25,11 @@ module BetterErrors
25
25
  filled.should == " "
26
26
 
27
27
  output, prompt, filled = repl.send_input "end"
28
- output.should == "=> nil\n"
28
+ if RUBY_VERSION >= "2.1.0"
29
+ output.should == "=> :f\n"
30
+ else
31
+ output.should == "=> nil\n"
32
+ end
29
33
  prompt.should == ">>"
30
34
  filled.should == ""
31
35
  end
@@ -6,77 +6,77 @@ module BetterErrors
6
6
  it "is true for application filenames" do
7
7
  BetterErrors.stub(:application_root).and_return("/abc/xyz")
8
8
  frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
9
-
9
+
10
10
  frame.application?.should be_true
11
11
  end
12
-
12
+
13
13
  it "is false for everything else" do
14
14
  BetterErrors.stub(:application_root).and_return("/abc/xyz")
15
15
  frame = StackFrame.new("/abc/nope", 123, "foo")
16
-
16
+
17
17
  frame.application?.should be_false
18
18
  end
19
-
19
+
20
20
  it "doesn't care if no application_root is set" do
21
21
  frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
22
-
22
+
23
23
  frame.application?.should be_false
24
24
  end
25
25
  end
26
-
26
+
27
27
  context "#gem?" do
28
28
  it "is true for gem filenames" do
29
29
  Gem.stub(:path).and_return(["/abc/xyz"])
30
30
  frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
31
-
31
+
32
32
  frame.gem?.should be_true
33
33
  end
34
-
34
+
35
35
  it "is false for everything else" do
36
36
  Gem.stub(:path).and_return(["/abc/xyz"])
37
37
  frame = StackFrame.new("/abc/nope", 123, "foo")
38
-
38
+
39
39
  frame.gem?.should be_false
40
40
  end
41
41
  end
42
-
42
+
43
43
  context "#application_path" do
44
44
  it "chops off the application root" do
45
45
  BetterErrors.stub(:application_root).and_return("/abc/xyz")
46
46
  frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
47
-
47
+
48
48
  frame.application_path.should == "app/controllers/crap_controller.rb"
49
49
  end
50
50
  end
51
-
51
+
52
52
  context "#gem_path" do
53
53
  it "chops of the gem path and stick (gem) there" do
54
54
  Gem.stub(:path).and_return(["/abc/xyz"])
55
55
  frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
56
-
56
+
57
57
  frame.gem_path.should == "whatever (1.2.3) lib/whatever.rb"
58
58
  end
59
-
59
+
60
60
  it "prioritizes gem path over application path" do
61
61
  BetterErrors.stub(:application_root).and_return("/abc/xyz")
62
62
  Gem.stub(:path).and_return(["/abc/xyz/vendor"])
63
63
  frame = StackFrame.new("/abc/xyz/vendor/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
64
-
64
+
65
65
  frame.gem_path.should == "whatever (1.2.3) lib/whatever.rb"
66
66
  end
67
67
  end
68
-
68
+
69
69
  context "#pretty_path" do
70
70
  it "returns #application_path for application paths" do
71
71
  BetterErrors.stub(:application_root).and_return("/abc/xyz")
72
72
  frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
73
73
  frame.pretty_path.should == frame.application_path
74
74
  end
75
-
75
+
76
76
  it "returns #gem_path for gem paths" do
77
77
  Gem.stub(:path).and_return(["/abc/xyz"])
78
78
  frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
79
-
79
+
80
80
  frame.pretty_path.should == frame.gem_path
81
81
  end
82
82
  end
@@ -90,28 +90,28 @@ module BetterErrors
90
90
  frames.first.filename.should == "my_file.rb"
91
91
  frames.first.line.should == 123
92
92
  end
93
-
93
+
94
94
  it "doesn't blow up if no method name is given" do
95
95
  error = StandardError.allocate
96
-
96
+
97
97
  error.stub(:backtrace).and_return(["foo.rb:123"])
98
98
  frames = StackFrame.from_exception(error)
99
99
  frames.first.filename.should == "foo.rb"
100
100
  frames.first.line.should == 123
101
-
101
+
102
102
  error.stub(:backtrace).and_return(["foo.rb:123: this is an error message"])
103
103
  frames = StackFrame.from_exception(error)
104
104
  frames.first.filename.should == "foo.rb"
105
105
  frames.first.line.should == 123
106
106
  end
107
-
107
+
108
108
  it "ignores a backtrace line if its format doesn't make any sense at all" do
109
109
  error = StandardError.allocate
110
110
  error.stub(:backtrace).and_return(["foo.rb:123:in `foo'", "C:in `find'", "bar.rb:123:in `bar'"])
111
111
  frames = StackFrame.from_exception(error)
112
112
  frames.count.should == 2
113
113
  end
114
-
114
+
115
115
  it "doesn't blow up if a filename contains a colon" do
116
116
  error = StandardError.allocate
117
117
  error.stub(:backtrace).and_return(["crap:filename.rb:123"])
@@ -125,11 +125,7 @@ module BetterErrors
125
125
  ::Kernel.binding
126
126
  end
127
127
  frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index", obj.my_binding)
128
- if RUBY_VERSION >= "2.0.0"
129
- frame.class_name.should == 'BasicObject'
130
- else
131
- frame.class_name.should be_nil
132
- end
128
+ frame.class_name.should == 'BasicObject'
133
129
  end
134
130
 
135
131
  it "sets method names properly" do
@@ -143,12 +139,12 @@ module BetterErrors
143
139
  end
144
140
 
145
141
  frame = StackFrame.from_exception(obj.my_method).first
146
- if RUBY_VERSION >= "2.0.0"
142
+ if BetterErrors.binding_of_caller_available?
147
143
  frame.method_name.should == "#my_method"
148
144
  frame.class_name.should == "String"
149
145
  else
150
146
  frame.method_name.should == "my_method"
151
- frame.class_name.should be_nil
147
+ frame.class_name.should == nil
152
148
  end
153
149
  end
154
150
 
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,5 @@
1
- require "simplecov"
2
- SimpleCov.start
3
-
4
1
  $: << File.expand_path("../../lib", __FILE__)
2
+
5
3
  ENV["EDITOR"] = nil
4
+
6
5
  require "better_errors"
7
- require "ostruct"
@@ -0,0 +1,9 @@
1
+ module Kernel
2
+ alias_method :require_with_binding_of_caller, :require
3
+
4
+ def require(feature)
5
+ raise LoadError if feature == "binding_of_caller"
6
+
7
+ require_with_binding_of_caller(feature)
8
+ end
9
+ end
metadata CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_errors
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charlie Somerville
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-08 00:00:00.000000000 Z
11
+ date: 2014-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erubis
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 2.6.6
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 2.6.6
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: coderay
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 1.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 1.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.0
41
55
  description: Provides a better error page for Rails and other Rack apps. Includes
42
56
  source code inspection, a live REPL and local/instance variable inspection for all
43
57
  stack frames.
@@ -47,9 +61,10 @@ executables: []
47
61
  extensions: []
48
62
  extra_rdoc_files: []
49
63
  files:
50
- - .gitignore
51
- - .travis.yml
52
- - .yardopts
64
+ - ".gitignore"
65
+ - ".travis.yml"
66
+ - ".yardopts"
67
+ - CHANGELOG.md
53
68
  - Gemfile
54
69
  - LICENSE.txt
55
70
  - README.md
@@ -59,10 +74,11 @@ files:
59
74
  - lib/better_errors/code_formatter.rb
60
75
  - lib/better_errors/code_formatter/html.rb
61
76
  - lib/better_errors/code_formatter/text.rb
62
- - lib/better_errors/core_ext/exception.rb
63
77
  - lib/better_errors/error_page.rb
78
+ - lib/better_errors/exception_extension.rb
64
79
  - lib/better_errors/middleware.rb
65
80
  - lib/better_errors/rails.rb
81
+ - lib/better_errors/raised_exception.rb
66
82
  - lib/better_errors/repl.rb
67
83
  - lib/better_errors/repl/basic.rb
68
84
  - lib/better_errors/repl/pry.rb
@@ -74,6 +90,7 @@ files:
74
90
  - spec/better_errors/code_formatter_spec.rb
75
91
  - spec/better_errors/error_page_spec.rb
76
92
  - spec/better_errors/middleware_spec.rb
93
+ - spec/better_errors/raised_exception_spec.rb
77
94
  - spec/better_errors/repl/basic_spec.rb
78
95
  - spec/better_errors/repl/pry_spec.rb
79
96
  - spec/better_errors/repl/shared_examples.rb
@@ -81,6 +98,7 @@ files:
81
98
  - spec/better_errors/support/my_source.rb
82
99
  - spec/better_errors_spec.rb
83
100
  - spec/spec_helper.rb
101
+ - spec/without_binding_of_caller.rb
84
102
  homepage: https://github.com/charliesome/better_errors
85
103
  licenses:
86
104
  - MIT
@@ -91,17 +109,17 @@ require_paths:
91
109
  - lib
92
110
  required_ruby_version: !ruby/object:Gem::Requirement
93
111
  requirements:
94
- - - '>='
112
+ - - ">="
95
113
  - !ruby/object:Gem::Version
96
- version: 1.9.2
114
+ version: 2.0.0
97
115
  required_rubygems_version: !ruby/object:Gem::Requirement
98
116
  requirements:
99
- - - '>='
117
+ - - ">="
100
118
  - !ruby/object:Gem::Version
101
119
  version: '0'
102
120
  requirements: []
103
121
  rubyforge_project:
104
- rubygems_version: 2.0.3
122
+ rubygems_version: 2.2.2
105
123
  signing_key:
106
124
  specification_version: 4
107
125
  summary: Better error page for Rails and other Rack apps
@@ -109,6 +127,7 @@ test_files:
109
127
  - spec/better_errors/code_formatter_spec.rb
110
128
  - spec/better_errors/error_page_spec.rb
111
129
  - spec/better_errors/middleware_spec.rb
130
+ - spec/better_errors/raised_exception_spec.rb
112
131
  - spec/better_errors/repl/basic_spec.rb
113
132
  - spec/better_errors/repl/pry_spec.rb
114
133
  - spec/better_errors/repl/shared_examples.rb
@@ -116,4 +135,4 @@ test_files:
116
135
  - spec/better_errors/support/my_source.rb
117
136
  - spec/better_errors_spec.rb
118
137
  - spec/spec_helper.rb
119
- has_rdoc:
138
+ - spec/without_binding_of_caller.rb
@@ -1,21 +0,0 @@
1
- class Exception
2
- original_set_backtrace = instance_method(:set_backtrace)
3
-
4
- if BetterErrors.binding_of_caller_available?
5
- define_method :set_backtrace 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
12
- end
13
- end
14
- original_set_backtrace.bind(self).call(*args)
15
- end
16
- end
17
-
18
- def __better_errors_bindings_stack
19
- @__better_errors_bindings_stack || []
20
- end
21
- end