party_foul 0.6.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,6 +6,14 @@
6
6
 
7
7
  Rails exceptions automatically opened as issues on Github
8
8
 
9
+ ## Looking for help? ##
10
+
11
+ If it is a bug [please open an issue on
12
+ Github](https://github.com/dockyard/party_foul/issues). If you need help using
13
+ the gem please ask the question on
14
+ [Stack Overflow](http://stackoverflow.com). Be sure to tag the
15
+ question with `DockYard` so we can find it.
16
+
9
17
  ## About ##
10
18
 
11
19
  `PartyFoul` captures exceptions in your application and does the
@@ -99,21 +107,47 @@ curl -u <github_login> -i -d "{ \"scopes\": [\"repo\"] }" \
99
107
  https://api.github.com/authorizations
100
108
  ```
101
109
 
102
- Add as the very last middleware in your production `Rack` stack.
103
-
104
110
  ## Customization ##
105
111
 
112
+ ### Labels ###
113
+
114
+ You can specify an additional array of labels that will be applied to the issues PartyFoul creates.
115
+
116
+ ```ruby
117
+ PartyFoul.configure do |config|
118
+ config.additional_labels = ['front-end']
119
+ end
120
+ ```
121
+
122
+ You can also provide a Proc that is passed the exception and the environment.
123
+
124
+ ```ruby
125
+ PartyFoul.configure do |config|
126
+ config.additional_labels = Proc.new do |exception, env|
127
+ labels = if env["HTTP_HOST"] =~ /beta\./
128
+ ['beta']
129
+ else
130
+ ['production']
131
+ end
132
+ if exception.message =~ /PG::Error/
133
+ labels << 'database'
134
+ end
135
+ labels
136
+ end
137
+ end
138
+ ```
139
+
106
140
  ### Background Processing ###
107
141
 
108
142
  You can specify the adapter with which the exceptions should be
109
143
  handled. By default, PartyFoul includes the
110
- [`PartyFoul::SyncAdapter`](https://github.com/dockyard/party_foul/tree/master/lib/party_foul/sync_adapter.rb)
144
+ [`PartyFoul::Processors::Sync`](https://github.com/dockyard/party_foul/tree/master/lib/party_foul/processors/sync.rb)
111
145
  which handles the exception synchronously. To use your own adapter,
112
146
  include the following in your `PartyFoul.configure` block:
113
147
 
114
148
  ```ruby
115
149
  PartyFoul.configure do |config|
116
- config.adapter = PartyFoul::Processors::MyBackgroundProcessor
150
+ config.processor = PartyFoul::Processors::MyBackgroundProcessor
117
151
  end
118
152
 
119
153
  class PartyFoul::Processors::MyBackgroundProcessor
@@ -125,8 +159,41 @@ end
125
159
 
126
160
  ```
127
161
 
128
- ### Using PartyFoul with Sidekiq
162
+ `PartyFoul` comes with the following background processing adapters:
163
+
164
+ * [PartyFoul::Processors::Sidekiq](https://github.com/dockyard/party_foul/blob/master/lib/party_foul/processors/sidekiq.rb)
165
+ * [PartyFoul::Processors::Resque](https://github.com/dockyard/party_foul/blob/master/lib/party_foul/processors/resque.rb)
166
+ * [PartyFoul::Processors::DelayedJob](https://github.com/dockyard/party_foul/blob/master/lib/party_foul/processors/delayed_job.rb)
167
+
168
+ These adapters are not loaded by default. You must explicitly require if
169
+ you want to use:
170
+
171
+ ```ruby
172
+ require 'party_foul/processors/sidekiq'
173
+
174
+ PartyFoul.configure do |config|
175
+ config.processor = PartyFoul::Processors::Sidekiq
176
+ end
177
+ ```
178
+
179
+ ## Tracking errors outside of an HTTP request
180
+
181
+ You may want to track errors outside of a reqular HTTP stack. In that
182
+ case you will need to make sure of the
183
+ `PartyFoul::RacklessExceptionHandler`.
184
+
185
+ The code that you want to handle should be wrapped like so:
186
+
187
+ ```ruby
188
+ begin
189
+ ... # some code that might raise an error
190
+ rescue => e
191
+ PartyFoul::RacklessExceptionHandler.handle(e, {class: class_name, method: method_name, params: message)
192
+ raise e
193
+ end
194
+ ```
129
195
 
196
+ ### Tracking errors in a Sidekiq worker
130
197
  In order to use PartyFoul for exception handling with Sidekiq you will need to create an initializer with some middleware configuration. The following example is based on using [Sidekiq with another exception notifiier server](https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/sidekiq.rb).
131
198
 
132
199
  File: config/initializers/partyfoul_sidekiq.rb
@@ -22,4 +22,11 @@ PartyFoul.configure do |config|
22
22
 
23
23
  # The branch for your deployed code
24
24
  # config.branch = 'master'
25
+
26
+ # Additional labels to add to issues created
27
+ # config.additional_labels = ['production']
28
+ # or
29
+ # config.additional_labels = Proc.new do |exception, env|
30
+ # []
31
+ # end
25
32
  end
@@ -46,7 +46,7 @@ class PartyFoul::ExceptionHandler
46
46
  # Will create a new issue and a comment with the proper details. All issues are labeled as 'bug'.
47
47
  def create_issue
48
48
  self.sha = PartyFoul.github.git_data.references.get(PartyFoul.owner, PartyFoul.repo, "heads/#{PartyFoul.branch}").object.sha
49
- issue = PartyFoul.github.issues.create(PartyFoul.owner, PartyFoul.repo, title: rendered_issue.title, body: rendered_issue.body, labels: ['bug'])
49
+ issue = PartyFoul.github.issues.create(PartyFoul.owner, PartyFoul.repo, title: rendered_issue.title, body: rendered_issue.body, labels: ['bug'] + rendered_issue.labels)
50
50
  PartyFoul.github.issues.comments.create(PartyFoul.owner, PartyFoul.repo, issue['number'], body: rendered_issue.comment)
51
51
  end
52
52
 
@@ -58,7 +58,7 @@ class PartyFoul::ExceptionHandler
58
58
  params = {body: rendered_issue.update_body(issue['body']), state: 'open'}
59
59
 
60
60
  if issue['state'] == 'closed'
61
- params[:labels] = ['bug', 'regression']
61
+ params[:labels] = (['bug', 'regression'] + issue['labels']).uniq
62
62
  end
63
63
 
64
64
  self.sha = PartyFoul.github.git_data.references.get(PartyFoul.owner, PartyFoul.repo, "heads/#{PartyFoul.branch}").object.sha
@@ -127,6 +127,17 @@ BODY
127
127
  end
128
128
  end
129
129
 
130
+ # Provides additional labels using the configured options
131
+ #
132
+ # @return [Array]
133
+ def labels
134
+ if PartyFoul.additional_labels.respond_to? :call
135
+ PartyFoul.additional_labels.call(self.exception, self.env) || []
136
+ else
137
+ PartyFoul.additional_labels || []
138
+ end
139
+ end
140
+
130
141
  private
131
142
 
132
143
  def app_root
@@ -0,0 +1,16 @@
1
+ require 'party_foul/processors/base'
2
+
3
+ class PartyFoul::Processors::DelayedJob < PartyFoul::Processors::Base
4
+ @queue = 'party_foul'
5
+
6
+ # Passes the exception and rack env data to DelayedJob to be processed later
7
+ #
8
+ # @param [Exception, Hash]
9
+ def self.handle(exception, env)
10
+ new.delay(queue: @queue).perform(Marshal.dump(exception), Marshal.dump(env))
11
+ end
12
+
13
+ def perform(exception, env)
14
+ PartyFoul::ExceptionHandler.new(Marshal.load(exception), Marshal.load(env)).run
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'party_foul/processors/base'
2
+
3
+ class PartyFoul::Processors::Resque < PartyFoul::Processors::Base
4
+ @queue = :party_foul
5
+
6
+ # Passes the exception and rack env data to Resque to be processed later
7
+ #
8
+ # @param [Exception, Hash]
9
+ def self.handle(exception, env)
10
+ Resque.enqueue(PartyFoul::Processors::Resque, Marshal.dump(exception), Marshal.dump(env))
11
+ end
12
+
13
+ def self.perform(exception, env)
14
+ PartyFoul::ExceptionHandler.new(Marshal.load(exception), Marshal.load(env)).run
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'party_foul/processors/base'
2
+
3
+ class PartyFoul::Processors::Sidekiq < PartyFoul::Processors::Base
4
+ include Sidekiq::Worker
5
+ sidekiq_options queue: 'party_foul'
6
+
7
+ # Passes the exception and rack env data to Sidekiq to be processed later
8
+ #
9
+ # @param [Exception, Hash]
10
+ def self.handle(exception, env)
11
+ perform_async(Marshal.dump(exception), Marshal.dump(env))
12
+ end
13
+
14
+ def perform(exception, env)
15
+ PartyFoul::ExceptionHandler.new(Marshal.load(exception), Marshal.load(env)).run
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module PartyFoul
2
- VERSION = '0.6.0'
2
+ VERSION = '1.0.0'
3
3
  end
data/lib/party_foul.rb CHANGED
@@ -2,7 +2,7 @@ require 'github_api'
2
2
 
3
3
  module PartyFoul
4
4
  class << self
5
- attr_accessor :github, :oauth_token, :endpoint, :owner, :repo, :blacklisted_exceptions, :processor, :web_url, :branch, :whitelisted_rack_variables
5
+ attr_accessor :github, :oauth_token, :endpoint, :owner, :repo, :blacklisted_exceptions, :processor, :web_url, :branch, :whitelisted_rack_variables, :additional_labels
6
6
  end
7
7
 
8
8
  def self.whitelisted_rack_variables
@@ -31,6 +31,65 @@ describe 'Party Foul Exception Handler' do
31
31
  PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
32
32
  PartyFoul::ExceptionHandler.new(nil, {}).run
33
33
  end
34
+
35
+ context 'when additional labels are configured' do
36
+ before do
37
+ PartyFoul.configure do |config|
38
+ config.additional_labels = ['custom', 'label']
39
+ end
40
+ end
41
+ after do
42
+ clean_up_party
43
+ end
44
+ it 'will open a new error on Github with the additional labels' do
45
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:body).returns('Test Body')
46
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:comment).returns('Test Comment')
47
+ PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
48
+ PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: []))
49
+ PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug', 'custom', 'label']).returns(Hashie::Mash.new('number' => 1))
50
+ PartyFoul.github.git_data.references.expects(:get).with('test_owner', 'test_repo', 'heads/deploy').returns(Hashie::Mash.new(object: Hashie::Mash.new(sha: 'abcdefg1234567890')))
51
+ PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
52
+ PartyFoul::ExceptionHandler.new(nil, {}).run
53
+ end
54
+ end
55
+
56
+ context 'when a proc for additional labels are configured' do
57
+ before do
58
+ PartyFoul.configure do |config|
59
+ config.additional_labels = Proc.new do |exception, env|
60
+ if env[:http_host] =~ /beta\./
61
+ ['beta']
62
+ elsif exception.message =~ /Database/
63
+ ['database_error']
64
+ end
65
+ end
66
+ end
67
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:body).returns('Test Body')
68
+ PartyFoul::IssueRenderers::Rails.any_instance.stubs(:comment).returns('Test Comment')
69
+ PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
70
+ PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: []))
71
+ PartyFoul.github.git_data.references.expects(:get).with('test_owner', 'test_repo', 'heads/deploy').returns(Hashie::Mash.new(object: Hashie::Mash.new(sha: 'abcdefg1234567890')))
72
+ PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
73
+ end
74
+ after do
75
+ clean_up_party
76
+ end
77
+
78
+ it 'will open a new error on Github with the default labels if no additional labels are returned from the proc' do
79
+ PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug']).returns(Hashie::Mash.new('number' => 1))
80
+ PartyFoul::ExceptionHandler.new(stub(:message => ''), {}).run
81
+ end
82
+
83
+ it 'will open a new error on Github with the additional labels based on the exception message' do
84
+ PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug', 'database_error']).returns(Hashie::Mash.new('number' => 1))
85
+ PartyFoul::ExceptionHandler.new(stub(:message => 'Database'), {}).run
86
+ end
87
+
88
+ it 'will open a new error on Github with the additional labels based on the env' do
89
+ PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug', 'beta']).returns(Hashie::Mash.new('number' => 1))
90
+ PartyFoul::ExceptionHandler.new(stub(:message => ''), {:http_host => 'beta.example.com'}).run
91
+ end
92
+ end
34
93
  end
35
94
 
36
95
  context 'when error is not new' do
@@ -52,8 +111,8 @@ describe 'Party Foul Exception Handler' do
52
111
  context 'and closed' do
53
112
  it 'will update the count on the body and re-open the issue' do
54
113
  PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
55
- PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: [{title: 'Test Title', body: 'Test Body', state: 'closed', number: 1}]))
56
- PartyFoul.github.issues.expects(:edit).with('test_owner', 'test_repo', 1, body: 'New Body', state: 'open', labels: ['bug', 'regression'])
114
+ PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: [{title: 'Test Title', body: 'Test Body', state: 'closed', number: 1, labels: ['staging']}]))
115
+ PartyFoul.github.issues.expects(:edit).with('test_owner', 'test_repo', 1, body: 'New Body', state: 'open', labels: ['bug', 'regression', 'staging'])
57
116
  PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
58
117
  PartyFoul.github.git_data.references.expects(:get).with('test_owner', 'test_repo', 'heads/deploy').returns(Hashie::Mash.new(object: Hashie::Mash.new(sha: 'abcdefg1234567890')))
59
118
  PartyFoul::ExceptionHandler.new(nil, {}).run
data/test/test_helper.rb CHANGED
@@ -24,7 +24,7 @@ module MiniTest::Expectations
24
24
  end
25
25
 
26
26
  def clean_up_party
27
- %w{github oauth_token endpoint owner repo blacklisted_exceptions processor web_url branch}.each do |attr|
27
+ %w{github oauth_token endpoint owner repo blacklisted_exceptions processor web_url branch additional_labels}.each do |attr|
28
28
  PartyFoul.send("#{attr}=", nil)
29
29
  end
30
30
  end
@@ -22,4 +22,11 @@ PartyFoul.configure do |config|
22
22
 
23
23
  # The branch for your deployed code
24
24
  # config.branch = 'master'
25
+
26
+ # Additional labels to add to issues created
27
+ # config.additional_labels = ['production']
28
+ # or
29
+ # config.additional_labels = Proc.new do |exception, env|
30
+ # []
31
+ # end
25
32
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: party_foul
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-02-04 00:00:00.000000000 Z
13
+ date: 2013-02-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: github_api
@@ -190,6 +190,9 @@ files:
190
190
  - lib/party_foul/issue_renderers.rb
191
191
  - lib/party_foul/middleware.rb
192
192
  - lib/party_foul/processors/base.rb
193
+ - lib/party_foul/processors/delayed_job.rb
194
+ - lib/party_foul/processors/resque.rb
195
+ - lib/party_foul/processors/sidekiq.rb
193
196
  - lib/party_foul/processors/sync.rb
194
197
  - lib/party_foul/processors.rb
195
198
  - lib/party_foul/rackless_exception_handler.rb
@@ -222,7 +225,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
222
225
  version: '0'
223
226
  segments:
224
227
  - 0
225
- hash: -845978866647605957
228
+ hash: 764634010609561347
226
229
  required_rubygems_version: !ruby/object:Gem::Requirement
227
230
  none: false
228
231
  requirements:
@@ -231,7 +234,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
231
234
  version: '0'
232
235
  segments:
233
236
  - 0
234
- hash: -845978866647605957
237
+ hash: 764634010609561347
235
238
  requirements: []
236
239
  rubyforge_project:
237
240
  rubygems_version: 1.8.23