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.
Files changed (52) hide show
  1. data/Gemfile.lock +5 -7
  2. data/README.md +82 -0
  3. data/default.gems +1 -0
  4. data/leadlight.gemspec +16 -7
  5. data/lib/leadlight.rb +18 -62
  6. data/lib/leadlight/basic_converter.rb +18 -0
  7. data/lib/leadlight/codec.rb +2 -0
  8. data/lib/leadlight/entity.rb +1 -0
  9. data/lib/leadlight/errors.rb +10 -2
  10. data/lib/leadlight/header_helpers.rb +11 -0
  11. data/lib/leadlight/hyperlinkable.rb +1 -1
  12. data/lib/leadlight/representation.rb +19 -1
  13. data/lib/leadlight/request.rb +44 -11
  14. data/lib/leadlight/service.rb +4 -9
  15. data/lib/leadlight/service_class_methods.rb +69 -0
  16. data/lib/leadlight/service_middleware.rb +2 -14
  17. data/lib/leadlight/tint.rb +6 -2
  18. data/lib/leadlight/tint_helper.rb +27 -4
  19. data/lib/leadlight/type_map.rb +101 -0
  20. data/spec/cassettes/Leadlight/authorized_GitHub_example/_user/has_the_expected_content.yml +8 -8
  21. data/spec/cassettes/Leadlight/authorized_GitHub_example/_user/indicates_the_expected_oath_scopes.yml +8 -8
  22. data/spec/cassettes/Leadlight/authorized_GitHub_example/adding_and_removing_team_members.yml +273 -284
  23. data/spec/cassettes/Leadlight/authorized_GitHub_example/{adding_and_removing_team_members/.yml → adding_and_removing_teams.yml} +57 -84
  24. data/spec/cassettes/Leadlight/authorized_GitHub_example/test_team/.yml +111 -117
  25. data/spec/cassettes/Leadlight/basic_GitHub_example/_root/.yml +58 -25
  26. data/spec/cassettes/Leadlight/basic_GitHub_example/_root/__location__/.yml +58 -25
  27. data/spec/cassettes/Leadlight/basic_GitHub_example/_root/should_be_a_204_no_content.yml +58 -25
  28. data/spec/cassettes/Leadlight/tinted_GitHub_example/_root/.yml +58 -25
  29. data/spec/cassettes/Leadlight/tinted_GitHub_example/_root/__location__/.yml +58 -25
  30. data/spec/cassettes/Leadlight/tinted_GitHub_example/_root/should_be_a_204_no_content.yml +58 -25
  31. data/spec/cassettes/Leadlight/tinted_GitHub_example/_user/has_the_expected_content.yml +122 -50
  32. data/spec/cassettes/Leadlight/tinted_GitHub_example/user_followers/.yml +190 -77
  33. data/spec/cassettes/Leadlight/tinted_GitHub_example/user_followers/should_be_able_to_follow_next_link.yml +260 -104
  34. data/spec/cassettes/Leadlight/tinted_GitHub_example/user_followers/should_be_enumerable.yml +616 -212
  35. data/spec/cassettes/Leadlight/tinted_GitHub_example/user_followers/should_be_enumerable_over_page_boundaries.yml +331 -131
  36. data/spec/cassettes/Leadlight/tinted_GitHub_example/user_followers/should_have_next_and_last_links.yml +190 -77
  37. data/spec/cassettes/Leadlight/tinted_GitHub_example/user_link/exists.yml +58 -25
  38. data/spec/cassettes/Leadlight/tinted_GitHub_example/user_link/links_to_the_expected_URL.yml +58 -25
  39. data/spec/leadlight/hyperlinkable_spec.rb +3 -1
  40. data/spec/leadlight/link_template_spec.rb +2 -1
  41. data/spec/leadlight/request_spec.rb +44 -21
  42. data/spec/leadlight/service_middleware_spec.rb +9 -46
  43. data/spec/leadlight/service_spec.rb +30 -24
  44. data/spec/leadlight/tint_helper_spec.rb +67 -1
  45. data/spec/leadlight/tint_spec.rb +69 -0
  46. data/spec/leadlight/type_map_spec.rb +127 -0
  47. data/spec/leadlight_spec.rb +102 -51
  48. data/spec/support/credentials.rb +10 -2
  49. data/spec/support/vcr.rb +1 -1
  50. metadata +61 -32
  51. data/lib/leadlight/type.rb +0 -71
  52. data/spec/leadlight/type_spec.rb +0 -137
@@ -23,7 +23,9 @@ module Leadlight
23
23
  }
24
24
 
25
25
  before do
26
- representation.stub(__response__: response, __service__: service)
26
+ representation.stub(__response__: response,
27
+ __service__: service,
28
+ __location__: '/foo')
27
29
  end
28
30
 
29
31
  describe '.extend' do
@@ -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.call(@env)
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(:faraday_response) { FakeFaradayResponse.new(faraday_env) }
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, body, {}).
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(faraday_response)
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
- faraday_response.run_completion_handlers
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
- faraday_response.run_completion_handlers
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
- faraday_env[:status] = 404
225
- expect { submit_and_complete.raise_on_error }.to raise_error(ResourceNotFound)
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
- faraday_env[:status] = 500
250
+ faraday_response.should_receive(:success?).and_return(false)
230
251
  subject.raise_on_error
231
- expect { submit_and_complete }.to raise_error(ServerError)
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
- test_stack.get('/').env
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
- test_stack.get('/').env[:leadlight_service].should equal(service)
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) { Class.new do include Service end }
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, url: nil)
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
- Request.should_receive(:new).and_return(request)
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
- Request.should_receive(:new).
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
- Request.should_receive(:new).
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
- Request.should_receive(:new).
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
- Request.should_receive(:new).
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
- Request.should_receive(:new).
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