class Page
Represents a connected client page with bound dynamic content areas.
Definitions
def initialize(resolver)
Signature
-
parameter
resolver
Live::Resolver
Used to resolve client-side elements to server-side element instances.
Implementation
def initialize(resolver)
@resolver = resolver
@elements = {}
@attached = {}
@updates = Async::Queue.new
end
def bind(element)
Bind a client-side element to a server side element.
Signature
-
parameter
element
Live::Element
The element to bind.
Implementation
def bind(element)
@elements[element.id] = element
element.bind(self)
end
def attach(element)
Attach a pre-existing element to the page, so that it may later be bound to this exact instance. You must later detach the element when it is no longer needed.
Implementation
def attach(element)
@attached[element.id] = element
end
def resolve(id, data = {})
Resolve a client-side element to a server side instance.
Signature
-
parameter
id
String
The unique identifier within the page.
-
parameter
data
Hash
The data associated with the element, typically stored as
data-
attributes.
Implementation
def resolve(id, data = {})
@attached.fetch(id) do
@resolver.call(id, data)
end
end
def handle(id, event)
Handle an event from the client. If the element could not be found, it is silently ignored.
Signature
-
parameter
id
String
The unique identifier of the element which forwarded the event.
-
parameter
event
String
The type of the event.
-
returns
Object
The result of the element handler, if the element was found.
-
returns
Nil
If the element could not be found.
Implementation
def handle(id, event)
if element = @elements[id]
return element.handle(event)
else
Console.warn(self, "Could not handle event:", id:, event:)
end
return nil
end
def process_message(message)
Process a single incoming message from the network.
Implementation
def process_message(message)
case message[0]
when "bind"
# Bind a client-side element to a server-side element.
if element = self.resolve(message[1], message[2])
self.bind(element)
else
Console.warn(self, "Could not resolve element:", message)
self.enqueue(["error", message[1], "Could not resolve element!"])
end
when "unbind"
# Unbind a client-side element from a server-side element.
if element = @elements.delete(message[1])
element.close unless @attached.key?(message[1])
else
Console.warn(self, "Could not unbind element:", message)
self.enqueue(["error", message[1], "Could not unbind element!"])
end
when "event"
# Handle an event from the client.
self.handle(message[1], message[2])
else
Console.warn(self, "Unhandled message:", message)
end
end
def run(connection, keep_alive: 10)
Run the event handling loop with the given websocket connection.
Signature
-
parameter
connection
Async::WebSocket::Connection
Implementation
def run(connection, keep_alive: 10)
Sync do |task|
last_update = Async::Clock.now
queue_task = task.async do
while update = @updates.dequeue
update.send(connection)
# Flush the output if there are no more updates:
if @updates.empty?
connection.flush
end
end
end
keep_alive_task = task.async do
while true
sleep(keep_alive)
duration = Async::Clock.now - last_update
# We synchronize all writes to the update queue:
if duration > keep_alive
@updates.enqueue(::Protocol::WebSocket::PingMessage.new)
end
end
end
while message = connection.read
last_update = Async::Clock.now
process_message(message.parse)
end
ensure
keep_alive_task&.stop
self.close
queue_task&.stop
end
end