leadlight 0.0.2 → 0.0.3
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/Gemfile.lock +5 -7
- data/README.md +82 -0
- data/default.gems +1 -0
- data/leadlight.gemspec +16 -7
- data/lib/leadlight.rb +18 -62
- data/lib/leadlight/basic_converter.rb +18 -0
- data/lib/leadlight/codec.rb +2 -0
- data/lib/leadlight/entity.rb +1 -0
- data/lib/leadlight/errors.rb +10 -2
- data/lib/leadlight/header_helpers.rb +11 -0
- data/lib/leadlight/hyperlinkable.rb +1 -1
- data/lib/leadlight/representation.rb +19 -1
- data/lib/leadlight/request.rb +44 -11
- data/lib/leadlight/service.rb +4 -9
- data/lib/leadlight/service_class_methods.rb +69 -0
- data/lib/leadlight/service_middleware.rb +2 -14
- data/lib/leadlight/tint.rb +6 -2
- data/lib/leadlight/tint_helper.rb +27 -4
- data/lib/leadlight/type_map.rb +101 -0
- data/spec/cassettes/Leadlight/authorized_GitHub_example/_user/has_the_expected_content.yml +8 -8
- data/spec/cassettes/Leadlight/authorized_GitHub_example/_user/indicates_the_expected_oath_scopes.yml +8 -8
- data/spec/cassettes/Leadlight/authorized_GitHub_example/adding_and_removing_team_members.yml +273 -284
- data/spec/cassettes/Leadlight/authorized_GitHub_example/{adding_and_removing_team_members/.yml → adding_and_removing_teams.yml} +57 -84
- data/spec/cassettes/Leadlight/authorized_GitHub_example/test_team/.yml +111 -117
- data/spec/cassettes/Leadlight/basic_GitHub_example/_root/.yml +58 -25
- data/spec/cassettes/Leadlight/basic_GitHub_example/_root/__location__/.yml +58 -25
- data/spec/cassettes/Leadlight/basic_GitHub_example/_root/should_be_a_204_no_content.yml +58 -25
- data/spec/cassettes/Leadlight/tinted_GitHub_example/_root/.yml +58 -25
- data/spec/cassettes/Leadlight/tinted_GitHub_example/_root/__location__/.yml +58 -25
- data/spec/cassettes/Leadlight/tinted_GitHub_example/_root/should_be_a_204_no_content.yml +58 -25
- data/spec/cassettes/Leadlight/tinted_GitHub_example/_user/has_the_expected_content.yml +122 -50
- data/spec/cassettes/Leadlight/tinted_GitHub_example/user_followers/.yml +190 -77
- data/spec/cassettes/Leadlight/tinted_GitHub_example/user_followers/should_be_able_to_follow_next_link.yml +260 -104
- data/spec/cassettes/Leadlight/tinted_GitHub_example/user_followers/should_be_enumerable.yml +616 -212
- data/spec/cassettes/Leadlight/tinted_GitHub_example/user_followers/should_be_enumerable_over_page_boundaries.yml +331 -131
- data/spec/cassettes/Leadlight/tinted_GitHub_example/user_followers/should_have_next_and_last_links.yml +190 -77
- data/spec/cassettes/Leadlight/tinted_GitHub_example/user_link/exists.yml +58 -25
- data/spec/cassettes/Leadlight/tinted_GitHub_example/user_link/links_to_the_expected_URL.yml +58 -25
- data/spec/leadlight/hyperlinkable_spec.rb +3 -1
- data/spec/leadlight/link_template_spec.rb +2 -1
- data/spec/leadlight/request_spec.rb +44 -21
- data/spec/leadlight/service_middleware_spec.rb +9 -46
- data/spec/leadlight/service_spec.rb +30 -24
- data/spec/leadlight/tint_helper_spec.rb +67 -1
- data/spec/leadlight/tint_spec.rb +69 -0
- data/spec/leadlight/type_map_spec.rb +127 -0
- data/spec/leadlight_spec.rb +102 -51
- data/spec/support/credentials.rb +10 -2
- data/spec/support/vcr.rb +1 -1
- metadata +61 -32
- data/lib/leadlight/type.rb +0 -71
- data/spec/leadlight/type_spec.rb +0 -137
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'spec_helper_lite'
|
2
2
|
require 'leadlight/link_template'
|
3
|
+
require 'leadlight/type_map'
|
3
4
|
|
4
5
|
module Leadlight
|
5
6
|
describe LinkTemplate do
|
6
7
|
|
7
8
|
# TODO: This setup is loony. Refactor.
|
8
9
|
subject { LinkTemplate.new(service, href, rel, title, options) }
|
9
|
-
let(:service) { stub(:service) }
|
10
|
+
let(:service) { stub(:service, type_map: TypeMap.new) }
|
10
11
|
let(:request) { stub(:request) }
|
11
12
|
let(:result) { stub(:result) }
|
12
13
|
let(:href) { '/TEST_PATH/{n}/{m}/' }
|
@@ -6,6 +6,14 @@ module Leadlight
|
|
6
6
|
describe Request do
|
7
7
|
include Timeout
|
8
8
|
|
9
|
+
before :all do
|
10
|
+
@old_abort_on_exception = Thread.abort_on_exception
|
11
|
+
Thread.abort_on_exception = true
|
12
|
+
end
|
13
|
+
|
14
|
+
after :all do
|
15
|
+
Thread.abort_on_exception = @old_abort_on_exception
|
16
|
+
end
|
9
17
|
|
10
18
|
# The Faraday connection API works like this:
|
11
19
|
#
|
@@ -29,24 +37,34 @@ module Leadlight
|
|
29
37
|
|
30
38
|
def run_completion_handlers(n=1)
|
31
39
|
n.times do
|
32
|
-
completion_handlers.pop
|
40
|
+
handler = completion_handlers.pop
|
41
|
+
handler.call(@env)
|
33
42
|
end
|
34
43
|
end
|
44
|
+
|
45
|
+
def success?
|
46
|
+
true
|
47
|
+
end
|
35
48
|
end
|
36
49
|
|
37
|
-
subject { Request.new(connection, url, http_method, params, body) }
|
50
|
+
subject { Request.new(service, connection, url, http_method, params, body) }
|
51
|
+
let(:service) { stub(:service, :type_map => type_map) }
|
52
|
+
let(:type_map) { stub(:type_map).as_null_object }
|
38
53
|
let(:connection) { stub(:connection, :run_request => faraday_response) }
|
39
54
|
let(:url) { stub(:url) }
|
40
55
|
let(:http_method){ :get }
|
41
56
|
let(:body) { stub(:body) }
|
42
57
|
let(:params) { {} }
|
43
|
-
let(:faraday_request) {stub(:faraday_request)}
|
58
|
+
let(:faraday_request) {stub(:faraday_request, options: {})}
|
44
59
|
let(:on_complete_handlers) { [] }
|
45
|
-
let(:
|
46
|
-
let(:faraday_env) { {:leadlight_representation => representation} }
|
60
|
+
let(:faraday_env) { {} }
|
47
61
|
let(:representation) { stub(:representation) }
|
62
|
+
let(:faraday_response) { FakeFaradayResponse.new(faraday_env) }
|
48
63
|
|
49
64
|
def run_completion_handlers
|
65
|
+
faraday_env[:status] ||= 200
|
66
|
+
faraday_env[:response] ||= faraday_response
|
67
|
+
faraday_env[:leadlight_representation] ||= representation
|
50
68
|
faraday_response.run_completion_handlers
|
51
69
|
end
|
52
70
|
|
@@ -55,7 +73,11 @@ module Leadlight
|
|
55
73
|
do_it(&block)
|
56
74
|
end
|
57
75
|
run_completion_handlers
|
58
|
-
t.join.value
|
76
|
+
t.join(1).value
|
77
|
+
end
|
78
|
+
|
79
|
+
before do
|
80
|
+
subject.stub!(:represent => representation)
|
59
81
|
end
|
60
82
|
|
61
83
|
context "for GET" do
|
@@ -74,17 +96,17 @@ module Leadlight
|
|
74
96
|
describe "#submit" do
|
75
97
|
it "starts a request runnning" do
|
76
98
|
connection.should_receive(:run_request).
|
77
|
-
with(http_method, url,
|
99
|
+
with(http_method, url, anything, {}).
|
78
100
|
and_return(faraday_response)
|
79
101
|
subject.submit
|
80
102
|
end
|
81
103
|
|
82
104
|
it "triggers the on_prepare_request hook in the block passed to #run_request" do
|
83
105
|
yielded = :nothing
|
84
|
-
faraday_request = stub
|
106
|
+
faraday_request = stub(options: {}, headers: {})
|
85
107
|
connection.stub(:run_request).
|
86
108
|
and_yield(faraday_request).
|
87
|
-
and_return(
|
109
|
+
and_return(stub.as_null_object)
|
88
110
|
subject.on_prepare_request do |request|
|
89
111
|
yielded = request
|
90
112
|
end
|
@@ -103,8 +125,8 @@ module Leadlight
|
|
103
125
|
trace << "wait finished"
|
104
126
|
end
|
105
127
|
trace << "completing request"
|
106
|
-
|
107
|
-
thread.join
|
128
|
+
run_completion_handlers
|
129
|
+
thread.join(1)
|
108
130
|
trace << "request completed"
|
109
131
|
trace.pop.should eq("completing request")
|
110
132
|
trace.pop.should eq("wait finished")
|
@@ -125,8 +147,8 @@ module Leadlight
|
|
125
147
|
trace << "submit"
|
126
148
|
subject.submit
|
127
149
|
trace << "completing request"
|
128
|
-
|
129
|
-
thread.join
|
150
|
+
run_completion_handlers
|
151
|
+
thread.join(1)
|
130
152
|
trace << "request completed"
|
131
153
|
trace.pop.should eq("submit")
|
132
154
|
trace.pop.should eq("completing request")
|
@@ -181,7 +203,7 @@ module Leadlight
|
|
181
203
|
subject.wait
|
182
204
|
end
|
183
205
|
run_completion_handlers
|
184
|
-
t.join
|
206
|
+
t.join(1)
|
185
207
|
end
|
186
208
|
|
187
209
|
it "queues hooks to be run on completion" do
|
@@ -197,8 +219,6 @@ module Leadlight
|
|
197
219
|
end
|
198
220
|
|
199
221
|
it "calls hooks with the faraday response" do
|
200
|
-
Faraday::Response.should_receive(:new).with(faraday_env).
|
201
|
-
and_return(faraday_response)
|
202
222
|
yielded = :nothing
|
203
223
|
subject.on_complete do |response|
|
204
224
|
yielded = response
|
@@ -216,21 +236,24 @@ module Leadlight
|
|
216
236
|
subject.wait
|
217
237
|
end
|
218
238
|
run_completion_handlers
|
219
|
-
t.join
|
239
|
+
t.join(1)
|
220
240
|
subject
|
221
241
|
end
|
222
242
|
|
223
243
|
it "raises an error when the response is a client error" do
|
224
|
-
|
225
|
-
|
244
|
+
faraday_response.should_receive(:success?).and_return(false)
|
245
|
+
subject.should_receive(:raise).with(representation)
|
246
|
+
submit_and_complete.raise_on_error
|
226
247
|
end
|
227
248
|
|
228
249
|
it "raises after completion when called before completion" do
|
229
|
-
|
250
|
+
faraday_response.should_receive(:success?).and_return(false)
|
230
251
|
subject.raise_on_error
|
231
|
-
|
252
|
+
subject.should_receive(:raise).with(representation)
|
253
|
+
submit_and_complete
|
232
254
|
end
|
233
255
|
|
234
256
|
end
|
257
|
+
|
235
258
|
end
|
236
259
|
end
|
@@ -17,65 +17,28 @@ module Leadlight
|
|
17
17
|
let(:app) { ->(env){stub(on_complete: nil)} }
|
18
18
|
let(:response) { [200, {}, ''] }
|
19
19
|
let(:result_env) {
|
20
|
-
|
20
|
+
do_get('/').env
|
21
21
|
}
|
22
22
|
let(:representation) { result_env[:leadlight_representation] }
|
23
|
+
let(:leadlight_request) { stub(:leadlight_request, represent: stub) }
|
24
|
+
|
25
|
+
def do_get(path)
|
26
|
+
test_stack.get(path) do |request|
|
27
|
+
request.options[:leadlight_request] = leadlight_request
|
28
|
+
end
|
29
|
+
end
|
23
30
|
|
24
31
|
before do
|
25
32
|
faraday_stubs.get('/') {response}
|
26
33
|
end
|
27
34
|
|
28
35
|
it 'adds :leadlight_service to env before app' do
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'extends the representation with Representation' do
|
33
|
-
representation.should be_a(Representation)
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'makes the representation hyperlinkable' do
|
37
|
-
representation.should be_a(Hyperlinkable)
|
36
|
+
do_get('/').env[:leadlight_service].should equal(service)
|
38
37
|
end
|
39
38
|
|
40
39
|
it 'requests JSON, then YAML, then XML, then HTML' do
|
41
40
|
result_env[:request_headers]['Accept'].
|
42
41
|
should eq('application/json, text/x-yaml, application/xml, application/xhtml+xml, text/html, text/plain')
|
43
42
|
end
|
44
|
-
|
45
|
-
context 'with a no-content response' do
|
46
|
-
let(:response) { [204, {}, ''] }
|
47
|
-
let(:result_env) {
|
48
|
-
test_stack.get('/').env
|
49
|
-
}
|
50
|
-
|
51
|
-
it 'sets :leadlight_representation to a Blank' do
|
52
|
-
result_env[:leadlight_representation].should be_a(Blank)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'with a blank JSON response' do
|
57
|
-
let(:response) {
|
58
|
-
[200, {'Content-Type' => 'application/json', 'Content-Length' => '0'}, '']
|
59
|
-
}
|
60
|
-
let(:result_env) {
|
61
|
-
test_stack.get('/').env
|
62
|
-
}
|
63
|
-
|
64
|
-
it 'sets :leadlight_representation to a Blank' do
|
65
|
-
result_env[:leadlight_representation].should be_a(Blank)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
context 'with a non-blank JSON response' do
|
70
|
-
let(:response) {
|
71
|
-
[200, {'Content-Type' => 'application/json', 'Content-Length' => '7'}, '[1,2,3]']
|
72
|
-
}
|
73
|
-
let(:result_env) {
|
74
|
-
test_stack.get('/').env
|
75
|
-
}
|
76
|
-
it 'sets :leadlight_representation to the result of parsing the JSON' do
|
77
|
-
result_env[:leadlight_representation].should eq([1,2,3])
|
78
|
-
end
|
79
|
-
end
|
80
43
|
end
|
81
44
|
end
|
@@ -4,7 +4,14 @@ require 'leadlight/service'
|
|
4
4
|
module Leadlight
|
5
5
|
describe Service do
|
6
6
|
subject { klass.new(service_options) }
|
7
|
-
let(:klass) {
|
7
|
+
let(:klass) {
|
8
|
+
Class.new do
|
9
|
+
include Service
|
10
|
+
|
11
|
+
def execute_hook(*)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
}
|
8
15
|
let(:connection) { stub(:connection, get: response) }
|
9
16
|
let(:representation) { stub(:representation) }
|
10
17
|
let(:response) { stub(:response, env: env) }
|
@@ -12,66 +19,65 @@ module Leadlight
|
|
12
19
|
let(:service_options) { {codec: codec} }
|
13
20
|
let(:codec) { stub(:codec) }
|
14
21
|
let(:request) { stub(:request).as_null_object }
|
22
|
+
let(:request_class) { stub(:request_class, new: request) }
|
15
23
|
|
16
24
|
before do
|
17
|
-
subject.stub(connection: connection,
|
25
|
+
subject.stub(connection: connection,
|
26
|
+
url: nil,
|
27
|
+
request_class: request_class)
|
18
28
|
end
|
19
29
|
|
20
30
|
shared_examples_for "an HTTP client" do |http_method|
|
21
31
|
describe "##{http_method}" do
|
22
|
-
before do
|
23
|
-
Request.stub(:new).and_return(request)
|
24
|
-
end
|
25
|
-
|
26
32
|
it 'returns a new request object' do
|
27
|
-
|
33
|
+
request_class.should_receive(:new).and_return(request)
|
28
34
|
subject.public_send(http_method, '/').should equal(request)
|
29
35
|
end
|
30
36
|
|
37
|
+
it 'passes self to the request' do
|
38
|
+
request_class.should_receive(:new).
|
39
|
+
with(subject, anything, anything, anything, anything, anything).
|
40
|
+
and_return(request)
|
41
|
+
subject.public_send(http_method, '/somepath')
|
42
|
+
end
|
43
|
+
|
31
44
|
it 'passes the connection to the request' do
|
32
|
-
|
33
|
-
with(connection, anything, anything, anything, anything).
|
45
|
+
request_class.should_receive(:new).
|
46
|
+
with(anything, connection, anything, anything, anything, anything).
|
34
47
|
and_return(request)
|
35
48
|
subject.public_send(http_method, '/somepath')
|
36
49
|
end
|
37
50
|
|
38
51
|
it 'passes the path to the request' do
|
39
|
-
|
40
|
-
with(anything, '/somepath', anything, anything, anything).
|
52
|
+
request_class.should_receive(:new).
|
53
|
+
with(anything, anything, '/somepath', anything, anything, anything).
|
41
54
|
and_return(request)
|
42
55
|
subject.public_send(http_method, '/somepath')
|
43
56
|
end
|
44
57
|
|
45
58
|
it 'passes the method to the request' do
|
46
|
-
|
47
|
-
with(anything, anything, http_method, anything, anything).
|
59
|
+
request_class.should_receive(:new).
|
60
|
+
with(anything, anything, anything, http_method, anything, anything).
|
48
61
|
and_return(request)
|
49
62
|
subject.public_send(http_method, '/somepath')
|
50
63
|
end
|
51
64
|
|
52
65
|
it 'passes the params to the request' do
|
53
66
|
params = stub
|
54
|
-
|
55
|
-
with(anything, anything, anything, params, anything).
|
67
|
+
request_class.should_receive(:new).
|
68
|
+
with(anything, anything, anything, anything, params, anything).
|
56
69
|
and_return(request)
|
57
70
|
subject.public_send(http_method, '/somepath', params)
|
58
71
|
end
|
59
72
|
|
60
73
|
it 'passes the body to the request' do
|
61
74
|
body = stub
|
62
|
-
|
63
|
-
with(anything, anything, anything, anything, body).
|
75
|
+
request_class.should_receive(:new).
|
76
|
+
with(anything, anything, anything, anything, anything, body).
|
64
77
|
and_return(request)
|
65
78
|
subject.public_send(http_method, '/somepath', {}, body)
|
66
79
|
end
|
67
80
|
|
68
|
-
it 'adds a prepare_request callback' do
|
69
|
-
faraday_request = stub(:faraday_request)
|
70
|
-
request.stub(:on_prepare_request).and_yield(faraday_request)
|
71
|
-
subject.should_receive(:prepare_request).with(faraday_request)
|
72
|
-
subject.public_send(http_method, '/')
|
73
|
-
end
|
74
|
-
|
75
81
|
context 'given a block' do
|
76
82
|
define_method(:do_it) do
|
77
83
|
subject.public_send(http_method, '/') do |yielded|
|
@@ -10,7 +10,8 @@ module Leadlight
|
|
10
10
|
}
|
11
11
|
let(:response) {
|
12
12
|
stub(:response,
|
13
|
-
env: env
|
13
|
+
env: env,
|
14
|
+
status: 200)
|
14
15
|
}
|
15
16
|
let(:env) { {response_headers: headers} }
|
16
17
|
let(:headers) {
|
@@ -75,7 +76,44 @@ module Leadlight
|
|
75
76
|
end
|
76
77
|
end
|
77
78
|
|
79
|
+
describe '#match_status' do
|
80
|
+
it 'allows execution to proceed on match' do
|
81
|
+
object.should_receive(:baz)
|
82
|
+
subject.exec_tint do
|
83
|
+
match_status(200)
|
84
|
+
baz
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'can match on a range' do
|
89
|
+
object.should_receive(:baz)
|
90
|
+
subject.exec_tint do
|
91
|
+
match_status(200..299)
|
92
|
+
baz
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'does not allow execution to proceed on no match' do
|
97
|
+
object.should_not_receive(:baz)
|
98
|
+
subject.exec_tint do
|
99
|
+
match_status(301)
|
100
|
+
baz
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
78
105
|
describe '#match' do
|
106
|
+
let(:successful_matcher) {
|
107
|
+
stub.tap do |matcher|
|
108
|
+
matcher.should_receive(:===).with(object).and_return(true)
|
109
|
+
end
|
110
|
+
}
|
111
|
+
let(:failing_matcher) {
|
112
|
+
stub.tap do |matcher|
|
113
|
+
matcher.should_receive(:===).with(object).and_return(false)
|
114
|
+
end
|
115
|
+
}
|
116
|
+
|
79
117
|
it 'allows execution to proceed on true return' do
|
80
118
|
object.should_receive(:baz)
|
81
119
|
subject.exec_tint do
|
@@ -84,6 +122,34 @@ module Leadlight
|
|
84
122
|
end
|
85
123
|
end
|
86
124
|
|
125
|
+
it 'can match a given param against the representation' do
|
126
|
+
matcher = successful_matcher
|
127
|
+
object.should_receive(:baz)
|
128
|
+
subject.exec_tint do
|
129
|
+
match matcher
|
130
|
+
baz
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'can match several params against the representation' do
|
135
|
+
matchers = [ failing_matcher, successful_matcher ]
|
136
|
+
object.should_receive(:baz)
|
137
|
+
subject.exec_tint do
|
138
|
+
match *matchers
|
139
|
+
baz
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'can match using params and a block' do
|
144
|
+
param_matcher = failing_matcher
|
145
|
+
block_matcher = proc { (2 + 2) == 4 }
|
146
|
+
object.should_receive(:baz)
|
147
|
+
subject.exec_tint do
|
148
|
+
match param_matcher, &block_matcher
|
149
|
+
baz
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
87
153
|
it 'does not allow execution to proceed on false return' do
|
88
154
|
object.should_not_receive(:baz)
|
89
155
|
subject.exec_tint do
|