ably-rest 0.7.5 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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