ably 0.7.5 → 0.7.6

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 (63) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +1 -0
  3. data/.gitmodules +3 -0
  4. data/README.md +46 -22
  5. data/SPEC.md +345 -240
  6. data/ably.gemspec +4 -2
  7. data/lib/ably/auth.rb +18 -14
  8. data/lib/ably/models/message.rb +1 -1
  9. data/lib/ably/models/paginated_resource.rb +31 -44
  10. data/lib/ably/models/presence_message.rb +1 -1
  11. data/lib/ably/models/stat.rb +67 -24
  12. data/lib/ably/models/stats_types.rb +131 -0
  13. data/lib/ably/modules/async_wrapper.rb +3 -2
  14. data/lib/ably/modules/message_emitter.rb +2 -2
  15. data/lib/ably/realtime.rb +1 -1
  16. data/lib/ably/realtime/channel.rb +24 -3
  17. data/lib/ably/realtime/channel/channel_manager.rb +1 -0
  18. data/lib/ably/realtime/client.rb +2 -2
  19. data/lib/ably/realtime/connection.rb +1 -1
  20. data/lib/ably/realtime/presence.rb +12 -1
  21. data/lib/ably/rest.rb +1 -1
  22. data/lib/ably/rest/channel.rb +4 -5
  23. data/lib/ably/rest/client.rb +5 -5
  24. data/lib/ably/rest/presence.rb +2 -2
  25. data/lib/ably/version.rb +1 -1
  26. data/spec/acceptance/realtime/channel_history_spec.rb +74 -23
  27. data/spec/acceptance/realtime/channel_spec.rb +3 -3
  28. data/spec/acceptance/realtime/client_spec.rb +3 -3
  29. data/spec/acceptance/realtime/connection_failures_spec.rb +2 -2
  30. data/spec/acceptance/realtime/connection_spec.rb +4 -4
  31. data/spec/acceptance/realtime/message_spec.rb +5 -5
  32. data/spec/acceptance/realtime/presence_history_spec.rb +56 -13
  33. data/spec/acceptance/realtime/presence_spec.rb +8 -8
  34. data/spec/acceptance/realtime/stats_spec.rb +1 -1
  35. data/spec/acceptance/realtime/time_spec.rb +1 -1
  36. data/spec/acceptance/rest/auth_spec.rb +31 -4
  37. data/spec/acceptance/rest/base_spec.rb +3 -3
  38. data/spec/acceptance/rest/channel_spec.rb +19 -19
  39. data/spec/acceptance/rest/channels_spec.rb +1 -1
  40. data/spec/acceptance/rest/client_spec.rb +9 -6
  41. data/spec/acceptance/rest/encoders_spec.rb +1 -1
  42. data/spec/acceptance/rest/message_spec.rb +10 -10
  43. data/spec/acceptance/rest/presence_spec.rb +81 -51
  44. data/spec/acceptance/rest/stats_spec.rb +46 -41
  45. data/spec/acceptance/rest/time_spec.rb +1 -1
  46. data/spec/shared/client_initializer_behaviour.rb +30 -19
  47. data/spec/spec_helper.rb +3 -0
  48. data/spec/support/markdown_spec_formatter.rb +1 -1
  49. data/spec/support/test_app.rb +11 -24
  50. data/spec/unit/auth_spec.rb +1 -1
  51. data/spec/unit/models/paginated_resource_spec.rb +81 -72
  52. data/spec/unit/models/stats_spec.rb +289 -0
  53. data/spec/unit/modules/async_wrapper_spec.rb +1 -1
  54. data/spec/unit/realtime/client_spec.rb +1 -1
  55. data/spec/unit/realtime/realtime_spec.rb +1 -1
  56. data/spec/unit/rest/channel_spec.rb +1 -1
  57. data/spec/unit/rest/client_spec.rb +8 -8
  58. data/spec/unit/rest/rest_spec.rb +1 -1
  59. data/spec/unit/util/crypto_spec.rb +1 -1
  60. metadata +55 -43
  61. data/spec/resources/crypto-data-128.json +0 -56
  62. data/spec/resources/crypto-data-256.json +0 -56
  63. data/spec/unit/models/stat_spec.rb +0 -113
@@ -47,8 +47,9 @@ module Ably::Modules
47
47
  operation_with_exception_handling = proc do
48
48
  begin
49
49
  yield
50
- rescue StandardError => e
51
- deferrable.fail e
50
+ rescue StandardError => err
51
+ logger.error "An exception in an AsyncWrapper block was caught. #{err.class}: #{err.message}\n#{err.backtrace.join("\n")}"
52
+ deferrable.fail err
52
53
  end
53
54
  end
54
55
 
@@ -6,7 +6,7 @@ module Ably::Modules
6
6
  module MessageEmitter
7
7
  # Subscribe to events on this object
8
8
  #
9
- # @param name [String,Symbol] Optional, the event name to subscribe to. Defaults to `:all` events
9
+ # @param names [String,Symbol] Optional, the event name(s) to subscribe to. Defaults to `:all` events
10
10
  # @yield [Object] For each event, the provided block is called with the event payload object
11
11
  #
12
12
  # @return [void]
@@ -22,7 +22,7 @@ module Ably::Modules
22
22
  # Unsubscribe the matching block for events on the this object.
23
23
  # If a block is not provided, all subscriptions will be unsubscribed
24
24
  #
25
- # @param name [String,Symbol] Optional, the event name to unsubscribe from. Defaults to `:all` events
25
+ # @param names [String,Symbol] Optional, the event name(s) to unsubscribe from. Defaults to `:all` events
26
26
  #
27
27
  # @return [void]
28
28
  #
@@ -50,7 +50,7 @@ module Ably
50
50
  # client = Ably::Realtime.new('key.id:secret')
51
51
  #
52
52
  # # create a new client authenticating with basic auth and a client_id
53
- # client = Ably::Realtime.new(api_key: 'key.id:secret', client_id: 'john')
53
+ # client = Ably::Realtime.new(key: 'key.id:secret', client_id: 'john')
54
54
  #
55
55
  def self.new(options, &token_request_block)
56
56
  Ably::Realtime::Client.new(options, &token_request_block)
@@ -23,7 +23,7 @@ module Ably
23
23
  # Channel::STATE.Detached
24
24
  # Channel::STATE.Failed
25
25
  #
26
- # Channels emit errors - use `on(:error)` to subscribe to errors
26
+ # Channels emit errors - use +on(:error)+ to subscribe to errors
27
27
  #
28
28
  # @!attribute [r] state
29
29
  # @return {Ably::Realtime::Connection::STATE} channel state
@@ -71,13 +71,18 @@ module Ably
71
71
  # @api private
72
72
  attr_reader :manager
73
73
 
74
+ # Serial number assigned to this channel when it was attached
75
+ # @return [Integer]
76
+ # @api private
77
+ attr_reader :attached_serial
78
+
74
79
  # Initialize a new Channel object
75
80
  #
76
81
  # @param client [Ably::Rest::Client]
77
82
  # @param name [String] The name of the channel
78
83
  # @param channel_options [Hash] Channel options, currently reserved for Encryption options
79
84
  # @option channel_options [Boolean] :encrypted setting this to true for this channel will encrypt & decrypt all messages automatically
80
- # @option channel_options [Hash] :cipher_params A hash of options to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of `cipher_params` options
85
+ # @option channel_options [Hash] :cipher_params A hash of options to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of +cipher_params+ options
81
86
  #
82
87
  def initialize(client, name, channel_options = {})
83
88
  ensure_utf_8 :name, name
@@ -181,13 +186,24 @@ module Ably
181
186
 
182
187
  # Return the message history of the channel
183
188
  #
189
+ # Once attached to a channel, you can retrieve messages published on the channel before the
190
+ # channel was attached with the option <tt>until_attach: true</tt>. This is very useful for
191
+ # developers who wish to subscribe to new realtime messages yet also display historical messages with
192
+ # the guarantee that no messages have been missed.
193
+ #
184
194
  # @param (see Ably::Rest::Channel#history)
185
195
  # @option options (see Ably::Rest::Channel#history)
196
+ # @option options [Boolean] :until_attach When true, request for history will be limited only to messages published before this channel was attached. Channel must be attached.
186
197
  #
187
- # @yield [Ably::Models::PaginatedResource<Ably::Models::Message>] An Array of {Ably::Models::Message} objects that supports paging (#next_page, #first_page)
198
+ # @yield [Ably::Models::PaginatedResource<Ably::Models::Message>] First {Ably::Models::PaginatedResource page} of {Ably::Models::Message} objects accessible with {Ably::Models::PaginatedResource#items #items}.
188
199
  #
189
200
  # @return [Ably::Util::SafeDeferrable]
190
201
  def history(options = {}, &callback)
202
+ if options.delete(:until_attach)
203
+ raise ArgumentError, 'option :until_attach cannot be specified if the channel is not attached' unless attached?
204
+ options[:from_serial] = attached_serial
205
+ end
206
+
191
207
  async_wrap(callback) do
192
208
  rest_channel.history(options.merge(async_blocking_operations: true))
193
209
  end
@@ -212,6 +228,11 @@ module Ably
212
228
  @error_reason = nil
213
229
  end
214
230
 
231
+ # @api private
232
+ def set_attached_serial(serial)
233
+ @attached_serial = serial
234
+ end
235
+
215
236
  # Used by {Ably::Modules::StateEmitter} to debug state changes
216
237
  # @api private
217
238
  def logger
@@ -40,6 +40,7 @@ module Ably::Realtime
40
40
  else
41
41
  channel.presence.manager.sync_not_expected
42
42
  end
43
+ channel.set_attached_serial attached_protocol_message.channel_serial
43
44
  end
44
45
 
45
46
  # An error has occurred on the channel
@@ -75,7 +75,7 @@ module Ably
75
75
  # client = Ably::Realtime::Client.new('key.id:secret')
76
76
  #
77
77
  # # create a new client and configure a client ID used for presence
78
- # client = Ably::Realtime::Client.new(api_key: 'key.id:secret', client_id: 'john')
78
+ # client = Ably::Realtime::Client.new(key: 'key.id:secret', client_id: 'john')
79
79
  #
80
80
  def initialize(options, &token_request_block)
81
81
  @rest_client = Ably::Rest::Client.new(options, &token_request_block)
@@ -114,7 +114,7 @@ module Ably
114
114
  # @param (see Ably::Rest::Client#stats)
115
115
  # @option options (see Ably::Rest::Client#stats)
116
116
  #
117
- # @yield [Ably::Models::PaginatedResource<Ably::Models::Stat>] An Array of Stats
117
+ # @yield [Ably::Models::PaginatedResource<Ably::Models::Stats>] An Array of Stats
118
118
  #
119
119
  # @return [Ably::Util::SafeDeferrable]
120
120
  #
@@ -150,7 +150,7 @@ module Ably
150
150
  # @yield [Integer] if a block is passed to this method, then this block will be called once the ping heartbeat is received with the time elapsed in milliseconds
151
151
  #
152
152
  # @example
153
- # client = Ably::Rest::Client.new(api_key: 'key.id:secret')
153
+ # client = Ably::Rest::Client.new(key: 'key.id:secret')
154
154
  # client.connection.ping do |ms_elapsed|
155
155
  # puts "Ping took #{ms_elapsed}ms"
156
156
  # end
@@ -265,14 +265,25 @@ module Ably::Realtime
265
265
 
266
266
  # Return the presence messages history for the channel
267
267
  #
268
+ # Once attached to a channel, you can retrieve presence message history on the channel before the
269
+ # channel was attached with the option <tt>until_attach: true</tt>. This is very useful for
270
+ # developers who wish to capture new presence events as well as retrieve historical presence state with
271
+ # the guarantee that no presence history has been missed.
272
+ #
268
273
  # @param (see Ably::Rest::Presence#history)
269
274
  # @option options (see Ably::Rest::Presence#history)
275
+ # @option options [Boolean] :until_attach When true, request for history will be limited only to messages published before the associated channel was attached. The associated channel must be attached.
270
276
  #
271
- # @yield [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of {Ably::Models::PresenceMessage} objects that supports paging (#next_page, #first_page)
277
+ # @yield [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResource page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResource#items #items}.
272
278
  #
273
279
  # @return [Ably::Util::SafeDeferrable]
274
280
  #
275
281
  def history(options = {}, &callback)
282
+ if options.delete(:until_attach)
283
+ raise ArgumentError, 'option :until_attach cannot be specified if the channel is not attached' unless channel.attached?
284
+ options[:from_serial] = channel.attached_serial
285
+ end
286
+
276
287
  async_wrap(callback) do
277
288
  rest_presence.history(options.merge(async_blocking_operations: true))
278
289
  end
@@ -34,7 +34,7 @@ module Ably
34
34
  # client = Ably::Rest.new('key.id:secret')
35
35
  #
36
36
  # # create a new client authenticating with basic auth and a client_id
37
- # client = Ably::Rest.new(api_key: 'key.id:secret', client_id: 'john')
37
+ # client = Ably::Rest.new(key: 'key.id:secret', client_id: 'john')
38
38
  #
39
39
  def self.new(options, &token_request_block)
40
40
  Ably::Rest::Client.new(options, &token_request_block)
@@ -20,7 +20,7 @@ module Ably
20
20
  # @param name [String] The name of the channel
21
21
  # @param channel_options [Hash] Channel options, currently reserved for Encryption options
22
22
  # @option channel_options [Boolean] :encrypted setting this to true for this channel will encrypt & decrypt all messages automatically
23
- # @option channel_options [Hash] :cipher_params A hash of options to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of `cipher_params` options
23
+ # @option channel_options [Hash] :cipher_params A hash of options to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of +cipher_params+ options
24
24
  #
25
25
  def initialize(client, name, channel_options = {})
26
26
  ensure_utf_8 :name, name
@@ -52,16 +52,15 @@ module Ably
52
52
  [201, 204].include?(response.status)
53
53
  end
54
54
 
55
- # Return the message history of the channel
55
+ # Return the message of the channel
56
56
  #
57
57
  # @param [Hash] options the options for the message history request
58
58
  # @option options [Integer,Time] :start Time or millisecond since epoch
59
59
  # @option options [Integer,Time] :end Time or millisecond since epoch
60
- # @option options [Symbol] :direction `:forwards` or `:backwards`
60
+ # @option options [Symbol] :direction +:forwards+ or +:backwards+
61
61
  # @option options [Integer] :limit Maximum number of messages to retrieve up to 10,000
62
- # @option options [Symbol] :by `:message`, `:bundle` or `:hour`. Defaults to `:message`
63
62
  #
64
- # @return [Ably::Models::PaginatedResource<Ably::Models::Message>] An Array of {Ably::Models::Message} objects that supports paging (#next_page, #first_page)
63
+ # @return [Ably::Models::PaginatedResource<Ably::Models::Message>] First {Ably::Models::PaginatedResource page} of {Ably::Models::Message} objects accessible with {Ably::Models::PaginatedResource#items #items}.
65
64
  def history(options = {})
66
65
  url = "#{base_path}/messages"
67
66
  options = options.dup
@@ -70,7 +70,7 @@ module Ably
70
70
  # @param [Hash,String] options an options Hash used to configure the client and the authentication, or String with an API key or Token ID
71
71
  # @option options (see Ably::Auth#authorise)
72
72
  # @option options [Boolean] :tls TLS is used by default, providing a value of false disables TLS. Please note Basic Auth is disallowed without TLS as secrets cannot be transmitted over unsecured connections.
73
- # @option options [String] :api_key API key comprising the key ID and key secret in a single string
73
+ # @option options [String] :key API key comprising the key ID and key secret in a single string
74
74
  # @option options [Boolean] :use_token_auth Will force Basic Auth if set to false, and TOken auth if set to true
75
75
  # @option options [String] :environment Specify 'sandbox' when testing the client library against an alternate Ably environment
76
76
  # @option options [Symbol] :protocol Protocol used to communicate with Ably, :json and :msgpack currently supported. Defaults to :msgpack
@@ -89,7 +89,7 @@ module Ably
89
89
  # client = Ably::Rest::Client.new('key.id:secret')
90
90
  #
91
91
  # # create a new client and configure a client ID used for presence
92
- # client = Ably::Rest::Client.new(api_key: 'key.id:secret', client_id: 'john')
92
+ # client = Ably::Rest::Client.new(key: 'key.id:secret', client_id: 'john')
93
93
  #
94
94
  def initialize(options, &token_request_block)
95
95
  raise ArgumentError, 'Options Hash is expected' if options.nil?
@@ -97,7 +97,7 @@ module Ably
97
97
  options = options.clone
98
98
  if options.kind_of?(String)
99
99
  options = if options.match(/^[\w]{2,}\.[\w]{2,}:[\w]{2,}$/)
100
- { api_key: options }
100
+ { key: options }
101
101
  else
102
102
  { token_id: options }
103
103
  end
@@ -152,7 +152,7 @@ module Ably
152
152
  # @option options [Integer] :limit Maximum number of stats to retrieve up to 10,000
153
153
  # @option options [Symbol] :by `:minute`, `:hour`, `:day` or `:month`. Defaults to `:minute`
154
154
  #
155
- # @return [Ably::Models::PaginatedResource<Ably::Models::Stat>] An Array of Stats
155
+ # @return [Ably::Models::PaginatedResource<Ably::Models::Stats>] An Array of Stats
156
156
  #
157
157
  def stats(options = {})
158
158
  options = {
@@ -163,7 +163,7 @@ module Ably
163
163
  [:start, :end].each { |option| options[option] = as_since_epoch(options[option]) if options.has_key?(option) }
164
164
 
165
165
  paginated_options = {
166
- coerce_into: 'Ably::Models::Stat'
166
+ coerce_into: 'Ably::Models::Stats'
167
167
  }
168
168
 
169
169
  url = '/stats'
@@ -28,7 +28,7 @@ module Ably
28
28
  # @option options [Symbol] :direction `:forwards` or `:backwards`
29
29
  # @option options [Integer] :limit Maximum number of members to retrieve up to 10,000
30
30
  #
31
- # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of {Ably::Models::PresenceMessage} objects that supports paging (#next_page, #first_page)
31
+ # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResource page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResource#items #items}.
32
32
  #
33
33
  def get(options = {})
34
34
  options = options.dup
@@ -55,7 +55,7 @@ module Ably
55
55
  # @option options [Symbol] :direction `:forwards` or `:backwards`
56
56
  # @option options [Integer] :limit Maximum number of presence messages to retrieve up to 10,000
57
57
  #
58
- # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of {Ably::Models::PresenceMessage} objects that supports paging (#next_page, #first_page)
58
+ # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResource page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResource#items #items}.
59
59
  #
60
60
  def history(options = {})
61
61
  url = "#{base_path}/history"
@@ -1,3 +1,3 @@
1
1
  module Ably
2
- VERSION = '0.7.5'
2
+ VERSION = '0.7.6'
3
3
  end
@@ -3,10 +3,11 @@ require 'spec_helper'
3
3
 
4
4
  describe Ably::Realtime::Channel, '#history', :event_machine do
5
5
  vary_by_protocol do
6
- let(:default_options) { options.merge(api_key: api_key, environment: environment, protocol: protocol) }
6
+ let(:default_options) { options.merge(key: api_key, environment: environment, protocol: protocol) }
7
7
 
8
8
  let(:client) { Ably::Realtime::Client.new(default_options) }
9
9
  let(:channel) { client.channel(channel_name) }
10
+ let(:rest_channel) { client.rest_client.channel(channel_name) }
10
11
 
11
12
  let(:client2) { Ably::Realtime::Client.new(default_options) }
12
13
  let(:channel2) { client2.channel(channel_name) }
@@ -21,20 +22,20 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
21
22
  channel.publish('event', payload) do |message|
22
23
  history = channel.history
23
24
  expect(history).to be_a(Ably::Util::SafeDeferrable)
24
- history.callback do |messages|
25
- expect(messages.count).to eql(1)
26
- expect(messages).to be_a(Ably::Models::PaginatedResource)
25
+ history.callback do |page|
26
+ expect(page.items.count).to eql(1)
27
+ expect(page).to be_a(Ably::Models::PaginatedResource)
27
28
  stop_reactor
28
29
  end
29
30
  end
30
31
  end
31
32
 
32
33
  context 'with a single client publishing and receiving' do
33
- it 'retrieves real-time history' do
34
+ it 'retrieves realtime history' do
34
35
  channel.publish('event', payload) do |message|
35
- channel.history do |history|
36
- expect(history.length).to eql(1)
37
- expect(history[0].data).to eql(payload)
36
+ channel.history do |page|
37
+ expect(page.items.length).to eql(1)
38
+ expect(page.items[0].data).to eql(payload)
38
39
  stop_reactor
39
40
  end
40
41
  end
@@ -42,15 +43,15 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
42
43
  end
43
44
 
44
45
  context 'with two clients publishing messages on the same channel' do
45
- it 'retrieves real-time history on both channels' do
46
+ it 'retrieves realtime history on both channels' do
46
47
  channel.publish('event', payload) do |message|
47
48
  channel2.publish('event', payload) do |message|
48
- channel.history do |history|
49
- expect(history.length).to eql(2)
50
- expect(history.map(&:data).uniq).to eql([payload])
49
+ channel.history do |page|
50
+ expect(page.items.length).to eql(2)
51
+ expect(page.items.map(&:data).uniq).to eql([payload])
51
52
 
52
- channel2.history do |history_2|
53
- expect(history_2.length).to eql(2)
53
+ channel2.history do |page_2|
54
+ expect(page_2.items.length).to eql(2)
54
55
  stop_reactor
55
56
  end
56
57
  end
@@ -65,18 +66,18 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
65
66
  let(:limit) { 15 }
66
67
 
67
68
  def ensure_message_history_direction_and_paging_is_correct(direction)
68
- channel.history(direction: direction, limit: limit) do |history|
69
- expect(history.length).to eql(limit)
69
+ channel.history(direction: direction, limit: limit) do |history_page|
70
+ expect(history_page.items.length).to eql(limit)
70
71
  limit.times do |index|
71
- expect(history[index].data).to eql("history#{index}")
72
+ expect(history_page.items[index].data).to eql("history#{index}")
72
73
  end
73
74
 
74
- history.next_page do |history|
75
- expect(history.length).to eql(limit)
75
+ history_page.next do |next_page|
76
+ expect(next_page.items.length).to eql(limit)
76
77
  limit.times do |index|
77
- expect(history[index].data).to eql("history#{index + limit}")
78
+ expect(next_page.items[index].data).to eql("history#{index + limit}")
78
79
  end
79
- expect(history.last_page?).to eql(true)
80
+ expect(next_page).to be_last
80
81
 
81
82
  stop_reactor
82
83
  end
@@ -141,8 +142,8 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
141
142
  channel.subscribe('event') do |message|
142
143
  messages << message
143
144
  if messages.count == batches * messages_per_batch
144
- channel.history do |history|
145
- expect(history.map(&:id).sort).to eql(messages.map(&:id).sort)
145
+ channel.history do |page|
146
+ expect(page.items.map(&:id).sort).to eql(messages.map(&:id).sort)
146
147
  stop_reactor
147
148
  end
148
149
  end
@@ -150,5 +151,55 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
150
151
  end
151
152
  end
152
153
  end
154
+
155
+ context 'with option until_attach: true' do
156
+ let(:event) { random_str }
157
+ let(:message_before_attach) { random_str }
158
+ let(:message_after_attach) { random_str }
159
+
160
+ it 'retrieves all messages before channel was attached' do
161
+ rest_channel.publish event, message_before_attach
162
+
163
+ channel.attach do
164
+ channel.publish(event, message_after_attach) do
165
+ channel.history(until_attach: true) do |messages|
166
+ expect(messages.items.count).to eql(1)
167
+ expect(messages.items.first.data).to eql(message_before_attach)
168
+ stop_reactor
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ context 'and two pages of messages' do
175
+ it 'retrieves two pages of messages before channel was attached' do
176
+ 10.times { rest_channel.publish event, message_before_attach }
177
+
178
+ channel.attach do
179
+ 10.times { rest_channel.publish event, message_after_attach }
180
+
181
+ EventMachine.add_timer(0.5) do
182
+ channel.history(until_attach: true, limit: 5) do |messages|
183
+ expect(messages.items.count).to eql(5)
184
+ expect(messages.items.map(&:data).uniq.first).to eql(message_before_attach)
185
+
186
+ messages.next do |next_page_messages|
187
+ expect(next_page_messages.items.count).to eql(5)
188
+ expect(next_page_messages.items.map(&:data).uniq.first).to eql(message_before_attach)
189
+ expect(next_page_messages).to be_last
190
+
191
+ stop_reactor
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ it 'raises an exception unless state is attached' do
200
+ expect { channel.history(until_attach: true) }.to raise_error(ArgumentError, /not attached/)
201
+ stop_reactor
202
+ end
203
+ end
153
204
  end
154
205
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
 
4
4
  describe Ably::Realtime::Channel, :event_machine do
5
5
  vary_by_protocol do
6
- let(:default_options) { { api_key: api_key, environment: environment, protocol: protocol } }
6
+ let(:default_options) { { key: api_key, environment: environment, protocol: protocol } }
7
7
  let(:client_options) { default_options }
8
8
 
9
9
  let(:client) { Ably::Realtime::Client.new(client_options) }
@@ -158,7 +158,7 @@ describe Ably::Realtime::Channel, :event_machine do
158
158
 
159
159
  context 'failure as a result of insufficient key permissions' do
160
160
  let(:restricted_client) do
161
- Ably::Realtime::Client.new(default_options.merge(api_key: restricted_api_key, log_level: :fatal))
161
+ Ably::Realtime::Client.new(default_options.merge(key: restricted_api_key, log_level: :fatal))
162
162
  end
163
163
  let(:restricted_channel) { restricted_client.channel("cannot_subscribe") }
164
164
 
@@ -202,7 +202,7 @@ describe Ably::Realtime::Channel, :event_machine do
202
202
  restricted_channel.once(:failed) do
203
203
  restricted_client.close do
204
204
  # A direct call to #authorise is synchronous
205
- restricted_client.auth.authorise(api_key: api_key)
205
+ restricted_client.auth.authorise(key: api_key)
206
206
 
207
207
  restricted_client.connect do
208
208
  restricted_channel.once(:attached) do