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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b389272cf70f88ade8e987e6df962c0e73761d738d7fc15e29134a6d3677f072
4
- data.tar.gz: adebf021e1a94bed6fd09a4c90364f3176873fcbfb0b691005262a7095768927
3
+ metadata.gz: bf47630cac82b5611f454d7c61b1db67d8802c91eabc819d44d2e1a82ffd1d96
4
+ data.tar.gz: ce5bcd2dd5a5bc0c98357eb963aaa38ff83c4e312da85c0f3b4e79ca546e6d72
5
5
  SHA512:
6
- metadata.gz: 8f36c200788c094e7662f14dad854db15b5bfe2e04c96e4bf546e2821be91eef549eb574a872476db632e2a6d00cbbe3e3670954756d5b801364d06cfa627809
7
- data.tar.gz: ac325892c144ae36d6417990f76271016782cb196a8cbb7d59645a14a4868804bdd2f61a813de2cff14a8053912115bb1e64961a0ea937023a7ea0ff04d6848e
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 'json'
7
- require 'securerandom'
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, **data)
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 'element'
7
- require_relative 'resolver'
6
+ require_relative "element"
7
+ require_relative "resolver"
8
8
 
9
- require 'async'
10
- require 'async/queue'
9
+ require "async"
10
+ require "async/queue"
11
11
 
12
- require 'protocol/websocket'
13
- require 'protocol/websocket/message'
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
- @resolver.call(id, data)
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
- element.close
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 'bind'
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(['error', message[1], "Could not resolve element!"])
96
+ @updates.enqueue(["error", message[1], "Could not resolve element!"])
75
97
  end
76
- when 'unbind'
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(['error', message[1], "Could not unbind element!"])
104
+ @updates.enqueue(["error", message[1], "Could not unbind element!"])
83
105
  end
84
- when 'event'
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 'element'
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, **data)
46
+ return klass.new(id, data)
47
47
  end
48
48
  end
49
49
  end
data/lib/live/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
6
  module Live
7
- VERSION = "0.12.0"
7
+ VERSION = "0.13.1"
8
8
  end
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 'element'
7
- require 'xrb/builder'
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.tag :div, id: @id, class: 'live', data: @data do
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
@@ -5,6 +5,6 @@
5
5
 
6
6
  require_relative "live/version"
7
7
 
8
- require_relative 'live/page'
9
- require_relative 'live/view'
10
- require_relative 'live/resolver'
8
+ require_relative "live/page"
9
+ require_relative "live/view"
10
+ require_relative "live/resolver"
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
- This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this project must agree to this document to have their contributions accepted.
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
- ### Contributor Covenant
35
+ ### Community Guidelines
36
36
 
37
- This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
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) The client-side JavaScript library.
42
- - [morphdom](https://github.com/patrick-steele-idem/morphdom) Efficiently update the client-side HTML.
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.12.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-07-03 00:00:00.000000000 Z
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.5.11
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