ably 1.0.7 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +14 -0
  3. data/.travis.yml +4 -4
  4. data/CHANGELOG.md +26 -3
  5. data/Rakefile +32 -0
  6. data/SPEC.md +920 -565
  7. data/ably.gemspec +9 -4
  8. data/lib/ably/auth.rb +28 -2
  9. data/lib/ably/exceptions.rb +8 -2
  10. data/lib/ably/models/channel_state_change.rb +1 -1
  11. data/lib/ably/models/connection_state_change.rb +1 -1
  12. data/lib/ably/models/device_details.rb +87 -0
  13. data/lib/ably/models/device_push_details.rb +86 -0
  14. data/lib/ably/models/error_info.rb +23 -2
  15. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -4
  16. data/lib/ably/models/protocol_message.rb +32 -2
  17. data/lib/ably/models/push_channel_subscription.rb +89 -0
  18. data/lib/ably/modules/conversions.rb +1 -1
  19. data/lib/ably/modules/encodeable.rb +1 -1
  20. data/lib/ably/modules/exception_codes.rb +128 -0
  21. data/lib/ably/modules/model_common.rb +15 -2
  22. data/lib/ably/modules/state_machine.rb +1 -1
  23. data/lib/ably/realtime.rb +1 -0
  24. data/lib/ably/realtime/auth.rb +1 -1
  25. data/lib/ably/realtime/channel.rb +24 -102
  26. data/lib/ably/realtime/channel/channel_manager.rb +2 -6
  27. data/lib/ably/realtime/channel/channel_state_machine.rb +2 -2
  28. data/lib/ably/realtime/channel/publisher.rb +74 -0
  29. data/lib/ably/realtime/channel/push_channel.rb +62 -0
  30. data/lib/ably/realtime/client.rb +87 -0
  31. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +6 -2
  32. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
  33. data/lib/ably/realtime/connection.rb +8 -5
  34. data/lib/ably/realtime/connection/connection_manager.rb +7 -7
  35. data/lib/ably/realtime/connection/websocket_transport.rb +1 -1
  36. data/lib/ably/realtime/presence.rb +4 -4
  37. data/lib/ably/realtime/presence/members_map.rb +3 -3
  38. data/lib/ably/realtime/push.rb +40 -0
  39. data/lib/ably/realtime/push/admin.rb +61 -0
  40. data/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
  41. data/lib/ably/realtime/push/device_registrations.rb +105 -0
  42. data/lib/ably/rest.rb +1 -0
  43. data/lib/ably/rest/channel.rb +33 -5
  44. data/lib/ably/rest/channel/push_channel.rb +62 -0
  45. data/lib/ably/rest/client.rb +137 -28
  46. data/lib/ably/rest/middleware/parse_message_pack.rb +17 -1
  47. data/lib/ably/rest/presence.rb +1 -0
  48. data/lib/ably/rest/push.rb +42 -0
  49. data/lib/ably/rest/push/admin.rb +54 -0
  50. data/lib/ably/rest/push/channel_subscriptions.rb +121 -0
  51. data/lib/ably/rest/push/device_registrations.rb +103 -0
  52. data/lib/ably/version.rb +7 -2
  53. data/spec/acceptance/realtime/auth_spec.rb +6 -8
  54. data/spec/acceptance/realtime/channel_spec.rb +166 -51
  55. data/spec/acceptance/realtime/client_spec.rb +149 -0
  56. data/spec/acceptance/realtime/connection_failures_spec.rb +1 -1
  57. data/spec/acceptance/realtime/connection_spec.rb +4 -4
  58. data/spec/acceptance/realtime/message_spec.rb +19 -17
  59. data/spec/acceptance/realtime/presence_spec.rb +5 -5
  60. data/spec/acceptance/realtime/push_admin_spec.rb +696 -0
  61. data/spec/acceptance/realtime/push_spec.rb +27 -0
  62. data/spec/acceptance/rest/auth_spec.rb +4 -3
  63. data/spec/acceptance/rest/base_spec.rb +2 -2
  64. data/spec/acceptance/rest/client_spec.rb +129 -10
  65. data/spec/acceptance/rest/message_spec.rb +175 -4
  66. data/spec/acceptance/rest/push_admin_spec.rb +896 -0
  67. data/spec/acceptance/rest/push_spec.rb +25 -0
  68. data/spec/acceptance/rest/time_spec.rb +1 -1
  69. data/spec/run_parallel_tests +33 -0
  70. data/spec/unit/logger_spec.rb +10 -3
  71. data/spec/unit/models/device_details_spec.rb +102 -0
  72. data/spec/unit/models/device_push_details_spec.rb +101 -0
  73. data/spec/unit/models/error_info_spec.rb +51 -3
  74. data/spec/unit/models/message_spec.rb +17 -2
  75. data/spec/unit/models/presence_message_spec.rb +1 -1
  76. data/spec/unit/models/push_channel_subscription_spec.rb +86 -0
  77. data/spec/unit/realtime/client_spec.rb +12 -0
  78. data/spec/unit/realtime/push_channel_spec.rb +36 -0
  79. data/spec/unit/rest/channel_spec.rb +8 -1
  80. data/spec/unit/rest/client_spec.rb +30 -0
  81. data/spec/unit/rest/push_channel_spec.rb +36 -0
  82. metadata +71 -8
@@ -0,0 +1,74 @@
1
+ module Ably::Realtime
2
+ class Channel
3
+ # Publisher module adds publishing capabilities to the current object
4
+ module Publisher
5
+ private
6
+
7
+ # Prepare and queue messages on the connection queue immediately
8
+ # @return [Ably::Util::SafeDeferrable]
9
+ def enqueue_messages_on_connection(client, raw_messages, channel_name, channel_options = {})
10
+ messages = Array(raw_messages).map do |raw_msg|
11
+ create_message(client, raw_msg, channel_options).tap do |message|
12
+ next if message.client_id.nil?
13
+ if message.client_id == '*'
14
+ raise Ably::Exceptions::IncompatibleClientId.new('Wildcard client_id is reserved and cannot be used when publishing messages')
15
+ end
16
+ if message.client_id && !message.client_id.kind_of?(String)
17
+ raise Ably::Exceptions::IncompatibleClientId.new('client_id must be a String when publishing messages')
18
+ end
19
+ unless client.auth.can_assume_client_id?(message.client_id)
20
+ raise Ably::Exceptions::IncompatibleClientId.new("Cannot publish with client_id '#{message.client_id}' as it is incompatible with the current configured client_id '#{client.client_id}'")
21
+ end
22
+ end
23
+ end
24
+
25
+ connection.send_protocol_message(
26
+ action: Ably::Models::ProtocolMessage::ACTION.Message.to_i,
27
+ channel: channel_name,
28
+ messages: messages
29
+ )
30
+
31
+ if messages.count == 1
32
+ # A message is a Deferrable so, if publishing only one message, simply return that Deferrable
33
+ messages.first
34
+ else
35
+ deferrable_for_multiple_messages(messages)
36
+ end
37
+ end
38
+
39
+ # A deferrable object that calls the success callback once all messages are delivered
40
+ # If any message fails, the errback is called immediately
41
+ # Only one callback or errback is ever called i.e. if a group of messages all fail, only once
42
+ # errback will be invoked
43
+ def deferrable_for_multiple_messages(messages)
44
+ expected_deliveries = messages.count
45
+ actual_deliveries = 0
46
+ failed = false
47
+
48
+ Ably::Util::SafeDeferrable.new(logger).tap do |deferrable|
49
+ messages.each do |message|
50
+ message.callback do
51
+ next if failed
52
+ actual_deliveries += 1
53
+ deferrable.succeed messages if actual_deliveries == expected_deliveries
54
+ end
55
+ message.errback do |error|
56
+ next if failed
57
+ failed = true
58
+ deferrable.fail error, message
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def create_message(client, message, channel_options)
65
+ Ably::Models::Message(message.dup).tap do |msg|
66
+ msg.encode(client.encoders, channel_options) do |encode_error, error_message|
67
+ client.logger.error error_message
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,62 @@
1
+ module Ably::Realtime
2
+ class Channel
3
+ # A push channel used for push notifications
4
+ # Each PushChannel maps to exactly one Realtime Channel
5
+ #
6
+ # @!attribute [r] channel
7
+ # @return [Ably::Realtime::Channel] Underlying channel object
8
+ #
9
+ class PushChannel
10
+ attr_reader :channel
11
+
12
+ def initialize(channel)
13
+ raise ArgumentError, "Unsupported channel type '#{channel.class}'" unless channel.kind_of?(Ably::Realtime::Channel)
14
+ @channel = channel
15
+ end
16
+
17
+ def to_s
18
+ "<PushChannel: name=#{channel.name}>"
19
+ end
20
+
21
+ # Subscribe local device for push notifications on this channel
22
+ #
23
+ # @note This is unsupported in the Ruby library
24
+ def subscribe_device(*args)
25
+ raise_unsupported
26
+ end
27
+
28
+ # Subscribe all devices registered to this client's authenticated client_id for push notifications on this channel
29
+ #
30
+ # @note This is unsupported in the Ruby library
31
+ def subscribe_client_id(*args)
32
+ raise_unsupported
33
+ end
34
+
35
+ # Unsubscribe local device for push notifications on this channel
36
+ #
37
+ # @note This is unsupported in the Ruby library
38
+ def unsubscribe_device(*args)
39
+ raise_unsupported
40
+ end
41
+
42
+ # Unsubscribe all devices registered to this client's authenticated client_id for push notifications on this channel
43
+ #
44
+ # @note This is unsupported in the Ruby library
45
+ def unsubscribe_client_id(*args)
46
+ raise_unsupported
47
+ end
48
+
49
+ # Get list of subscriptions on this channel for this device or authenticate client_id
50
+ #
51
+ # @note This is unsupported in the Ruby library
52
+ def get_subscriptions(*args)
53
+ raise_unsupported
54
+ end
55
+
56
+ private
57
+ def raise_unsupported
58
+ raise Ably::Exceptions::PushNotificationsNotSupported, 'This device does not support receiving or subscribing to push notifications. All PushChannel methods are unavailable'
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,4 +1,5 @@
1
1
  require 'uri'
2
+ require 'ably/realtime/channel/publisher'
2
3
 
3
4
  module Ably
4
5
  module Realtime
@@ -21,6 +22,9 @@ module Ably
21
22
  #
22
23
  class Client
23
24
  include Ably::Modules::AsyncWrapper
25
+ include Ably::Realtime::Channel::Publisher
26
+ include Ably::Modules::Conversions
27
+
24
28
  extend Forwardable
25
29
 
26
30
  DOMAIN = 'realtime.ably.io'
@@ -38,6 +42,7 @@ module Ably
38
42
 
39
43
  # The {Ably::Rest::Client REST client} instantiated with the same credentials and configuration that is used for all REST operations such as authentication
40
44
  # @return [Ably::Rest::Client]
45
+ # @private
41
46
  attr_reader :rest_client
42
47
 
43
48
  # When false the client suppresses messages originating from this connection being echoed back on the same connection. Defaults to true
@@ -162,6 +167,12 @@ module Ably
162
167
  connection.connect(&block)
163
168
  end
164
169
 
170
+ # Push notification object for publishing and managing push notifications
171
+ # @return [Ably::Realtime::Push]
172
+ def push
173
+ @push ||= Push.new(self)
174
+ end
175
+
165
176
  # (see Ably::Rest::Client#request)
166
177
  # @yield [Ably::Models::HttpPaginatedResponse<>] An Array of Stats
167
178
  #
@@ -172,6 +183,74 @@ module Ably
172
183
  end
173
184
  end
174
185
 
186
+ # Publish one or more messages to the specified channel.
187
+ #
188
+ # This method allows messages to be efficiently published to Ably without instancing a {Ably::Realtime::Channel} object.
189
+ # If you want to publish a high rate of messages to Ably without instancing channels or using the REST API, then this method
190
+ # is recommended. However, channel options such as encryption are not supported with this method. If you need to specify channel options
191
+ # we recommend you use the {Ably::Realtime::Channel} +publish+ method without attaching to each channel, unless you also want to subscribe
192
+ # to published messages on that channel.
193
+ #
194
+ # Note: This feature is still in beta. As such, we cannot guarantee the API will not change in future.
195
+ #
196
+ # @param channel [String] The channel name you want to publish the message(s) to
197
+ # @param name [String, Array<Ably::Models::Message|Hash>, nil] The event name of the message to publish, or an Array of [Ably::Model::Message] objects or [Hash] objects with +:name+ and +:data+ pairs
198
+ # @param data [String, ByteArray, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument
199
+ # @param attributes [Hash, nil] Optional additional message attributes such as :client_id or :connection_id, applied when name attribute is nil or a string
200
+ #
201
+ # @yield [Ably::Models::Message,Array<Ably::Models::Message>] On success, will call the block with the {Ably::Models::Message} if a single message is published, or an Array of {Ably::Models::Message} when multiple messages are published
202
+ # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
203
+ #
204
+ # @example
205
+ # # Publish a single message
206
+ # client.publish 'activityChannel', click', { x: 1, y: 2 }
207
+ #
208
+ # # Publish an array of message Hashes
209
+ # messages = [
210
+ # { name: 'click', { x: 1, y: 2 } },
211
+ # { name: 'click', { x: 2, y: 3 } }
212
+ # ]
213
+ # client.publish 'activityChannel', messages
214
+ #
215
+ # # Publish an array of Ably::Models::Message objects
216
+ # messages = [
217
+ # Ably::Models::Message(name: 'click', { x: 1, y: 2 })
218
+ # Ably::Models::Message(name: 'click', { x: 2, y: 3 })
219
+ # ]
220
+ # client.publish 'activityChannel', messages
221
+ #
222
+ # client.publish('activityChannel', 'click', 'body') do |message|
223
+ # puts "#{message.name} event received with #{message.data}"
224
+ # end
225
+ #
226
+ # client.publish('activityChannel', 'click', 'body').errback do |error, message|
227
+ # puts "#{message.name} was not received, error #{error.message}"
228
+ # end
229
+ #
230
+ def publish(channel_name, name, data = nil, attributes = {}, &success_block)
231
+ if !connection.can_publish_messages?
232
+ error = Ably::Exceptions::MessageQueueingDisabled.new("Message cannot be published. Client is not allowed to queue messages when connection is in state #{connection.state}")
233
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
234
+ end
235
+
236
+ messages = if name.kind_of?(Enumerable)
237
+ name
238
+ else
239
+ name = ensure_utf_8(:name, name, allow_nil: true)
240
+ ensure_supported_payload data
241
+ [{ name: name, data: data }.merge(attributes)]
242
+ end
243
+
244
+ if messages.length > Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE
245
+ error = Ably::Exceptions::InvalidRequest.new("It is not possible to publish more than #{Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE} messages with a single publish request.")
246
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
247
+ end
248
+
249
+ enqueue_messages_on_connection(self, messages, channel_name).tap do |deferrable|
250
+ deferrable.callback(&success_block) if block_given?
251
+ end
252
+ end
253
+
175
254
  # @!attribute [r] endpoint
176
255
  # @return [URI::Generic] Default Ably Realtime endpoint used for all requests
177
256
  def endpoint
@@ -214,6 +293,14 @@ module Ably
214
293
  @fallback_endpoints[fallback_endpoint_index % @fallback_endpoints.count]
215
294
  end
216
295
 
296
+ # The local device detilas
297
+ # @return [Ably::Models::LocalDevice]
298
+ #
299
+ # @note This is unsupported in the Ruby library
300
+ def device
301
+ raise Ably::Exceptions::PushNotificationsNotSupported, 'This device does not support receiving or subscribing to push notifications. The local device object is not unavailable'
302
+ end
303
+
217
304
  private
218
305
  def endpoint_for_host(host)
219
306
  port = if use_tls?
@@ -102,7 +102,11 @@ module Ably::Realtime
102
102
  if channel.attached?
103
103
  channel.manager.duplicate_attached_received protocol_message
104
104
  else
105
- channel.transition_state_machine :attached, reason: protocol_message.error, resumed: protocol_message.has_channel_resumed_flag?, protocol_message: protocol_message
105
+ if channel.failed?
106
+ logger.warn "Ably::Realtime::Client::IncomingMessageDispatcher - Received an ATTACHED protocol message for FAILED channel #{channel.name}. Ignoring ATTACHED message"
107
+ else
108
+ channel.transition_state_machine :attached, reason: protocol_message.error, resumed: protocol_message.has_channel_resumed_flag?, protocol_message: protocol_message
109
+ end
106
110
  end
107
111
  end
108
112
 
@@ -132,7 +136,7 @@ module Ably::Realtime
132
136
  client.auth.authorize
133
137
 
134
138
  else
135
- error = Ably::Exceptions::ProtocolError.new("Protocol Message Action #{protocol_message.action} is unsupported by this MessageDispatcher", 400, 80013)
139
+ error = Ably::Exceptions::ProtocolError.new("Protocol Message Action #{protocol_message.action} is unsupported by this MessageDispatcher", 400, Ably::Exceptions::Codes::PROTOCOL_ERROR)
136
140
  logger.fatal error.message
137
141
  end
138
142
  end
@@ -52,7 +52,7 @@ module Ably::Realtime
52
52
  protocol_message = outgoing_queue.shift
53
53
 
54
54
  if (!connection.transport)
55
- protocol_message.fail Ably::Exceptions::TransportClosed.new('Transport disconnected unexpectedly', nil, 80003)
55
+ protocol_message.fail Ably::Exceptions::TransportClosed.new('Transport disconnected unexpectedly', nil, Ably::Exceptions::Codes::DISCONNECTED)
56
56
  next
57
57
  end
58
58
 
@@ -79,6 +79,9 @@ module Ably
79
79
  websocket_heartbeats_disabled: false,
80
80
  }.freeze
81
81
 
82
+ # Max number of messages to bundle in a single ProtocolMessage
83
+ MAX_PROTOCOL_MESSAGE_BATCH_SIZE = 50
84
+
82
85
  # A unique public identifier for this connection, used to identify this member in presence events and messages
83
86
  # @return [String]
84
87
  attr_reader :id
@@ -225,7 +228,7 @@ module Ably
225
228
  #
226
229
  def ping(&block)
227
230
  if initialized? || suspended? || closing? || closed? || failed?
228
- error = Ably::Models::ErrorInfo.new(message: "Cannot send a ping when the connection is #{state}", code: 80003)
231
+ error = Ably::Models::ErrorInfo.new(message: "Cannot send a ping when the connection is #{state}", code: Ably::Exceptions::Codes::DISCONNECTED)
229
232
  return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
230
233
  end
231
234
 
@@ -256,7 +259,7 @@ module Ably
256
259
  once_or_if([:suspended, :closing, :closed, :failed]) do
257
260
  next if finished
258
261
  finished = true
259
- deferrable.fail Ably::Models::ErrorInfo.new(message: "Ping failed as connection has changed state to #{state}", code: 80003)
262
+ deferrable.fail Ably::Models::ErrorInfo.new(message: "Ping failed as connection has changed state to #{state}", code: Ably::Exceptions::Codes::DISCONNECTED)
260
263
  end
261
264
 
262
265
  EventMachine.add_timer(defaults.fetch(:realtime_request_timeout)) do
@@ -265,7 +268,7 @@ module Ably
265
268
  __incoming_protocol_msgbus__.unsubscribe(:protocol_message, &wait_for_ping)
266
269
  error_msg = "Ping timed out after #{defaults.fetch(:realtime_request_timeout)}s"
267
270
  logger.warn { error_msg }
268
- deferrable.fail Ably::Models::ErrorInfo.new(message: error_msg, code: 50003)
271
+ deferrable.fail Ably::Models::ErrorInfo.new(message: error_msg, code: Ably::Exceptions::Codes::TIMEOUT_ERROR)
269
272
  safe_yield block, nil if block_given?
270
273
  end
271
274
  end
@@ -280,7 +283,7 @@ module Ably
280
283
  EventMachine::HttpRequest.new(url).get.tap do |http|
281
284
  http.errback do
282
285
  yield false if block_given?
283
- deferrable.fail Ably::Exceptions::ConnectionFailed.new("Unable to connect to #{url}", nil, 80000)
286
+ deferrable.fail Ably::Exceptions::ConnectionFailed.new("Unable to connect to #{url}", nil, Ably::Exceptions::Codes::CONNECTION_FAILED)
284
287
  end
285
288
  http.callback do
286
289
  EventMachine.next_tick do
@@ -289,7 +292,7 @@ module Ably
289
292
  if result
290
293
  deferrable.succeed
291
294
  else
292
- deferrable.fail Ably::Exceptions::ConnectionFailed.new("Unexpected response from #{url} (#{http.response_header.status})", 400, 40000)
295
+ deferrable.fail Ably::Exceptions::ConnectionFailed.new("Unexpected response from #{url} (#{http.response_header.status})", 400, Ably::Exceptions::Codes::BAD_REQUEST)
293
296
  end
294
297
  end
295
298
  end
@@ -43,7 +43,7 @@ module Ably::Realtime
43
43
  end
44
44
 
45
45
  unless client.auth.authentication_security_requirements_met?
46
- connection.transition_state_machine :failed, reason: Ably::Exceptions::InsecureRequest.new('Cannot use Basic Auth over non-TLS connections', 401, 40103)
46
+ connection.transition_state_machine :failed, reason: Ably::Exceptions::InsecureRequest.new('Cannot use Basic Auth over non-TLS connections', 401, Ably::Exceptions::Codes::INVALID_USE_OF_BASIC_AUTH_OVER_NONTLS_TRANSPORT)
47
47
  return
48
48
  end
49
49
 
@@ -61,7 +61,7 @@ module Ably::Realtime
61
61
 
62
62
  logger.debug { "ConnectionManager: Setting up automatic connection timeout timer for #{realtime_request_timeout}s" }
63
63
  create_timeout_timer_whilst_in_state(:connecting, realtime_request_timeout) do
64
- connection_opening_failed Ably::Exceptions::ConnectionTimeout.new("Connection to Ably timed out after #{realtime_request_timeout}s", nil, 80014)
64
+ connection_opening_failed Ably::Exceptions::ConnectionTimeout.new("Connection to Ably timed out after #{realtime_request_timeout}s", nil, Ably::Exceptions::Codes::CONNECTION_TIMED_OUT)
65
65
  end
66
66
  end
67
67
 
@@ -72,7 +72,7 @@ module Ably::Realtime
72
72
  if error.kind_of?(Ably::Exceptions::BaseAblyException)
73
73
  # Authentication errors that indicate the authentication failure is terminal should move to the failed state
74
74
  if ([401, 403].include?(error.status) && !RESOLVABLE_ERROR_CODES.fetch(:token_expired).include?(error.code)) ||
75
- (error.code == Ably::Exceptions::INVALID_CLIENT_ID)
75
+ (error.code == Ably::Exceptions::Codes::INVALID_CLIENT_ID)
76
76
  connection.transition_state_machine :failed, reason: error
77
77
  return
78
78
  end
@@ -80,7 +80,7 @@ module Ably::Realtime
80
80
 
81
81
  logger.warn { "ConnectionManager: Connection to #{connection.current_host}:#{connection.port} failed; #{error.message}" }
82
82
  next_state = get_next_retry_state_info
83
- connection.transition_state_machine next_state.fetch(:state), retry_in: next_state.fetch(:pause), reason: Ably::Exceptions::ConnectionError.new("Connection failed: #{error.message}", nil, 80000, error)
83
+ connection.transition_state_machine next_state.fetch(:state), retry_in: next_state.fetch(:pause), reason: Ably::Exceptions::ConnectionError.new("Connection failed: #{error.message}", nil, Ably::Exceptions::Codes::CONNECTION_FAILED, error)
84
84
  end
85
85
 
86
86
  # Called whenever a new connection is made
@@ -319,7 +319,7 @@ module Ably::Realtime
319
319
  @liveness_timer = EventMachine::Timer.new(connection.heartbeat_interval + 0.1) do
320
320
  if connection.connected? && (connection.time_since_connection_confirmed_alive? >= connection.heartbeat_interval)
321
321
  msg = "No activity seen from realtime in #{connection.heartbeat_interval}; assuming connection has dropped";
322
- error = Ably::Exceptions::ConnectionTimeout.new(msg, 80003, 408)
322
+ error = Ably::Exceptions::ConnectionTimeout.new(msg, Ably::Exceptions::Codes::DISCONNECTED, 408)
323
323
  connection.transition_state_machine! :disconnected, reason: error
324
324
  end
325
325
  end
@@ -492,9 +492,9 @@ module Ably::Realtime
492
492
  connection.transition_state_machine :closed
493
493
  elsif !connection.closed? && !connection.disconnected? && !connection.failed? && !connection.suspended?
494
494
  exception = if reason
495
- Ably::Exceptions::TransportClosed.new(reason, nil, 80003)
495
+ Ably::Exceptions::TransportClosed.new(reason, nil, Ably::Exceptions::Codes::DISCONNECTED)
496
496
  else
497
- Ably::Exceptions::TransportClosed.new('Transport disconnected unexpectedly', nil, 80003)
497
+ Ably::Exceptions::TransportClosed.new('Transport disconnected unexpectedly', nil, Ably::Exceptions::Codes::DISCONNECTED)
498
498
  end
499
499
  next_state = get_next_retry_state_info
500
500
  connection.transition_state_machine next_state.fetch(:state), retry_in: next_state.fetch(:pause), reason: exception
@@ -157,7 +157,7 @@ module Ably::Realtime
157
157
  logger.debug { "WebsocketTransport: Prot msg recv <=: #{action_name} - #{event_data}" }
158
158
 
159
159
  if protocol_message.invalid?
160
- error = Ably::Exceptions::ProtocolError.new("Invalid Protocol Message received: #{event_data}\nConnection moving to the failed state as the protocol is invalid and unsupported", 400, 80013)
160
+ error = Ably::Exceptions::ProtocolError.new("Invalid Protocol Message received: #{event_data}\nConnection moving to the failed state as the protocol is invalid and unsupported", 400, Ably::Exceptions::Codes::PROTOCOL_ERROR)
161
161
  logger.fatal { "WebsocketTransport: #{error.message}" }
162
162
  failed_protocol_message = Ably::Models::ProtocolMessage.new(
163
163
  action: Ably::Models::ProtocolMessage::ACTION.Error,
@@ -233,7 +233,7 @@ module Ably::Realtime
233
233
  deferrable.fail Ably::Exceptions::InvalidState.new(
234
234
  'Presence state is out of sync as channel is SUSPENDED. Presence#get on a SUSPENDED channel is only supported with option wait_for_sync: false',
235
235
  nil,
236
- 91005
236
+ Ably::Exceptions::Codes::PRESENCE_STATE_IS_OUT_OF_SYNC
237
237
  )
238
238
  end
239
239
  return deferrable
@@ -330,7 +330,7 @@ module Ably::Realtime
330
330
  def send_presence_protocol_message(presence_action, client_id, data)
331
331
  presence_message = create_presence_message(presence_action, client_id, data)
332
332
  unless presence_message.client_id
333
- raise Ably::Exceptions::Standard.new('Unable to enter create presence message without a client_id', 400, 91000)
333
+ raise Ably::Exceptions::Standard.new('Unable to enter create presence message without a client_id', 400, Ably::Exceptions::Codes::UNABLE_TO_ENTER_PRESENCE_CHANNEL_NO_CLIENTID)
334
334
  end
335
335
 
336
336
  protocol_message = {
@@ -437,13 +437,13 @@ module Ably::Realtime
437
437
 
438
438
  def attach_channel_then(deferrable)
439
439
  if channel.detached? || channel.failed?
440
- deferrable.fail Ably::Exceptions::InvalidState.new("Operation is not allowed when channel is in #{channel.state}", 400, 91001)
440
+ deferrable.fail Ably::Exceptions::InvalidState.new("Operation is not allowed when channel is in #{channel.state}", 400, Ably::Exceptions::Codes::UNABLE_TO_ENTER_PRESENCE_CHANNEL_INVALID_CHANNEL_STATE)
441
441
  else
442
442
  channel.unsafe_once(:attached, :detached, :failed) do |channel_state_change|
443
443
  if channel_state_change.current == :attached
444
444
  yield
445
445
  else
446
- deferrable.fail Ably::Exceptions::InvalidState.new("Operation failed as channel transitioned to #{channel_state_change.current}", 400, 91001)
446
+ deferrable.fail Ably::Exceptions::InvalidState.new("Operation failed as channel transitioned to #{channel_state_change.current}", 400, Ably::Exceptions::Codes::UNABLE_TO_ENTER_PRESENCE_CHANNEL_INVALID_CHANNEL_STATE)
447
447
  end
448
448
  end
449
449
  channel.attach
@@ -275,7 +275,7 @@ module Ably::Realtime
275
275
  presence_message_client_id = presence_message.client_id || client.auth.client_id
276
276
  re_enter_error = Ably::Models::ErrorInfo.new(
277
277
  message: "unable to automatically re-enter presence channel for client_id '#{presence_message_client_id}'. Source error code #{error.code} and message '#{error.message}'",
278
- code: 91004
278
+ code: Ably::Exceptions::Codes::UNABLE_TO_AUTOMATICALLY_REENTER_PRESENCE_CHANNEL
279
279
  )
280
280
  channel.emit :update, Ably::Models::ChannelStateChange.new(
281
281
  current: channel.state,
@@ -312,14 +312,14 @@ module Ably::Realtime
312
312
  when Ably::Models::PresenceMessage::ACTION.Leave
313
313
  remove_presence_member presence_message
314
314
  else
315
- Ably::Exceptions::ProtocolError.new("Protocol error, unknown presence action #{presence_message.action}", 400, 80013)
315
+ Ably::Exceptions::ProtocolError.new("Protocol error, unknown presence action #{presence_message.action}", 400, Ably::Exceptions::Codes::PROTOCOL_ERROR)
316
316
  end
317
317
  end
318
318
 
319
319
  def ensure_presence_message_is_valid(presence_message)
320
320
  return true if presence_message.connection_id
321
321
 
322
- error = Ably::Exceptions::ProtocolError.new("Protocol error, presence message is missing connectionId", 400, 80013)
322
+ error = Ably::Exceptions::ProtocolError.new("Protocol error, presence message is missing connectionId", 400, Ably::Exceptions::Codes::PROTOCOL_ERROR)
323
323
  logger.error { "PresenceMap: On channel '#{channel.name}' error: #{error}" }
324
324
  end
325
325