travis-gh 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/lib/gh/cache.rb +80 -0
- data/lib/gh/case.rb +13 -0
- data/lib/gh/custom_limit.rb +29 -0
- data/lib/gh/error.rb +71 -0
- data/lib/gh/instrumentation.rb +39 -0
- data/lib/gh/lazy_loader.rb +28 -0
- data/lib/gh/link_follower.rb +25 -0
- data/lib/gh/merge_commit.rb +89 -0
- data/lib/gh/nested_resources.rb +51 -0
- data/lib/gh/normalizer.rb +121 -0
- data/lib/gh/pagination.rb +64 -0
- data/lib/gh/parallel.rb +68 -0
- data/lib/gh/remote.rb +163 -0
- data/lib/gh/response.rb +101 -0
- data/lib/gh/response_wrapper.rb +14 -0
- data/lib/gh/response_x_header_formatter.rb +13 -0
- data/lib/gh/stack.rb +64 -0
- data/lib/gh/token_check.rb +35 -0
- data/lib/gh/version.rb +6 -0
- data/lib/gh/wrapper.rb +235 -0
- data/lib/gh.rb +84 -0
- metadata +179 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 194688122c14fd85510b9a099c5857c7e6638a5525a6414e1303d4422606b050
|
4
|
+
data.tar.gz: 43c35e35441b76db6c188f348ef4a522b67b0db766401d1231256c48f9a8abb4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c036a243cd1c7b6dad6f66e9b47d5a2e0bbfbb206ff52fe3b66e3131255e54a576d91afb47c977def57c3d15edac5fc1800779168fd3d177b4e503bbe614eae2
|
7
|
+
data.tar.gz: b58de545c86a16fc389d221049d757c23c0aa39d76c0b2e9faf645a276824c5a13fd25c2d3d175be58b9e607740adfd337342206238e85a2e161ef469e7ff8c0
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Konstantin Haase
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/gh/cache.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gh'
|
4
|
+
|
5
|
+
module GH
|
6
|
+
# Public: This class caches responses.
|
7
|
+
class Cache < Wrapper
|
8
|
+
# Public: Get/set cache to use. Compatible with Rails/ActiveSupport cache.
|
9
|
+
attr_accessor :cache
|
10
|
+
|
11
|
+
# Internal: Simple in-memory cache basically implementing a copying GC.
|
12
|
+
class SimpleCache
|
13
|
+
# Internal: Initializes a new SimpleCache.
|
14
|
+
#
|
15
|
+
# size - Number of objects to hold in cache.
|
16
|
+
def initialize(size = 2048)
|
17
|
+
@old = {}
|
18
|
+
@new = {}
|
19
|
+
@size = size / 2
|
20
|
+
@mutex = Mutex.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# Internal: Tries to fetch a value from the cache and if it doesn't exist, generates it from the
|
24
|
+
# block given.
|
25
|
+
def fetch(key)
|
26
|
+
if @new.size > @size
|
27
|
+
@mutex.synchronize do
|
28
|
+
if @new.size > @size
|
29
|
+
@old = @new
|
30
|
+
@new = {}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
@new[key] ||= @old[key] || yield
|
35
|
+
end
|
36
|
+
|
37
|
+
# Internal: ...
|
38
|
+
def clear
|
39
|
+
@mutex.synchronize do
|
40
|
+
@old = {}
|
41
|
+
@new = {}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Internal: Initializes a new Cache instance.
|
47
|
+
def setup(*)
|
48
|
+
# self.cache ||= Rails.cache if defined? Rails.cache and defined? RAILS_CACHE
|
49
|
+
# self.cache ||= ActiveSupport::Cache.lookup_store if defined? ActiveSupport::Cache.lookup_store
|
50
|
+
self.cache ||= SimpleCache.new
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
# Public: ...
|
55
|
+
def reset
|
56
|
+
super
|
57
|
+
clear_partial or clear_all
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def fetch_resource(key)
|
63
|
+
cache.fetch(prefixed(key)) { super }
|
64
|
+
end
|
65
|
+
|
66
|
+
def clear_partial
|
67
|
+
return false unless cache.respond_to? :delete_matched
|
68
|
+
|
69
|
+
pattern = '^' << Regexp.escape(prefixed(''))
|
70
|
+
cache.delete_matched Regexp.new(pattern)
|
71
|
+
true
|
72
|
+
rescue NotImplementedError
|
73
|
+
false
|
74
|
+
end
|
75
|
+
|
76
|
+
def clear_all
|
77
|
+
cache.clear
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/gh/case.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GH
|
4
|
+
# Adds Client info so even unauthenticated requests can use a custom request limit
|
5
|
+
class CustomLimit < Wrapper
|
6
|
+
attr_accessor :client_id, :client_secret
|
7
|
+
|
8
|
+
def setup(backend, options)
|
9
|
+
@client_id = options[:client_id]
|
10
|
+
@client_secret = options[:client_secret]
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def full_url(key)
|
15
|
+
return super unless client_id
|
16
|
+
|
17
|
+
url = super
|
18
|
+
params = url.query_values || {}
|
19
|
+
|
20
|
+
unless params.include? 'client_id'
|
21
|
+
params['client_id'] = client_id
|
22
|
+
params['client_secret'] = client_secret
|
23
|
+
end
|
24
|
+
|
25
|
+
url.query_values = params
|
26
|
+
url
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/gh/error.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gh'
|
4
|
+
|
5
|
+
module GH
|
6
|
+
class Error < StandardError
|
7
|
+
attr_reader :info
|
8
|
+
|
9
|
+
def initialize(error = nil, payload = nil, info = {})
|
10
|
+
super(error)
|
11
|
+
|
12
|
+
info = info.merge(error.info) if error.respond_to?(:info) && error.info.is_a?(Hash)
|
13
|
+
error = error.error while error.respond_to? :error
|
14
|
+
@info = info.merge(error:, payload:)
|
15
|
+
|
16
|
+
return unless error
|
17
|
+
|
18
|
+
set_backtrace error.backtrace if error.respond_to? :backtrace
|
19
|
+
return unless error.respond_to?(:response) && error.response
|
20
|
+
|
21
|
+
case response = error.response
|
22
|
+
when Hash
|
23
|
+
@info[:response_status] = response[:status]
|
24
|
+
@info[:response_headers] = response[:headers]
|
25
|
+
@info[:response_body] = response[:body]
|
26
|
+
when Faraday::Response
|
27
|
+
@info[:response_status] = response.status
|
28
|
+
@info[:response_headers] = response.headers
|
29
|
+
@info[:response_body] = response.body
|
30
|
+
else
|
31
|
+
@info[:response] = response
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def payload
|
36
|
+
info[:payload]
|
37
|
+
end
|
38
|
+
|
39
|
+
def error
|
40
|
+
info[:error]
|
41
|
+
end
|
42
|
+
|
43
|
+
def message
|
44
|
+
(['GH request failed'] + info.map { |k, v| entry(k, v) }).join("\n")
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def entry(key, value)
|
50
|
+
value = "#{value.class}: #{value.message}" if value.is_a?(Exception)
|
51
|
+
value = value.inspect unless value.is_a?(String)
|
52
|
+
value.gsub!(/"Basic .+"|(client_(?:id|secret)=)[^&\s]+/, '\1[removed]')
|
53
|
+
"#{key}: ".ljust(20) + value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class TokenInvalid < Error
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.Error(conditions)
|
61
|
+
Module.new do
|
62
|
+
define_singleton_method(:===) do |exception|
|
63
|
+
return false unless exception.is_a?(Error) && !exception.info.nil?
|
64
|
+
|
65
|
+
# rubocop:disable Style/CaseEquality
|
66
|
+
conditions.all? { |k, v| v === exception.info[k] }
|
67
|
+
# rubocop:enable Style/CaseEquality
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gh'
|
4
|
+
|
5
|
+
module GH
|
6
|
+
# Public: This class caches responses.
|
7
|
+
class Instrumentation < Wrapper
|
8
|
+
# Public: Get/set instrumenter to use. Compatible with ActiveSupport::Notification and Travis::EventLogger.
|
9
|
+
attr_accessor :instrumenter
|
10
|
+
|
11
|
+
def setup(backend, options)
|
12
|
+
self.instrumenter ||= Travis::EventLogger.method(:notify) if defined? Travis::EventLogger
|
13
|
+
self.instrumenter ||= ActiveSupport::Notifications.method(:instrument) if defined? ActiveSupport::Notifications
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def http(verb, url, *)
|
18
|
+
instrument(:http, verb:, url:) { super }
|
19
|
+
end
|
20
|
+
|
21
|
+
def load(data)
|
22
|
+
instrument(:load, data:) { super }
|
23
|
+
end
|
24
|
+
|
25
|
+
def [](key)
|
26
|
+
instrument(:access, key:) { super }
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def instrument(type, payload = {})
|
32
|
+
return yield unless instrumenter
|
33
|
+
|
34
|
+
result = nil
|
35
|
+
instrumenter.call("#{type}.gh", payload.merge(gh: frontend)) { result = yield }
|
36
|
+
result
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gh'
|
4
|
+
|
5
|
+
module GH
|
6
|
+
# Public: ...
|
7
|
+
class LazyLoader < Wrapper
|
8
|
+
wraps GH::Normalizer
|
9
|
+
double_dispatch
|
10
|
+
|
11
|
+
def modify_hash(hash, loaded = false) # rubocop:disable Style/OptionalBooleanParameter
|
12
|
+
hash = super(hash)
|
13
|
+
link = hash['_links']['self'] unless loaded || hash['_links'].nil?
|
14
|
+
setup_lazy_loading(hash, link['href']) if link
|
15
|
+
hash
|
16
|
+
rescue StandardError => e
|
17
|
+
raise Error.new(e, hash)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def lazy_load(hash, _key, link)
|
23
|
+
modify_hash(backend[link].data, true)
|
24
|
+
rescue StandardError => e
|
25
|
+
raise Error.new(e, hash)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GH
|
4
|
+
class LinkFollower < Wrapper
|
5
|
+
wraps GH::Normalizer
|
6
|
+
double_dispatch
|
7
|
+
|
8
|
+
def modify_hash(hash)
|
9
|
+
hash = super(hash)
|
10
|
+
setup_lazy_loading(hash) if hash['_links']
|
11
|
+
hash
|
12
|
+
rescue StandardError => e
|
13
|
+
raise Error.new(e, hash)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def lazy_load(hash, key)
|
19
|
+
link = hash['_links'][key]
|
20
|
+
{ key => self[link['href']] } if link
|
21
|
+
rescue StandardError => e
|
22
|
+
raise Error.new(e, hash)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gh'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
module GH
|
7
|
+
# Public: ...
|
8
|
+
class MergeCommit < Wrapper
|
9
|
+
wraps GH::Normalizer
|
10
|
+
double_dispatch
|
11
|
+
|
12
|
+
def setup(backend, options)
|
13
|
+
@ssl = options[:ssl]
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def modify_hash(hash)
|
18
|
+
setup_lazy_loading(super)
|
19
|
+
rescue StandardError => e
|
20
|
+
raise Error.new(e, hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def lazy_load(hash, key)
|
26
|
+
return unless key =~ (/^(merge|head|base)_commit$/) && hash.include?('mergeable')
|
27
|
+
return unless merge_commit?(hash)
|
28
|
+
|
29
|
+
fields = pull_request_refs(hash)
|
30
|
+
fields['base_commit'] ||= commit_for hash, hash['base']
|
31
|
+
fields['head_commit'] ||= commit_for hash, hash['head']
|
32
|
+
fields
|
33
|
+
rescue StandardError => e
|
34
|
+
raise Error.new(e, hash)
|
35
|
+
end
|
36
|
+
|
37
|
+
def commit_for(from, hash)
|
38
|
+
{ 'sha' => hash['sha'], 'ref' => hash['ref'],
|
39
|
+
'_links' => { 'self' => { 'href' => git_url_for(from, hash['sha']) } } }
|
40
|
+
end
|
41
|
+
|
42
|
+
def git_url_for(hash, commitish)
|
43
|
+
hash['_links']['self']['href'].gsub(%r{/pulls/(\d+)$}, "/git/#{commitish}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def pull_request_refs(hash)
|
47
|
+
link = git_url_for(hash, 'refs/pull/\1')
|
48
|
+
commits = self[link].map do |data|
|
49
|
+
ref = data['ref']
|
50
|
+
name = "#{ref.split('/').last}_commit"
|
51
|
+
object = data['object'].merge 'ref' => ref
|
52
|
+
[name, object]
|
53
|
+
end
|
54
|
+
commits.to_h
|
55
|
+
end
|
56
|
+
|
57
|
+
def merge_commit?(hash)
|
58
|
+
force_merge_commit(hash)
|
59
|
+
hash['mergeable']
|
60
|
+
end
|
61
|
+
|
62
|
+
def github_done_checking?(hash)
|
63
|
+
case hash['mergeable_state']
|
64
|
+
when 'checking' then false
|
65
|
+
when 'unknown' then hash['merged']
|
66
|
+
when 'clean', 'dirty', 'unstable', 'stable', 'blocked', 'behind', 'has_hooks', 'draft' then true
|
67
|
+
else raise "unknown mergeable_state #{hash['mergeable_state'].inspect} for #{url(hash)}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def force_merge_commit(hash)
|
72
|
+
Timeout.timeout(180) do
|
73
|
+
update(hash) until github_done_checking? hash
|
74
|
+
end
|
75
|
+
rescue TimeoutError
|
76
|
+
status = hash['mergeable_state'].inspect
|
77
|
+
raise TimeoutError, "gave up waiting for github to check the merge status (current status is #{status})"
|
78
|
+
end
|
79
|
+
|
80
|
+
def update(hash)
|
81
|
+
hash.merge! backend[url(hash)]
|
82
|
+
sleep 0.5
|
83
|
+
end
|
84
|
+
|
85
|
+
def url(hash)
|
86
|
+
hash['_links']['self']['href']
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gh'
|
4
|
+
|
5
|
+
module GH
|
6
|
+
# Public: ...
|
7
|
+
class NestedResources < Wrapper
|
8
|
+
wraps GH::Normalizer
|
9
|
+
double_dispatch
|
10
|
+
|
11
|
+
def modify_hash(hash, loaded = false) # rubocop:disable Style/OptionalBooleanParameter
|
12
|
+
hash = super(hash)
|
13
|
+
link = hash['_links']['self'] unless loaded || hash['_links'].nil?
|
14
|
+
set_links hash, Addressable::URI.parse(link['href']) if link
|
15
|
+
hash
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(hash, link, name, path = name)
|
19
|
+
hash['_links'][name] ||= { 'href' => nested(link, path) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def nested(link, path)
|
23
|
+
new_link = link.dup
|
24
|
+
if path.start_with? '/'
|
25
|
+
new_link.path = path
|
26
|
+
else
|
27
|
+
new_link.path += path
|
28
|
+
end
|
29
|
+
new_link
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_links(hash, link)
|
33
|
+
case link.path
|
34
|
+
when '/gists'
|
35
|
+
add hash, link, 'public'
|
36
|
+
add hash, link, 'starred'
|
37
|
+
when %r{^/repos/[^/]+/[^/]+$}
|
38
|
+
add hash, link, 'commits', 'git/commits'
|
39
|
+
add hash, link, 'refs', 'git/refs'
|
40
|
+
add hash, link, 'tags', 'git/tags'
|
41
|
+
add hash, link, 'issues'
|
42
|
+
when %r{^/repos/[^/]+/[^/]+/issues/\d+$}
|
43
|
+
add hash, link, 'comments'
|
44
|
+
add hash, link, 'events'
|
45
|
+
when '/user'
|
46
|
+
add hash, link, 'gists', '/gists'
|
47
|
+
add hash, link, 'issues', '/issues'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gh'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
module GH
|
7
|
+
# Public: A Wrapper class that deals with normalizing Github responses.
|
8
|
+
class Normalizer < Wrapper
|
9
|
+
def generate_response(key, response)
|
10
|
+
result = super
|
11
|
+
links(result)['self'] ||= { 'href' => frontend.full_url(key).to_s } if result.respond_to? :to_hash
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
double_dispatch
|
18
|
+
|
19
|
+
def links(hash)
|
20
|
+
hash = hash.data if hash.respond_to? :data
|
21
|
+
hash['_links'] ||= {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_link(hash, type, href)
|
25
|
+
links(hash)[type] = { 'href' => href }
|
26
|
+
end
|
27
|
+
|
28
|
+
def modify_response(response)
|
29
|
+
response = response.dup
|
30
|
+
response.data = modify response.data
|
31
|
+
response
|
32
|
+
end
|
33
|
+
|
34
|
+
def modify_hash(hash)
|
35
|
+
corrected = {}
|
36
|
+
corrected.default_proc = hash.default_proc if hash.default_proc
|
37
|
+
|
38
|
+
hash.each_pair do |key, value|
|
39
|
+
key = modify_key(key, value)
|
40
|
+
next if modify_url(corrected, key, value)
|
41
|
+
next if modify_time(corrected, key, value)
|
42
|
+
|
43
|
+
corrected[key] = modify(value)
|
44
|
+
end
|
45
|
+
|
46
|
+
modify_user(corrected)
|
47
|
+
corrected
|
48
|
+
end
|
49
|
+
|
50
|
+
TIME_KEYS = %w[date timestamp committed_at created_at merged_at closed_at datetime time].freeze
|
51
|
+
TIME_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\S*$/
|
52
|
+
|
53
|
+
def modify_time(hash, key, value)
|
54
|
+
return unless TIME_KEYS.include?(key) || (TIME_PATTERN === value)
|
55
|
+
|
56
|
+
should_be = key == 'timestamp' ? 'date' : key
|
57
|
+
raise ArgumentError if (RUBY_VERSION < '1.9') && (value == '') # TODO: remove this line. duh.
|
58
|
+
|
59
|
+
time = begin
|
60
|
+
Time.at(value)
|
61
|
+
rescue StandardError
|
62
|
+
Time.parse(value.to_s)
|
63
|
+
end
|
64
|
+
hash[should_be] = time.utc.xmlschema if time
|
65
|
+
rescue ArgumentError, TypeError
|
66
|
+
hash[should_be] = value
|
67
|
+
end
|
68
|
+
|
69
|
+
def modify_user(hash)
|
70
|
+
hash['owner'] ||= hash.delete('user') if hash['created_at'] && hash['user']
|
71
|
+
hash['author'] ||= hash.delete('user') if hash['committed_at'] && hash['user']
|
72
|
+
|
73
|
+
hash['committer'] ||= hash['author'] if hash['author']
|
74
|
+
hash['author'] ||= hash['committer'] if hash['committer']
|
75
|
+
|
76
|
+
modify_user_fields hash['owner']
|
77
|
+
modify_user_fields hash['user']
|
78
|
+
end
|
79
|
+
|
80
|
+
def modify_user_fields(hash)
|
81
|
+
return unless hash.is_a?(Hash)
|
82
|
+
|
83
|
+
hash['login'] = hash.delete('name') if hash['name']
|
84
|
+
set_link hash, 'self', "users/#{hash['login']}" unless links(hash).include? 'self'
|
85
|
+
end
|
86
|
+
|
87
|
+
def modify_url(hash, key, value)
|
88
|
+
case key
|
89
|
+
when 'blog'
|
90
|
+
set_link(hash, key, value)
|
91
|
+
when 'url'
|
92
|
+
type = value.to_s.start_with?(api_host.to_s) ? 'self' : 'html'
|
93
|
+
set_link(hash, type, value)
|
94
|
+
when /^(.+)_url$/
|
95
|
+
set_link(hash, ::Regexp.last_match(1), value)
|
96
|
+
when 'config'
|
97
|
+
hash[key] = value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def modify_key(key, value = nil)
|
102
|
+
case key
|
103
|
+
when 'gravatar_url' then 'avatar_url'
|
104
|
+
when 'org' then 'organization'
|
105
|
+
when 'orgs' then 'organizations'
|
106
|
+
when 'username' then 'login'
|
107
|
+
when 'repo' then 'repository'
|
108
|
+
when 'repos' then modify_key('repositories', value)
|
109
|
+
when /^repos?_(.*)$/ then "repository_#{::Regexp.last_match(1)}"
|
110
|
+
when /^(.*)_repo$/ then "#{::Regexp.last_match(1)}_repository"
|
111
|
+
when /^(.*)_repos$/ then "#{::Regexp.last_match(1)}_repositories"
|
112
|
+
when 'commit', 'commit_id', 'id' then value.to_s =~ /^\w{40}$/ ? 'sha' : key
|
113
|
+
when 'comments' then value.is_a?(Numeric) ? 'comment_count' : key
|
114
|
+
when 'forks' then value.is_a?(Numeric) ? 'fork_count' : key
|
115
|
+
when 'repositories' then value.is_a?(Numeric) ? 'repository_count' : key
|
116
|
+
when /^(.*)s_count$/ then "#{::Regexp.last_match(1)}_count"
|
117
|
+
else key
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GH
|
4
|
+
class Pagination < Wrapper
|
5
|
+
class Paginated
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize(page, url, gh)
|
9
|
+
@page = page
|
10
|
+
@next_url = url
|
11
|
+
@gh = gh
|
12
|
+
end
|
13
|
+
|
14
|
+
def each(&block)
|
15
|
+
return enum_for(:each) unless block
|
16
|
+
|
17
|
+
@page.each(&block)
|
18
|
+
next_page.each(&block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"[#{first.inspect}, ...]"
|
23
|
+
end
|
24
|
+
|
25
|
+
def [](value)
|
26
|
+
raise TypeError, "index has to be an Integer, got #{value.class}" unless value.is_a? Integer
|
27
|
+
return @page[value] if value < @page.size
|
28
|
+
|
29
|
+
next_page[value - @page.size]
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_ary
|
33
|
+
to_a # replace with better implementation (use in_parallel)
|
34
|
+
end
|
35
|
+
|
36
|
+
def headers
|
37
|
+
@page.headers
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def next_page
|
43
|
+
@next_page ||= @gh[@next_url]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
wraps GH::Normalizer
|
48
|
+
double_dispatch
|
49
|
+
|
50
|
+
def fetch_resource(key)
|
51
|
+
url = frontend.full_url(key)
|
52
|
+
params = url.query_values || {}
|
53
|
+
params['per_page'] ||= 100
|
54
|
+
url.query_values = params
|
55
|
+
super url.request_uri
|
56
|
+
end
|
57
|
+
|
58
|
+
def modify_response(response)
|
59
|
+
return response unless response.respond_to?(:to_ary) && response.headers['link'] =~ (/<([^>]+)>;\s*rel="next"/)
|
60
|
+
|
61
|
+
Paginated.new(response, ::Regexp.last_match(1), self)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|