honeybadger 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- honeybadger (1.1.0)
4
+ honeybadger (1.2.0)
5
5
  activesupport
6
6
  json
7
7
 
@@ -23,7 +23,7 @@ GEM
23
23
  net-sftp (>= 2.0.0)
24
24
  net-ssh (>= 2.0.14)
25
25
  net-ssh-gateway (>= 1.1.0)
26
- coderay (1.0.6)
26
+ coderay (1.0.7)
27
27
  cucumber (0.10.7)
28
28
  builder (>= 2.1.2)
29
29
  diff-lcs (>= 1.1.2)
@@ -35,7 +35,7 @@ GEM
35
35
  ffi (1.0.11)
36
36
  gherkin (2.4.21)
37
37
  json (>= 1.4.6)
38
- guard (1.2.1)
38
+ guard (1.2.3)
39
39
  listen (>= 0.4.2)
40
40
  thor (>= 0.14.6)
41
41
  guard-test (0.5.0)
@@ -43,12 +43,12 @@ GEM
43
43
  test-unit (~> 2.2)
44
44
  highline (1.6.13)
45
45
  json (1.7.3)
46
- listen (0.4.6)
46
+ listen (0.4.7)
47
47
  rb-fchange (~> 0.0.5)
48
48
  rb-fsevent (~> 0.9.1)
49
49
  rb-inotify (~> 0.8.8)
50
50
  metaclass (0.0.1)
51
- method_source (0.7.1)
51
+ method_source (0.8)
52
52
  mocha (0.10.5)
53
53
  metaclass (~> 0.0.1)
54
54
  multi_json (1.3.6)
@@ -59,10 +59,10 @@ GEM
59
59
  net-ssh (2.5.2)
60
60
  net-ssh-gateway (1.1.0)
61
61
  net-ssh (>= 1.99.1)
62
- pry (0.9.9.6)
62
+ pry (0.9.10)
63
63
  coderay (~> 1.0.5)
64
- method_source (~> 0.7.1)
65
- slop (>= 2.4.4, < 3)
64
+ method_source (~> 0.8)
65
+ slop (~> 3.3.1)
66
66
  rack (1.1.3)
67
67
  rake (0.9.2.2)
68
68
  rb-fchange (0.0.5)
@@ -85,10 +85,10 @@ GEM
85
85
  multi_json (~> 1.0)
86
86
  simplecov-html (~> 0.5.3)
87
87
  simplecov-html (0.5.3)
88
- slop (2.4.4)
88
+ slop (3.3.2)
89
89
  term-ansicolor (1.0.7)
90
- test-unit (2.5.0)
91
- thor (0.15.3)
90
+ test-unit (2.5.1)
91
+ thor (0.15.4)
92
92
 
93
93
  PLATFORMS
94
94
  ruby
data/README.md CHANGED
@@ -14,8 +14,12 @@ Add the Honeybadger gem to your gemfile:
14
14
 
15
15
  gem 'honeybadger'
16
16
 
17
- Create an initializer in config/initializers and configure your API key
18
- for your project:
17
+ Then generate the initializer:
18
+
19
+ rails generate honeybadger --api-key <Your Api Key>
20
+
21
+ If you prefer to manually create the initializer, that's simple enough.
22
+ Just put the code below in config/initializers/honeybadger.rb
19
23
 
20
24
  # Uncomment the following line if running lower than Rails 3.2
21
25
  # require 'honeybadger/rails'
@@ -30,7 +34,7 @@ That's it!
30
34
  Add the honeybadger gem to your app. In config/environment.rb:
31
35
 
32
36
  config.gem 'honeybadger'
33
-
37
+
34
38
  or if you are using bundler:
35
39
 
36
40
  gem 'honeybadger', :require => 'honeybadger/rails'
@@ -67,7 +71,7 @@ middleware:
67
71
  app = Rack::Builder.app do
68
72
  run lambda { |env| raise "Rack down" }
69
73
  end
70
-
74
+
71
75
  use Honeybadger::Rack
72
76
  run app
73
77
 
@@ -90,7 +94,7 @@ Using honeybadger in a Sinatra app is just like a Rack app:
90
94
 
91
95
  ## Usage
92
96
 
93
- For the most part, Honeybadger works for itself.
97
+ For the most part, Honeybadger works for itself.
94
98
 
95
99
  It intercepts the exception middleware calls, sends notifications and continues the middleware call chain.
96
100
 
@@ -120,6 +124,23 @@ this rake task (from RAILS_ROOT):
120
124
  If everything is configured properly, that task will send a notice to Honeybadger
121
125
  which will be visible immediately.
122
126
 
127
+ ## User Tracking
128
+
129
+ Honeybadger will automatically try to associate errors and notices with the current user.
130
+
131
+ By default, it tries to call `current_user` when rescuing exceptions in ActionController.
132
+ If you want to use a different method, you may do so in the honeybadger initializer:
133
+
134
+ Honeybadger.configure do |config|
135
+ ...
136
+ # The current user method to call for errors rescued in ActionController
137
+ config.current_user_method = :a_controller_method_which_returns_the_user
138
+ end
139
+
140
+ Honeybadger assumes that the object returned by `current_user` will respond to `#id`,
141
+ and will optionally include the user's email address if the user object also responds
142
+ to `#email`.
143
+
123
144
  ## Going beyond exceptions
124
145
 
125
146
  You can also pass a hash to `Honeybadger.notify` method and store whatever you want,
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
3
  require 'date'
4
+ require 'json'
4
5
  begin
5
6
  require 'cucumber/rake/task'
6
7
  rescue LoadError
@@ -193,6 +194,14 @@ namespace :cucumber do
193
194
  end
194
195
  end
195
196
 
197
+ desc "Update supported versions: Run this to pull down latest rails versions from rubygems"
198
+ task :update_supported_versions do
199
+ down_to = Gem::Version.new('3.0.0')
200
+ versions = JSON.parse `curl https://rubygems.org/api/v1/versions/rails.json 2> /dev/null`
201
+ supported_versions = versions.map { |v| Gem::Version.new(v['number']) }.reject { |v| v < down_to || v.prerelease? }.sort
202
+ `echo '#{supported_versions.join("\n")}' > SUPPORTED_RAILS_VERSIONS`
203
+ end
204
+
196
205
  #############################################################################
197
206
  #
198
207
  # Packaging tasks
@@ -11,11 +11,20 @@
11
11
  3.0.10
12
12
  3.0.11
13
13
  3.0.12
14
+ 3.0.13
15
+ 3.0.14
16
+ 3.0.15
17
+ 3.0.16
18
+ 3.0.17
14
19
  3.1.0
15
20
  3.1.1
16
21
  3.1.2
17
22
  3.1.3
18
23
  3.1.4
24
+ 3.1.5
25
+ 3.1.6
26
+ 3.1.7
27
+ 3.1.8
19
28
  3.2.0
20
29
  3.2.1
21
30
  3.2.2
@@ -23,3 +32,5 @@
23
32
  3.2.4
24
33
  3.2.5
25
34
  3.2.6
35
+ 3.2.7
36
+ 3.2.8
@@ -199,6 +199,17 @@ Feature: Install the Gem in a Rails application
199
199
  And I perform a request to "http://example.com:123/test/index?param=value"
200
200
  Then I should receive a Honeybadger notification
201
201
 
202
+ Scenario: Notify honeybadger within a metal controller
203
+ When I configure the Honeybadger shim
204
+ And I configure usage of Honeybadger
205
+ And I define a metal response for "TestController#index":
206
+ """
207
+ raise RuntimeError, "some message"
208
+ """
209
+ And I route "/test/index" to "test#index"
210
+ And I perform a request to "http://example.com:123/test/index?param=value"
211
+ Then I should receive a Honeybadger notification
212
+
202
213
  Scenario: Reporting 404s
203
214
  When I configure the Honeybadger shim
204
215
  And I configure usage of Honeybadger
@@ -195,12 +195,12 @@ When /^I install the "([^\"]*)" plugin$/ do |plugin_name|
195
195
  FileUtils.mkdir_p("#{rails_root}/vendor/plugins/#{plugin_name}")
196
196
  end
197
197
 
198
- When /^I define a response for "([^\"]*)":$/ do |controller_and_action, definition|
198
+ When /^I define a( metal)? response for "([^\"]*)":$/ do |metal, controller_and_action, definition|
199
199
  controller_class_name, action = controller_and_action.split('#')
200
200
  controller_name = controller_class_name.underscore
201
201
  controller_file_name = File.join(rails_root, 'app', 'controllers', "#{controller_name}.rb")
202
202
  File.open(controller_file_name, "w") do |file|
203
- file.puts "class #{controller_class_name} < ApplicationController"
203
+ file.puts "class #{controller_class_name} < #{ metal ? 'ActionController::Metal' : 'ApplicationController'}"
204
204
  file.puts "def consider_all_requests_local; false; end"
205
205
  file.puts "def local_request?; false; end"
206
206
  file.puts "def #{action}"
@@ -66,7 +66,7 @@ class HoneybadgerGenerator < Rails::Generator::Base
66
66
  end
67
67
 
68
68
  def heroku_api_key
69
- heroku_var("(hoptoad|honeybadger)_api_key",options[:app]).split.find {|x| x unless x.blank?}
69
+ heroku_var("(honeybadger)_api_key",options[:app]).split.find {|x| x unless x.blank?}
70
70
  end
71
71
 
72
72
  def heroku?
data/honeybadger.gemspec CHANGED
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
4
4
  s.rubygems_version = '1.3.5'
5
5
 
6
6
  s.name = 'honeybadger'
7
- s.version = '1.1.0'
8
- s.date = '2012-07-26'
7
+ s.version = '1.2.0'
8
+ s.date = '2012-09-21'
9
9
 
10
10
  s.summary = "Error reports you can be happy about."
11
11
  s.description = "Make managing application errors a more pleasant experience."
data/lib/honeybadger.rb CHANGED
@@ -19,7 +19,7 @@ require 'honeybadger/sender'
19
19
  require 'honeybadger/railtie' if defined?(Rails::Railtie)
20
20
 
21
21
  module Honeybadger
22
- VERSION = '1.1.0'
22
+ VERSION = '1.2.0'
23
23
  LOG_PREFIX = "** [Honeybadger] "
24
24
 
25
25
  HEADERS = {
@@ -16,25 +16,42 @@ module Honeybadger
16
16
  # Public: The method of the line (such as index)
17
17
  attr_reader :method
18
18
 
19
+ # Public: Filtered representations
20
+ attr_reader :filtered_file, :filtered_number, :filtered_method
21
+
19
22
  # Public: Parses a single line of a given backtrace
20
23
  #
21
24
  # unparsed_line - The raw line from +caller+ or some backtrace
22
25
  #
23
26
  # Returns the parsed backtrace line
24
- def self.parse(unparsed_line)
25
- _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
26
- new(file, number, method)
27
+ def self.parse(unparsed_line, opts = {})
28
+ filters = opts[:filters] || []
29
+ filtered_line = filters.inject(unparsed_line) do |line, proc|
30
+ proc.call(line)
31
+ end
32
+
33
+ if filtered_line
34
+ _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
35
+ _, *filtered_args = filtered_line.match(INPUT_FORMAT).to_a
36
+ new(file, number, method, *filtered_args)
37
+ else
38
+ nil
39
+ end
27
40
  end
28
41
 
29
- def initialize(file, number, method)
30
- self.file = file
31
- self.number = number
32
- self.method = method
42
+ def initialize(file, number, method, filtered_file = file,
43
+ filtered_number = number, filtered_method = method)
44
+ self.filtered_file = filtered_file
45
+ self.filtered_number = filtered_number
46
+ self.filtered_method = filtered_method
47
+ self.file = file
48
+ self.number = number
49
+ self.method = method
33
50
  end
34
51
 
35
52
  # Public: Reconstructs the line in a readable fashion
36
53
  def to_s
37
- "#{file}:#{number}:in `#{method}'"
54
+ "#{filtered_file}:#{filtered_number}:in `#{filtered_method}'"
38
55
  end
39
56
 
40
57
  def ==(other)
@@ -45,9 +62,35 @@ module Honeybadger
45
62
  "<Line:#{to_s}>"
46
63
  end
47
64
 
65
+ # Public: An excerpt from the source file, lazily loaded to preserve
66
+ # performance
67
+ def source(radius = 2)
68
+ @source ||= get_source(file, number, radius)
69
+ end
70
+
48
71
  private
49
72
 
50
- attr_writer :file, :number, :method
73
+ attr_writer :file, :number, :method, :filtered_file, :filtered_number, :filtered_method
74
+
75
+ # Private: Open source file and read line(s)
76
+ #
77
+ # Returns an array of line(s) from source file
78
+ def get_source(file, number, radius = 2)
79
+ if file && File.exists?(file)
80
+ before = after = radius
81
+ start = (number.to_i - 1) - before
82
+ start = 0 and before = 1 if start <= 0
83
+ duration = before + 1 + after
84
+
85
+ l = 0
86
+ File.open(file) do |f|
87
+ start.times { f.gets ; l += 1 }
88
+ return Hash[duration.times.map { (line = f.gets) ? [(l += 1), line] : nil }.compact]
89
+ end
90
+ else
91
+ {}
92
+ end
93
+ end
51
94
  end
52
95
 
53
96
  # Public: holder for an Array of Backtrace::Line instances
@@ -56,17 +99,10 @@ module Honeybadger
56
99
  def self.parse(ruby_backtrace, opts = {})
57
100
  ruby_lines = split_multiline_backtrace(ruby_backtrace)
58
101
 
59
- filters = opts[:filters] || []
60
- filtered_lines = ruby_lines.to_a.map do |line|
61
- filters.inject(line) do |line, proc|
62
- proc.call(line)
63
- end
102
+ lines = ruby_lines.collect do |unparsed_line|
103
+ Line.parse(unparsed_line, opts)
64
104
  end.compact
65
105
 
66
- lines = filtered_lines.collect do |unparsed_line|
67
- Line.parse(unparsed_line)
68
- end
69
-
70
106
  instance = new(lines)
71
107
  end
72
108
 
@@ -5,7 +5,7 @@ module Honeybadger
5
5
  :ignore_user_agent, :notifier_name, :notifier_url, :notifier_version,
6
6
  :params_filters, :project_root, :port, :protocol, :proxy_host, :proxy_pass,
7
7
  :proxy_port, :proxy_user, :secure, :use_system_ssl_cert_chain, :framework,
8
- :user_information, :rescue_rake_exceptions].freeze
8
+ :user_information, :rescue_rake_exceptions, :source_extract_radius].freeze
9
9
 
10
10
  # The API key for your project, found on the project edit form.
11
11
  attr_accessor :api_key
@@ -84,15 +84,21 @@ module Honeybadger
84
84
  # The framework Honeybadger is configured to use
85
85
  attr_accessor :framework
86
86
 
87
+ # The current user method to call for errors rescued in ActionController
88
+ attr_accessor :current_user_method
89
+
87
90
  # Should Honeybadger catch exceptions from Rake tasks?
88
91
  # (boolean or nil; set to nil to catch exceptions when rake isn't running from a terminal; default is nil)
89
92
  attr_accessor :rescue_rake_exceptions
90
93
 
94
+ # The radius around trace line to include in source excerpt
95
+ attr_accessor :source_extract_radius
96
+
91
97
  DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
92
98
 
93
99
  DEFAULT_BACKTRACE_FILTERS = [
94
100
  lambda { |line|
95
- if defined?(Honeybadger.configuration.project_root) && Honeybadger.configuration.project_root.to_s != ''
101
+ if defined?(Honeybadger.configuration.project_root) && Honeybadger.configuration.project_root.to_s != ''
96
102
  line.sub(/#{Honeybadger.configuration.project_root}/, "[PROJECT_ROOT]")
97
103
  else
98
104
  line
@@ -138,6 +144,8 @@ module Honeybadger
138
144
  @framework = 'Standalone'
139
145
  @user_information = 'Honeybadger Error {{error_id}}'
140
146
  @rescue_rake_exceptions = nil
147
+ @current_user_method = :current_user
148
+ @source_extract_radius = 2
141
149
  end
142
150
 
143
151
  # Public: Takes a block and adds it to the list of backtrace filters. When
@@ -11,6 +11,12 @@ module Honeybadger
11
11
  # The name of the class of error (such as RuntimeError)
12
12
  attr_reader :error_class
13
13
 
14
+ # Excerpt from source file
15
+ attr_reader :source_extract
16
+
17
+ # The number of lines of context to include before and after source excerpt
18
+ attr_reader :source_extract_radius
19
+
14
20
  # The name of the server environment (such as "production")
15
21
  attr_reader :environment_name
16
22
 
@@ -64,6 +70,9 @@ module Honeybadger
64
70
  # The host name where this error occurred (if any)
65
71
  attr_reader :hostname
66
72
 
73
+ # The user affected by the exception, if available
74
+ attr_reader :user
75
+
67
76
  def initialize(args)
68
77
  self.args = args
69
78
  self.exception = args[:exception]
@@ -87,13 +96,17 @@ module Honeybadger
87
96
 
88
97
  self.environment_name = args[:environment_name]
89
98
  self.cgi_data = args[:cgi_data] || args[:rack_env]
99
+ self.user = args[:user]
90
100
  self.backtrace = Backtrace.parse(exception_attribute(:backtrace, caller), :filters => self.backtrace_filters)
91
101
  self.error_class = exception_attribute(:error_class) {|exception| exception.class.name }
92
102
  self.error_message = exception_attribute(:error_message, 'Notification') do |exception|
93
103
  "#{exception.class.name}: #{exception.message}"
94
104
  end
95
105
 
96
- self.hostname = local_hostname
106
+ self.hostname = local_hostname
107
+
108
+ self.source_extract_radius = args[:source_extract_radius] || 2
109
+ self.source_extract = extract_source_from_backtrace
97
110
 
98
111
  also_use_rack_params_filters
99
112
  find_session_data
@@ -114,7 +127,8 @@ module Honeybadger
114
127
  :error => {
115
128
  :class => error_class,
116
129
  :message => error_message,
117
- :backtrace => backtrace
130
+ :backtrace => backtrace,
131
+ :source => source_extract
118
132
  },
119
133
  :request => {
120
134
  :url => url,
@@ -122,7 +136,8 @@ module Honeybadger
122
136
  :action => action,
123
137
  :params => parameters,
124
138
  :session => session_data,
125
- :cgi_data => cgi_data
139
+ :cgi_data => cgi_data,
140
+ :user => user
126
141
  },
127
142
  :server => {
128
143
  :project_root => project_root,
@@ -169,7 +184,8 @@ module Honeybadger
169
184
  :backtrace_filters, :parameters, :params_filters, :environment_filters,
170
185
  :session_data, :project_root, :url, :ignore, :ignore_by_filters,
171
186
  :notifier_name, :notifier_url, :notifier_version, :component, :action,
172
- :cgi_data, :environment_name, :hostname
187
+ :cgi_data, :environment_name, :hostname, :user, :source_extract,
188
+ :source_extract_radius
173
189
 
174
190
  # Private: Arguments given in the initializer
175
191
  attr_accessor :args
@@ -247,6 +263,23 @@ module Honeybadger
247
263
  end
248
264
  end
249
265
 
266
+ def extract_source_from_backtrace
267
+ if backtrace.lines.empty?
268
+ nil
269
+ else
270
+ # ActionView::Template::Error has its own source_extract method.
271
+ # If present, use that instead.
272
+ if exception.respond_to?(:source_extract)
273
+ Hash[exception_attribute(:source_extract).split("\n").map do |line|
274
+ parts = line.split(': ')
275
+ [parts[0].strip, parts[1] || '']
276
+ end]
277
+ else
278
+ backtrace.lines.first.source(source_extract_radius)
279
+ end
280
+ end
281
+ end
282
+
250
283
  def filter(hash)
251
284
  if params_filters
252
285
  hash.each do |key, value|
@@ -7,7 +7,8 @@ module Honeybadger
7
7
  :controller => params[:controller],
8
8
  :action => params[:action],
9
9
  :url => honeybadger_request_url,
10
- :cgi_data => honeybadger_filter_if_filtering(request.env) }
10
+ :cgi_data => honeybadger_filter_if_filtering(request.env),
11
+ :user => honeybadger_user_info }
11
12
  end
12
13
 
13
14
  private
@@ -54,6 +55,17 @@ module Honeybadger
54
55
  end
55
56
  end
56
57
 
58
+ def honeybadger_user_info
59
+ method = Honeybadger.configuration.current_user_method
60
+
61
+ if respond_to?(method) && user = send(method)
62
+ {
63
+ :id => user.id,
64
+ :email => user.respond_to?(:email) ? user.email : 'N/A'
65
+ }
66
+ end
67
+ end
68
+
57
69
  def honeybadger_request_url
58
70
  url = "#{request.protocol}#{request.host}"
59
71
 
@@ -17,7 +17,7 @@ module Honeybadger
17
17
  controller = env['action_controller.instance']
18
18
  env['honeybadger.error_id'] = Honeybadger.
19
19
  notify_or_ignore(exception,
20
- (controller.try(:honeybadger_request_data) || {:rack_env => env})) unless skip_user_agent?(env)
20
+ (controller.respond_to?(:honeybadger_request_data) ? controller.honeybadger_request_data : {:rack_env => env})) unless skip_user_agent?(env)
21
21
  if defined?(controller.rescue_action_in_public_without_honeybadger)
22
22
  controller.rescue_action_in_public_without_honeybadger(exception)
23
23
  end
@@ -24,7 +24,7 @@ namespace :honeybadger do
24
24
  end
25
25
 
26
26
  heroku_rails_env = heroku_var("rails_env")
27
- heroku_api_key = heroku_var("(honeybadger)_api_key").split.find {|x| x unless x.blank?} ||
27
+ heroku_api_key = heroku_var("honeybadger_api_key").split.find {|x| x unless x.blank?} ||
28
28
  Honeybadger.configuration.api_key
29
29
 
30
30
  command = %Q(heroku addons:add deployhooks:http --url="https://api.honeybadger.io/v1/deploys?deploy[environment]=#{heroku_rails_env}&api_key=#{heroku_api_key}")
@@ -57,8 +57,13 @@ module HoneybadgerTasks
57
57
  else
58
58
  response = http.request(post)
59
59
 
60
- puts response.body
61
- return Net::HTTPSuccess === response
60
+ if Net::HTTPSuccess === response
61
+ puts "Succesfully recorded deployment"
62
+ return true
63
+ else
64
+ puts response.body
65
+ return false
66
+ end
62
67
  end
63
68
  end
64
69
  end
@@ -76,7 +76,7 @@ class HoneybadgerGenerator < Rails::Generators::Base
76
76
  end
77
77
 
78
78
  def heroku_api_key
79
- heroku_var("(hoptoad|honeybadger)_api_key",options[:app]).split.find {|x| x unless x.blank?}
79
+ heroku_var("honeybadger_api_key",options[:app]).split.find {|x| x unless x.blank?}
80
80
  end
81
81
 
82
82
  def heroku?
@@ -1,4 +1,5 @@
1
1
  require 'test_helper'
2
+ require 'stringio'
2
3
 
3
4
  class BacktraceTest < Honeybadger::UnitTest
4
5
  should "parse a backtrace into lines" do
@@ -55,6 +56,66 @@ class BacktraceTest < Honeybadger::UnitTest
55
56
  assert_equal expected_backtrace, original_backtrace
56
57
  end
57
58
 
59
+ context "when source file exists" do
60
+ setup do
61
+ source = <<-RUBY
62
+ $:<<'lib'
63
+ require 'honeybadger'
64
+
65
+ begin
66
+ raise StandardError
67
+ rescue => e
68
+ puts Honeybadger::Notice.new(exception: e).backtrace.to_json
69
+ end
70
+ RUBY
71
+
72
+ array = [
73
+ "app/models/user.rb:2:in `magic'",
74
+ "app/concerns/authenticated_controller.rb:4:in `authorize'",
75
+ "app/controllers/users_controller.rb:8:in `index'"
76
+ ]
77
+
78
+ ['app/models/user.rb', 'app/concerns/authenticated_controller.rb', 'app/controllers/users_controller.rb'].each do |file|
79
+ File.expects(:exists?).with(file).returns true
80
+ File.expects(:open).with(file).yields StringIO.new(source)
81
+ end
82
+
83
+ @backtrace = Honeybadger::Backtrace.parse(array)
84
+ end
85
+
86
+ should "include a snippet from the source file for each line of the backtrace" do
87
+ assert_equal 4, @backtrace.lines.first.source.keys.size
88
+ assert_match /\$:<</, @backtrace.lines.first.source[1]
89
+ assert_match /require/, @backtrace.lines.first.source[2]
90
+ assert_match /\n/, @backtrace.lines.first.source[3]
91
+ assert_match /begin/, @backtrace.lines.first.source[4]
92
+
93
+ assert_equal 5, @backtrace.lines.second.source.keys.size
94
+ assert_match /require/, @backtrace.lines.second.source[2]
95
+ assert_match /\n/, @backtrace.lines.second.source[3]
96
+ assert_match /begin/, @backtrace.lines.second.source[4]
97
+ assert_match /StandardError/, @backtrace.lines.second.source[5]
98
+ assert_match /rescue/, @backtrace.lines.second.source[6]
99
+
100
+ assert_equal 3, @backtrace.lines.third.source.keys.size
101
+ assert_match /rescue/, @backtrace.lines.third.source[6]
102
+ assert_match /Honeybadger/, @backtrace.lines.third.source[7]
103
+ assert_match /end/, @backtrace.lines.third.source[8]
104
+ end
105
+ end
106
+
107
+ should "fail gracefully when looking up snippet and file doesn't exist" do
108
+ array = [
109
+ "app/models/user.rb:13:in `magic'",
110
+ "app/controllers/users_controller.rb:8:in `index'"
111
+ ]
112
+
113
+ backtrace = Honeybadger::Backtrace.parse(array)
114
+
115
+ assert_empty backtrace.lines.first.source
116
+ assert_empty backtrace.lines.second.source
117
+ end
118
+
58
119
  context "with a project root" do
59
120
  setup do
60
121
  @project_root = '/some/path'
@@ -20,7 +20,7 @@ class CapistranoTest < Honeybadger::UnitTest
20
20
  should "log when calling honeybadger:deploy task" do
21
21
  @configuration.set(:current_revision, '084505b1c0e0bcf1526e673bb6ac99fbcb18aecc')
22
22
  @configuration.set(:repository, 'repository')
23
- @configuration.set(:current_release, '/home/deploy/rails_app/hoptoad')
23
+ @configuration.set(:current_release, '/home/deploy/rails_app/honeybadger')
24
24
  io = StringIO.new
25
25
  logger = Capistrano::Logger.new(:output => io)
26
26
  logger.level = Capistrano::Logger::MAX_LEVEL
@@ -27,6 +27,8 @@ class ConfigurationTest < Honeybadger::UnitTest
27
27
  assert_config_default :ignore,
28
28
  Honeybadger::Configuration::IGNORE_DEFAULT
29
29
  assert_config_default :framework, 'Standalone'
30
+ assert_config_default :current_user_method, :current_user
31
+ assert_config_default :source_extract_radius, 2
30
32
  end
31
33
 
32
34
  should "provide default values for secure connections" do
@@ -67,6 +69,7 @@ class ConfigurationTest < Honeybadger::UnitTest
67
69
  assert_config_overridable :notifier_url
68
70
  assert_config_overridable :environment_name
69
71
  assert_config_overridable :logger
72
+ assert_config_overridable :current_user_method
70
73
  end
71
74
 
72
75
  should "have an api key" do
@@ -96,7 +96,7 @@ class HoneybadgerTasksTest < Honeybadger::UnitTest
96
96
  end
97
97
 
98
98
  before_should "puts the response body on success" do
99
- HoneybadgerTasks.expects(:puts).with("body")
99
+ HoneybadgerTasks.expects(:puts).with("Succesfully recorded deployment")
100
100
  @http_proxy.expects(:request).with(any_parameters).returns(successful_response('body'))
101
101
  end
102
102
 
@@ -122,7 +122,7 @@ class HoneybadgerTasksTest < Honeybadger::UnitTest
122
122
 
123
123
  context "in a configured project with custom host" do
124
124
  setup do
125
- Honeybadger.configure do |config|
125
+ Honeybadger.configure do |config|
126
126
  config.api_key = "1234123412341234"
127
127
  config.host = "custom.host"
128
128
  config.secure = false
@@ -57,6 +57,10 @@ class NoticeTest < Honeybadger::UnitTest
57
57
  assert_equal 'index', build_notice(:action => 'index').action
58
58
  end
59
59
 
60
+ should "accept source excerpt radius" do
61
+ assert_equal 3, build_notice(:source_extract_radius => 3).source_extract_radius
62
+ end
63
+
60
64
  should "accept a url" do
61
65
  url = 'http://some.host/uri'
62
66
  notice = build_notice(:url => url)
@@ -68,31 +72,61 @@ class NoticeTest < Honeybadger::UnitTest
68
72
  assert_equal hostname, notice.hostname
69
73
  end
70
74
 
71
- should "accept a backtrace from an exception or hash" do
72
- array = ["user.rb:34:in `crazy'"]
73
- exception = build_exception
74
- exception.set_backtrace array
75
- backtrace = Honeybadger::Backtrace.parse(array)
76
- notice_from_exception = build_notice(:exception => exception)
75
+ context "with a backtrace" do
76
+ setup do
77
+ @backtrace_array = ['my/file/backtrace:3']
78
+ @exception = build_exception
79
+ @exception.set_backtrace(@backtrace_array)
80
+ end
81
+
82
+ should "accept a backtrace from an exception or hash" do
83
+ backtrace = Honeybadger::Backtrace.parse(@backtrace_array)
84
+ notice_from_exception = build_notice(:exception => @exception)
77
85
 
86
+ assert_equal backtrace,
87
+ notice_from_exception.backtrace,
88
+ "backtrace was not correctly set from an exception"
78
89
 
79
- assert_equal backtrace,
80
- notice_from_exception.backtrace,
81
- "backtrace was not correctly set from an exception"
90
+ notice_from_hash = build_notice(:backtrace => @backtrace_array)
91
+ assert_equal backtrace,
92
+ notice_from_hash.backtrace,
93
+ "backtrace was not correctly set from a hash"
94
+ end
82
95
 
83
- notice_from_hash = build_notice(:backtrace => array)
84
- assert_equal backtrace,
85
- notice_from_hash.backtrace,
86
- "backtrace was not correctly set from a hash"
96
+ should "pass its backtrace filters for parsing" do
97
+ Honeybadger::Backtrace.expects(:parse).with(@backtrace_array, {:filters => 'foo'}).returns(mock(:lines => []))
98
+
99
+ notice = Honeybadger::Notice.new({:exception => @exception, :backtrace_filters => 'foo'})
100
+ end
101
+
102
+ should "pass its backtrace line filters for parsing" do
103
+ Honeybadger::Backtrace::Line.expects(:parse).with(@backtrace_array.first, {:filters => 'foo'})
104
+
105
+ notice = Honeybadger::Notice.new({:exception => @exception, :backtrace_filters => 'foo'})
106
+ end
107
+
108
+ should "include source extract from backtrace" do
109
+ backtrace = Honeybadger::Backtrace.parse(@backtrace_array)
110
+ notice_from_exception = build_notice(:exception => @exception)
111
+
112
+ assert_equal backtrace.lines.first.source, notice_from_exception.source_extract
113
+ end
87
114
  end
88
115
 
89
- should "pass its backtrace filters for parsing" do
90
- backtrace_array = ['my/file/backtrace:3']
116
+ should "Use source extract from view when reporting an ActionView::Template::Error" do
117
+ # TODO: I would like to stub out a real ActionView::Template::Error, but we're
118
+ # currently locked at actionpack 2.3.8. Perhaps if one day we upgrade...
119
+ source = <<-ERB
120
+ 1: <%= current_user.name %>
121
+ 2: </div>
122
+ 3:
123
+ 4: <div>
124
+ ERB
91
125
  exception = build_exception
92
- exception.set_backtrace(backtrace_array)
93
- Honeybadger::Backtrace.expects(:parse).with(backtrace_array, {:filters => 'foo'})
126
+ exception.stubs(:source_extract).returns(source)
127
+ notice = Honeybadger::Notice.new({:exception => exception})
94
128
 
95
- notice = Honeybadger::Notice.new({:exception => exception, :backtrace_filters => 'foo'})
129
+ assert_equal({ '1' => ' <%= current_user.name %>', '2' => '</div>', '3' => '', '4' => '<div>'}, notice.source_extract)
96
130
  end
97
131
 
98
132
  should "set the error class from an exception or hash" do
@@ -135,6 +169,10 @@ class NoticeTest < Honeybadger::UnitTest
135
169
  assert_equal data, notice.cgi_data, "should take CGI data from a hash"
136
170
  end
137
171
 
172
+ should "accept user" do
173
+ assert_equal 'foo@bar.com', build_notice(:user => 'foo@bar.com').user
174
+ end
175
+
138
176
  should "accept notifier information" do
139
177
  params = { :notifier_name => 'a name for a notifier',
140
178
  :notifier_version => '1.0.5',
@@ -196,12 +234,21 @@ class NoticeTest < Honeybadger::UnitTest
196
234
  assert_nil notice.url
197
235
  assert_nil notice.controller
198
236
  assert_nil notice.action
237
+ assert_nil notice.user
199
238
 
200
239
  json = notice.to_json
201
240
  payload = JSON.parse(json)
202
241
  assert_nil payload['request']['url']
203
242
  assert_nil payload['request']['component']
204
243
  assert_nil payload['request']['action']
244
+ assert_nil payload['request']['user']
245
+ end
246
+
247
+ should "send user in request" do
248
+ notice = build_notice(:user => 'foo@bar.com')
249
+ json = notice.to_json
250
+ payload = JSON.parse(json)
251
+ assert_equal payload['request']['user'], 'foo@bar.com'
205
252
  end
206
253
 
207
254
  %w(url controller action).each do |var|
@@ -288,7 +335,6 @@ class NoticeTest < Honeybadger::UnitTest
288
335
  end
289
336
  end
290
337
 
291
-
292
338
  should "ensure #to_ary is called on objects that support it" do
293
339
  assert_nothing_raised do
294
340
  build_notice(:session => { :object => stub(:to_ary => {}) })
@@ -113,6 +113,7 @@ class ActionControllerCatcherTest < Honeybadger::UnitTest
113
113
  klass.local = opts[:local]
114
114
  controller = klass.new
115
115
  controller.stubs(:rescue_action_in_public_without_honeybadger)
116
+ controller.stubs(Honeybadger.configuration.current_user_method).returns(opts[:current_user]) if opts[:current_user]
116
117
  opts[:request].query_parameters = opts[:request].query_parameters.merge(opts[:params] || {})
117
118
  opts[:request].session = ActionController::TestSession.new(opts[:session] || {})
118
119
  # Prevents request.fullpath from crashing Rails in tests
@@ -297,4 +298,27 @@ class ActionControllerCatcherTest < Honeybadger::UnitTest
297
298
  assert_received(session, :data) { |expect| expect.at_least_once }
298
299
  assert_caught_and_sent
299
300
  end
301
+
302
+ should "call current_user_method if overridden" do
303
+ Honeybadger.configuration.current_user_method = :rockstar
304
+
305
+ current_user = mock( :id => 1 )
306
+ controller = process_action_with_automatic_notification(:current_user => current_user)
307
+ assert_received(controller, :rockstar) { |expect| expect.at_least_once }
308
+ assert_caught_and_sent
309
+ end
310
+
311
+ should "include current_user.id in request user data" do
312
+ current_user = stub( :id => 1 )
313
+ controller = process_action_with_automatic_notification(:current_user => current_user)
314
+ assert_equal({ :id => 1, :email => 'N/A' }, controller.honeybadger_request_data[:user])
315
+ assert_caught_and_sent
316
+ end
317
+
318
+ should "include current_user.email in request user data" do
319
+ current_user = stub( :id => 1, :email => 'foo@bar.com' )
320
+ controller = process_action_with_automatic_notification(:current_user => current_user)
321
+ assert_equal({ :id => 1, :email => 'foo@bar.com' }, controller.honeybadger_request_data[:user])
322
+ assert_caught_and_sent
323
+ end
300
324
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honeybadger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.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-07-26 00:00:00.000000000 Z
12
+ date: 2012-09-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json