opsb-octopussy 0.3.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/lib/octopussy.rb +62 -0
- data/lib/octopussy/client.rb +402 -0
- data/lib/octopussy/event.rb +77 -0
- data/lib/octopussy/repo.rb +49 -0
- data/test/helper.rb +40 -0
- data/test/octopussy_test.rb +769 -0
- data/test/repo_test.rb +51 -0
- metadata +173 -0
data/lib/octopussy.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
require 'httparty'
|
4
|
+
require 'hashie'
|
5
|
+
Hash.send :include, Hashie::HashExtensions
|
6
|
+
|
7
|
+
libdir = File.dirname(__FILE__)
|
8
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
9
|
+
|
10
|
+
require 'octopussy/repo'
|
11
|
+
require 'octopussy/event'
|
12
|
+
require 'octopussy/client'
|
13
|
+
|
14
|
+
module Octopussy
|
15
|
+
extend SingleForwardable
|
16
|
+
|
17
|
+
VERSION = "0.3.0".freeze
|
18
|
+
|
19
|
+
class OctopussyError < StandardError
|
20
|
+
attr_reader :data
|
21
|
+
|
22
|
+
def initialize(data)
|
23
|
+
@data = data
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ClientError < StandardError; end
|
29
|
+
class ServerError < OctopussyError; end
|
30
|
+
class General < OctopussyError; end
|
31
|
+
|
32
|
+
class RateLimitExceeded < ClientError; end
|
33
|
+
class Unauthorized < ClientError; end
|
34
|
+
class NotFound < ClientError; end
|
35
|
+
|
36
|
+
class Unavailable < StandardError; end
|
37
|
+
class InformOctopussy < StandardError; end
|
38
|
+
|
39
|
+
def self.client; Client.new end
|
40
|
+
|
41
|
+
# Users
|
42
|
+
def_delegators :client, :search_users, :user, :followers, :following, :follows?, :watched
|
43
|
+
|
44
|
+
# Issues
|
45
|
+
def_delegators :client, :search_issues, :issues, :issue
|
46
|
+
|
47
|
+
# Repos
|
48
|
+
def_delegators :client, :branches, :collaborators, :contributors, :languages, :list_repos,
|
49
|
+
:network, :repo, :search_repos, :tags
|
50
|
+
|
51
|
+
# Network Meta
|
52
|
+
def_delegators :client, :network_meta, :network_data
|
53
|
+
|
54
|
+
# Trees
|
55
|
+
def_delegators :client, :tree, :blob, :raw
|
56
|
+
|
57
|
+
# Commits
|
58
|
+
def_delegators :client, :list_commits, :commit
|
59
|
+
|
60
|
+
# Timeline
|
61
|
+
def_delegators :client, :public_timeline
|
62
|
+
end
|
@@ -0,0 +1,402 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
module Octopussy
|
3
|
+
class Client
|
4
|
+
include HTTParty
|
5
|
+
format :json
|
6
|
+
base_uri "http://github.com/api/v2/json"
|
7
|
+
|
8
|
+
attr_reader :login, :token
|
9
|
+
|
10
|
+
# :login => 'pengwynn', :token => 'your_github_api_key'
|
11
|
+
def initialize(auth={})
|
12
|
+
if auth[:password].nil?
|
13
|
+
@login = auth[:login]
|
14
|
+
@token = auth[:token]
|
15
|
+
self.class.basic_auth(nil, nil)
|
16
|
+
else
|
17
|
+
@login = auth[:login]
|
18
|
+
self.class.basic_auth(@login, auth[:password])
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
def search_users(q)
|
24
|
+
q = CGI.escape(q)
|
25
|
+
response = self.class.get("/user/search/#{q}")
|
26
|
+
Hashie::Mash.new(response).users
|
27
|
+
end
|
28
|
+
|
29
|
+
def user(login=self.login)
|
30
|
+
response = self.class.get("/user/show/#{login}", :query => auth_params)
|
31
|
+
Hashie::Mash.new(response).user
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def update_user(values={})
|
36
|
+
response = self.class.post("/user/show/#{self.login}", :query => auth_params, :body => {:values => values})
|
37
|
+
Hashie::Mash.new(response).user
|
38
|
+
end
|
39
|
+
|
40
|
+
def followers(login=self.login)
|
41
|
+
response = self.class.get("/user/show/#{login}/followers")
|
42
|
+
Hashie::Mash.new(response).users
|
43
|
+
end
|
44
|
+
|
45
|
+
def following(login=self.login)
|
46
|
+
response = self.class.get("/user/show/#{login}/following")
|
47
|
+
Hashie::Mash.new(response).users
|
48
|
+
end
|
49
|
+
|
50
|
+
def follow!(username)
|
51
|
+
response = self.class.post("/user/follow/#{username}", :query => auth_params)
|
52
|
+
Hashie::Mash.new(response).users
|
53
|
+
end
|
54
|
+
|
55
|
+
def unfollow!(username)
|
56
|
+
response = self.class.post("/user/unfollow/#{username}", :query => auth_params)
|
57
|
+
Hashie::Mash.new(response).users
|
58
|
+
end
|
59
|
+
|
60
|
+
def follows?(*args)
|
61
|
+
target = args.pop
|
62
|
+
username = args.first
|
63
|
+
username ||= self.login
|
64
|
+
return if username.nil?
|
65
|
+
self.following(username).include?(target)
|
66
|
+
end
|
67
|
+
|
68
|
+
def watched(login=self.login)
|
69
|
+
response = self.class.get("/repos/watched/#{login}")
|
70
|
+
Hashie::Mash.new(response).repositories
|
71
|
+
end
|
72
|
+
|
73
|
+
def emails
|
74
|
+
response = self.class.get("/user/emails", :query => auth_params)
|
75
|
+
Hashie::Mash.new(response).emails
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_email(email)
|
79
|
+
response = self.class.post("/user/email/add", :query => auth_params, :body => {:email => email})
|
80
|
+
Hashie::Mash.new(response).emails
|
81
|
+
end
|
82
|
+
|
83
|
+
def remove_email(email)
|
84
|
+
response = self.class.post("/user/email/remove", :query => auth_params, :body => {:email => email})
|
85
|
+
Hashie::Mash.new(response).emails
|
86
|
+
end
|
87
|
+
|
88
|
+
def keys
|
89
|
+
response = self.class.get("/user/keys", :query => auth_params)
|
90
|
+
Hashie::Mash.new(response).public_keys
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_key(title, key)
|
94
|
+
response = self.class.post("/user/key/add", :query => auth_params, :body => {:title => title, :key => key})
|
95
|
+
Hashie::Mash.new(response).public_keys
|
96
|
+
end
|
97
|
+
|
98
|
+
def remove_key(id)
|
99
|
+
response = self.class.post("/user/key/remove", :query => auth_params, :body => {:id => id})
|
100
|
+
Hashie::Mash.new(response).public_keys
|
101
|
+
end
|
102
|
+
|
103
|
+
# Issues
|
104
|
+
|
105
|
+
def search_issues(repo, state, q)
|
106
|
+
repo = Repo.new(repo)
|
107
|
+
response = self.class.get("/issues/search/#{repo.username}/#{repo.name}/#{state}/#{q}")
|
108
|
+
Hashie::Mash.new(response).issues
|
109
|
+
end
|
110
|
+
|
111
|
+
def issues(repo, state)
|
112
|
+
repo = Repo.new(repo)
|
113
|
+
response = self.class.get("/issues/list/#{repo.username}/#{repo.name}/#{state}")
|
114
|
+
Hashie::Mash.new(response).issues
|
115
|
+
end
|
116
|
+
|
117
|
+
def issue(repo, id)
|
118
|
+
repo = Repo.new(repo)
|
119
|
+
response = self.class.get("/issues/show/#{repo.username}/#{repo.name}/#{id}")
|
120
|
+
Hashie::Mash.new(response).issue
|
121
|
+
end
|
122
|
+
|
123
|
+
def open_issue(repo, title, body)
|
124
|
+
repo = Repo.new(repo)
|
125
|
+
response = self.class.post("/issues/open/#{repo.username}/#{repo.name}", :body => {:title => title, :body => body})
|
126
|
+
Hashie::Mash.new(response).issue
|
127
|
+
end
|
128
|
+
|
129
|
+
def close_issue(repo, number)
|
130
|
+
repo = Repo.new(repo)
|
131
|
+
response = self.class.post("/issues/close/#{repo.username}/#{repo.name}/#{number}")
|
132
|
+
Hashie::Mash.new(response).issue
|
133
|
+
end
|
134
|
+
|
135
|
+
def reopen_issue(repo, number)
|
136
|
+
repo = Repo.new(repo)
|
137
|
+
response = self.class.post("/issues/reopen/#{repo.username}/#{repo.name}/#{number}")
|
138
|
+
Hashie::Mash.new(response).issue
|
139
|
+
end
|
140
|
+
|
141
|
+
def update_issue(repo, number, title, body)
|
142
|
+
repo = Repo.new(repo)
|
143
|
+
response = self.class.post("/issues/edit/#{repo.username}/#{repo.name}/#{number}", :body => {:title => title, :body => body})
|
144
|
+
Hashie::Mash.new(response).issue
|
145
|
+
end
|
146
|
+
|
147
|
+
def labels(repo)
|
148
|
+
repo = Repo.new(repo)
|
149
|
+
response = self.class.get("/issues/labels/#{repo.username}/#{repo.name}")
|
150
|
+
Hashie::Mash.new(response).labels
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_label(repo, number, label)
|
154
|
+
repo = Repo.new(repo)
|
155
|
+
response = self.class.post("/issues/label/add/#{repo.username}/#{repo.name}/#{label}/#{number}")
|
156
|
+
Hashie::Mash.new(response).labels
|
157
|
+
end
|
158
|
+
|
159
|
+
def remove_label(repo, number, label)
|
160
|
+
repo = Repo.new(repo)
|
161
|
+
response = self.class.post("/issues/label/remove/#{repo.username}/#{repo.name}/#{label}/#{number}")
|
162
|
+
Hashie::Mash.new(response).labels
|
163
|
+
end
|
164
|
+
|
165
|
+
def add_comment(repo, number, comment)
|
166
|
+
repo = Repo.new(repo)
|
167
|
+
response = self.class.post("/issues/comment/#{repo.username}/#{repo.name}/#{number}", :body => {:comment => comment})
|
168
|
+
Hashie::Mash.new(response).comment
|
169
|
+
end
|
170
|
+
|
171
|
+
# Repos
|
172
|
+
|
173
|
+
def search_repos(q)
|
174
|
+
q = CGI.escape(q)
|
175
|
+
response = self.class.get("/repos/search/#{q}")
|
176
|
+
Hashie::Mash.new(response).repositories
|
177
|
+
end
|
178
|
+
|
179
|
+
def watch(repo)
|
180
|
+
repo = Repo.new(repo)
|
181
|
+
response = self.class.post("/repos/watch/#{repo.username}/#{repo.name}", :query => auth_params)
|
182
|
+
Hashie::Mash.new(response).repository
|
183
|
+
end
|
184
|
+
|
185
|
+
def unwatch(repo)
|
186
|
+
repo = Repo.new(repo)
|
187
|
+
response = self.class.post("/repos/unwatch/#{repo.username}/#{repo.name}", :query => auth_params)
|
188
|
+
Hashie::Mash.new(response).repository
|
189
|
+
end
|
190
|
+
|
191
|
+
def fork(repo)
|
192
|
+
repo = Repo.new(repo)
|
193
|
+
response = self.class.post("/repos/fork/#{repo.username}/#{repo.name}", :query => auth_params)
|
194
|
+
Hashie::Mash.new(response).repository
|
195
|
+
end
|
196
|
+
|
197
|
+
# :name, :description, :homepage, :public
|
198
|
+
def create(options)
|
199
|
+
response = self.class.post("/repos/create", :query => auth_params, :body => options)
|
200
|
+
Hashie::Mash.new(response).repository
|
201
|
+
end
|
202
|
+
|
203
|
+
def delete(repo, delete_token={})
|
204
|
+
repo = Repo.new(repo)
|
205
|
+
response = self.class.post("/repos/delete/#{repo.name}", :query => auth_params, :body => {:delete_token => delete_token})
|
206
|
+
Hashie::Mash.new(response).repository
|
207
|
+
end
|
208
|
+
|
209
|
+
def confirm_delete(repo, delete_token)
|
210
|
+
delete(repo, delete_token)
|
211
|
+
end
|
212
|
+
|
213
|
+
def set_private(repo)
|
214
|
+
repo = Repo.new(repo)
|
215
|
+
response = self.class.post("/repos/set/private/#{repo.name}", :query => auth_params)
|
216
|
+
Hashie::Mash.new(response).repository
|
217
|
+
end
|
218
|
+
|
219
|
+
def set_public(repo)
|
220
|
+
repo = Repo.new(repo)
|
221
|
+
response = self.class.post("/repos/set/public/#{repo.name}", :query => auth_params)
|
222
|
+
Hashie::Mash.new(response).repository
|
223
|
+
end
|
224
|
+
|
225
|
+
def deploy_keys(repo)
|
226
|
+
repo = Repo.new(repo)
|
227
|
+
response = self.class.get("/repos/keys/#{repo.name}", :query => auth_params)
|
228
|
+
Hashie::Mash.new(response).public_keys
|
229
|
+
end
|
230
|
+
|
231
|
+
def add_deploy_key(repo, key, title='')
|
232
|
+
repo = Repo.new(repo)
|
233
|
+
response = self.class.post("/repos/key/#{repo.name}/add", :query => auth_params, :body => {:title => title, :key => key})
|
234
|
+
Hashie::Mash.new(response).public_keys
|
235
|
+
end
|
236
|
+
|
237
|
+
def remove_deploy_key(repo, id)
|
238
|
+
repo = Repo.new(repo)
|
239
|
+
response = self.class.post("/repos/key/#{repo.name}/remove", :query => auth_params, :body => {:id => id})
|
240
|
+
Hashie::Mash.new(response).public_keys
|
241
|
+
end
|
242
|
+
|
243
|
+
def collaborators(repo)
|
244
|
+
repo = Repo.new(repo)
|
245
|
+
response = self.class.post("/repos/show/#{repo.username}/#{repo.name}/collaborators", :query => auth_params)
|
246
|
+
Hashie::Mash.new(response).collaborators
|
247
|
+
end
|
248
|
+
|
249
|
+
def contributors(repo)
|
250
|
+
repo = Repo.new(repo)
|
251
|
+
response = self.class.get("/repos/show/#{repo.username}/#{repo.name}/contributors")
|
252
|
+
Hashie::Mash.new(response).contributors
|
253
|
+
end
|
254
|
+
|
255
|
+
def repo(repo)
|
256
|
+
repo = Repo.new(repo)
|
257
|
+
response = self.class.get("/repos/show/#{repo.username}/#{repo.name}", :query => auth_params)
|
258
|
+
Hashie::Mash.new(response).repository
|
259
|
+
end
|
260
|
+
|
261
|
+
# pass options without the "values[x]" descriped in the API docs:
|
262
|
+
# set_repo_info('user/repo', :description => "hey!", :has_wiki => false)
|
263
|
+
def set_repo_info(repo, options)
|
264
|
+
repo = Repo.new(repo)
|
265
|
+
# post body needs to be "values[has_wiki]=false"
|
266
|
+
response = self.class.post("/repos/show/#{repo.username}/#{repo.name}",
|
267
|
+
:body => options.keys.reduce({}) { |a,v| a["values[#{v}]"] = options[v]; a }.merge(auth_params))
|
268
|
+
Hashie::Mash.new(response).repository
|
269
|
+
end
|
270
|
+
|
271
|
+
def list_repos(username = nil)
|
272
|
+
if username.nil? && !@login.nil?
|
273
|
+
username = login
|
274
|
+
elsif username.nil?
|
275
|
+
raise ArgumentError, 'you must provide a username'
|
276
|
+
end
|
277
|
+
response = self.class.get("/repos/show/#{username}", :query => auth_params)
|
278
|
+
Hashie::Mash.new(response).repositories
|
279
|
+
end
|
280
|
+
|
281
|
+
def add_collaborator(repo, collaborator)
|
282
|
+
repo = Repo.new(repo)
|
283
|
+
response = self.class.post("/repos/collaborators/#{repo.name}/add/#{collaborator}", :query => auth_params)
|
284
|
+
Hashie::Mash.new(response).collaborators
|
285
|
+
end
|
286
|
+
|
287
|
+
def remove_collaborator(repo, collaborator)
|
288
|
+
repo = Repo.new(repo)
|
289
|
+
response = self.class.post("/repos/collaborators/#{repo.name}/remove/#{collaborator}", :query => auth_params)
|
290
|
+
Hashie::Mash.new(response).collaborators
|
291
|
+
end
|
292
|
+
|
293
|
+
def network(repo)
|
294
|
+
repo = Repo.new(repo)
|
295
|
+
response = self.class.get("/repos/show/#{repo.username}/#{repo.name}/network")
|
296
|
+
Hashie::Mash.new(response).network
|
297
|
+
end
|
298
|
+
|
299
|
+
def languages(repo)
|
300
|
+
repo = Repo.new(repo)
|
301
|
+
response = self.class.get("/repos/show/#{repo.username}/#{repo.name}/languages")
|
302
|
+
Hashie::Mash.new(response).languages
|
303
|
+
end
|
304
|
+
|
305
|
+
def tags(repo)
|
306
|
+
repo = Repo.new(repo)
|
307
|
+
response = self.class.get("/repos/show/#{repo.username}/#{repo.name}/tags")
|
308
|
+
Hashie::Mash.new(response).tags
|
309
|
+
end
|
310
|
+
|
311
|
+
def branches(repo)
|
312
|
+
repo = Repo.new(repo)
|
313
|
+
response = self.class.get("/repos/show/#{repo.username}/#{repo.name}/branches", :query => auth_params)
|
314
|
+
Hashie::Mash.new(response).branches
|
315
|
+
end
|
316
|
+
|
317
|
+
# Network
|
318
|
+
|
319
|
+
def network_meta(repo)
|
320
|
+
repo = Repo.new(repo)
|
321
|
+
response = self.class.get("http://github.com/#{repo.username}/#{repo.name}/network_meta")
|
322
|
+
Hashie::Mash.new(response)
|
323
|
+
end
|
324
|
+
|
325
|
+
def network_data(repo, nethash)
|
326
|
+
repo = Repo.new(repo)
|
327
|
+
response = self.class.get("http://github.com/#{repo.username}/#{repo.name}/network_data_chunk", :query => {:nethash => nethash})
|
328
|
+
Hashie::Mash.new(response).commits
|
329
|
+
end
|
330
|
+
|
331
|
+
# Trees
|
332
|
+
|
333
|
+
def tree(repo, sha)
|
334
|
+
repo = Repo.new(repo)
|
335
|
+
response = self.class.get("http://github.com/api/v2/json/tree/show/#{repo.username}/#{repo.name}/#{sha}", :query => auth_params)
|
336
|
+
Hashie::Mash.new(response).tree
|
337
|
+
end
|
338
|
+
|
339
|
+
def blob(repo, sha, path)
|
340
|
+
repo = Repo.new(repo)
|
341
|
+
response = self.class.get("http://github.com/api/v2/json/blob/show/#{repo.username}/#{repo.name}/#{sha}/#{path}", :query => auth_params)
|
342
|
+
Hashie::Mash.new(response).blob
|
343
|
+
end
|
344
|
+
|
345
|
+
def raw(repo, sha)
|
346
|
+
repo = Repo.new(repo)
|
347
|
+
response = self.class.get("http://github.com/api/v2/yaml/blob/show/#{repo.username}/#{repo.name}/#{sha}", :query => auth_params)
|
348
|
+
response.body
|
349
|
+
end
|
350
|
+
|
351
|
+
# Commits
|
352
|
+
|
353
|
+
def list_commits(repo, branch="master")
|
354
|
+
repo = Repo.new(repo)
|
355
|
+
response = self.class.get("http://github.com/api/v2/json/commits/list/#{repo.username}/#{repo.name}/#{branch}")
|
356
|
+
Hashie::Mash.new(response).commits
|
357
|
+
end
|
358
|
+
|
359
|
+
def commit(repo, sha)
|
360
|
+
repo = Repo.new(repo)
|
361
|
+
response = self.class.get("http://github.com/api/v2/json/commits/show/#{repo.username}/#{repo.name}/#{sha}")
|
362
|
+
Hashie::Mash.new(response).commit
|
363
|
+
end
|
364
|
+
|
365
|
+
def public_timeline(username = nil)
|
366
|
+
username ||= @login
|
367
|
+
if username.nil?
|
368
|
+
path = "http://github.com/timeline.json"
|
369
|
+
else
|
370
|
+
path = "http://github.com/#{username}.json"
|
371
|
+
end
|
372
|
+
response = self.class.get(path)
|
373
|
+
response.map{|item| Hashie::Mash.new(item)}
|
374
|
+
end
|
375
|
+
|
376
|
+
def timeline
|
377
|
+
response = self.class.get("http://github.com/#{@login}.private.json", :query => auth_params)
|
378
|
+
response.map{|item| Hashie::Mash.new(item)}
|
379
|
+
end
|
380
|
+
|
381
|
+
private
|
382
|
+
|
383
|
+
def auth_params
|
384
|
+
@token.nil? ? {} : {:login => @login, :token => @token}
|
385
|
+
end
|
386
|
+
|
387
|
+
def self.get(*args); handle_response super end
|
388
|
+
def self.post(*args); handle_response super end
|
389
|
+
|
390
|
+
def self.handle_response(response)
|
391
|
+
case response.code
|
392
|
+
when 401; raise Unauthorized.new
|
393
|
+
when 403; raise RateLimitExceeded.new
|
394
|
+
when 404; raise NotFound.new
|
395
|
+
when 400...500; raise ClientError.new
|
396
|
+
when 500...600; raise ServerError.new(response.code)
|
397
|
+
else; response
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
end
|