live 0.2.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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