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.
- checksums.yaml +4 -4
- data/lib/submodules/ably-ruby/CHANGELOG.md +48 -61
- data/lib/submodules/ably-ruby/README.md +6 -0
- data/lib/submodules/ably-ruby/ably.gemspec +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/http_paginated_response.rb +90 -0
- data/lib/submodules/ably-ruby/lib/ably/models/paginated_result.rb +5 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +30 -15
- data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +25 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +3 -3
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +13 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +3 -3
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +60 -3
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +29 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +8 -6
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +52 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +44 -8
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +79 -38
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +2 -4
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +69 -21
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +10 -12
- data/lib/submodules/ably-ruby/spec/unit/models/http_paginated_result_spec.rb +380 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +109 -51
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +3 -3
- 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
|
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
|
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
|
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
|