ably-rest 0.7.3 → 0.7.5
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.
- checksums.yaml +8 -8
- data/.travis.yml +1 -0
- data/SPEC.md +480 -472
- data/lib/ably-rest.rb +1 -1
- data/lib/submodules/ably-ruby/LICENSE.txt +1 -1
- data/lib/submodules/ably-ruby/README.md +107 -24
- data/lib/submodules/ably-ruby/SPEC.md +531 -398
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +24 -16
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +9 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +17 -9
- data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +12 -8
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +18 -10
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +15 -4
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +4 -3
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +31 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +77 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_deferrable.rb +71 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_yield.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +28 -8
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +0 -5
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +24 -29
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +54 -11
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +21 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +7 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +29 -26
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +4 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +41 -9
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +72 -24
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +26 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +19 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +74 -208
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +264 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_manager.rb +59 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_state_machine.rb +64 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +6 -2
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +3 -1
- data/lib/submodules/ably-ruby/lib/ably/util/safe_deferrable.rb +18 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +28 -6
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +116 -46
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +55 -10
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +32 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +456 -96
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +96 -7
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +8 -0
- data/lib/submodules/ably-ruby/spec/shared/safe_deferrable_behaviour.rb +71 -0
- data/lib/submodules/ably-ruby/spec/support/api_helper.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +13 -7
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +15 -14
- data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +4 -4
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +17 -17
- data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +4 -4
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +28 -9
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +50 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +76 -2
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +51 -20
- data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +3 -3
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +30 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +52 -26
- data/lib/submodules/ably-ruby/spec/unit/realtime/safe_deferrable_spec.rb +12 -0
- data/spec/spec_helper.rb +5 -0
- metadata +12 -4
- data/lib/submodules/ably-ruby/.ruby-version.old +0 -1
@@ -34,7 +34,7 @@ module Ably
|
|
34
34
|
# Creates an Auth object
|
35
35
|
#
|
36
36
|
# @param [Ably::Rest::Client] client {Ably::Rest::Client} this Auth object uses
|
37
|
-
# @param options (see Ably::Rest::Client#initialize)
|
37
|
+
# @param [Hash] options (see Ably::Rest::Client#initialize)
|
38
38
|
# @option (see Ably::Rest::Client#initialize)
|
39
39
|
# @yield (see Ably::Rest::Client#initialize)
|
40
40
|
#
|
@@ -53,12 +53,7 @@ module Ably
|
|
53
53
|
raise ArgumentError, 'api_key and key_id or key_secret are mutually exclusive. Provider either an api_key or key_id & key_secret'
|
54
54
|
end
|
55
55
|
|
56
|
-
if auth_options[:api_key]
|
57
|
-
api_key_parts = auth_options[:api_key].to_s.match(/(?<id>[\w_-]+\.[\w_-]+):(?<secret>[\w_-]+)/)
|
58
|
-
raise ArgumentError, 'api_key is invalid' unless api_key_parts
|
59
|
-
auth_options[:key_id] = api_key_parts[:id].encode(Encoding::UTF_8)
|
60
|
-
auth_options[:key_secret] = api_key_parts[:secret].encode(Encoding::UTF_8)
|
61
|
-
end
|
56
|
+
split_api_key_into_key_and_secret! auth_options if auth_options[:api_key]
|
62
57
|
|
63
58
|
if using_basic_auth? && !api_key_present?
|
64
59
|
raise ArgumentError, 'api_key is missing. Either an API key, token, or token auth method must be provided'
|
@@ -78,6 +73,7 @@ module Ably
|
|
78
73
|
#
|
79
74
|
# @param [Hash] options the options for the token request
|
80
75
|
# @option options (see #request_token)
|
76
|
+
# @option options [String] :api_key API key comprising the key ID and key secret in a single string
|
81
77
|
# @option options [Boolean] :force obtains a new token even if the current token is valid
|
82
78
|
#
|
83
79
|
# @yield (see #request_token)
|
@@ -102,6 +98,9 @@ module Ably
|
|
102
98
|
return current_token unless current_token.expired?
|
103
99
|
end
|
104
100
|
|
101
|
+
options = options.clone
|
102
|
+
split_api_key_into_key_and_secret! options if options[:api_key]
|
103
|
+
|
105
104
|
@options = @options.merge(options)
|
106
105
|
@default_token_block = token_request_block if block_given?
|
107
106
|
|
@@ -157,10 +156,13 @@ module Ably
|
|
157
156
|
|
158
157
|
token_request = IdiomaticRubyWrapper(token_request)
|
159
158
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
159
|
+
if token_request.has_key?(:issued_at) && token_request.has_key?(:expires)
|
160
|
+
Ably::Models::Token.new(token_request)
|
161
|
+
else
|
162
|
+
response = client.post("/keys/#{token_request.fetch(:id)}/requestToken", token_request.hash, send_auth_header: false, disable_automatic_reauthorise: true)
|
163
|
+
body = IdiomaticRubyWrapper(response.body)
|
164
|
+
Ably::Models::Token.new(body.fetch(:access_token))
|
165
|
+
end
|
164
166
|
end
|
165
167
|
|
166
168
|
# Creates and signs a token request that can then subsequently be used by any client to request a token
|
@@ -188,9 +190,10 @@ module Ably
|
|
188
190
|
# # :mac=>"881oZHeFo6oMim7N64y2vFHtSlpQ2gn/uE56a8gUxHw="
|
189
191
|
# # }
|
190
192
|
def create_token_request(options = {})
|
191
|
-
token_attributes = %w(id client_id ttl timestamp capability nonce)
|
193
|
+
token_attributes = %w(id client_id ttl timestamp capability nonce persisted)
|
192
194
|
|
193
195
|
token_options = options.clone
|
196
|
+
|
194
197
|
request_key_id = token_options.delete(:key_id) || key_id
|
195
198
|
request_key_secret = token_options.delete(:key_secret) || key_secret
|
196
199
|
|
@@ -296,7 +299,7 @@ module Ably
|
|
296
299
|
end
|
297
300
|
|
298
301
|
private
|
299
|
-
attr_reader :default_token_block
|
302
|
+
attr_reader :client, :default_token_block
|
300
303
|
|
301
304
|
def ensure_api_key_sent_over_secure_connection
|
302
305
|
raise Ably::Exceptions::InsecureRequestError, 'Cannot use Basic Auth over non-TLS connections' unless authentication_security_requirements_met?
|
@@ -308,6 +311,14 @@ module Ably
|
|
308
311
|
"Basic #{encode64("#{api_key}")}"
|
309
312
|
end
|
310
313
|
|
314
|
+
def split_api_key_into_key_and_secret!(options)
|
315
|
+
api_key_parts = options[:api_key].to_s.match(/(?<id>[\w_-]+\.[\w_-]+):(?<secret>[\w_-]+)/)
|
316
|
+
raise ArgumentError, 'api_key is invalid' unless api_key_parts
|
317
|
+
|
318
|
+
options[:key_id] = api_key_parts[:id].encode(Encoding::UTF_8)
|
319
|
+
options[:key_secret] = api_key_parts[:secret].encode(Encoding::UTF_8)
|
320
|
+
end
|
321
|
+
|
311
322
|
def token_auth_id
|
312
323
|
if token_id
|
313
324
|
token_id
|
@@ -431,8 +442,5 @@ module Ably
|
|
431
442
|
def api_key_present?
|
432
443
|
key_id && key_secret
|
433
444
|
end
|
434
|
-
|
435
|
-
private
|
436
|
-
attr_reader :client
|
437
445
|
end
|
438
446
|
end
|
@@ -44,9 +44,15 @@ module Ably
|
|
44
44
|
# Connection Timeout accessing Realtime or REST service
|
45
45
|
class ConnectionTimeoutError < ConnectionError; end
|
46
46
|
|
47
|
+
# Connection closed unexpectedly
|
48
|
+
class ConnectionClosedError < ConnectionError; end
|
49
|
+
|
47
50
|
# Invalid State Change error on a {https://github.com/gocardless/statesman Statesman State Machine}
|
48
51
|
class StateChangeError < BaseAblyException; end
|
49
52
|
|
53
|
+
# The state of the object is not suitable for this operation
|
54
|
+
class IncompatibleStateForOperation < BaseAblyException; end
|
55
|
+
|
50
56
|
# A generic Ably exception taht supports a status & code.
|
51
57
|
# See https://github.com/ably/ably-common/blob/master/protocol/errors.json for a list of Ably errors
|
52
58
|
class Standard < BaseAblyException; end
|
@@ -65,5 +71,8 @@ module Ably
|
|
65
71
|
|
66
72
|
# The token request could not be created
|
67
73
|
class TokenRequestError < BaseAblyException; end
|
74
|
+
|
75
|
+
# The message could not be delivered to the server
|
76
|
+
class MessageDeliveryError < BaseAblyException; end
|
68
77
|
end
|
69
78
|
end
|
@@ -2,17 +2,17 @@ module Ably::Models
|
|
2
2
|
# Convert messsage argument to a {Message} object and associate with a protocol message if provided
|
3
3
|
#
|
4
4
|
# @param message [Message,Hash] A message object or Hash of message properties
|
5
|
-
# @param
|
5
|
+
# @param [Hash] options (see Message#initialize)
|
6
6
|
#
|
7
7
|
# @return [Message]
|
8
|
-
def self.Message(message,
|
8
|
+
def self.Message(message, options = {})
|
9
9
|
case message
|
10
10
|
when Message
|
11
11
|
message.tap do
|
12
|
-
message.assign_to_protocol_message protocol_message
|
12
|
+
message.assign_to_protocol_message options[:protocol_message] if options[:protocol_message]
|
13
13
|
end
|
14
14
|
else
|
15
|
-
Message.new(message,
|
15
|
+
Message.new(message, options)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -41,15 +41,18 @@ module Ably::Models
|
|
41
41
|
include Ably::Modules::Conversions
|
42
42
|
include Ably::Modules::Encodeable
|
43
43
|
include Ably::Modules::ModelCommon
|
44
|
-
include
|
44
|
+
include Ably::Modules::SafeDeferrable if defined?(Ably::Realtime)
|
45
45
|
|
46
46
|
# {Message} initializer
|
47
47
|
#
|
48
|
-
# @param
|
49
|
-
# @param
|
48
|
+
# @param hash_object [Hash] object with the underlying message details
|
49
|
+
# @param [Hash] options an options Hash for this initializer
|
50
|
+
# @option options [ProtocolMessage] :protocol_message An optional protocol message to assocate the presence message with
|
51
|
+
# @option options [Logger] :logger An optional Logger to be used by {Ably::Modules::SafeDeferrable} if an exception is caught in a callback
|
50
52
|
#
|
51
|
-
def initialize(hash_object,
|
52
|
-
@
|
53
|
+
def initialize(hash_object, options = {})
|
54
|
+
@logger = options[:logger] # Logger expected for SafeDeferrable
|
55
|
+
@protocol_message = options[:protocol_message]
|
53
56
|
@raw_hash_object = hash_object
|
54
57
|
|
55
58
|
set_hash_object hash_object
|
@@ -128,5 +131,10 @@ module Ably::Models
|
|
128
131
|
def set_hash_object(hash)
|
129
132
|
@hash_object = IdiomaticRubyWrapper(hash.clone.freeze, stop_at: [:data])
|
130
133
|
end
|
134
|
+
|
135
|
+
def logger
|
136
|
+
return logger if logger
|
137
|
+
protocol_message.logger if protocol_message
|
138
|
+
end
|
131
139
|
end
|
132
140
|
end
|
@@ -31,23 +31,23 @@ module Ably::Models
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# Retrieve the first page of results.
|
34
|
-
# When used as part of the {Ably::Realtime} library, it will return a {
|
34
|
+
# When used as part of the {Ably::Realtime} library, it will return a {Ably::Util::SafeDeferrable} object,
|
35
35
|
# and allows an optional success callback block to be provided.
|
36
36
|
#
|
37
|
-
# @return [PaginatedResource,
|
37
|
+
# @return [PaginatedResource,Ably::Util::SafeDeferrable]
|
38
38
|
def first_page(&success_callback)
|
39
|
-
|
39
|
+
async_wrap_if_realtime(success_callback) do
|
40
40
|
PaginatedResource.new(client.get(pagination_url('first')), base_url, client, pagination_options, &each_block)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
# Retrieve the next page of results.
|
45
|
-
# When used as part of the {Ably::Realtime} library, it will return a {
|
45
|
+
# When used as part of the {Ably::Realtime} library, it will return a {Ably::Util::SafeDeferrable} object,
|
46
46
|
# and allows an optional success callback block to be provided.
|
47
47
|
#
|
48
|
-
# @return [PaginatedResource,
|
48
|
+
# @return [PaginatedResource,Ably::Util::SafeDeferrable]
|
49
49
|
def next_page(&success_callback)
|
50
|
-
|
50
|
+
async_wrap_if_realtime(success_callback) do
|
51
51
|
raise Ably::Exceptions::InvalidPageError, 'There are no more pages' if supports_pagination? && last_page?
|
52
52
|
PaginatedResource.new(client.get(pagination_url('next')), base_url, client, pagination_options, &each_block)
|
53
53
|
end
|
@@ -161,13 +161,17 @@ module Ably::Models
|
|
161
161
|
}
|
162
162
|
end
|
163
163
|
|
164
|
-
def
|
165
|
-
if
|
164
|
+
def async_wrap_if_realtime(success_callback, &operation)
|
165
|
+
if make_async
|
166
166
|
raise 'EventMachine is required for asynchronous operations' unless defined?(EventMachine)
|
167
167
|
async_wrap success_callback, &operation
|
168
168
|
else
|
169
169
|
yield
|
170
170
|
end
|
171
171
|
end
|
172
|
+
|
173
|
+
def logger
|
174
|
+
client.logger
|
175
|
+
end
|
172
176
|
end
|
173
177
|
end
|
@@ -2,17 +2,17 @@ module Ably::Models
|
|
2
2
|
# Convert presence_messsage argument to a {PresenceMessage} object and associate with a protocol message if provided
|
3
3
|
#
|
4
4
|
# @param presence_message [PresenceMessage,Hash] A presence message object or Hash of presence message properties
|
5
|
-
# @param
|
5
|
+
# @param [Hash] options (see PresenceMessage#initialize)
|
6
6
|
#
|
7
7
|
# @return [PresenceMessage]
|
8
|
-
def self.PresenceMessage(presence_message,
|
8
|
+
def self.PresenceMessage(presence_message, options = {})
|
9
9
|
case presence_message
|
10
10
|
when PresenceMessage
|
11
11
|
presence_message.tap do
|
12
|
-
presence_message.assign_to_protocol_message protocol_message
|
12
|
+
presence_message.assign_to_protocol_message options[:protocol_message] if options[:protocol_message]
|
13
13
|
end
|
14
14
|
else
|
15
|
-
PresenceMessage.new(presence_message,
|
15
|
+
PresenceMessage.new(presence_message, options)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -41,7 +41,7 @@ module Ably::Models
|
|
41
41
|
include Ably::Modules::Conversions
|
42
42
|
include Ably::Modules::Encodeable
|
43
43
|
include Ably::Modules::ModelCommon
|
44
|
-
include
|
44
|
+
include Ably::Modules::SafeDeferrable if defined?(Ably::Realtime)
|
45
45
|
extend Ably::Modules::Enum
|
46
46
|
|
47
47
|
ACTION = ruby_enum('ACTION',
|
@@ -52,13 +52,16 @@ module Ably::Models
|
|
52
52
|
:update
|
53
53
|
)
|
54
54
|
|
55
|
-
# {
|
55
|
+
# {PresenceMessage} initializer
|
56
56
|
#
|
57
|
-
# @param
|
58
|
-
# @param
|
57
|
+
# @param hash_object [Hash] object with the underlying presence message details
|
58
|
+
# @param [Hash] options an options Hash for this initializer
|
59
|
+
# @option options [ProtocolMessage] :protocol_message An optional protocol message to assocate the presence message with
|
60
|
+
# @option options [Logger] :logger An optional Logger to be used by {Ably::Modules::SafeDeferrable} if an exception is caught in a callback
|
59
61
|
#
|
60
|
-
def initialize(hash_object,
|
61
|
-
@
|
62
|
+
def initialize(hash_object, options = {})
|
63
|
+
@logger = options[:logger] # Logger expected for SafeDeferrable
|
64
|
+
@protocol_message = options[:protocol_message]
|
62
65
|
@raw_hash_object = hash_object
|
63
66
|
|
64
67
|
set_hash_object hash_object
|
@@ -143,5 +146,10 @@ module Ably::Models
|
|
143
146
|
def set_hash_object(hash)
|
144
147
|
@hash_object = IdiomaticRubyWrapper(hash.clone.freeze, stop_at: [:data])
|
145
148
|
end
|
149
|
+
|
150
|
+
def logger
|
151
|
+
return logger if logger
|
152
|
+
protocol_message.logger if protocol_message
|
153
|
+
end
|
146
154
|
end
|
147
155
|
end
|
@@ -37,7 +37,7 @@ module Ably::Models
|
|
37
37
|
#
|
38
38
|
class ProtocolMessage
|
39
39
|
include Ably::Modules::ModelCommon
|
40
|
-
include
|
40
|
+
include Ably::Modules::SafeDeferrable if defined?(Ably::Realtime)
|
41
41
|
extend Ably::Modules::Enum
|
42
42
|
|
43
43
|
# Actions which are sent by the Ably Realtime API
|
@@ -69,7 +69,15 @@ module Ably::Models
|
|
69
69
|
[ACTION.Presence, ACTION.Message].include?(ACTION(for_action))
|
70
70
|
end
|
71
71
|
|
72
|
-
|
72
|
+
# {ProtocolMessage} initializer
|
73
|
+
#
|
74
|
+
# @param hash_object [Hash] object with the underlying protocol message data
|
75
|
+
# @param [Hash] options an options Hash for this initializer
|
76
|
+
# @option options [Logger] :logger An optional Logger to be used by {Ably::Modules::SafeDeferrable} if an exception is caught in a callback
|
77
|
+
#
|
78
|
+
def initialize(hash_object, options = {})
|
79
|
+
@logger = options[:logger] # Logger expected for SafeDeferrable
|
80
|
+
|
73
81
|
@raw_hash_object = hash_object
|
74
82
|
@hash_object = IdiomaticRubyWrapper(@raw_hash_object.clone)
|
75
83
|
|
@@ -147,7 +155,7 @@ module Ably::Models
|
|
147
155
|
def messages
|
148
156
|
@messages ||=
|
149
157
|
Array(hash[:messages]).map do |message|
|
150
|
-
Ably::Models.Message(message, self)
|
158
|
+
Ably::Models.Message(message, protocol_message: self)
|
151
159
|
end
|
152
160
|
end
|
153
161
|
|
@@ -158,7 +166,7 @@ module Ably::Models
|
|
158
166
|
def presence
|
159
167
|
@presence ||=
|
160
168
|
Array(hash[:presence]).map do |message|
|
161
|
-
Ably::Models.PresenceMessage(message, self)
|
169
|
+
Ably::Models.PresenceMessage(message, protocol_message: self)
|
162
170
|
end
|
163
171
|
end
|
164
172
|
|
@@ -206,5 +214,8 @@ module Ably::Models
|
|
206
214
|
action_enum = action rescue nil
|
207
215
|
!action_enum || (ack_required? && !has_serial?)
|
208
216
|
end
|
217
|
+
|
218
|
+
private
|
219
|
+
attr_reader :logger
|
209
220
|
end
|
210
221
|
end
|
@@ -7,6 +7,7 @@ module Ably::Modules
|
|
7
7
|
#
|
8
8
|
# @note using this AsyncWrapper should only be used for methods that are used less frequently and typically
|
9
9
|
# not run with levels of concurrency due to the limited number of threads available to EventMachine by default.
|
10
|
+
# This module requires that the method #logger is defined.
|
10
11
|
#
|
11
12
|
# @example
|
12
13
|
# class BlockingOperation
|
@@ -32,15 +33,15 @@ module Ably::Modules
|
|
32
33
|
module AsyncWrapper
|
33
34
|
private
|
34
35
|
|
35
|
-
# Will yield the provided block in a new thread and return an {
|
36
|
+
# Will yield the provided block in a new thread and return an {Ably::Util::SafeDeferrable}
|
36
37
|
#
|
37
38
|
# @yield [Object] operation block that is run in a thread
|
38
|
-
# @return [
|
39
|
+
# @return [Ably::Util::SafeDeferrable]
|
39
40
|
#
|
40
41
|
def async_wrap(success_callback = nil)
|
41
42
|
raise ArgumentError, 'Block required' unless block_given?
|
42
43
|
|
43
|
-
|
44
|
+
Ably::Util::SafeDeferrable.new(logger).tap do |deferrable|
|
44
45
|
deferrable.callback &success_callback if success_callback
|
45
46
|
|
46
47
|
operation_with_exception_handling = proc do
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ably/modules/safe_yield'
|
2
|
+
|
1
3
|
module Ably
|
2
4
|
module Modules
|
3
5
|
# EventEmitter provides methods to attach to public events and trigger events on any class instance
|
@@ -5,6 +7,8 @@ module Ably
|
|
5
7
|
# EventEmitter are typically used for public interfaces, and as such, may be overriden in
|
6
8
|
# the classes to enforce `event` names match expected values.
|
7
9
|
#
|
10
|
+
# @note This module requires that the method #logger is defined.
|
11
|
+
#
|
8
12
|
# @example
|
9
13
|
# class Example
|
10
14
|
# include Modules::EventEmitter
|
@@ -16,6 +20,8 @@ module Ably
|
|
16
20
|
# #=> "Signal Test received"
|
17
21
|
#
|
18
22
|
module EventEmitter
|
23
|
+
include Ably::Modules::SafeYield
|
24
|
+
|
19
25
|
module ClassMethods
|
20
26
|
attr_reader :event_emitter_coerce_proc
|
21
27
|
|
@@ -49,6 +55,15 @@ module Ably
|
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
58
|
+
# Equivalent of {#on} but any exception raised in a block will bubble up and cause this client library to fail.
|
59
|
+
# This method should only be used internally by the client library.
|
60
|
+
# @api private
|
61
|
+
def unsafe_on(*event_names, &block)
|
62
|
+
event_names.each do |event_name|
|
63
|
+
callbacks[callbacks_event_coerced(event_name)] << proc_for_block(block, unsafe: true)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
52
67
|
# On receiving an event maching the event_name, call the provided block only once and remove the registered callback
|
53
68
|
#
|
54
69
|
# @param [Array<String>] event_names event name
|
@@ -60,12 +75,25 @@ module Ably
|
|
60
75
|
end
|
61
76
|
end
|
62
77
|
|
78
|
+
# Equivalent of {#once} but any exception raised in a block will bubble up and cause this client library to fail.
|
79
|
+
# This method should only be used internally by the client library.
|
80
|
+
# @api private
|
81
|
+
def unsafe_once(*event_names, &block)
|
82
|
+
event_names.each do |event_name|
|
83
|
+
callbacks[callbacks_event_coerced(event_name)] << proc_for_block(block, delete_once_run: true, unsafe: true)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
63
87
|
# Trigger an event with event_name that will in turn call all matching callbacks setup with `on`
|
64
88
|
def trigger(event_name, *args)
|
65
89
|
callbacks[callbacks_event_coerced(event_name)].
|
66
90
|
clone.
|
67
91
|
select do |proc_hash|
|
68
|
-
proc_hash[:
|
92
|
+
if proc_hash[:unsafe]
|
93
|
+
proc_hash[:trigger_proc].call *args
|
94
|
+
else
|
95
|
+
safe_yield proc_hash[:trigger_proc], *args
|
96
|
+
end
|
69
97
|
end.each do |callback|
|
70
98
|
callbacks[callbacks_event_coerced(event_name)].delete callback
|
71
99
|
end
|
@@ -108,7 +136,8 @@ module Ably
|
|
108
136
|
block.call *args
|
109
137
|
true if options[:delete_once_run]
|
110
138
|
end,
|
111
|
-
block: block
|
139
|
+
block: block,
|
140
|
+
unsafe: options[:unsafe]
|
112
141
|
}
|
113
142
|
end
|
114
143
|
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'ably/util/pub_sub'
|
2
|
+
|
3
|
+
module Ably::Modules
|
4
|
+
# Message emitter, subscriber and unsubscriber (Pub/Sub) functionality common to Channels and Presence
|
5
|
+
# In addition to standard Pub/Sub functionality, it allows subscribers to subscribe to :all.
|
6
|
+
module MessageEmitter
|
7
|
+
# Subscribe to events on this object
|
8
|
+
#
|
9
|
+
# @param name [String,Symbol] Optional, the event name to subscribe to. Defaults to `:all` events
|
10
|
+
# @yield [Object] For each event, the provided block is called with the event payload object
|
11
|
+
#
|
12
|
+
# @return [void]
|
13
|
+
#
|
14
|
+
def subscribe(*names, &callback)
|
15
|
+
raise ArgumentError, 'Block required to subscribe to events' unless block_given?
|
16
|
+
names = :all unless names && !names.empty?
|
17
|
+
Array(names).uniq.each do |name|
|
18
|
+
message_emitter_subscriptions[message_emitter_subscriptions_message_name_key(name)] << callback
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Unsubscribe the matching block for events on the this object.
|
23
|
+
# If a block is not provided, all subscriptions will be unsubscribed
|
24
|
+
#
|
25
|
+
# @param name [String,Symbol] Optional, the event name to unsubscribe from. Defaults to `:all` events
|
26
|
+
#
|
27
|
+
# @return [void]
|
28
|
+
#
|
29
|
+
def unsubscribe(*names, &callback)
|
30
|
+
names = :all unless names && !names.empty?
|
31
|
+
Array(names).each do |name|
|
32
|
+
if name == :all
|
33
|
+
message_emitter_subscriptions.keys
|
34
|
+
else
|
35
|
+
Array(message_emitter_subscriptions_message_name_key(name))
|
36
|
+
end.each do |key|
|
37
|
+
message_emitter_subscriptions[key].delete_if do |block|
|
38
|
+
!block_given? || callback == block
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Emit a message to message subscribers
|
45
|
+
#
|
46
|
+
# param name [String,Symbol] the event name
|
47
|
+
# param payload [Object] the event object to emit
|
48
|
+
#
|
49
|
+
# @return [void]
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
def emit_message(name, payload)
|
53
|
+
raise 'Event name is required' unless name
|
54
|
+
|
55
|
+
message_emitter_subscriptions[:all].each { |cb| cb.call(payload) }
|
56
|
+
message_emitter_subscriptions[name].each { |cb| cb.call(payload) }
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def message_emitter_subscriptions
|
61
|
+
@message_emitter_subscriptions ||= Hash.new { |hash, key| hash[key] = [] }
|
62
|
+
end
|
63
|
+
|
64
|
+
def message_emitter_subscriptions_message_name_key(name)
|
65
|
+
if name == :all
|
66
|
+
:all
|
67
|
+
else
|
68
|
+
message_emitter_subscriptions_coerce_message_key(name)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# this method can be overwritten easily to enforce use of set key types§
|
73
|
+
def message_emitter_subscriptions_coerce_message_key(name)
|
74
|
+
name.to_s
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|