ably 1.0.7 → 1.1.0

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 (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