github_api 0.3.9 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +331 -0
- data/features/README.rdoc +26 -0
- data/features/cassettes/repos/branches.yml +79 -0
- data/features/cassettes/repos/tags.yml +167 -0
- data/features/cassettes/repos/teams.yml +38 -0
- data/features/repos.feature +28 -0
- data/features/step_definitions/common_steps.rb +11 -0
- data/features/step_definitions/github_api_steps.rb +23 -0
- data/features/support/vcr.rb +6 -0
- data/lib/github_api.rb +12 -2
- data/lib/github_api/api.rb +9 -5
- data/lib/github_api/connection.rb +4 -4
- data/lib/github_api/constants.rb +38 -0
- data/lib/github_api/error.rb +5 -2
- data/lib/github_api/page_iterator.rb +118 -0
- data/lib/github_api/page_links.rb +45 -0
- data/lib/github_api/paged_request.rb +34 -0
- data/lib/github_api/repos.rb +2 -2
- data/lib/github_api/response/helpers.rb +8 -1
- data/lib/github_api/response/raise_error.rb +3 -0
- data/lib/github_api/result.rb +86 -8
- data/lib/github_api/utils/url.rb +46 -0
- data/lib/github_api/version.rb +2 -2
- data/spec/README.rdoc +4 -0
- data/spec/github/page_iterator_spec.rb +189 -0
- data/spec/github/page_links_spec.rb +34 -0
- data/spec/github/paged_request_spec.rb +38 -0
- data/spec/github/response/helpers_spec.rb +18 -0
- data/spec/github/result_spec.rb +67 -4
- data/spec/github/utils/url_spec.rb +36 -0
- metadata +37 -10
- data/README.rdoc +0 -235
@@ -0,0 +1,34 @@
|
|
1
|
+
module Github
|
2
|
+
module PagedRequest
|
3
|
+
include Github::Constants
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
FIRST_PAGE = 1
|
8
|
+
|
9
|
+
PER_PAGE = 25
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :page, :per_page
|
13
|
+
end
|
14
|
+
|
15
|
+
def default_page_size
|
16
|
+
Github.api_client.per_page ? Github.api_client.per_page : PER_PAGE
|
17
|
+
end
|
18
|
+
|
19
|
+
def default_page
|
20
|
+
Github.api_client.page ? Github.api_client.page : FIRST_PAGE
|
21
|
+
end
|
22
|
+
|
23
|
+
def page_request(path, params={})
|
24
|
+
params[PARAM_PER_PAGE] = default_page_size unless params[PARAM_PER_PAGE]
|
25
|
+
params[PARAM_PAGE] = default_page unless params[PARAM_PAGE]
|
26
|
+
|
27
|
+
Github::PagedRequest.page = params[PARAM_PAGE]
|
28
|
+
Github::PagedRequest.per_page = params[PARAM_PER_PAGE]
|
29
|
+
|
30
|
+
Github.api_client.get path, params
|
31
|
+
end
|
32
|
+
|
33
|
+
end # PagedRequest
|
34
|
+
end # Github
|
data/lib/github_api/repos.rb
CHANGED
@@ -225,11 +225,11 @@ module Github
|
|
225
225
|
def repos(*args)
|
226
226
|
params = args.last.is_a?(Hash) ? args.pop : {}
|
227
227
|
_normalize_params_keys(params)
|
228
|
-
|
228
|
+
_merge_user_into_params!(params) unless params.has_key?('user')
|
229
229
|
_filter_params_keys(%w[ org user type ], params)
|
230
230
|
|
231
231
|
response = if (user_name = params.delete("user"))
|
232
|
-
get("/users/#{user_name}/repos")
|
232
|
+
get("/users/#{user_name}/repos", params)
|
233
233
|
elsif (org_name = params.delete("org"))
|
234
234
|
get("/orgs/#{org_name}/repos", params)
|
235
235
|
else
|
@@ -6,7 +6,14 @@ module Github
|
|
6
6
|
class Response::Helpers < Response
|
7
7
|
|
8
8
|
def on_complete(env)
|
9
|
-
env[:body].
|
9
|
+
env[:body].class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
10
|
+
include Github::Result
|
11
|
+
|
12
|
+
def env
|
13
|
+
@env
|
14
|
+
end
|
15
|
+
|
16
|
+
RUBY_EVAL
|
10
17
|
env[:body].instance_eval { @env = env }
|
11
18
|
end
|
12
19
|
|
@@ -16,6 +16,9 @@ module Github
|
|
16
16
|
raise Github::Forbidden.new(response_message(env), env[:response_headers])
|
17
17
|
when 404
|
18
18
|
raise Github::ResourceNotFound.new(response_message(env), env[:response_headers])
|
19
|
+
|
20
|
+
when 422
|
21
|
+
raise Github::UnprocessableEntitty.new(response_message(env), env[:response_headers])
|
19
22
|
when 500
|
20
23
|
raise Github::InternalServerError.new(response_message(env), env[:response_headers])
|
21
24
|
when 503
|
data/lib/github_api/result.rb
CHANGED
@@ -2,16 +2,17 @@
|
|
2
2
|
|
3
3
|
module Github
|
4
4
|
module Result
|
5
|
+
include Github::Constants
|
5
6
|
|
6
|
-
|
7
|
-
CONTENT_TYPE = 'Content-Type'.freeze
|
8
|
-
CONTENT_LENGTH = 'content-length'.freeze
|
9
|
-
|
10
|
-
attr_reader :env
|
7
|
+
# TODO Add result counts method to check total items looking at result links
|
11
8
|
|
12
9
|
# Requests are limited to API v3 to 5000 per hour.
|
13
|
-
def
|
14
|
-
loaded? ? @env[:response_headers][
|
10
|
+
def ratelimit_limit
|
11
|
+
loaded? ? @env[:response_headers][RATELIMIT_LIMIT] : nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def ratelimit_remaining
|
15
|
+
loaded? ? @env[:response_headers][RATELIMIT_REMAINING] : nil
|
15
16
|
end
|
16
17
|
|
17
18
|
def content_type
|
@@ -30,12 +31,89 @@ module Github
|
|
30
31
|
(200..299).include? status
|
31
32
|
end
|
32
33
|
|
34
|
+
# Returns raw body
|
33
35
|
def body
|
34
36
|
loaded? ? @env[:body] : nil
|
35
37
|
end
|
36
38
|
|
37
39
|
def loaded?
|
38
|
-
|
40
|
+
!!@env
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return page links
|
44
|
+
def links
|
45
|
+
@@links = Github::PageLinks.new(@env[:response_headers])
|
46
|
+
end
|
47
|
+
|
48
|
+
# Iterator like each for response pages. If there are no pages to
|
49
|
+
# iterate over this method will return nothing.
|
50
|
+
def each_page
|
51
|
+
while page_iterator.has_next?
|
52
|
+
yield next_page #page_iterator.next
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Retrives the result of the first page. Returns <tt>nil</tt> if there is
|
57
|
+
# no first page - either because you are already on the first page
|
58
|
+
# or there are no pages at all in the result.
|
59
|
+
def first_page
|
60
|
+
first_request = page_iterator.first
|
61
|
+
self.instance_eval { @env = first_request.env } if first_request
|
62
|
+
first_request
|
63
|
+
end
|
64
|
+
|
65
|
+
# Retrives the result of the next page. Returns <tt>nil</tt> if there is
|
66
|
+
# no next page or no pages at all.
|
67
|
+
def next_page
|
68
|
+
next_request = page_iterator.next
|
69
|
+
self.instance_eval { @env = next_request.env } if next_request
|
70
|
+
next_request
|
71
|
+
end
|
72
|
+
|
73
|
+
# Retrives the result of the previous page. Returns <tt>nil</tt> if there is
|
74
|
+
# no previous page or no pages at all.
|
75
|
+
def prev_page
|
76
|
+
prev_request = page_iterator.prev
|
77
|
+
self.instance_eval { @env = prev_request.env } if prev_request
|
78
|
+
prev_request
|
79
|
+
end
|
80
|
+
alias :previous_page :prev_page
|
81
|
+
|
82
|
+
# Retrives the result of the last page. Returns <tt>nil</tt> if there is
|
83
|
+
# no last page - either because you are already on the last page,
|
84
|
+
# there is only one page or there are no pages at all in the result.
|
85
|
+
def last_page
|
86
|
+
last_request = page_iterator.last
|
87
|
+
self.instance_eval { @env = last_request.env } if last_request
|
88
|
+
last_request
|
89
|
+
end
|
90
|
+
|
91
|
+
# Retrives a specific result for a page given page number.
|
92
|
+
# The <tt>page_number</tt> parameter is not validate, hitting a page
|
93
|
+
# that does not exist will return Github API error. Consequently, if
|
94
|
+
# there is only one page, this method returns nil
|
95
|
+
def page(page_number)
|
96
|
+
request = page_iterator.get_page(page_number)
|
97
|
+
self.instance_eval { @env = request.env } if request
|
98
|
+
request
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns <tt>true</tt> if there is another page in the result set,
|
102
|
+
# otherwise <tt>false</tt>
|
103
|
+
def has_next_page?
|
104
|
+
page_iterator.has_next?
|
105
|
+
end
|
106
|
+
|
107
|
+
# Repopulates objects for new values
|
108
|
+
def reset
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
# Internally used page iterator
|
115
|
+
def page_iterator # :nodoc:
|
116
|
+
@@page_iterator = Github::PageIterator.new(@env)
|
39
117
|
end
|
40
118
|
|
41
119
|
end # Result
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Github
|
4
|
+
module Utils
|
5
|
+
module Url
|
6
|
+
extend self
|
7
|
+
|
8
|
+
DEFAULT_QUERY_SEP = /[&;] */n
|
9
|
+
|
10
|
+
KEY_VALUE_SEP = '='.freeze
|
11
|
+
|
12
|
+
def escape(s) CGI.escape s.to_s end
|
13
|
+
|
14
|
+
def unescape(s) CGI.unescape s.to_s end
|
15
|
+
|
16
|
+
def parse_query(query_string)
|
17
|
+
return '' if query_string.nil? || query_string.empty?
|
18
|
+
params = {}
|
19
|
+
|
20
|
+
query_string.split(DEFAULT_QUERY_SEP).each do |part|
|
21
|
+
k, v = part.split(KEY_VALUE_SEP, 2).map { |el| unescape(el) }
|
22
|
+
|
23
|
+
if cur = params[k]
|
24
|
+
if cur.class == Array
|
25
|
+
params[k] << v
|
26
|
+
else
|
27
|
+
params[k] = [cur, v]
|
28
|
+
end
|
29
|
+
else
|
30
|
+
params[k] = v
|
31
|
+
end
|
32
|
+
end
|
33
|
+
params
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_query_for_param(query_string, name)
|
37
|
+
parse_query(query_string).each do |key, val|
|
38
|
+
return val.first if (name == key) && val.is_a?(Array)
|
39
|
+
return val if (name == key)
|
40
|
+
end
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
|
44
|
+
end # Url
|
45
|
+
end # Utils
|
46
|
+
end # Github
|
data/lib/github_api/version.rb
CHANGED
data/spec/README.rdoc
CHANGED
@@ -9,6 +9,10 @@ To run the specs first run the +bundle+ command to install the necessary gems an
|
|
9
9
|
|
10
10
|
The specs currently require Ruby 1.9.x.
|
11
11
|
|
12
|
+
In order to run only features
|
13
|
+
|
14
|
+
rake features
|
15
|
+
|
12
16
|
== Coverage
|
13
17
|
|
14
18
|
GithubAPI will run coverage only on demand and to run it do
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Github::PageIterator do
|
4
|
+
|
5
|
+
let(:link) {
|
6
|
+
"<https://api.github.com/users/wycats/repos?page=4&per_page=20>; rel=\"next\", <https://api.github.com/users/wycats/repos?page=6&per_page=20>; rel=\"last\", <https://api.github.com/users/wycats/repos?page=1&per_page=20>; rel=\"first\", <https://api.github.com/users/wycats/repos?page=2&per_page=20>; rel=\"prev\""
|
7
|
+
}
|
8
|
+
let(:env) { { :response_headers => {'Link' => link } } }
|
9
|
+
let(:first) { "https://api.github.com/users/wycats/repos?page=1&per_page=20" }
|
10
|
+
let(:nexxt) { "https://api.github.com/users/wycats/repos?page=4&per_page=20" }
|
11
|
+
let(:prev) { "https://api.github.com/users/wycats/repos?page=2&per_page=20" }
|
12
|
+
let(:last) { "https://api.github.com/users/wycats/repos?page=6&per_page=20" }
|
13
|
+
let(:user) { 'wycats' }
|
14
|
+
|
15
|
+
let(:instance) { Github::PageIterator.new(env) }
|
16
|
+
|
17
|
+
it { described_class::ATTRIBUTES.should_not be_nil }
|
18
|
+
|
19
|
+
context 'initialization' do
|
20
|
+
|
21
|
+
it { instance.first_page.should eq 1 }
|
22
|
+
it { instance.first_page_uri.should eq first }
|
23
|
+
it { instance.next_page.should eq 4 }
|
24
|
+
it { instance.next_page_uri.should eq nexxt }
|
25
|
+
it { instance.prev_page.should eq 2 }
|
26
|
+
it { instance.prev_page_uri.should eq prev }
|
27
|
+
it { instance.last_page.should eql 6 }
|
28
|
+
it { instance.last_page_uri.should eq last }
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'has_next?' do
|
33
|
+
it "return true when next_page_uri is present" do
|
34
|
+
instance.has_next?.should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns false when next_page_uri is nil" do
|
38
|
+
instance.should_receive(:next_page_uri).and_return nil
|
39
|
+
instance.has_next?.should be_false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'first' do
|
44
|
+
before do
|
45
|
+
Github.new
|
46
|
+
instance.stub(:has_next?).and_return true
|
47
|
+
stub_get("/users/#{user}/repos").
|
48
|
+
with(:query => { 'per_page' => '20'}).
|
49
|
+
to_return(:body => '', :status => 200,
|
50
|
+
:headers => {
|
51
|
+
:content_type => "application/json; charset=utf-8",
|
52
|
+
'Link' => link
|
53
|
+
}
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'returns nil if there are no more pages' do
|
58
|
+
instance.stub(:first_page_uri).and_return false
|
59
|
+
instance.first.should be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'performs request' do
|
63
|
+
link.stub(:links).and_return link
|
64
|
+
instance.stub(:update_page_links)
|
65
|
+
instance.should_receive(:page_request).
|
66
|
+
with("https://api.github.com/users/#{user}/repos", 'per_page' => 20).
|
67
|
+
and_return link
|
68
|
+
instance.first
|
69
|
+
end
|
70
|
+
end # first
|
71
|
+
|
72
|
+
context 'next' do
|
73
|
+
before do
|
74
|
+
Github.new
|
75
|
+
instance.stub(:has_next?).and_return true
|
76
|
+
stub_get("/users/#{user}/repos").
|
77
|
+
with(:query => { 'page' => '4', 'per_page' => '20'}).
|
78
|
+
to_return(:body => '', :status => 200,
|
79
|
+
:headers => {
|
80
|
+
:content_type => "application/json; charset=utf-8",
|
81
|
+
'Link' => link
|
82
|
+
}
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'returns nil if there are no more pages' do
|
87
|
+
instance.stub(:has_next?).and_return false
|
88
|
+
instance.next.should be_nil
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'performs request' do
|
92
|
+
link.stub(:links).and_return link
|
93
|
+
instance.stub(:update_page_links)
|
94
|
+
instance.should_receive(:page_request).
|
95
|
+
with("https://api.github.com/users/#{user}/repos",
|
96
|
+
'page' => 4,'per_page' => 20).and_return link
|
97
|
+
instance.next
|
98
|
+
end
|
99
|
+
end # next
|
100
|
+
|
101
|
+
context 'prev' do
|
102
|
+
before do
|
103
|
+
Github.new
|
104
|
+
instance.stub(:has_next?).and_return true
|
105
|
+
stub_get("/users/#{user}/repos").
|
106
|
+
with(:query => { 'page' => '2', 'per_page' => '20'}).
|
107
|
+
to_return(:body => '', :status => 200,
|
108
|
+
:headers => {
|
109
|
+
:content_type => "application/json; charset=utf-8",
|
110
|
+
'Link' => link
|
111
|
+
}
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'returns nil if there are no previous pages' do
|
116
|
+
instance.stub(:prev_page_uri).and_return false
|
117
|
+
instance.prev.should be_nil
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'performs request' do
|
121
|
+
link.stub(:links).and_return link
|
122
|
+
instance.stub(:update_page_links)
|
123
|
+
instance.should_receive(:page_request).
|
124
|
+
with("https://api.github.com/users/#{user}/repos",
|
125
|
+
'page' => 2,'per_page' => 20).and_return link
|
126
|
+
instance.prev
|
127
|
+
end
|
128
|
+
end # prev
|
129
|
+
|
130
|
+
context 'last' do
|
131
|
+
before do
|
132
|
+
Github.new
|
133
|
+
instance.stub(:has_next?).and_return true
|
134
|
+
stub_get("/users/#{user}/repos").
|
135
|
+
with(:query => { 'page' => '6', 'per_page' => '20'}).
|
136
|
+
to_return(:body => '', :status => 200,
|
137
|
+
:headers => {
|
138
|
+
:content_type => "application/json; charset=utf-8",
|
139
|
+
'Link' => link
|
140
|
+
}
|
141
|
+
)
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'returns nil if there is not last page' do
|
145
|
+
instance.stub(:last_page_uri).and_return false
|
146
|
+
instance.last.should be_nil
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'performs request' do
|
150
|
+
link.stub(:links).and_return link
|
151
|
+
instance.stub(:update_page_links)
|
152
|
+
instance.should_receive(:page_request).
|
153
|
+
with("https://api.github.com/users/#{user}/repos",
|
154
|
+
'page' => 6,'per_page' => 20).and_return link
|
155
|
+
instance.last
|
156
|
+
end
|
157
|
+
end # last
|
158
|
+
|
159
|
+
context 'get_page' do
|
160
|
+
before do
|
161
|
+
Github.new
|
162
|
+
instance.stub(:has_next?).and_return true
|
163
|
+
stub_get("/users/#{user}/repos").
|
164
|
+
with(:query => { 'page' => '6', 'per_page' => '20'}).
|
165
|
+
to_return(:body => '', :status => 200,
|
166
|
+
:headers => {
|
167
|
+
:content_type => "application/json; charset=utf-8",
|
168
|
+
'Link' => link
|
169
|
+
}
|
170
|
+
)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'returns nil if there are no pages' do
|
174
|
+
instance.stub(:first_page_uri).and_return nil
|
175
|
+
instance.stub(:last_page_uri).and_return nil
|
176
|
+
instance.get_page(5).should be_nil
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'finds a single page' do
|
180
|
+
instance.should_receive(:update_page_links)
|
181
|
+
instance.should_receive(:page_request).
|
182
|
+
with("https://api.github.com/users/#{user}/repos",
|
183
|
+
'page' => 2, 'per_page' => 20).and_return link
|
184
|
+
link.stub(:links).and_return link
|
185
|
+
instance.get_page(2)
|
186
|
+
end
|
187
|
+
end # get_page
|
188
|
+
|
189
|
+
end # Github::PageIterator
|