Class: Live::Page
- Inherits:
-
Object
- Object
- Live::Page
- Defined in:
- lib/live/page.rb
Overview
Represents a connected client page with bound dynamic content areas.
Instance Method Summary collapse
-
#attach(element) ⇒ Object
Attach a pre-existing element to the page, so that it may later be bound to this exact instance.
-
#bind(element) ⇒ Object
Bind a client-side element to a server side element.
- #close ⇒ Object
- #detach(element) ⇒ Object
- #enqueue(update) ⇒ Object
-
#handle(id, event) ⇒ Object
Handle an event from the client.
-
#initialize(resolver) ⇒ Page
constructor
A new instance of Page.
-
#process_message(message) ⇒ Object
Process a single incoming message from the network.
-
#resolve(id, data = {}) ⇒ Object
Resolve a client-side element to a server side instance.
-
#run(connection, keep_alive: 10) ⇒ Object
Run the event handling loop with the given websocket connection.
Constructor Details
#initialize(resolver) ⇒ Page
Returns a new instance of Page.
21 22 23 24 25 26 27 28 |
# File 'lib/live/page.rb', line 21 def initialize(resolver) @resolver = resolver @elements = {} @attached = {} @updates = Async::Queue.new end |
Instance Method Details
#attach(element) ⇒ Object
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.
40 41 42 |
# File 'lib/live/page.rb', line 40 def attach(element) @attached[element.id] = element end |
#bind(element) ⇒ Object
Bind a client-side element to a server side element.
32 33 34 35 36 |
# File 'lib/live/page.rb', line 32 def bind(element) @elements[element.id] = element element.bind(self) end |
#close ⇒ Object
74 75 76 77 78 79 80 81 82 |
# File 'lib/live/page.rb', line 74 def close @elements.each do |id, element| begin element.close rescue => error Console::Event::Failure.for(error).emit(self) end end end |
#detach(element) ⇒ Object
44 45 46 47 48 |
# File 'lib/live/page.rb', line 44 def detach(element) if @attached.delete(element.id) element.close end end |
#enqueue(update) ⇒ Object
84 85 86 |
# File 'lib/live/page.rb', line 84 def enqueue(update) @updates.enqueue(::Protocol::WebSocket::TextMessage.generate(update)) end |
#handle(id, event) ⇒ Object
Handle an event from the client. If the element could not be found, it is silently ignored.
64 65 66 67 68 69 70 71 72 |
# File 'lib/live/page.rb', line 64 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 |
#process_message(message) ⇒ Object
Process a single incoming message from the network.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/live/page.rb', line 89 def () case [0] when "bind" # Bind a client-side element to a server-side element. if element = self.resolve([1], [2]) self.bind(element) else Console.warn(self, "Could not resolve element:", ) self.enqueue(["error", [1], "Could not resolve element!"]) end when "unbind" # Unbind a client-side element from a server-side element. if element = @elements.delete([1]) element.close unless @attached.key?([1]) else Console.warn(self, "Could not unbind element:", ) self.enqueue(["error", [1], "Could not unbind element!"]) end when "event" # Handle an event from the client. self.handle([1], [2]) else Console.warn(self, "Unhandled message:", ) end end |
#resolve(id, data = {}) ⇒ Object
Resolve a client-side element to a server side instance.
53 54 55 56 57 |
# File 'lib/live/page.rb', line 53 def resolve(id, data = {}) @attached.fetch(id) do @resolver.call(id, data) end end |
#run(connection, keep_alive: 10) ⇒ Object
Run the event handling loop with the given websocket connection.
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/live/page.rb', line 117 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 = connection.read last_update = Async::Clock.now (.parse) end ensure keep_alive_task&.stop self.close queue_task&.stop end end |