ably-rest 0.7.5 → 0.8.1

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 (76) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +2 -0
  3. data/README.md +41 -15
  4. data/SPEC.md +654 -518
  5. data/lib/submodules/ably-ruby/.gitignore +1 -0
  6. data/lib/submodules/ably-ruby/.gitmodules +3 -0
  7. data/lib/submodules/ably-ruby/README.md +54 -26
  8. data/lib/submodules/ably-ruby/SPEC.md +468 -322
  9. data/lib/submodules/ably-ruby/ably.gemspec +4 -2
  10. data/lib/submodules/ably-ruby/lib/ably/auth.rb +185 -131
  11. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +1 -1
  12. data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +31 -44
  13. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +2 -2
  14. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +1 -2
  15. data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +67 -24
  16. data/lib/submodules/ably-ruby/lib/ably/models/stats_types.rb +131 -0
  17. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +101 -0
  18. data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +108 -0
  19. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +3 -2
  20. data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +1 -1
  21. data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +2 -2
  22. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +3 -7
  23. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +32 -5
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +1 -0
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +4 -8
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +5 -3
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +12 -1
  28. data/lib/submodules/ably-ruby/lib/ably/rest.rb +3 -7
  29. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +13 -10
  30. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +19 -20
  31. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +14 -12
  32. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  33. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +74 -23
  34. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +3 -3
  35. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +18 -18
  36. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +5 -5
  37. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +12 -12
  38. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +5 -5
  39. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +56 -13
  40. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +8 -8
  41. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +1 -1
  42. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +1 -1
  43. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +262 -158
  44. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +11 -9
  45. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +28 -21
  46. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +1 -1
  47. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +30 -27
  48. data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +1 -1
  49. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +10 -10
  50. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +93 -56
  51. data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +50 -45
  52. data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +1 -1
  53. data/lib/submodules/ably-ruby/spec/rspec_config.rb +3 -2
  54. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +36 -28
  55. data/lib/submodules/ably-ruby/spec/spec_helper.rb +3 -0
  56. data/lib/submodules/ably-ruby/spec/support/api_helper.rb +3 -3
  57. data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +1 -1
  58. data/lib/submodules/ably-ruby/spec/support/test_app.rb +20 -33
  59. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +18 -1
  60. data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +81 -72
  61. data/lib/submodules/ably-ruby/spec/unit/models/stats_spec.rb +289 -0
  62. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +111 -0
  63. data/lib/submodules/ably-ruby/spec/unit/models/token_request_spec.rb +110 -0
  64. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +1 -1
  65. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +1 -1
  66. data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +1 -1
  67. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +1 -1
  68. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +8 -8
  69. data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +1 -1
  70. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +1 -1
  71. metadata +9 -7
  72. data/lib/submodules/ably-ruby/lib/ably/models/token.rb +0 -74
  73. data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +0 -56
  74. data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +0 -56
  75. data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +0 -113
  76. data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +0 -86
@@ -0,0 +1,101 @@
1
+ module Ably::Models
2
+ # Convert token details argument to a {TokenDetails} object
3
+ #
4
+ # @param attributes [TokenDetails,Hash] A {TokenDetails} object or Hash of token and meta data attributes
5
+ # @option attributes (see TokenDetails#initialize)
6
+ #
7
+ # @return [TokenDetails]
8
+ def self.TokenDetails(attributes)
9
+ case attributes
10
+ when TokenDetails
11
+ return attributes
12
+ else
13
+ TokenDetails.new(attributes)
14
+ end
15
+ end
16
+
17
+ # TokenDetails is a class providing details of the token string and the token's associated metadata,
18
+ # constructed from the response from Ably when request in a token via the REST API.
19
+ #
20
+ # Ruby {Time} objects are supported in place of Ably ms since epoch time fields. However, if a numeric is provided
21
+ # it must always be expressed in milliseconds as the Ably API always uses milliseconds for time fields.
22
+ #
23
+ class TokenDetails
24
+ include Ably::Modules::ModelCommon
25
+
26
+ # Buffer in seconds before a token is considered unusable
27
+ # For example, if buffer is 10s, the token can no longer be used for new requests 9s before it expires
28
+ TOKEN_EXPIRY_BUFFER = 5
29
+
30
+ def initialize(attributes)
31
+ @hash_object = IdiomaticRubyWrapper(attributes.clone.freeze)
32
+ end
33
+
34
+ # @param attributes
35
+ # @option attributes [String] :token token used to authenticate requests
36
+ # @option attributes [String] :key_name API key name used to create this token
37
+ # @option attributes [Time,Integer] :issued Time the token was issued as Time or Integer in milliseconds
38
+ # @option attributes [Time,Integer] :expires Time the token expires as Time or Integer in milliseconds
39
+ # @option attributes [String] :capability JSON stringified capabilities assigned to this token
40
+ # @option attributes [String] :client_id client ID assigned to this token
41
+ #
42
+ def initialize(attributes = {})
43
+ @hash_object = IdiomaticRubyWrapper(attributes.clone)
44
+
45
+ %w(issued expires).map(&:to_sym).each do |time_attribute|
46
+ hash[time_attribute] = (hash[time_attribute].to_f * 1000).round if hash[time_attribute].kind_of?(Time)
47
+ end
48
+
49
+ hash.freeze
50
+ end
51
+
52
+ # @!attribute [r] token
53
+ # @return [String] Token used to authenticate requests
54
+ def token
55
+ hash.fetch(:token)
56
+ end
57
+
58
+ # @!attribute [r] key_name
59
+ # @return [String] API key name used to create this token. An API key is made up of an API key name and secret delimited by a +:+
60
+ def key_name
61
+ hash.fetch(:key_name)
62
+ end
63
+
64
+ # @!attribute [r] issued
65
+ # @return [Time] Time the token was issued
66
+ def issued
67
+ as_time_from_epoch(hash.fetch(:issued), granularity: :ms)
68
+ end
69
+
70
+ # @!attribute [r] expires
71
+ # @return [Time] Time the token expires
72
+ def expires
73
+ as_time_from_epoch(hash.fetch(:expires), granularity: :ms)
74
+ end
75
+
76
+ # @!attribute [r] capability
77
+ # @return [Hash] Capabilities assigned to this token
78
+ def capability
79
+ JSON.parse(hash.fetch(:capability))
80
+ end
81
+
82
+ # @!attribute [r] client_id
83
+ # @return [String] Optional client ID assigned to this token
84
+ def client_id
85
+ hash[:client_id]
86
+ end
87
+
88
+ # Returns true if token is expired or about to expire
89
+ #
90
+ # @return [Boolean]
91
+ def expired?
92
+ expires < Time.now + TOKEN_EXPIRY_BUFFER
93
+ end
94
+
95
+ # @!attribute [r] hash
96
+ # @return [Hash] Access the token details Hash object ruby'fied to use symbolized keys
97
+ def hash
98
+ @hash_object
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,108 @@
1
+ module Ably::Models
2
+ # Convert token request argument to a {TokenRequest} object
3
+ #
4
+ # @param attributes [TokenRequest,Hash] A {TokenRequest} object or Hash of attributes to create a new token request
5
+ # @option attributes (see TokenRequest#initialize)
6
+ #
7
+ # @return [TokenRequest]
8
+ def self.TokenRequest(attributes)
9
+ case attributes
10
+ when TokenRequest
11
+ return attributes
12
+ else
13
+ TokenRequest.new(attributes)
14
+ end
15
+ end
16
+
17
+
18
+ # TokenRequest is a class that stores the attributes of a token request
19
+ #
20
+ # Ruby {Time} objects are supported in place of Ably ms since epoch time fields. However, if a numeric is provided
21
+ # it must always be expressed in milliseconds as the Ably API always uses milliseconds for time fields.
22
+ #
23
+ class TokenRequest
24
+ include Ably::Modules::ModelCommon
25
+
26
+ # @param attributes
27
+ # @option attributes [Integer] :ttl requested time to live for the token in milliseconds
28
+ # @option attributes [Time,Integer] :timestamp the timestamp of this request in milliseconds or as a {Time}
29
+ # @option attributes [String] :key_name API key name of the key against which this request is made
30
+ # @option attributes [String] :capability JSON stringified capability of the token
31
+ # @option attributes [String] :client_id client ID to associate with this token
32
+ # @option attributes [String] :nonce an opaque nonce string of at least 16 characters
33
+ # @option attributes [String] :mac the Message Authentication Code for this request
34
+ #
35
+ def initialize(attributes = {})
36
+ @hash_object = IdiomaticRubyWrapper(attributes.clone)
37
+ hash[:timestamp] = (hash[:timestamp].to_f * 1000).round if hash[:timestamp].kind_of?(Time)
38
+ hash.freeze
39
+ end
40
+
41
+ # @!attribute [r] key_name
42
+ # @return [String] API key name of the key against which this request is made. An API key is made up of an API key name and secret delimited by a +:+
43
+ def key_name
44
+ hash.fetch(:key_name)
45
+ end
46
+
47
+ # @!attribute [r] ttl
48
+ # @return [Integer] requested time to live for the token in seconds. If the token request is successful,
49
+ # the TTL of the returned token will be less than or equal to this value depending on application
50
+ # settings and the attributes of the issuing key.
51
+ # TTL when sent to Ably is in milliseconds.
52
+ def ttl
53
+ hash.fetch(:ttl) / 1000
54
+ end
55
+
56
+ # @!attribute [r] capability
57
+ # @return [Hash] capability of the token. If the token request is successful,
58
+ # the capability of the returned token will be the intersection of
59
+ # this capability with the capability of the issuing key.
60
+ def capability
61
+ JSON.parse(hash.fetch(:capability))
62
+ end
63
+
64
+ # @!attribute [r] client_id
65
+ # @return [String] the client ID to associate with this token. The generated token
66
+ # may be used to authenticate as this clientId.
67
+ def client_id
68
+ hash[:client_id]
69
+ end
70
+
71
+ # @!attribute [r] timestamp
72
+ # @return [Time] the timestamp of this request.
73
+ # Timestamps, in conjunction with the nonce, are used to prevent
74
+ # token requests from being replayed.
75
+ # Timestamp when sent to Ably is in milliseconds.
76
+ def timestamp
77
+ as_time_from_epoch(hash.fetch(:timestamp), granularity: :ms)
78
+ end
79
+
80
+ # @!attribute [r] nonce
81
+ # @return [String] an opaque nonce string of at least 16 characters to ensure
82
+ # uniqueness of this request. Any subsequent request using the
83
+ # same nonce will be rejected.
84
+ def nonce
85
+ hash.fetch(:nonce)
86
+ end
87
+
88
+ # @!attribute [r] mac
89
+ # @return [String] the Message Authentication Code for this request. See the
90
+ # {https://www.ably.io/documentation Ably Authentication documentation} for more details.
91
+ def mac
92
+ hash.fetch(:mac)
93
+ end
94
+
95
+ # Requests that the token is always persisted
96
+ # @api private
97
+ #
98
+ def persisted
99
+ hash.fetch(:persisted)
100
+ end
101
+
102
+ # @!attribute [r] hash
103
+ # @return [Hash] the token request Hash object ruby'fied to use symbolized keys
104
+ def hash
105
+ @hash_object
106
+ end
107
+ end
108
+ end
@@ -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
 
@@ -18,7 +18,7 @@ module Ably::Modules
18
18
  end
19
19
 
20
20
  def user_agent
21
- "Ably Ruby client #{Ably::VERSION} (https://ably.io)"
21
+ "Ably Ruby client #{Ably::VERSION} (https://www.ably.io)"
22
22
  end
23
23
 
24
24
  def setup_outgoing_middleware(builder)
@@ -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
  #
@@ -39,10 +39,6 @@ module Ably
39
39
  # @param (see Ably::Realtime::Client#initialize)
40
40
  # @option options (see Ably::Realtime::Client#initialize)
41
41
  #
42
- # @yield (see Ably::Realtime::Client#initialize)
43
- # @yieldparam (see Ably::Realtime::Client#initialize)
44
- # @yieldreturn (see Ably::Realtime::Client#initialize)
45
- #
46
42
  # @return [Ably::Realtime::Client]
47
43
  #
48
44
  # @example
@@ -50,10 +46,10 @@ module Ably
50
46
  # client = Ably::Realtime.new('key.id:secret')
51
47
  #
52
48
  # # 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')
49
+ # client = Ably::Realtime.new(key: 'key.id:secret', client_id: 'john')
54
50
  #
55
- def self.new(options, &token_request_block)
56
- Ably::Realtime::Client.new(options, &token_request_block)
51
+ def self.new(options)
52
+ Ably::Realtime::Client.new(options)
57
53
  end
58
54
  end
59
55
  end
@@ -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
@@ -95,7 +100,9 @@ module Ably
95
100
  setup_presence
96
101
  end
97
102
 
98
- # Publish a message on the channel
103
+ # Publish a message on the channel.
104
+ #
105
+ # When publishing a message, if the channel is not attached, the channel is implicitly attached
99
106
  #
100
107
  # @param name [String] The event name of the message
101
108
  # @param data [String,ByteArray] payload for the message
@@ -122,7 +129,9 @@ module Ably
122
129
  end
123
130
  end
124
131
 
125
- # Subscribe to messages matching providing event name, or all messages if event name not provided
132
+ # Subscribe to messages matching providing event name, or all messages if event name not provided.
133
+ #
134
+ # When subscribing to messages, if the channel is not attached, the channel is implicitly attached
126
135
  #
127
136
  # @param names [String] The event name of the message to subscribe to if provided. Defaults to all events.
128
137
  # @yield [Ably::Models::Message] For each message received, the block is called
@@ -172,6 +181,8 @@ module Ably
172
181
  # presence on the channel and may also be used to obtain presence information
173
182
  # and change events for other members of the channel.
174
183
  #
184
+ # When accessing presence, if the channel is not attached, the channel is implicitly attached
185
+ #
175
186
  # @return {Ably::Realtime::Presence}
176
187
  #
177
188
  def presence
@@ -181,13 +192,24 @@ module Ably
181
192
 
182
193
  # Return the message history of the channel
183
194
  #
195
+ # If the channel is attached, you can retrieve messages published on the channel before the
196
+ # channel was attached with the option <tt>until_attach: true</tt>. This is useful when a developer
197
+ # wishes to display historical messages with the guarantee that no messages have been missed since attach.
198
+ #
184
199
  # @param (see Ably::Rest::Channel#history)
185
200
  # @option options (see Ably::Rest::Channel#history)
201
+ # @option options [Boolean] :until_attach When true, the history request will be limited only to messages published before this channel was attached. Channel must be attached
186
202
  #
187
- # @yield [Ably::Models::PaginatedResource<Ably::Models::Message>] An Array of {Ably::Models::Message} objects that supports paging (#next_page, #first_page)
203
+ # @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
204
  #
189
205
  # @return [Ably::Util::SafeDeferrable]
206
+ #
190
207
  def history(options = {}, &callback)
208
+ if options.delete(:until_attach)
209
+ raise ArgumentError, 'option :until_attach is invalid as the channel is not attached' unless attached?
210
+ options[:from_serial] = attached_serial
211
+ end
212
+
191
213
  async_wrap(callback) do
192
214
  rest_channel.history(options.merge(async_blocking_operations: true))
193
215
  end
@@ -212,6 +234,11 @@ module Ably
212
234
  @error_reason = nil
213
235
  end
214
236
 
237
+ # @api private
238
+ def set_attached_serial(serial)
239
+ @attached_serial = serial
240
+ end
241
+
215
242
  # Used by {Ably::Modules::StateEmitter} to debug state changes
216
243
  # @api private
217
244
  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
@@ -64,10 +64,6 @@ module Ably
64
64
  # @option options [String] :recover When a recover option is specified a connection inherits the state of a previous connection that may have existed under a different instance of the Realtime library, please refer to the API documentation for further information on connection state recovery
65
65
  # @option options [Boolean] :connect_automatically By default as soon as the client library is instantiated it will connect to Ably. You can optionally set this to false and explicitly connect.
66
66
  #
67
- # @yield (see Ably::Rest::Client#initialize)
68
- # @yieldparam (see Ably::Rest::Client#initialize)
69
- # @yieldreturn (see Ably::Rest::Client#initialize)
70
- #
71
67
  # @return [Ably::Realtime::Client]
72
68
  #
73
69
  # @example
@@ -75,10 +71,10 @@ module Ably
75
71
  # client = Ably::Realtime::Client.new('key.id:secret')
76
72
  #
77
73
  # # 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')
74
+ # client = Ably::Realtime::Client.new(key: 'key.id:secret', client_id: 'john')
79
75
  #
80
- def initialize(options, &token_request_block)
81
- @rest_client = Ably::Rest::Client.new(options, &token_request_block)
76
+ def initialize(options)
77
+ @rest_client = Ably::Rest::Client.new(options)
82
78
  @auth = @rest_client.auth
83
79
  @channels = Ably::Realtime::Channels.new(self)
84
80
  @echo_messages = @rest_client.options.fetch(:echo_messages, true) == false ? false : true
@@ -114,7 +110,7 @@ module Ably
114
110
  # @param (see Ably::Rest::Client#stats)
115
111
  # @option options (see Ably::Rest::Client#stats)
116
112
  #
117
- # @yield [Ably::Models::PaginatedResource<Ably::Models::Stat>] An Array of Stats
113
+ # @yield [Ably::Models::PaginatedResource<Ably::Models::Stats>] An Array of Stats
118
114
  #
119
115
  # @return [Ably::Util::SafeDeferrable]
120
116
  #
@@ -10,8 +10,9 @@ module Ably
10
10
  # connected: 2
11
11
  # disconnected: 3
12
12
  # suspended: 4
13
- # closed: 5
14
- # failed: 6
13
+ # closing: 5
14
+ # closed: 6
15
+ # failed: 7
15
16
  #
16
17
  # Note that the states are available as Enum-like constants:
17
18
  #
@@ -20,6 +21,7 @@ module Ably
20
21
  # Connection::STATE.Connected
21
22
  # Connection::STATE.Disconnected
22
23
  # Connection::STATE.Suspended
24
+ # Connection::STATE.Closing
23
25
  # Connection::STATE.Closed
24
26
  # Connection::STATE.Failed
25
27
  #
@@ -150,7 +152,7 @@ module Ably
150
152
  # @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
153
  #
152
154
  # @example
153
- # client = Ably::Rest::Client.new(api_key: 'key.id:secret')
155
+ # client = Ably::Rest::Client.new(key: 'key.id:secret')
154
156
  # client.connection.ping do |ms_elapsed|
155
157
  # puts "Ping took #{ms_elapsed}ms"
156
158
  # 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