ably-rest 0.9.3 → 1.0.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/ably-rest.gemspec +2 -1
  3. data/lib/submodules/ably-ruby/.travis.yml +6 -4
  4. data/lib/submodules/ably-ruby/CHANGELOG.md +52 -61
  5. data/lib/submodules/ably-ruby/README.md +10 -0
  6. data/lib/submodules/ably-ruby/SPEC.md +1473 -852
  7. data/lib/submodules/ably-ruby/ably.gemspec +2 -1
  8. data/lib/submodules/ably-ruby/lib/ably/auth.rb +57 -25
  9. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +34 -8
  10. data/lib/submodules/ably-ruby/lib/ably/logger.rb +10 -1
  11. data/lib/submodules/ably-ruby/lib/ably/models/auth_details.rb +42 -0
  12. data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +18 -4
  13. data/lib/submodules/ably-ruby/lib/ably/models/connection_details.rb +6 -3
  14. data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +4 -3
  15. data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +1 -1
  16. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +12 -1
  17. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +101 -97
  18. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +13 -1
  19. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +20 -3
  20. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +7 -3
  21. data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +17 -7
  22. data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +29 -14
  23. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +7 -4
  24. data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +2 -4
  25. data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +7 -3
  26. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +2 -0
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +79 -31
  28. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +62 -26
  29. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +154 -65
  30. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +14 -15
  31. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +16 -3
  32. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +38 -29
  33. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -1
  34. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +108 -49
  35. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +165 -59
  36. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +22 -3
  37. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +19 -10
  38. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +67 -45
  39. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +198 -36
  40. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_manager.rb +30 -6
  41. data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_state_machine.rb +5 -12
  42. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +3 -3
  43. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +21 -8
  44. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +1 -3
  45. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +2 -2
  46. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
  47. data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +1 -1
  48. data/lib/submodules/ably-ruby/lib/ably/util/safe_deferrable.rb +26 -0
  49. data/lib/submodules/ably-ruby/lib/ably/version.rb +2 -2
  50. data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +416 -99
  51. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +5 -3
  52. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +1011 -160
  53. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +2 -2
  54. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +458 -27
  55. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +436 -97
  56. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +52 -23
  57. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +5 -3
  58. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1160 -105
  59. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +151 -22
  60. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +1 -1
  61. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +88 -27
  62. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +42 -15
  63. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +4 -4
  64. data/lib/submodules/ably-ruby/spec/rspec_config.rb +2 -1
  65. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +2 -2
  66. data/lib/submodules/ably-ruby/spec/shared/safe_deferrable_behaviour.rb +6 -2
  67. data/lib/submodules/ably-ruby/spec/support/debug_failure_helper.rb +20 -4
  68. data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +32 -1
  69. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +4 -11
  70. data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +28 -2
  71. data/lib/submodules/ably-ruby/spec/unit/models/auth_details_spec.rb +49 -0
  72. data/lib/submodules/ably-ruby/spec/unit/models/channel_state_change_spec.rb +23 -3
  73. data/lib/submodules/ably-ruby/spec/unit/models/connection_details_spec.rb +12 -1
  74. data/lib/submodules/ably-ruby/spec/unit/models/connection_state_change_spec.rb +15 -4
  75. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +34 -2
  76. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +73 -2
  77. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +64 -6
  78. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +1 -1
  79. data/lib/submodules/ably-ruby/spec/unit/models/token_request_spec.rb +1 -1
  80. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +2 -1
  81. data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +69 -0
  82. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +149 -22
  83. data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +9 -3
  84. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +1 -1
  85. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +8 -5
  86. data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +1 -1
  87. data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +4 -3
  88. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +1 -1
  89. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +3 -3
  90. metadata +7 -5
@@ -11,7 +11,7 @@ module Ably::Models
11
11
  # @!attribute [r] attributes
12
12
  # @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
13
13
  #
14
- class ErrorInfo
14
+ class ErrorInfo < Ably::Exceptions::BaseAblyException
15
15
  include Ably::Modules::ModelCommon
16
16
 
17
17
  def initialize(hash_object)
@@ -124,6 +124,17 @@ module Ably::Models
124
124
  @protocol_message
125
125
  end
126
126
 
127
+ # Contains any arbitrary key value pairs which may also contain other primitive JSON types, JSON-encodable objects or JSON-encodable arrays.
128
+ # The extras field is provided to contain message metadata and/or ancillary payloads in support of specific functionality, e.g. push
129
+ # @api private
130
+ def extras
131
+ attributes[:extras].tap do |val|
132
+ unless val.kind_of?(IdiomaticRubyWrapper) || val.kind_of?(Array) || val.kind_of?(Hash) || val.nil?
133
+ raise ArgumentError, "extras contains an unsupported type #{val.class}"
134
+ end
135
+ end
136
+ end
137
+
127
138
  private
128
139
  def raw_hash_object
129
140
  @raw_hash_object
@@ -134,7 +145,7 @@ module Ably::Models
134
145
  end
135
146
 
136
147
  def set_attributes_object(new_attributes)
137
- @attributes = IdiomaticRubyWrapper(new_attributes.clone.freeze, stop_at: [:data])
148
+ @attributes = IdiomaticRubyWrapper(new_attributes.clone.freeze, stop_at: [:data, :extras])
138
149
  end
139
150
 
140
151
  def logger
@@ -7,115 +7,119 @@ require 'ably/modules/conversions'
7
7
  # of the message is defined as 'json'.
8
8
  # Encrypted messages are encoded & decoded by the Cipher encoder.
9
9
  #
10
- module Ably::Models::MessageEncoders
11
- extend Ably::Modules::Conversions
10
+ module Ably
11
+ module Models
12
+ module MessageEncoders
13
+ extend Ably::Modules::Conversions
12
14
 
13
- # Base interface for an Ably Encoder
14
- #
15
- class Base
16
- attr_reader :client, :options
15
+ # Base interface for an Ably Encoder
16
+ #
17
+ class Base
18
+ attr_reader :client, :options
17
19
 
18
- def initialize(client, options = {})
19
- @client = client
20
- @options = options
21
- end
20
+ def initialize(client, options = {})
21
+ @client = client
22
+ @options = options
23
+ end
22
24
 
23
- # #encode is called once before a message is sent to Ably
24
- #
25
- # It is the responsibility of the #encode method to detect the intended encoding and modify the :data & :encoding properties of the message object.
26
- #
27
- # @param [Hash] message the message as a Hash object received directly from Ably.
28
- # The message contains properties :name, :data, :encoding, :timestamp, and optionally :id and :client_id.
29
- # This #encode method should modify the message Hash if any encoding action is to be taken
30
- # @param [Hash] channel_options the options used to initialize the channel that this message was received on
31
- #
32
- # @return [void]
33
- def encode(message, channel_options)
34
- raise "Not yet implemented"
35
- end
25
+ # #encode is called once before a message is sent to Ably
26
+ #
27
+ # It is the responsibility of the #encode method to detect the intended encoding and modify the :data & :encoding properties of the message object.
28
+ #
29
+ # @param [Hash] message the message as a Hash object received directly from Ably.
30
+ # The message contains properties :name, :data, :encoding, :timestamp, and optionally :id and :client_id.
31
+ # This #encode method should modify the message Hash if any encoding action is to be taken
32
+ # @param [Hash] channel_options the options used to initialize the channel that this message was received on
33
+ #
34
+ # @return [void]
35
+ def encode(message, channel_options)
36
+ raise "Not yet implemented"
37
+ end
36
38
 
37
- # #decode is called once for every encoding step
38
- # i.e. if message encoding arrives with 'utf-8/cipher+aes-128-cbc/base64'
39
- # the decoder will call #decode once for each encoding part such as 'base64', then 'cipher+aes-128-cbc', and finally 'utf-8'
40
- #
41
- # It is the responsibility of the #decode method to detect the current encoding part and modify the :data & :encoding properties of the message object.
42
- #
43
- # @param [Hash] message the message as a Hash object received directly from Ably.
44
- # The message contains properties :name, :data, :encoding, :timestamp, and optionally :id and :client_id.
45
- # This #encode method should modify the message Hash if any decoding action is to be taken
46
- # @param [Hash] channel_options the options used to initialize the channel that this message was received on
47
- #
48
- # @return [void]
49
- def decode(message, channel_options)
50
- raise "Not yet implemented"
51
- end
39
+ # #decode is called once for every encoding step
40
+ # i.e. if message encoding arrives with 'utf-8/cipher+aes-128-cbc/base64'
41
+ # the decoder will call #decode once for each encoding part such as 'base64', then 'cipher+aes-128-cbc', and finally 'utf-8'
42
+ #
43
+ # It is the responsibility of the #decode method to detect the current encoding part and modify the :data & :encoding properties of the message object.
44
+ #
45
+ # @param [Hash] message the message as a Hash object received directly from Ably.
46
+ # The message contains properties :name, :data, :encoding, :timestamp, and optionally :id and :client_id.
47
+ # This #encode method should modify the message Hash if any decoding action is to be taken
48
+ # @param [Hash] channel_options the options used to initialize the channel that this message was received on
49
+ #
50
+ # @return [void]
51
+ def decode(message, channel_options)
52
+ raise "Not yet implemented"
53
+ end
52
54
 
53
- # Add encoding to the message Hash.
54
- # Ensures that encoding delimeter is used where required i.e utf-8/cipher+aes-128-cbc/base64
55
- #
56
- # @param [Hash] message the message as a Hash object received directly from Ably.
57
- # @param [String] encoding encoding to add to the current encoding
58
- #
59
- # @return [void]
60
- def add_encoding_to_message(encoding, message)
61
- message[:encoding] = [message[:encoding], encoding].compact.join('/')
62
- end
55
+ # Add encoding to the message Hash.
56
+ # Ensures that encoding delimeter is used where required i.e utf-8/cipher+aes-128-cbc/base64
57
+ #
58
+ # @param [Hash] message the message as a Hash object received directly from Ably.
59
+ # @param [String] encoding encoding to add to the current encoding
60
+ #
61
+ # @return [void]
62
+ def add_encoding_to_message(encoding, message)
63
+ message[:encoding] = [message[:encoding], encoding].compact.join('/')
64
+ end
63
65
 
64
- # Returns the right most encoding form a meessage encoding, and nil if none exists
65
- # i.e. current_encoding_part('utf-8/cipher+aes-128-cbc/base64') => 'base64'
66
- #
67
- # @return [String,nil]
68
- def current_encoding_part(message)
69
- if message[:encoding]
70
- message[:encoding].split('/')[-1]
71
- end
72
- end
66
+ # Returns the right most encoding form a meessage encoding, and nil if none exists
67
+ # i.e. current_encoding_part('utf-8/cipher+aes-128-cbc/base64') => 'base64'
68
+ #
69
+ # @return [String,nil]
70
+ def current_encoding_part(message)
71
+ if message[:encoding]
72
+ message[:encoding].split('/')[-1]
73
+ end
74
+ end
73
75
 
74
- # Strip the current encoding part within the message Hash.
75
- #
76
- # For example, calling this method on an :encoding value of 'utf-8/cipher+aes-128-cbc/base64' would update the attribute
77
- # :encoding to 'utf-8/cipher+aes-128-cbc'
78
- #
79
- # @param [Hash] message the message as a Hash object received directly from Ably.
80
- #
81
- # @return [void]
82
- def strip_current_encoding_part(message)
83
- raise "Cannot strip encoding when there is no encoding for this message" unless message[:encoding]
84
- message[:encoding] = message[:encoding].split('/')[0...-1].join('/')
85
- message[:encoding] = nil if message[:encoding].empty?
86
- end
76
+ # Strip the current encoding part within the message Hash.
77
+ #
78
+ # For example, calling this method on an :encoding value of 'utf-8/cipher+aes-128-cbc/base64' would update the attribute
79
+ # :encoding to 'utf-8/cipher+aes-128-cbc'
80
+ #
81
+ # @param [Hash] message the message as a Hash object received directly from Ably.
82
+ #
83
+ # @return [void]
84
+ def strip_current_encoding_part(message)
85
+ raise "Cannot strip encoding when there is no encoding for this message" unless message[:encoding]
86
+ message[:encoding] = message[:encoding].split('/')[0...-1].join('/')
87
+ message[:encoding] = nil if message[:encoding].empty?
88
+ end
87
89
 
88
- # True of the message data payload is empty
89
- #
90
- # @param [Hash] message the message as a Hash object received directly from Ably.
91
- #
92
- # @return [Boolean]
93
- def is_empty?(message)
94
- message[:data].nil? || message[:data] == ''
95
- end
96
- end
90
+ # True of the message data payload is empty
91
+ #
92
+ # @param [Hash] message the message as a Hash object received directly from Ably.
93
+ #
94
+ # @return [Boolean]
95
+ def is_empty?(message)
96
+ message[:data].nil? || message[:data] == ''
97
+ end
98
+ end
97
99
 
98
- # @api private
99
- def self.register_default_encoders(client, options = {})
100
- binary_protocol = !!options[:binary_protocol]
101
- client.register_encoder Ably::Models::MessageEncoders::Utf8
102
- client.register_encoder Ably::Models::MessageEncoders::Json
103
- client.register_encoder Ably::Models::MessageEncoders::Cipher
104
- client.register_encoder Ably::Models::MessageEncoders::Base64, binary_protocol: binary_protocol
105
- end
100
+ # @api private
101
+ def self.register_default_encoders(client, options = {})
102
+ binary_protocol = !!options[:binary_protocol]
103
+ client.register_encoder Ably::Models::MessageEncoders::Utf8
104
+ client.register_encoder Ably::Models::MessageEncoders::Json
105
+ client.register_encoder Ably::Models::MessageEncoders::Cipher
106
+ client.register_encoder Ably::Models::MessageEncoders::Base64, binary_protocol: binary_protocol
107
+ end
108
+
109
+ # @api private
110
+ def self.encoder_from(encoder, options)
111
+ encoder_klass = if encoder.kind_of?(String)
112
+ encoder.split('::').inject(Kernel) do |base, klass_name|
113
+ base.public_send(:const_get, klass_name)
114
+ end
115
+ else
116
+ encoder
117
+ end
106
118
 
107
- # @api private
108
- def self.encoder_from(encoder, options)
109
- encoder_klass = if encoder.kind_of?(String)
110
- encoder.split('::').inject(Kernel) do |base, klass_name|
111
- base.public_send(:const_get, klass_name)
119
+ raise "Encoder must inherit from `Ably::Models::MessageEncoders::Base`" unless encoder_klass.ancestors.include?(Ably::Models::MessageEncoders::Base)
120
+ encoder_klass.new(self, options)
112
121
  end
113
- else
114
- encoder
115
122
  end
116
-
117
- raise "Encoder must inherit from `Ably::Models::MessageEncoders::Base`" unless encoder_klass.ancestors.include?(Ably::Models::MessageEncoders::Base)
118
- encoder_klass.new(self, options)
119
123
  end
120
124
  end
121
125
 
@@ -123,7 +123,6 @@ module Ably::Models
123
123
  end.to_json
124
124
  end
125
125
 
126
-
127
126
  # Assign this presence message to a ProtocolMessage before delivery to the Ably system
128
127
  # @api private
129
128
  def assign_to_protocol_message(protocol_message)
@@ -145,6 +144,19 @@ module Ably::Models
145
144
  @protocol_message
146
145
  end
147
146
 
147
+ # Create a static shallow clone of this object with the optional attributes to overide existing values
148
+ # Shallow clones have no dependency on the originating ProtocolMessage as all field values are stored as opposed to calculated
149
+ # Clones are useful when the original PresenceMessage needs to be mutated, such as storing in a PresenceMap with action :present
150
+ def shallow_clone(new_attributes = {})
151
+ new_attributes = IdiomaticRubyWrapper(new_attributes.clone.freeze, stop_at: [:data])
152
+
153
+ self.class.new(attributes.to_hash.merge(
154
+ id: new_attributes[:id] || id,
155
+ connection_id: new_attributes[:connection_id] || connection_id,
156
+ timestamp: new_attributes[:timestamp] || as_since_epoch(timestamp)
157
+ ).merge(new_attributes.to_hash))
158
+ end
159
+
148
160
  private
149
161
  def raw_hash_object
150
162
  @raw_hash_object
@@ -8,6 +8,8 @@ module Ably::Models
8
8
  #
9
9
  # @!attribute [r] action
10
10
  # @return [ACTION] Protocol Message action {Ably::Modules::Enum} from list of {ACTION}. Returns nil if action is unsupported by protocol
11
+ # @!attribute [r] auth
12
+ # @return [Ably::Models::AuthDetails] Authentication details used to perform authentication upgrades over an existing transport
11
13
  # @!attribute [r] count
12
14
  # @return [Integer] The count field is used for ACK and NACK actions. See {http://docs.ably.io/client-lib-development-guide/protocol/#message-acknowledgement message acknowledgement protocol}
13
15
  # @!attribute [r] error
@@ -15,7 +17,7 @@ module Ably::Models
15
17
  # @!attribute [r] channel
16
18
  # @return [String] Channel name for messages
17
19
  # @!attribute [r] channel_serial
18
- # @return [String] Contains a serial number for a message on the current channelƒ
20
+ # @return [String] Contains a serial number for a message on the current channel
19
21
  # @!attribute [r] connection_id
20
22
  # @return [String] Contains a string public identifier for the connection
21
23
  # @!attribute [r] connection_key
@@ -62,13 +64,14 @@ module Ably::Models
62
64
  detached: 13,
63
65
  presence: 14,
64
66
  message: 15,
65
- sync: 16
67
+ sync: 16,
68
+ auth: 17
66
69
  )
67
70
 
68
71
  # Indicates this protocol message action will generate an ACK response such as :message or :presence
69
72
  # @api private
70
73
  def self.ack_required?(for_action)
71
- [ACTION.Presence, ACTION.Message].include?(ACTION(for_action))
74
+ ACTION(for_action).match_any?(ACTION.Presence, ACTION.Message)
72
75
  end
73
76
 
74
77
  # {ProtocolMessage} initializer
@@ -193,10 +196,24 @@ module Ably::Models
193
196
  flags & 1 == 1
194
197
  end
195
198
 
199
+ # @api private
200
+ def has_backlog_flag?
201
+ flags & 2 == 2
202
+ end
203
+
204
+ # @api private
205
+ def has_channel_resumed_flag?
206
+ flags & 4 == 4
207
+ end
208
+
196
209
  def connection_details
197
210
  @connection_details ||= Ably::Models::ConnectionDetails(attributes[:connection_details])
198
211
  end
199
212
 
213
+ def auth
214
+ @auth ||= Ably::Models::AuthDetails(attributes[:auth])
215
+ end
216
+
200
217
  # Indicates this protocol message will generate an ACK response when sent
201
218
  # Examples of protocol messages required ACK include :message and :presence
202
219
  # @api private
@@ -38,7 +38,7 @@ module Ably::Modules
38
38
  # @yield [Object] operation block that is run in a thread
39
39
  # @return [Ably::Util::SafeDeferrable]
40
40
  #
41
- def async_wrap(success_callback = nil)
41
+ def async_wrap(success_callback = nil, custom_error_handling = nil)
42
42
  raise ArgumentError, 'Block required' unless block_given?
43
43
 
44
44
  Ably::Util::SafeDeferrable.new(logger).tap do |deferrable|
@@ -48,8 +48,12 @@ module Ably::Modules
48
48
  begin
49
49
  yield
50
50
  rescue StandardError => err
51
- logger.error "An exception in an AsyncWrapper block was caught. #{err.class}: #{err.message}\n#{err.backtrace.join("\n")}"
52
- deferrable.fail err
51
+ if custom_error_handling
52
+ custom_error_handling.call err, deferrable
53
+ else
54
+ logger.error { "An exception in an AsyncWrapper block was caught. #{err.class}: #{err.message}\n#{err.backtrace.join("\n")}" }
55
+ deferrable.fail err
56
+ end
53
57
  end
54
58
  end
55
59
 
@@ -49,11 +49,11 @@ module Ably::Modules
49
49
  def get(identifier)
50
50
  case identifier
51
51
  when Symbol
52
- by_symbol.fetch(identifier)
52
+ by_symbol.fetch(identifier) { raise KeyError, "#{name} key not found: :#{identifier}" }
53
53
  when String
54
- by_symbol.fetch(convert_to_snake_case_symbol(identifier))
54
+ by_symbol.fetch(convert_to_snake_case_symbol(identifier)) { raise KeyError, "#{name} key not found: '#{identifier}'" }
55
55
  when Numeric
56
- by_index.fetch(identifier)
56
+ by_index.fetch(identifier) { raise KeyError, "#{name} key not found: #{identifier}" }
57
57
  when ancestors.first
58
58
  identifier
59
59
  else
@@ -89,6 +89,12 @@ module Ably::Modules
89
89
  @enum_name
90
90
  end
91
91
 
92
+ # Array of Enum values as symbols
93
+ # @return [Array<Symbol>]
94
+ def to_sym_arr
95
+ @by_symbol.keys
96
+ end
97
+
92
98
  private
93
99
  def by_index
94
100
  @by_index
@@ -158,7 +164,7 @@ module Ably::Modules
158
164
 
159
165
  # Allow comparison of Enum objects based on:
160
166
  #
161
- # * Other equivalent Enum objects
167
+ # * Other equivalent Enum objects compared by Symbol (not Integer value)
162
168
  # * Symbol
163
169
  # * String
164
170
  # * Integer index of Enum
@@ -171,13 +177,17 @@ module Ably::Modules
171
177
  self.to_sym == convert_to_snake_case_symbol(other)
172
178
  when Numeric
173
179
  self.to_i == other.to_i
174
- when self.class
175
- self.to_i == other.to_i
176
180
  else
177
- false
181
+ if other.kind_of?(Ably::Modules::Enum::Base)
182
+ self.to_sym == other.to_sym
183
+ end
178
184
  end
179
185
  end
180
186
 
187
+ def match_any?(*enums)
188
+ enums.any? { |enum| self.==(enum) }
189
+ end
190
+
181
191
  private
182
192
  def name
183
193
  @name
@@ -54,7 +54,7 @@ module Ably
54
54
  end
55
55
 
56
56
  # Equivalent of {#on} but any exception raised in a block will bubble up and cause this client library to fail.
57
- # This method should only be used internally by the client library.
57
+ # This method is designed to be used internally by the client library.
58
58
  # @api private
59
59
  def unsafe_on(*event_names, &block)
60
60
  add_callback event_names, proc_for_block(block, unsafe: true)
@@ -70,7 +70,7 @@ module Ably
70
70
  end
71
71
 
72
72
  # Equivalent of {#once} but any exception raised in a block will bubble up and cause this client library to fail.
73
- # This method should only be used internally by the client library.
73
+ # This method is designed to be used internally by the client library.
74
74
  # @api private
75
75
  def unsafe_once(*event_names, &block)
76
76
  add_callback event_names, proc_for_block(block, delete_once_run: true, unsafe: true)
@@ -101,30 +101,45 @@ module Ably
101
101
  #
102
102
  # @return [void]
103
103
  def off(*event_names, &block)
104
+ off_internal(false, *event_names, &block)
105
+ end
106
+
107
+ # Equivalent of {#off} but only unsafe listeners are removed.
108
+ # This method is designed to be used internally by the client library.
109
+ # @api private
110
+ def unsafe_off(*event_names, &block)
111
+ off_internal(true, *event_names, &block)
112
+ end
113
+
114
+ private
115
+ def off_internal(unsafe, *event_names, &block)
104
116
  keys = if event_names.empty?
105
117
  callbacks.keys
106
118
  else
107
119
  event_names
108
120
  end
109
121
 
110
- keys.each do |event_name|
111
- if block_given?
112
- callbacks[callbacks_event_coerced(event_name)].delete_if { |proc_hash| proc_hash[:block] == block }
113
- else
114
- callbacks[callbacks_event_coerced(event_name)].clear
122
+ if event_names.empty?
123
+ callbacks_any.delete_if do |proc_hash|
124
+ if block_given?
125
+ (proc_hash[:unsafe] == unsafe) && (proc_hash[:block] == block)
126
+ else
127
+ proc_hash[:unsafe] == unsafe
128
+ end
115
129
  end
116
130
  end
117
131
 
118
- if event_names.empty?
119
- if block_given?
120
- callbacks_any.delete_if { |proc_hash| proc_hash[:block] == block }
121
- else
122
- callbacks_any.clear
132
+ keys.each do |event_name|
133
+ callbacks[callbacks_event_coerced(event_name)].delete_if do |proc_hash|
134
+ if block_given?
135
+ (proc_hash[:unsafe] == unsafe) && (proc_hash[:block] == block)
136
+ else
137
+ proc_hash[:unsafe] == unsafe
138
+ end
123
139
  end
124
140
  end
125
141
  end
126
142
 
127
- private
128
143
  def self.included(klass)
129
144
  klass.extend ClassMethods
130
145
  end
@@ -148,7 +163,7 @@ module Ably
148
163
  true if options[:delete_once_run]
149
164
  end,
150
165
  block: block,
151
- unsafe: options[:unsafe]
166
+ unsafe: options[:unsafe] || false
152
167
  }
153
168
  end
154
169