leadlight 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|