ably-rest 0.8.14 → 0.8.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +4 -4
  2. data/lib/submodules/ably-ruby/CHANGELOG.md +48 -61
  3. data/lib/submodules/ably-ruby/README.md +6 -0
  4. data/lib/submodules/ably-ruby/ably.gemspec +1 -1
  5. data/lib/submodules/ably-ruby/lib/ably/models/http_paginated_response.rb +90 -0
  6. data/lib/submodules/ably-ruby/lib/ably/models/paginated_result.rb +5 -0
  7. data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +30 -15
  8. data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +25 -0
  9. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +3 -3
  10. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +13 -0
  11. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +2 -2
  12. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +3 -3
  13. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +60 -3
  14. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  15. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +29 -0
  16. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +8 -6
  17. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +52 -0
  18. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +44 -8
  19. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +79 -38
  20. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +2 -4
  21. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +69 -21
  22. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +10 -12
  23. data/lib/submodules/ably-ruby/spec/unit/models/http_paginated_result_spec.rb +380 -0
  24. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +109 -51
  25. data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +3 -3
  26. metadata +5 -4
@@ -69,14 +69,12 @@ describe Ably::Rest::Presence do
69
69
  }
70
70
  end
71
71
  let(:endpoint) do
72
- client.endpoint.tap do |client_end_point|
73
- client_end_point.user = key_name
74
- client_end_point.password = key_secret
75
- end
72
+ client.endpoint
76
73
  end
77
74
  let!(:get_stub) {
78
75
  query_params = query_options.map { |k, v| "#{k}=#{v}" }.join('&')
79
76
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence?#{query_params}").
77
+ with(basic_auth: [key_name, key_secret]).
80
78
  to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
81
79
  }
82
80
  let(:channel_name) { random_str }
@@ -182,10 +180,7 @@ describe Ably::Rest::Presence do
182
180
  let(:user) { 'appid.keyuid' }
183
181
  let(:secret) { random_str(8) }
184
182
  let(:endpoint) do
185
- client.endpoint.tap do |client_end_point|
186
- client_end_point.user = user
187
- client_end_point.password = secret
188
- end
183
+ client.endpoint
189
184
  end
190
185
  let(:client) do
191
186
  Ably::Rest::Client.new(key: "#{user}:#{secret}")
@@ -201,6 +196,7 @@ describe Ably::Rest::Presence do
201
196
  let!(:history_stub) {
202
197
  query_params = history_options.map { |k, v| "#{k}=#{v}" }.join('&')
203
198
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence/history?#{query_params}").
199
+ with(basic_auth: [user, secret]).
204
200
  to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
205
201
  }
206
202
 
@@ -241,6 +237,7 @@ describe Ably::Rest::Presence do
241
237
  let!(:history_stub) {
242
238
  query_params = history_options.map { |k, v| "#{k}=#{v}" }.join('&')
243
239
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence/history?#{query_params}").
240
+ with(basic_auth: [user, secret]).
244
241
  to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
245
242
  }
246
243
 
@@ -310,10 +307,7 @@ describe Ably::Rest::Presence do
310
307
  let(:user) { 'appid.keyuid' }
311
308
  let(:secret) { random_str(8) }
312
309
  let(:endpoint) do
313
- client.endpoint.tap do |client_end_point|
314
- client_end_point.user = user
315
- client_end_point.password = secret
316
- end
310
+ client.endpoint
317
311
  end
318
312
  let(:client) do
319
313
  Ably::Rest::Client.new(client_options.merge(key: "#{user}:#{secret}"))
@@ -348,6 +342,7 @@ describe Ably::Rest::Presence do
348
342
  context '#get' do
349
343
  let!(:get_stub) {
350
344
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence?limit=100").
345
+ with(basic_auth: [user, secret]).
351
346
  to_return(:body => serialized_encoded_message, :headers => { 'Content-Type' => content_type })
352
347
  }
353
348
 
@@ -365,6 +360,7 @@ describe Ably::Rest::Presence do
365
360
  context '#history' do
366
361
  let!(:history_stub) {
367
362
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence/history?direction=backwards&limit=100").
363
+ with(basic_auth: [user, secret]).
368
364
  to_return(:body => serialized_encoded_message, :headers => { 'Content-Type' => content_type })
369
365
  }
370
366
 
@@ -395,6 +391,7 @@ describe Ably::Rest::Presence do
395
391
  let(:client_options) { default_options.merge(log_level: :fatal) }
396
392
  let!(:get_stub) {
397
393
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence?limit=100").
394
+ with(basic_auth: [user, secret]).
398
395
  to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type })
399
396
  }
400
397
  let(:presence_message) { presence.get.items.first }
@@ -419,6 +416,7 @@ describe Ably::Rest::Presence do
419
416
  let(:client_options) { default_options.merge(log_level: :fatal) }
420
417
  let!(:history_stub) {
421
418
  stub_request(:get, "#{endpoint}/channels/#{Addressable::URI.encode(channel_name)}/presence/history?direction=backwards&limit=100").
419
+ with(basic_auth: [user, secret]).
422
420
  to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type })
423
421
  }
424
422
  let(:presence_message) { presence.history.items.first }
@@ -0,0 +1,380 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ describe "Ably::Models::HttpPaginatedResponse: #HP1 -> #HP8" do
5
+ let(:paginated_result_class) { Ably::Models::HttpPaginatedResponse }
6
+ let(:headers) { Hash.new }
7
+ let(:client) do
8
+ instance_double('Ably::Rest::Client', logger: Ably::Models::NilLogger.new).tap do |client|
9
+ allow(client).to receive(:get).and_return(http_response)
10
+ end
11
+ end
12
+ let(:body) do
13
+ [
14
+ { 'id' => 0 },
15
+ { 'id' => 1 }
16
+ ]
17
+ end
18
+ let(:status) { "200" }
19
+ let(:http_response) do
20
+ instance_double('Faraday::Response', {
21
+ body: body,
22
+ headers: headers,
23
+ status: status
24
+ })
25
+ end
26
+ let(:base_url) { 'http://rest.ably.io/channels/channel_name' }
27
+ let(:full_url) { "#{base_url}/whatever?param=exists" }
28
+ let(:paginated_result_options) { Hash.new }
29
+ let(:first_paged_request) { paginated_result_class.new(http_response, full_url, client, paginated_result_options) }
30
+ subject { first_paged_request }
31
+
32
+ context '#items' do
33
+ it 'returns correct length from body' do
34
+ expect(subject.items.length).to eql(body.length)
35
+ end
36
+
37
+ it 'is Enumerable' do
38
+ expect(subject.items).to be_kind_of(Enumerable)
39
+ end
40
+
41
+ it 'is iterable' do
42
+ expect(subject.items.map { |d| d }).to eql(body)
43
+ end
44
+
45
+ context '#each' do
46
+ it 'returns an enumerator' do
47
+ expect(subject.items.each).to be_a(Enumerator)
48
+ end
49
+
50
+ it 'yields each item' do
51
+ items = []
52
+ subject.items.each do |item|
53
+ items << item
54
+ end
55
+ expect(items).to eq(body)
56
+ end
57
+ end
58
+
59
+ it 'provides [] accessor method' do
60
+ expect(subject.items[0][:id]).to eql(body[0][:id])
61
+ expect(subject.items[1][:id]).to eql(body[1][:id])
62
+ expect(subject.items[2]).to be_nil
63
+ end
64
+
65
+ specify '#first gets the first item in page' do
66
+ expect(subject.items.first[:id]).to eql(body[0][:id])
67
+ end
68
+
69
+ specify '#last gets the last item in page' do
70
+ expect(subject.items.last[:id]).to eql(body[1][:id])
71
+ end
72
+
73
+ context 'with coercion', :api_private do
74
+ let(:paginated_result_options) { { coerce_into: 'OpenStruct' } }
75
+
76
+ it 'returns coerced objects' do
77
+ expect(subject.items.first).to be_a(OpenStruct)
78
+ expect(subject.items.first.id).to eql(body.first['id'])
79
+ end
80
+ end
81
+ end
82
+
83
+ context 'paged transformations', :api_private do
84
+ let(:headers) do
85
+ {
86
+ 'link' => [
87
+ '<./history?index=1>; rel="next"'
88
+ ].join(', ')
89
+ }
90
+ end
91
+ let(:paged_client) do
92
+ instance_double('Ably::Rest::Client', logger: Ably::Models::NilLogger.new).tap do |client|
93
+ allow(client).to receive(:get).and_return(http_response_page2)
94
+ end
95
+ end
96
+ let(:body_page2) do
97
+ [
98
+ { id: 2 },
99
+ { id: 3 }
100
+ ]
101
+ end
102
+ let(:http_response_page2) do
103
+ instance_double('Faraday::Response', {
104
+ body: body_page2,
105
+ headers: headers
106
+ })
107
+ end
108
+
109
+ context 'with each block' do
110
+ subject do
111
+ paginated_result_class.new(http_response, full_url, paged_client, paginated_result_options) do |result|
112
+ result[:added_attribute_from_block] = "id:#{result[:id]}"
113
+ result
114
+ end
115
+ end
116
+
117
+ it 'calls the block for each result after retrieving the results' do
118
+ expect(subject.items[0][:added_attribute_from_block]).to eql("id:#{body[0][:id]}")
119
+ end
120
+
121
+ it 'calls the block for each result on second page after retrieving the results' do
122
+ page_1_first_id = subject.items[0][:id]
123
+ next_page = subject.next
124
+
125
+ expect(next_page.items[0][:added_attribute_from_block]).to eql("id:#{body_page2[0][:id]}")
126
+ expect(next_page.items[0][:id]).to_not eql(page_1_first_id)
127
+ end
128
+ end
129
+
130
+ if defined?(Ably::Realtime)
131
+ context 'with option async_blocking_operations: true' do
132
+ include RSpec::EventMachine
133
+
134
+ subject do
135
+ paginated_result_class.new(http_response, full_url, paged_client, async_blocking_operations: true)
136
+ end
137
+
138
+ context '#next' do
139
+ it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
140
+ run_reactor do
141
+ expect(subject.next).to be_a(Ably::Util::SafeDeferrable)
142
+ stop_reactor
143
+ end
144
+ end
145
+
146
+ it 'allows a success callback block to be added' do
147
+ run_reactor do
148
+ subject.next do |paginated_result|
149
+ expect(paginated_result).to be_a(Ably::Models::HttpPaginatedResponse)
150
+ stop_reactor
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ context '#first' do
157
+ it 'calls the errback callback when first page headers are missing' do
158
+ run_reactor do
159
+ subject.next do |paginated_result|
160
+ deferrable = subject.first
161
+ deferrable.errback do |error|
162
+ expect(error).to be_a(Ably::Exceptions::PageMissing)
163
+ stop_reactor
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ context 'with non paged http response' do
174
+ it 'is the last page' do
175
+ expect(subject).to be_last
176
+ end
177
+
178
+ it 'does not have next page' do
179
+ expect(subject).to_not have_next
180
+ end
181
+
182
+ it 'does not support pagination' do
183
+ expect(subject.supports_pagination?).to_not eql(true)
184
+ end
185
+
186
+ it 'returns nil when accessing next page' do
187
+ expect(subject.next).to be_nil
188
+ end
189
+
190
+ it 'returns nil when accessing first page' do
191
+ expect(subject.first).to be_nil
192
+ end
193
+ end
194
+
195
+ context 'with paged http response' do
196
+ let(:base_url) { 'http://rest.ably.io/channels/channel_name' }
197
+ let(:full_url) { "#{base_url}/messages" }
198
+ let(:headers) do
199
+ {
200
+ 'link' => [
201
+ '<./history?index=0>; rel="first"',
202
+ '<./history?index=0>; rel="current"',
203
+ '<./history?index=1>; rel="next"'
204
+ ].join(', ')
205
+ }
206
+ end
207
+
208
+ it 'has next page' do
209
+ expect(subject).to have_next
210
+ end
211
+
212
+ it 'is not the last page' do
213
+ expect(subject).to_not be_last
214
+ end
215
+
216
+ it 'supports pagination' do
217
+ expect(subject.supports_pagination?).to eql(true)
218
+ end
219
+
220
+ context 'accessing next page' do
221
+ let(:next_body) do
222
+ [ { id: 2 } ]
223
+ end
224
+ let(:next_headers) do
225
+ {
226
+ 'link' => [
227
+ '<./history?index=0>; rel="first"',
228
+ '<./history?index=1>; rel="current"'
229
+ ].join(', ')
230
+ }
231
+ end
232
+ let(:next_http_response) do
233
+ double('http_response', {
234
+ body: next_body,
235
+ headers: next_headers
236
+ })
237
+ end
238
+ let(:subject) { first_paged_request.next }
239
+
240
+ before do
241
+ expect(client).to receive(:get).with("#{base_url}/history?index=1").and_return(next_http_response).once
242
+ end
243
+
244
+ it 'returns another HttpPaginatedResponse' do
245
+ expect(subject).to be_a(paginated_result_class)
246
+ end
247
+
248
+ it 'retrieves the next page of results' do
249
+ expect(subject.items.length).to eql(next_body.length)
250
+ expect(subject.items[0][:id]).to eql(next_body[0][:id])
251
+ end
252
+
253
+ it 'does not have a next page' do
254
+ expect(subject).to_not have_next
255
+ end
256
+
257
+ it 'is the last page' do
258
+ expect(subject).to be_last
259
+ end
260
+
261
+ it 'returns nil when trying to access the last page when it is the last page' do
262
+ expect(subject).to be_last
263
+ expect(subject.next).to be_nil
264
+ end
265
+
266
+ context 'and then first page' do
267
+ before do
268
+ expect(client).to receive(:get).with("#{base_url}/history?index=0").and_return(http_response).once
269
+ end
270
+ subject { first_paged_request.next.first }
271
+
272
+ it 'returns a HttpPaginatedResponse' do
273
+ expect(subject).to be_a(paginated_result_class)
274
+ end
275
+
276
+ it 'retrieves the first page of results' do
277
+ expect(subject.items.length).to eql(body.length)
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ context 'response metadata' do
284
+ context 'successful response' do
285
+ let(:headers) { { 'Content-type' => 'application/json' } }
286
+ let(:status) { 200 }
287
+
288
+ specify '#success? is true' do
289
+ expect(subject).to be_succes
290
+ end
291
+
292
+ specify '#status_code reflects status code' do
293
+ expect(subject.status_code).to eql(200)
294
+ end
295
+
296
+ specify '#error_code to be empty' do
297
+ expect(subject.error_code).to be_nil
298
+ end
299
+
300
+ specify '#error_message to be empty' do
301
+ expect(subject.error_message).to be_nil
302
+ end
303
+
304
+ specify '#headers to be a hash' do
305
+ expect(subject.headers['Content-type']).to eql('application/json')
306
+ end
307
+ end
308
+
309
+ context 'failed response' do
310
+ let(:headers) { { 'X-Ably-Errormessage' => 'Fault', 'X-Ably-Errorcode' => '500' } }
311
+ let(:status) { 500 }
312
+
313
+ specify '#success? is false' do
314
+ expect(subject).to_not be_succes
315
+ end
316
+
317
+ specify '#status_code reflects status code' do
318
+ expect(subject.status_code).to eql(500)
319
+ end
320
+
321
+ specify '#error_code to be populated' do
322
+ expect(subject.error_code).to eql(500)
323
+ end
324
+
325
+ specify '#error_message to be populated' do
326
+ expect(subject.error_message).to eql('Fault')
327
+ end
328
+
329
+ specify '#headers to be present' do
330
+ expect(subject.headers['X-Ably-Errormessage']).to eql('Fault')
331
+ end
332
+ end
333
+ end
334
+
335
+ context '#items Array conversion and nil handling #HP3' do
336
+ context 'with Json Array' do
337
+ let(:body) do
338
+ [
339
+ { 'id' => 0 },
340
+ { 'id' => 1 }
341
+ ]
342
+ end
343
+
344
+ it 'is an array' do
345
+ expect(subject.items.length).to eql(2)
346
+ end
347
+ end
348
+
349
+ context 'with Json Object' do
350
+ let(:body) do
351
+ { 'id' => 0 }
352
+ end
353
+
354
+ it 'is an array' do
355
+ expect(subject.items.length).to eql(1)
356
+ expect(subject.items.first['id']).to eql(0)
357
+ end
358
+ end
359
+
360
+ context 'with empty response' do
361
+ let(:body) do
362
+ ''
363
+ end
364
+
365
+ it 'is an array' do
366
+ expect(subject.items.length).to eql(0)
367
+ end
368
+ end
369
+
370
+ context 'with nil response' do
371
+ let(:body) do
372
+ nil
373
+ end
374
+
375
+ it 'is an array' do
376
+ expect(subject.items.length).to eql(0)
377
+ end
378
+ end
379
+ end
380
+ end