gh 0.0.1 → 0.1.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/.travis.yml +5 -0
- data/README.md +14 -1
- data/lib/gh.rb +15 -7
- data/lib/gh/cache.rb +29 -4
- data/lib/gh/lazy_loader.rb +21 -0
- data/lib/gh/normalizer.rb +15 -30
- data/lib/gh/remote.rb +19 -1
- data/lib/gh/response.rb +3 -2
- data/lib/gh/stack.rb +7 -0
- data/lib/gh/version.rb +1 -1
- data/lib/gh/wrapper.rb +92 -6
- data/spec/cache_spec.rb +11 -0
- data/spec/gh_spec.rb +7 -0
- data/spec/lazy_loader_spec.rb +59 -0
- data/spec/normalizer_spec.rb +14 -3
- data/spec/spec_helper.rb +21 -2
- metadata +17 -12
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -22,4 +22,17 @@ api = GH::Stack.build do
|
|
22
22
|
use GH::Normalizer
|
23
23
|
use GH::Remote, username: "admin", password: "admin"
|
24
24
|
end
|
25
|
-
```
|
25
|
+
```
|
26
|
+
|
27
|
+
Usage example:
|
28
|
+
|
29
|
+
``` ruby
|
30
|
+
GH.with username: 'rkh' password: 'abc123' do
|
31
|
+
sven = GH['users/svenfuchs']
|
32
|
+
|
33
|
+
if sven['hireable']
|
34
|
+
# recruiter has to be provided by some different library
|
35
|
+
Recruiter.contact sven['email']
|
36
|
+
end
|
37
|
+
end
|
38
|
+
```
|
data/lib/gh.rb
CHANGED
@@ -5,26 +5,34 @@ require 'forwardable'
|
|
5
5
|
module GH
|
6
6
|
autoload :Cache, 'gh/cache'
|
7
7
|
autoload :Case, 'gh/case'
|
8
|
+
autoload :LazyLoader, 'gh/lazy_loader'
|
8
9
|
autoload :Normalizer, 'gh/normalizer'
|
9
10
|
autoload :Remote, 'gh/remote'
|
10
11
|
autoload :Response, 'gh/response'
|
11
12
|
autoload :Stack, 'gh/stack'
|
12
13
|
autoload :Wrapper, 'gh/wrapper'
|
13
14
|
|
14
|
-
def self.[](key)
|
15
|
-
backend = Thread.current[:GH] ||= DefaultStack.build
|
16
|
-
backend[key]
|
17
|
-
end
|
18
|
-
|
19
15
|
def self.with(backend)
|
20
16
|
backend = DefaultStack.build(backend) if Hash === backend
|
21
|
-
was,
|
17
|
+
was, self.current = current, backend
|
22
18
|
yield
|
23
19
|
ensure
|
24
|
-
|
20
|
+
self.current = was
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.current
|
24
|
+
Thread.current[:GH] ||= DefaultStack.new
|
25
25
|
end
|
26
26
|
|
27
|
+
def self.current=(backend)
|
28
|
+
Thread.current[:GH] = backend
|
29
|
+
end
|
30
|
+
|
31
|
+
extend SingleForwardable
|
32
|
+
def_delegators :current, :api_host, :[], :reset
|
33
|
+
|
27
34
|
DefaultStack = Stack.new do
|
35
|
+
use LazyLoader
|
28
36
|
use Cache
|
29
37
|
use Normalizer
|
30
38
|
use Remote
|
data/lib/gh/cache.rb
CHANGED
@@ -2,8 +2,7 @@ require 'gh'
|
|
2
2
|
require 'thread'
|
3
3
|
|
4
4
|
module GH
|
5
|
-
# Public: This class
|
6
|
-
# Note that it is usually used implicitely by other wrapper classes if not specified.
|
5
|
+
# Public: This class caches responses.
|
7
6
|
class Cache < Wrapper
|
8
7
|
# Public: Get/set cache to use. Compatible with Rails/ActiveSupport cache.
|
9
8
|
attr_accessor :cache
|
@@ -20,9 +19,14 @@ module GH
|
|
20
19
|
# Internal: Tries to fetch a value from the cache and if it doesn't exist, generates it from the
|
21
20
|
# block given.
|
22
21
|
def fetch(key)
|
23
|
-
@mutex.
|
22
|
+
@mutex.synchronize { @old, @new = @new, {} if @new.size > @size } if @new.size > @size
|
24
23
|
@new[key] ||= @old[key] || yield
|
25
24
|
end
|
25
|
+
|
26
|
+
# Internal: ...
|
27
|
+
def clear
|
28
|
+
@mutex.synchronize { @old, @new = {}, {} }
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
32
|
# Internal: Initializes a new Cache instance.
|
@@ -33,6 +37,12 @@ module GH
|
|
33
37
|
super
|
34
38
|
end
|
35
39
|
|
40
|
+
# Public: ...
|
41
|
+
def reset
|
42
|
+
super
|
43
|
+
clear_partial or clear_all
|
44
|
+
end
|
45
|
+
|
36
46
|
# Public: Retrieves resources from Github and caches response for future access.
|
37
47
|
#
|
38
48
|
# Examples
|
@@ -41,7 +51,22 @@ module GH
|
|
41
51
|
#
|
42
52
|
# Returns the Response.
|
43
53
|
def [](key)
|
44
|
-
cache.fetch(
|
54
|
+
cache.fetch(prefixed(key)) { super }
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def clear_partial
|
60
|
+
return false unless cache.respond_to? :delete_matched
|
61
|
+
pattern = "^" << Regexp.escape(prefixed(""))
|
62
|
+
cache.delete_matched Regexp.new(pattern)
|
63
|
+
true
|
64
|
+
rescue NotImplementedError
|
65
|
+
false
|
66
|
+
end
|
67
|
+
|
68
|
+
def clear_all
|
69
|
+
cache.clear
|
45
70
|
end
|
46
71
|
end
|
47
72
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'gh'
|
2
|
+
|
3
|
+
module GH
|
4
|
+
# Public: ...
|
5
|
+
class LazyLoader < Wrapper
|
6
|
+
double_dispatch
|
7
|
+
|
8
|
+
def modify_hash(hash)
|
9
|
+
hash = super
|
10
|
+
link = hash['_links'].try(:[], 'self')
|
11
|
+
setup_lazy_loading(hash, link['href']) if link
|
12
|
+
hash
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def lazy_load(hash, key, link)
|
18
|
+
backend[link]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/gh/normalizer.rb
CHANGED
@@ -9,15 +9,13 @@ module GH
|
|
9
9
|
# Returns normalized Response.
|
10
10
|
def [](key)
|
11
11
|
result = super
|
12
|
-
links(result)['self'] ||= { 'href' => full_url(key).to_s } if result.
|
12
|
+
links(result)['self'] ||= { 'href' => full_url(key).to_s } if result.respond_to? :to_hash
|
13
13
|
result
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
17
17
|
|
18
|
-
|
19
|
-
normalize super
|
20
|
-
end
|
18
|
+
double_dispatch
|
21
19
|
|
22
20
|
def links(hash)
|
23
21
|
hash = hash.data if hash.respond_to? :data
|
@@ -28,32 +26,32 @@ module GH
|
|
28
26
|
links(hash)[type] = {"href" => href}
|
29
27
|
end
|
30
28
|
|
31
|
-
def
|
29
|
+
def modify_response(response)
|
32
30
|
response = response.dup
|
33
|
-
response.data =
|
31
|
+
response.data = modify response.data
|
34
32
|
response
|
35
33
|
end
|
36
34
|
|
37
|
-
def
|
35
|
+
def modify_hash(hash)
|
38
36
|
corrected = {}
|
39
37
|
corrected.default_proc = hash.default_proc if hash.default_proc
|
40
38
|
|
41
39
|
hash.each_pair do |key, value|
|
42
|
-
key =
|
43
|
-
next if
|
44
|
-
next if
|
45
|
-
corrected[key] =
|
40
|
+
key = modify_key(key, value)
|
41
|
+
next if modify_url(corrected, key, value)
|
42
|
+
next if modify_time(corrected, key, value)
|
43
|
+
corrected[key] = modify(value)
|
46
44
|
end
|
47
45
|
|
48
|
-
|
46
|
+
modify_user(corrected)
|
49
47
|
corrected
|
50
48
|
end
|
51
49
|
|
52
|
-
def
|
50
|
+
def modify_time(hash, key, value)
|
53
51
|
hash['date'] = Time.at(value).xmlschema if key == 'timestamp'
|
54
52
|
end
|
55
53
|
|
56
|
-
def
|
54
|
+
def modify_user(hash)
|
57
55
|
hash['owner'] ||= hash.delete('user') if hash['created_at'] and hash['user']
|
58
56
|
hash['author'] ||= hash.delete('user') if hash['committed_at'] and hash['user']
|
59
57
|
|
@@ -61,7 +59,7 @@ module GH
|
|
61
59
|
hash['author'] ||= hash['committer'] if hash['committer']
|
62
60
|
end
|
63
61
|
|
64
|
-
def
|
62
|
+
def modify_url(hash, key, value)
|
65
63
|
case key
|
66
64
|
when "blog"
|
67
65
|
set_link(hash, key, value)
|
@@ -73,14 +71,14 @@ module GH
|
|
73
71
|
end
|
74
72
|
end
|
75
73
|
|
76
|
-
def
|
74
|
+
def modify_key(key, value = nil)
|
77
75
|
case key
|
78
76
|
when 'gravatar_url' then 'avatar_url'
|
79
77
|
when 'org' then 'organization'
|
80
78
|
when 'orgs' then 'organizations'
|
81
79
|
when 'username' then 'login'
|
82
80
|
when 'repo' then 'repository'
|
83
|
-
when 'repos' then
|
81
|
+
when 'repos' then modify_key('repositories', value)
|
84
82
|
when /^repos?_(.*)$/ then "repository_#{$1}"
|
85
83
|
when /^(.*)_repo$/ then "#{$1}_repository"
|
86
84
|
when /^(.*)_repos$/ then "#{$1}_repositories"
|
@@ -92,18 +90,5 @@ module GH
|
|
92
90
|
else key
|
93
91
|
end
|
94
92
|
end
|
95
|
-
|
96
|
-
def normalize_array(array)
|
97
|
-
array.map { |e| normalize(e) }
|
98
|
-
end
|
99
|
-
|
100
|
-
def normalize(object)
|
101
|
-
case object
|
102
|
-
when Hash then normalize_hash(object)
|
103
|
-
when Array then normalize_array(object)
|
104
|
-
when Response then normalize_response(object)
|
105
|
-
else object
|
106
|
-
end
|
107
|
-
end
|
108
93
|
end
|
109
94
|
end
|
data/lib/gh/remote.rb
CHANGED
@@ -5,7 +5,7 @@ module GH
|
|
5
5
|
# Public: This class deals with HTTP requests to Github. It is the base Wrapper you always want to use.
|
6
6
|
# Note that it is usually used implicitely by other wrapper classes if not specified.
|
7
7
|
class Remote < Wrapper
|
8
|
-
attr_reader :api_host, :connection, :headers
|
8
|
+
attr_reader :api_host, :connection, :headers, :prefix
|
9
9
|
|
10
10
|
# Public: Generates a new Rempte instance.
|
11
11
|
#
|
@@ -33,6 +33,11 @@ module GH
|
|
33
33
|
"Accept-Charset" => "utf-8"
|
34
34
|
}
|
35
35
|
|
36
|
+
@prefix = ""
|
37
|
+
@prefix << "#{token}@" if token
|
38
|
+
@prefix << "#{username}:#{password}@" if username and password
|
39
|
+
@prefix << @api_host.host
|
40
|
+
|
36
41
|
@connection = Faraday.new(:url => api_host) do |builder|
|
37
42
|
builder.request(:token_auth, token) if token
|
38
43
|
builder.request(:basic_auth, username, password) if username and password
|
@@ -61,8 +66,21 @@ module GH
|
|
61
66
|
modify(response.body, response.headers)
|
62
67
|
end
|
63
68
|
|
69
|
+
# Public: ...
|
70
|
+
def reset
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: ...
|
74
|
+
def load(data)
|
75
|
+
modify(data)
|
76
|
+
end
|
77
|
+
|
64
78
|
private
|
65
79
|
|
80
|
+
def identifier(key)
|
81
|
+
path_for(key)
|
82
|
+
end
|
83
|
+
|
66
84
|
def modify(body, headers = {})
|
67
85
|
return body if body.is_a? Response
|
68
86
|
Response.new(headers, body)
|
data/lib/gh/response.rb
CHANGED
data/lib/gh/stack.rb
CHANGED
@@ -48,5 +48,12 @@ module GH
|
|
48
48
|
klass.new backend, @options.merge(opts).merge(options)
|
49
49
|
end
|
50
50
|
end
|
51
|
+
|
52
|
+
# Public: ...
|
53
|
+
def replace(old_class, new_class)
|
54
|
+
@stack.map! { |klass, options| [old_class == klass ? new_class : klass, options] }
|
55
|
+
end
|
56
|
+
|
57
|
+
alias_method :new, :build
|
51
58
|
end
|
52
59
|
end
|
data/lib/gh/version.rb
CHANGED
data/lib/gh/wrapper.rb
CHANGED
@@ -22,6 +22,7 @@ module GH
|
|
22
22
|
# end
|
23
23
|
class Wrapper
|
24
24
|
extend Forwardable
|
25
|
+
include Case
|
25
26
|
|
26
27
|
# Public: Get wrapped layer.
|
27
28
|
attr_reader :backend
|
@@ -29,6 +30,11 @@ module GH
|
|
29
30
|
# Public: Returns the URI used for sending out web request.
|
30
31
|
def_delegator :backend, :api_host
|
31
32
|
|
33
|
+
# Public: Retrieves resources from Github.
|
34
|
+
def self.[](key)
|
35
|
+
new[key]
|
36
|
+
end
|
37
|
+
|
32
38
|
# Public: Retrieves resources from Github.
|
33
39
|
#
|
34
40
|
# By default, this method is delegated to the next layer on the stack
|
@@ -40,7 +46,7 @@ module GH
|
|
40
46
|
# Internal: Get/set default layer to wrap when creating a new instance.
|
41
47
|
def self.wraps(klass = nil)
|
42
48
|
@wraps = klass if klass
|
43
|
-
@wraps
|
49
|
+
@wraps ||= Remote
|
44
50
|
end
|
45
51
|
|
46
52
|
# Public: Initialize a new Wrapper.
|
@@ -54,6 +60,7 @@ module GH
|
|
54
60
|
|
55
61
|
# Public: Set wrapped layer.
|
56
62
|
def backend=(layer)
|
63
|
+
reset
|
57
64
|
layer.frontend = self
|
58
65
|
@backend = layer
|
59
66
|
end
|
@@ -68,16 +75,75 @@ module GH
|
|
68
75
|
@frontend ? @frontend.frontend : self
|
69
76
|
end
|
70
77
|
|
78
|
+
# Public: ...
|
71
79
|
def inspect
|
72
80
|
"#<#{self.class}: #{backend.inspect}>"
|
73
81
|
end
|
74
82
|
|
83
|
+
# Internal: ...
|
84
|
+
def prefixed(key)
|
85
|
+
prefix + "#" + identifier(key)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Public: ...
|
89
|
+
def reset
|
90
|
+
backend.reset if backend
|
91
|
+
end
|
92
|
+
|
93
|
+
# Public: ...
|
94
|
+
def load(data)
|
95
|
+
modify backend.load(data)
|
96
|
+
end
|
97
|
+
|
75
98
|
private
|
76
99
|
|
77
|
-
def
|
100
|
+
def identifier(key)
|
101
|
+
backend.prefixed(key)
|
102
|
+
end
|
103
|
+
|
104
|
+
def prefix
|
105
|
+
self.class.name
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.double_dispatch
|
109
|
+
define_method(:modify) { |data| double_dispatch(data) }
|
110
|
+
end
|
111
|
+
|
112
|
+
def double_dispatch(data)
|
113
|
+
case data
|
114
|
+
when respond_to(:to_gh) then modify_response(data)
|
115
|
+
when respond_to(:to_hash) then modify_hash(data)
|
116
|
+
when respond_to(:to_ary) then modify_array(data)
|
117
|
+
when respond_to(:to_str) then modify_string(data)
|
118
|
+
when respond_to(:to_int) then modify_integer(data)
|
119
|
+
else modify_unkown data
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def modify_response(response)
|
124
|
+
result = double_dispatch response.data
|
125
|
+
result.respond_to?(:to_gh) ? result.to_gh : Response.new({}, result)
|
126
|
+
end
|
127
|
+
|
128
|
+
def modify(data, *)
|
78
129
|
data
|
79
130
|
end
|
80
131
|
|
132
|
+
def modify_array(array)
|
133
|
+
array.map { |e| modify(e) }
|
134
|
+
end
|
135
|
+
|
136
|
+
def modify_hash(hash, &block)
|
137
|
+
corrected = {}
|
138
|
+
hash.each_pair { |k,v| corrected[k] = modify(v) }
|
139
|
+
corrected.default_proc = hash.default_proc if hash.default_proc
|
140
|
+
corrected
|
141
|
+
end
|
142
|
+
|
143
|
+
alias modify_string modify
|
144
|
+
alias modify_integer modify
|
145
|
+
alias modify_unkown modify
|
146
|
+
|
81
147
|
def setup(backend, options)
|
82
148
|
self.backend = Wrapper === backend ? backend : self.class.wraps.new(backend, options)
|
83
149
|
end
|
@@ -90,13 +156,33 @@ module GH
|
|
90
156
|
end
|
91
157
|
|
92
158
|
def full_url(key)
|
93
|
-
api_host +
|
159
|
+
uri = api_host + Addressable::URI.parse(key)
|
160
|
+
raise ArgumentError, "URI out of scope: #{key}" if uri.host != api_host.host
|
161
|
+
uri
|
162
|
+
end
|
163
|
+
|
164
|
+
def setup_default_proc(hash, &block)
|
165
|
+
old_proc = hash.default_proc
|
166
|
+
hash.default_proc = proc do |hash, key|
|
167
|
+
value = old_proc.call(hash, key) if old_proc
|
168
|
+
value = block[hash, key] if value.nil?
|
169
|
+
value
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def setup_lazy_loading(hash, *args)
|
174
|
+
loaded = false
|
175
|
+
setup_default_proc hash do |hash, key|
|
176
|
+
next if loaded
|
177
|
+
fields = lazy_load(hash, key, *args)
|
178
|
+
hash.merge! fields
|
179
|
+
loaded = true
|
180
|
+
fields[key]
|
181
|
+
end
|
94
182
|
end
|
95
183
|
|
96
184
|
def path_for(key)
|
97
|
-
|
98
|
-
raise ArgumentError, "URI out of scope: #{key}" if uri.host and uri.host != api_host.host
|
99
|
-
uri.request_uri
|
185
|
+
full_url(key).request_uri
|
100
186
|
end
|
101
187
|
end
|
102
188
|
end
|
data/spec/cache_spec.rb
CHANGED
@@ -14,4 +14,15 @@ describe GH::Cache do
|
|
14
14
|
subject['users/rkh']['name'].should be == "Konstantin Haase"
|
15
15
|
requests.count.should be == 2
|
16
16
|
end
|
17
|
+
|
18
|
+
it 'cache is resettable' do
|
19
|
+
subject['users/rkh']['name'].should be == "Konstantin Haase"
|
20
|
+
subject['users/rkh']['name'].should be == "Konstantin Haase"
|
21
|
+
requests.count.should be == 1
|
22
|
+
|
23
|
+
subject.reset
|
24
|
+
requests.count.should be == 0
|
25
|
+
subject['users/rkh']['name'].should be == "Konstantin Haase"
|
26
|
+
requests.count.should be == 1
|
27
|
+
end
|
17
28
|
end
|
data/spec/gh_spec.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GH::LazyLoader do
|
4
|
+
before { subject.backend = GH::Normalizer.new(GH::MockBackend.new) }
|
5
|
+
|
6
|
+
let! :raw do
|
7
|
+
hash = subject.backend['users/rkh'].to_hash
|
8
|
+
hash.delete 'name'
|
9
|
+
hash
|
10
|
+
end
|
11
|
+
|
12
|
+
let :rkh do
|
13
|
+
subject.load(raw)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'send http requests for missing fields' do
|
17
|
+
should_request(1) { rkh['name'].should be == 'Konstantin Haase' }
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'does not send http requests for existing fields' do
|
21
|
+
should_not_request { rkh['login'].should be == 'rkh' }
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'allows traversing into nested structures' do
|
25
|
+
sven = subject.backend['users/svenfuchs'].to_hash
|
26
|
+
sven['friends'] = [raw]
|
27
|
+
sven.delete 'name'
|
28
|
+
|
29
|
+
sven = subject.load(sven)
|
30
|
+
should_request(1) { sven['friends'][0]['name'].should be == 'Konstantin Haase' }
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'does not request twice if the field does not exist upstream' do
|
34
|
+
should_request(1) { 2.times { rkh['foo'] } }
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'does not skip an already existing default proc' do
|
38
|
+
count = 0
|
39
|
+
raw.default_proc = proc { |hash, key| count += 1 if key == 'foo' }
|
40
|
+
rkh = subject.load(raw)
|
41
|
+
|
42
|
+
should_not_request do
|
43
|
+
rkh['foo'].should be == 1
|
44
|
+
rkh['foo'].should be == 2
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'is still loading missing fields, even if a default proc is set' do
|
49
|
+
count = 0
|
50
|
+
raw.default_proc = proc { |hash, key| count += 1 if key == 'foo' }
|
51
|
+
rkh = subject.load(raw)
|
52
|
+
|
53
|
+
should_request 1 do
|
54
|
+
rkh['foo'].should be == 1
|
55
|
+
rkh['name'].should be == 'Konstantin Haase'
|
56
|
+
rkh['foo'].should be == 2
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/spec/normalizer_spec.rb
CHANGED
@@ -4,16 +4,16 @@ describe GH::Normalizer do
|
|
4
4
|
before { subject.backend = GH::MockBackend.new }
|
5
5
|
|
6
6
|
def normalize(payload)
|
7
|
-
data['payload'] = payload
|
7
|
+
data['/payload'] = payload
|
8
8
|
end
|
9
9
|
|
10
10
|
def with_headers(headers = {})
|
11
11
|
response = GH::Response.new(headers)
|
12
|
-
data['payload'], response.data = response, data['payload']
|
12
|
+
data['/payload'], response.data = response, data['/payload']
|
13
13
|
end
|
14
14
|
|
15
15
|
def normalized
|
16
|
-
subject['payload']
|
16
|
+
subject['/payload']
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'is set up properly' do
|
@@ -25,6 +25,12 @@ describe GH::Normalizer do
|
|
25
25
|
normalized['foo'].should be == 'bar'
|
26
26
|
end
|
27
27
|
|
28
|
+
it 'allows normalization with #load' do
|
29
|
+
result = subject.load("org" => "foo")
|
30
|
+
result.should_not include("org")
|
31
|
+
result["organization"].should be == "foo"
|
32
|
+
end
|
33
|
+
|
28
34
|
it 'works for deeply nested fields'
|
29
35
|
it 'works for lists'
|
30
36
|
|
@@ -256,5 +262,10 @@ describe GH::Normalizer do
|
|
256
262
|
normalized['_links'].should_not include('html')
|
257
263
|
normalized['_links']['self']['href'].should be == 'http://api.github.com/foo'
|
258
264
|
end
|
265
|
+
|
266
|
+
it 'passes through true' do
|
267
|
+
normalize 'foo' => true
|
268
|
+
normalized['foo'].should be == true
|
269
|
+
end
|
259
270
|
end
|
260
271
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,8 +5,9 @@ require 'fileutils'
|
|
5
5
|
|
6
6
|
module GH
|
7
7
|
module TestHelpers
|
8
|
-
def backend
|
9
|
-
|
8
|
+
def backend(layer = subject)
|
9
|
+
return layer if layer.backend.nil? or layer.is_a? MockBackend
|
10
|
+
backend layer.backend
|
10
11
|
end
|
11
12
|
|
12
13
|
def requests
|
@@ -16,6 +17,16 @@ module GH
|
|
16
17
|
def data
|
17
18
|
backend.data
|
18
19
|
end
|
20
|
+
|
21
|
+
def should_request(num = 1, &block)
|
22
|
+
was = requests.count
|
23
|
+
yield
|
24
|
+
(requests.count - was).should be == num
|
25
|
+
end
|
26
|
+
|
27
|
+
def should_not_request(&block)
|
28
|
+
should_request(0, &block)
|
29
|
+
end
|
19
30
|
end
|
20
31
|
|
21
32
|
class MockBackend < Wrapper
|
@@ -45,6 +56,12 @@ module GH
|
|
45
56
|
result
|
46
57
|
end
|
47
58
|
|
59
|
+
def reset
|
60
|
+
super
|
61
|
+
@data.clear
|
62
|
+
@requests.clear
|
63
|
+
end
|
64
|
+
|
48
65
|
private
|
49
66
|
|
50
67
|
def allow_http
|
@@ -59,4 +76,6 @@ end
|
|
59
76
|
|
60
77
|
RSpec.configure do |c|
|
61
78
|
c.include GH::TestHelpers
|
79
|
+
c.before { GH::DefaultStack.replace GH::Remote, GH::MockBackend }
|
80
|
+
c.after { GH.reset }
|
62
81
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-04-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70185635369900 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70185635369900
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: webmock
|
27
|
-
requirement: &
|
27
|
+
requirement: &70185635369280 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70185635369280
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: faraday
|
38
|
-
requirement: &
|
38
|
+
requirement: &70185635368560 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0.7'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70185635368560
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: backports
|
49
|
-
requirement: &
|
49
|
+
requirement: &70185635367800 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '2.3'
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70185635367800
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: multi_json
|
60
|
-
requirement: &
|
60
|
+
requirement: &70185635367120 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '1.0'
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70185635367120
|
69
69
|
description: multi-layer client for the github api v3
|
70
70
|
email:
|
71
71
|
- konstantin.mailinglists@googlemail.com
|
@@ -83,6 +83,7 @@ files:
|
|
83
83
|
- lib/gh/cache.rb
|
84
84
|
- lib/gh/case.rb
|
85
85
|
- lib/gh/faraday.rb
|
86
|
+
- lib/gh/lazy_loader.rb
|
86
87
|
- lib/gh/normalizer.rb
|
87
88
|
- lib/gh/remote.rb
|
88
89
|
- lib/gh/response.rb
|
@@ -90,6 +91,8 @@ files:
|
|
90
91
|
- lib/gh/version.rb
|
91
92
|
- lib/gh/wrapper.rb
|
92
93
|
- spec/cache_spec.rb
|
94
|
+
- spec/gh_spec.rb
|
95
|
+
- spec/lazy_loader_spec.rb
|
93
96
|
- spec/normalizer_spec.rb
|
94
97
|
- spec/payloads/users/rkh.yml
|
95
98
|
- spec/payloads/users/svenfuchs.yml
|
@@ -124,6 +127,8 @@ specification_version: 3
|
|
124
127
|
summary: layered github client
|
125
128
|
test_files:
|
126
129
|
- spec/cache_spec.rb
|
130
|
+
- spec/gh_spec.rb
|
131
|
+
- spec/lazy_loader_spec.rb
|
127
132
|
- spec/normalizer_spec.rb
|
128
133
|
- spec/payloads/users/rkh.yml
|
129
134
|
- spec/payloads/users/svenfuchs.yml
|