ably-rest 0.8.14 → 0.8.15

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 (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