ably-rest 0.7.3 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +1 -0
  3. data/SPEC.md +480 -472
  4. data/lib/ably-rest.rb +1 -1
  5. data/lib/submodules/ably-ruby/LICENSE.txt +1 -1
  6. data/lib/submodules/ably-ruby/README.md +107 -24
  7. data/lib/submodules/ably-ruby/SPEC.md +531 -398
  8. data/lib/submodules/ably-ruby/lib/ably/auth.rb +24 -16
  9. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +9 -0
  10. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +17 -9
  11. data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +12 -8
  12. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +18 -10
  13. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +15 -4
  14. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +4 -3
  15. data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +31 -2
  16. data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +77 -0
  17. data/lib/submodules/ably-ruby/lib/ably/modules/safe_deferrable.rb +71 -0
  18. data/lib/submodules/ably-ruby/lib/ably/modules/safe_yield.rb +41 -0
  19. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +28 -8
  20. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +0 -5
  21. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +24 -29
  22. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +54 -11
  23. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +21 -6
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +7 -2
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +29 -26
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +4 -4
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +41 -9
  28. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +72 -24
  29. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +26 -4
  30. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +19 -6
  31. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +74 -208
  32. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +264 -0
  33. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_manager.rb +59 -0
  34. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_state_machine.rb +64 -0
  35. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +1 -1
  36. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +6 -2
  37. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
  38. data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +3 -1
  39. data/lib/submodules/ably-ruby/lib/ably/util/safe_deferrable.rb +18 -0
  40. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  41. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +2 -2
  42. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +28 -6
  43. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +116 -46
  44. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +55 -10
  45. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +32 -0
  46. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +456 -96
  47. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +2 -2
  48. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +2 -2
  49. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +96 -7
  50. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +8 -0
  51. data/lib/submodules/ably-ruby/spec/shared/safe_deferrable_behaviour.rb +71 -0
  52. data/lib/submodules/ably-ruby/spec/support/api_helper.rb +1 -1
  53. data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +1 -1
  54. data/lib/submodules/ably-ruby/spec/support/test_app.rb +13 -7
  55. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +15 -14
  56. data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +4 -4
  57. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +17 -17
  58. data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +4 -4
  59. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +28 -9
  60. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +50 -0
  61. data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +76 -2
  62. data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +51 -20
  63. data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +3 -3
  64. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +30 -0
  65. data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +52 -26
  66. data/lib/submodules/ably-ruby/spec/unit/realtime/safe_deferrable_spec.rb +12 -0
  67. data/spec/spec_helper.rb +5 -0
  68. metadata +12 -4
  69. data/lib/submodules/ably-ruby/.ruby-version.old +0 -1
@@ -0,0 +1,71 @@
1
+ require 'eventmachine'
2
+
3
+ module Ably::Modules
4
+ # SafeDeferrable module provides an EventMachine::Deferrable interface to the object it is included in
5
+ # and is safe to use for for public interfaces of this client library.
6
+ # Any exceptions raised in the success or failure callbacks is caught and logged to #logger
7
+ #
8
+ # An exception in a callback provided by a developer should not break this client library
9
+ # and stop further execution of code.
10
+ #
11
+ # @note this Module requires that the method #logger is available
12
+ #
13
+ # See http://www.rubydoc.info/gems/eventmachine/1.0.7/EventMachine/Deferrable
14
+ #
15
+ module SafeDeferrable
16
+ include EventMachine::Deferrable
17
+
18
+ # Specify a block to be executed if and when the Deferrable object receives
19
+ # a status of :succeeded.
20
+ # See http://www.rubydoc.info/gems/eventmachine/1.0.7/EventMachine/Deferrable#callback-instance_method
21
+ def callback(&block)
22
+ super do |*args|
23
+ safe_deferrable_block(*args, &block)
24
+ end
25
+ end
26
+
27
+ # Specify a block to be executed if and when the Deferrable object receives
28
+ # a status of :failed.
29
+ # See http://www.rubydoc.info/gems/eventmachine/1.0.7/EventMachine/Deferrable#errback-instance_method
30
+ def errback(&block)
31
+ super do |*args|
32
+ safe_deferrable_block(*args, &block)
33
+ end
34
+ end
35
+
36
+ # Mark the Deferrable as succeeded and trigger all callbacks
37
+ # See http://www.rubydoc.info/gems/eventmachine/1.0.7/EventMachine/Deferrable#succeed-instance_method
38
+ def succeed(*args)
39
+ super(*args)
40
+ end
41
+
42
+ # Mark the Deferrable as failed and trigger all callbacks
43
+ # See http://www.rubydoc.info/gems/eventmachine/1.0.7/EventMachine/Deferrable#fail-instance_method
44
+ def fail(*args)
45
+ super(*args)
46
+ end
47
+
48
+ private
49
+ def safe_deferrable_block(*args)
50
+ yield *args
51
+ rescue StandardError => e
52
+ message = "An exception in a Deferrable callback was caught. #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
53
+ if defined?(:logger) && logger.respond_to?(:error)
54
+ logger.error message
55
+ else
56
+ fallback_logger.error message
57
+ end
58
+ end
59
+
60
+ def fallback_logger
61
+ @fallback_logger ||= ::Logger.new(STDOUT).tap do |logger|
62
+ logger.formatter = proc do |severity, datetime, progname, msg|
63
+ [
64
+ "#{datetime.strftime("%Y-%m-%d %H:%M:%S.%L")} #{::Logger::SEV_LABEL[severity]} #{msg}",
65
+ "Warning: SafeDeferrable expects the method #logger to be defined in the class it is included in, the method was not found in #{self.class}"
66
+ ].join("\n")
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,41 @@
1
+ module Ably::Modules
2
+ # SafeYield provides the method safe_yield that will yield to the consumer
3
+ # who provided a block, however any exceptions will be caught, logged, and
4
+ # operation of the client library will continue.
5
+ #
6
+ # An exception in a callback provided by a developer should not break this client library
7
+ # and stop further execution of code.
8
+ #
9
+ # @note this Module requires that the method #logger is available
10
+ #
11
+ # @api private
12
+ module SafeYield
13
+ private
14
+
15
+ def safe_yield(block, *args)
16
+ block.call *args
17
+ rescue StandardError => e
18
+ message = "An exception in an external block was caught. #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
19
+ safe_yield_log_error message
20
+ end
21
+
22
+ def safe_yield_log_error(message)
23
+ if defined?(:logger) && logger.respond_to?(:error)
24
+ return logger.error message
25
+ end
26
+ rescue StandardError => e
27
+ fallback_logger.error message
28
+ end
29
+
30
+ def fallback_logger
31
+ @fallback_logger ||= ::Logger.new(STDOUT).tap do |logger|
32
+ logger.formatter = proc do |severity, datetime, progname, msg|
33
+ [
34
+ "#{datetime.strftime("%Y-%m-%d %H:%M:%S.%L")} #{::Logger::SEV_LABEL[severity]} #{msg}",
35
+ "Warning: SafeYield expects the method #logger to be defined in the class it is included in, the method was not found in #{self.class}"
36
+ ].join("\n")
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -6,6 +6,8 @@ module Ably::Modules
6
6
  # It also ensures the EventEmitter is configured to retrict permitted events to the
7
7
  # the available STATEs and :error.
8
8
  #
9
+ # @note This module requires that the method #logger is defined.
10
+ #
9
11
  # @example
10
12
  # class Connection
11
13
  # include Ably::Modules::EventEmitter
@@ -91,22 +93,29 @@ module Ably::Modules
91
93
  end if failure_block
92
94
 
93
95
  Array(target_states).each do |target_state|
94
- once target_state, &success_wrapper
96
+ safe_unsafe_method options[:unsafe], :once, target_state, &success_wrapper
95
97
 
96
- once_state_changed do |*args|
98
+ safe_unsafe_method options[:unsafe], :once_state_changed do |*args|
97
99
  failure_wrapper.call *args unless state == target_state
98
100
  end if failure_block
99
101
  end
100
102
  end
101
103
  end
102
104
 
105
+ # Equivalent of {#once_or_if} but any exception raised in a block will bubble up and cause this client library to fail.
106
+ # This method should only be used internally by the client library.
107
+ # @api private
108
+ def unsafe_once_or_if(target_states, options = {}, &block)
109
+ once_or_if(target_states, options.merge(unsafe: true), &block)
110
+ end
111
+
103
112
  # Calls the block once when the state changes
104
113
  #
105
114
  # @yield block is called once the state changes
106
115
  # @return [void]
107
116
  #
108
117
  # @api private
109
- def once_state_changed(&block)
118
+ def once_state_changed(options = {}, &block)
110
119
  raise ArgumentError, 'Block required' unless block_given?
111
120
 
112
121
  once_block = proc do |*args|
@@ -114,17 +123,24 @@ module Ably::Modules
114
123
  yield *args
115
124
  end
116
125
 
117
- once *self.class::STATE.map, &once_block
126
+ safe_unsafe_method options[:unsafe], :once, *self.class::STATE.map, &once_block
127
+ end
128
+
129
+ # Equivalent of {#once_state_changed} but any exception raised in a block will bubble up and cause this client library to fail.
130
+ # This method should only be used internally by the client library.
131
+ # @api private
132
+ def unsafe_once_state_changed(&block)
133
+ once_state_changed(unsafe: true, &block)
118
134
  end
119
135
 
120
136
  private
121
137
 
122
- # Returns an {EventMachine::Deferrable} and once the target state is reached, the
123
- # success block if provided and {EventMachine::Deferrable#callback} is called.
124
- # If the state changes to any other state, the {EventMachine::Deferrable#errback} is called.
138
+ # Returns an {Ably::Util::SafeDeferrable} and once the target state is reached, the
139
+ # success block if provided and {Ably::Util::SafeDeferrable#callback} is called.
140
+ # If the state changes to any other state, the {Ably::Util::SafeDeferrable#errback} is called.
125
141
  #
126
142
  def deferrable_for_state_change_to(target_state)
127
- EventMachine::DefaultDeferrable.new.tap do |deferrable|
143
+ Ably::Util::SafeDeferrable.new(logger).tap do |deferrable|
128
144
  once_or_if(target_state, else: proc { |*args| deferrable.fail self, *args }) do
129
145
  yield self if block_given?
130
146
  deferrable.succeed self
@@ -149,5 +165,9 @@ module Ably::Modules
149
165
  end
150
166
  end
151
167
  end
168
+
169
+ def safe_unsafe_method(unsafe, method_name, *args, &block)
170
+ public_send("#{'unsafe_' if unsafe}#{method_name}", *args, &block)
171
+ end
152
172
  end
153
173
  end
@@ -5,13 +5,8 @@ require 'ably/modules/event_emitter'
5
5
 
6
6
  require 'ably/realtime/channel'
7
7
  require 'ably/realtime/channels'
8
- require 'ably/realtime/channel/channel_manager'
9
- require 'ably/realtime/channel/channel_state_machine'
10
8
  require 'ably/realtime/client'
11
9
  require 'ably/realtime/connection'
12
- require 'ably/realtime/connection/connection_manager'
13
- require 'ably/realtime/connection/connection_state_machine'
14
- require 'ably/realtime/connection/websocket_transport'
15
10
  require 'ably/realtime/presence'
16
11
 
17
12
  Dir.glob(File.expand_path("models/*.rb", File.dirname(__FILE__))).each do |file|
@@ -33,6 +33,7 @@ module Ably
33
33
  include Ably::Modules::EventEmitter
34
34
  include Ably::Modules::EventMachineHelpers
35
35
  include Ably::Modules::AsyncWrapper
36
+ include Ably::Modules::MessageEmitter
36
37
  extend Ably::Modules::Enum
37
38
 
38
39
  STATE = ruby_enum('STATE',
@@ -84,7 +85,6 @@ module Ably
84
85
  @client = client
85
86
  @name = name
86
87
  @options = channel_options.clone.freeze
87
- @subscriptions = Hash.new { |hash, key| hash[key] = [] }
88
88
  @queue = []
89
89
 
90
90
  @state_machine = ChannelStateMachine.new(self)
@@ -92,6 +92,7 @@ module Ably
92
92
  @manager = ChannelManager.new(self, client.connection)
93
93
 
94
94
  setup_event_handlers
95
+ setup_presence
95
96
  end
96
97
 
97
98
  # Publish a message on the channel
@@ -123,33 +124,25 @@ module Ably
123
124
 
124
125
  # Subscribe to messages matching providing event name, or all messages if event name not provided
125
126
  #
126
- # @param name [String] The event name of the message to subscribe to if provided. Defaults to all events.
127
+ # @param names [String] The event name of the message to subscribe to if provided. Defaults to all events.
127
128
  # @yield [Ably::Models::Message] For each message received, the block is called
128
129
  #
129
130
  # @return [void]
130
131
  #
131
- def subscribe(name = :all, &callback)
132
+ def subscribe(*names, &callback)
132
133
  attach unless attached? || attaching?
133
- subscriptions[message_name_key(name)] << callback
134
+ super
134
135
  end
135
136
 
136
137
  # Unsubscribe the matching block for messages matching providing event name, or all messages if event name not provided.
137
138
  # If a block is not provided, all subscriptions will be unsubscribed
138
139
  #
139
- # @param name [String] The event name of the message to subscribe to if provided. Defaults to all events.
140
+ # @param names [String] The event name of the message to subscribe to if provided. Defaults to all events.
140
141
  #
141
142
  # @return [void]
142
143
  #
143
- def unsubscribe(name = :all, &callback)
144
- if message_name_key(name) == :all
145
- subscriptions.keys
146
- else
147
- Array(message_name_key(name))
148
- end.each do |key|
149
- subscriptions[key].delete_if do |block|
150
- !block_given? || callback == block
151
- end
152
- end
144
+ def unsubscribe(*names, &callback)
145
+ super
153
146
  end
154
147
 
155
148
  # Attach to this channel, and call the block if provided when attached.
@@ -157,7 +150,7 @@ module Ably
157
150
  # to need to call attach explicitly.
158
151
  #
159
152
  # @yield [Ably::Realtime::Channel] Block is called as soon as this channel is in the Attached state
160
- # @return [EventMachine::Deferrable] Deferrable that supports both success (callback) and failure (errback) callback
153
+ # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callback
161
154
  #
162
155
  def attach(&success_block)
163
156
  transition_state_machine :attaching if can_transition_to?(:attaching)
@@ -183,7 +176,7 @@ module Ably
183
176
  #
184
177
  def presence
185
178
  attach if initialized?
186
- @presence ||= Presence.new(self)
179
+ @presence
187
180
  end
188
181
 
189
182
  # Return the message history of the channel
@@ -193,7 +186,7 @@ module Ably
193
186
  #
194
187
  # @yield [Ably::Models::PaginatedResource<Ably::Models::Message>] An Array of {Ably::Models::Message} objects that supports paging (#next_page, #first_page)
195
188
  #
196
- # @return [EventMachine::Deferrable]
189
+ # @return [Ably::Util::SafeDeferrable]
197
190
  def history(options = {}, &callback)
198
191
  async_wrap(callback) do
199
192
  rest_channel.history(options.merge(async_blocking_operations: true))
@@ -214,6 +207,11 @@ module Ably
214
207
  @error_reason = error
215
208
  end
216
209
 
210
+ # @api private
211
+ def clear_error_reason
212
+ @error_reason = nil
213
+ end
214
+
217
215
  # Used by {Ably::Modules::StateEmitter} to debug state changes
218
216
  # @api private
219
217
  def logger
@@ -221,14 +219,12 @@ module Ably
221
219
  end
222
220
 
223
221
  private
224
- attr_reader :queue, :subscriptions
222
+ attr_reader :queue
225
223
 
226
224
  def setup_event_handlers
227
225
  __incoming_msgbus__.subscribe(:message) do |message|
228
226
  message.decode self
229
-
230
- subscriptions[:all].each { |cb| cb.call(message) }
231
- subscriptions[message.name].each { |cb| cb.call(message) }
227
+ emit_message message.name, message
232
228
  end
233
229
 
234
230
  on(STATE.Attached) do
@@ -273,7 +269,7 @@ module Ably
273
269
  message.merge!(data: data) unless data.nil?
274
270
  message.merge!(clientId: client.client_id) if client.client_id
275
271
 
276
- Ably::Models::Message.new(message, nil).tap do |message|
272
+ Ably::Models::Message.new(message, logger: logger).tap do |message|
277
273
  message.encode self
278
274
  end
279
275
  end
@@ -286,13 +282,12 @@ module Ably
286
282
  client.connection
287
283
  end
288
284
 
289
- def message_name_key(name)
290
- if name == :all
291
- :all
292
- else
293
- name.to_s
294
- end
285
+ def setup_presence
286
+ @presence ||= Presence.new(self)
295
287
  end
296
288
  end
297
289
  end
298
290
  end
291
+
292
+ require 'ably/realtime/channel/channel_manager'
293
+ require 'ably/realtime/channel/channel_state_machine'
@@ -13,13 +13,7 @@ module Ably::Realtime
13
13
  @channel = channel
14
14
  @connection = connection
15
15
 
16
- connection.on(:closed) do
17
- channel.transition_state_machine :detaching if can_transition_to?(:detaching)
18
- end
19
-
20
- connection.on(:failed) do |error|
21
- channel.transition_state_machine :failed, error if can_transition_to?(:failed)
22
- end
16
+ setup_connection_event_handlers
23
17
  end
24
18
 
25
19
  # Commence attachment
@@ -39,12 +33,12 @@ module Ably::Realtime
39
33
  end
40
34
  end
41
35
 
42
- # Commence presence SYNC if applicable
43
- def sync(attached_protocol_message)
36
+ # Channel is attached, notify presence if sync is expected
37
+ def attached(attached_protocol_message)
44
38
  if attached_protocol_message.has_presence_flag?
45
- channel.presence.sync_started
39
+ channel.presence.manager.sync_expected
46
40
  else
47
- channel.presence.sync_completed
41
+ channel.presence.manager.sync_not_expected
48
42
  end
49
43
  end
50
44
 
@@ -59,6 +53,45 @@ module Ably::Realtime
59
53
  channel.transition_state_machine! :detaching, error
60
54
  end
61
55
 
56
+ # When a channel is no longer attached or has failed,
57
+ # all messages awaiting an ACK response should fail immediately
58
+ def fail_messages_awaiting_ack(error)
59
+ # Allow a short time for other queued operations to complete before failing all messages
60
+ EventMachine.add_timer(0.1) do
61
+ error = Ably::Exceptions::MessageDeliveryError.new('Channel is no longer in a state suitable to deliver this message to the server') unless error
62
+ fail_messages_in_queue connection.__pending_message_ack_queue__, error
63
+ fail_messages_in_queue connection.__outgoing_message_queue__, error
64
+ end
65
+ end
66
+
67
+ def fail_messages_in_queue(queue, error)
68
+ queue.delete_if do |protocol_message|
69
+ if protocol_message.channel == channel.name
70
+ nack_messages protocol_message, error
71
+ true
72
+ end
73
+ end
74
+ end
75
+
76
+ def nack_messages(protocol_message, error)
77
+ (protocol_message.messages + protocol_message.presence).each do |message|
78
+ logger.debug "Calling NACK failure callbacks for #{message.class.name} - #{message.to_json}, protocol message: #{protocol_message}"
79
+ message.fail message, error
80
+ end
81
+ logger.debug "Calling NACK failure callbacks for #{protocol_message.class.name} - #{protocol_message.to_json}"
82
+ protocol_message.fail protocol_message, error
83
+ end
84
+
85
+ def drop_pending_queue_from_ack(ack_protocol_message)
86
+ message_serial_up_to = ack_protocol_message.message_serial + ack_protocol_message.count - 1
87
+ connection.__pending_message_ack_queue__.drop_while do |protocol_message|
88
+ if protocol_message.message_serial <= message_serial_up_to
89
+ yield protocol_message
90
+ true
91
+ end
92
+ end
93
+ end
94
+
62
95
  private
63
96
 
64
97
  attr_reader :channel, :connection
@@ -84,6 +117,16 @@ module Ably::Realtime
84
117
  )
85
118
  end
86
119
 
120
+ def setup_connection_event_handlers
121
+ connection.unsafe_on(:closed) do
122
+ channel.transition_state_machine :detaching if can_transition_to?(:detaching)
123
+ end
124
+
125
+ connection.unsafe_on(:failed) do |error|
126
+ channel.transition_state_machine :failed, error if can_transition_to?(:failed)
127
+ end
128
+ end
129
+
87
130
  def logger
88
131
  connection.logger
89
132
  end
@@ -35,7 +35,7 @@ module Ably::Realtime
35
35
  end
36
36
 
37
37
  before_transition(to: [:attached]) do |channel, current_transition|
38
- channel.manager.sync current_transition.metadata
38
+ channel.manager.attached current_transition.metadata
39
39
  end
40
40
 
41
41
  after_transition(to: [:detaching]) do |channel, current_transition|
@@ -43,17 +43,31 @@ module Ably::Realtime
43
43
  end
44
44
 
45
45
  after_transition(to: [:detached]) do |channel, current_transition|
46
- channel.manager.emit_error current_transition.metadata if current_transition.metadata
46
+ channel.manager.fail_messages_awaiting_ack nil_unless_error(current_transition.metadata)
47
+ channel.manager.emit_error current_transition.metadata if is_error_type?(current_transition.metadata)
47
48
  end
48
49
 
49
50
  after_transition(to: [:failed]) do |channel, current_transition|
50
- channel.manager.emit_error current_transition.metadata
51
+ channel.manager.fail_messages_awaiting_ack nil_unless_error(current_transition.metadata)
52
+ channel.manager.emit_error current_transition.metadata if is_error_type?(current_transition.metadata)
51
53
  end
52
54
 
53
55
  # Transitions responsible for updating channel#error_reason
54
- before_transition(to: [:attached, :detached, :failed]) do |channel, current_transition|
55
- reason = current_transition.metadata if is_error_type?(current_transition.metadata)
56
- channel.set_failed_channel_error_reason reason
56
+ before_transition(to: [:failed]) do |channel, current_transition|
57
+ channel.set_failed_channel_error_reason current_transition.metadata if is_error_type?(current_transition.metadata)
58
+ end
59
+
60
+ before_transition(to: [:attached, :detached]) do |channel, current_transition|
61
+ if is_error_type?(current_transition.metadata)
62
+ channel.set_failed_channel_error_reason current_transition.metadata
63
+ else
64
+ # Attached & Detached are "healthy" final states so reset the error reason
65
+ channel.clear_error_reason
66
+ end
67
+ end
68
+
69
+ def self.nil_unless_error(error_object)
70
+ error_object if is_error_type?(error_object)
57
71
  end
58
72
 
59
73
  private
@@ -61,6 +75,7 @@ module Ably::Realtime
61
75
  object
62
76
  end
63
77
 
78
+ # Logged needs to be defined as it is used by {Ably::Modules::StateMachine}
64
79
  def logger
65
80
  channel.logger
66
81
  end