better_errors 0.9.0 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of better_errors might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 785a0ad660868a52fd48ab6d3b7c2f0271a9c32d
4
- data.tar.gz: 40d2e1dab31d7aeca46c9d6761ab907e5c944b87
5
- SHA512:
6
- metadata.gz: 715085c73baaa36af3b73294f1de9d61b8dd44011e310f1d2145fe9156d624c8fe1f089c1558ad5156c37b7f9386c43eb4573e5e8e485a7e0cda6ef1c70fc82e
7
- data.tar.gz: 56cf655e78b89ecdbd960008e7f77973ed91c2d2583ad6c3d6c0fa9cf1b88aa098c37525b7072f801b4e2ae62fdf371d2a8cbd2e947397063104900c6594c5d2
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NjYyNmFjYTQ1MjJmN2M4ZTIyODQ0MGY2YzNjOTE5ODU5ODU2NzQyZQ==
5
+ data.tar.gz: !binary |-
6
+ OWRjYzA4NTI5YzI1YWViZDAyMjlhN2RmOWQ4NDA3MWYwMTlkMzUyNA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MTBhZDMwMTI5MzFiZTYwNzNlNzk2YWI2NTE2OTYzODc2ODZjZjFkYjQ4MGIw
10
+ MGI5ZGM5NzVkMzQzZmUzYzBjOTJlYmYzMWZjMDZlMjZjYmQzNjQ2NDM5OTY5
11
+ NTdlNDMwMjFhNTlhY2VhYWU1YzVlMTA2ZGZmYmI0OGYwYzYxNmQ=
12
+ data.tar.gz: !binary |-
13
+ ZTZmNjc4ZTRmZjlhZTZhOTJhMjc1MDM0NDAwMDMyYjdkNzY4MDdkOTNlN2Nk
14
+ YjBhMjhlNjE3YTZhZmFkODU1OTk1MGFkMjk4MWRhMzljNzc4ZTczMzdhMTBk
15
+ MTRjNWJhMGU4NDllYWFiMDFiOWNiMmNhZTQ4OGU2MzE3MGQyMzQ=
data/.travis.yml CHANGED
@@ -1,3 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.0.0
4
+ - jruby-1.7.4
data/Gemfile CHANGED
@@ -1,3 +1,11 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
+
5
+ gem "rake"
6
+ gem "rspec", "2.14.1"
7
+ gem "binding_of_caller", platforms: :ruby
8
+ gem "pry"
9
+ gem "simplecov"
10
+ gem "yard"
11
+ gem "kramdown"
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Charlie Somerville
1
+ Copyright (c) 2013 Charlie Somerville
2
2
 
3
3
  MIT License
4
4
 
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Better Errors
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)
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
 
@@ -67,7 +67,7 @@ require "better_errors"
67
67
 
68
68
  configure :development do
69
69
  use BetterErrors::Middleware
70
- BetterErrors.application_root = File.expand_path("..", __FILE__)
70
+ BetterErrors.application_root = __dir__
71
71
  end
72
72
 
73
73
  get "/" do
@@ -75,27 +75,18 @@ get "/" do
75
75
  end
76
76
  ```
77
77
 
78
- ## Compatibility
79
-
80
- * **Supported**
81
- * MRI 1.9.2, 1.9.3, 2.0.0
82
- * JRuby (1.9 mode) - *advanced features unsupported*
83
- * Rubinius (1.9 mode) - *advanced features unsupported*
84
-
85
- [![Build Status](https://travis-ci.org/charliesome/better_errors.png)](https://travis-ci.org/charliesome/better_errors)
86
-
87
78
  ### Unicorn, Puma, and other multi-worker servers
88
79
 
89
80
  Better Errors works by leaving a lot of context in server process memory. If
90
- you're using a web server that runs muliple "workers" it's likely that a second
81
+ you're using a web server that runs multiple "workers" it's likely that a second
91
82
  request (as happens when you click on a stack frame) will hit a different
92
83
  worker. That worker won't have the necessary context in memory, and you'll see
93
84
  a `Session Expired` message.
94
85
 
95
- If this is the case for you, consider turing the number of workers to one (1)
96
- in `development`. Another option would be to use `rails server`, or another
97
- single-process web server, when you are trying to troubleshoot an issue in
98
- development.
86
+ If this is the case for you, consider turning the number of workers to one (1)
87
+ in `development`. Another option would be to use Webrick, Mongrel, Thin,
88
+ or another single-process server as your `rails server`, when you are trying
89
+ to troubleshoot an issue in development.
99
90
 
100
91
  ## Get in touch!
101
92
 
@@ -18,14 +18,6 @@ Gem::Specification.new do |s|
18
18
  s.require_paths = ["lib"]
19
19
 
20
20
  s.required_ruby_version = ">= 1.9.2"
21
-
22
- s.add_development_dependency "rake"
23
- s.add_development_dependency "rspec", "~> 2.12.0"
24
- s.add_development_dependency "binding_of_caller"
25
- s.add_development_dependency "pry"
26
- s.add_development_dependency "simplecov"
27
- s.add_development_dependency "yard"
28
- s.add_development_dependency "redcarpet"
29
21
 
30
22
  s.add_dependency "erubis", ">= 2.6.6"
31
23
  s.add_dependency "coderay", ">= 1.0.0"
data/lib/better_errors.rb CHANGED
@@ -11,25 +11,32 @@ require "better_errors/code_formatter"
11
11
  require "better_errors/repl"
12
12
 
13
13
  module BetterErrors
14
+ POSSIBLE_EDITOR_PRESETS = [
15
+ { symbols: [:emacs, :emacsclient], sniff: /emacs/i, url: "emacs://open?url=file://%{file}&line=%{line}" },
16
+ { symbols: [:macvim, :mvim], sniff: /vim/i, url: proc { |file, line| "mvim://open?url=file://#{file}&line=#{line}" } },
17
+ { symbols: [:sublime, :subl, :st], sniff: /subl/i, url: "subl://open?url=file://%{file}&line=%{line}" },
18
+ { symbols: [:textmate, :txmt, :tm], sniff: /mate/i, url: "txmt://open?url=file://%{file}&line=%{line}" },
19
+ ]
20
+
14
21
  class << self
15
22
  # The path to the root of the application. Better Errors uses this property
16
23
  # to determine if a file in a backtrace should be considered an application
17
24
  # frame. If you are using Better Errors with Rails, you do not need to set
18
25
  # this attribute manually.
19
- #
26
+ #
20
27
  # @return [String]
21
28
  attr_accessor :application_root
22
-
29
+
23
30
  # The logger to use when logging exception details and backtraces. If you
24
31
  # are using Better Errors with Rails, you do not need to set this attribute
25
32
  # manually. If this attribute is `nil`, nothing will be logged.
26
- #
33
+ #
27
34
  # @return [Logger, nil]
28
35
  attr_accessor :logger
29
36
 
30
37
  # @private
31
38
  attr_accessor :binding_of_caller_available
32
-
39
+
33
40
  # @private
34
41
  alias_method :binding_of_caller_available?, :binding_of_caller_available
35
42
 
@@ -41,64 +48,61 @@ module BetterErrors
41
48
 
42
49
  # Returns a proc, which when called with a filename and line number argument,
43
50
  # returns a URL to open the filename and line in the selected editor.
44
- #
51
+ #
45
52
  # Generates TextMate URLs by default.
46
- #
53
+ #
47
54
  # BetterErrors.editor["/some/file", 123]
48
55
  # # => txmt://open?url=file:///some/file&line=123
49
- #
56
+ #
50
57
  # @return [Proc]
51
58
  def self.editor
52
59
  @editor
53
60
  end
54
-
61
+
55
62
  # Configures how Better Errors generates open-in-editor URLs.
56
- #
63
+ #
57
64
  # @overload BetterErrors.editor=(sym)
58
65
  # Uses one of the preset editor configurations. Valid symbols are:
59
66
  #
60
67
  # * `:textmate`, `:txmt`, `:tm`
61
68
  # * `:sublime`, `:subl`, `:st`
62
69
  # * `:macvim`
63
- #
70
+ #
64
71
  # @param [Symbol] sym
65
- #
72
+ #
66
73
  # @overload BetterErrors.editor=(str)
67
74
  # Uses `str` as the format string for generating open-in-editor URLs.
68
- #
75
+ #
69
76
  # Use `%{file}` and `%{line}` as placeholders for the actual values.
70
- #
77
+ #
71
78
  # @example
72
79
  # BetterErrors.editor = "my-editor://open?url=%{file}&line=%{line}"
73
- #
80
+ #
74
81
  # @param [String] str
75
- #
82
+ #
76
83
  # @overload BetterErrors.editor=(proc)
77
84
  # Uses `proc` to generate open-in-editor URLs. The proc will be called
78
85
  # with `file` and `line` parameters when a URL needs to be generated.
79
- #
86
+ #
80
87
  # Your proc should take care to escape `file` appropriately with
81
88
  # `URI.encode_www_form_component` (please note that `URI.escape` is **not**
82
89
  # a suitable substitute.)
83
- #
90
+ #
84
91
  # @example
85
92
  # BetterErrors.editor = proc { |file, line|
86
93
  # "my-editor://open?url=#{URI.encode_www_form_component file}&line=#{line}"
87
94
  # }
88
- #
95
+ #
89
96
  # @param [Proc] proc
90
- #
97
+ #
91
98
  def self.editor=(editor)
92
- case editor
93
- when :textmate, :txmt, :tm
94
- self.editor = "txmt://open?url=file://%{file}&line=%{line}"
95
- when :sublime, :subl, :st
96
- self.editor = "subl://open?url=file://%{file}&line=%{line}"
97
- when :macvim, :mvim
98
- self.editor = proc { |file, line| "mvim://open?url=file://#{file}&line=#{line}" }
99
- when :emacs
100
- self.editor = "emacs://open?url=file://%{file}&line=%{line}"
101
- when String
99
+ POSSIBLE_EDITOR_PRESETS.each do |config|
100
+ if config[:symbols].include?(editor)
101
+ return self.editor = config[:url]
102
+ end
103
+ end
104
+
105
+ if editor.is_a? String
102
106
  self.editor = proc { |file, line| editor % { file: URI.encode_www_form_component(file), line: line } }
103
107
  else
104
108
  if editor.respond_to? :call
@@ -116,8 +120,18 @@ module BetterErrors
116
120
  def self.use_pry!
117
121
  REPL::PROVIDERS.unshift const: :Pry, impl: "better_errors/repl/pry"
118
122
  end
119
-
120
- BetterErrors.editor = :textmate
123
+
124
+ # Automatically sniffs a default editor preset based on the EDITOR
125
+ # environment variable.
126
+ #
127
+ # @return [Symbol]
128
+ def self.default_editor
129
+ POSSIBLE_EDITOR_PRESETS.detect(-> { {} }) { |config|
130
+ ENV["EDITOR"] =~ config[:sniff]
131
+ }[:url] || :textmate
132
+ end
133
+
134
+ BetterErrors.editor = default_editor
121
135
  end
122
136
 
123
137
  begin
@@ -36,9 +36,9 @@ module BetterErrors
36
36
  end
37
37
 
38
38
  def each_line_of(lines, &blk)
39
- line_range.zip(lines).map do |current_line, str|
39
+ line_range.zip(lines).map { |current_line, str|
40
40
  yield (current_line == line), current_line, str
41
- end
41
+ }
42
42
  end
43
43
 
44
44
  def highlighted_lines
@@ -6,14 +6,21 @@ module BetterErrors
6
6
  end
7
7
 
8
8
  def formatted_lines
9
- each_line_of highlighted_lines do |highlight, current_line, str|
9
+ each_line_of(highlighted_lines) { |highlight, current_line, str|
10
10
  class_name = highlight ? "highlight" : ""
11
- sprintf '<pre class="%s">%5d %s</pre>', class_name, current_line, str
12
- end
11
+ sprintf '<pre class="%s">%s</pre>', class_name, str
12
+ }
13
+ end
14
+
15
+ def formatted_nums
16
+ each_line_of(highlighted_lines) { |highlight, current_line, str|
17
+ class_name = highlight ? "highlight" : ""
18
+ sprintf '<span class="%s">%5d</span>', class_name, current_line
19
+ }
13
20
  end
14
21
 
15
22
  def formatted_code
16
- %{<div class="code">#{super}</div>}
23
+ %{<div class="code_linenums">#{formatted_nums.join}</div><div class="code">#{super}</div>}
17
24
  end
18
25
  end
19
26
  end
@@ -6,9 +6,9 @@ module BetterErrors
6
6
  end
7
7
 
8
8
  def formatted_lines
9
- each_line_of context_lines do |highlight, current_line, str|
9
+ each_line_of(context_lines) { |highlight, current_line, str|
10
10
  sprintf '%s %3d %s', (highlight ? '>' : ' '), current_line, str
11
- end
11
+ }
12
12
  end
13
13
  end
14
14
  end
@@ -40,11 +40,12 @@ module BetterErrors
40
40
  return { error: "REPL unavailable in this stack frame" }
41
41
  end
42
42
 
43
- result, prompt =
43
+ result, prompt, prefilled_input =
44
44
  (@repls[index] ||= REPL.provider.new(binding)).send_input(code)
45
45
 
46
46
  { result: result,
47
47
  prompt: prompt,
48
+ prefilled_input: prefilled_input,
48
49
  highlighted_input: CodeRay.scan(code, :ruby).div(wrap: nil) }
49
50
  end
50
51
 
@@ -64,8 +64,8 @@ module BetterErrors
64
64
  def allow_ip?(env)
65
65
  # REMOTE_ADDR is not in the rack spec, so some application servers do
66
66
  # not provide it.
67
- return true unless env["REMOTE_ADDR"]
68
- ip = IPAddr.new env["REMOTE_ADDR"]
67
+ return true unless env["REMOTE_ADDR"] and !env["REMOTE_ADDR"].strip.empty?
68
+ ip = IPAddr.new env["REMOTE_ADDR"].split("%").first
69
69
  ALLOWED_IPS.any? { |subnet| subnet.include? ip }
70
70
  end
71
71
 
@@ -85,10 +85,10 @@ module BetterErrors
85
85
  rescue Exception => ex
86
86
  @error_page = @handler.new ex, env
87
87
  log_exception
88
- show_error_page(env)
88
+ show_error_page(env, ex)
89
89
  end
90
90
 
91
- def show_error_page(env)
91
+ def show_error_page(env, exception=nil)
92
92
  type, content = if @error_page
93
93
  if text?(env)
94
94
  [ 'plain', @error_page.render('text') ]
@@ -99,7 +99,12 @@ module BetterErrors
99
99
  [ 'html', no_errors_page ]
100
100
  end
101
101
 
102
- [500, { "Content-Type" => "text/#{type}; charset=utf-8" }, [content]]
102
+ status_code = 500
103
+ if defined? ActionDispatch::ExceptionWrapper
104
+ status_code = ActionDispatch::ExceptionWrapper.new(env, exception).status_code
105
+ end
106
+
107
+ [status_code, { "Content-Type" => "text/#{type}; charset=utf-8" }, [content]]
103
108
  end
104
109
 
105
110
  def text?(env)
@@ -15,9 +15,9 @@ module BetterErrors
15
15
  end
16
16
 
17
17
  def self.detect
18
- PROVIDERS.find do |prov|
18
+ PROVIDERS.find { |prov|
19
19
  test_provider prov
20
- end
20
+ }
21
21
  end
22
22
 
23
23
  def self.test_provider(provider)
@@ -6,7 +6,7 @@ module BetterErrors
6
6
  end
7
7
 
8
8
  def send_input(str)
9
- [execute(str), ">>"]
9
+ [execute(str), ">>", ""]
10
10
  end
11
11
 
12
12
  private
@@ -40,19 +40,24 @@ module BetterErrors
40
40
  @output = Output.new
41
41
  @pry = ::Pry.new input: @input, output: @output
42
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
43
  @fiber.resume
48
44
  end
49
45
 
50
46
  def send_input(str)
51
47
  local ::Pry.config, color: false, pager: false do
52
- @continued_expression = true
53
48
  @fiber.resume "#{str}\n"
54
- [@output.read_buffer, @continued_expression ? ".." : ">>"]
49
+ [@output.read_buffer, *prompt]
50
+ end
51
+ end
52
+
53
+ def prompt
54
+ if indent = @pry.instance_variable_get(:@indent) and !indent.indent_level.empty?
55
+ ["..", indent.indent_level]
56
+ else
57
+ [">>", ""]
55
58
  end
59
+ rescue
60
+ [">>", ""]
56
61
  end
57
62
 
58
63
  private
@@ -1,8 +1,10 @@
1
+ require "set"
2
+
1
3
  module BetterErrors
2
4
  # @private
3
5
  class StackFrame
4
6
  def self.from_exception(exception)
5
- if exception.__better_errors_bindings_stack.any?
7
+ if has_binding_stack?(exception)
6
8
  list = exception.__better_errors_bindings_stack.map { |binding|
7
9
  file = binding.eval "__FILE__"
8
10
  line = binding.eval "__LINE__"
@@ -16,12 +18,36 @@ module BetterErrors
16
18
  }.compact
17
19
  end
18
20
 
19
- if exception.is_a?(SyntaxError) && exception.to_s =~ /\A(.*):(\d*):/
20
- list.unshift StackFrame.new($1, $2.to_i, "")
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
21
25
  end
22
26
 
23
27
  list
24
28
  end
29
+
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
25
51
 
26
52
  attr_reader :filename, :line, :name, :frame_binding
27
53
 
@@ -85,7 +111,11 @@ module BetterErrors
85
111
  return {} unless frame_binding
86
112
  frame_binding.eval("local_variables").each_with_object({}) do |name, hash|
87
113
  begin
88
- hash[name] = frame_binding.eval(name.to_s)
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
89
119
  rescue NameError => e
90
120
  # local_variables sometimes returns broken variables.
91
121
  # https://bugs.ruby-lang.org/issues/7536
@@ -110,14 +140,18 @@ module BetterErrors
110
140
 
111
141
  private
112
142
  def set_pretty_method_name
143
+ return if RUBY_VERSION < "2.0.0"
144
+
113
145
  name =~ /\A(block (\([^)]+\) )?in )?/
114
146
  recv = frame_binding.eval("self")
115
- return unless method_name = frame_binding.eval("__method__")
116
- if recv.is_a? Module
147
+
148
+ return unless method_name = frame_binding.eval("::Kernel.__method__")
149
+
150
+ if Kernel.instance_method(:is_a?).bind(recv).call Module
117
151
  @class_name = "#{$1}#{recv}"
118
152
  @method_name = ".#{method_name}"
119
153
  else
120
- @class_name = "#{$1}#{recv.class}"
154
+ @class_name = "#{$1}#{Kernel.instance_method(:class).bind(recv).call}"
121
155
  @method_name = "##{method_name}"
122
156
  end
123
157
  end