github_api 0.3.9 → 0.4.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/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
|