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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f33999c1164c25bd9fccfac8d7be846b6ad536ae04da0302b450662a7db1624f
4
- data.tar.gz: 0bbd2f3b7b06819b9ae5cb8b057277a1885fe7d44598238a46d21704eb62173c
3
+ metadata.gz: e6f7751e42637cac42f2129ab5acbe1ef1c5109a10ef8f1c380b98e2d986829b
4
+ data.tar.gz: f7686de5fa9fd454568d4ccc5735b9146b70a23643e7611da6a0578d5b1213c9
5
5
  SHA512:
6
- metadata.gz: a5bcd37c979d0155cd70b822f7959ae7d97661c7652f2e945cc9b9ff6ff4a099600894f8348b0b135abeb90dd1156e724ed2b063b68c2c908c5823240b41af3c
7
- data.tar.gz: 68aae390d5d46ca5e81d4f333a40d3db1e9fbf8394d41600576dace4b0a2bd2df5c94ab8795cbdd156bab01e07ecc0b372d84853c5e99c965481b4fb1f9a0ce6
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 handle(event, details)
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
- def handle(id, event, details)
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, details)
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 run(connection)
61
- reader_task = start_reader(connection)
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
- private
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
- def start_reader(connection)
76
- Async do
77
- while message = connection.read
78
- Console.logger.debug(self, "Reading message:", message)
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
- if id = message[:bind] and data = message[:data]
81
- if element = self.resolve(id, data)
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
@@ -34,6 +34,7 @@ module Live
34
34
  @allowed = {}
35
35
  end
36
36
 
37
+ # @attribute [Hash(String, Class)] A map of allowed class names.
37
38
  attr :allowed
38
39
 
39
40
  def freeze
data/lib/live/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Live
4
- VERSION = "0.2.0"
4
+ VERSION = "0.5.0"
5
5
  end
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
- def append!(node)
33
- rpc(:append, [@id, node.to_html])
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
- def prepend!(node)
37
- rpc(:prepend, [@id, node.to_html])
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.2.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
- date: 2021-04-19 00:00:00.000000000 Z
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: trenni
43
+ name: async-websocket
15
44
  requirement: !ruby/object:Gem::Requirement
16
45
  requirements:
17
- - - ">="
46
+ - - "~>"
18
47
  - !ruby/object:Gem::Version
19
- version: '0'
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: '0'
55
+ version: 0.22.0
27
56
  - !ruby/object:Gem::Dependency
28
- name: async-websocket
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.2.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