better_errors 0.2.0 → 0.3.0

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.

data/.gitignore CHANGED
@@ -1,3 +1,6 @@
1
1
  *.gem
2
2
  tmp
3
3
  Gemfile.lock
4
+ coverage
5
+ .yardoc
6
+ doc
@@ -0,0 +1 @@
1
+ --markup markdown --no-private
@@ -0,0 +1,9 @@
1
+ # Contributing to Better Errors
2
+
3
+ Please make sure to follow these guidelines when contributing code to Better Errors. They will improve the chances of your pull request being merged and they will make my life easier.
4
+
5
+ * If you are contributing a large-ish change, please split your changes up into small, logical commits. This is so I have the option of merging in some but not all of your commits. If you just give me one huge commit and there are things that I don't want to merge in, I won't.
6
+
7
+ * Whitespace at the end of lines of code is not OK, but empty lines with indentation is fine. Please do not remove these when sending in changes.
8
+
9
+ * Don't change things that are unrelated to your main changes unnecessarily. Send another pull request for these changes - don't try to sneak them in.
data/README.md CHANGED
@@ -13,7 +13,7 @@ Better Errors replaces the standard Rails error page with a much better and more
13
13
 
14
14
  ## Installation
15
15
 
16
- Add this line to your application's Gemfile (under the **development** group):
16
+ Add this to your Gemfile:
17
17
 
18
18
  ```ruby
19
19
  group :development do
@@ -21,7 +21,9 @@ group :development do
21
21
  end
22
22
  ```
23
23
 
24
- If you would like to use Better Errors' **advanced features** (REPL, local/instance variable inspection, pretty stack frame names), you need to add the [`binding_of_caller`](https://github.com/banister/binding_of_caller) gem to your Gemfile:
24
+ **NOTE:** It is *critical* you put better\_errors in the **development** section. **Do NOT run better_errors in production, or on Internet facing hosts.**
25
+
26
+ If you would like to use Better Errors' **advanced features** (REPL, local/instance variable inspection, pretty stack frame names), you need to add the [`binding_of_caller`](https://github.com/banister/binding_of_caller) gem by [@banisterfiend](http://twitter.com/banisterfiend) to your Gemfile:
25
27
 
26
28
  ```ruby
27
29
  gem "binding_of_caller"
@@ -62,6 +64,10 @@ end
62
64
 
63
65
  * Calling `yield` from the REPL segfaults MRI 1.9.x.
64
66
 
67
+ ## Get in touch!
68
+
69
+ If you're using better_errors, I'd love to hear from you. Drop me a line and tell me what you think!
70
+
65
71
  ## Contributing
66
72
 
67
73
  1. Fork it
@@ -18,6 +18,10 @@ Gem::Specification.new do |s|
18
18
  s.require_paths = ["lib"]
19
19
 
20
20
  s.add_development_dependency "rake"
21
+ s.add_development_dependency "rspec", "~> 2.12.0"
22
+ s.add_development_dependency "binding_of_caller"
23
+ s.add_development_dependency "simplecov"
24
+ s.add_development_dependency "yard"
21
25
 
22
26
  s.add_dependency "erubis", ">= 2.7.0"
23
27
  s.add_dependency "coderay", ">= 1.0.0"
@@ -1,33 +1,119 @@
1
1
  require "pp"
2
2
  require "erubis"
3
3
  require "coderay"
4
+ require "uri"
4
5
 
5
6
  require "better_errors/version"
6
7
  require "better_errors/error_page"
7
8
  require "better_errors/stack_frame"
8
9
  require "better_errors/middleware"
10
+ require "better_errors/disable_logging_middleware"
9
11
  require "better_errors/code_formatter"
10
12
  require "better_errors/repl"
11
13
 
12
- class << BetterErrors
13
- attr_accessor :application_root, :binding_of_caller_available, :logger
14
+ module BetterErrors
15
+ class << self
16
+ # The path to the root of the application. Better Errors uses this property
17
+ # to determine if a file in a backtrace should be considered an application
18
+ # frame. If you are using Better Errors with Rails, you do not need to set
19
+ # this attribute manually.
20
+ #
21
+ # @return [String]
22
+ attr_accessor :application_root
23
+
24
+ # The logger to use when logging exception details and backtraces. If you
25
+ # are using Better Errors with Rails, you do not need to set this attribute
26
+ # manually. If this attribute is `nil`, nothing will be logged.
27
+ #
28
+ # @return [Logger, nil]
29
+ attr_accessor :logger
30
+
31
+ # @private
32
+ attr_accessor :binding_of_caller_available
33
+
34
+ # @private
35
+ alias_method :binding_of_caller_available?, :binding_of_caller_available
36
+ end
37
+
38
+ # Returns a proc, which when called with a filename and line number argument,
39
+ # returns a URL to open the filename and line in the selected editor.
40
+ #
41
+ # Generates TextMate URLs by default.
42
+ #
43
+ # BetterErrors.editor["/some/file", 123]
44
+ # # => txmt://open?url=file:///some/file&line=123
45
+ #
46
+ # @return [Proc]
47
+ def self.editor
48
+ @editor
49
+ end
50
+
51
+ # Configures how Better Errors generates open-in-editor URLs.
52
+ #
53
+ # @overload BetterErrors.editor=(sym)
54
+ # Uses one of the preset editor configurations. Valid symbols are:
55
+ #
56
+ # * `:textmate`, `:txmt`, `:tm`
57
+ # * `:sublime`, `:subl`, `:st`
58
+ # * `:macvim`
59
+ #
60
+ # @param [Symbol] sym
61
+ #
62
+ # @overload BetterErrors.editor=(str)
63
+ # Uses `str` as the format string for generating open-in-editor URLs.
64
+ #
65
+ # Use `%{file}` and `%{line}` as placeholders for the actual values.
66
+ #
67
+ # @example
68
+ # BetterErrors.editor = "my-editor://open?url=%{file}&line=%{line}"
69
+ #
70
+ # @param [String] str
71
+ #
72
+ # @overload BetterErrors.editor=(proc)
73
+ # Uses `proc` to generate open-in-editor URLs. The proc will be called
74
+ # with `file` and `line` parameters when a URL needs to be generated.
75
+ #
76
+ # Your proc should take care to escape `file` appropriately with
77
+ # `URI.encode_www_form_component` (please note that `URI.escape` is **not**
78
+ # a suitable substitute.)
79
+ #
80
+ # @example
81
+ # BetterErrors.editor = proc { |file, line|
82
+ # "my-editor://open?url=#{URI.encode_www_form_component file}&line=#{line}"
83
+ # }
84
+ #
85
+ # @param [Proc] proc
86
+ #
87
+ def self.editor=(editor)
88
+ case editor
89
+ when :textmate, :txmt, :tm
90
+ self.editor = "txmt://open?url=file://%{file}&line=%{line}"
91
+ when :sublime, :subl, :st
92
+ self.editor = "subl://open?url=file://%{file}&line=%{line}"
93
+ when :macvim, :mvim
94
+ self.editor = "mvim://open?url=file://%{file}&line=%{line}"
95
+ when String
96
+ self.editor = proc { |file, line| editor % { file: URI.encode_www_form_component(file), line: line } }
97
+ else
98
+ if editor.respond_to? :call
99
+ @editor = editor
100
+ else
101
+ raise TypeError, "Expected editor to be a valid editor key, a format string or a callable."
102
+ end
103
+ end
104
+ end
14
105
 
15
- alias_method :binding_of_caller_available?, :binding_of_caller_available
106
+ BetterErrors.editor = :textmate
16
107
  end
17
108
 
18
109
  begin
110
+ $:.unshift "/Users/charlie/code/binding_of_caller/lib"
19
111
  require "binding_of_caller"
20
112
  BetterErrors.binding_of_caller_available = true
21
113
  rescue LoadError => e
22
114
  BetterErrors.binding_of_caller_available = false
23
115
  end
24
116
 
25
- unless BetterErrors.binding_of_caller_available?
26
- warn "BetterErrors: binding_of_caller gem unavailable, cannot display local variables on error pages."
27
- warn "Add 'binding_of_caller' to your Gemfile to make this warning go away."
28
- warn ""
29
- end
30
-
31
117
  require "better_errors/core_ext/exception"
32
118
 
33
119
  require "better_errors/rails" if defined? Rails::Railtie
@@ -1,4 +1,5 @@
1
1
  module BetterErrors
2
+ # @private
2
3
  class CodeFormatter
3
4
  FILE_TYPES = {
4
5
  ".rb" => :ruby,
@@ -0,0 +1,16 @@
1
+ module BetterErrors
2
+ # @private
3
+ class DisableLoggingMiddleware
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ original_level = BetterErrors.logger.level
10
+ BetterErrors.logger.level = Logger::ERROR if env['PATH_INFO'].index("/__better_errors") == 0
11
+ @app.call(env)
12
+ ensure
13
+ BetterErrors.logger.level = original_level
14
+ end
15
+ end
16
+ end
@@ -1,6 +1,8 @@
1
+ require "cgi"
1
2
  require "json"
2
3
 
3
4
  module BetterErrors
5
+ # @private
4
6
  class ErrorPage
5
7
  def self.template_path(template_name)
6
8
  File.expand_path("../templates/#{template_name}.erb", __FILE__)
@@ -26,6 +28,7 @@ module BetterErrors
26
28
  def do_variables(opts)
27
29
  index = opts["index"].to_i
28
30
  @frame = backtrace_frames[index]
31
+ @var_start_time = Time.now.to_f
29
32
  { html: render("variable_info") }
30
33
  end
31
34
 
@@ -50,6 +53,22 @@ module BetterErrors
50
53
  end
51
54
 
52
55
  private
56
+ def editor_url(frame)
57
+ BetterErrors.editor[frame.filename, frame.line]
58
+ end
59
+
60
+ def rack_session
61
+ env['rack.session']
62
+ end
63
+
64
+ def rails_params
65
+ env['action_dispatch.request.parameters']
66
+ end
67
+
68
+ def uri_prefix
69
+ env["SCRIPT_NAME"] || ""
70
+ end
71
+
53
72
  def exception_message
54
73
  if exception.is_a?(SyntaxError) && exception.message =~ /\A.*:\d*: (.*)$/
55
74
  $1
@@ -67,11 +86,19 @@ module BetterErrors
67
86
  end
68
87
 
69
88
  def request_path
70
- env["REQUEST_PATH"]
89
+ env["PATH_INFO"]
71
90
  end
72
91
 
73
92
  def highlighted_code_block(frame)
74
93
  CodeFormatter.new(frame.filename, frame.line).html
75
94
  end
95
+
96
+ def inspect_value(obj)
97
+ CGI.escapeHTML(obj.inspect)
98
+ rescue NoMethodError
99
+ "<span class='unsupported'>(object doesn't support inspect)</span>"
100
+ rescue Exception => e
101
+ "<span class='unsupported'>(exception was raised in inspect)</span>"
102
+ end
76
103
  end
77
104
  end
@@ -1,15 +1,43 @@
1
1
  require "json"
2
2
 
3
3
  module BetterErrors
4
+ # Better Errors' error handling middleware. Including this in your middleware
5
+ # stack will show a Better Errors error page for exceptions raised below this
6
+ # middleware.
7
+ #
8
+ # If you are using Ruby on Rails, you do not need to manually insert this
9
+ # middleware into your middleware stack.
10
+ #
11
+ # @example Sinatra
12
+ # require "better_errors"
13
+ #
14
+ # if development?
15
+ # use BetterErrors::Middleware
16
+ # end
17
+ #
18
+ # @example Rack
19
+ # require "better_errors"
20
+ # if ENV["RACK_ENV"] == "development"
21
+ # use BetterErrors::Middleware
22
+ # end
23
+ #
4
24
  class Middleware
25
+ # A new instance of BetterErrors::Middleware
26
+ #
27
+ # @param app The Rack app/middleware to wrap with Better Errors
28
+ # @param handler The error handler to use.
5
29
  def initialize(app, handler = ErrorPage)
6
30
  @app = app
7
31
  @handler = handler
8
32
  end
9
33
 
34
+ # Calls the Better Errors middleware
35
+ #
36
+ # @param [Hash] env
37
+ # @return [Array]
10
38
  def call(env)
11
39
  case env["PATH_INFO"]
12
- when %r{\A/__better_errors/(?<oid>\d+)/(?<method>\w+)\z}
40
+ when %r{\A/__better_errors/(?<oid>-?\d+)/(?<method>\w+)\z}
13
41
  internal_call env, $~
14
42
  when %r{\A/__better_errors/?\z}
15
43
  show_error_page env
@@ -28,8 +56,16 @@ module BetterErrors
28
56
  end
29
57
 
30
58
  def show_error_page(env)
31
- [500, { "Content-Type" => "text/html; charset=utf-8" }, [@error_page.render]]
59
+ content = if @error_page
60
+ @error_page.render
61
+ else
62
+ "<h1>No errors</h1><p>No errors have been recorded yet.</p><hr>" +
63
+ "<code>Better Errors v#{BetterErrors::VERSION}</code>"
64
+ end
65
+
66
+ [500, { "Content-Type" => "text/html; charset=utf-8" }, [content]]
32
67
  end
68
+
33
69
 
34
70
  def log_exception
35
71
  return unless BetterErrors.logger
@@ -1,8 +1,10 @@
1
1
  module BetterErrors
2
+ # @private
2
3
  class Railtie < Rails::Railtie
3
4
  initializer "better_errors.configure_rails_initialization" do
4
5
  unless Rails.env.production?
5
6
  Rails.application.middleware.use BetterErrors::Middleware
7
+ Rails.application.middleware.insert_before Rails::Rack::Logger, BetterErrors::DisableLoggingMiddleware
6
8
  BetterErrors.logger = Rails.logger
7
9
  BetterErrors.application_root = Rails.root.to_s
8
10
  end
@@ -1,4 +1,5 @@
1
1
  module BetterErrors
2
+ # @private
2
3
  module REPL
3
4
  PROVIDERS = [
4
5
  { impl: "better_errors/repl/basic",
@@ -52,15 +52,25 @@ module BetterErrors
52
52
  end
53
53
 
54
54
  def send_input(str)
55
- old_pry_config_color = ::Pry.config.color
56
- ::Pry.config.color = false
57
- @continued_expression = true
58
- @fiber.resume "#{str}\n"
59
- # TODO - indent with `pry_indent.current_prefix`
60
- # TODO - use proper pry prompt
61
- [@output.read_buffer, @continued_expression ? ".." : ">>"]
55
+ local ::Pry.config, color: false, pager: false do
56
+ @continued_expression = true
57
+ @fiber.resume "#{str}\n"
58
+ [@output.read_buffer, @continued_expression ? ".." : ">>"]
59
+ end
60
+ end
61
+
62
+ private
63
+ def local(obj, attrs)
64
+ old_attrs = {}
65
+ attrs.each do |k, v|
66
+ old_attrs[k] = obj.send k
67
+ obj.send "#{k}=", v
68
+ end
69
+ yield
62
70
  ensure
63
- ::Pry.config.color = old_pry_config_color
71
+ old_attrs.each do |k, v|
72
+ obj.send "#{k}=", v
73
+ end
64
74
  end
65
75
  end
66
76
  end
@@ -1,10 +1,11 @@
1
1
  module BetterErrors
2
+ # @private
2
3
  class StackFrame
3
4
  def self.from_exception(exception)
4
5
  idx_offset = 0
5
6
  list = exception.backtrace.each_with_index.map do |frame, idx|
6
7
  frame_binding = exception.__better_errors_bindings_stack[idx - idx_offset]
7
- md = /\A(?<file>.*):(?<line>\d*)(:in `(?<name>.*)')?\z/.match(frame)
8
+ next unless md = /\A(?<file>.*?):(?<line>\d+)(:in `(?<name>.*)')?/.match(frame)
8
9
 
9
10
  # prevent mismatching frames in the backtrace with the binding stack
10
11
  if frame_binding and frame_binding.eval("__FILE__") != md[:file]
@@ -13,7 +14,7 @@ module BetterErrors
13
14
  end
14
15
 
15
16
  StackFrame.new(md[:file], md[:line].to_i, md[:name], frame_binding)
16
- end
17
+ end.compact
17
18
 
18
19
  if exception.is_a?(SyntaxError) && exception.to_s =~ /\A(.*):(\d*):/
19
20
  list.unshift StackFrame.new($1, $2.to_i, "")
@@ -35,7 +36,7 @@ module BetterErrors
35
36
 
36
37
  def application?
37
38
  root = BetterErrors.application_root
38
- starts_with? filename, root if root
39
+ filename.index(root) == 0 if root
39
40
  end
40
41
 
41
42
  def application_path
@@ -43,12 +44,12 @@ module BetterErrors
43
44
  end
44
45
 
45
46
  def gem?
46
- Gem.path.any? { |path| starts_with? filename, path }
47
+ Gem.path.any? { |path| filename.index(path) == 0 }
47
48
  end
48
49
 
49
50
  def gem_path
50
51
  Gem.path.each do |path|
51
- if starts_with? filename, path
52
+ if filename.index(path) == 0
52
53
  return filename.gsub("#{path}/gems/", "(gem) ")
53
54
  end
54
55
  end
@@ -63,10 +64,10 @@ module BetterErrors
63
64
  end
64
65
 
65
66
  def context
66
- if application?
67
- :application
68
- elsif gem?
67
+ if gem?
69
68
  :gem
69
+ elsif application?
70
+ :application
70
71
  else
71
72
  :dunno
72
73
  end
@@ -116,9 +117,5 @@ module BetterErrors
116
117
  @method_name = "##{method_name}"
117
118
  end
118
119
  end
119
-
120
- def starts_with?(haystack, needle)
121
- haystack[0, needle.length] == needle
122
- end
123
120
  end
124
121
  end
@@ -422,6 +422,16 @@
422
422
  font-weight: bold;
423
423
  font-size: 10pt;
424
424
  }
425
+
426
+ .trace_info .title .location a {
427
+ color:inherit;
428
+ text-decoration:none;
429
+ border-bottom:1px solid #aaaaaa;
430
+ }
431
+
432
+ .trace_info .title .location a:hover {
433
+ border-color:#666666;
434
+ }
425
435
 
426
436
  .trace_info .title .name {
427
437
  float: right;
@@ -493,7 +503,7 @@
493
503
 
494
504
  .console pre {
495
505
  padding: 10px 10px 0 10px;
496
- max-height: 200px;
506
+ max-height: 400px;
497
507
  overflow-x: none;
498
508
  overflow-y: auto;
499
509
  margin-bottom: -3px;
@@ -594,6 +604,8 @@
594
604
  font-weight: bold;
595
605
  font-size: 0.8em;
596
606
  padding-right: 20px;
607
+
608
+ word-wrap: break-word;
597
609
  }
598
610
 
599
611
  .sub table td pre {
@@ -608,6 +620,12 @@
608
620
  white-space: normal;
609
621
  }
610
622
 
623
+ /* "(object doesn't support inspect)" */
624
+ .sub .unsupported {
625
+ font-family: sans-serif;
626
+ color: #777;
627
+ }
628
+
611
629
  /* ---------------------------------------------------------------------
612
630
  * Scrollbar
613
631
  * --------------------------------------------------------------------- */
@@ -662,16 +680,7 @@
662
680
  </nav>
663
681
  <ul class="frames">
664
682
  <% backtrace_frames.each_with_index do |frame, index| %>
665
- <li
666
- class="<%= frame.context %>"
667
- style="display: none"
668
- data-context="<%= frame.context %>"
669
- data-full-filename="<%= frame.filename %>"
670
- data-filename="<%= frame.pretty_path %>"
671
- data-line="<%= frame.line %>"
672
- data-name="<%= frame.name %>"
673
- data-index="<%= index %>"
674
- >
683
+ <li class="<%= frame.context %>" style="display: none" data-context="<%= frame.context %>" data-index="<%= index %>">
675
684
  <span class='stroke'></span>
676
685
  <i class="icon <%= frame.context %>"></i>
677
686
  <div class="info">
@@ -688,41 +697,8 @@
688
697
  </nav>
689
698
 
690
699
  <% backtrace_frames.each_with_index do |frame, index| %>
691
- <div class="frame_info" id="frame_info_<%= index %>" style="display:none;">
692
- <header class="trace_info">
693
- <div class="title">
694
- <h2 class="name"><%= frame.name %></h2>
695
- <div class="location"><span class="filename"><%= frame.pretty_path %></span></div>
696
- </div>
697
-
698
- <%== highlighted_code_block frame %>
699
-
700
- <% if BetterErrors.binding_of_caller_available? && frame.frame_binding %>
701
- <div class="repl">
702
- <div class="console">
703
- <pre></pre>
704
- <div class="prompt"><span>&gt;&gt;</span> <input/></div>
705
- </div>
706
- </div>
707
- <% end %>
708
- </header>
709
-
710
- <% if BetterErrors.binding_of_caller_available? && frame.frame_binding %>
711
- <div class="hint">
712
- This a live shell. Type in here.
713
- </div>
714
-
715
- <div class="variable_info"></div>
716
- <% end %>
717
-
718
- <% unless BetterErrors.binding_of_caller_available? %>
719
- <div class="hint">
720
- <strong>Tip:</strong> add <code>gem "binding_of_caller"</code> to your Gemfile to enable the REPL and local/instance variable inspection.
721
- </div>
722
- <% end %>
723
- </div>
700
+ <div class="frame_info" id="frame_info_<%= index %>" style="display:none;"></div>
724
701
  <% end %>
725
- <div style="clear:both"></div>
726
702
  </section>
727
703
  </body>
728
704
  <script>
@@ -736,7 +712,7 @@
736
712
 
737
713
  function apiCall(method, opts, cb) {
738
714
  var req = new XMLHttpRequest();
739
- req.open("POST", "/__better_errors/" + OID + "/" + method, true);
715
+ req.open("POST", <%== uri_prefix.gsub("<", "&lt;").inspect %> + "/__better_errors/" + OID + "/" + method, true);
740
716
  req.setRequestHeader("Content-Type", "application/json");
741
717
  req.send(JSON.stringify(opts));
742
718
  req.onreadystatechange = function() {
@@ -859,34 +835,41 @@
859
835
  }
860
836
  };
861
837
 
862
- function populateVariableInfo(index) {
838
+ function switchTo(el) {
839
+ if(previousFrameInfo) previousFrameInfo.style.display = "none";
840
+ previousFrameInfo = el;
841
+
842
+ el.style.display = "block";
843
+
844
+ var replInput = el.querySelector('.console input');
845
+ if (replInput) replInput.focus();
846
+ }
847
+
848
+ function selectFrameInfo(index) {
863
849
  var el = allFrameInfos[index];
864
- var varInfo = el.querySelector(".variable_info");
865
- if(varInfo && varInfo.innerHTML == "") {
850
+ if(el) {
851
+ if (el.loaded) {
852
+ return switchTo(el);
853
+ }
854
+
866
855
  apiCall("variables", { "index": index }, function(response) {
856
+ el.loaded = true;
867
857
  if(response.error) {
868
- varInfo.innerHTML = "<span class='error'>" + escapeHTML(response.error) + "</span>";
858
+ el.innerHTML = "<span class='error'>" + escapeHTML(response.error) + "</span>";
869
859
  } else {
870
- varInfo.innerHTML = response.html;
860
+ el.innerHTML = response.html;
861
+
862
+ var repl = el.querySelector(".repl .console");
863
+ if(repl) {
864
+ new REPL(index).install(repl);
865
+ }
866
+
867
+ switchTo(el);
871
868
  }
872
869
  });
873
870
  }
874
871
  }
875
872
 
876
- function selectFrameInfo(index) {
877
- populateVariableInfo(index);
878
-
879
- if(previousFrameInfo) {
880
- previousFrameInfo.style.display = "none";
881
- }
882
- previousFrameInfo = allFrameInfos[index];
883
- previousFrameInfo.style.display = "block";
884
-
885
- if(REPL.all[index]) {
886
- REPL.all[index].focus();
887
- }
888
- }
889
-
890
873
  for(var i = 0; i < allFrames.length; i++) {
891
874
  (function(i, el) {
892
875
  var el = allFrames[i];
@@ -899,10 +882,6 @@
899
882
 
900
883
  selectFrameInfo(el.attributes["data-index"].value);
901
884
  };
902
- var repl = allFrameInfos[i].querySelector(".repl .console");
903
- if(repl) {
904
- new REPL(i).install(repl);
905
- }
906
885
  })(i);
907
886
  }
908
887
 
@@ -910,7 +889,7 @@
910
889
  (
911
890
  document.querySelector(".frames li.application") ||
912
891
  document.querySelector(".frames li")
913
- ).click();
892
+ ).onclick();
914
893
 
915
894
  var applicationFramesButton = document.getElementById("application_frames");
916
895
  var allFramesButton = document.getElementById("all_frames");
@@ -937,7 +916,7 @@
937
916
  return false;
938
917
  };
939
918
 
940
- applicationFramesButton.click();
919
+ applicationFramesButton.onclick();
941
920
  })();
942
921
  </script>
943
922
  </html>
@@ -1,9 +1,55 @@
1
+ <header class="trace_info">
2
+ <div class="title">
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>
5
+ </div>
6
+
7
+ <%== highlighted_code_block @frame %>
8
+
9
+ <% if BetterErrors.binding_of_caller_available? && @frame.frame_binding %>
10
+ <div class="repl">
11
+ <div class="console">
12
+ <pre></pre>
13
+ <div class="prompt"><span>&gt;&gt;</span> <input/></div>
14
+ </div>
15
+ </div>
16
+ <% end %>
17
+ </header>
18
+
19
+ <% if BetterErrors.binding_of_caller_available? && @frame.frame_binding %>
20
+ <div class="hint">
21
+ This a live shell. Type in here.
22
+ </div>
23
+
24
+ <div class="variable_info"></div>
25
+ <% end %>
26
+
27
+ <% unless BetterErrors.binding_of_caller_available? %>
28
+ <div class="hint">
29
+ <strong>Tip:</strong> add <code>gem "binding_of_caller"</code> to your Gemfile to enable the REPL and local/instance variable inspection.
30
+ </div>
31
+ <% end %>
32
+
33
+ <div class="sub">
34
+ <h3>Request info</h3>
35
+ <div class='inset variables'>
36
+ <table class="var_table">
37
+ <% if rails_params %>
38
+ <tr><td class="name">Request parameters</td><td><pre><%== inspect_value rails_params %></pre></td></tr>
39
+ <% end %>
40
+ <% if rack_session %>
41
+ <tr><td class="name">Rack session</td><td><pre><%== inspect_value rack_session %></pre></td></tr>
42
+ <% end %>
43
+ </table>
44
+ </div>
45
+ </div>
46
+
1
47
  <div class="sub">
2
48
  <h3>Local Variables</h3>
3
49
  <div class='inset variables'>
4
50
  <table class="var_table">
5
51
  <% @frame.local_variables.each do |name, value| %>
6
- <tr><td class="name"><%= name %></td><td><pre><%= value.inspect %></pre></td></tr>
52
+ <tr><td class="name"><%= name %></td><td><pre><%== inspect_value value %></pre></td></tr>
7
53
  <% end %>
8
54
  </table>
9
55
  </div>
@@ -14,8 +60,10 @@
14
60
  <div class="inset variables">
15
61
  <table class="var_table">
16
62
  <% @frame.instance_variables.each do |name, value| %>
17
- <tr><td class="name"><%= name %></td><td><pre><%= value.inspect %></pre></td></tr>
63
+ <tr><td class="name"><%= name %></td><td><pre><%== inspect_value value %></pre></td></tr>
18
64
  <% end %>
19
65
  </table>
20
66
  </div>
21
67
  </div>
68
+
69
+ <!-- <%= Time.now.to_f - @var_start_time %> seconds -->
@@ -1,3 +1,3 @@
1
1
  module BetterErrors
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -2,9 +2,9 @@ require "spec_helper"
2
2
 
3
3
  module BetterErrors
4
4
  describe ErrorPage do
5
- let(:exception) { raise ZeroDivisionError, "you divided by zero you silly goose!" rescue $! }
5
+ let!(:exception) { raise ZeroDivisionError, "you divided by zero you silly goose!" rescue $! }
6
6
 
7
- let(:error_page) { ErrorPage.new exception, { "REQUEST_PATH" => "/some/path" } }
7
+ let(:error_page) { ErrorPage.new exception, { "PATH_INFO" => "/some/path" } }
8
8
 
9
9
  let(:response) { error_page.render }
10
10
 
@@ -2,10 +2,30 @@ require "spec_helper"
2
2
 
3
3
  module BetterErrors
4
4
  describe Middleware do
5
+ let(:app) { Middleware.new(->env { ":)" }) }
6
+
5
7
  it "should pass non-error responses through" do
6
- app = Middleware.new(->env { ":)" })
7
8
  app.call({}).should == ":)"
8
9
  end
10
+
11
+ it "should call the internal methods" do
12
+ app.should_receive :internal_call
13
+ app.call("PATH_INFO" => "/__better_errors/1/preform_awesomness")
14
+ end
15
+
16
+ it "should show the error page" do
17
+ app.should_receive :show_error_page
18
+ app.call("PATH_INFO" => "/__better_errors/")
19
+ end
20
+
21
+ context "when requesting the /__better_errors manually" do
22
+ let(:app) { Middleware.new(->env { ":)" }) }
23
+
24
+ it "should show that no errors have been recorded" do
25
+ status, headers, body = app.call("PATH_INFO" => "/__better_errors")
26
+ body.join.should match /No errors have been recorded yet./
27
+ end
28
+ end
9
29
 
10
30
  context "when handling an error" do
11
31
  let(:app) { Middleware.new(->env { raise "oh no :(" }) }
@@ -56,6 +56,29 @@ module BetterErrors
56
56
 
57
57
  frame.gem_path.should == "(gem) whatever-1.2.3/lib/whatever.rb"
58
58
  end
59
+
60
+ it "should prioritize gem path over application path" do
61
+ BetterErrors.stub!(:application_root).and_return("/abc/xyz")
62
+ Gem.stub!(:path).and_return(["/abc/xyz/vendor"])
63
+ frame = StackFrame.new("/abc/xyz/vendor/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
64
+
65
+ frame.gem_path.should == "(gem) whatever-1.2.3/lib/whatever.rb"
66
+ end
67
+ end
68
+
69
+ context "#pretty_path" do
70
+ it "should return #application_path for application paths" do
71
+ BetterErrors.stub!(:application_root).and_return("/abc/xyz")
72
+ frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
73
+ frame.pretty_path.should == frame.application_path
74
+ end
75
+
76
+ it "should return #gem_path for gem paths" do
77
+ Gem.stub!(:path).and_return(["/abc/xyz"])
78
+ frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
79
+
80
+ frame.pretty_path.should == frame.gem_path
81
+ end
59
82
  end
60
83
 
61
84
  it "should special case SyntaxErrors" do
@@ -80,5 +103,19 @@ module BetterErrors
80
103
  frames.first.filename.should == "foo.rb"
81
104
  frames.first.line.should == 123
82
105
  end
106
+
107
+ it "should ignore a backtrace line if its format doesn't make any sense at all" do
108
+ error = StandardError.new
109
+ error.stub!(:backtrace).and_return(["foo.rb:123:in `foo'", "C:in `find'", "bar.rb:123:in `bar'"])
110
+ frames = StackFrame.from_exception(error)
111
+ frames.count.should == 2
112
+ end
113
+
114
+ it "should not blow up if a filename contains a colon" do
115
+ error = StandardError.new
116
+ error.stub!(:backtrace).and_return(["crap:filename.rb:123"])
117
+ frames = StackFrame.from_exception(error)
118
+ frames.first.filename.should == "crap:filename.rb"
119
+ end
83
120
  end
84
121
  end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ describe BetterErrors do
4
+ context ".editor" do
5
+ it "should default to textmate" do
6
+ subject.editor["foo.rb", 123].should == "txmt://open?url=file://foo.rb&line=123"
7
+ end
8
+
9
+ it "should url escape the filename" do
10
+ subject.editor["&.rb", 0].should == "txmt://open?url=file://%26.rb&line=0"
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,6 @@
1
+ require "simplecov"
2
+ SimpleCov.start
3
+
1
4
  $: << File.expand_path("../../lib", __FILE__)
2
5
  require "better_errors"
3
6
  require "ostruct"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_errors
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-11 00:00:00.000000000 Z
12
+ date: 2012-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -27,6 +27,70 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.12.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.12.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: binding_of_caller
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: simplecov
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: yard
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
30
94
  - !ruby/object:Gem::Dependency
31
95
  name: erubis
32
96
  requirement: !ruby/object:Gem::Requirement
@@ -69,6 +133,8 @@ extensions: []
69
133
  extra_rdoc_files: []
70
134
  files:
71
135
  - .gitignore
136
+ - .yardopts
137
+ - CONTRIBUTING.md
72
138
  - Gemfile
73
139
  - LICENSE.txt
74
140
  - README.md
@@ -77,6 +143,7 @@ files:
77
143
  - lib/better_errors.rb
78
144
  - lib/better_errors/code_formatter.rb
79
145
  - lib/better_errors/core_ext/exception.rb
146
+ - lib/better_errors/disable_logging_middleware.rb
80
147
  - lib/better_errors/error_page.rb
81
148
  - lib/better_errors/middleware.rb
82
149
  - lib/better_errors/rails.rb
@@ -93,6 +160,7 @@ files:
93
160
  - spec/better_errors/repl/basic_spec.rb
94
161
  - spec/better_errors/stack_frame_spec.rb
95
162
  - spec/better_errors/support/my_source.rb
163
+ - spec/better_errors_spec.rb
96
164
  - spec/spec_helper.rb
97
165
  homepage: https://github.com/charliesome/better_errors
98
166
  licenses:
@@ -126,5 +194,6 @@ test_files:
126
194
  - spec/better_errors/repl/basic_spec.rb
127
195
  - spec/better_errors/stack_frame_spec.rb
128
196
  - spec/better_errors/support/my_source.rb
197
+ - spec/better_errors_spec.rb
129
198
  - spec/spec_helper.rb
130
199
  has_rdoc: