party_foul 0.4.0 → 0.5.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/README.md +5 -3
- data/lib/generators/party_foul/templates/party_foul.rb +3 -0
- data/lib/party_foul/exception_handler.rb +22 -0
- data/lib/party_foul/issue_renderer.rb +50 -2
- data/lib/party_foul/issue_renderers/rack.rb +6 -0
- data/lib/party_foul/issue_renderers/rails.rb +6 -0
- data/lib/party_foul/processors/sync.rb +4 -0
- data/lib/party_foul/version.rb +1 -1
- data/lib/party_foul.rb +42 -1
- data/test/party_foul/configure_test.rb +7 -0
- data/test/party_foul/exception_handler_test.rb +6 -0
- data/test/test_helper.rb +1 -1
- data/test/tmp/config/initializers/party_foul.rb +3 -0
- metadata +4 -4
data/README.md
CHANGED
@@ -87,6 +87,9 @@ PartyFoul.configure do |config|
|
|
87
87
|
|
88
88
|
# The repository for this application
|
89
89
|
config.repo = 'repo_name'
|
90
|
+
|
91
|
+
# The branch for your deployed code
|
92
|
+
# config.branch = 'master'
|
90
93
|
end
|
91
94
|
```
|
92
95
|
|
@@ -147,9 +150,8 @@ the change in one of the [different issue renderer adapters](https://github.com/
|
|
147
150
|
|
148
151
|
## Authors ##
|
149
152
|
|
150
|
-
[Brian Cardarella](http://twitter.com/bcardarella)
|
151
|
-
|
152
|
-
[Dan McClain](http://twitter.com/_danmcclain)
|
153
|
+
* [Brian Cardarella](http://twitter.com/bcardarella)
|
154
|
+
* [Dan McClain](http://twitter.com/_danmcclain)
|
153
155
|
|
154
156
|
[We are very thankful for the many contributors](https://github.com/dockyard/party_foul/graphs/contributors)
|
155
157
|
|
@@ -1,10 +1,19 @@
|
|
1
1
|
class PartyFoul::ExceptionHandler
|
2
2
|
attr_accessor :rendered_issue
|
3
3
|
|
4
|
+
# This handler will pass the exception and env from Rack off to a processor.
|
5
|
+
# The default PartyFoul processor will work synchronously. Processor adapters can be written
|
6
|
+
# to push this logic to a background job if desired.
|
7
|
+
#
|
8
|
+
# @param [Exception, Hash]
|
4
9
|
def self.handle(exception, env)
|
5
10
|
PartyFoul.processor.handle(exception, env)
|
6
11
|
end
|
7
12
|
|
13
|
+
# Makes an attempt to determine what framework is being used and will use the proper
|
14
|
+
# IssueRenderer.
|
15
|
+
#
|
16
|
+
# @param [Exception, Hash]
|
8
17
|
def initialize(exception, env)
|
9
18
|
renderer_klass = if defined?(Rails)
|
10
19
|
PartyFoul::RailsIssueRenderer
|
@@ -15,6 +24,8 @@ class PartyFoul::ExceptionHandler
|
|
15
24
|
self.rendered_issue = renderer_klass.new(exception, env)
|
16
25
|
end
|
17
26
|
|
27
|
+
# Begins to process the exception for Github Issues. Makes an attempt
|
28
|
+
# to find the issue. If found will update the issue. If not found will create a new issue.
|
18
29
|
def run
|
19
30
|
if issue = find_issue
|
20
31
|
update_issue(issue)
|
@@ -23,6 +34,7 @@ class PartyFoul::ExceptionHandler
|
|
23
34
|
end
|
24
35
|
end
|
25
36
|
|
37
|
+
# Hits the Github API to find the matching issue using the fingerprint.
|
26
38
|
def find_issue
|
27
39
|
unless issue = PartyFoul.github.search.issues(owner: PartyFoul.owner, repo: PartyFoul.repo, state: 'open', keyword: fingerprint).issues.first
|
28
40
|
issue = PartyFoul.github.search.issues(owner: PartyFoul.owner, repo: PartyFoul.repo, state: 'closed', keyword: fingerprint).issues.first
|
@@ -31,11 +43,16 @@ class PartyFoul::ExceptionHandler
|
|
31
43
|
issue
|
32
44
|
end
|
33
45
|
|
46
|
+
# Will create a new issue and a comment with the proper details. All issues are labeled as 'bug'.
|
34
47
|
def create_issue
|
48
|
+
self.sha = PartyFoul.github.git_data.references.get(PartyFoul.owner, PartyFoul.repo, "heads/#{PartyFoul.branch}").object.sha
|
35
49
|
issue = PartyFoul.github.issues.create(PartyFoul.owner, PartyFoul.repo, title: rendered_issue.title, body: rendered_issue.body, labels: ['bug'])
|
36
50
|
PartyFoul.github.issues.comments.create(PartyFoul.owner, PartyFoul.repo, issue['number'], body: rendered_issue.comment)
|
37
51
|
end
|
38
52
|
|
53
|
+
# Updates the given issue. If the issue is labeled as 'wontfix' nothing is done. If the issue is closed the issue is reopened and labeled as 'regression'.
|
54
|
+
#
|
55
|
+
# @param [Hashie::Mash]
|
39
56
|
def update_issue(issue)
|
40
57
|
unless issue.key?('labels') && issue['labels'].include?('wontfix')
|
41
58
|
params = {body: rendered_issue.update_body(issue['body']), state: 'open'}
|
@@ -44,6 +61,7 @@ class PartyFoul::ExceptionHandler
|
|
44
61
|
params[:labels] = ['bug', 'regression']
|
45
62
|
end
|
46
63
|
|
64
|
+
self.sha = PartyFoul.github.git_data.references.get(PartyFoul.owner, PartyFoul.repo, "heads/#{PartyFoul.branch}").object.sha
|
47
65
|
PartyFoul.github.issues.edit(PartyFoul.owner, PartyFoul.repo, issue['number'], params)
|
48
66
|
PartyFoul.github.issues.comments.create(PartyFoul.owner, PartyFoul.repo, issue['number'], body: rendered_issue.comment)
|
49
67
|
end
|
@@ -54,4 +72,8 @@ class PartyFoul::ExceptionHandler
|
|
54
72
|
def fingerprint
|
55
73
|
rendered_issue.fingerprint
|
56
74
|
end
|
75
|
+
|
76
|
+
def sha=(sha)
|
77
|
+
rendered_issue.sha = sha
|
78
|
+
end
|
57
79
|
end
|
@@ -1,37 +1,68 @@
|
|
1
1
|
class PartyFoul::IssueRenderer
|
2
|
-
attr_accessor :exception, :env
|
2
|
+
attr_accessor :exception, :env, :sha
|
3
3
|
|
4
|
+
# A new renderer instance for Githug issues
|
5
|
+
#
|
6
|
+
# @param [Exception, Hash]
|
4
7
|
def initialize(exception, env)
|
5
8
|
self.exception = exception
|
6
9
|
self.env = env
|
7
10
|
end
|
8
11
|
|
12
|
+
# Derived title of the issue. Must be implemented by the adapter class
|
13
|
+
#
|
14
|
+
# @return [NotImplementedError]
|
9
15
|
def title
|
10
16
|
raise NotImplementedError
|
11
17
|
end
|
12
18
|
|
19
|
+
# Will compile the template for an issue body as defined in
|
20
|
+
# {PartyFoul.issue_template}
|
21
|
+
#
|
22
|
+
# @return [String]
|
13
23
|
def body
|
14
24
|
compile_template(PartyFoul.issue_template)
|
15
25
|
end
|
16
26
|
|
27
|
+
# Will compile the template for a comment body as defined in
|
28
|
+
# {PartyFoul.comment_template}
|
17
29
|
def comment
|
18
30
|
compile_template(PartyFoul.comment_template)
|
19
31
|
end
|
20
32
|
|
33
|
+
# Compiles the stack trace for use in the issue body. Lines in the
|
34
|
+
# stack trace that are part of the application will be rendered as
|
35
|
+
# links to the relative file and line on Github based upon
|
36
|
+
# {PartyFoul.web_url}, {PartyFoul.owner}, {PartyFoul.repo}, and
|
37
|
+
# {PartyFoul.branch}. The branch will be used at the time the
|
38
|
+
# exception happens to grab the SHA for that branch at that time for
|
39
|
+
# the purpose of linking.
|
40
|
+
#
|
41
|
+
# @return [String]
|
21
42
|
def stack_trace
|
22
43
|
exception.backtrace.map do |line|
|
23
44
|
if matches = extract_file_name_and_line_number(line)
|
24
|
-
"<a href='#{PartyFoul.repo_url}/
|
45
|
+
"<a href='#{PartyFoul.repo_url}/blob/#{sha}/#{matches[2]}#L#{matches[3]}'>#{line}</a>"
|
25
46
|
else
|
26
47
|
line
|
27
48
|
end
|
28
49
|
end.join("\n")
|
29
50
|
end
|
30
51
|
|
52
|
+
# A SHA1 hex digested representation of the title. The fingerprint is
|
53
|
+
# used to create a unique value in the issue body. This value is used
|
54
|
+
# for seraching when matching issues happen again in the future.
|
55
|
+
#
|
56
|
+
# @return [String]
|
31
57
|
def fingerprint
|
32
58
|
Digest::SHA1.hexdigest(title)
|
33
59
|
end
|
34
60
|
|
61
|
+
# Will update the issue body. The count and the time stamp will both
|
62
|
+
# be updated. If the format of the issue body fails to match for
|
63
|
+
# whatever reason the issue body will be reset.
|
64
|
+
#
|
65
|
+
# @return [String]
|
35
66
|
def update_body(old_body)
|
36
67
|
begin
|
37
68
|
current_count = old_body.match(/<th>Count<\/th><td>(\d+)<\/td>/)[1].to_i
|
@@ -43,22 +74,39 @@ class PartyFoul::IssueRenderer
|
|
43
74
|
end
|
44
75
|
end
|
45
76
|
|
77
|
+
# The params hash at the time the exception occurred. This method is
|
78
|
+
# overriden for each framework adapter. It should return a hash.
|
79
|
+
#
|
80
|
+
# @return [NotImplementedError]
|
46
81
|
def params
|
47
82
|
raise NotImplementedError
|
48
83
|
end
|
49
84
|
|
85
|
+
# The timestamp when the exception occurred.
|
86
|
+
#
|
87
|
+
# @return [String]
|
50
88
|
def occurred_at
|
51
89
|
Time.now.strftime('%B %d, %Y %H:%M:%S %z')
|
52
90
|
end
|
53
91
|
|
92
|
+
# IP address of the client who triggered the exception
|
93
|
+
#
|
94
|
+
# @return [String]
|
54
95
|
def ip_address
|
55
96
|
env['REMOTE_ADDR']
|
56
97
|
end
|
57
98
|
|
99
|
+
# The session hash for the client at the time of the exception
|
100
|
+
#
|
101
|
+
# @return [Hash]
|
58
102
|
def session
|
59
103
|
env['rack.session']
|
60
104
|
end
|
61
105
|
|
106
|
+
# HTTP Headers hash from the request. Headers can be filtered out by
|
107
|
+
# adding matching key names to {PartyFoul.filtered_http_headers}
|
108
|
+
#
|
109
|
+
# @return [Hash]
|
62
110
|
def http_headers
|
63
111
|
env.keys.select { |key| key =~ /^HTTP_(\w+)/ && !(PartyFoul.filtered_http_headers || []).include?($1.split('_').map(&:capitalize).join('-')) }.sort.inject({}) do |hash, key|
|
64
112
|
hash[key.split('HTTP_').last.split('_').map(&:capitalize).join('-')] = env[key]
|
@@ -2,10 +2,16 @@ require 'party_foul/issue_renderer'
|
|
2
2
|
|
3
3
|
module PartyFoul
|
4
4
|
class RackIssueRenderer < IssueRenderer
|
5
|
+
# Rack params
|
6
|
+
#
|
7
|
+
# @return [Hash]
|
5
8
|
def params
|
6
9
|
env['QUERY_STRING']
|
7
10
|
end
|
8
11
|
|
12
|
+
# Title for the issue comprised of (exception) "message"
|
13
|
+
#
|
14
|
+
# @return [String]
|
9
15
|
def title
|
10
16
|
%{(#{exception.class}) "#{exception.message}"}
|
11
17
|
end
|
@@ -2,11 +2,17 @@ require 'party_foul/issue_renderer'
|
|
2
2
|
|
3
3
|
module PartyFoul
|
4
4
|
class RailsIssueRenderer < IssueRenderer
|
5
|
+
# Rails params hash. Filtered parms are respected.
|
6
|
+
#
|
7
|
+
# @return [Hash]
|
5
8
|
def params
|
6
9
|
parameter_filter = ActionDispatch::Http::ParameterFilter.new(env["action_dispatch.parameter_filter"])
|
7
10
|
parameter_filter.filter(env['action_dispatch.request.path_parameters'])
|
8
11
|
end
|
9
12
|
|
13
|
+
# Title for the issue comprised of Controller#action (exception) "message"
|
14
|
+
#
|
15
|
+
# @return [String]
|
10
16
|
def title
|
11
17
|
%{#{env['action_controller.instance'].class}##{env['action_dispatch.request.path_parameters']['action']} (#{exception.class}) "#{exception.message}"}
|
12
18
|
end
|
@@ -1,4 +1,8 @@
|
|
1
1
|
class PartyFoul::Processors::Sync
|
2
|
+
# Passes the exception and rack env data to the ExceptionHandler and
|
3
|
+
# runs everything synchronously.
|
4
|
+
#
|
5
|
+
# @param [Exception, Hash]
|
2
6
|
def self.handle(exception, env)
|
3
7
|
PartyFoul::ExceptionHandler.new(exception, env).run
|
4
8
|
end
|
data/lib/party_foul/version.rb
CHANGED
data/lib/party_foul.rb
CHANGED
@@ -4,13 +4,27 @@ module PartyFoul
|
|
4
4
|
class << self
|
5
5
|
attr_accessor :github, :oauth_token, :endpoint, :owner, :repo,
|
6
6
|
:ignored_exceptions, :processor, :issue_template,
|
7
|
-
:comment_template, :filtered_http_headers, :web_url
|
7
|
+
:comment_template, :filtered_http_headers, :web_url, :branch
|
8
8
|
end
|
9
9
|
|
10
|
+
# The git branch that is used for linking in the stack trace
|
11
|
+
#
|
12
|
+
# @return [String] Defaults to 'master' if not set
|
13
|
+
def self.branch
|
14
|
+
@branch ||= 'master'
|
15
|
+
end
|
16
|
+
|
17
|
+
# The web url for Github. This is only interesting for Enterprise
|
18
|
+
# users
|
19
|
+
#
|
20
|
+
# @return [String] Defaults to 'https://github.com' if not set
|
10
21
|
def self.web_url
|
11
22
|
@web_url ||= 'https://github.com'
|
12
23
|
end
|
13
24
|
|
25
|
+
# The template used for rendering the body of a new issue
|
26
|
+
#
|
27
|
+
# @return [String]
|
14
28
|
def self.issue_template
|
15
29
|
@issue_template ||=
|
16
30
|
<<-TEMPLATE
|
@@ -26,6 +40,9 @@ Fingerprint: `:fingerprint`
|
|
26
40
|
TEMPLATE
|
27
41
|
end
|
28
42
|
|
43
|
+
# The template used for rendering the body of a new comment
|
44
|
+
#
|
45
|
+
# @return [String]
|
29
46
|
def self.comment_template
|
30
47
|
@comment_template ||=
|
31
48
|
<<-TEMPLATE
|
@@ -39,14 +56,38 @@ Fingerprint: `:fingerprint`
|
|
39
56
|
TEMPLATE
|
40
57
|
end
|
41
58
|
|
59
|
+
# The collection of exceptions that should not be captured. Members of
|
60
|
+
# the collection must be string representations of the exception. For
|
61
|
+
# example:
|
62
|
+
#
|
63
|
+
# # This is good
|
64
|
+
# ['ActiveRecord::RecordNotFound']
|
65
|
+
#
|
66
|
+
# # This is not
|
67
|
+
# [ActiveRecord::RecordNotFound]
|
68
|
+
#
|
69
|
+
# @return [Array]
|
42
70
|
def self.ignored_exceptions
|
43
71
|
@ignored_exceptions || []
|
44
72
|
end
|
45
73
|
|
74
|
+
# The url of the repository. Built using the {.web_url}, {.owner}, and {.repo}
|
75
|
+
# values
|
76
|
+
#
|
77
|
+
# @return [String]
|
46
78
|
def self.repo_url
|
47
79
|
"#{web_url}/#{owner}/#{repo}"
|
48
80
|
end
|
49
81
|
|
82
|
+
# The configure block for PartyFoul. Use to initialize settings
|
83
|
+
#
|
84
|
+
# PartyFoul.configure do |config|
|
85
|
+
# config.owner 'dockyard'
|
86
|
+
# config.repo 'test_app'
|
87
|
+
# config.oauth_token = ENV['oauth_token']
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# @param [Block]
|
50
91
|
def self.configure(&block)
|
51
92
|
yield self
|
52
93
|
self.processor ||= PartyFoul::Processors::Sync
|
@@ -14,6 +14,7 @@ describe 'Party Foul Confg' do
|
|
14
14
|
config.endpoint = 'http://api.example.com'
|
15
15
|
config.owner = 'test_owner'
|
16
16
|
config.repo = 'test_repo'
|
17
|
+
config.branch = 'master'
|
17
18
|
end
|
18
19
|
|
19
20
|
PartyFoul.ignored_exceptions.must_equal ['StandardError']
|
@@ -23,5 +24,11 @@ describe 'Party Foul Confg' do
|
|
23
24
|
PartyFoul.owner.must_equal 'test_owner'
|
24
25
|
PartyFoul.repo.must_equal 'test_repo'
|
25
26
|
PartyFoul.repo_url.must_equal 'http://example.com/test_owner/test_repo'
|
27
|
+
PartyFoul.branch.must_equal 'master'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'has default values' do
|
31
|
+
PartyFoul.web_url.must_equal 'https://github.com'
|
32
|
+
PartyFoul.branch.must_equal 'master'
|
26
33
|
end
|
27
34
|
end
|
@@ -8,8 +8,10 @@ describe 'Party Foul Exception Handler' do
|
|
8
8
|
config.repo = 'test_repo'
|
9
9
|
end
|
10
10
|
|
11
|
+
PartyFoul.stubs(:branch).returns('deploy')
|
11
12
|
PartyFoul.github.stubs(:issues).returns(mock('Issues'))
|
12
13
|
PartyFoul.github.stubs(:search).returns(mock('Search'))
|
14
|
+
PartyFoul.github.git_data.references.stubs(:get)
|
13
15
|
PartyFoul.github.issues.stubs(:create)
|
14
16
|
PartyFoul.github.issues.stubs(:edit)
|
15
17
|
PartyFoul.github.issues.stubs(:comments).returns(mock('Comments'))
|
@@ -25,6 +27,7 @@ describe 'Party Foul Exception Handler' do
|
|
25
27
|
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'open').returns(Hashie::Mash.new(issues: []))
|
26
28
|
PartyFoul.github.search.stubs(:issues).with(owner: 'test_owner', repo: 'test_repo', keyword: 'test_fingerprint', state: 'closed').returns(Hashie::Mash.new(issues: []))
|
27
29
|
PartyFoul.github.issues.expects(:create).with('test_owner', 'test_repo', title: 'Test Title', body: 'Test Body', :labels => ['bug']).returns(Hashie::Mash.new('number' => 1))
|
30
|
+
PartyFoul.github.git_data.references.expects(:get).with('test_owner', 'test_repo', 'heads/deploy').returns(Hashie::Mash.new(object: Hashie::Mash.new(sha: 'abcdefg1234567890')))
|
28
31
|
PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
|
29
32
|
PartyFoul::ExceptionHandler.new(nil, {}).run
|
30
33
|
end
|
@@ -41,6 +44,7 @@ describe 'Party Foul Exception Handler' do
|
|
41
44
|
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}]))
|
42
45
|
PartyFoul.github.issues.expects(:edit).with('test_owner', 'test_repo', 1, body: 'New Body', state: 'open')
|
43
46
|
PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
|
47
|
+
PartyFoul.github.git_data.references.expects(:get).with('test_owner', 'test_repo', 'heads/deploy').returns(Hashie::Mash.new(object: Hashie::Mash.new(sha: 'abcdefg1234567890')))
|
44
48
|
PartyFoul::ExceptionHandler.new(nil, {}).run
|
45
49
|
end
|
46
50
|
end
|
@@ -51,6 +55,7 @@ describe 'Party Foul Exception Handler' do
|
|
51
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}]))
|
52
56
|
PartyFoul.github.issues.expects(:edit).with('test_owner', 'test_repo', 1, body: 'New Body', state: 'open', labels: ['bug', 'regression'])
|
53
57
|
PartyFoul.github.issues.comments.expects(:create).with('test_owner', 'test_repo', 1, body: 'Test Comment')
|
58
|
+
PartyFoul.github.git_data.references.expects(:get).with('test_owner', 'test_repo', 'heads/deploy').returns(Hashie::Mash.new(object: Hashie::Mash.new(sha: 'abcdefg1234567890')))
|
54
59
|
PartyFoul::ExceptionHandler.new(nil, {}).run
|
55
60
|
end
|
56
61
|
end
|
@@ -64,6 +69,7 @@ describe 'Party Foul Exception Handler' do
|
|
64
69
|
PartyFoul.github.issues.expects(:create).never
|
65
70
|
PartyFoul.github.issues.expects(:edit).never
|
66
71
|
PartyFoul.github.issues.comments.expects(:create).never
|
72
|
+
PartyFoul.github.git_data.references.expects(:get).never
|
67
73
|
PartyFoul::ExceptionHandler.new(nil, {}).run
|
68
74
|
end
|
69
75
|
end
|
data/test/test_helper.rb
CHANGED
@@ -21,7 +21,7 @@ module MiniTest::Expectations
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def clean_up_party
|
24
|
-
%w{github oauth_token endpoint owner repo ignored_exceptions processor issue_template comment_template filtered_http_headers web_url}.each do |attr|
|
24
|
+
%w{github oauth_token endpoint owner repo ignored_exceptions processor issue_template comment_template filtered_http_headers web_url branch}.each do |attr|
|
25
25
|
PartyFoul.send("#{attr}=", nil)
|
26
26
|
end
|
27
27
|
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.
|
4
|
+
version: 0.5.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-01-
|
13
|
+
date: 2013-01-28 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: github_api
|
@@ -200,7 +200,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
200
200
|
version: '0'
|
201
201
|
segments:
|
202
202
|
- 0
|
203
|
-
hash: -
|
203
|
+
hash: -549281873562555121
|
204
204
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
205
|
none: false
|
206
206
|
requirements:
|
@@ -209,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
209
209
|
version: '0'
|
210
210
|
segments:
|
211
211
|
- 0
|
212
|
-
hash: -
|
212
|
+
hash: -549281873562555121
|
213
213
|
requirements: []
|
214
214
|
rubyforge_project:
|
215
215
|
rubygems_version: 1.8.23
|