party_foul 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -4,12 +4,12 @@
4
4
  [![Dependency Status](https://gemnasium.com/dockyard/party_foul.png?travis)](https://gemnasium.com/dockyard/party_foul)
5
5
  [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/dockyard/party_foul)
6
6
 
7
- Rails exceptions automatically opened as issues on Github
7
+ Rails exceptions automatically opened as issues on GitHub
8
8
 
9
9
  ## Looking for help? ##
10
10
 
11
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
12
+ GitHub](https://github.com/dockyard/party_foul/issues). If you need help using
13
13
  the gem please ask the question on
14
14
  [Stack Overflow](http://stackoverflow.com). Be sure to tag the
15
15
  question with `DockYard` so we can find it.
@@ -19,7 +19,7 @@ question with `DockYard` so we can find it.
19
19
  `PartyFoul` captures exceptions in your application and does the
20
20
  following:
21
21
 
22
- 1. Attempt to find a matching issue in your Github repo
22
+ 1. Attempt to find a matching issue in your GitHub repo
23
23
  2. If no matching issue is found an new issue is created with a
24
24
  unique title, session information, and stack trace. The issue is
25
25
  tagged as a `bug`. A new comment is added with relevant data on the
@@ -36,7 +36,7 @@ application state.
36
36
 
37
37
  ## Installation ##
38
38
 
39
- **Note** We highly recommend that you create a new Github account that is
39
+ **Note** We highly recommend that you create a new GitHub account that is
40
40
  a collaborator on your repository. Use this new account's credentials
41
41
  for the installation below. If you use your own account you will
42
42
  not receive emails when issues are created, updated, reopened, etc...
@@ -55,7 +55,7 @@ If you are using Rails you can run the install generator.
55
55
  rails g party_foul:install
56
56
  ```
57
57
 
58
- This prompts you for the Github credentials of the account that is
58
+ This prompts you for the GitHub credentials of the account that is
59
59
  opening the issues. The OAuth token for that account is stored
60
60
  in `config/initializers/party_foul.rb`. You may want to remove the token
61
61
  string and store in an environment variable. It is best not to store the
@@ -76,15 +76,15 @@ PartyFoul.configure do |config|
76
76
  # The constants here *must* be represented as strings
77
77
  config.blacklisted_exceptions = ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError']
78
78
 
79
- # The OAuth token for the account that is opening the issues on Github
79
+ # The OAuth token for the account that is opening the issues on GitHub
80
80
  config.oauth_token = 'abcdefgh1234567890'
81
81
 
82
- # The API endpoint for Github. Unless you are hosting a private
83
- # instance of Enterprise Github you do not need to include this
82
+ # The API endpoint for GitHub. Unless you are hosting a private
83
+ # instance of Enterprise GitHub you do not need to include this
84
84
  config.endpoint = 'https://api.github.com'
85
85
 
86
- # The Web URL for Github. Unless you are hosting a private
87
- # instance of Enterprise Github you do not need to include this
86
+ # The Web URL for GitHub. Unless you are hosting a private
87
+ # instance of Enterprise GitHub you do not need to include this
88
88
  config.web_url = 'https://github.com'
89
89
 
90
90
  # The organization or user that owns the target repository
@@ -176,6 +176,17 @@ PartyFoul.configure do |config|
176
176
  end
177
177
  ```
178
178
 
179
+ ### Limiting Comments
180
+
181
+ You can specify a limit on the number of comments added to each issue. The main issue will still be updated
182
+ with a count and time for each occurrence, regardless of the limit.
183
+
184
+ ```ruby
185
+ PartyFoul.configure do |config|
186
+ config.comment_limit = 10
187
+ end
188
+ ```
189
+
179
190
  ## Tracking errors outside of an HTTP request
180
191
 
181
192
  You may want to track errors outside of a reqular HTTP stack. In that
@@ -7,8 +7,8 @@ module PartyFoul
7
7
  source_root File.expand_path('../templates', __FILE__)
8
8
 
9
9
  def create_initializer_file
10
- login = ask 'Github login:'
11
- password = STDIN.noecho { ask 'Github password:' }
10
+ login = ask 'GitHub login:'
11
+ password = STDIN.noecho { ask 'GitHub password:' }
12
12
  @owner = ask_with_default "\nRepository owner:", login
13
13
  @repo = ask 'Repository name:'
14
14
  @endpoint = ask_with_default 'Api Endpoint:', 'https://api.github.com'
@@ -19,7 +19,7 @@ module PartyFoul
19
19
  @oauth_token = github.oauth.create(scopes: ['repo'], note: "PartyFoul #{@owner}/#{@repo}", note_url: "#{@web_url}/#{@owner}/#{@repo}").token
20
20
  template 'party_foul.rb', 'config/initializers/party_foul.rb'
21
21
  rescue Github::Error::Unauthorized
22
- say 'There was an error retrieving your Github OAuth token'
22
+ say 'There was an error retrieving your GitHub OAuth token'
23
23
  end
24
24
  end
25
25
 
@@ -3,15 +3,15 @@ PartyFoul.configure do |config|
3
3
  # The constants here *must* be represented as strings
4
4
  config.blacklisted_exceptions = ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError']
5
5
 
6
- # The OAuth token for the account that is opening the issues on Github
6
+ # The OAuth token for the account that is opening the issues on GitHub
7
7
  config.oauth_token = '<%= @oauth_token %>'
8
8
 
9
- # The API endpoint for Github. Unless you are hosting a private
10
- # instance of Enterprise Github you do not need to include this
9
+ # The API endpoint for GitHub. Unless you are hosting a private
10
+ # instance of Enterprise GitHub you do not need to include this
11
11
  config.endpoint = '<%= @endpoint %>'
12
12
 
13
- # The Web URL for Github. Unless you are hosting a private
14
- # instance of Enterprise Github you do not need to include this
13
+ # The Web URL for GitHub. Unless you are hosting a private
14
+ # instance of Enterprise GitHub you do not need to include this
15
15
  config.web_url = '<%= @web_url %>'
16
16
 
17
17
  # The organization or user that owns the target repository
@@ -29,4 +29,7 @@ PartyFoul.configure do |config|
29
29
  # config.additional_labels = Proc.new do |exception, env|
30
30
  # []
31
31
  # end
32
+
33
+ # Limit the number of comments per issue
34
+ # config.comment_limit = 10
32
35
  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, :additional_labels
5
+ attr_accessor :github, :oauth_token, :endpoint, :owner, :repo, :blacklisted_exceptions, :processor, :web_url, :branch, :whitelisted_rack_variables, :additional_labels, :comment_limit
6
6
  end
7
7
 
8
8
  def self.whitelisted_rack_variables
@@ -16,7 +16,7 @@ module PartyFoul
16
16
  @branch ||= 'master'
17
17
  end
18
18
 
19
- # The web url for Github. This is only interesting for Enterprise
19
+ # The web url for GitHub. This is only interesting for Enterprise
20
20
  # users
21
21
  #
22
22
  # @return [String] Defaults to 'https://github.com' if not set
@@ -24,7 +24,7 @@ module PartyFoul
24
24
  @web_url ||= 'https://github.com'
25
25
  end
26
26
 
27
- # The api endpoint for Github. This is only interesting for Enterprise
27
+ # The api endpoint for GitHub. This is only interesting for Enterprise
28
28
  # users
29
29
  #
30
30
  # @return [String] Defaults to 'https://api.github.com' if not set
@@ -71,7 +71,7 @@ module PartyFoul
71
71
  # config.oauth_token = ENV['oauth_token']
72
72
  # end
73
73
  #
74
- # Will also setup for Github api connections
74
+ # Will also setup for GitHub api connections
75
75
  #
76
76
  # @param [Block]
77
77
  def self.configure(&block)
@@ -24,7 +24,7 @@ class PartyFoul::ExceptionHandler
24
24
  self.rendered_issue = renderer_klass.new(exception, env)
25
25
  end
26
26
 
27
- # Begins to process the exception for Github Issues. Makes an attempt
27
+ # Begins to process the exception for GitHub Issues. Makes an attempt
28
28
  # to find the issue. If found will update the issue. If not found will create a new issue.
29
29
  def run
30
30
  if issue = find_issue
@@ -34,7 +34,7 @@ class PartyFoul::ExceptionHandler
34
34
  end
35
35
  end
36
36
 
37
- # Hits the Github API to find the matching issue using the fingerprint.
37
+ # Hits the GitHub API to find the matching issue using the fingerprint.
38
38
  def find_issue
39
39
  unless issue = PartyFoul.github.search.issues(owner: PartyFoul.owner, repo: PartyFoul.repo, state: 'open', keyword: fingerprint).issues.first
40
40
  issue = PartyFoul.github.search.issues(owner: PartyFoul.owner, repo: PartyFoul.repo, state: 'closed', keyword: fingerprint).issues.first
@@ -63,7 +63,9 @@ class PartyFoul::ExceptionHandler
63
63
 
64
64
  self.sha = PartyFoul.github.git_data.references.get(PartyFoul.owner, PartyFoul.repo, "heads/#{PartyFoul.branch}").object.sha
65
65
  PartyFoul.github.issues.edit(PartyFoul.owner, PartyFoul.repo, issue['number'], params)
66
- PartyFoul.github.issues.comments.create(PartyFoul.owner, PartyFoul.repo, issue['number'], body: rendered_issue.comment)
66
+ unless comment_limit_met?(issue['body'])
67
+ PartyFoul.github.issues.comments.create(PartyFoul.owner, PartyFoul.repo, issue['number'], body: rendered_issue.comment)
68
+ end
67
69
  end
68
70
  end
69
71
 
@@ -80,4 +82,13 @@ class PartyFoul::ExceptionHandler
80
82
  def sha=(sha)
81
83
  rendered_issue.sha = sha
82
84
  end
85
+
86
+ def occurrence_count(body)
87
+ result = body.match(/<th>Count<\/th><td>(\d+)<\/td>/)
88
+ result.nil? ? 0 : result[1].to_i
89
+ end
90
+
91
+ def comment_limit_met?(body)
92
+ !!PartyFoul.comment_limit && PartyFoul.comment_limit <= occurrence_count(body)
93
+ end
83
94
  end
@@ -1,7 +1,10 @@
1
+ require 'cgi'
2
+
1
3
  class PartyFoul::IssueRenderers::Base
2
4
  attr_accessor :exception, :env, :sha
5
+ attr_reader :body
3
6
 
4
- # A new renderer instance for Githug issues
7
+ # A new renderer instance for GitHub issues
5
8
  #
6
9
  # @param [Exception, Hash]
7
10
  def initialize(exception, env)
@@ -9,11 +12,11 @@ class PartyFoul::IssueRenderers::Base
9
12
  self.env = env
10
13
  end
11
14
 
12
- # Derived title of the issue. Must be implemented by the adapter class
15
+ # Title of the issue with any object ids masked
13
16
  #
14
- # @return [NotImplementedError]
17
+ # @return [String]
15
18
  def title
16
- raise NotImplementedError
19
+ raw_title.gsub(/#<(\w+):0x\w+?>/, "#<\\1:0xXXXXXX>")
17
20
  end
18
21
 
19
22
  # Renders the issue body
@@ -22,7 +25,7 @@ class PartyFoul::IssueRenderers::Base
22
25
  #
23
26
  # @return [String]
24
27
  def body
25
- <<-BODY
28
+ @body ||= <<-BODY
26
29
  #{build_table_from_hash(body_options)}
27
30
 
28
31
  ## Stack Trace
@@ -41,7 +44,7 @@ BODY
41
44
 
42
45
  # Compiles the stack trace for use in the issue body. Lines in the
43
46
  # stack trace that are part of the application will be rendered as
44
- # links to the relative file and line on Github based upon
47
+ # links to the relative file and line on GitHub based upon
45
48
  # {PartyFoul.web_url}, {PartyFoul.owner}, {PartyFoul.repo}, and
46
49
  # {PartyFoul.branch}. The branch will be used at the time the
47
50
  # exception happens to grab the SHA for that branch at that time for
@@ -76,7 +79,7 @@ BODY
76
79
  begin
77
80
  current_count = old_body.match(/<th>Count<\/th><td>(\d+)<\/td>/)[1].to_i
78
81
  old_body.sub!("<th>Count</th><td>#{current_count}</td>", "<th>Count</th><td>#{current_count + 1}</td>")
79
- old_body.sub!(/<th>Last Occurrence<\/th><td>.+<\/td>/, "<th>Last Occurrence</th><td>#{occurred_at}</td>")
82
+ old_body.sub!(/<th>Last Occurrence<\/th><td>.+?<\/td>/, "<th>Last Occurrence</th><td>#{occurred_at}</td>")
80
83
  old_body
81
84
  rescue
82
85
  self.body
@@ -87,7 +90,7 @@ BODY
87
90
  #
88
91
  # @return [String]
89
92
  def occurred_at
90
- Time.now.strftime('%B %d, %Y %H:%M:%S %z')
93
+ @occurred_at ||= Time.now.strftime('%B %d, %Y %H:%M:%S %z')
91
94
  end
92
95
 
93
96
  # The hash used for building the table in issue body
@@ -122,6 +125,8 @@ BODY
122
125
  key, value = row
123
126
  if row[1].kind_of?(Hash)
124
127
  value = build_table_from_hash(row[1])
128
+ else
129
+ value = CGI.escapeHTML(value.to_s)
125
130
  end
126
131
  rows << "<tr><th>#{key}</th><td>#{value}</td></tr>"
127
132
  end
@@ -147,4 +152,8 @@ BODY
147
152
  def extract_file_name_and_line_number(backtrace_line)
148
153
  backtrace_line.match(/#{app_root}\/((.+?):(\d+))/)
149
154
  end
155
+
156
+ def raw_title
157
+ raise NotImplementedError
158
+ end
150
159
  end
@@ -1,11 +1,4 @@
1
1
  class PartyFoul::IssueRenderers::Rack < PartyFoul::IssueRenderers::Base
2
- # Title for the issue comprised of (exception) "message"
3
- #
4
- # @return [String]
5
- def title
6
- %{(#{exception.class}) "#{exception.message}"}
7
- end
8
-
9
2
  def comment_options
10
3
  super.merge(URL: url, Params: params, Session: session, 'IP Address' => ip_address, 'HTTP Headers' => http_headers)
11
4
  end
@@ -42,4 +35,10 @@ class PartyFoul::IssueRenderers::Rack < PartyFoul::IssueRenderers::Base
42
35
  def http_headers
43
36
  { Version: env['HTTP_VERSION'], 'User Agent' => env['HTTP_USER_AGENT'], 'Accept Encoding' => env['HTTP_ACCEPT_ENCODING'], Accept: env['HTTP_ACCEPT'] }
44
37
  end
38
+
39
+ private
40
+
41
+ def raw_title
42
+ %{(#{exception.class}) "#{exception.message}"}
43
+ end
45
44
  end
@@ -13,14 +13,13 @@ class PartyFoul::IssueRenderers::Rackless < PartyFoul::IssueRenderers::Base
13
13
  env[:params]
14
14
  end
15
15
 
16
- # Title for the issue comprised of Controller#action (exception) "message"
17
- #
18
- # @return [String]
19
- def title
20
- %{#{env[:class]}##{env[:method]} (#{exception.class}) "#{exception.message}"}
21
- end
22
-
23
16
  def comment_options
24
17
  super.merge(Params: params)
25
18
  end
19
+
20
+ private
21
+
22
+ def raw_title
23
+ %{#{env[:class]}##{env[:method]} (#{exception.class}) "#{exception.message}"}
24
+ end
26
25
  end
@@ -1,11 +1,4 @@
1
1
  class PartyFoul::IssueRenderers::Rails < PartyFoul::IssueRenderers::Rack
2
- # Title for the issue comprised of Controller#action (exception) "message"
3
- #
4
- # @return [String]
5
- def title
6
- %{#{env['action_controller.instance'].class}##{env['action_dispatch.request.path_parameters']['action']} (#{exception.class}) "#{exception.message}"}
7
- end
8
-
9
2
  # Rails params hash. Filtered parms are respected.
10
3
  #
11
4
  # @return [Hash]
@@ -27,4 +20,8 @@ class PartyFoul::IssueRenderers::Rails < PartyFoul::IssueRenderers::Rack
27
20
  def app_root
28
21
  Rails.root
29
22
  end
23
+
24
+ def raw_title
25
+ %{#{env['action_controller.instance'].class}##{env['action_dispatch.request.path_parameters']['action']} (#{exception.class}) "#{exception.message}"}
26
+ end
30
27
  end
@@ -1,3 +1,3 @@
1
1
  module PartyFoul
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -15,6 +15,7 @@ describe 'Party Foul Confg' do
15
15
  config.owner = 'test_owner'
16
16
  config.repo = 'test_repo'
17
17
  config.branch = 'master'
18
+ config.comment_limit = 10
18
19
  end
19
20
 
20
21
  PartyFoul.blacklisted_exceptions.must_equal ['StandardError']
@@ -25,6 +26,7 @@ describe 'Party Foul Confg' do
25
26
  PartyFoul.repo.must_equal 'test_repo'
26
27
  PartyFoul.repo_url.must_equal 'http://example.com/test_owner/test_repo'
27
28
  PartyFoul.branch.must_equal 'master'
29
+ PartyFoul.comment_limit.must_equal 10
28
30
  end
29
31
 
30
32
  it 'has default values' do
@@ -21,7 +21,7 @@ describe 'Party Foul Exception Handler' do
21
21
  end
22
22
 
23
23
  context 'when error is new' do
24
- it 'will open a new error on Github' do
24
+ it 'will open a new error on GitHub' do
25
25
  PartyFoul::IssueRenderers::Rails.any_instance.stubs(:body).returns('Test Body')
26
26
  PartyFoul::IssueRenderers::Rails.any_instance.stubs(:comment).returns('Test Comment')
27
27
  PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
@@ -41,7 +41,7 @@ describe 'Party Foul Exception Handler' do
41
41
  after do
42
42
  clean_up_party
43
43
  end
44
- it 'will open a new error on Github with the additional labels' do
44
+ it 'will open a new error on GitHub with the additional labels' do
45
45
  PartyFoul::IssueRenderers::Rails.any_instance.stubs(:body).returns('Test Body')
46
46
  PartyFoul::IssueRenderers::Rails.any_instance.stubs(:comment).returns('Test Comment')
47
47
  PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
@@ -75,17 +75,17 @@ describe 'Party Foul Exception Handler' do
75
75
  clean_up_party
76
76
  end
77
77
 
78
- it 'will open a new error on Github with the default labels if no additional labels are returned from the proc' do
78
+ it 'will open a new error on GitHub with the default labels if no additional labels are returned from the proc' do
79
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
80
  PartyFoul::ExceptionHandler.new(stub(:message => ''), {}).run
81
81
  end
82
82
 
83
- it 'will open a new error on Github with the additional labels based on the exception message' do
83
+ it 'will open a new error on GitHub with the additional labels based on the exception message' do
84
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
85
  PartyFoul::ExceptionHandler.new(stub(:message => 'Database'), {}).run
86
86
  end
87
87
 
88
- it 'will open a new error on Github with the additional labels based on the env' do
88
+ it 'will open a new error on GitHub with the additional labels based on the env' do
89
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
90
  PartyFoul::ExceptionHandler.new(stub(:message => ''), {:http_host => 'beta.example.com'}).run
91
91
  end
@@ -99,11 +99,24 @@ describe 'Party Foul Exception Handler' do
99
99
  end
100
100
 
101
101
  context 'and open' do
102
- it 'will update the issue' do
102
+ before do
103
103
  PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: [{title: 'Test Title', body: 'Test Body', state: 'open', number: 1}]))
104
104
  PartyFoul.github.issues.expects(:edit).with('test_owner', 'test_repo', 1, body: 'New Body', state: 'open')
105
105
  PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
106
106
  PartyFoul.github.git_data.references.expects(:get).with('test_owner', 'test_repo', 'heads/deploy').returns(Hashie::Mash.new(object: Hashie::Mash.new(sha: 'abcdefg1234567890')))
107
+ end
108
+
109
+ it 'will update the issue' do
110
+ PartyFoul::ExceptionHandler.new(nil, {}).run
111
+ end
112
+
113
+ it "doesn't post a comment if the limit has been met" do
114
+ PartyFoul.configure do |config|
115
+ config.comment_limit = 10
116
+ end
117
+ PartyFoul::ExceptionHandler.any_instance.expects(:occurrence_count).returns(10)
118
+ PartyFoul.github.issues.comments.unstub(:create) # Necessary for the `never` expectation to work.
119
+ PartyFoul.github.issues.comments.expects(:create).never
107
120
  PartyFoul::ExceptionHandler.new(nil, {}).run
108
121
  end
109
122
  end
@@ -132,4 +145,51 @@ describe 'Party Foul Exception Handler' do
132
145
  PartyFoul::ExceptionHandler.new(nil, {}).run
133
146
  end
134
147
  end
148
+
149
+ describe '#occurrence_count' do
150
+ before do
151
+ @handler = PartyFoul::ExceptionHandler.new(nil, {})
152
+ end
153
+
154
+ it "returns the count" do
155
+ @handler.send(:occurrence_count, "<th>Count</th><td>1</td>").must_equal 1
156
+ end
157
+
158
+ it "returns 0 if no count is found" do
159
+ @handler.send(:occurrence_count, "Unexpected Body").must_equal 0
160
+ end
161
+ end
162
+
163
+ describe '#comment_limit_met?' do
164
+ before do
165
+ @handler = PartyFoul::ExceptionHandler.new(nil, {})
166
+ end
167
+
168
+ context "with no limit" do
169
+ it "returns false when there is no limit" do
170
+ PartyFoul.configure do |config|
171
+ config.comment_limit = nil
172
+ end
173
+ @handler.send(:comment_limit_met?, "").must_equal false
174
+ end
175
+ end
176
+
177
+ context "with a limit" do
178
+ before do
179
+ PartyFoul.configure do |config|
180
+ config.comment_limit = 10
181
+ end
182
+ end
183
+
184
+ it "returns false when there is a limit that has not been hit" do
185
+ @handler.stubs(:occurrence_count).returns(1)
186
+ @handler.send(:comment_limit_met?, "").must_equal false
187
+ end
188
+
189
+ it "returns true if the limit has been hit" do
190
+ @handler.stubs(:occurrence_count).returns(10)
191
+ @handler.send(:comment_limit_met?, "").must_equal true
192
+ end
193
+ end
194
+ end
135
195
  end
@@ -21,11 +21,11 @@ describe 'Party Foul Issue Renderer Base' do
21
21
  end
22
22
 
23
23
  it 'updates count and timestamp' do
24
- body = <<-BODY
24
+ body = <<-BODY.gsub(/\n/, '')
25
25
  <table>
26
26
  <tr><th>Exception</th><td>Test Exception</td></tr>
27
- <tr><th>Count</th><td>1</td></tr>
28
27
  <tr><th>Last Occurrence</th><td>January 01, 1970 00:00:00 -0500</td></tr>
28
+ <tr><th>Count</th><td>1</td></tr>
29
29
  </table>
30
30
 
31
31
  ## Stack Trace
@@ -35,11 +35,11 @@ Fingerprint: `abcdefg1234567890`
35
35
 
36
36
  Time.stubs(:now).returns(Time.new(1985, 10, 25, 1, 22, 0, '-05:00'))
37
37
 
38
- expected_body = <<-BODY
38
+ expected_body = <<-BODY.gsub(/\n/, '')
39
39
  <table>
40
40
  <tr><th>Exception</th><td>Test Exception</td></tr>
41
- <tr><th>Count</th><td>2</td></tr>
42
41
  <tr><th>Last Occurrence</th><td>October 25, 1985 01:22:00 -0500</td></tr>
42
+ <tr><th>Count</th><td>2</td></tr>
43
43
  </table>
44
44
 
45
45
  ## Stack Trace
@@ -79,6 +79,13 @@ Fingerprint: `abcdefg1234567890`
79
79
  expected = '<table><tr><th>Value 1</th><td>abc</td></tr><tr><th>Value 2</th><td><table><tr><th>Value A</th><td>123</td></tr><tr><th>Value B</th><td>456</td></tr></table></td></tr></table>'
80
80
  rendered_issue.build_table_from_hash(hash).must_equal expected
81
81
  end
82
+
83
+ it 'escapes HTML entities' do
84
+ rendered_issue = PartyFoul::IssueRenderers::Base.new(nil, nil)
85
+ hash = { 'Value 1' => 'Error in #<Foo>' }
86
+ expected = '<table><tr><th>Value 1</th><td>Error in #&lt;Foo&gt;</td></tr></table>'
87
+ rendered_issue.build_table_from_hash(hash).must_equal expected
88
+ end
82
89
  end
83
90
 
84
91
  describe '#fingerprint' do
@@ -88,4 +95,21 @@ Fingerprint: `abcdefg1234567890`
88
95
  rendered_issue.fingerprint.must_equal Digest::SHA1.hexdigest(rendered_issue.title)
89
96
  end
90
97
  end
98
+
99
+ describe '#occurred_at' do
100
+ it 'memoizes the time' do
101
+ rendered_issue = PartyFoul::IssueRenderers::Base.new(nil, nil)
102
+ expected = rendered_issue.occurred_at
103
+ Time.stubs(:now).returns(Time.new(1970, 1, 1, 0, 0, 1, '-05:00'))
104
+ rendered_issue.occurred_at.must_equal expected
105
+ end
106
+ end
107
+
108
+ describe '#title' do
109
+ it 'masks the object ids in the raw_title' do
110
+ rendered_issue = PartyFoul::IssueRenderers::Base.new(nil, nil)
111
+ rendered_issue.stubs(:raw_title).returns('Error for #<ClassName:0xabcdefg1234567>')
112
+ rendered_issue.title.must_equal 'Error for #<ClassName:0xXXXXXX>'
113
+ end
114
+ end
91
115
  end
@@ -11,14 +11,14 @@ describe 'Rack Issue Renderer' do
11
11
  end
12
12
  end
13
13
 
14
- describe '#title' do
14
+ describe '#raw_title' do
15
15
  before do
16
16
  @exception = Exception.new('message')
17
17
  end
18
18
 
19
19
  it 'constructs the title with the class and instance method' do
20
20
  @rendered_issue = PartyFoul::IssueRenderers::Rack.new(@exception, {})
21
- @rendered_issue.title.must_equal %{(Exception) "message"}
21
+ @rendered_issue.send(:raw_title).must_equal %{(Exception) "message"}
22
22
  end
23
23
  end
24
24
  end
@@ -16,14 +16,14 @@ describe 'Rackless Issue Renderer' do
16
16
  end
17
17
  end
18
18
 
19
- describe '#title' do
19
+ describe '#raw_title' do
20
20
  before do
21
21
  @exception = Exception.new('message')
22
22
  @rendered_issue = PartyFoul::IssueRenderers::Rackless.new(@exception, @env)
23
23
  end
24
24
 
25
25
  it 'constructs the title with the controller and action' do
26
- @rendered_issue.title.must_equal %{Worker#perform (Exception) "message"}
26
+ @rendered_issue.send(:raw_title).must_equal %{Worker#perform (Exception) "message"}
27
27
  end
28
28
  end
29
29
  end
@@ -23,7 +23,7 @@ describe 'Rails Issue Renderer' do
23
23
  end
24
24
  end
25
25
 
26
- describe '#title' do
26
+ describe '#raw_title' do
27
27
  before do
28
28
  @exception = Exception.new('message')
29
29
  controller_instance = mock('Controller')
@@ -36,7 +36,7 @@ describe 'Rails Issue Renderer' do
36
36
  end
37
37
 
38
38
  it 'constructs the title with the controller and action' do
39
- @rendered_issue.title.must_equal %{LandingController#index (Exception) "message"}
39
+ @rendered_issue.send(:raw_title).must_equal %{LandingController#index (Exception) "message"}
40
40
  end
41
41
  end
42
42
  end
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 additional_labels}.each do |attr|
27
+ %w{github oauth_token endpoint owner repo blacklisted_exceptions processor web_url branch additional_labels comment_limit}.each do |attr|
28
28
  PartyFoul.send("#{attr}=", nil)
29
29
  end
30
30
  end
@@ -3,15 +3,15 @@ PartyFoul.configure do |config|
3
3
  # The constants here *must* be represented as strings
4
4
  config.blacklisted_exceptions = ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError']
5
5
 
6
- # The OAuth token for the account that is opening the issues on Github
6
+ # The OAuth token for the account that is opening the issues on GitHub
7
7
  config.oauth_token = 'test_token'
8
8
 
9
- # The API endpoint for Github. Unless you are hosting a private
10
- # instance of Enterprise Github you do not need to include this
9
+ # The API endpoint for GitHub. Unless you are hosting a private
10
+ # instance of Enterprise GitHub you do not need to include this
11
11
  config.endpoint = 'http://api.example.com'
12
12
 
13
- # The Web URL for Github. Unless you are hosting a private
14
- # instance of Enterprise Github you do not need to include this
13
+ # The Web URL for GitHub. Unless you are hosting a private
14
+ # instance of Enterprise GitHub you do not need to include this
15
15
  config.web_url = 'http://example.com'
16
16
 
17
17
  # The organization or user that owns the target repository
@@ -29,4 +29,7 @@ PartyFoul.configure do |config|
29
29
  # config.additional_labels = Proc.new do |exception, env|
30
30
  # []
31
31
  # end
32
+
33
+ # Limit the number of comments per issue
34
+ # config.comment_limit = 10
32
35
  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: 1.0.0
4
+ version: 1.1.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-06 00:00:00.000000000 Z
13
+ date: 2013-02-16 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: github_api
@@ -172,7 +172,7 @@ dependencies:
172
172
  - - ! '>='
173
173
  - !ruby/object:Gem::Version
174
174
  version: '0'
175
- description: Auto-submit Rails exceptions as new isues on Github
175
+ description: Auto-submit Rails exceptions as new isues on GitHub
176
176
  email:
177
177
  - bcardarella@gmail.com
178
178
  - rubygems@danmcclain.net
@@ -225,7 +225,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
225
225
  version: '0'
226
226
  segments:
227
227
  - 0
228
- hash: 764634010609561347
228
+ hash: -627459678726509707
229
229
  required_rubygems_version: !ruby/object:Gem::Requirement
230
230
  none: false
231
231
  requirements:
@@ -234,13 +234,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
234
234
  version: '0'
235
235
  segments:
236
236
  - 0
237
- hash: 764634010609561347
237
+ hash: -627459678726509707
238
238
  requirements: []
239
239
  rubyforge_project:
240
240
  rubygems_version: 1.8.23
241
241
  signing_key:
242
242
  specification_version: 3
243
- summary: Auto-submit Rails exceptions as new isues on Github
243
+ summary: Auto-submit Rails exceptions as new isues on GitHub
244
244
  test_files:
245
245
  - test/generator_test.rb
246
246
  - test/party_foul/configure_test.rb