ably 0.1.5 → 0.1.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 (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