live 0.12.0 → 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/live/element.rb +77 -4
- data/lib/live/page.rb +40 -18
- data/lib/live/resolver.rb +2 -2
- data/lib/live/version.rb +1 -1
- data/lib/live/view.rb +3 -60
- data/lib/live.rb +3 -3
- data/license.md +1 -0
- data/readme.md +9 -5
- data.tar.gz.sig +0 -0
- metadata +4 -3
- 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: bf47630cac82b5611f454d7c61b1db67d8802c91eabc819d44d2e1a82ffd1d96
|
4
|
+
data.tar.gz: ce5bcd2dd5a5bc0c98357eb963aaa38ff83c4e312da85c0f3b4e79ca546e6d72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd5eaca0a5038361c60d13de5bc8b5e8e5ce66f0e828c74c0c199d6a8d9706bee31fbeff1365155b41b59241c2be4f405f6d1186c44b928510d1d94ad7e42000
|
7
|
+
data.tar.gz: 0f0956b2f4a4d79af54ee01e965585d370633eac1ad23ded24c817e43f0b7b8dd681a5187607fe2fdf4fa52be385f4c20ec25a09c55ceee5c00fe846c8a4cde1
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/live/element.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2021-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
6
|
+
require "json"
|
7
|
+
require "securerandom"
|
8
8
|
|
9
9
|
module Live
|
10
10
|
class PageError < RuntimeError
|
@@ -16,13 +16,18 @@ module Live
|
|
16
16
|
SecureRandom.uuid
|
17
17
|
end
|
18
18
|
|
19
|
+
def self.mount(parent, id, data = {})
|
20
|
+
full_id = parent.id + ":" + id
|
21
|
+
|
22
|
+
self.new(full_id, data)
|
23
|
+
end
|
24
|
+
|
19
25
|
# @parameter id [String] The unique identifier within the page.
|
20
26
|
# @parameter data [Hash] The data associated with the element, typically stored as `data-` attributes.
|
21
|
-
def initialize(id = Element.unique_id,
|
27
|
+
def initialize(id = Element.unique_id, data = {})
|
22
28
|
@id = id
|
23
29
|
@data = data
|
24
30
|
@data[:class] ||= self.class.name
|
25
|
-
|
26
31
|
@page = nil
|
27
32
|
end
|
28
33
|
|
@@ -32,6 +37,9 @@ module Live
|
|
32
37
|
# The data associated with the element.
|
33
38
|
attr :data
|
34
39
|
|
40
|
+
# @attribute [Page | Nil] The page this elemenet is bound to.
|
41
|
+
attr :page
|
42
|
+
|
35
43
|
# Generate a JavaScript string which forwards the specified event to the server.
|
36
44
|
# @parameter detail [Hash] The detail associated with the forwarded event.
|
37
45
|
def forward_event(detail = nil)
|
@@ -77,5 +85,70 @@ module Live
|
|
77
85
|
raise PageError, "Element is not bound to a page, make sure to implement #close!"
|
78
86
|
end
|
79
87
|
end
|
88
|
+
|
89
|
+
def script(code, **options)
|
90
|
+
rpc(:script, @id, code, options)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Update the content of the client-side element by rendering this view.
|
94
|
+
def update!(**options)
|
95
|
+
rpc(:update, @id, self.to_html, options)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Replace the content of the client-side element by rendering this view.
|
99
|
+
# @parameter selector [String] The CSS selector to replace.
|
100
|
+
# @parameter node [String] The HTML to replace.
|
101
|
+
def replace(selector, fragment = nil, **options, &block)
|
102
|
+
fragment ||= XRB::Builder.fragment(&block)
|
103
|
+
|
104
|
+
rpc(:replace, selector, fragment.to_s, options)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Prepend to the content of the client-side element by appending the specified element.
|
108
|
+
# @parameter selector [String] The CSS selector to prepend to.
|
109
|
+
# @parameter node [String] The HTML to prepend.
|
110
|
+
def prepend(selector, fragment = nil, **options, &block)
|
111
|
+
fragment ||= XRB::Builder.fragment(&block)
|
112
|
+
|
113
|
+
rpc(:prepend, selector, fragment.to_s, options)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Append to the content of the client-side element by appending the specified element.
|
117
|
+
# @parameter selector [String] The CSS selector to append to.
|
118
|
+
# @parameter node [String] The HTML to prepend.
|
119
|
+
def append(selector, fragment = nil, **options, &block)
|
120
|
+
fragment ||= XRB::Builder.fragment(&block)
|
121
|
+
|
122
|
+
rpc(:append, selector, fragment.to_s, options)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Remove the specified element from the client-side element.
|
126
|
+
# @parameter selector [String] The CSS selector to remove.
|
127
|
+
def remove(selector, **options)
|
128
|
+
rpc(:remove, selector, options)
|
129
|
+
end
|
130
|
+
|
131
|
+
def dispatch_event(selector, type, **options)
|
132
|
+
rpc(:dispatch_event, selector, event, options)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Render the element.
|
136
|
+
# @parameter builder [XRB::Builder] The HTML builder.
|
137
|
+
def render(builder)
|
138
|
+
builder.text(self.class.name)
|
139
|
+
end
|
140
|
+
|
141
|
+
# @returns [Object] The generated HTML.
|
142
|
+
def to_html
|
143
|
+
XRB::Builder.fragment do |builder|
|
144
|
+
render(builder)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Convenience method for rendering the view as a string.
|
149
|
+
# @returns [String] The generated HTML.
|
150
|
+
def to_s
|
151
|
+
to_html.to_s
|
152
|
+
end
|
80
153
|
end
|
81
154
|
end
|
data/lib/live/page.rb
CHANGED
@@ -3,14 +3,16 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2021-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
6
|
+
require_relative "element"
|
7
|
+
require_relative "resolver"
|
8
8
|
|
9
|
-
require
|
10
|
-
require
|
9
|
+
require "async"
|
10
|
+
require "async/queue"
|
11
11
|
|
12
|
-
require
|
13
|
-
require
|
12
|
+
require "protocol/websocket"
|
13
|
+
require "protocol/websocket/message"
|
14
|
+
|
15
|
+
require "console/event/failure"
|
14
16
|
|
15
17
|
module Live
|
16
18
|
# Represents a connected client page with bound dynamic content areas.
|
@@ -20,6 +22,8 @@ module Live
|
|
20
22
|
@resolver = resolver
|
21
23
|
|
22
24
|
@elements = {}
|
25
|
+
@attached = {}
|
26
|
+
|
23
27
|
@updates = Async::Queue.new
|
24
28
|
end
|
25
29
|
|
@@ -34,11 +38,25 @@ module Live
|
|
34
38
|
element.bind(self)
|
35
39
|
end
|
36
40
|
|
41
|
+
# Attach a pre-existing element to the page, so that it may later be bound to this exact instance.
|
42
|
+
# You must later detach the element when it is no longer needed.
|
43
|
+
def attach(element)
|
44
|
+
@attached[element.id] = element
|
45
|
+
end
|
46
|
+
|
47
|
+
def detach(element)
|
48
|
+
if @attached.delete(element.id)
|
49
|
+
element.close
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
37
53
|
# Resolve a client-side element to a server side instance.
|
38
54
|
# @parameter id [String] The unique identifier within the page.
|
39
55
|
# @parameter data [Hash] The data associated with the element, typically stored as `data-` attributes.
|
40
|
-
def resolve(id, data)
|
41
|
-
@
|
56
|
+
def resolve(id, data = {})
|
57
|
+
@attached.fetch(id) do
|
58
|
+
@resolver.call(id, data)
|
59
|
+
end
|
42
60
|
end
|
43
61
|
|
44
62
|
# Handle an event from the client. If the element could not be found, it is silently ignored.
|
@@ -58,30 +76,34 @@ module Live
|
|
58
76
|
|
59
77
|
def close
|
60
78
|
@elements.each do |id, element|
|
61
|
-
|
79
|
+
begin
|
80
|
+
element.close
|
81
|
+
rescue => error
|
82
|
+
Console::Event::Failure.for(error).emit(self)
|
83
|
+
end
|
62
84
|
end
|
63
85
|
end
|
64
86
|
|
65
87
|
# Process a single incoming message from the network.
|
66
88
|
def process_message(message)
|
67
89
|
case message[0]
|
68
|
-
when
|
90
|
+
when "bind"
|
69
91
|
# Bind a client-side element to a server-side element.
|
70
92
|
if element = self.resolve(message[1], message[2])
|
71
93
|
self.bind(element)
|
72
94
|
else
|
73
95
|
Console.warn(self, "Could not resolve element:", message)
|
74
|
-
@updates.enqueue([
|
96
|
+
@updates.enqueue(["error", message[1], "Could not resolve element!"])
|
75
97
|
end
|
76
|
-
when
|
98
|
+
when "unbind"
|
77
99
|
# Unbind a client-side element from a server-side element.
|
78
100
|
if element = @elements.delete(message[1])
|
79
|
-
element.close
|
101
|
+
element.close unless @attached.key?(message[1])
|
80
102
|
else
|
81
103
|
Console.warn(self, "Could not unbind element:", message)
|
82
|
-
@updates.enqueue([
|
104
|
+
@updates.enqueue(["error", message[1], "Could not unbind element!"])
|
83
105
|
end
|
84
|
-
when
|
106
|
+
when "event"
|
85
107
|
# Handle an event from the client.
|
86
108
|
self.handle(message[1], message[2])
|
87
109
|
else
|
@@ -95,14 +117,14 @@ module Live
|
|
95
117
|
Sync do |task|
|
96
118
|
queue_task = task.async do
|
97
119
|
while update = @updates.dequeue
|
98
|
-
Console.debug(self, "Sending update:", update)
|
99
120
|
::Protocol::WebSocket::TextMessage.generate(update).send(connection)
|
121
|
+
|
122
|
+
# Flush the output if there are no more updates:
|
100
123
|
connection.flush if @updates.empty?
|
101
124
|
end
|
102
125
|
end
|
103
126
|
|
104
127
|
while message = connection.read
|
105
|
-
Console.debug(self, "Reading message:", message)
|
106
128
|
process_message(message.parse)
|
107
129
|
end
|
108
130
|
ensure
|
@@ -111,4 +133,4 @@ module Live
|
|
111
133
|
end
|
112
134
|
end
|
113
135
|
end
|
114
|
-
end
|
136
|
+
end
|
data/lib/live/resolver.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2021-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "element"
|
7
7
|
|
8
8
|
module Live
|
9
9
|
# Resolves a client-side tag into a server-side instance.
|
@@ -43,7 +43,7 @@ module Live
|
|
43
43
|
# @returns [Element] The element instance if it was allowed.
|
44
44
|
def call(id, data)
|
45
45
|
if klass = @allowed[data[:class]]
|
46
|
-
return klass.new(id,
|
46
|
+
return klass.new(id, data)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
data/lib/live/version.rb
CHANGED
data/lib/live/view.rb
CHANGED
@@ -3,76 +3,19 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2021-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require
|
6
|
+
require_relative "element"
|
7
|
+
require "xrb/builder"
|
8
8
|
|
9
9
|
module Live
|
10
10
|
# Represents a single division of content on the page an provides helpers for rendering the content.
|
11
11
|
class View < Element
|
12
|
-
def script(code, **options)
|
13
|
-
rpc(:script, @id, code, options)
|
14
|
-
end
|
15
|
-
|
16
|
-
# Update the content of the client-side element by rendering this view.
|
17
|
-
def update!(**options)
|
18
|
-
rpc(:update, @id, self.to_html, options)
|
19
|
-
end
|
20
|
-
|
21
|
-
# Replace the content of the client-side element by rendering this view.
|
22
|
-
# @parameter selector [String] The CSS selector to replace.
|
23
|
-
# @parameter node [String] The HTML to replace.
|
24
|
-
def replace(selector, fragment = nil, **options, &block)
|
25
|
-
fragment ||= XRB::Builder.fragment(&block)
|
26
|
-
|
27
|
-
rpc(:replace, selector, fragment.to_s, options)
|
28
|
-
end
|
29
|
-
|
30
|
-
# Prepend to the content of the client-side element by appending the specified element.
|
31
|
-
# @parameter selector [String] The CSS selector to prepend to.
|
32
|
-
# @parameter node [String] The HTML to prepend.
|
33
|
-
def prepend(selector, fragment = nil, **options, &block)
|
34
|
-
fragment ||= XRB::Builder.fragment(&block)
|
35
|
-
|
36
|
-
rpc(:prepend, selector, fragment.to_s, options)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Append to the content of the client-side element by appending the specified element.
|
40
|
-
# @parameter selector [String] The CSS selector to append to.
|
41
|
-
# @parameter node [String] The HTML to prepend.
|
42
|
-
def append(selector, fragment = nil, **options, &block)
|
43
|
-
fragment ||= XRB::Builder.fragment(&block)
|
44
|
-
|
45
|
-
rpc(:append, selector, fragment.to_s, options)
|
46
|
-
end
|
47
|
-
|
48
|
-
# Remove the specified element from the client-side element.
|
49
|
-
# @parameter selector [String] The CSS selector to remove.
|
50
|
-
def remove(selector, **options)
|
51
|
-
rpc(:remove, selector, options)
|
52
|
-
end
|
53
|
-
|
54
|
-
def dispatch_event(selector, type, **options)
|
55
|
-
rpc(:dispatch_event, selector, event, options)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Render the element.
|
59
|
-
# @parameter builder [XRB::Builder] The HTML builder.
|
60
|
-
def render(builder)
|
61
|
-
end
|
62
|
-
|
63
12
|
# @returns [Object] The generated HTML.
|
64
13
|
def to_html
|
65
14
|
XRB::Builder.fragment do |builder|
|
66
|
-
builder.
|
15
|
+
builder.inline_tag :div, id: @id, class: "live", data: @data do
|
67
16
|
render(builder)
|
68
17
|
end
|
69
18
|
end
|
70
19
|
end
|
71
|
-
|
72
|
-
# Convenience method for rendering the view as a string.
|
73
|
-
# @returns [String] The generated HTML.
|
74
|
-
def to_s
|
75
|
-
to_html.to_s
|
76
|
-
end
|
77
20
|
end
|
78
21
|
end
|
data/lib/live.rb
CHANGED
data/license.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
Copyright, 2021-2024, by Samuel Williams.
|
4
4
|
Copyright, 2023, by Olle Jonsson.
|
5
|
+
Copyright, 2024, by Tatsuhiro Ujihisa.
|
5
6
|
|
6
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
8
|
of this software and associated documentation files (the "Software"), to deal
|
data/readme.md
CHANGED
@@ -30,14 +30,18 @@ We welcome contributions to this project.
|
|
30
30
|
|
31
31
|
### Developer Certificate of Origin
|
32
32
|
|
33
|
-
|
33
|
+
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
34
34
|
|
35
|
-
###
|
35
|
+
### Community Guidelines
|
36
36
|
|
37
|
-
This project is
|
37
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
38
38
|
|
39
39
|
## See Also
|
40
40
|
|
41
|
-
- [live-js](https://github.com/socketry/live-js)
|
42
|
-
- [morphdom](https://github.com/patrick-steele-idem/morphdom)
|
41
|
+
- [live-js](https://github.com/socketry/live-js) – The client-side JavaScript library.
|
42
|
+
- [morphdom](https://github.com/patrick-steele-idem/morphdom) – Efficiently update the client-side HTML.
|
43
43
|
- [stimulus-reflex](https://github.com/hopsoft/stimulus_reflex) — An alternative framework which provides similar functionality.
|
44
|
+
|
45
|
+
### Examples
|
46
|
+
|
47
|
+
- [Flappy Bird](https://github.com/socketry/flappy-bird) – A clone of the classic Flappy Bird game.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: live
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
- Olle Jonsson
|
9
|
+
- Tatsuhiro Ujihisa
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain:
|
@@ -38,7 +39,7 @@ cert_chain:
|
|
38
39
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
39
40
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
40
41
|
-----END CERTIFICATE-----
|
41
|
-
date: 2024-
|
42
|
+
date: 2024-09-23 00:00:00.000000000 Z
|
42
43
|
dependencies:
|
43
44
|
- !ruby/object:Gem::Dependency
|
44
45
|
name: async-websocket
|
@@ -103,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
104
|
- !ruby/object:Gem::Version
|
104
105
|
version: '0'
|
105
106
|
requirements: []
|
106
|
-
rubygems_version: 3.
|
107
|
+
rubygems_version: 3.4.19
|
107
108
|
signing_key:
|
108
109
|
specification_version: 4
|
109
110
|
summary: Live HTML tags updated via a WebSocket.
|
metadata.gz.sig
CHANGED
Binary file
|