how_is 18.0.4 → 18.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/exe/how_is CHANGED
@@ -8,11 +8,9 @@ require "how_is/cli"
8
8
  begin
9
9
  result = HowIs::CLI.parse(ARGV)
10
10
  rescue HowIs::CLI::OptionsError => e
11
- if ENV['SHOW_TRACE']
12
- raise
13
- else
14
- abort "Error: #{e.message}"
15
- end
11
+ raise if ENV["SHOW_TRACE"]
12
+
13
+ abort "Error: #{e.message}"
16
14
  end
17
15
 
18
16
  options = result[:options]
@@ -45,10 +43,7 @@ begin
45
43
  )
46
44
  end
47
45
  rescue => e
48
- if ENV['SHOW_TRACE']
49
- raise
50
- else
51
- abort "Error: #{e.message}"
52
- end
53
- end
46
+ raise if ENV["SHOW_TRACE"]
54
47
 
48
+ abort "Error: #{e.message}"
49
+ end
@@ -0,0 +1,78 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.github.com/repos/how-is/example-repository/commits?since=2017-01-01
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/vnd.github.v3+json,application/vnd.github.beta+json;q=0.5,application/json;q=0.1
12
+ Accept-Charset:
13
+ - utf-8
14
+ User-Agent:
15
+ - Github API Ruby Gem 0.17.0
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Server:
24
+ - GitHub.com
25
+ Date:
26
+ - Sat, 05 Aug 2017 21:05:34 GMT
27
+ Content-Type:
28
+ - application/json; charset=utf-8
29
+ Transfer-Encoding:
30
+ - chunked
31
+ Status:
32
+ - 200 OK
33
+ X-Ratelimit-Limit:
34
+ - '60'
35
+ X-Ratelimit-Remaining:
36
+ - '6'
37
+ X-Ratelimit-Reset:
38
+ - '1501967489'
39
+ Cache-Control:
40
+ - public, max-age=60, s-maxage=60
41
+ Vary:
42
+ - Accept
43
+ Etag:
44
+ - W/"c4e64de253131e8983284594017e505c"
45
+ Last-Modified:
46
+ - Sat, 05 Aug 2017 20:39:01 GMT
47
+ X-Github-Media-Type:
48
+ - github.v3; format=json
49
+ Access-Control-Expose-Headers:
50
+ - ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
51
+ X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
52
+ Access-Control-Allow-Origin:
53
+ - "*"
54
+ Content-Security-Policy:
55
+ - default-src 'none'
56
+ Strict-Transport-Security:
57
+ - max-age=31536000; includeSubdomains; preload
58
+ X-Content-Type-Options:
59
+ - nosniff
60
+ X-Frame-Options:
61
+ - deny
62
+ X-Xss-Protection:
63
+ - 1; mode=block
64
+ X-Runtime-Rack:
65
+ - '0.046194'
66
+ X-Github-Request-Id:
67
+ - DA1A:795E:9C82C1:19614C6:5986331E
68
+ body:
69
+ encoding: ASCII-8BIT
70
+ string: '[{"sha":"40c01ab6ebec6cbd8ad9e521a732f941c169e557","commit":{"author":{"name":"Ellen
71
+ Marie Dash","email":"me@duckie.co","date":"2017-08-05T20:39:01Z"},"committer":{"name":"Ellen
72
+ Marie Dash","email":"me@duckie.co","date":"2017-08-05T20:39:01Z"},"message":"meep","tree":{"sha":"6911e0637822f44b83f04f47821adab56fdbc0b9","url":"https://api.github.com/repos/how-is/example-repository/git/trees/6911e0637822f44b83f04f47821adab56fdbc0b9"},"url":"https://api.github.com/repos/how-is/example-repository/git/commits/40c01ab6ebec6cbd8ad9e521a732f941c169e557","comment_count":0},"url":"https://api.github.com/repos/how-is/example-repository/commits/40c01ab6ebec6cbd8ad9e521a732f941c169e557","html_url":"https://github.com/how-is/example-repository/commit/40c01ab6ebec6cbd8ad9e521a732f941c169e557","comments_url":"https://api.github.com/repos/how-is/example-repository/commits/40c01ab6ebec6cbd8ad9e521a732f941c169e557/comments","author":{"login":"duckinator","id":39698,"avatar_url":"https://avatars3.githubusercontent.com/u/39698?v=4","gravatar_id":"","url":"https://api.github.com/users/duckinator","html_url":"https://github.com/duckinator","followers_url":"https://api.github.com/users/duckinator/followers","following_url":"https://api.github.com/users/duckinator/following{/other_user}","gists_url":"https://api.github.com/users/duckinator/gists{/gist_id}","starred_url":"https://api.github.com/users/duckinator/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/duckinator/subscriptions","organizations_url":"https://api.github.com/users/duckinator/orgs","repos_url":"https://api.github.com/users/duckinator/repos","events_url":"https://api.github.com/users/duckinator/events{/privacy}","received_events_url":"https://api.github.com/users/duckinator/received_events","type":"User","site_admin":false},"committer":{"login":"duckinator","id":39698,"avatar_url":"https://avatars3.githubusercontent.com/u/39698?v=4","gravatar_id":"","url":"https://api.github.com/users/duckinator","html_url":"https://github.com/duckinator","followers_url":"https://api.github.com/users/duckinator/followers","following_url":"https://api.github.com/users/duckinator/following{/other_user}","gists_url":"https://api.github.com/users/duckinator/gists{/gist_id}","starred_url":"https://api.github.com/users/duckinator/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/duckinator/subscriptions","organizations_url":"https://api.github.com/users/duckinator/orgs","repos_url":"https://api.github.com/users/duckinator/repos","events_url":"https://api.github.com/users/duckinator/events{/privacy}","received_events_url":"https://api.github.com/users/duckinator/received_events","type":"User","site_admin":false},"parents":[{"sha":"3794aa1c4b76623748faf280abe5760b76823162","url":"https://api.github.com/repos/how-is/example-repository/commits/3794aa1c4b76623748faf280abe5760b76823162","html_url":"https://github.com/how-is/example-repository/commit/3794aa1c4b76623748faf280abe5760b76823162"}]},{"sha":"3794aa1c4b76623748faf280abe5760b76823162","commit":{"author":{"name":"fake
73
+ author","email":"fake@duckinator.net","date":"2017-08-05T20:23:10Z"},"committer":{"name":"fake
74
+ author","email":"fake@duckinator.net","date":"2017-08-05T20:23:10Z"},"message":"test
75
+ commit","tree":{"sha":"8286e548e330cfe01efcf7189f4df1fa53e777a7","url":"https://api.github.com/repos/how-is/example-repository/git/trees/8286e548e330cfe01efcf7189f4df1fa53e777a7"},"url":"https://api.github.com/repos/how-is/example-repository/git/commits/3794aa1c4b76623748faf280abe5760b76823162","comment_count":0},"url":"https://api.github.com/repos/how-is/example-repository/commits/3794aa1c4b76623748faf280abe5760b76823162","html_url":"https://github.com/how-is/example-repository/commit/3794aa1c4b76623748faf280abe5760b76823162","comments_url":"https://api.github.com/repos/how-is/example-repository/commits/3794aa1c4b76623748faf280abe5760b76823162/comments","author":null,"committer":null,"parents":[{"sha":"9e29405efa433529b86722542b8fb4b34dfd9edd","url":"https://api.github.com/repos/how-is/example-repository/commits/9e29405efa433529b86722542b8fb4b34dfd9edd","html_url":"https://github.com/how-is/example-repository/commit/9e29405efa433529b86722542b8fb4b34dfd9edd"}]}]'
76
+ http_version:
77
+ recorded_at: Sat, 05 Aug 2017 21:05:34 GMT
78
+ recorded_with: VCR 3.0.3
@@ -0,0 +1,219 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.github.com/repos/how-is/example-repository/commits?since=2017-01-01
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/vnd.github.v3+json,application/vnd.github.beta+json;q=0.5,application/json;q=0.1
12
+ Accept-Charset:
13
+ - utf-8
14
+ User-Agent:
15
+ - Github API Ruby Gem 0.17.0
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Server:
24
+ - GitHub.com
25
+ Date:
26
+ - Sat, 05 Aug 2017 21:08:49 GMT
27
+ Content-Type:
28
+ - application/json; charset=utf-8
29
+ Transfer-Encoding:
30
+ - chunked
31
+ Status:
32
+ - 200 OK
33
+ X-Ratelimit-Limit:
34
+ - '60'
35
+ X-Ratelimit-Remaining:
36
+ - '5'
37
+ X-Ratelimit-Reset:
38
+ - '1501967489'
39
+ Cache-Control:
40
+ - public, max-age=60, s-maxage=60
41
+ Vary:
42
+ - Accept
43
+ Etag:
44
+ - W/"c4e64de253131e8983284594017e505c"
45
+ Last-Modified:
46
+ - Sat, 05 Aug 2017 20:39:01 GMT
47
+ X-Github-Media-Type:
48
+ - github.v3; format=json
49
+ Access-Control-Expose-Headers:
50
+ - ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
51
+ X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
52
+ Access-Control-Allow-Origin:
53
+ - "*"
54
+ Content-Security-Policy:
55
+ - default-src 'none'
56
+ Strict-Transport-Security:
57
+ - max-age=31536000; includeSubdomains; preload
58
+ X-Content-Type-Options:
59
+ - nosniff
60
+ X-Frame-Options:
61
+ - deny
62
+ X-Xss-Protection:
63
+ - 1; mode=block
64
+ X-Runtime-Rack:
65
+ - '0.044265'
66
+ X-Github-Request-Id:
67
+ - DA58:795D:BB06DF:178487C:598633E1
68
+ body:
69
+ encoding: ASCII-8BIT
70
+ string: '[{"sha":"40c01ab6ebec6cbd8ad9e521a732f941c169e557","commit":{"author":{"name":"Ellen
71
+ Marie Dash","email":"me@duckie.co","date":"2017-08-05T20:39:01Z"},"committer":{"name":"Ellen
72
+ Marie Dash","email":"me@duckie.co","date":"2017-08-05T20:39:01Z"},"message":"meep","tree":{"sha":"6911e0637822f44b83f04f47821adab56fdbc0b9","url":"https://api.github.com/repos/how-is/example-repository/git/trees/6911e0637822f44b83f04f47821adab56fdbc0b9"},"url":"https://api.github.com/repos/how-is/example-repository/git/commits/40c01ab6ebec6cbd8ad9e521a732f941c169e557","comment_count":0},"url":"https://api.github.com/repos/how-is/example-repository/commits/40c01ab6ebec6cbd8ad9e521a732f941c169e557","html_url":"https://github.com/how-is/example-repository/commit/40c01ab6ebec6cbd8ad9e521a732f941c169e557","comments_url":"https://api.github.com/repos/how-is/example-repository/commits/40c01ab6ebec6cbd8ad9e521a732f941c169e557/comments","author":{"login":"duckinator","id":39698,"avatar_url":"https://avatars3.githubusercontent.com/u/39698?v=4","gravatar_id":"","url":"https://api.github.com/users/duckinator","html_url":"https://github.com/duckinator","followers_url":"https://api.github.com/users/duckinator/followers","following_url":"https://api.github.com/users/duckinator/following{/other_user}","gists_url":"https://api.github.com/users/duckinator/gists{/gist_id}","starred_url":"https://api.github.com/users/duckinator/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/duckinator/subscriptions","organizations_url":"https://api.github.com/users/duckinator/orgs","repos_url":"https://api.github.com/users/duckinator/repos","events_url":"https://api.github.com/users/duckinator/events{/privacy}","received_events_url":"https://api.github.com/users/duckinator/received_events","type":"User","site_admin":false},"committer":{"login":"duckinator","id":39698,"avatar_url":"https://avatars3.githubusercontent.com/u/39698?v=4","gravatar_id":"","url":"https://api.github.com/users/duckinator","html_url":"https://github.com/duckinator","followers_url":"https://api.github.com/users/duckinator/followers","following_url":"https://api.github.com/users/duckinator/following{/other_user}","gists_url":"https://api.github.com/users/duckinator/gists{/gist_id}","starred_url":"https://api.github.com/users/duckinator/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/duckinator/subscriptions","organizations_url":"https://api.github.com/users/duckinator/orgs","repos_url":"https://api.github.com/users/duckinator/repos","events_url":"https://api.github.com/users/duckinator/events{/privacy}","received_events_url":"https://api.github.com/users/duckinator/received_events","type":"User","site_admin":false},"parents":[{"sha":"3794aa1c4b76623748faf280abe5760b76823162","url":"https://api.github.com/repos/how-is/example-repository/commits/3794aa1c4b76623748faf280abe5760b76823162","html_url":"https://github.com/how-is/example-repository/commit/3794aa1c4b76623748faf280abe5760b76823162"}]},{"sha":"3794aa1c4b76623748faf280abe5760b76823162","commit":{"author":{"name":"fake
73
+ author","email":"fake@duckinator.net","date":"2017-08-05T20:23:10Z"},"committer":{"name":"fake
74
+ author","email":"fake@duckinator.net","date":"2017-08-05T20:23:10Z"},"message":"test
75
+ commit","tree":{"sha":"8286e548e330cfe01efcf7189f4df1fa53e777a7","url":"https://api.github.com/repos/how-is/example-repository/git/trees/8286e548e330cfe01efcf7189f4df1fa53e777a7"},"url":"https://api.github.com/repos/how-is/example-repository/git/commits/3794aa1c4b76623748faf280abe5760b76823162","comment_count":0},"url":"https://api.github.com/repos/how-is/example-repository/commits/3794aa1c4b76623748faf280abe5760b76823162","html_url":"https://github.com/how-is/example-repository/commit/3794aa1c4b76623748faf280abe5760b76823162","comments_url":"https://api.github.com/repos/how-is/example-repository/commits/3794aa1c4b76623748faf280abe5760b76823162/comments","author":null,"committer":null,"parents":[{"sha":"9e29405efa433529b86722542b8fb4b34dfd9edd","url":"https://api.github.com/repos/how-is/example-repository/commits/9e29405efa433529b86722542b8fb4b34dfd9edd","html_url":"https://github.com/how-is/example-repository/commit/9e29405efa433529b86722542b8fb4b34dfd9edd"}]}]'
76
+ http_version:
77
+ recorded_at: Sat, 05 Aug 2017 21:08:49 GMT
78
+ - request:
79
+ method: get
80
+ uri: https://api.github.com/repos/how-is/example-repository/commits?author=me@duckie.co&until=2017-01-01
81
+ body:
82
+ encoding: US-ASCII
83
+ string: ''
84
+ headers:
85
+ Accept:
86
+ - application/vnd.github.v3+json,application/vnd.github.beta+json;q=0.5,application/json;q=0.1
87
+ Accept-Charset:
88
+ - utf-8
89
+ User-Agent:
90
+ - Github API Ruby Gem 0.17.0
91
+ Accept-Encoding:
92
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
93
+ response:
94
+ status:
95
+ code: 200
96
+ message: OK
97
+ headers:
98
+ Server:
99
+ - GitHub.com
100
+ Date:
101
+ - Sat, 05 Aug 2017 21:08:50 GMT
102
+ Content-Type:
103
+ - application/json; charset=utf-8
104
+ Transfer-Encoding:
105
+ - chunked
106
+ Status:
107
+ - 200 OK
108
+ X-Ratelimit-Limit:
109
+ - '60'
110
+ X-Ratelimit-Remaining:
111
+ - '4'
112
+ X-Ratelimit-Reset:
113
+ - '1501967489'
114
+ Cache-Control:
115
+ - public, max-age=60, s-maxage=60
116
+ Vary:
117
+ - Accept
118
+ Etag:
119
+ - W/"63ec50619f12827a37594ae87db65a6d"
120
+ Last-Modified:
121
+ - Sun, 07 Aug 2016 03:52:54 GMT
122
+ X-Github-Media-Type:
123
+ - github.v3; format=json
124
+ Access-Control-Expose-Headers:
125
+ - ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
126
+ X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
127
+ Access-Control-Allow-Origin:
128
+ - "*"
129
+ Content-Security-Policy:
130
+ - default-src 'none'
131
+ Strict-Transport-Security:
132
+ - max-age=31536000; includeSubdomains; preload
133
+ X-Content-Type-Options:
134
+ - nosniff
135
+ X-Frame-Options:
136
+ - deny
137
+ X-Xss-Protection:
138
+ - 1; mode=block
139
+ X-Runtime-Rack:
140
+ - '0.044928'
141
+ X-Github-Request-Id:
142
+ - DA59:795E:9CC15F:196C854:598633E2
143
+ body:
144
+ encoding: ASCII-8BIT
145
+ string: '[{"sha":"9e29405efa433529b86722542b8fb4b34dfd9edd","commit":{"author":{"name":"Ellen
146
+ Marie Dash","email":"me@duckie.co","date":"2016-08-07T03:52:54Z"},"committer":{"name":"Ellen
147
+ Marie Dash","email":"me@duckie.co","date":"2016-08-07T03:52:54Z"},"message":"Initial
148
+ commit","tree":{"sha":"7bee2a10cc8c7c036d5006fa0aacadd869d09c4e","url":"https://api.github.com/repos/how-is/example-repository/git/trees/7bee2a10cc8c7c036d5006fa0aacadd869d09c4e"},"url":"https://api.github.com/repos/how-is/example-repository/git/commits/9e29405efa433529b86722542b8fb4b34dfd9edd","comment_count":0},"url":"https://api.github.com/repos/how-is/example-repository/commits/9e29405efa433529b86722542b8fb4b34dfd9edd","html_url":"https://github.com/how-is/example-repository/commit/9e29405efa433529b86722542b8fb4b34dfd9edd","comments_url":"https://api.github.com/repos/how-is/example-repository/commits/9e29405efa433529b86722542b8fb4b34dfd9edd/comments","author":{"login":"duckinator","id":39698,"avatar_url":"https://avatars3.githubusercontent.com/u/39698?v=4","gravatar_id":"","url":"https://api.github.com/users/duckinator","html_url":"https://github.com/duckinator","followers_url":"https://api.github.com/users/duckinator/followers","following_url":"https://api.github.com/users/duckinator/following{/other_user}","gists_url":"https://api.github.com/users/duckinator/gists{/gist_id}","starred_url":"https://api.github.com/users/duckinator/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/duckinator/subscriptions","organizations_url":"https://api.github.com/users/duckinator/orgs","repos_url":"https://api.github.com/users/duckinator/repos","events_url":"https://api.github.com/users/duckinator/events{/privacy}","received_events_url":"https://api.github.com/users/duckinator/received_events","type":"User","site_admin":false},"committer":{"login":"duckinator","id":39698,"avatar_url":"https://avatars3.githubusercontent.com/u/39698?v=4","gravatar_id":"","url":"https://api.github.com/users/duckinator","html_url":"https://github.com/duckinator","followers_url":"https://api.github.com/users/duckinator/followers","following_url":"https://api.github.com/users/duckinator/following{/other_user}","gists_url":"https://api.github.com/users/duckinator/gists{/gist_id}","starred_url":"https://api.github.com/users/duckinator/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/duckinator/subscriptions","organizations_url":"https://api.github.com/users/duckinator/orgs","repos_url":"https://api.github.com/users/duckinator/repos","events_url":"https://api.github.com/users/duckinator/events{/privacy}","received_events_url":"https://api.github.com/users/duckinator/received_events","type":"User","site_admin":false},"parents":[]}]'
149
+ http_version:
150
+ recorded_at: Sat, 05 Aug 2017 21:08:50 GMT
151
+ - request:
152
+ method: get
153
+ uri: https://api.github.com/repos/how-is/example-repository/commits?author=fake@duckinator.net&until=2017-01-01
154
+ body:
155
+ encoding: US-ASCII
156
+ string: ''
157
+ headers:
158
+ Accept:
159
+ - application/vnd.github.v3+json,application/vnd.github.beta+json;q=0.5,application/json;q=0.1
160
+ Accept-Charset:
161
+ - utf-8
162
+ User-Agent:
163
+ - Github API Ruby Gem 0.17.0
164
+ Accept-Encoding:
165
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
166
+ response:
167
+ status:
168
+ code: 200
169
+ message: OK
170
+ headers:
171
+ Server:
172
+ - GitHub.com
173
+ Date:
174
+ - Sat, 05 Aug 2017 21:08:51 GMT
175
+ Content-Type:
176
+ - application/json; charset=utf-8
177
+ Content-Length:
178
+ - '2'
179
+ Status:
180
+ - 200 OK
181
+ X-Ratelimit-Limit:
182
+ - '60'
183
+ X-Ratelimit-Remaining:
184
+ - '3'
185
+ X-Ratelimit-Reset:
186
+ - '1501967489'
187
+ Cache-Control:
188
+ - public, max-age=60, s-maxage=60
189
+ Vary:
190
+ - Accept
191
+ Etag:
192
+ - '"fafd06a937c504cece6726ba7201b0a1"'
193
+ X-Github-Media-Type:
194
+ - github.v3; format=json
195
+ Access-Control-Expose-Headers:
196
+ - ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
197
+ X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
198
+ Access-Control-Allow-Origin:
199
+ - "*"
200
+ Content-Security-Policy:
201
+ - default-src 'none'
202
+ Strict-Transport-Security:
203
+ - max-age=31536000; includeSubdomains; preload
204
+ X-Content-Type-Options:
205
+ - nosniff
206
+ X-Frame-Options:
207
+ - deny
208
+ X-Xss-Protection:
209
+ - 1; mode=block
210
+ X-Runtime-Rack:
211
+ - '0.033466'
212
+ X-Github-Request-Id:
213
+ - DA5A:795A:69CBD4:E6BF0D:598633E2
214
+ body:
215
+ encoding: UTF-8
216
+ string: "[]"
217
+ http_version:
218
+ recorded_at: Sat, 05 Aug 2017 21:08:51 GMT
219
+ recorded_with: VCR 3.0.3
data/how_is.gemspec CHANGED
@@ -31,5 +31,6 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency "timecop", "~> 0.8.1"
32
32
  spec.add_development_dependency "vcr", "~> 3.0"
33
33
  spec.add_development_dependency "webmock"
34
- spec.add_development_dependency "rubocop", "~> 0.47.0"
34
+ spec.add_development_dependency "rubocop", "~> 0.49.1"
35
+ spec.add_development_dependency "github_changelog_generator"
35
36
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'contracts'
4
- require 'ostruct'
5
- require 'date'
6
- require 'json'
3
+ require "contracts"
4
+ require "ostruct"
5
+ require "date"
6
+ require "json"
7
7
 
8
8
  class HowIs
9
9
  ##
@@ -11,11 +11,12 @@ class HowIs
11
11
  class Analysis < OpenStruct
12
12
  end
13
13
 
14
+ # Creates Analysis objects with input data formatted in useful ways.
14
15
  class Analyzer
15
16
  include Contracts::Core
16
17
 
17
18
  ##
18
- # Raised when attempting to export to an unsupported format.
19
+ # Raised when attempting to import to an unsupported format.
19
20
  class UnsupportedImportFormat < StandardError
20
21
  def initialize(format)
21
22
  super("Unsupported import format: #{format}")
@@ -23,7 +24,7 @@ class HowIs
23
24
  end
24
25
 
25
26
  ##
26
- # Generates and returns an analysis.i
27
+ # Generates and returns an analysis.
27
28
  #
28
29
  # @param data [Fetcher::Results] The results gathered by Fetcher.
29
30
  # @param analysis_class (You don't need this.) A class to use instead of
@@ -43,7 +44,7 @@ class HowIs
43
44
  number_of_pulls: pulls.length,
44
45
 
45
46
  issues_with_label: with_label_links(num_with_label(issues), data.repository),
46
- issues_with_no_label: {'link' => nil, 'total' => num_with_no_label(issues)},
47
+ issues_with_no_label: {"link" => nil, "total" => num_with_no_label(issues)},
47
48
 
48
49
  average_issue_age: average_age_for(issues),
49
50
  average_pull_age: average_age_for(pulls),
@@ -64,15 +65,15 @@ class HowIs
64
65
  # @param data [Hash] The hash to generate an Analysis from.
65
66
  def self.from_hash(data)
66
67
  hash = data.map { |k, v|
67
- v = DateTime.parse(v) if k.end_with?('_date')
68
+ v = DateTime.parse(v) if k.end_with?("_date")
68
69
 
69
70
  [k, v]
70
71
  }.to_h
71
72
 
72
73
  hash.keys.each do |key|
73
- next unless hash[key].is_a?(Hash) && hash[key]['date']
74
+ next unless hash[key].is_a?(Hash) && hash[key]["date"]
74
75
 
75
- hash[key]['date'] = DateTime.parse(hash[key]['date'])
76
+ hash[key]["date"] = DateTime.parse(hash[key]["date"])
76
77
  end
77
78
 
78
79
  Analysis.new(hash)
@@ -90,10 +91,10 @@ class HowIs
90
91
 
91
92
  hash = Hash.new(0)
92
93
  issues_or_pulls.each do |iop|
93
- next unless iop['labels']
94
+ next unless iop["labels"]
94
95
 
95
- iop['labels'].each do |label|
96
- hash[label['name']] += 1
96
+ iop["labels"].each do |label|
97
+ hash[label["name"]] += 1
97
98
  end
98
99
  end
99
100
  hash
@@ -101,16 +102,16 @@ class HowIs
101
102
 
102
103
  # Returns the number of issues with no label.
103
104
  def num_with_no_label(issues)
104
- issues.select { |x| x['labels'].empty? }.length
105
+ issues.select { |x| x["labels"].empty? }.length
105
106
  end
106
107
 
107
108
  # Given an Array of dates, average the timestamps and return the date that
108
109
  # represents.
109
110
  def average_date_for(issues_or_pulls)
110
- timestamps = issues_or_pulls.map { |iop| Date.parse(iop['created_at']).strftime('%s').to_i }
111
+ timestamps = issues_or_pulls.map { |iop| Date.parse(iop["created_at"]).strftime("%s").to_i }
111
112
  average_timestamp = timestamps.reduce(:+) / issues_or_pulls.length
112
113
 
113
- DateTime.strptime(average_timestamp.to_s, '%s')
114
+ DateTime.strptime(average_timestamp.to_s, "%s")
114
115
  end
115
116
 
116
117
  # Given an Array of issues or pulls, return the average age of them.
@@ -118,32 +119,11 @@ class HowIs
118
119
  def average_age_for(issues_or_pulls)
119
120
  return nil if issues_or_pulls.empty?
120
121
 
121
- ages = issues_or_pulls.map { |iop| time_ago_in_seconds(iop['created_at']) }
122
- raw_average = ages.reduce(:+) / ages.length
122
+ ages = issues_or_pulls.map { |iop| time_ago_in_seconds(iop["created_at"]) }
123
+ average_age_in_seconds = ages.reduce(:+) / ages.length
123
124
 
124
- seconds_in_a_year = 31_556_926
125
- seconds_in_a_month = 2_629_743
126
- seconds_in_a_week = 604_800
127
- seconds_in_a_day = 86_400
128
-
129
- years = raw_average / seconds_in_a_year
130
- years_remainder = raw_average % seconds_in_a_year
131
-
132
- months = years_remainder / seconds_in_a_month
133
- months_remainder = years_remainder % seconds_in_a_month
134
-
135
- weeks = months_remainder / seconds_in_a_week
136
- weeks_remainder = months_remainder % seconds_in_a_week
137
-
138
- days = weeks_remainder / seconds_in_a_day
139
-
140
- values = [
141
- [years, "year"],
142
- [months, "month"],
143
- [weeks, "week"],
144
- [days, "day"],
145
- ].reject { |(v, _)| v == 0 }.map { |(v, k)|
146
- k += 's' if v != 1
125
+ values = period_pairs_for(average_age_in_seconds).reject { |(v, _)| v.zero? }.map { |(v, k)|
126
+ k += "s" if v != 1
147
127
  [v, k]
148
128
  }
149
129
 
@@ -160,7 +140,7 @@ class HowIs
160
140
  end
161
141
 
162
142
  def sort_iops_by_created_at(issues_or_pulls)
163
- issues_or_pulls.sort_by { |x| DateTime.parse(x['created_at']) }
143
+ issues_or_pulls.sort_by { |x| DateTime.parse(x["created_at"]) }
164
144
  end
165
145
 
166
146
  # Given an Array of issues or pulls, return the oldest.
@@ -181,7 +161,7 @@ class HowIs
181
161
 
182
162
  # Given an issue or PR, returns the date it was created.
183
163
  def date_for(issue_or_pull)
184
- DateTime.parse(issue_or_pull['created_at'])
164
+ DateTime.parse(issue_or_pull["created_at"])
185
165
  end
186
166
 
187
167
  private
@@ -192,7 +172,7 @@ class HowIs
192
172
  labels.map { |label, num_issues|
193
173
  label_link = "https://github.com/#{repository}/issues?q=" + CGI.escape("is:open is:issue label:\"#{label}\"")
194
174
 
195
- [label, {'link' => label_link, 'total' => num_issues}]
175
+ [label, {"link" => label_link, "total" => num_issues}]
196
176
  }.to_h
197
177
  end
198
178
 
@@ -206,11 +186,37 @@ class HowIs
206
186
 
207
187
  ret = {}
208
188
 
209
- ret['html_url'] = iop['html_url']
210
- ret['number'] = iop['number']
211
- ret['date'] = date_for(iop)
189
+ ret["html_url"] = iop["html_url"]
190
+ ret["number"] = iop["number"]
191
+ ret["date"] = date_for(iop)
212
192
 
213
193
  ret
214
194
  end
195
+
196
+ SECONDS_IN_A_YEAR = 31_556_926
197
+ SECONDS_IN_A_MONTH = 2_629_743
198
+ SECONDS_IN_A_WEEK = 604_800
199
+ SECONDS_IN_A_DAY = 86_400
200
+
201
+ # Calculates a list of pairs of value and period label.
202
+ #
203
+ # @param age_in_seconds [Float]
204
+ #
205
+ # @return [Array<Array>] The input age_in_seconds expressed as different
206
+ # units, as pairs of value and unit name.
207
+ def period_pairs_for(age_in_seconds)
208
+ years_remainder = age_in_seconds % SECONDS_IN_A_YEAR
209
+
210
+ months_remainder = years_remainder % SECONDS_IN_A_MONTH
211
+
212
+ weeks_remainder = months_remainder % SECONDS_IN_A_WEEK
213
+
214
+ [
215
+ [age_in_seconds / SECONDS_IN_A_YEAR, "year"],
216
+ [years_remainder / SECONDS_IN_A_MONTH, "month"],
217
+ [months_remainder / SECONDS_IN_A_WEEK, "week"],
218
+ [weeks_remainder / SECONDS_IN_A_DAY, "day"],
219
+ ]
220
+ end
215
221
  end
216
222
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HowIs
4
+ # Investigates who is a new committer since given date
5
+ #
6
+ # # /repos/:owner/:repo/commits?since=<start date for the report>
7
+ class Contributions
8
+ # @param github [Github] configured github client
9
+ # @param since_date [String] A value which fits Repos.Commits "since" and
10
+ # "until" fields. This supports many formats, for
11
+ # example a timestamp in ISO 8601 format:
12
+ # YYYY-MM-DDTHH:MM:SSZ.
13
+ # @param user [String] GitHub user of repository
14
+ # @param repo [String] GitHub repository name
15
+ def initialize(github:, since_date:, user:, repo:)
16
+ @github = github
17
+ @since_date = since_date
18
+ @user = user
19
+ @repo = repo
20
+ end
21
+
22
+ # Returns a list of contributors that have zero commits before the @since_date.
23
+ #
24
+ # @return [Hash{String => Hash] Committers keyed by GitHub login name
25
+ def new_contributors
26
+ # author: GitHub login, name or email by which to filter by commit author.
27
+ all_contributors.select do |email, _committer|
28
+ @github.repos.commits.list(user: @user,
29
+ repo: @repo,
30
+ until: @since_date,
31
+ author: email).count.zero?
32
+ end
33
+ end
34
+
35
+ # @return [Hash{String => Hash}] Author information keyed by author's email
36
+ def all_contributors
37
+ commits.map { |api_response|
38
+ [api_response.commit.author.email, api_response.commit.author.to_h]
39
+ }.to_h
40
+ end
41
+
42
+ def commits
43
+ @github.repos.commits.list(user: @user, repo: @repo, since: @since_date)
44
+ end
45
+ end
46
+ end
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'contracts'
4
- require 'github_api'
5
- require 'how_is/pulse'
3
+ require "contracts"
4
+ require "github_api"
5
+ require "how_is/pulse"
6
6
 
7
- ##
8
- # Fetches data from GitHub.
9
7
  class HowIs
8
+ ##
9
+ # Fetches data from GitHub.
10
10
  class Fetcher
11
11
  include Contracts::Core
12
12
 
@@ -15,7 +15,7 @@ class HowIs
15
15
  #
16
16
  # Implemented as a class instead of passing around a Hash so that it can
17
17
  # be more easily referenced by Contracts.
18
- class Results < Struct.new(:repository, :issues, :pulls, :pulse)
18
+ Results = Struct.new(:repository, :issues, :pulls, :pulse) do
19
19
  include Contracts::Core
20
20
 
21
21
  Contract String, C::ArrayOf[Hash], C::ArrayOf[Hash], String => nil
@@ -37,12 +37,12 @@ class HowIs
37
37
  pulse = nil)
38
38
  github ||= Github.new(auto_pagination: true)
39
39
  pulse ||= HowIs::Pulse.new(repository)
40
- user, repo = repository.split('/', 2)
40
+ user, repo = repository.split("/", 2)
41
41
 
42
42
  unless user && repo
43
- raise HowIs::CLI::OptionsError, 'To generate a report from GitHub, ' \
44
- 'provide the repository ' \
45
- 'username/project. Quitting!'
43
+ raise HowIs::CLI::OptionsError, "To generate a report from GitHub, " \
44
+ "provide the repository " \
45
+ "username/project. Quitting!"
46
46
  end
47
47
 
48
48
  issues = github.issues.list user: user, repo: repo