fcoury-octopi 0.0.3 → 0.0.4

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.rdoc CHANGED
@@ -28,6 +28,7 @@ Octopi is a Ruby interface to GitHub API v2 (http://develop.github.com). It's un
28
28
  # to get all repos for user: user.repositories
29
29
  repo = user.repository("octopi") # same as: Repository.find("fcoury", "octopi")
30
30
  puts "Repository: #{repo.name} - #{repo.description} (by #{repo.owner}) - #{repo.url}"
31
+ puts " Tags: #{repo.tags and repo.tags.map {|t| t.name}.join(", ")}"
31
32
 
32
33
  # commits of a the repository
33
34
  first_commit = repo.commits.first
@@ -47,8 +48,8 @@ Octopi is a Ruby interface to GitHub API v2 (http://develop.github.com). It's un
47
48
 
48
49
  == Author
49
50
 
50
- Felipe Coury - http://felipecoury.com<br/>
51
- HasMany.info blog - http://hasmany.info
51
+ * Felipe Coury - http://felipecoury.com
52
+ * HasMany.info blog - http://hasmany.info
52
53
 
53
54
  == Copyright
54
55
 
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 0
3
- :patch: 3
3
+ :patch: 4
4
4
  :major: 0
@@ -0,0 +1,32 @@
1
+ class Base
2
+ def initialize(api, hash)
3
+ @api = api
4
+ @keys = []
5
+
6
+ raise "Missing data for #{@resource}" unless hash
7
+
8
+ hash.each_pair do |k,v|
9
+ @keys << k
10
+ next if k =~ /\./
11
+ instance_variable_set("@#{k}", v)
12
+
13
+ self.class.send :define_method, "#{k}=" do |v|
14
+ instance_variable_set("@#{k}", v)
15
+ end
16
+
17
+ self.class.send :define_method, k do
18
+ instance_variable_get("@#{k}")
19
+ end
20
+ end
21
+ end
22
+
23
+ def property(p, v)
24
+ path = "#{self.class.path_for(:resource)}/#{p}"
25
+ @api.find(path, self.class.resource_name(:singular), v)
26
+ end
27
+ def save
28
+ hash = {}
29
+ @keys.each { |k| hash[k] = send(k) }
30
+ @api.save(self.path_for(:resource), hash)
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ module Octopi
2
+ class Blob < Base
3
+ include Resource
4
+ set_resource_name "blob"
5
+
6
+ resource_path "/blob/show/:id"
7
+
8
+ def self.find(user, repo, sha, path=nil)
9
+ user = user.login if user.is_a? User
10
+ repo = repo.name if repo.is_a? Repository
11
+ if path
12
+ super [user,repo,sha,path]
13
+ else
14
+ blob = ANONYMOUS_API.get_raw(path_for(:resource),
15
+ {:id => [user,repo,sha].join('/')})
16
+ new(ANONYMOUS_API, {:text => blob})
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ module Octopi
2
+ class Commit < Base
3
+ include Resource
4
+ find_path "/commits/list/:query"
5
+ resource_path "/commits/show/:id"
6
+
7
+ attr_accessor :repository
8
+
9
+ def self.find_all(user, name, branch = "master", repo = nil)
10
+ repository = repo if repo.is_a? Repository
11
+ user = user.login if user.is_a? User
12
+ repo = repo.name if repo.is_a? Repository
13
+ name = repo.name if name.is_a? Repository
14
+ commits = super [user, name, branch]
15
+ commits.each { |c| c.repository = repository } if repository
16
+ commits
17
+ end
18
+
19
+ def self.find(*args)
20
+ if args.last.is_a?(Commit)
21
+ commit = args.pop
22
+ super "#{commit.repo_identifier}"
23
+ else
24
+ user, name, sha = *args
25
+ user = user.login if user.is_a? User
26
+ name = repo.name if name.is_a? Repository
27
+ super [user, name, sha]
28
+ end
29
+ end
30
+
31
+ def details
32
+ self.class.find(self)
33
+ end
34
+
35
+ def repo_identifier
36
+ url_parts = url.split('/')
37
+ if @repository
38
+ parts = [@repository.owner, @repository.name, url_parts[6]]
39
+ else
40
+ parts = [url_parts[3], url_parts[4], url_parts[6]]
41
+ end
42
+
43
+ puts parts.join('/')
44
+ parts.join('/')
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,14 @@
1
+ module Octopi
2
+ class FileObject < Base
3
+ include Resource
4
+ set_resource_name "tree"
5
+
6
+ resource_path "/tree/show/:id"
7
+
8
+ def self.find(user, repo, sha)
9
+ user = user.login if user.is_a? User
10
+ repo = repo.name if repo.is_a? Repository
11
+ super [user,repo,sha]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ module Octopi
2
+ class Repository < Base
3
+ include Resource
4
+ set_resource_name "repository", "repositories"
5
+
6
+ find_path "/repos/search/:query"
7
+ resource_path "/repos/show/:id"
8
+
9
+ def tags
10
+ Tag.find(self.owner, self.name)
11
+ end
12
+
13
+ def clone_url
14
+ #FIXME: Return "git@github.com:#{self.owner}/#{self.name}.git" if
15
+ #user's logged in and owns this repo.
16
+ "git://github.com/#{self.owner}/#{self.name}.git"
17
+ end
18
+
19
+ def self.find_by_user(user)
20
+ user = user.login if user.is_a? User
21
+ find_plural(user, :resource)
22
+ end
23
+
24
+ def self.find(user, name)
25
+ user = user.login if user.is_a? User
26
+ name = repo.name if name.is_a? Repository
27
+ super [user,name]
28
+ end
29
+
30
+ def self.find_all(*args)
31
+ # FIXME: This should be URI escaped, but have to check how the API
32
+ # handles escaped characters first.
33
+ super args.join(" ").gsub(/ /,'+')
34
+ end
35
+
36
+ def commits(branch = "master")
37
+ Commit.find_all(owner, name, branch, self)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,66 @@
1
+ module Octopi
2
+ module Resource
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ base.set_resource_name(base.name)
6
+ (@@resources||={})[base.resource_name(:singular)] = base
7
+ (@@resources||={})[base.resource_name(:plural)] = base
8
+ end
9
+
10
+ def self.for(name)
11
+ @@resources[name]
12
+ end
13
+
14
+ module ClassMethods
15
+ def set_resource_name(singular, plural = "#{singular}s")
16
+ @resource_name = {:singular => declassify(singular), :plural => declassify(plural)}
17
+ end
18
+
19
+ def resource_name(key)
20
+ @resource_name[key]
21
+ end
22
+
23
+ def declassify(s)
24
+ (s.split('::').last || '').downcase if s
25
+ end
26
+
27
+ def find_path(path)
28
+ (@path_spec||={})[:find] = path
29
+ end
30
+
31
+ def resource_path(path)
32
+ (@path_spec||={})[:resource] = path
33
+ end
34
+
35
+ def find(s)
36
+ s = s.join('/') if s.is_a? Array
37
+ result = ANONYMOUS_API.find(path_for(:resource), @resource_name[:singular], s)
38
+ key = result.keys.first
39
+ if result[key].is_a? Array
40
+ result[key].map do |r|
41
+ new(ANONYMOUS_API, r)
42
+ end
43
+ else
44
+ Resource.for(key).new(ANONYMOUS_API, result[key])
45
+ end
46
+ end
47
+
48
+ def find_all(s)
49
+ find_plural(s, :find)
50
+ end
51
+
52
+ def find_plural(s,path)
53
+ s = s.join('/') if s.is_a? Array
54
+ ANONYMOUS_API.find_all(path_for(path), @resource_name[:plural], s).
55
+ map do |item|
56
+ payload = block_given? ? yield(item) : item
57
+ new(ANONYMOUS_API, payload)
58
+ end
59
+ end
60
+
61
+ def path_for(type)
62
+ @path_spec[type]
63
+ end
64
+ end
65
+ end
66
+ end
data/lib/octopi/tag.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Octopi
2
+ class Tag < Base
3
+ include Resource
4
+ set_resource_name "tag"
5
+
6
+ resource_path "/repos/show/:id"
7
+
8
+ def self.find(user, repo)
9
+ user = user.login if user.is_a? User
10
+ repo = repo.name if repo.is_a? Repository
11
+ find_plural([user,repo,'tags'], :resource){
12
+ |i| {:name => i.first, :hash => i.last }
13
+ }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ module Octopi
2
+ class User < Base
3
+ include Resource
4
+
5
+ find_path "/user/search/:query"
6
+ resource_path "/user/show/:id"
7
+
8
+ def repositories
9
+ Repository.find_by_user(login)
10
+ end
11
+
12
+ def repository(name)
13
+ Repository.find(login, name)
14
+ end
15
+
16
+ # takes one param, deep that indicates if returns
17
+ # only the user login or an user object
18
+ %w[followers following].each do |method|
19
+ define_method(method) do
20
+ user_property(method, false)
21
+ end
22
+ define_method("#{method}!") do
23
+ user_property(method, true)
24
+ end
25
+ end
26
+
27
+ def user_property(property, deep)
28
+ users = []
29
+ property(property, login).each_pair do |k,v|
30
+ return v unless deep
31
+
32
+ v.each { |u| users << User.find(u) }
33
+ end
34
+
35
+ users
36
+ end
37
+ end
38
+ end
data/lib/octopi.rb CHANGED
@@ -12,6 +12,11 @@ module Octopi
12
12
 
13
13
  class Api
14
14
  include HTTParty
15
+ CONTENT_TYPE = {
16
+ 'yaml' => 'application/x-yaml',
17
+ 'json' => 'application/json',
18
+ 'xml' => 'application/sml'
19
+ }
15
20
  base_uri "http://github.com/api/v2"
16
21
 
17
22
  attr_accessor :format
@@ -46,207 +51,42 @@ module Octopi
46
51
  def find_all(path, result_key, query)
47
52
  get(path, { :query => query, :id => query })[result_key]
48
53
  end
49
-
54
+ def get_raw(path, params)
55
+ get(path, params, 'plain')
56
+ end
57
+
50
58
  private
51
59
  def get(path, params = {}, format = "yaml")
52
60
  params.each_pair do |k,v|
53
61
  path = path.gsub(":#{k.to_s}", v)
54
62
  end
55
- # puts "GET: /#{format}#{path}"
56
- self.class.get("/#{format}#{path}")
57
- end
58
- end
59
-
60
- class Base
61
- def initialize(api, hash)
62
- @api = api
63
- @keys = []
64
-
65
- raise "Missing data for #{@resource}" unless hash
66
-
67
- hash.each_pair do |k,v|
68
- @keys << k
69
- instance_variable_set("@#{k}", v)
70
-
71
- self.class.send :define_method, "#{k}=" do |v|
72
- instance_variable_set("@#{k}", v)
73
- end
74
-
75
- self.class.send :define_method, k do
76
- instance_variable_get("@#{k}")
77
- end
78
- end
79
- end
80
-
81
- def property(p, v)
82
- path = "#{self.class.path_for(:resource)}/#{p}"
83
- @api.find(path, self.class.resource_name(:singular), v)
84
- end
85
-
86
- def save
87
- hash = {}
88
- @keys.each { |k| hash[k] = send(k) }
89
- @api.save(self.path_for(:resource), hash)
90
- end
91
- end
92
-
93
- module Resource
94
- def self.included(base)
95
- base.extend ClassMethods
96
- base.set_resource_name(base.name)
97
- (@@resources||={})[base.resource_name(:singular)] = base
98
- (@@resources||={})[base.resource_name(:plural)] = base
99
- end
100
-
101
- def self.for(name)
102
- @@resources[name]
103
- end
104
-
105
- module ClassMethods
106
- def set_resource_name(singular, plural = "#{singular}s")
107
- @resource_name = {:singular => declassify(singular), :plural => declassify(plural)}
108
- end
109
-
110
- def resource_name(key)
111
- @resource_name[key]
112
- end
113
-
114
- def declassify(s)
115
- (s.split('::').last || '').downcase if s
116
- end
117
-
118
- def find_path(path)
119
- (@path_spec||={})[:find] = path
120
- end
121
-
122
- def resource_path(path)
123
- (@path_spec||={})[:resource] = path
124
- end
125
-
126
- def find(s)
127
- result = ANONYMOUS_API.find(path_for(:resource), @resource_name[:singular], s)
128
- key = result.keys.first
129
- Resource.for(key).new(ANONYMOUS_API, result[key])
130
- end
131
-
132
- def find_all(s)
133
- find_plural(s, :find)
134
- end
135
-
136
- def find_plural(s,path)
137
- all = []
138
- result = ANONYMOUS_API.find_all(path_for(path), @resource_name[:plural], s)
139
- result.each do |item|
140
- all << new(ANONYMOUS_API, item)
141
- end
142
- all
143
- end
144
-
145
- def path_for(type)
146
- @path_spec[type]
147
- end
148
- end
149
- end
150
-
151
- class User < Base
152
- include Resource
153
-
154
- find_path "/user/search/:query"
155
- resource_path "/user/show/:id"
156
-
157
- def repositories
158
- Repository.find_by_user(login)
159
- end
160
-
161
- def repository(name)
162
- Repository.find(login, name)
163
- end
164
-
165
- # takes one param, deep that indicates if returns
166
- # only the user login or an user object
167
- %w[followers following].each do |method|
168
- define_method(method) do
169
- user_property(method, false)
170
- end
171
- define_method("#{method}!") do
172
- user_property(method, true)
173
- end
174
- end
175
-
176
- def user_property(property, deep)
177
- users = []
178
- property(property, login).each_pair do |k,v|
179
- return v unless deep
180
-
181
- v.each { |u| users << User.find(u) }
182
- end
183
-
184
- users
63
+ resp = self.class.get("/#{format}#{path}")
64
+ # FIXME: This fails for showing raw Git data because that call returns
65
+ # text/html as the content type. This issue has been reported.
66
+ ctype = resp.headers['content-type'].first
67
+ raise FormatError, [ctype, format] unless
68
+ ctype.match(/^#{CONTENT_TYPE[format]};/)
69
+ raise APIError,
70
+ "GitHub returned status #{resp.code}" unless resp.code.to_i == 200
71
+ if format == 'yaml' && resp['error']
72
+ raise APIError, resp['error'].first['error']
73
+ end
74
+ resp
185
75
  end
186
76
  end
187
77
 
188
- class Repository < Base
189
- include Resource
190
- set_resource_name "repository", "repositories"
191
-
192
- find_path "/repos/search/:query"
193
- resource_path "/repos/show/:id"
194
-
195
- def self.find_by_user(user)
196
- find_plural(user, :resource)
197
- end
198
-
199
- def self.find(user, name)
200
- super "#{user}/#{name}"
201
- end
202
-
203
- def self.find_all(*args)
204
- super args.join("+")
205
- end
206
-
207
- def commits(branch = "master")
208
- Commit.find_all(owner, name, branch, self)
209
- end
210
- end
78
+ %w{base resource user tag repository file_object blob commit}.
79
+ each{|f| require "#{File.dirname(__FILE__)}/octopi/#{f}"}
80
+
81
+ class FormatError < StandardError
82
+ def initialize(f)
83
+ $stderr.puts "Got unexpected format (got #{f.first} for #{f.last})"
84
+ end
85
+ end
211
86
 
212
- class Commit < Base
213
- include Resource
214
- find_path "/commits/list/:query"
215
- resource_path "/commits/show/:id"
216
-
217
- attr_accessor :repository
218
-
219
- def self.find_all(user, name, branch = "master", repo = nil)
220
- commits = super("#{user}/#{name}/#{branch}")
221
- commits.each { |c| c.repository = repo } if repo
222
- commits
223
- end
224
-
225
- def self.find(*args)
226
- if args.last.is_a?(Commit)
227
- commit = args.pop
228
- super "#{commit.repo_identifier}"
229
- else
230
- user, name, sha = *args
231
- super "#{user}/#{name}/#{sha}"
232
- end
233
- end
234
-
235
- def details
236
- self.class.find(self)
237
- end
238
-
239
- def repo_identifier
240
- url_parts = url.split('/')
241
- if @repository
242
- parts = [@repository.owner, @repository.name, url_parts[6]]
243
- else
244
- parts = [url_parts[3], url_parts[4], url_parts[6]]
245
- end
246
-
247
- puts parts.join('/')
248
- parts.join('/')
249
- end
250
- end
251
- class APIError < StandardError; end
87
+ class APIError < StandardError
88
+ def initialize(m)
89
+ $stderr.puts m
90
+ end
91
+ end
252
92
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fcoury-octopi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Coury
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-19 00:00:00 -07:00
12
+ date: 2009-04-20 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -25,6 +25,15 @@ extra_rdoc_files:
25
25
  files:
26
26
  - README.rdoc
27
27
  - VERSION.yml
28
+ - lib/octopi
29
+ - lib/octopi/base.rb
30
+ - lib/octopi/blob.rb
31
+ - lib/octopi/commit.rb
32
+ - lib/octopi/file_object.rb
33
+ - lib/octopi/repository.rb
34
+ - lib/octopi/resource.rb
35
+ - lib/octopi/tag.rb
36
+ - lib/octopi/user.rb
28
37
  - lib/octopi.rb
29
38
  - test/octopi_test.rb
30
39
  - test/test_helper.rb