pakyow-realtime 0.11.3 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/{pakyow-realtime/CHANGELOG.md → CHANGELOG.md} +5 -0
- data/LICENSE +4 -0
- data/{pakyow-realtime/README.md → README.md} +1 -2
- data/lib/pakyow/environment/realtime/config.rb +29 -0
- data/lib/pakyow/realtime/actions/upgrader.rb +29 -0
- data/lib/pakyow/realtime/behavior/config.rb +42 -0
- data/lib/pakyow/realtime/behavior/rendering/install_websocket.rb +57 -0
- data/lib/pakyow/realtime/behavior/serialization.rb +42 -0
- data/lib/pakyow/realtime/behavior/server.rb +42 -0
- data/lib/pakyow/realtime/behavior/silencing.rb +25 -0
- data/lib/pakyow/realtime/channel.rb +23 -0
- data/lib/pakyow/realtime/context.rb +38 -0
- data/lib/pakyow/realtime/framework.rb +49 -0
- data/lib/pakyow/realtime/helpers/broadcasting.rb +13 -0
- data/lib/pakyow/realtime/helpers/socket.rb +13 -0
- data/lib/pakyow/realtime/helpers/subscriptions.rb +35 -0
- data/lib/pakyow/realtime/server/adapters/memory.rb +127 -0
- data/lib/pakyow/realtime/server/adapters/redis.rb +277 -0
- data/lib/pakyow/realtime/server.rb +152 -0
- data/lib/pakyow/realtime/websocket.rb +157 -0
- data/lib/pakyow/realtime.rb +13 -0
- metadata +73 -44
- data/pakyow-realtime/LICENSE +0 -20
- data/pakyow-realtime/lib/pakyow/realtime/config.rb +0 -20
- data/pakyow-realtime/lib/pakyow/realtime/connection.rb +0 -18
- data/pakyow-realtime/lib/pakyow/realtime/context.rb +0 -68
- data/pakyow-realtime/lib/pakyow/realtime/delegate.rb +0 -112
- data/pakyow-realtime/lib/pakyow/realtime/exceptions.rb +0 -6
- data/pakyow-realtime/lib/pakyow/realtime/ext/request.rb +0 -10
- data/pakyow-realtime/lib/pakyow/realtime/helpers.rb +0 -40
- data/pakyow-realtime/lib/pakyow/realtime/hooks.rb +0 -41
- data/pakyow-realtime/lib/pakyow/realtime/message_handler.rb +0 -57
- data/pakyow-realtime/lib/pakyow/realtime/message_handlers/call_route.rb +0 -34
- data/pakyow-realtime/lib/pakyow/realtime/message_handlers/ping.rb +0 -8
- data/pakyow-realtime/lib/pakyow/realtime/redis_subscription.rb +0 -61
- data/pakyow-realtime/lib/pakyow/realtime/registries/redis_registry.rb +0 -107
- data/pakyow-realtime/lib/pakyow/realtime/registries/simple_registry.rb +0 -40
- data/pakyow-realtime/lib/pakyow/realtime/websocket.rb +0 -209
- data/pakyow-realtime/lib/pakyow/realtime.rb +0 -19
- data/pakyow-realtime/lib/pakyow-realtime.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e741f872b4f4c8c70d0b0435f69dea7a686e1d8ba95b3416ae89b8f3a50bd6a2
|
4
|
+
data.tar.gz: da45c2b4b7238ab709aeb7a243d6e2ee3f6869a86fc0d4d99e02a701960cc56a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a44fd08cb285611e91b7129d266885ad30e6cecc02b33ba3b7bbacb36cee82228515437b027f305fa7cedd03af4c0c502be4a3b7d95ba9d0da83d60dc3b31576
|
7
|
+
data.tar.gz: 9524a3586f595db6a0c9d4f9538b9f8424be88aa627f8ad8f91030d1157ef9e84ee242ef9ca48ac2405f443a5a70d8b787e265c60c4ee4ca703064a0fb0dd1c4
|
data/LICENSE
ADDED
@@ -146,8 +146,7 @@ Source code can be downloaded as part of the Pakyow project on Github:
|
|
146
146
|
|
147
147
|
# License
|
148
148
|
|
149
|
-
Pakyow Realtime is
|
150
|
-
License](http://opensource.org/licenses/MIT).
|
149
|
+
Pakyow Realtime is free and open-source under the [LGPLv3 license](https://choosealicense.com/licenses/lgpl-3.0/).
|
151
150
|
|
152
151
|
# Support
|
153
152
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/extension"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
module Environment
|
7
|
+
module Realtime
|
8
|
+
module Config
|
9
|
+
extend Support::Extension
|
10
|
+
|
11
|
+
apply_extension do
|
12
|
+
configurable :realtime do
|
13
|
+
setting :server, true
|
14
|
+
|
15
|
+
setting :adapter, :memory
|
16
|
+
setting :adapter_settings, {}
|
17
|
+
|
18
|
+
defaults :production do
|
19
|
+
setting :adapter, :redis
|
20
|
+
setting :adapter_settings do
|
21
|
+
Pakyow.config.redis.to_h
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/message_verifier"
|
4
|
+
|
5
|
+
require "pakyow/realtime/websocket"
|
6
|
+
|
7
|
+
module Pakyow
|
8
|
+
module Realtime
|
9
|
+
module Actions
|
10
|
+
class Upgrader
|
11
|
+
def call(connection)
|
12
|
+
if websocket?(connection)
|
13
|
+
WebSocket.new(connection.verifier.verify(connection.params[:id]), connection)
|
14
|
+
connection.halt
|
15
|
+
end
|
16
|
+
rescue Support::MessageVerifier::TamperedMessage
|
17
|
+
connection.status = 403
|
18
|
+
connection.halt
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def websocket?(connection)
|
24
|
+
connection.path == "/pw-socket"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/extension"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
module Realtime
|
7
|
+
module Behavior
|
8
|
+
module Config
|
9
|
+
extend Support::Extension
|
10
|
+
|
11
|
+
apply_extension do
|
12
|
+
configurable :realtime do
|
13
|
+
setting :adapter_settings, {}
|
14
|
+
setting :path, "pw-socket"
|
15
|
+
setting :endpoint
|
16
|
+
setting :log_initial_request, false
|
17
|
+
|
18
|
+
defaults :production do
|
19
|
+
setting :adapter_settings do
|
20
|
+
{ key_prefix: [Pakyow.config.redis.key_prefix, config.name].join("/") }
|
21
|
+
end
|
22
|
+
|
23
|
+
setting :log_initial_request, true
|
24
|
+
end
|
25
|
+
|
26
|
+
configurable :timeouts do
|
27
|
+
# Give sockets 60 seconds to connect before cleaning up their state.
|
28
|
+
#
|
29
|
+
setting :initial, 60
|
30
|
+
|
31
|
+
# When a socket disconnects, keep state around for 24 hours before
|
32
|
+
# cleaning up. This improves the user experience in cases such as
|
33
|
+
# when a browser window is left open on a sleeping computer.
|
34
|
+
#
|
35
|
+
setting :disconnect, 24 * 60 * 60
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/extension"
|
4
|
+
|
5
|
+
require "pakyow/presenter/view"
|
6
|
+
|
7
|
+
module Pakyow
|
8
|
+
module Realtime
|
9
|
+
module Behavior
|
10
|
+
module Rendering
|
11
|
+
module InstallWebsocket
|
12
|
+
extend Support::Extension
|
13
|
+
|
14
|
+
apply_extension do
|
15
|
+
build do |view|
|
16
|
+
if head = view.head
|
17
|
+
head.append(Support::SafeStringHelpers.html_safe("<meta name=\"pw-socket\" ui=\"socket\">"))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attach do |presenter, app:|
|
22
|
+
presenter.render node: -> {
|
23
|
+
node = object.each_significant_node(:meta).find { |meta_node|
|
24
|
+
meta_node.attributes[:name] == "pw-socket"
|
25
|
+
}
|
26
|
+
|
27
|
+
unless node.nil?
|
28
|
+
Presenter::View.from_object(node)
|
29
|
+
end
|
30
|
+
} do
|
31
|
+
endpoint = app.config.realtime.endpoint
|
32
|
+
|
33
|
+
unless endpoint
|
34
|
+
endpoint = if Pakyow.config.server.proxy
|
35
|
+
# Connect directly to the app in development, since the proxy does not support websocket connections.
|
36
|
+
#
|
37
|
+
File.join("ws://#{Pakyow.config.server.host}:#{Pakyow.config.server.port}", app.config.realtime.path)
|
38
|
+
else
|
39
|
+
File.join("#{presentables[:__ws_protocol]}://#{presentables[:__ws_authority]}", app.config.realtime.path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
attributes["data-ui"] = "socket(endpoint: #{endpoint}?id=#{presentables[:__verifier].sign(presentables[:__socket_client_id])})"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
expose do |connection|
|
48
|
+
connection.set(:__verifier, connection.verifier)
|
49
|
+
connection.set(:__ws_protocol, connection.secure? ? "wss" : "ws")
|
50
|
+
connection.set(:__ws_authority, connection.authority)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
require "pakyow/support/extension"
|
6
|
+
require "pakyow/support/serializer"
|
7
|
+
|
8
|
+
module Pakyow
|
9
|
+
module Realtime
|
10
|
+
module Behavior
|
11
|
+
# Persists the in-memory realtime server across restarts.
|
12
|
+
#
|
13
|
+
module Serialization
|
14
|
+
extend Support::Extension
|
15
|
+
|
16
|
+
apply_extension do
|
17
|
+
on "shutdown", priority: :high do
|
18
|
+
if Pakyow.config.realtime.adapter == :memory && instance_variable_defined?(:@websocket_server)
|
19
|
+
realtime_server_serializer.serialize
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
after "boot" do
|
24
|
+
if Pakyow.config.realtime.adapter == :memory
|
25
|
+
realtime_server_serializer.deserialize
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private def realtime_server_serializer
|
31
|
+
Support::Serializer.new(
|
32
|
+
@websocket_server.adapter,
|
33
|
+
name: "#{config.name}-realtime",
|
34
|
+
path: File.join(
|
35
|
+
Pakyow.config.root, "tmp", "state"
|
36
|
+
)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/deep_freeze"
|
4
|
+
require "pakyow/support/extension"
|
5
|
+
|
6
|
+
require "pakyow/realtime/server"
|
7
|
+
|
8
|
+
module Pakyow
|
9
|
+
module Realtime
|
10
|
+
module Behavior
|
11
|
+
module Server
|
12
|
+
extend Support::Extension
|
13
|
+
|
14
|
+
apply_extension do
|
15
|
+
extend Support::DeepFreeze
|
16
|
+
unfreezable :websocket_server
|
17
|
+
attr_reader :websocket_server
|
18
|
+
|
19
|
+
after "initialize", priority: :high do
|
20
|
+
@websocket_server = if is_a?(Plugin)
|
21
|
+
parent.websocket_server
|
22
|
+
else
|
23
|
+
Realtime::Server.new(
|
24
|
+
Pakyow.config.realtime.adapter,
|
25
|
+
Pakyow.config.realtime.adapter_settings.to_h.merge(
|
26
|
+
config.realtime.adapter_settings.to_h
|
27
|
+
),
|
28
|
+
config.realtime.timeouts
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
on "shutdown" do
|
34
|
+
if instance_variable_defined?(:@websocket_server)
|
35
|
+
@websocket_server.shutdown
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/support/extension"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
module Realtime
|
7
|
+
module Behavior
|
8
|
+
# Silences asset requests from being logged.
|
9
|
+
#
|
10
|
+
module Silencing
|
11
|
+
extend Support::Extension
|
12
|
+
|
13
|
+
apply_extension do
|
14
|
+
on "load" do
|
15
|
+
unless config.realtime.log_initial_request
|
16
|
+
Pakyow.silence do |connection|
|
17
|
+
connection.path.start_with?(File.join("/", config.realtime.path))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
module Realtime
|
5
|
+
class Channel
|
6
|
+
class << self
|
7
|
+
def parse(qualified_channel)
|
8
|
+
Channel.new(*qualified_channel.split("::", 2))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :name, :qualifier
|
13
|
+
|
14
|
+
def initialize(channel_name, qualifier = nil)
|
15
|
+
@name, @qualifier = channel_name, qualifier
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
[@name, @qualifier].join("::")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
module Realtime
|
5
|
+
# Deals with realtime connections in context of an app. Instances are
|
6
|
+
# returned by the `socket` helper method during routing.
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Context
|
10
|
+
# @api private
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
# Push a message down one or more channels.
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def push(msg, *channels)
|
19
|
+
delegate.push(msg, *channels)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Push a message down a channel directed at a specific client,
|
23
|
+
# identified by key.
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def push_to_key(msg, channel, key, propagated: false)
|
27
|
+
delegate.push_to_key(msg, channel, key, propagated: propagated)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns an instance of the connection delegate.
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
def delegate
|
34
|
+
Delegate.instance
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/framework"
|
4
|
+
|
5
|
+
require "pakyow/realtime/helpers/broadcasting"
|
6
|
+
require "pakyow/realtime/helpers/subscriptions"
|
7
|
+
require "pakyow/realtime/helpers/socket"
|
8
|
+
|
9
|
+
require "pakyow/realtime/behavior/config"
|
10
|
+
require "pakyow/realtime/behavior/serialization"
|
11
|
+
require "pakyow/realtime/behavior/server"
|
12
|
+
require "pakyow/realtime/behavior/silencing"
|
13
|
+
|
14
|
+
require "pakyow/realtime/actions/upgrader"
|
15
|
+
|
16
|
+
require "pakyow/realtime/behavior/rendering/install_websocket"
|
17
|
+
|
18
|
+
module Pakyow
|
19
|
+
module Realtime
|
20
|
+
class Framework < Pakyow::Framework(:realtime)
|
21
|
+
def boot
|
22
|
+
object.class_eval do
|
23
|
+
register_helper :active, Helpers::Broadcasting
|
24
|
+
register_helper :active, Helpers::Subscriptions
|
25
|
+
register_helper :passive, Helpers::Socket
|
26
|
+
|
27
|
+
# Socket events are triggered on the app.
|
28
|
+
#
|
29
|
+
events :join, :leave
|
30
|
+
|
31
|
+
include Behavior::Config
|
32
|
+
include Behavior::Server
|
33
|
+
include Behavior::Silencing
|
34
|
+
include Behavior::Serialization
|
35
|
+
|
36
|
+
isolated :Renderer do
|
37
|
+
include Behavior::Rendering::InstallWebsocket
|
38
|
+
end
|
39
|
+
|
40
|
+
isolated :Connection do
|
41
|
+
after "initialize" do
|
42
|
+
set(:__socket_client_id, params[:socket_client_id] || Support::MessageVerifier.key)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pakyow/realtime/channel"
|
4
|
+
|
5
|
+
module Pakyow
|
6
|
+
module Realtime
|
7
|
+
module Helpers
|
8
|
+
module Subscriptions
|
9
|
+
def subscribe(channel, *qualifiers)
|
10
|
+
channels = if qualifiers.empty?
|
11
|
+
Channel.new(channel)
|
12
|
+
else
|
13
|
+
qualifiers.map { |qualifier|
|
14
|
+
Channel.new(channel, qualifier)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
app.websocket_server.socket_subscribe(socket_client_id, *channels)
|
19
|
+
end
|
20
|
+
|
21
|
+
def unsubscribe(channel, *qualifiers)
|
22
|
+
channels = if qualifiers.empty?
|
23
|
+
Channel.new(channel, "*")
|
24
|
+
else
|
25
|
+
qualifiers.map { |qualifier|
|
26
|
+
Channel.new(channel, qualifier)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
app.websocket_server.socket_unsubscribe(*channels)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "concurrent/array"
|
4
|
+
require "concurrent/hash"
|
5
|
+
require "concurrent/scheduled_task"
|
6
|
+
|
7
|
+
module Pakyow
|
8
|
+
module Realtime
|
9
|
+
class Server
|
10
|
+
module Adapters
|
11
|
+
# Manages websocket channels in memory.
|
12
|
+
#
|
13
|
+
# Great for development, not for use in production!
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
class Memory
|
17
|
+
def initialize(server, _config)
|
18
|
+
@server = server
|
19
|
+
|
20
|
+
@socket_ids_by_channel = Concurrent::Hash.new
|
21
|
+
@channels_by_socket_id = Concurrent::Hash.new
|
22
|
+
@expirations_for_socket_id = Concurrent::Hash.new
|
23
|
+
@socket_instances_by_socket_id = Concurrent::Hash.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def connect
|
27
|
+
# intentionally empty
|
28
|
+
end
|
29
|
+
|
30
|
+
def disconnect
|
31
|
+
# intentionally empty
|
32
|
+
end
|
33
|
+
|
34
|
+
def socket_subscribe(socket_id, *channels)
|
35
|
+
channels.each do |channel|
|
36
|
+
channel = channel.to_s.to_sym
|
37
|
+
(@socket_ids_by_channel[channel] ||= Concurrent::Array.new) << socket_id
|
38
|
+
(@channels_by_socket_id[socket_id] ||= Concurrent::Array.new) << channel
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def socket_unsubscribe(*channels)
|
43
|
+
channels.each do |channel|
|
44
|
+
channel = Regexp.new(channel.to_s)
|
45
|
+
|
46
|
+
@socket_ids_by_channel.select { |key|
|
47
|
+
key.to_s.match?(channel)
|
48
|
+
}.each do |key, socket_ids|
|
49
|
+
@socket_ids_by_channel.delete(key)
|
50
|
+
|
51
|
+
socket_ids.each do |socket_id|
|
52
|
+
@channels_by_socket_id[socket_id]&.delete(key)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def subscription_broadcast(channel, message)
|
59
|
+
@server.transmit_message_to_connection_ids(message, socket_ids_for_channel(channel))
|
60
|
+
end
|
61
|
+
|
62
|
+
def expire(socket_id, seconds)
|
63
|
+
task = Concurrent::ScheduledTask.execute(seconds) {
|
64
|
+
channels_for_socket_id(socket_id).each do |channel|
|
65
|
+
@channels_by_socket_id.delete(socket_id)
|
66
|
+
@socket_ids_by_channel[channel].delete(socket_id)
|
67
|
+
@socket_instances_by_socket_id.delete(socket_id)
|
68
|
+
end
|
69
|
+
}
|
70
|
+
|
71
|
+
@expirations_for_socket_id[socket_id] ||= []
|
72
|
+
@expirations_for_socket_id[socket_id] << task
|
73
|
+
end
|
74
|
+
|
75
|
+
def persist(socket_id)
|
76
|
+
(@expirations_for_socket_id[socket_id] || []).each(&:cancel)
|
77
|
+
@expirations_for_socket_id.delete(socket_id)
|
78
|
+
end
|
79
|
+
|
80
|
+
def expiring?(socket_id)
|
81
|
+
@expirations_for_socket_id[socket_id]&.any?
|
82
|
+
end
|
83
|
+
|
84
|
+
def current!(socket_id, socket_instance_id)
|
85
|
+
@socket_instances_by_socket_id[socket_id] = socket_instance_id
|
86
|
+
end
|
87
|
+
|
88
|
+
def current?(socket_id, socket_instance_id)
|
89
|
+
@socket_instances_by_socket_id[socket_id] == socket_instance_id
|
90
|
+
end
|
91
|
+
|
92
|
+
SERIALIZABLE_IVARS = %i(
|
93
|
+
@socket_ids_by_channel
|
94
|
+
@channels_by_socket_id
|
95
|
+
@socket_instances_by_socket_id
|
96
|
+
).freeze
|
97
|
+
|
98
|
+
def serialize
|
99
|
+
SERIALIZABLE_IVARS.each_with_object({}) do |ivar, hash|
|
100
|
+
hash[ivar] = instance_variable_get(ivar)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
def socket_ids_for_channel(channel)
|
107
|
+
channel = Regexp.new(channel.to_s)
|
108
|
+
|
109
|
+
@socket_ids_by_channel.select { |key|
|
110
|
+
key.to_s.match?(channel)
|
111
|
+
}.each_with_object([]) do |(_, socket_ids_for_channel), socket_ids|
|
112
|
+
socket_ids.concat(
|
113
|
+
socket_ids_for_channel.reject { |socket_id|
|
114
|
+
expiring?(socket_id)
|
115
|
+
}
|
116
|
+
)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def channels_for_socket_id(socket_id)
|
121
|
+
@channels_by_socket_id[socket_id] || []
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|