hubba 0.7.0 → 1.0.1
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +5 -4
- data/README.md +146 -146
- data/Rakefile +30 -30
- data/lib/hubba/config.rb +51 -51
- data/lib/hubba/github.rb +210 -210
- data/lib/hubba/reposet.rb +83 -83
- data/lib/hubba/stats.rb +284 -246
- data/lib/hubba/update.rb +46 -43
- data/lib/hubba/update_traffic.rb +51 -51
- data/lib/hubba/version.rb +18 -18
- data/lib/hubba.rb +46 -46
- data/test/helper.rb +7 -7
- data/test/test_config.rb +31 -31
- metadata +8 -9
data/lib/hubba/github.rb
CHANGED
@@ -1,210 +1,210 @@
|
|
1
|
-
module Hubba
|
2
|
-
|
3
|
-
|
4
|
-
class Github
|
5
|
-
BASE_URL = 'https://api.github.com'
|
6
|
-
|
7
|
-
###############
|
8
|
-
## (nested) classes for "wrapped" response (parsed json body)
|
9
|
-
class Resource
|
10
|
-
attr_reader :data
|
11
|
-
def initialize( data )
|
12
|
-
@data = data
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class Repos < Resource
|
17
|
-
def names
|
18
|
-
## sort by name
|
19
|
-
data.map { |item| item['name'] }.sort
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class Orgs < Resource
|
24
|
-
def logins
|
25
|
-
## sort by name
|
26
|
-
data.map { |item| item['login'] }.sort
|
27
|
-
end
|
28
|
-
alias_method :names, :logins ## add name alias - why? why not?
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
def initialize( token: nil,
|
34
|
-
user: nil,
|
35
|
-
password: nil )
|
36
|
-
@token = nil
|
37
|
-
@user = nil
|
38
|
-
@password = nil
|
39
|
-
|
40
|
-
if token ## 1a) give preference to passed in token
|
41
|
-
@token = token
|
42
|
-
elsif user && password ## 1b) or passed in user/password credentials
|
43
|
-
@user = user
|
44
|
-
@password = password
|
45
|
-
elsif Hubba.config.token ## 2a) followed by configured (or env) token
|
46
|
-
@token = Hubba.config.token
|
47
|
-
elsif Hubba.config.user && Hubba.config.password ## 2b)
|
48
|
-
@user = Hubba.config.user
|
49
|
-
@password = Hubba.config.password
|
50
|
-
else ## 3)
|
51
|
-
## no token or credentials passed in or configured
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def user( name )
|
58
|
-
Resource.new( get "/users/#{name}" )
|
59
|
-
end
|
60
|
-
|
61
|
-
|
62
|
-
def user_repos( name )
|
63
|
-
Repos.new( get "/users/#{name}/repos" ) ## add ?per_page=100 - why? why not?
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
|
-
##
|
68
|
-
# note: pagination
|
69
|
-
# requests that return multiple items will be paginated to 30 items by default.
|
70
|
-
# You can specify further pages with the ?page parameter.
|
71
|
-
# For some resources, you can also set a custom page size up to 100
|
72
|
-
# with the ?per_page=100 parameter
|
73
|
-
|
74
|
-
def user_orgs( name )
|
75
|
-
Orgs.new( get "/users/#{name}/orgs?per_page=100" )
|
76
|
-
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
def org( name )
|
81
|
-
Resource.new( get "/orgs/#{name}" )
|
82
|
-
end
|
83
|
-
|
84
|
-
def org_repos( name )
|
85
|
-
Repos.new( get "/orgs/#{name}/repos?per_page=100" )
|
86
|
-
end
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
def repo( full_name ) ## full_name (handle) e.g. henrythemes/jekyll-starter-theme
|
91
|
-
Resource.new( get "/repos/#{full_name}" )
|
92
|
-
end
|
93
|
-
|
94
|
-
def repo_languages( full_name )
|
95
|
-
Resource.new( get "/repos/#{full_name}/languages" )
|
96
|
-
end
|
97
|
-
|
98
|
-
def repo_topics( full_name )
|
99
|
-
## note: requires "api preview" accept headers (overwrites default v3+json)
|
100
|
-
## e.g. application/vnd.github.mercy-preview+json
|
101
|
-
Resource.new( get( "/repos/#{full_name}/topics", preview: 'mercy' ) )
|
102
|
-
end
|
103
|
-
|
104
|
-
|
105
|
-
def repo_commits( full_name )
|
106
|
-
Resource.new( get "/repos/#{full_name}/commits" )
|
107
|
-
end
|
108
|
-
|
109
|
-
|
110
|
-
def repo_traffic_clones( full_name )
|
111
|
-
# Get repository clones
|
112
|
-
# Get the total number of clones and breakdown per day or week
|
113
|
-
# for the last 14 days.
|
114
|
-
# Timestamps are aligned to UTC midnight of the beginning of the day or week.
|
115
|
-
# Week begins on Monday.
|
116
|
-
Resource.new( get "/repos/#{full_name}/traffic/clones" )
|
117
|
-
end
|
118
|
-
|
119
|
-
def repo_traffic_views( full_name )
|
120
|
-
# Get page views
|
121
|
-
# Get the total number of views and breakdown per day or week
|
122
|
-
# for the last 14 days.
|
123
|
-
# Timestamps are aligned to UTC midnight of the beginning of the day or week.
|
124
|
-
# Week begins on Monday.
|
125
|
-
Resource.new( get "/repos/#{full_name}/traffic/views" )
|
126
|
-
end
|
127
|
-
|
128
|
-
|
129
|
-
def repo_traffic_popular_paths( full_name )
|
130
|
-
# Get top referral paths
|
131
|
-
# Get the top 10 popular contents over the last 14 days.
|
132
|
-
Resource.new( get "/repos/#{full_name}/traffic/popular/paths" )
|
133
|
-
end
|
134
|
-
|
135
|
-
def repo_traffic_popular_referrers( full_name )
|
136
|
-
# Get top referral sources
|
137
|
-
# Get the top 10 referrers over the last 14 days.
|
138
|
-
Resource.new( get "/repos/#{full_name}/traffic/popular/referrers" )
|
139
|
-
end
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
private
|
145
|
-
def get( request_uri, preview: nil )
|
146
|
-
|
147
|
-
puts "GET #{request_uri}"
|
148
|
-
|
149
|
-
## note: request_uri ALWAYS starts with leading /, thus use + for now!!!
|
150
|
-
# e.g. /users/geraldb
|
151
|
-
# /users/geraldb/repos
|
152
|
-
url = BASE_URL + request_uri
|
153
|
-
|
154
|
-
|
155
|
-
headers = {}
|
156
|
-
## add default headers if nothing (custom) set / passed-in
|
157
|
-
headers['User-Agent'] = "ruby/hubba v#{VERSION}" ## required by GitHub API
|
158
|
-
headers['Accept'] = if preview # e.g. mercy or ???
|
159
|
-
"application/vnd.github.#{preview}-preview+json"
|
160
|
-
else
|
161
|
-
'application/vnd.github.v3+json' ## default - recommend by GitHub API
|
162
|
-
end
|
163
|
-
|
164
|
-
auth = []
|
165
|
-
## check if credentials (user/password) present - if yes, use basic auth
|
166
|
-
if @token
|
167
|
-
puts " using (personal access) token - starting with: #{@token[0..6]}**********"
|
168
|
-
headers['Authorization'] = "token #{@token}"
|
169
|
-
## token works like:
|
170
|
-
## curl -H 'Authorization: token my_access_token' https://api.github.com/user/repos
|
171
|
-
elsif @user && @password
|
172
|
-
puts " using basic auth - user: #{@user}, password: ***"
|
173
|
-
## use credential auth "tuple" (that is, array with two string items) for now
|
174
|
-
## or use Webclient::HttpBasicAuth or something - why? why not?
|
175
|
-
auth = [@user, @password]
|
176
|
-
# req.basic_auth( @user, @password )
|
177
|
-
else
|
178
|
-
puts " using no credentials (no token, no user/password)"
|
179
|
-
end
|
180
|
-
|
181
|
-
res = Webclient.get( url,
|
182
|
-
headers: headers,
|
183
|
-
auth: auth )
|
184
|
-
|
185
|
-
# Get specific header
|
186
|
-
# response["content-type"]
|
187
|
-
# => "text/html; charset=UTF-8"
|
188
|
-
|
189
|
-
# Iterate all response headers.
|
190
|
-
# puts "HTTP HEADERS:"
|
191
|
-
# res.headers.each do |key, value|
|
192
|
-
# puts " #{key}: >#{value}<"
|
193
|
-
# end
|
194
|
-
# puts
|
195
|
-
|
196
|
-
# => "location => http://www.google.com/"
|
197
|
-
# => "content-type => text/html; charset=UTF-8"
|
198
|
-
# ...
|
199
|
-
|
200
|
-
if res.status.ok?
|
201
|
-
res.json
|
202
|
-
else
|
203
|
-
puts "!! HTTP ERROR: #{res.status.code} #{res.status.message}:"
|
204
|
-
pp res.raw
|
205
|
-
exit 1
|
206
|
-
end
|
207
|
-
end # method get
|
208
|
-
|
209
|
-
end # class Github
|
210
|
-
end # module Hubba
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
|
4
|
+
class Github
|
5
|
+
BASE_URL = 'https://api.github.com'
|
6
|
+
|
7
|
+
###############
|
8
|
+
## (nested) classes for "wrapped" response (parsed json body)
|
9
|
+
class Resource
|
10
|
+
attr_reader :data
|
11
|
+
def initialize( data )
|
12
|
+
@data = data
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Repos < Resource
|
17
|
+
def names
|
18
|
+
## sort by name
|
19
|
+
data.map { |item| item['name'] }.sort
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Orgs < Resource
|
24
|
+
def logins
|
25
|
+
## sort by name
|
26
|
+
data.map { |item| item['login'] }.sort
|
27
|
+
end
|
28
|
+
alias_method :names, :logins ## add name alias - why? why not?
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
def initialize( token: nil,
|
34
|
+
user: nil,
|
35
|
+
password: nil )
|
36
|
+
@token = nil
|
37
|
+
@user = nil
|
38
|
+
@password = nil
|
39
|
+
|
40
|
+
if token ## 1a) give preference to passed in token
|
41
|
+
@token = token
|
42
|
+
elsif user && password ## 1b) or passed in user/password credentials
|
43
|
+
@user = user
|
44
|
+
@password = password
|
45
|
+
elsif Hubba.config.token ## 2a) followed by configured (or env) token
|
46
|
+
@token = Hubba.config.token
|
47
|
+
elsif Hubba.config.user && Hubba.config.password ## 2b)
|
48
|
+
@user = Hubba.config.user
|
49
|
+
@password = Hubba.config.password
|
50
|
+
else ## 3)
|
51
|
+
## no token or credentials passed in or configured
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
def user( name )
|
58
|
+
Resource.new( get "/users/#{name}" )
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def user_repos( name )
|
63
|
+
Repos.new( get "/users/#{name}/repos" ) ## add ?per_page=100 - why? why not?
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
##
|
68
|
+
# note: pagination
|
69
|
+
# requests that return multiple items will be paginated to 30 items by default.
|
70
|
+
# You can specify further pages with the ?page parameter.
|
71
|
+
# For some resources, you can also set a custom page size up to 100
|
72
|
+
# with the ?per_page=100 parameter
|
73
|
+
|
74
|
+
def user_orgs( name )
|
75
|
+
Orgs.new( get "/users/#{name}/orgs?per_page=100" )
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
def org( name )
|
81
|
+
Resource.new( get "/orgs/#{name}" )
|
82
|
+
end
|
83
|
+
|
84
|
+
def org_repos( name )
|
85
|
+
Repos.new( get "/orgs/#{name}/repos?per_page=100" )
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
def repo( full_name ) ## full_name (handle) e.g. henrythemes/jekyll-starter-theme
|
91
|
+
Resource.new( get "/repos/#{full_name}" )
|
92
|
+
end
|
93
|
+
|
94
|
+
def repo_languages( full_name )
|
95
|
+
Resource.new( get "/repos/#{full_name}/languages" )
|
96
|
+
end
|
97
|
+
|
98
|
+
def repo_topics( full_name )
|
99
|
+
## note: requires "api preview" accept headers (overwrites default v3+json)
|
100
|
+
## e.g. application/vnd.github.mercy-preview+json
|
101
|
+
Resource.new( get( "/repos/#{full_name}/topics", preview: 'mercy' ) )
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def repo_commits( full_name )
|
106
|
+
Resource.new( get "/repos/#{full_name}/commits" )
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def repo_traffic_clones( full_name )
|
111
|
+
# Get repository clones
|
112
|
+
# Get the total number of clones and breakdown per day or week
|
113
|
+
# for the last 14 days.
|
114
|
+
# Timestamps are aligned to UTC midnight of the beginning of the day or week.
|
115
|
+
# Week begins on Monday.
|
116
|
+
Resource.new( get "/repos/#{full_name}/traffic/clones" )
|
117
|
+
end
|
118
|
+
|
119
|
+
def repo_traffic_views( full_name )
|
120
|
+
# Get page views
|
121
|
+
# Get the total number of views and breakdown per day or week
|
122
|
+
# for the last 14 days.
|
123
|
+
# Timestamps are aligned to UTC midnight of the beginning of the day or week.
|
124
|
+
# Week begins on Monday.
|
125
|
+
Resource.new( get "/repos/#{full_name}/traffic/views" )
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
def repo_traffic_popular_paths( full_name )
|
130
|
+
# Get top referral paths
|
131
|
+
# Get the top 10 popular contents over the last 14 days.
|
132
|
+
Resource.new( get "/repos/#{full_name}/traffic/popular/paths" )
|
133
|
+
end
|
134
|
+
|
135
|
+
def repo_traffic_popular_referrers( full_name )
|
136
|
+
# Get top referral sources
|
137
|
+
# Get the top 10 referrers over the last 14 days.
|
138
|
+
Resource.new( get "/repos/#{full_name}/traffic/popular/referrers" )
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
|
143
|
+
|
144
|
+
private
|
145
|
+
def get( request_uri, preview: nil )
|
146
|
+
|
147
|
+
puts "GET #{request_uri}"
|
148
|
+
|
149
|
+
## note: request_uri ALWAYS starts with leading /, thus use + for now!!!
|
150
|
+
# e.g. /users/geraldb
|
151
|
+
# /users/geraldb/repos
|
152
|
+
url = BASE_URL + request_uri
|
153
|
+
|
154
|
+
|
155
|
+
headers = {}
|
156
|
+
## add default headers if nothing (custom) set / passed-in
|
157
|
+
headers['User-Agent'] = "ruby/hubba v#{VERSION}" ## required by GitHub API
|
158
|
+
headers['Accept'] = if preview # e.g. mercy or ???
|
159
|
+
"application/vnd.github.#{preview}-preview+json"
|
160
|
+
else
|
161
|
+
'application/vnd.github.v3+json' ## default - recommend by GitHub API
|
162
|
+
end
|
163
|
+
|
164
|
+
auth = []
|
165
|
+
## check if credentials (user/password) present - if yes, use basic auth
|
166
|
+
if @token
|
167
|
+
puts " using (personal access) token - starting with: #{@token[0..6]}**********"
|
168
|
+
headers['Authorization'] = "token #{@token}"
|
169
|
+
## token works like:
|
170
|
+
## curl -H 'Authorization: token my_access_token' https://api.github.com/user/repos
|
171
|
+
elsif @user && @password
|
172
|
+
puts " using basic auth - user: #{@user}, password: ***"
|
173
|
+
## use credential auth "tuple" (that is, array with two string items) for now
|
174
|
+
## or use Webclient::HttpBasicAuth or something - why? why not?
|
175
|
+
auth = [@user, @password]
|
176
|
+
# req.basic_auth( @user, @password )
|
177
|
+
else
|
178
|
+
puts " using no credentials (no token, no user/password)"
|
179
|
+
end
|
180
|
+
|
181
|
+
res = Webclient.get( url,
|
182
|
+
headers: headers,
|
183
|
+
auth: auth )
|
184
|
+
|
185
|
+
# Get specific header
|
186
|
+
# response["content-type"]
|
187
|
+
# => "text/html; charset=UTF-8"
|
188
|
+
|
189
|
+
# Iterate all response headers.
|
190
|
+
# puts "HTTP HEADERS:"
|
191
|
+
# res.headers.each do |key, value|
|
192
|
+
# puts " #{key}: >#{value}<"
|
193
|
+
# end
|
194
|
+
# puts
|
195
|
+
|
196
|
+
# => "location => http://www.google.com/"
|
197
|
+
# => "content-type => text/html; charset=UTF-8"
|
198
|
+
# ...
|
199
|
+
|
200
|
+
if res.status.ok?
|
201
|
+
res.json
|
202
|
+
else
|
203
|
+
puts "!! HTTP ERROR: #{res.status.code} #{res.status.message}:"
|
204
|
+
pp res.raw
|
205
|
+
exit 1
|
206
|
+
end
|
207
|
+
end # method get
|
208
|
+
|
209
|
+
end # class Github
|
210
|
+
end # module Hubba
|
data/lib/hubba/reposet.rb
CHANGED
@@ -1,83 +1,83 @@
|
|
1
|
-
module Hubba
|
2
|
-
|
3
|
-
|
4
|
-
## orgs - include repos form org(anizations) too
|
5
|
-
## cache - save json response to cache_dir - change to/use debug/tmp_dir? - why? why not?
|
6
|
-
def self.reposet( *users, orgs: true,
|
7
|
-
cache: false )
|
8
|
-
# users = [users] if users.is_a?( String ) ### wrap in array if single user
|
9
|
-
|
10
|
-
gh = Github.new
|
11
|
-
|
12
|
-
forks = []
|
13
|
-
|
14
|
-
h = {}
|
15
|
-
users.each do |user|
|
16
|
-
res = gh.user_repos( user )
|
17
|
-
save_json( "#{config.cache_dir}/users~#{user}~repos.json", res.data ) if cache
|
18
|
-
|
19
|
-
repos = []
|
20
|
-
####
|
21
|
-
# check for forked repos (auto-exclude personal by default)
|
22
|
-
# note: forked repos in orgs get NOT auto-excluded!!!
|
23
|
-
res.data.each do |repo|
|
24
|
-
fork = repo['fork']
|
25
|
-
if fork
|
26
|
-
print "FORK "
|
27
|
-
forks << "#{repo['full_name']} (AUTO-EXCLUDED)"
|
28
|
-
else
|
29
|
-
print " "
|
30
|
-
repos << repo['name']
|
31
|
-
end
|
32
|
-
print repo['full_name']
|
33
|
-
print "\n"
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
h[ "#{user} (#{repos.size})" ] = repos.sort
|
38
|
-
end
|
39
|
-
|
40
|
-
|
41
|
-
## all repos from orgs
|
42
|
-
## note: for now only use first (primary user) - why? why not?
|
43
|
-
if orgs
|
44
|
-
user = users[0]
|
45
|
-
res = gh.user_orgs( user )
|
46
|
-
save_json( "#{config.cache_dir}/users~#{user}~orgs.json", res.data ) if cache
|
47
|
-
|
48
|
-
|
49
|
-
logins = res.logins.each do |login|
|
50
|
-
## next if ['xxx'].include?( login ) ## add orgs here to skip
|
51
|
-
|
52
|
-
res = gh.org_repos( login )
|
53
|
-
save_json( "#{config.cache_dir}/orgs~#{login}~repos.json", res.data ) if cache
|
54
|
-
|
55
|
-
repos = []
|
56
|
-
res.data.each do |repo|
|
57
|
-
fork = repo['fork']
|
58
|
-
if fork
|
59
|
-
print "FORK "
|
60
|
-
forks << repo['full_name']
|
61
|
-
repos << repo['name']
|
62
|
-
else
|
63
|
-
print " "
|
64
|
-
repos << repo['name']
|
65
|
-
end
|
66
|
-
print repo['full_name']
|
67
|
-
print "\n"
|
68
|
-
end
|
69
|
-
|
70
|
-
h[ "#{login} (#{repos.size})" ] = repos.sort
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
if forks.size > 0
|
75
|
-
puts
|
76
|
-
puts "#{forks.size} fork(s):"
|
77
|
-
puts forks
|
78
|
-
end
|
79
|
-
|
80
|
-
h
|
81
|
-
end ## method reposet
|
82
|
-
|
83
|
-
end # module Hubba
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
|
4
|
+
## orgs - include repos form org(anizations) too
|
5
|
+
## cache - save json response to cache_dir - change to/use debug/tmp_dir? - why? why not?
|
6
|
+
def self.reposet( *users, orgs: true,
|
7
|
+
cache: false )
|
8
|
+
# users = [users] if users.is_a?( String ) ### wrap in array if single user
|
9
|
+
|
10
|
+
gh = Github.new
|
11
|
+
|
12
|
+
forks = []
|
13
|
+
|
14
|
+
h = {}
|
15
|
+
users.each do |user|
|
16
|
+
res = gh.user_repos( user )
|
17
|
+
save_json( "#{config.cache_dir}/users~#{user}~repos.json", res.data ) if cache
|
18
|
+
|
19
|
+
repos = []
|
20
|
+
####
|
21
|
+
# check for forked repos (auto-exclude personal by default)
|
22
|
+
# note: forked repos in orgs get NOT auto-excluded!!!
|
23
|
+
res.data.each do |repo|
|
24
|
+
fork = repo['fork']
|
25
|
+
if fork
|
26
|
+
print "FORK "
|
27
|
+
forks << "#{repo['full_name']} (AUTO-EXCLUDED)"
|
28
|
+
else
|
29
|
+
print " "
|
30
|
+
repos << repo['name']
|
31
|
+
end
|
32
|
+
print repo['full_name']
|
33
|
+
print "\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
h[ "#{user} (#{repos.size})" ] = repos.sort
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
## all repos from orgs
|
42
|
+
## note: for now only use first (primary user) - why? why not?
|
43
|
+
if orgs
|
44
|
+
user = users[0]
|
45
|
+
res = gh.user_orgs( user )
|
46
|
+
save_json( "#{config.cache_dir}/users~#{user}~orgs.json", res.data ) if cache
|
47
|
+
|
48
|
+
|
49
|
+
logins = res.logins.each do |login|
|
50
|
+
## next if ['xxx'].include?( login ) ## add orgs here to skip
|
51
|
+
|
52
|
+
res = gh.org_repos( login )
|
53
|
+
save_json( "#{config.cache_dir}/orgs~#{login}~repos.json", res.data ) if cache
|
54
|
+
|
55
|
+
repos = []
|
56
|
+
res.data.each do |repo|
|
57
|
+
fork = repo['fork']
|
58
|
+
if fork
|
59
|
+
print "FORK "
|
60
|
+
forks << repo['full_name']
|
61
|
+
repos << repo['name']
|
62
|
+
else
|
63
|
+
print " "
|
64
|
+
repos << repo['name']
|
65
|
+
end
|
66
|
+
print repo['full_name']
|
67
|
+
print "\n"
|
68
|
+
end
|
69
|
+
|
70
|
+
h[ "#{login} (#{repos.size})" ] = repos.sort
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if forks.size > 0
|
75
|
+
puts
|
76
|
+
puts "#{forks.size} fork(s):"
|
77
|
+
puts forks
|
78
|
+
end
|
79
|
+
|
80
|
+
h
|
81
|
+
end ## method reposet
|
82
|
+
|
83
|
+
end # module Hubba
|