ably-rest 0.9.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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