ably 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -1
  3. data/ably.gemspec +4 -3
  4. data/lib/ably.rb +6 -2
  5. data/lib/ably/auth.rb +24 -16
  6. data/lib/ably/exceptions.rb +16 -5
  7. data/lib/ably/{realtime/models → models}/error_info.rb +9 -11
  8. data/lib/ably/models/idiomatic_ruby_wrapper.rb +57 -26
  9. data/lib/ably/{realtime/models → models}/message.rb +45 -38
  10. data/lib/ably/{realtime/models → models}/nil_channel.rb +4 -4
  11. data/lib/ably/{rest/models/paged_resource.rb → models/paginated_resource.rb} +21 -10
  12. data/lib/ably/models/presence_message.rb +126 -0
  13. data/lib/ably/{realtime/models → models}/protocol_message.rb +76 -38
  14. data/lib/ably/models/token.rb +74 -0
  15. data/lib/ably/modules/channels_collection.rb +49 -0
  16. data/lib/ably/modules/conversions.rb +2 -0
  17. data/lib/ably/modules/event_emitter.rb +43 -8
  18. data/lib/ably/modules/event_machine_helpers.rb +1 -0
  19. data/lib/ably/modules/http_helpers.rb +9 -2
  20. data/lib/ably/modules/message_pack.rb +14 -0
  21. data/lib/ably/modules/model_common.rb +29 -0
  22. data/lib/ably/modules/{state.rb → state_emitter.rb} +8 -7
  23. data/lib/ably/realtime.rb +37 -7
  24. data/lib/ably/realtime/channel.rb +154 -31
  25. data/lib/ably/realtime/channels.rb +47 -0
  26. data/lib/ably/realtime/client.rb +39 -33
  27. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +50 -21
  28. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +9 -11
  29. data/lib/ably/realtime/connection.rb +148 -79
  30. data/lib/ably/realtime/connection/connection_state_machine.rb +111 -0
  31. data/lib/ably/realtime/connection/websocket_transport.rb +161 -0
  32. data/lib/ably/realtime/presence.rb +270 -0
  33. data/lib/ably/rest.rb +14 -3
  34. data/lib/ably/rest/channel.rb +3 -3
  35. data/lib/ably/rest/channels.rb +26 -12
  36. data/lib/ably/rest/client.rb +42 -25
  37. data/lib/ably/rest/middleware/exceptions.rb +21 -23
  38. data/lib/ably/rest/middleware/external_exceptions.rb +8 -10
  39. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
  40. data/lib/ably/rest/middleware/parse_json.rb +9 -2
  41. data/lib/ably/rest/middleware/parse_message_pack.rb +6 -2
  42. data/lib/ably/rest/presence.rb +4 -4
  43. data/lib/ably/version.rb +1 -1
  44. data/spec/acceptance/realtime/channel_history_spec.rb +125 -0
  45. data/spec/acceptance/realtime/channel_spec.rb +135 -63
  46. data/spec/acceptance/realtime/connection_spec.rb +86 -0
  47. data/spec/acceptance/realtime/message_spec.rb +116 -94
  48. data/spec/acceptance/realtime/presence_history_spec.rb +0 -0
  49. data/spec/acceptance/realtime/presence_spec.rb +277 -0
  50. data/spec/acceptance/rest/auth_spec.rb +351 -347
  51. data/spec/acceptance/rest/base_spec.rb +43 -26
  52. data/spec/acceptance/rest/channel_spec.rb +88 -83
  53. data/spec/acceptance/rest/channels_spec.rb +32 -28
  54. data/spec/acceptance/rest/presence_spec.rb +83 -63
  55. data/spec/acceptance/rest/stats_spec.rb +38 -37
  56. data/spec/acceptance/rest/time_spec.rb +10 -6
  57. data/spec/integration/modules/{state_spec.rb → state_emitter_spec.rb} +16 -2
  58. data/spec/spec_helper.rb +14 -0
  59. data/spec/support/api_helper.rb +4 -0
  60. data/spec/support/model_helper.rb +28 -9
  61. data/spec/support/protocol_msgbus_helper.rb +8 -1
  62. data/spec/support/test_app.rb +24 -14
  63. data/spec/unit/{realtime → models}/error_info_spec.rb +4 -4
  64. data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +46 -9
  65. data/spec/unit/models/message_spec.rb +229 -0
  66. data/spec/unit/{rest/paged_resource_spec.rb → models/paginated_resource_spec.rb} +19 -11
  67. data/spec/unit/models/presence_message_spec.rb +230 -0
  68. data/spec/unit/models/protocol_message_spec.rb +280 -0
  69. data/spec/unit/{token_spec.rb → models/token_spec.rb} +18 -22
  70. data/spec/unit/modules/conversions_spec.rb +1 -1
  71. data/spec/unit/modules/event_emitter_spec.rb +36 -4
  72. data/spec/unit/realtime/channel_spec.rb +76 -2
  73. data/spec/unit/realtime/channels_spec.rb +50 -0
  74. data/spec/unit/realtime/client_spec.rb +31 -1
  75. data/spec/unit/realtime/connection_spec.rb +8 -15
  76. data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +6 -6
  77. data/spec/unit/realtime/presence_spec.rb +100 -0
  78. data/spec/unit/rest/channels_spec.rb +48 -0
  79. metadata +72 -38
  80. data/lib/ably/realtime/models/shared.rb +0 -17
  81. data/lib/ably/rest/models/message.rb +0 -64
  82. data/lib/ably/rest/models/presence_message.rb +0 -21
  83. data/lib/ably/token.rb +0 -80
  84. data/spec/unit/realtime/message_spec.rb +0 -117
  85. data/spec/unit/realtime/protocol_message_spec.rb +0 -172
  86. data/spec/unit/rest/message_spec.rb +0 -75
data/lib/ably/rest.rb CHANGED
@@ -1,12 +1,20 @@
1
1
  require "ably/rest/channel"
2
2
  require "ably/rest/channels"
3
3
  require "ably/rest/client"
4
- require "ably/rest/models/message"
5
- require "ably/rest/models/paged_resource"
6
- require "ably/rest/models/presence_message"
7
4
  require "ably/rest/presence"
8
5
 
6
+ Dir.glob(File.expand_path("ably/models/*.rb", File.dirname(__FILE__))).each do |file|
7
+ require file
8
+ end
9
+
9
10
  module Ably
11
+ # Rest provides the top-level class to be instanced for the Ably Rest library
12
+ #
13
+ # @example
14
+ # client = Ably::Rest.new("xxxxx")
15
+ # channel = client.channel("test")
16
+ # channel.publish "greeting", "data"
17
+ #
10
18
  module Rest
11
19
  # Convenience method providing an alias to {Ably::Rest::Client} constructor.
12
20
  #
@@ -23,6 +31,9 @@ module Ably
23
31
  # # create a new client authenticating with basic auth
24
32
  # client = Ably::Rest.new('key.id:secret')
25
33
  #
34
+ # # create a new client authenticating with basic auth and a client_id
35
+ # client = Ably::Rest.new(api_key: 'key.id:secret', client_id: 'john')
36
+ #
26
37
  def self.new(options, &auth_block)
27
38
  Ably::Rest::Client.new(options, &auth_block)
28
39
  end
@@ -31,7 +31,7 @@ module Ably
31
31
 
32
32
  response = client.post("#{base_path}/publish", payload)
33
33
 
34
- response.status == 201
34
+ [201, 204].include?(response.status)
35
35
  end
36
36
 
37
37
  # Return the message history of the channel
@@ -43,7 +43,7 @@ module Ably
43
43
  # @option options [Integer] :limit Maximum number of messages to retrieve up to 10,000
44
44
  # @option options [Symbol] :by `:message`, `:bundle` or `:hour`. Defaults to `:message`
45
45
  #
46
- # @return [Models::PagedResource<Models::Message>] An Array of hashes representing the message history that supports paging (next, first)
46
+ # @return [Ably::Models::PaginatedResource<Ably::Models::Message>] An Array of hashes representing the message history that supports paging (next, first)
47
47
  def history(options = {})
48
48
  url = "#{base_path}/messages"
49
49
 
@@ -52,7 +52,7 @@ module Ably
52
52
 
53
53
  response = client.get(url, options.merge(merge_options))
54
54
 
55
- Models::PagedResource.new(response, url, client, coerce_into: 'Ably::Rest::Models::Message')
55
+ Ably::Models::PaginatedResource.new(response, url, client, coerce_into: 'Ably::Models::Message')
56
56
  end
57
57
 
58
58
  def presence
@@ -1,29 +1,43 @@
1
1
  module Ably
2
2
  module Rest
3
3
  class Channels
4
- attr_reader :client
4
+ include Ably::Modules::ChannelsCollection
5
5
 
6
- # Initialize a new Channels object
7
- #
8
- # {Ably::Rest::Channels} provides simple accessor methods to access a {Ably::Rest::Channel} object
6
+ # @return [Ably::Rest::Channels]
9
7
  def initialize(client)
10
- @client = client
11
- @channels = {}
8
+ super client, Ably::Rest::Channel
12
9
  end
13
10
 
14
- # Return a REST {Ably::Rest::Channel} for the given name
11
+ # @!method get(name, channel_options = {})
12
+ # Return a {Ably::Rest::Channel} for the given name
15
13
  #
16
14
  # @param name [String] The name of the channel
17
15
  # @param channel_options [Hash] Channel options, currently reserved for Encryption options
16
+ # @return [Ably::Rest::Channel}
17
+ def get(*args)
18
+ super
19
+ end
20
+
21
+ # @!method fetch(name, &missing_block)
22
+ # Return a {Ably::Rest::Channel} for the given name if it exists, else the block will be called.
23
+ # This method is intentionally similar to {http://ruby-doc.org/core-2.1.3/Hash.html#method-i-fetch Hash#fetch} providing a simple way to check if a channel exists or not without creating one
18
24
  #
25
+ # @param name [String] The name of the channel
26
+ # @yield [options] (optional) if a missing_block is passed to this method and no channel exists matching the name, this block is called
27
+ # @yieldparam [String] name of the missing channel
19
28
  # @return [Ably::Rest::Channel]
20
- def get(name, channel_options = {})
21
- @channels[name] ||= Ably::Rest::Channel.new(client, name, channel_options)
29
+ def fetch(*args)
30
+ super
22
31
  end
23
- alias_method :[], :get
24
32
 
25
- def close(channel)
26
- @channels.delete(channel)
33
+ # Destroy the {Ably::Rest::Channel} and releases the associated resources.
34
+ #
35
+ # Releasing a {Ably::Rest::Channel} is not typically necessary as a channel consumes no resources other than the memory footprint of the
36
+ # {Ably::Rest::Channel} object. Explicitly release channels to free up resources if required
37
+ #
38
+ # @return [void]
39
+ def release(*args)
40
+ super
27
41
  end
28
42
  end
29
43
  end
@@ -14,10 +14,12 @@ module Ably
14
14
  # @return [String] A client ID, used for identifying this client for presence purposes
15
15
  # @!attribute [r] auth_options
16
16
  # @return [Hash] {Ably::Auth} options configured for this client
17
- # @!attribute [r] tls
18
- # @return [Boolean] True if client is configured to use TLS for all Ably communication
19
17
  # @!attribute [r] environment
20
18
  # @return [String] May contain 'sandbox' when testing the client library against an alternate Ably environment
19
+ # @!attribute [r] log_level
20
+ # @return [Logger::Severity] Log level configured for this {Client}
21
+ # @!attribute [r] channels
22
+ # @return [Aby::Rest::Channels] The collection of {Ably::Rest::Channel}s that have been created
21
23
  class Client
22
24
  include Ably::Modules::Conversions
23
25
  include Ably::Modules::HttpHelpers
@@ -25,18 +27,24 @@ module Ably
25
27
 
26
28
  DOMAIN = "rest.ably.io"
27
29
 
28
- attr_reader :tls, :environment, :protocol, :auth, :channels, :log_level
30
+ attr_reader :environment, :protocol, :auth, :channels, :log_level
29
31
  def_delegators :auth, :client_id, :auth_options
30
32
 
33
+ # The additional options passed to this Client's #initialize method not available as attributes of this class
34
+ # @return [Hash]
35
+ # @api private
36
+ attr_reader :options
37
+
31
38
  # Creates a {Ably::Rest::Client Rest Client} and configures the {Ably::Auth} object for the connection.
32
39
  #
33
40
  # @param [Hash,String] options an options Hash used to configure the client and the authentication, or String with an API key
34
41
  # @option options (see Ably::Auth#authorise)
35
- # @option options [Boolean] :tls TLS is used by default, providing a value of false disbles TLS. Please note Basic Auth is disallowed without TLS as secrets cannot be transmitted over unsecured connections.
36
- # @option options [String] :api_key API key comprising the key ID and key secret in a single string
37
- # @option options [String] :environment Specify 'sandbox' when testing the client library against an alternate Ably environment
38
- # @option options [Symbol] :protocol Protocol used to communicate with Ably, :json and :msgpack currently supported. Defaults to :msgpack.
39
- # @option options [Logger::Severity] :log_level Log level for the standard Logger that outputs to STDOUT. Defaults to Logger::WARN, can be set to Logger::FATAL, Logger::ERROR, Logger::WARN, Logger::INFO, Logger::DEBUG
42
+ # @option options [Boolean] :tls TLS is used by default, providing a value of false disbles TLS. Please note Basic Auth is disallowed without TLS as secrets cannot be transmitted over unsecured connections.
43
+ # @option options [String] :api_key API key comprising the key ID and key secret in a single string
44
+ # @option options [String] :environment Specify 'sandbox' when testing the client library against an alternate Ably environment
45
+ # @option options [Symbol] :protocol Protocol used to communicate with Ably, :json and :msgpack currently supported. Defaults to :msgpack
46
+ # @option options [Boolean] :use_binary_protocol Protocol used to communicate with Ably, defaults to true and uses MessagePack protocol. This option will overide :protocol option
47
+ # @option options [Logger::Severity,Symbol] :log_level Log level for the standard Logger that outputs to STDOUT. Defaults to Logger::ERROR, can be set to :fatal (Logger::FATAL), :error (Logger::ERROR), :warn (Logger::WARN), :info (Logger::INFO), :debug (Logger::DEBUG)
40
48
  #
41
49
  # @yield (see Ably::Auth#authorise)
42
50
  # @yieldparam (see Ably::Auth#authorise)
@@ -53,19 +61,28 @@ module Ably
53
61
  #
54
62
  def initialize(options, &auth_block)
55
63
  options = options.clone
56
-
57
64
  if options.kind_of?(String)
58
65
  options = { api_key: options }
59
66
  end
60
67
 
61
68
  @tls = options.delete(:tls) == false ? false : true
62
69
  @environment = options.delete(:environment) # nil is production
63
- @protocol = options.delete(:protocol) || :json # TODO: Default to :msgpack when protocol MsgPack support added
70
+ @protocol = options.delete(:protocol) || :msgpack
64
71
  @debug_http = options.delete(:debug_http)
65
- @log_level = options.delete(:log_level) || Logger::WARN
72
+ @log_level = options.delete(:log_level) || Logger::ERROR
66
73
 
74
+ @log_level = Logger.const_get(log_level.to_s.upcase) if log_level.kind_of?(Symbol) || log_level.kind_of?(String)
75
+
76
+ options.delete(:use_binary_protocol).tap do |use_binary_protocol|
77
+ if use_binary_protocol == true
78
+ @protocol = :msgpack
79
+ elsif use_binary_protocol == false
80
+ @protocol = :json
81
+ end
82
+ end
67
83
  raise ArgumentError, 'Protocol is invalid. Must be either :msgpack or :json' unless [:msgpack, :json].include?(@protocol)
68
84
 
85
+ @options = options.freeze
69
86
  @auth = Auth.new(self, options, &auth_block)
70
87
  @channels = Ably::Rest::Channels.new(self)
71
88
  end
@@ -79,7 +96,7 @@ module Ably
79
96
  channels.get(name, channel_options)
80
97
  end
81
98
 
82
- # Return the stats for the application
99
+ # Retrieve the stats for the application
83
100
  #
84
101
  # @return [Array] An Array of hashes representing the stats
85
102
  def stats(params = {})
@@ -95,7 +112,7 @@ module Ably
95
112
  end
96
113
  end
97
114
 
98
- # Return the Ably service time
115
+ # Retrieve the Ably service time
99
116
  #
100
117
  # @return [Time] The time as reported by the Ably service
101
118
  def time
@@ -104,9 +121,8 @@ module Ably
104
121
  as_time_from_epoch(response.body.first)
105
122
  end
106
123
 
107
- # True if client is configured to use TLS for all Ably communication
108
- #
109
- # @return [Boolean]
124
+ # @!attribute [r] use_tls?
125
+ # @return [Boolean] True if client is configured to use TLS for all Ably communication
110
126
  def use_tls?
111
127
  @tls == true
112
128
  end
@@ -125,9 +141,8 @@ module Ably
125
141
  request(:post, path, params, options)
126
142
  end
127
143
 
128
- # Default Ably REST endpoint used for all requests
129
- #
130
- # @return [URI::Generic]
144
+ # @!attribute [r] endpoint
145
+ # @return [URI::Generic] Default Ably REST endpoint used for all requests
131
146
  def endpoint
132
147
  URI::Generic.build(
133
148
  scheme: use_tls? ? "https" : "http",
@@ -135,15 +150,17 @@ module Ably
135
150
  )
136
151
  end
137
152
 
153
+ # @!attribute [r] logger
154
+ # @return [Logger] The Logger configured for this client when the client was instantiated.
155
+ # Configure the log_level with the `:log_level` option, refer to {Client#initialize}
138
156
  def logger
139
157
  @logger ||= Logger.new(STDOUT).tap do |logger|
140
158
  logger.level = log_level
141
159
  end
142
160
  end
143
161
 
144
- # Mime type used for HTTP requests
145
- #
146
- # @return [String]
162
+ # @!attribute [r] mime_type
163
+ # @return [String] Mime type used for HTTP requests
147
164
  def mime_type
148
165
  case protocol
149
166
  when :json
@@ -174,7 +191,7 @@ module Ably
174
191
  auth.authorise force: true
175
192
  retry
176
193
  else
177
- raise Ably::Exceptions::InvalidToken.new(e.message, status: e.status, code: e.code)
194
+ raise Ably::Exceptions::InvalidToken.new(e.message, e.status, e.code)
178
195
  end
179
196
  end
180
197
  end
@@ -208,16 +225,16 @@ module Ably
208
225
  # @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
209
226
  def middleware
210
227
  @middleware ||= Faraday::RackBuilder.new do |builder|
211
- setup_middleware builder
228
+ setup_outgoing_middleware builder
212
229
 
213
230
  # Raise exceptions if response code is invalid
214
231
  builder.use Ably::Rest::Middleware::Exceptions
215
232
 
233
+ setup_incoming_middleware builder, fail_if_unsupported_mime_type: true
216
234
 
217
235
  # Log HTTP requests if log level is DEBUG option set
218
236
  builder.response :logger if log_level == Logger::DEBUG
219
237
 
220
-
221
238
  # Set Faraday's HTTP adapter
222
239
  builder.adapter Faraday.default_adapter
223
240
  end
@@ -4,35 +4,33 @@ module Ably
4
4
  module Rest
5
5
  module Middleware
6
6
  # HTTP exceptions raised by Ably due to an error status code
7
- # Ably returns JSON error codes and messages so include this if possible in the exception messages
7
+ # Ably returns JSON/Msgpack error codes and messages so include this if possible in the exception messages
8
8
  class Exceptions < Faraday::Response::Middleware
9
- def call(env)
10
- @app.call(env).on_complete do
11
- if env[:status] >= 400
12
- error_status_code = env[:status]
13
- error_code = nil
9
+ def on_complete(env)
10
+ if env.status >= 400
11
+ error_status_code = env.status
12
+ error_code = nil
14
13
 
15
- begin
16
- error = JSON.parse(env[:body])['error']
17
- error_status_code = error['statusCode'].to_i if error['statusCode']
18
- error_code = error['code'].to_i if error['code']
14
+ if env.body.kind_of?(Hash)
15
+ error = env.body.fetch('error', {})
16
+ error_status_code = error['statusCode'].to_i if error['statusCode']
17
+ error_code = error['code'].to_i if error['code']
19
18
 
20
- if error
21
- message = "#{error['message']} (status: #{error_status_code}, code: #{error_code})"
22
- else
23
- message = env[:body]
24
- end
25
- rescue JSON::ParserError
26
- message = env[:body]
19
+ if error
20
+ message = "#{error['message']} (status: #{error_status_code}, code: #{error_code})"
21
+ else
22
+ message = env.body
27
23
  end
24
+ else
25
+ message = env.body
26
+ end
28
27
 
29
- message = "Unknown server error" if message.to_s.strip == ''
28
+ message = "Unknown server error" if message.to_s.strip == ''
30
29
 
31
- if env[:status] >= 500
32
- raise Ably::Exceptions::ServerError, message
33
- else
34
- raise Ably::Exceptions::InvalidRequest.new(message, status: error_status_code, code: error_code)
35
- end
30
+ if env.status >= 500
31
+ raise Ably::Exceptions::ServerError, message
32
+ else
33
+ raise Ably::Exceptions::InvalidRequest.new(message, error_status_code, error_code)
36
34
  end
37
35
  end
38
36
  end
@@ -6,17 +6,15 @@ module Ably
6
6
  # HTTP exceptions raised due to a status code error on a 3rd party site
7
7
  # Used by auth calls
8
8
  class ExternalExceptions < Faraday::Response::Middleware
9
- def call(env)
10
- @app.call(env).on_complete do
11
- if env[:status] >= 400
12
- error_status_code = env[:status]
13
- message = "Error #{error_status_code}: #{(env[:body] || '')[0...200]}"
9
+ def on_complete(env)
10
+ if env.status >= 400
11
+ error_status_code = env.status
12
+ message = "Error #{error_status_code}: #{(env.body || '')[0...200]}"
14
13
 
15
- if error_status_code >= 500
16
- raise Ably::Exceptions::ServerError, message
17
- else
18
- raise Ably::Exceptions::InvalidRequest, message
19
- end
14
+ if error_status_code >= 500
15
+ raise Ably::Exceptions::ServerError, message
16
+ else
17
+ raise Ably::Exceptions::InvalidRequest, message
20
18
  end
21
19
  end
22
20
  end
@@ -0,0 +1,17 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Ably
5
+ module Rest
6
+ module Middleware
7
+ class FailIfUnsupportedMimeType < Faraday::Response::Middleware
8
+ def on_complete(env)
9
+ unless env.response_headers['Ably-Middleware-Parsed'] == true
10
+ raise Ably::Exceptions::InvalidResponseBody,
11
+ "Content Type #{env.response_headers['Content-Type']} is not supported by this client library"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -6,11 +6,18 @@ module Ably
6
6
  module Middleware
7
7
  class ParseJson < Faraday::Response::Middleware
8
8
  def on_complete(env)
9
- env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
9
+ if env.response_headers['Content-Type'] == 'application/json'
10
+ env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
11
+ env.response_headers['Ably-Middleware-Parsed'] = true
12
+ end
10
13
  end
11
14
 
12
15
  def parse(body)
13
- JSON.parse(body)
16
+ if body.length > 0
17
+ JSON.parse(body)
18
+ else
19
+ body
20
+ end
14
21
  rescue JSON::ParserError => e
15
22
  raise Ably::Exceptions::InvalidResponseBody, "Expected JSON response: #{e.message}"
16
23
  end
@@ -7,13 +7,17 @@ module Ably
7
7
  class ParseMessagePack < Faraday::Response::Middleware
8
8
  def on_complete(env)
9
9
  if env.response_headers['Content-Type'] == 'application/x-msgpack'
10
- env.body = parse(env.body)
10
+ env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
11
11
  env.response_headers['Ably-Middleware-Parsed'] = true
12
12
  end
13
13
  end
14
14
 
15
15
  def parse(body)
16
- MessagePack.unpack(body)
16
+ if body.length > 0
17
+ MessagePack.unpack(body)
18
+ else
19
+ body
20
+ end
17
21
  rescue MessagePack::MalformedFormatError => e
18
22
  raise Ably::Exceptions::InvalidResponseBody, "Expected MessagePack response: #{e.message}"
19
23
  end
@@ -16,11 +16,11 @@ module Ably
16
16
 
17
17
  # Obtain the set of members currently present for a channel
18
18
  #
19
- # @return [Models::PagedResource<Models::PresenceMessage>] An Array of presence-message Hash objects that supports paging (next, first)
19
+ # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of presence-message Hash objects that supports paging (next, first)
20
20
  #
21
21
  def get(options = {})
22
22
  response = client.get(base_path, options)
23
- Models::PagedResource.new(response, base_path, client, coerce_into: 'Ably::Rest::Models::PresenceMessage')
23
+ Ably::Models::PaginatedResource.new(response, base_path, client, coerce_into: 'Ably::Models::PresenceMessage')
24
24
  end
25
25
 
26
26
  # Return the presence messages history for the channel
@@ -31,7 +31,7 @@ module Ably
31
31
  # @option options [Symbol] :direction `:forwards` or `:backwards`
32
32
  # @option options [Integer] :limit Maximum number of presence messages to retrieve up to 10,000
33
33
  #
34
- # @return [Models::PagedResource<Models::PresenceMessage>] An Array of presence-message Hash objects that supports paging (next, first)
34
+ # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of presence-message Hash objects that supports paging (next, first)
35
35
  #
36
36
  def history(options = {})
37
37
  url = "#{base_path}/history"
@@ -41,7 +41,7 @@ module Ably
41
41
 
42
42
  response = client.get(url, options.merge(merge_options))
43
43
 
44
- Models::PagedResource.new(response, url, client, coerce_into: 'Ably::Rest::Models::PresenceMessage')
44
+ Ably::Models::PaginatedResource.new(response, url, client, coerce_into: 'Ably::Models::PresenceMessage')
45
45
  end
46
46
 
47
47
  private