ably 0.1.4 → 0.1.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 +4 -4
- data/ably.gemspec +1 -0
- data/lib/ably/auth.rb +9 -13
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +27 -39
- data/lib/ably/modules/conversions.rb +31 -10
- data/lib/ably/modules/enum.rb +201 -0
- data/lib/ably/modules/event_emitter.rb +81 -0
- data/lib/ably/modules/event_machine_helpers.rb +21 -0
- data/lib/ably/modules/http_helpers.rb +13 -0
- data/lib/ably/modules/state.rb +67 -0
- data/lib/ably/realtime.rb +6 -1
- data/lib/ably/realtime/channel.rb +117 -56
- data/lib/ably/realtime/client.rb +7 -50
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +116 -0
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +63 -0
- data/lib/ably/realtime/connection.rb +97 -14
- data/lib/ably/realtime/models/error_info.rb +3 -2
- data/lib/ably/realtime/models/message.rb +28 -3
- data/lib/ably/realtime/models/nil_channel.rb +21 -0
- data/lib/ably/realtime/models/protocol_message.rb +35 -27
- data/lib/ably/rest/client.rb +39 -23
- data/lib/ably/rest/middleware/external_exceptions.rb +1 -1
- data/lib/ably/rest/middleware/parse_json.rb +7 -2
- data/lib/ably/rest/middleware/parse_message_pack.rb +23 -0
- data/lib/ably/rest/models/paged_resource.rb +4 -4
- data/lib/ably/util/pub_sub.rb +32 -0
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_spec.rb +1 -0
- data/spec/acceptance/realtime/message_spec.rb +136 -0
- data/spec/acceptance/rest/base_spec.rb +51 -1
- data/spec/acceptance/rest/presence_spec.rb +7 -2
- data/spec/integration/modules/state_spec.rb +66 -0
- data/spec/{unit → integration/rest}/auth.rb +0 -0
- data/spec/support/api_helper.rb +5 -2
- data/spec/support/protocol_msgbus_helper.rb +29 -0
- data/spec/support/test_app.rb +14 -3
- data/spec/unit/{conversions.rb → modules/conversions_spec.rb} +1 -1
- data/spec/unit/modules/enum_spec.rb +263 -0
- data/spec/unit/modules/event_emitter_spec.rb +81 -0
- data/spec/unit/modules/pub_sub_spec.rb +74 -0
- data/spec/unit/realtime/channel_spec.rb +27 -0
- data/spec/unit/realtime/client_spec.rb +8 -0
- data/spec/unit/realtime/connection_spec.rb +40 -0
- data/spec/unit/realtime/error_info_spec.rb +9 -1
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +36 -0
- data/spec/unit/realtime/message_spec.rb +2 -2
- data/spec/unit/realtime/protocol_message_spec.rb +78 -9
- data/spec/unit/rest/{rest_spec.rb → client_spec.rb} +0 -0
- data/spec/unit/rest/message_spec.rb +1 -1
- metadata +51 -9
- data/lib/ably/realtime/callbacks.rb +0 -15
@@ -0,0 +1,21 @@
|
|
1
|
+
module Ably::Modules
|
2
|
+
module EventMachineHelpers
|
3
|
+
private
|
4
|
+
|
5
|
+
# This method allows looped blocks to be run at the next EventMachine tick
|
6
|
+
# @example
|
7
|
+
# x = 0
|
8
|
+
# less_than_3 = -> { x < 3 }
|
9
|
+
# non_blocking_loop_while(less_than_3) do
|
10
|
+
# x += 1
|
11
|
+
# end
|
12
|
+
def non_blocking_loop_while(lambda, &execution_block)
|
13
|
+
if lambda.call
|
14
|
+
yield
|
15
|
+
EventMachine.next_tick do
|
16
|
+
non_blocking_loop_while(lambda, &execution_block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'base64'
|
2
2
|
|
3
|
+
require 'ably/rest/middleware/external_exceptions'
|
4
|
+
require 'ably/rest/middleware/parse_json'
|
5
|
+
require 'ably/rest/middleware/parse_message_pack'
|
6
|
+
|
3
7
|
module Ably::Modules
|
4
8
|
module HttpHelpers
|
5
9
|
protected
|
@@ -10,5 +14,14 @@ module Ably::Modules
|
|
10
14
|
def user_agent
|
11
15
|
"Ably Ruby client #{Ably::VERSION} (https://ably.io)"
|
12
16
|
end
|
17
|
+
|
18
|
+
def setup_middleware(builder)
|
19
|
+
# Convert request params to "www-form-urlencoded"
|
20
|
+
builder.use Faraday::Request::UrlEncoded
|
21
|
+
|
22
|
+
# Parse JSON / MsgPack response bodies. ParseJson must be first (default) parsing middleware
|
23
|
+
builder.use Ably::Rest::Middleware::ParseJson
|
24
|
+
builder.use Ably::Rest::Middleware::ParseMessagePack
|
25
|
+
end
|
13
26
|
end
|
14
27
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Ably::Modules
|
2
|
+
# State module adds a set of generic state related methods to a class on the assumption that
|
3
|
+
# the instance variable @state is used exclusively, the {Enum} STATE is defined prior to inclusion of this
|
4
|
+
# module, and the class is an {EventEmitter}
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class Connection
|
8
|
+
# include Ably::Modules::EventEmitter
|
9
|
+
# extend Ably::Modules::Enum
|
10
|
+
# STATE = ruby_enum('STATE',
|
11
|
+
# :initialized,
|
12
|
+
# :connecting,
|
13
|
+
# :connected
|
14
|
+
# )
|
15
|
+
# include Ably::Modules::State
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# connection = Connection.new
|
19
|
+
# connection.state = :connecting # emits :connecting event via EventEmitter, returns STATE.Connecting
|
20
|
+
# connection.state?(:connected) # => false
|
21
|
+
# connection.connecting? # => true
|
22
|
+
# connection.state # => STATE.Connecting
|
23
|
+
# connection.state = :invalid # raises an Exception as only a valid state can be defined
|
24
|
+
# connection.trigger :invalid # raises an Exception as only a valid state can be used for EventEmitter
|
25
|
+
# connection.change_state :connected # emits :connected event via EventEmitter, returns STATE.Connected
|
26
|
+
#
|
27
|
+
module State
|
28
|
+
# Current state {Ably::Modules::Enum}
|
29
|
+
#
|
30
|
+
# @return [Symbol] state
|
31
|
+
def state
|
32
|
+
STATE(@state)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Evaluates if check_state matches current state
|
36
|
+
#
|
37
|
+
# @return [Boolean]
|
38
|
+
def state?(check_state)
|
39
|
+
state == check_state
|
40
|
+
end
|
41
|
+
|
42
|
+
# Set the current state {Ably::Modules::Enum}
|
43
|
+
#
|
44
|
+
# @return [Symbol] new state
|
45
|
+
def state=(new_state)
|
46
|
+
if state != new_state
|
47
|
+
logger.debug("#{self.class}: State changed from #{state} => #{new_state}") if respond_to?(:logger, true)
|
48
|
+
@state = STATE(new_state)
|
49
|
+
trigger @state
|
50
|
+
end
|
51
|
+
end
|
52
|
+
alias_method :change_state, :state=
|
53
|
+
|
54
|
+
private
|
55
|
+
def self.included(klass)
|
56
|
+
klass.configure_event_emitter coerce_into: Proc.new { |event| klass::STATE(event) }
|
57
|
+
|
58
|
+
klass::STATE.each do |state_predicate|
|
59
|
+
klass.instance_eval do
|
60
|
+
define_method("#{state_predicate.to_sym}?") do
|
61
|
+
state?(state_predicate)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/ably/realtime.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require "eventmachine"
|
2
2
|
require "websocket/driver"
|
3
3
|
|
4
|
-
require "ably/
|
4
|
+
require "ably/modules/event_emitter"
|
5
|
+
|
5
6
|
require "ably/realtime/channel"
|
6
7
|
require "ably/realtime/client"
|
7
8
|
require "ably/realtime/connection"
|
@@ -9,8 +10,12 @@ require "ably/realtime/connection"
|
|
9
10
|
require "ably/realtime/models/shared"
|
10
11
|
require "ably/realtime/models/error_info"
|
11
12
|
require "ably/realtime/models/message"
|
13
|
+
require "ably/realtime/models/nil_channel"
|
12
14
|
require "ably/realtime/models/protocol_message"
|
13
15
|
|
16
|
+
require "ably/realtime/client/incoming_message_dispatcher"
|
17
|
+
require "ably/realtime/client/outgoing_message_dispatcher"
|
18
|
+
|
14
19
|
module Ably
|
15
20
|
module Realtime
|
16
21
|
def self.new(*args)
|
@@ -1,59 +1,118 @@
|
|
1
1
|
module Ably
|
2
2
|
module Realtime
|
3
|
+
# The Channel class represents a Channel belonging to this application.
|
4
|
+
# The Channel instance allows messages to be published and
|
5
|
+
# received, and controls the lifecycle of this instance's
|
6
|
+
# attachment to the channel.
|
7
|
+
#
|
8
|
+
# Channels will always be in one of the following states:
|
9
|
+
#
|
10
|
+
# initialized: 0
|
11
|
+
# attaching: 1
|
12
|
+
# attached: 2
|
13
|
+
# detaching: 3
|
14
|
+
# detached: 4
|
15
|
+
# failed: 5
|
16
|
+
#
|
17
|
+
# Note that the states are available as Enum-like constants:
|
18
|
+
#
|
19
|
+
# Channel::STATE.Initialized
|
20
|
+
# Channel::STATE.Attaching
|
21
|
+
# Channel::STATE.Attached
|
22
|
+
# Channel::STATE.Detaching
|
23
|
+
# Channel::STATE.Detached
|
24
|
+
# Channel::STATE.Failed
|
25
|
+
#
|
26
|
+
# @!attribute [r] state
|
27
|
+
# @return {Ably::Realtime::Connection::STATE} channel state
|
28
|
+
#
|
3
29
|
class Channel
|
4
30
|
include Ably::Modules::Conversions
|
5
|
-
include
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
31
|
+
include Ably::Modules::EventEmitter
|
32
|
+
include Ably::Modules::EventMachineHelpers
|
33
|
+
extend Ably::Modules::Enum
|
34
|
+
|
35
|
+
STATE = ruby_enum('STATE',
|
36
|
+
:initialized,
|
37
|
+
:attaching,
|
38
|
+
:attached,
|
39
|
+
:detaching,
|
40
|
+
:detached,
|
41
|
+
:failed
|
42
|
+
)
|
43
|
+
include Ably::Modules::State
|
44
|
+
|
45
|
+
# Max number of messages to bundle in a single ProtocolMessage
|
46
|
+
MAX_PROTOCOL_MESSAGE_BATCH_SIZE = 50
|
15
47
|
|
16
48
|
attr_reader :client, :name
|
17
49
|
|
18
|
-
# Retrieve a state symbol by the integer value
|
19
|
-
def self.state_sym_for(state_int)
|
20
|
-
@states_index_by_int ||= STATES.invert.freeze
|
21
|
-
@states_index_by_int[state_int]
|
22
|
-
end
|
23
|
-
|
24
50
|
def initialize(client, name)
|
25
|
-
@client
|
26
|
-
@name
|
27
|
-
@subscriptions
|
28
|
-
@queue
|
51
|
+
@client = client
|
52
|
+
@name = name
|
53
|
+
@subscriptions = Hash.new { |hash, key| hash[key] = [] }
|
54
|
+
@queue = []
|
55
|
+
@state = STATE.Initialized
|
29
56
|
|
30
|
-
|
57
|
+
setup_event_handlers
|
58
|
+
end
|
31
59
|
|
32
|
-
|
33
|
-
|
34
|
-
|
60
|
+
# Publish a message on the channel
|
61
|
+
#
|
62
|
+
# @param event [String] The event name of the message
|
63
|
+
# @param data [String,ByteArray] payload for the message
|
64
|
+
# @yield [Ably::Realtime::Models::Message] On success, will call the block with the {Ably::Realtime::Models::Message}
|
65
|
+
# @return [Ably::Realtime::Models::Message]
|
66
|
+
#
|
67
|
+
def publish(event, data, &callback)
|
68
|
+
Models::Message.new({
|
69
|
+
name: event,
|
70
|
+
data: data,
|
71
|
+
timestamp: as_since_epoch(Time.now),
|
72
|
+
client_id: client.client_id
|
73
|
+
}, nil).tap do |message|
|
74
|
+
message.callback(&callback) if block_given?
|
75
|
+
queue_message message
|
35
76
|
end
|
77
|
+
end
|
36
78
|
|
37
|
-
|
38
|
-
|
39
|
-
|
79
|
+
def subscribe(event = :all, &blk)
|
80
|
+
event = event.to_s unless event == :all
|
81
|
+
attach unless attached? || attaching?
|
82
|
+
@subscriptions[event] << blk
|
83
|
+
end
|
84
|
+
|
85
|
+
def attach
|
86
|
+
unless attached? || attaching?
|
87
|
+
change_state STATE.Attaching
|
88
|
+
send_attach_protocol_message
|
40
89
|
end
|
41
90
|
end
|
42
91
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
self.class.state_sym_for(@state)
|
92
|
+
def __incoming_protocol_msgbus__
|
93
|
+
@__incoming_protocol_msgbus__ ||= Ably::Util::PubSub.new(
|
94
|
+
coerce_into: Proc.new { |event| Models::ProtocolMessage::ACTION(event) }
|
95
|
+
)
|
48
96
|
end
|
49
97
|
|
50
|
-
|
51
|
-
|
52
|
-
|
98
|
+
private
|
99
|
+
attr_reader :queue
|
100
|
+
|
101
|
+
def setup_event_handlers
|
102
|
+
__incoming_protocol_msgbus__.subscribe(:message) do |message|
|
103
|
+
@subscriptions[:all].each { |cb| cb.call(message) }
|
104
|
+
@subscriptions[message.name].each { |cb| cb.call(message) }
|
105
|
+
end
|
106
|
+
|
107
|
+
on(:attached) do
|
108
|
+
process_queue
|
109
|
+
end
|
53
110
|
end
|
54
111
|
|
55
|
-
|
56
|
-
|
112
|
+
# Queue message and process queue if channel is attached.
|
113
|
+
# If channel is not yet attached, attempt to attach it before the message queue is processed.
|
114
|
+
def queue_message(message)
|
115
|
+
queue << message
|
57
116
|
|
58
117
|
if attached?
|
59
118
|
process_queue
|
@@ -62,34 +121,36 @@ module Ably
|
|
62
121
|
end
|
63
122
|
end
|
64
123
|
|
65
|
-
def
|
66
|
-
|
67
|
-
attach unless attached?
|
68
|
-
@subscriptions[event] << blk
|
124
|
+
def messages_in_queue?
|
125
|
+
!queue.empty?
|
69
126
|
end
|
70
127
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
128
|
+
# Move messages from Channel Queue into Outgoing Connection Queue
|
129
|
+
def process_queue
|
130
|
+
condition = -> { attached? && messages_in_queue? }
|
131
|
+
non_blocking_loop_while(condition) do
|
132
|
+
send_messages_within_protocol_message(queue.shift(MAX_PROTOCOL_MESSAGE_BATCH_SIZE))
|
75
133
|
end
|
76
134
|
end
|
77
135
|
|
78
|
-
def
|
79
|
-
|
136
|
+
def send_messages_within_protocol_message(messages)
|
137
|
+
client.connection.send_protocol_message(
|
138
|
+
action: Models::ProtocolMessage::ACTION.Message.to_i,
|
139
|
+
channel: name,
|
140
|
+
messages: messages
|
141
|
+
)
|
80
142
|
end
|
81
143
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
raise ArgumentError, "#{new_state} is not a valid state" unless STATES.values.include?(new_state)
|
88
|
-
@state = new_state
|
144
|
+
def send_attach_protocol_message
|
145
|
+
client.connection.send_protocol_message(
|
146
|
+
action: Models::ProtocolMessage::ACTION.Attach.to_i,
|
147
|
+
channel: name
|
148
|
+
)
|
89
149
|
end
|
90
150
|
|
91
|
-
|
92
|
-
|
151
|
+
# Used by {Ably::Modules::State} to debug state changes
|
152
|
+
def logger
|
153
|
+
client.logger
|
93
154
|
end
|
94
155
|
end
|
95
156
|
end
|
data/lib/ably/realtime/client.rb
CHANGED
@@ -13,14 +13,13 @@ module Ably
|
|
13
13
|
# @!attribute [r] environment
|
14
14
|
# (see Ably::Rest::Client#environment)
|
15
15
|
class Client
|
16
|
-
include Callbacks
|
17
16
|
extend Forwardable
|
18
17
|
|
19
18
|
DOMAIN = 'realtime.ably.io'
|
20
19
|
|
21
20
|
attr_reader :channels, :auth
|
22
21
|
def_delegators :auth, :client_id, :auth_options
|
23
|
-
def_delegators :@rest_client, :tls, :environment, :use_tls
|
22
|
+
def_delegators :@rest_client, :tls, :environment, :use_tls?, :logger, :log_level
|
24
23
|
|
25
24
|
# Creates a {Ably::Realtime::Client Realtime Client} and configures the {Ably::Auth} object for the connection.
|
26
25
|
#
|
@@ -29,7 +28,6 @@ module Ably
|
|
29
28
|
# @option options [Boolean] :queue_messages If false, this disables the default behaviour whereby the library queues messages on a connection in the disconnected or connecting states
|
30
29
|
# @option options [Boolean] :echo_messages If false, prevents messages originating from this connection being echoed back on the same connection
|
31
30
|
# @option options [String] :recover This option allows a connection to inherit the state of a previous connection that may have existed under an different instance of the Realtime library.
|
32
|
-
# @option options [Boolean] :debug_http Send HTTP & websocket debugging information for all messages/requests sent and received to STDOUT
|
33
31
|
#
|
34
32
|
# @yield (see Ably::Rest::Client#initialize)
|
35
33
|
# @yieldparam (see Ably::Rest::Client#initialize)
|
@@ -48,20 +46,6 @@ module Ably
|
|
48
46
|
@rest_client = Ably::Rest::Client.new(options)
|
49
47
|
@auth = @rest_client.auth
|
50
48
|
@message_serial = 0
|
51
|
-
|
52
|
-
on(:attached) do |protocol_message|
|
53
|
-
channel = channel(protocol_message.channel)
|
54
|
-
|
55
|
-
channel.trigger(:attached)
|
56
|
-
end
|
57
|
-
|
58
|
-
on(:message) do |protocol_message|
|
59
|
-
channel = channel(protocol_message.channel)
|
60
|
-
|
61
|
-
protocol_message.messages.each do |message|
|
62
|
-
channel.trigger(:message, message)
|
63
|
-
end
|
64
|
-
end
|
65
49
|
end
|
66
50
|
|
67
51
|
def token
|
@@ -77,27 +61,6 @@ module Ably
|
|
77
61
|
@channels[name] ||= Ably::Realtime::Channel.new(self, name)
|
78
62
|
end
|
79
63
|
|
80
|
-
def send_messages(channel_name, messages)
|
81
|
-
payload = {
|
82
|
-
action: Models::ProtocolMessage.action!(:message),
|
83
|
-
channel: channel_name,
|
84
|
-
messages: messages
|
85
|
-
}
|
86
|
-
|
87
|
-
payload.merge!(clientId: client_id) unless client_id.nil?
|
88
|
-
|
89
|
-
connection.send(payload)
|
90
|
-
end
|
91
|
-
|
92
|
-
def attach_to_channel(channel_name)
|
93
|
-
payload = {
|
94
|
-
action: Models::ProtocolMessage.action!(:attach),
|
95
|
-
channel: channel_name
|
96
|
-
}
|
97
|
-
|
98
|
-
connection.send(payload)
|
99
|
-
end
|
100
|
-
|
101
64
|
# Default Ably Realtime endpoint used for all requests
|
102
65
|
#
|
103
66
|
# @return [URI::Generic]
|
@@ -113,21 +76,15 @@ module Ably
|
|
113
76
|
host = endpoint.host
|
114
77
|
port = use_tls? ? 443 : 80
|
115
78
|
|
116
|
-
EventMachine.connect(host, port, Connection, self)
|
79
|
+
EventMachine.connect(host, port, Connection, self).tap do |connection|
|
80
|
+
connection.on(:connected) do
|
81
|
+
IncomingMessageDispatcher.new(self)
|
82
|
+
OutgoingMessageDispatcher.new(self)
|
83
|
+
end
|
84
|
+
end
|
117
85
|
end
|
118
86
|
end
|
119
87
|
|
120
|
-
# When true, will send HTTP & websocket debugging information for all messages/requests sent and received to STDOUT
|
121
|
-
#
|
122
|
-
# @return [Boolean]
|
123
|
-
def debug_http?
|
124
|
-
rest_client.debug_http?
|
125
|
-
end
|
126
|
-
|
127
|
-
def log_http(message)
|
128
|
-
$stdout.puts "#{Time.now.strftime('%H:%M:%S')} #{message}" if debug_http?
|
129
|
-
end
|
130
|
-
|
131
88
|
private
|
132
89
|
attr_reader :rest_client
|
133
90
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Ably::Realtime
|
2
|
+
class Client
|
3
|
+
# IncomingMessageDispatcher is a (private) class that is used to dispatch {Ably::Realtime::Models::ProtocolMessage} that are
|
4
|
+
# received from Ably via the {Ably::Realtime::Connection}
|
5
|
+
class IncomingMessageDispatcher
|
6
|
+
ACTION = Models::ProtocolMessage::ACTION
|
7
|
+
|
8
|
+
def initialize(client)
|
9
|
+
@client = client
|
10
|
+
subscribe_to_incoming_protocol_messages
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
attr_reader :client
|
15
|
+
|
16
|
+
def connection
|
17
|
+
client.connection
|
18
|
+
end
|
19
|
+
|
20
|
+
def channels
|
21
|
+
client.channels
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_channel(channel_name)
|
25
|
+
channels.fetch(channel_name) do
|
26
|
+
logger.warn "Received channel message for non-existent channel"
|
27
|
+
Models::NilChannel.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def logger
|
32
|
+
client.logger
|
33
|
+
end
|
34
|
+
|
35
|
+
def dispatch_protocol_message(*args)
|
36
|
+
protocol_message = args.first
|
37
|
+
|
38
|
+
unless protocol_message.kind_of?(Models::ProtocolMessage)
|
39
|
+
raise ArgumentError, "Expected a ProtocolMessage. Received #{protocol_message}"
|
40
|
+
end
|
41
|
+
|
42
|
+
unless [:nack, :error].include?(protocol_message.action)
|
43
|
+
logger.debug "#{protocol_message.action} received: #{protocol_message}"
|
44
|
+
end
|
45
|
+
|
46
|
+
case protocol_message.action
|
47
|
+
when ACTION.Heartbeat
|
48
|
+
when ACTION.Ack
|
49
|
+
ack_pending_queue_for_message_serial(protocol_message) if protocol_message.has_message_serial?
|
50
|
+
|
51
|
+
when ACTION.Nack
|
52
|
+
logger.warn "NACK received: #{protocol_message}"
|
53
|
+
nack_pending_queue_for_message_serial(protocol_message) if protocol_message.has_message_serial?
|
54
|
+
|
55
|
+
when ACTION.Connect, ACTION.Connected
|
56
|
+
when ACTION.Disconnect, ACTION.Disconnected
|
57
|
+
when ACTION.Close
|
58
|
+
when ACTION.Closed
|
59
|
+
when ACTION.Error
|
60
|
+
logger.error "Error received: #{protocol_message.error}"
|
61
|
+
|
62
|
+
when ACTION.Attach
|
63
|
+
when ACTION.Attached
|
64
|
+
get_channel(protocol_message.channel).change_state Ably::Realtime::Channel::STATE.Attached
|
65
|
+
|
66
|
+
when ACTION.Detach
|
67
|
+
when ACTION.Detached
|
68
|
+
get_channel(protocol_message.channel).change_state Ably::Realtime::Channel::STATE.Detached
|
69
|
+
|
70
|
+
when ACTION.Presence
|
71
|
+
when ACTION.Message
|
72
|
+
protocol_message.messages.each do |message|
|
73
|
+
get_channel(protocol_message.channel).__incoming_protocol_msgbus__.publish :message, message
|
74
|
+
end
|
75
|
+
|
76
|
+
else
|
77
|
+
raise ArgumentError, "Protocol Message Action #{protocol_message.action} is unsupported by this MessageDispatcher"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def ack_pending_queue_for_message_serial(ack_protocol_message)
|
82
|
+
drop_pending_queue_from_ack(ack_protocol_message) do |protocol_message|
|
83
|
+
protocol_message.messages.each do |message|
|
84
|
+
logger.debug "Calling ACK success callbacks for #{message.to_json}"
|
85
|
+
message.succeed message
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def nack_pending_queue_for_message_serial(nack_protocol_message)
|
91
|
+
drop_pending_queue_from_ack(nack_protocol_message) do |protocol_message|
|
92
|
+
protocol_message.messages.each do |message|
|
93
|
+
logger.debug "Calling NACK failure callbacks for #{message.to_json}"
|
94
|
+
message.fail message, nack_protocol_message.error
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def drop_pending_queue_from_ack(ack_protocol_message)
|
100
|
+
message_serial_up_to = ack_protocol_message.message_serial + ack_protocol_message.count - 1
|
101
|
+
connection.__pending_message_queue__.drop_while do |protocol_message|
|
102
|
+
if protocol_message.message_serial <= message_serial_up_to
|
103
|
+
yield protocol_message
|
104
|
+
true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def subscribe_to_incoming_protocol_messages
|
110
|
+
connection.__incoming_protocol_msgbus__.subscribe(:message) do |*args|
|
111
|
+
dispatch_protocol_message *args
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|