honeybadger 1.1.0 → 1.2.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.
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