live 0.2.0 → 0.5.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
- checksums.yaml.gz.sig +0 -0
- data/lib/live/element.rb +18 -1
- data/lib/live/page.rb +56 -28
- data/lib/live/resolver.rb +1 -0
- data/lib/live/version.rb +1 -1
- data/lib/live/view.rb +13 -4
- data.tar.gz.sig +0 -0
- metadata +39 -10
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e6f7751e42637cac42f2129ab5acbe1ef1c5109a10ef8f1c380b98e2d986829b
|
4
|
+
data.tar.gz: f7686de5fa9fd454568d4ccc5735b9146b70a23643e7611da6a0578d5b1213c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5dbd73c760708403ad2d16c4343bfc850fd381bb1d37c6d129327015597653227c8b812d6e3eea34b23960cc87d849f9a6cefa97d3154e17c2c6236dbcc7fe33
|
7
|
+
data.tar.gz: 613b770cfeb6c1ef2bfd9b0972186e92361ba4774d3b6786b24bceb698da82ced5b817b5427cff6994a88a692e52b77448d490448761b277f53b7374b86becfe
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data/lib/live/element.rb
CHANGED
@@ -23,7 +23,10 @@
|
|
23
23
|
require 'json'
|
24
24
|
|
25
25
|
module Live
|
26
|
+
# Represents a single dynamic content area on the page.
|
26
27
|
class Element
|
28
|
+
# @parameter id [String] The unique identifier within the page.
|
29
|
+
# @parameter data [Hash] The data associated with the element, typically stored as `data-` attributes.
|
27
30
|
def initialize(id, **data)
|
28
31
|
@id = id
|
29
32
|
@data = data
|
@@ -32,8 +35,11 @@ module Live
|
|
32
35
|
@page = nil
|
33
36
|
end
|
34
37
|
|
38
|
+
# The unique id within the bound page.
|
35
39
|
attr :id
|
36
40
|
|
41
|
+
# Generate a JavaScript string which forwards the specified event to the server.
|
42
|
+
# @parameter details [Hash] The details associated with the forwarded event.
|
37
43
|
def forward(details = nil)
|
38
44
|
if details
|
39
45
|
"live.forward(#{JSON.dump(@id)}, event, #{JSON.dump(details)})"
|
@@ -42,13 +48,24 @@ module Live
|
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
51
|
+
# Bind this tag to a dynamically updating page.
|
52
|
+
# @parameter page [Live::Page]
|
45
53
|
def bind(page)
|
46
54
|
@page = page
|
47
55
|
end
|
48
56
|
|
49
|
-
def
|
57
|
+
def close
|
58
|
+
@page = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
# Handle a client event, typically as triggered by {#forward}.
|
62
|
+
# @parameter event [String] The type of the event.
|
63
|
+
def handle(event)
|
50
64
|
end
|
51
65
|
|
66
|
+
# Enqueue a remote procedure call to the currently bound page.
|
67
|
+
# @parameter method [Symbol] The name of the remote functio to invoke.
|
68
|
+
# @parameter arguments [Array]
|
52
69
|
def rpc(method, arguments)
|
53
70
|
# This update might not be sent right away. Therefore, mutable arguments may be serialized to JSON at a later time (or never). This could be a race condition:
|
54
71
|
@page.updates.enqueue([method, arguments])
|
data/lib/live/page.rb
CHANGED
@@ -26,8 +26,12 @@ require_relative 'resolver'
|
|
26
26
|
require 'async'
|
27
27
|
require 'async/queue'
|
28
28
|
|
29
|
+
require 'protocol/websocket/json_message'
|
30
|
+
|
29
31
|
module Live
|
32
|
+
# Represents a connected client page with bound dynamic content areas.
|
30
33
|
class Page
|
34
|
+
# @parameter resolver [Live::Resolver] Used to resolve client-side elements to server-side element instances.
|
31
35
|
def initialize(resolver)
|
32
36
|
@resolver = resolver
|
33
37
|
|
@@ -35,21 +39,33 @@ module Live
|
|
35
39
|
@updates = Async::Queue.new
|
36
40
|
end
|
37
41
|
|
42
|
+
# The queue of outstanding events to be sent to the client.
|
38
43
|
attr :updates
|
39
44
|
|
45
|
+
# Bind a client-side element to a server side element.
|
46
|
+
# @parameter element [Live::Element] The element to bind.
|
40
47
|
def bind(element)
|
41
48
|
@elements[element.id] = element
|
42
49
|
|
43
50
|
element.bind(self)
|
44
51
|
end
|
45
52
|
|
53
|
+
# Resolve a client-side element to a server side instance.
|
54
|
+
# @parameter id [String] The unique identifier within the page.
|
55
|
+
# @parameter data [Hash] The data associated with the element, typically stored as `data-` attributes.
|
46
56
|
def resolve(id, data)
|
47
57
|
@resolver.call(id, data)
|
48
58
|
end
|
49
59
|
|
50
|
-
|
60
|
+
# Handle an event from the client. If the element could not be found, it is silently ignored.
|
61
|
+
# @parameter id [String] The unique identifier of the element which forwarded the event.
|
62
|
+
# @parameter event [String] The type of the event.
|
63
|
+
# @parameter details [Hash] The associated details if any.
|
64
|
+
# @returns [Object] The result of the element handler, if the element was found.
|
65
|
+
# @returns [Nil] If the element could not be found.
|
66
|
+
def handle(id, event)
|
51
67
|
if element = @elements[id]
|
52
|
-
return element.handle(event
|
68
|
+
return element.handle(event)
|
53
69
|
else
|
54
70
|
Console.logger.warn(self, "Could not handle event:", event, details)
|
55
71
|
end
|
@@ -57,39 +73,51 @@ module Live
|
|
57
73
|
return nil
|
58
74
|
end
|
59
75
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
while update = @updates.dequeue
|
64
|
-
Console.logger.debug(self, "Sending update:", update)
|
65
|
-
|
66
|
-
connection.write(update)
|
67
|
-
connection.flush if @updates.empty?
|
76
|
+
def close
|
77
|
+
@elements.each do |id, element|
|
78
|
+
element.close
|
68
79
|
end
|
69
|
-
ensure
|
70
|
-
reader_task&.stop
|
71
80
|
end
|
72
81
|
|
73
|
-
|
82
|
+
# Process a single incoming message from the network.
|
83
|
+
def process_message(message)
|
84
|
+
if id = message[:bind] and data = message[:data]
|
85
|
+
if element = self.resolve(id, data)
|
86
|
+
self.bind(element)
|
87
|
+
else
|
88
|
+
Console.logger.warn(self, "Could not resolve element:", message)
|
89
|
+
end
|
90
|
+
elsif id = message[:id]
|
91
|
+
self.handle(id, message[:event])
|
92
|
+
else
|
93
|
+
Console.logger.warn(self, "Unhandled message:", message)
|
94
|
+
end
|
95
|
+
end
|
74
96
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
97
|
+
# Run the event handling loop with the given websocket connection.
|
98
|
+
# @parameter connection [Async::WebSocket::Connection]
|
99
|
+
def run(connection)
|
100
|
+
queue_task = Async do
|
101
|
+
while update = @updates.dequeue
|
102
|
+
Console.logger.debug(self, "Sending update:", update)
|
79
103
|
|
80
|
-
|
81
|
-
|
82
|
-
self.bind(element)
|
83
|
-
else
|
84
|
-
Console.logger.warn(self, "Could not resolve element:", message)
|
85
|
-
end
|
86
|
-
elsif id = message[:id]
|
87
|
-
self.handle(id, message[:event], message[:details])
|
88
|
-
else
|
89
|
-
Console.logger.warn(self, "Unhandled message:", message)
|
90
|
-
end
|
104
|
+
connection.write(::Protocol::WebSocket::JSONMessage.generate(update))
|
105
|
+
connection.flush if @updates.empty?
|
91
106
|
end
|
92
107
|
end
|
108
|
+
|
109
|
+
while message = connection.read
|
110
|
+
Console.logger.debug(self, "Reading message:", message)
|
111
|
+
|
112
|
+
if json_message = ::Protocol::WebSocket::JSONMessage.wrap(message)
|
113
|
+
process_message(json_message.parse)
|
114
|
+
else
|
115
|
+
Console.logger.warn(self, "Unhandled message:", message)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
ensure
|
119
|
+
self.close
|
120
|
+
queue_task&.stop
|
93
121
|
end
|
94
122
|
end
|
95
123
|
end
|
data/lib/live/resolver.rb
CHANGED
data/lib/live/version.rb
CHANGED
data/lib/live/view.rb
CHANGED
@@ -24,22 +24,31 @@ require_relative 'element'
|
|
24
24
|
require 'trenni/builder'
|
25
25
|
|
26
26
|
module Live
|
27
|
+
# Represents a single division of content on the page an provides helpers for rendering the content.
|
27
28
|
class View < Element
|
29
|
+
# Replace the content of the client-side element by rendering this view.
|
28
30
|
def replace!
|
29
31
|
rpc(:replace, [@id, self.to_html])
|
30
32
|
end
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
+
# Append to the content of the client-side element by appending the specified element.
|
35
|
+
# @parameter node [Live::Element] The element to append.
|
36
|
+
def append!(element)
|
37
|
+
rpc(:append, [@id, element.to_html])
|
34
38
|
end
|
35
39
|
|
36
|
-
|
37
|
-
|
40
|
+
# Prepend to the content of the client-side element by appending the specified element.
|
41
|
+
# @parameter node [Live::Element] The element to prepend.
|
42
|
+
def prepend!(element)
|
43
|
+
rpc(:prepend, [@id, element.to_html])
|
38
44
|
end
|
39
45
|
|
46
|
+
# Render the element.
|
47
|
+
# @parameter builder [Trenni::Builder] The HTML builder.
|
40
48
|
def render(builder)
|
41
49
|
end
|
42
50
|
|
51
|
+
# @returns [Object] The generated HTML.
|
43
52
|
def to_html
|
44
53
|
Trenni::Builder.fragment do |builder|
|
45
54
|
builder.tag :div, id: @id, class: 'live', data: @data do
|
data.tar.gz.sig
ADDED
Binary file
|
metadata
CHANGED
@@ -1,31 +1,60 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: live
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
|
-
cert_chain:
|
11
|
-
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
|
14
|
+
ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
|
15
|
+
CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
|
16
|
+
MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
|
17
|
+
MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
|
18
|
+
bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
|
19
|
+
igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
|
20
|
+
9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
|
21
|
+
sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
|
22
|
+
e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
|
23
|
+
XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
|
24
|
+
RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
|
25
|
+
tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
|
26
|
+
zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
|
27
|
+
xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
|
28
|
+
BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
|
29
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
|
30
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
|
31
|
+
cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
|
32
|
+
xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
|
33
|
+
c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
|
34
|
+
8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
|
35
|
+
JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
|
36
|
+
eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
|
37
|
+
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
38
|
+
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
39
|
+
-----END CERTIFICATE-----
|
40
|
+
date: 2022-08-22 00:00:00.000000000 Z
|
12
41
|
dependencies:
|
13
42
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
43
|
+
name: async-websocket
|
15
44
|
requirement: !ruby/object:Gem::Requirement
|
16
45
|
requirements:
|
17
|
-
- - "
|
46
|
+
- - "~>"
|
18
47
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
48
|
+
version: 0.22.0
|
20
49
|
type: :runtime
|
21
50
|
prerelease: false
|
22
51
|
version_requirements: !ruby/object:Gem::Requirement
|
23
52
|
requirements:
|
24
|
-
- - "
|
53
|
+
- - "~>"
|
25
54
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
55
|
+
version: 0.22.0
|
27
56
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
57
|
+
name: trenni
|
29
58
|
requirement: !ruby/object:Gem::Requirement
|
30
59
|
requirements:
|
31
60
|
- - ">="
|
@@ -125,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
154
|
- !ruby/object:Gem::Version
|
126
155
|
version: '0'
|
127
156
|
requirements: []
|
128
|
-
rubygems_version: 3.
|
157
|
+
rubygems_version: 3.3.7
|
129
158
|
signing_key:
|
130
159
|
specification_version: 4
|
131
160
|
summary: Live HTML tags updated via a WebSocket.
|
metadata.gz.sig
ADDED
Binary file
|