ably 0.6.2 → 0.7.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.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.ruby-version.old +1 -0
- data/.travis.yml +0 -2
- data/Rakefile +22 -4
- data/SPEC.md +1676 -0
- data/ably.gemspec +1 -1
- data/lib/ably.rb +0 -8
- data/lib/ably/auth.rb +54 -46
- data/lib/ably/exceptions.rb +19 -5
- data/lib/ably/logger.rb +1 -1
- data/lib/ably/models/error_info.rb +1 -1
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +11 -9
- data/lib/ably/models/message.rb +15 -12
- data/lib/ably/models/message_encoders/base.rb +6 -5
- data/lib/ably/models/message_encoders/base64.rb +1 -0
- data/lib/ably/models/message_encoders/cipher.rb +6 -3
- data/lib/ably/models/message_encoders/json.rb +1 -0
- data/lib/ably/models/message_encoders/utf8.rb +2 -9
- data/lib/ably/models/nil_logger.rb +20 -0
- data/lib/ably/models/paginated_resource.rb +5 -2
- data/lib/ably/models/presence_message.rb +21 -12
- data/lib/ably/models/protocol_message.rb +22 -6
- data/lib/ably/modules/ably.rb +11 -0
- data/lib/ably/modules/async_wrapper.rb +2 -0
- data/lib/ably/modules/conversions.rb +23 -3
- data/lib/ably/modules/encodeable.rb +2 -1
- data/lib/ably/modules/enum.rb +2 -0
- data/lib/ably/modules/event_emitter.rb +7 -1
- data/lib/ably/modules/event_machine_helpers.rb +2 -0
- data/lib/ably/modules/http_helpers.rb +2 -0
- data/lib/ably/modules/model_common.rb +12 -2
- data/lib/ably/modules/state_emitter.rb +76 -0
- data/lib/ably/modules/state_machine.rb +53 -0
- data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/ably/modules/uses_state_machine.rb +74 -0
- data/lib/ably/realtime.rb +4 -2
- data/lib/ably/realtime/channel.rb +51 -58
- data/lib/ably/realtime/channel/channel_manager.rb +91 -0
- data/lib/ably/realtime/channel/channel_state_machine.rb +68 -0
- data/lib/ably/realtime/client.rb +70 -26
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +31 -13
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
- data/lib/ably/realtime/connection.rb +135 -92
- data/lib/ably/realtime/connection/connection_manager.rb +216 -33
- data/lib/ably/realtime/connection/connection_state_machine.rb +30 -73
- data/lib/ably/realtime/models/nil_channel.rb +10 -1
- data/lib/ably/realtime/presence.rb +336 -92
- data/lib/ably/rest.rb +2 -2
- data/lib/ably/rest/channel.rb +13 -4
- data/lib/ably/rest/client.rb +138 -38
- data/lib/ably/rest/middleware/logger.rb +24 -3
- data/lib/ably/rest/presence.rb +12 -7
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +101 -85
- data/spec/acceptance/realtime/channel_spec.rb +461 -120
- data/spec/acceptance/realtime/client_spec.rb +119 -0
- data/spec/acceptance/realtime/connection_failures_spec.rb +499 -0
- data/spec/acceptance/realtime/connection_spec.rb +571 -97
- data/spec/acceptance/realtime/message_spec.rb +347 -333
- data/spec/acceptance/realtime/presence_history_spec.rb +35 -40
- data/spec/acceptance/realtime/presence_spec.rb +769 -239
- data/spec/acceptance/realtime/stats_spec.rb +14 -22
- data/spec/acceptance/realtime/time_spec.rb +16 -20
- data/spec/acceptance/rest/auth_spec.rb +425 -364
- data/spec/acceptance/rest/base_spec.rb +108 -176
- data/spec/acceptance/rest/channel_spec.rb +89 -89
- data/spec/acceptance/rest/channels_spec.rb +30 -32
- data/spec/acceptance/rest/client_spec.rb +273 -0
- data/spec/acceptance/rest/encoders_spec.rb +185 -0
- data/spec/acceptance/rest/message_spec.rb +186 -163
- data/spec/acceptance/rest/presence_spec.rb +150 -111
- data/spec/acceptance/rest/stats_spec.rb +45 -40
- data/spec/acceptance/rest/time_spec.rb +8 -10
- data/spec/rspec_config.rb +10 -1
- data/spec/shared/client_initializer_behaviour.rb +212 -0
- data/spec/{support/model_helper.rb → shared/model_behaviour.rb} +6 -6
- data/spec/{support/protocol_msgbus_helper.rb → shared/protocol_msgbus_behaviour.rb} +1 -1
- data/spec/spec_helper.rb +9 -0
- data/spec/support/api_helper.rb +11 -0
- data/spec/support/event_machine_helper.rb +101 -3
- data/spec/support/markdown_spec_formatter.rb +90 -0
- data/spec/support/private_api_formatter.rb +36 -0
- data/spec/support/protocol_helper.rb +32 -0
- data/spec/support/random_helper.rb +15 -0
- data/spec/support/test_app.rb +4 -0
- data/spec/unit/auth_spec.rb +68 -0
- data/spec/unit/logger_spec.rb +77 -66
- data/spec/unit/models/error_info_spec.rb +1 -1
- data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +2 -3
- data/spec/unit/models/message_encoders/base64_spec.rb +2 -2
- data/spec/unit/models/message_encoders/cipher_spec.rb +2 -2
- data/spec/unit/models/message_encoders/utf8_spec.rb +2 -46
- data/spec/unit/models/message_spec.rb +160 -15
- data/spec/unit/models/paginated_resource_spec.rb +29 -27
- data/spec/unit/models/presence_message_spec.rb +163 -20
- data/spec/unit/models/protocol_message_spec.rb +43 -8
- data/spec/unit/modules/async_wrapper_spec.rb +2 -3
- data/spec/unit/modules/conversions_spec.rb +1 -1
- data/spec/unit/modules/enum_spec.rb +2 -3
- data/spec/unit/modules/event_emitter_spec.rb +62 -5
- data/spec/unit/modules/state_emitter_spec.rb +283 -0
- data/spec/unit/realtime/channel_spec.rb +107 -2
- data/spec/unit/realtime/channels_spec.rb +1 -0
- data/spec/unit/realtime/client_spec.rb +8 -48
- data/spec/unit/realtime/connection_spec.rb +3 -3
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +2 -2
- data/spec/unit/realtime/presence_spec.rb +13 -4
- data/spec/unit/realtime/realtime_spec.rb +0 -11
- data/spec/unit/realtime/websocket_transport_spec.rb +2 -2
- data/spec/unit/rest/channel_spec.rb +109 -0
- data/spec/unit/rest/channels_spec.rb +4 -3
- data/spec/unit/rest/client_spec.rb +30 -125
- data/spec/unit/rest/rest_spec.rb +10 -0
- data/spec/unit/util/crypto_spec.rb +10 -5
- data/spec/unit/util/pub_sub_spec.rb +5 -5
- metadata +44 -12
- data/spec/integration/modules/state_emitter_spec.rb +0 -80
- data/spec/integration/rest/auth.rb +0 -9
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'ably/models/message_encoders/base'
|
2
2
|
|
3
3
|
module Ably::Models::MessageEncoders
|
4
4
|
# Utf8 Encoder and Decoder
|
@@ -8,10 +8,7 @@ module Ably::Models::MessageEncoders
|
|
8
8
|
ENCODING_ID = 'utf-8'
|
9
9
|
|
10
10
|
def encode(message, channel_options)
|
11
|
-
|
12
|
-
message[:data] = message[:data].force_encoding(Encoding::UTF_8)
|
13
|
-
add_encoding_to_message ENCODING_ID, message
|
14
|
-
end
|
11
|
+
# no encoding of UTF-8 required
|
15
12
|
end
|
16
13
|
|
17
14
|
def decode(message, channel_options)
|
@@ -25,9 +22,5 @@ module Ably::Models::MessageEncoders
|
|
25
22
|
def is_utf8_encoded?(message)
|
26
23
|
current_encoding_part(message).to_s.match(/^#{ENCODING_ID}$/i)
|
27
24
|
end
|
28
|
-
|
29
|
-
def is_json_encoded?(message)
|
30
|
-
current_encoding_part(message).to_s.match(/^json$/i)
|
31
|
-
end
|
32
25
|
end
|
33
26
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Ably::Models
|
2
|
+
# When Log Level set to none, this NilLogger is used to silence all logging
|
3
|
+
# NilLogger provides a Ruby Logger compatible interface
|
4
|
+
class NilLogger
|
5
|
+
def null_method(*args)
|
6
|
+
end
|
7
|
+
|
8
|
+
def level
|
9
|
+
:none
|
10
|
+
end
|
11
|
+
|
12
|
+
def level=(value)
|
13
|
+
level
|
14
|
+
end
|
15
|
+
|
16
|
+
[:fatal, :error, :warn, :info, :debug].each do |method|
|
17
|
+
alias_method method, :null_method
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -5,7 +5,7 @@ module Ably::Models
|
|
5
5
|
# Paging information is provided by Ably in the LINK HTTP headers
|
6
6
|
class PaginatedResource
|
7
7
|
include Enumerable
|
8
|
-
include Ably::Modules::AsyncWrapper
|
8
|
+
include Ably::Modules::AsyncWrapper if defined?(EventMachine)
|
9
9
|
|
10
10
|
# @param [Faraday::Response] http_response Initial HTTP response from an Ably request to a paged resource
|
11
11
|
# @param [String] base_url Base URL for request that generated the http_response so that subsequent paged requests can be made
|
@@ -27,7 +27,9 @@ module Ably::Models
|
|
27
27
|
|
28
28
|
@body = if @coerce_into
|
29
29
|
http_response.body.map do |item|
|
30
|
-
|
30
|
+
@coerce_into.split('::').inject(Kernel) do |base, klass_name|
|
31
|
+
base.public_send(:const_get, klass_name)
|
32
|
+
end.new(item)
|
31
33
|
end
|
32
34
|
else
|
33
35
|
http_response.body
|
@@ -168,6 +170,7 @@ module Ably::Models
|
|
168
170
|
|
169
171
|
def async_wrap_if(is_realtime, success_callback, &operation)
|
170
172
|
if is_realtime
|
173
|
+
raise 'EventMachine is required for asynchronous operations' unless defined?(EventMachine)
|
171
174
|
async_wrap success_callback, &operation
|
172
175
|
else
|
173
176
|
yield
|
@@ -23,8 +23,10 @@ module Ably::Models
|
|
23
23
|
# @return [STATE] the state change event signified by a PresenceMessage
|
24
24
|
# @!attribute [r] client_id
|
25
25
|
# @return [String] The client_id associated with this presence state
|
26
|
-
# @!attribute [r]
|
26
|
+
# @!attribute [r] connection_id
|
27
27
|
# @return [String] A unique member identifier, disambiguating situations where a given client_id is present on multiple connections simultaneously
|
28
|
+
# @!attribute [r] member_key
|
29
|
+
# @return [String] A unique connection and client_id identifier ensuring multiple connected clients with the same client_id are unique
|
28
30
|
# @!attribute [r] data
|
29
31
|
# @return [Object] Optional client-defined status or other event payload associated with this state
|
30
32
|
# @!attribute [r] encoding
|
@@ -36,12 +38,15 @@ module Ably::Models
|
|
36
38
|
# @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
|
37
39
|
#
|
38
40
|
class PresenceMessage
|
39
|
-
include Ably::Modules::
|
41
|
+
include Ably::Modules::Conversions
|
40
42
|
include Ably::Modules::Encodeable
|
43
|
+
include Ably::Modules::ModelCommon
|
41
44
|
include EventMachine::Deferrable
|
42
45
|
extend Ably::Modules::Enum
|
43
46
|
|
44
47
|
ACTION = ruby_enum('ACTION',
|
48
|
+
:absent,
|
49
|
+
:present,
|
45
50
|
:enter,
|
46
51
|
:leave,
|
47
52
|
:update
|
@@ -57,16 +62,28 @@ module Ably::Models
|
|
57
62
|
@raw_hash_object = hash_object
|
58
63
|
|
59
64
|
set_hash_object hash_object
|
65
|
+
|
66
|
+
ensure_utf_8 :client_id, client_id, allow_nil: true
|
67
|
+
ensure_utf_8 :connection_id, connection_id, allow_nil: true
|
68
|
+
ensure_utf_8 :encoding, encoding, allow_nil: true
|
60
69
|
end
|
61
70
|
|
62
|
-
%w( client_id
|
71
|
+
%w( client_id data encoding ).each do |attribute|
|
63
72
|
define_method attribute do
|
64
73
|
hash[attribute.to_sym]
|
65
74
|
end
|
66
75
|
end
|
67
76
|
|
68
77
|
def id
|
69
|
-
hash
|
78
|
+
hash.fetch(:id) { "#{protocol_message.id!}:#{protocol_message_index}" }
|
79
|
+
end
|
80
|
+
|
81
|
+
def connection_id
|
82
|
+
hash.fetch(:connection_id) { protocol_message.connection_id if assigned_to_protocol_message? }
|
83
|
+
end
|
84
|
+
|
85
|
+
def member_key
|
86
|
+
"#{connection_id}:#{client_id}"
|
70
87
|
end
|
71
88
|
|
72
89
|
def timestamp
|
@@ -123,14 +140,6 @@ module Ably::Models
|
|
123
140
|
protocol_message.presence.index(self)
|
124
141
|
end
|
125
142
|
|
126
|
-
def connection_id
|
127
|
-
protocol_message.connection_id
|
128
|
-
end
|
129
|
-
|
130
|
-
def message_serial
|
131
|
-
protocol_message.message_serial
|
132
|
-
end
|
133
|
-
|
134
143
|
def set_hash_object(hash)
|
135
144
|
@hash_object = IdiomaticRubyWrapper(hash.clone.freeze, stop_at: [:data])
|
136
145
|
end
|
@@ -7,7 +7,7 @@ module Ably::Models
|
|
7
7
|
# for further details on the members of a ProtocolMessage
|
8
8
|
#
|
9
9
|
# @!attribute [r] action
|
10
|
-
# @return [ACTION] Protocol Message action {Ably::Modules::Enum} from list of {ACTION}. Returns nil if action is unsupported by protocol
|
10
|
+
# @return [ACTION] Protocol Message action {Ably::Modules::Enum} from list of {ACTION}. Returns nil if action is unsupported by protocol
|
11
11
|
# @!attribute [r] count
|
12
12
|
# @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
13
|
# @!attribute [r] error_info
|
@@ -17,7 +17,9 @@ module Ably::Models
|
|
17
17
|
# @!attribute [r] channel_serial
|
18
18
|
# @return [String] Contains a serial number for a message on the current channel
|
19
19
|
# @!attribute [r] connection_id
|
20
|
-
# @return [String] Contains a string connection
|
20
|
+
# @return [String] Contains a string public identifier for the connection
|
21
|
+
# @!attribute [r] connection_key
|
22
|
+
# @return [String] Contains a string private connection key used to recover this connection
|
21
23
|
# @!attribute [r] connection_serial
|
22
24
|
# @return [Bignum] Contains a serial number for a message sent from the server to the client
|
23
25
|
# @!attribute [r] message_serial
|
@@ -25,9 +27,11 @@ module Ably::Models
|
|
25
27
|
# @!attribute [r] timestamp
|
26
28
|
# @return [Time] An optional timestamp, applied by the service in messages sent to the client, to indicate the system time at which the message was sent (milliseconds past epoch)
|
27
29
|
# @!attribute [r] messages
|
28
|
-
# @return [Message] A {ProtocolMessage} with a `:message` action contains one or more messages belonging to a channel
|
30
|
+
# @return [Message] A {ProtocolMessage} with a `:message` action contains one or more messages belonging to a channel
|
29
31
|
# @!attribute [r] presence
|
30
|
-
# @return [PresenceMessage] A {ProtocolMessage} with a `:presence` action contains one or more presence updates belonging to a channel
|
32
|
+
# @return [PresenceMessage] A {ProtocolMessage} with a `:presence` action contains one or more presence updates belonging to a channel
|
33
|
+
# @!attribute [r] flags
|
34
|
+
# @return [Integer] Flags inidicating special ProtocolMessage states
|
31
35
|
# @!attribute [r] hash
|
32
36
|
# @return [Hash] Access the protocol message Hash object ruby'fied to use symbolized keys
|
33
37
|
#
|
@@ -56,7 +60,8 @@ module Ably::Models
|
|
56
60
|
detach: 12,
|
57
61
|
detached: 13,
|
58
62
|
presence: 14,
|
59
|
-
message: 15
|
63
|
+
message: 15,
|
64
|
+
sync: 16
|
60
65
|
)
|
61
66
|
|
62
67
|
# Indicates this protocol message action will generate an ACK response such as :message or :presence
|
@@ -74,7 +79,7 @@ module Ably::Models
|
|
74
79
|
@hash_object.freeze
|
75
80
|
end
|
76
81
|
|
77
|
-
%w(
|
82
|
+
%w(id channel channel_serial connection_id connection_key).each do |attribute|
|
78
83
|
define_method attribute do
|
79
84
|
hash[attribute.to_sym]
|
80
85
|
end
|
@@ -157,6 +162,17 @@ module Ably::Models
|
|
157
162
|
end
|
158
163
|
end
|
159
164
|
|
165
|
+
# Flags as bits
|
166
|
+
def flags
|
167
|
+
Integer(hash[:flags])
|
168
|
+
rescue TypeError
|
169
|
+
0
|
170
|
+
end
|
171
|
+
|
172
|
+
def has_presence_flag?
|
173
|
+
flags & 1 == 1
|
174
|
+
end
|
175
|
+
|
160
176
|
# Indicates this protocol message will generate an ACK response when sent
|
161
177
|
# Examples of protocol messages required ACK include :message and :presence
|
162
178
|
def ack_required?
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Ably is the base namespace for the Ably {Ably::Realtime Realtime} & {Ably::Rest Rest} client libraries.
|
2
|
+
#
|
3
|
+
# Please refer to the {file:README.md Readme} on getting started.
|
4
|
+
#
|
5
|
+
# @see file:README.md README
|
6
|
+
module Ably
|
7
|
+
# Fallback hosts to use when a connection to rest/realtime.ably.io is not possible due to
|
8
|
+
# network failures either at the client, between the client and Ably, within an Ably data center, or at the IO domain registrar
|
9
|
+
#
|
10
|
+
FALLBACK_HOSTS = %w(A.ably-realtime.com B.ably-realtime.com C.ably-realtime.com D.ably-realtime.com E.ably-realtime.com)
|
11
|
+
end
|
@@ -5,7 +5,9 @@ module Ably::Modules
|
|
5
5
|
extend self
|
6
6
|
|
7
7
|
private
|
8
|
-
def as_since_epoch(time,
|
8
|
+
def as_since_epoch(time, options = {})
|
9
|
+
granularity = options.fetch(:granularity, :ms)
|
10
|
+
|
9
11
|
case time
|
10
12
|
when Time
|
11
13
|
time.to_f * multiplier_from_granularity(granularity)
|
@@ -16,7 +18,9 @@ module Ably::Modules
|
|
16
18
|
end.to_i
|
17
19
|
end
|
18
20
|
|
19
|
-
def as_time_from_epoch(time,
|
21
|
+
def as_time_from_epoch(time, options = {})
|
22
|
+
granularity = options.fetch(:granularity, :ms)
|
23
|
+
|
20
24
|
case time
|
21
25
|
when Numeric
|
22
26
|
Time.at(time / multiplier_from_granularity(granularity))
|
@@ -39,7 +43,9 @@ module Ably::Modules
|
|
39
43
|
end
|
40
44
|
|
41
45
|
# Convert key to mixedCase from mixed_case
|
42
|
-
def convert_to_mixed_case(key,
|
46
|
+
def convert_to_mixed_case(key, options = {})
|
47
|
+
force_camel = options.fetch(:force_camel, false)
|
48
|
+
|
43
49
|
key.to_s.
|
44
50
|
split('_').
|
45
51
|
each_with_index.map do |str, index|
|
@@ -66,5 +72,19 @@ module Ably::Modules
|
|
66
72
|
def convert_to_lower_case(key)
|
67
73
|
key.to_s.gsub('_', '')
|
68
74
|
end
|
75
|
+
|
76
|
+
# Ensures that the string value is converted to UTF-8 encoding
|
77
|
+
# Unless option allow_nil: true, an {ArgumentError} is raised if the string_value is not a string
|
78
|
+
#
|
79
|
+
# @return <void>
|
80
|
+
#
|
81
|
+
def ensure_utf_8(field_name, string_value, options = {})
|
82
|
+
unless options[:allow_nil] && string_value.nil?
|
83
|
+
raise ArgumentError, "#{field_name} must be a String" unless string_value.kind_of?(String)
|
84
|
+
end
|
85
|
+
string_value.encode!(Encoding::UTF_8) if string_value
|
86
|
+
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError => e
|
87
|
+
raise ArgumentError, "#{field_name} could not be converted to UTF-8: #{e.message}"
|
88
|
+
end
|
69
89
|
end
|
70
90
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'base64'
|
2
|
+
require 'ably/exceptions'
|
2
3
|
|
3
4
|
module Ably::Modules
|
4
5
|
# Provides methods to allow this model's `data` property to be encoded and decoded based on the `encoding` property.
|
@@ -57,8 +58,8 @@ module Ably::Modules
|
|
57
58
|
|
58
59
|
set_hash_object message_hash
|
59
60
|
rescue Ably::Exceptions::CipherError => cipher_error
|
60
|
-
channel.client.logger.error "Encoder error #{cipher_error.code} trying to #{method} message: #{cipher_error.message}"
|
61
61
|
if channel.respond_to?(:trigger)
|
62
|
+
channel.client.logger.error "Encoder error #{cipher_error.code} trying to #{method} message: #{cipher_error.message}"
|
62
63
|
channel.trigger :error, cipher_error
|
63
64
|
else
|
64
65
|
raise cipher_error
|
data/lib/ably/modules/enum.rb
CHANGED
@@ -62,7 +62,13 @@ module Ably
|
|
62
62
|
|
63
63
|
# Trigger an event with event_name that will in turn call all matching callbacks setup with `on`
|
64
64
|
def trigger(event_name, *args)
|
65
|
-
callbacks[callbacks_event_coerced(event_name)].
|
65
|
+
callbacks[callbacks_event_coerced(event_name)].
|
66
|
+
clone.
|
67
|
+
select do |proc_hash|
|
68
|
+
proc_hash[:trigger_proc].call(*args)
|
69
|
+
end.each do |callback|
|
70
|
+
callbacks[callbacks_event_coerced(event_name)].delete callback
|
71
|
+
end
|
66
72
|
end
|
67
73
|
|
68
74
|
# Remove all callbacks for event_name.
|
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'base64'
|
2
|
+
require 'ably/modules/conversions'
|
3
|
+
require 'ably/modules/message_pack'
|
2
4
|
|
3
5
|
module Ably::Modules
|
4
6
|
# Common model functionality shared across many {Ably::Models}
|
5
7
|
module ModelCommon
|
6
|
-
include
|
7
|
-
include
|
8
|
+
include Conversions
|
9
|
+
include MessagePack
|
8
10
|
|
9
11
|
# Provide a normal Hash accessor to the underlying raw message object
|
10
12
|
#
|
@@ -27,5 +29,13 @@ module Ably::Modules
|
|
27
29
|
def to_json(*args)
|
28
30
|
as_json.to_json(*args)
|
29
31
|
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def ensure_utf8_string_for(attribute, value)
|
35
|
+
if value
|
36
|
+
raise ArgumentError, "#{attribute} must be a String" unless value.kind_of?(String)
|
37
|
+
raise ArgumentError, "#{attribute} cannot use ASCII_8BIT encoding, please use UTF_8 encoding" unless value.encoding == Encoding::UTF_8
|
38
|
+
end
|
39
|
+
end
|
30
40
|
end
|
31
41
|
end
|
@@ -26,6 +26,7 @@ module Ably::Modules
|
|
26
26
|
# connection.state = :invalid # raises an Exception as only a valid state can be defined
|
27
27
|
# connection.trigger :invalid # raises an Exception as only a valid state can be used for EventEmitter
|
28
28
|
# connection.change_state :connected # emits :connected event via EventEmitter, returns STATE.Connected
|
29
|
+
# connection.once_or_if(:connected) { puts 'block called once when state is connected or becomes connected' }
|
29
30
|
#
|
30
31
|
module StateEmitter
|
31
32
|
# Current state {Ably::Modules::Enum}
|
@@ -55,7 +56,82 @@ module Ably::Modules
|
|
55
56
|
end
|
56
57
|
alias_method :change_state, :state=
|
57
58
|
|
59
|
+
# If the current state matches the target_state argument the block is called immediately.
|
60
|
+
# Else the block is called once when the target_state is reached.
|
61
|
+
#
|
62
|
+
# If the option block :else is provided then if any state other than target_state is reached, the :else block is called,
|
63
|
+
# however only one of the blocks will ever be called
|
64
|
+
#
|
65
|
+
# @param [Symbol,Ably::Modules::Enum,Array] target_states a single state or array of states that once met, will fire the success block only once
|
66
|
+
# @param [Hash] options
|
67
|
+
# @option options [Proc] :else block called once the state has changed to anything but target_state
|
68
|
+
#
|
69
|
+
# @yield block is called if the state is matched immediately or once when the state is reached
|
70
|
+
#
|
71
|
+
# @return [void]
|
72
|
+
def once_or_if(target_states, options = {}, &success_block)
|
73
|
+
raise ArgumentError, 'Block is expected' unless block_given?
|
74
|
+
|
75
|
+
if Array(target_states).any? { |target_state| state == target_state }
|
76
|
+
success_block.call
|
77
|
+
else
|
78
|
+
failure_block = options.fetch(:else, nil)
|
79
|
+
failure_wrapper = nil
|
80
|
+
|
81
|
+
success_wrapper = Proc.new do
|
82
|
+
success_block.call
|
83
|
+
off &success_wrapper
|
84
|
+
off &failure_wrapper if failure_wrapper
|
85
|
+
end
|
86
|
+
|
87
|
+
failure_wrapper = proc do |*args|
|
88
|
+
failure_block.call *args
|
89
|
+
off &success_wrapper
|
90
|
+
off &failure_wrapper
|
91
|
+
end if failure_block
|
92
|
+
|
93
|
+
Array(target_states).each do |target_state|
|
94
|
+
once target_state, &success_wrapper
|
95
|
+
|
96
|
+
once_state_changed do |*args|
|
97
|
+
failure_wrapper.call *args unless state == target_state
|
98
|
+
end if failure_block
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Calls the block once when the state changes
|
104
|
+
#
|
105
|
+
# @yield block is called once the state changes
|
106
|
+
# @return [void]
|
107
|
+
#
|
108
|
+
# @api private
|
109
|
+
def once_state_changed(&block)
|
110
|
+
raise ArgumentError, 'Block is expected' unless block_given?
|
111
|
+
|
112
|
+
once_block = proc do |*args|
|
113
|
+
off *self.class::STATE.map, &once_block
|
114
|
+
yield *args
|
115
|
+
end
|
116
|
+
|
117
|
+
once *self.class::STATE.map, &once_block
|
118
|
+
end
|
119
|
+
|
58
120
|
private
|
121
|
+
|
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.
|
125
|
+
#
|
126
|
+
def deferrable_for_state_change_to(target_state, &success_block)
|
127
|
+
EventMachine::DefaultDeferrable.new.tap do |deferrable|
|
128
|
+
once_or_if(target_state, else: proc { |*args| deferrable.fail self, *args }) do
|
129
|
+
success_block.call self if block_given?
|
130
|
+
deferrable.succeed self
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
59
135
|
def self.included(klass)
|
60
136
|
klass.configure_event_emitter coerce_into: Proc.new { |event|
|
61
137
|
if event == :error
|