live 0.11.0 → 0.13.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: 41876a4b4870415b70d8e5747dce78f8eeed867b524bad32b9a0146c56296f41
4
- data.tar.gz: ba656166a150f8d5a2da0ce37bc358159eb082db4b6340ef751cc2ff3b9f00d1
3
+ metadata.gz: '08c6f4d6d5b5fc0e7ba635b617256c06ca84c925b528b99e4ab758bf6461af19'
4
+ data.tar.gz: 69266f1a602d85f9f3d14104a2548304498ed38310f34ffa5dfe9e3e9d48aed6
5
5
  SHA512:
6
- metadata.gz: 39d76c89db7212a85596ef74630816a88107fc752ac8e932f76b43d5e2d943bbb4e631f4a78a44b7c9e4c6ef2095defce9c5d8da83f9334f171afe20d9a41ad6
7
- data.tar.gz: 86484b279c0a6c16f4d46fe2220627ba35a74810413108377bf4f8002c84c95681bea4f1d7c760842b48bdc10e5c5c7cecdc9e3c7723700df3f415e51256d1f4
6
+ metadata.gz: a4e608d98be19c629dd8cba31cb5be8eeac52de4a5fb007ba4822f0baf8b473e16aa4db604585b8ff481d39292c2986408b2d3977667b77552de1cff31b4a85c
7
+ data.tar.gz: 1fa963047b5369012f008c57061a585345d98922082d3c744b032ae15c8599a928f7a9c5b12169ac2242cb1f013b55404cce13f4cd4a2763259eda23b8fe06ac
checksums.yaml.gz.sig CHANGED
@@ -1,4 +1 @@
1
- b׺ޚW$���;����"V�>��ǣz��6)���K)m�N�P1e��5r{� 睷��'PR���Xr���M�~O�=t�H!ߑ����� ze��&�Pt>��%�Ŕ
2
- �p��m�j ]P�H2��E�
3
- #�Q��fJF�`��ʹ���Ⱥ2��`
4
- ��"����u�#�E�4-�r�<ހ�O�7�-�Ñ���ĨCqvBB��T�?F�,�����e=��q��X�.43�O�!��O�"y΅="�j��9
1
+ U̦G�� x��?l19a!HT��t���4��c>�7�{TݨC���M�f�]�ij2��2�(QM(�� rJ:�cNG@$�ң���Ε_T+uǧr� * �,(��=+�U�6�U h/|bS�}C2"]�U28$F铄(S5N!�8���@D�a�bsD3�ƵN��a������O`��;��rd�����3)�{��EuxrX��w��,�� �`ۓ�����~�~2��V����z���P�lB+y��#���9z��C���F���c K_�{�$+�}��8*�$ h��gp�G�b@���é�U����`���~�HK�SB���#k�d�ǥ���,u��@�mw \������(TtBsBo��O%��
data/lib/live/element.rb CHANGED
@@ -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
@@ -9,7 +9,8 @@ require_relative 'resolver'
9
9
  require 'async'
10
10
  require 'async/queue'
11
11
 
12
- require 'protocol/websocket/json_message'
12
+ require 'protocol/websocket'
13
+ require 'protocol/websocket/message'
13
14
 
14
15
  module Live
15
16
  # Represents a connected client page with bound dynamic content areas.
@@ -19,6 +20,8 @@ module Live
19
20
  @resolver = resolver
20
21
 
21
22
  @elements = {}
23
+ @attached = {}
24
+
22
25
  @updates = Async::Queue.new
23
26
  end
24
27
 
@@ -33,11 +36,25 @@ module Live
33
36
  element.bind(self)
34
37
  end
35
38
 
39
+ # Attach a pre-existing element to the page, so that it may later be bound to this exact instance.
40
+ # You must later detach the element when it is no longer needed.
41
+ def attach(element)
42
+ @attached[element.id] = element
43
+ end
44
+
45
+ def detach(element)
46
+ if @attached.delete(element.id)
47
+ element.close
48
+ end
49
+ end
50
+
36
51
  # Resolve a client-side element to a server side instance.
37
52
  # @parameter id [String] The unique identifier within the page.
38
53
  # @parameter data [Hash] The data associated with the element, typically stored as `data-` attributes.
39
- def resolve(id, data)
40
- @resolver.call(id, data)
54
+ def resolve(id, data = {})
55
+ @attached.fetch(id) do
56
+ @resolver.call(id, data)
57
+ end
41
58
  end
42
59
 
43
60
  # Handle an event from the client. If the element could not be found, it is silently ignored.
@@ -75,7 +92,7 @@ module Live
75
92
  when 'unbind'
76
93
  # Unbind a client-side element from a server-side element.
77
94
  if element = @elements.delete(message[1])
78
- element.close
95
+ element.close unless @attached.key?(message[1])
79
96
  else
80
97
  Console.warn(self, "Could not unbind element:", message)
81
98
  @updates.enqueue(['error', message[1], "Could not unbind element!"])
@@ -91,27 +108,23 @@ module Live
91
108
  # Run the event handling loop with the given websocket connection.
92
109
  # @parameter connection [Async::WebSocket::Connection]
93
110
  def run(connection)
94
- queue_task = Async do
95
- while update = @updates.dequeue
96
- Console.debug(self, "Sending update:", update)
97
-
98
- connection.write(::Protocol::WebSocket::JSONMessage.generate(update))
99
- connection.flush if @updates.empty?
111
+ Sync do |task|
112
+ queue_task = task.async do
113
+ while update = @updates.dequeue
114
+ Console.debug(self, "Sending update:", update)
115
+ ::Protocol::WebSocket::TextMessage.generate(update).send(connection)
116
+ connection.flush if @updates.empty?
117
+ end
100
118
  end
101
- end
102
-
103
- while message = connection.read
104
- Console.debug(self, "Reading message:", message)
105
119
 
106
- if json_message = ::Protocol::WebSocket::JSONMessage.wrap(message)
107
- process_message(json_message.parse)
108
- else
109
- Console.warn(self, "Unhandled message:", message)
120
+ while message = connection.read
121
+ Console.debug(self, "Reading message:", message)
122
+ process_message(message.parse)
110
123
  end
124
+ ensure
125
+ self.close
126
+ queue_task&.stop
111
127
  end
112
- ensure
113
- self.close
114
- queue_task&.stop
115
128
  end
116
129
  end
117
130
  end
data/lib/live/resolver.rb CHANGED
@@ -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.11.0"
7
+ VERSION = "0.13.0"
8
8
  end
data/lib/live/view.rb CHANGED
@@ -9,70 +9,13 @@ require 'xrb/builder'
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/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.11.0
4
+ version: 0.13.0
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-05-12 00:00:00.000000000 Z
42
+ date: 2024-07-05 00:00:00.000000000 Z
42
43
  dependencies:
43
44
  - !ruby/object:Gem::Dependency
44
45
  name: async-websocket
@@ -46,14 +47,14 @@ dependencies:
46
47
  requirements:
47
48
  - - "~>"
48
49
  - !ruby/object:Gem::Version
49
- version: '0.23'
50
+ version: '0.27'
50
51
  type: :runtime
51
52
  prerelease: false
52
53
  version_requirements: !ruby/object:Gem::Requirement
53
54
  requirements:
54
55
  - - "~>"
55
56
  - !ruby/object:Gem::Version
56
- version: '0.23'
57
+ version: '0.27'
57
58
  - !ruby/object:Gem::Dependency
58
59
  name: xrb
59
60
  requirement: !ruby/object:Gem::Requirement
@@ -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.3
107
+ rubygems_version: 3.5.11
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