datastar 1.0.0.beta.3 → 1.0.0.pre.1
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 +4 -4
- data/README.md +57 -26
- data/examples/test.ru +8 -12
- data/lib/datastar/consts.rb +26 -35
- data/lib/datastar/dispatcher.rb +26 -24
- data/lib/datastar/server_sent_event_generator.rb +94 -47
- data/lib/datastar/version.rb +1 -1
- metadata +31 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc457ac67196befbcb6a6c1510b970e81e728261dff0987d42d7760e358ad7fe
|
4
|
+
data.tar.gz: 5dcf48dc09ee9adb4c08f53d2ef63df316a34929ca6d3bcf0d15cf0d27083540
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edba4b371362360de36fcfb7379a80b96ac5743c7d765a264bf8dbd21a223147e44677d05fea02f1136d8db5aa84579af943e3a0593662d4cecdbfe5155df96a
|
7
|
+
data.tar.gz: fa551cb816ca6cfe0ea46ec53c9b1d51256009c6eb378b77df51a1376a297ec3fea97bebd26ef8dab330d857f366bbbacf32dd7e1a6c20d97b9e097942ee5301
|
data/README.md
CHANGED
@@ -44,7 +44,7 @@ There are two ways to use this gem in HTTP handlers:
|
|
44
44
|
#### One-off update:
|
45
45
|
|
46
46
|
```ruby
|
47
|
-
datastar.
|
47
|
+
datastar.patch_elements(%(<h1 id="title">Hello, World!</h1>))
|
48
48
|
```
|
49
49
|
In this mode, the response is closed after the fragment is sent.
|
50
50
|
|
@@ -52,11 +52,11 @@ In this mode, the response is closed after the fragment is sent.
|
|
52
52
|
|
53
53
|
```ruby
|
54
54
|
datastar.stream do |sse|
|
55
|
-
sse.
|
55
|
+
sse.patch_elements(%(<h1 id="title">Hello, World!</h1>))
|
56
56
|
# Streaming multiple updates
|
57
57
|
100.times do |i|
|
58
58
|
sleep 1
|
59
|
-
sse.
|
59
|
+
sse.patch_elements(%(<h1 id="title">Hello, World #{i}!</h1>))
|
60
60
|
end
|
61
61
|
end
|
62
62
|
```
|
@@ -72,14 +72,14 @@ Their updates are linearized and sent to the browser as they are produced.
|
|
72
72
|
datastar.stream do |sse|
|
73
73
|
100.times do |i|
|
74
74
|
sleep 1
|
75
|
-
sse.
|
75
|
+
sse.patch_elements(%(<h1 id="slow">#{i}!</h1>))
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
79
|
datastar.stream do |sse|
|
80
80
|
1000.times do |i|
|
81
81
|
sleep 0.1
|
82
|
-
sse.
|
82
|
+
sse.patch_elements(%(<h1 id="fast">#{i}!</h1>))
|
83
83
|
end
|
84
84
|
end
|
85
85
|
```
|
@@ -90,48 +90,73 @@ See the [examples](https://github.com/starfederation/datastar/tree/main/examples
|
|
90
90
|
|
91
91
|
All these methods are available in both the one-off and the streaming modes.
|
92
92
|
|
93
|
-
#### `
|
94
|
-
See https://data-star.dev/reference/sse_events#datastar-
|
93
|
+
#### `patch_elements`
|
94
|
+
See https://data-star.dev/reference/sse_events#datastar-patch-elements
|
95
95
|
|
96
96
|
```ruby
|
97
|
-
sse.
|
97
|
+
sse.patch_elements(%(<div id="foo">\n<span>hello</span>\n</div>))
|
98
98
|
|
99
99
|
# or a Phlex view object
|
100
|
-
sse.
|
100
|
+
sse.patch_elements(UserComponent.new)
|
101
101
|
|
102
102
|
# Or pass options
|
103
|
-
sse.
|
103
|
+
sse.patch_elements(
|
104
104
|
%(<div id="foo">\n<span>hello</span>\n</div>),
|
105
|
-
|
105
|
+
mode: 'append'
|
106
106
|
)
|
107
107
|
```
|
108
108
|
|
109
|
-
|
110
|
-
See https://data-star.dev/reference/sse_events#datastar-remove-fragments
|
109
|
+
You can patch multiple elements at once by passing an array of elements (or components):
|
111
110
|
|
112
111
|
```ruby
|
113
|
-
sse.
|
112
|
+
sse.patch_elements([
|
113
|
+
%(<div id="foo">\n<span>hello</span>\n</div>),
|
114
|
+
%(<div id="bar">\n<span>world</span>\n</div>)
|
115
|
+
])
|
116
|
+
```
|
117
|
+
|
118
|
+
#### `remove_elements`
|
119
|
+
|
120
|
+
Sugar on top of `#patch_elements`
|
121
|
+
See https://data-star.dev/reference/sse_events#datastar-patch-elements
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
sse.remove_elements('#users')
|
114
125
|
```
|
115
126
|
|
116
|
-
#### `
|
117
|
-
See https://data-star.dev/reference/sse_events#datastar-
|
127
|
+
#### `patch_signals`
|
128
|
+
See https://data-star.dev/reference/sse_events#datastar-patch-signals
|
118
129
|
|
119
130
|
```ruby
|
120
|
-
sse.
|
131
|
+
sse.patch_signals(count: 4, user: { name: 'John' })
|
121
132
|
```
|
122
133
|
|
123
134
|
#### `remove_signals`
|
124
|
-
|
135
|
+
|
136
|
+
Sugar on top of `#patch_signals`
|
125
137
|
|
126
138
|
```ruby
|
127
139
|
sse.remove_signals(['user.name', 'user.email'])
|
128
140
|
```
|
129
141
|
|
130
142
|
#### `execute_script`
|
131
|
-
|
143
|
+
|
144
|
+
Sugar on top of `#patch_elements`. Appends a temporary `<script>` tag to the DOM, which will execute the script in the browser.
|
132
145
|
|
133
146
|
```ruby
|
134
|
-
sse.
|
147
|
+
sse.execute_script(%(alert('Hello World!'))
|
148
|
+
```
|
149
|
+
|
150
|
+
Pass `attributes` that will be added to the `<script>` tag:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
sse.execute_script(%(alert('Hello World!')), attributes: { type: 'text/javascript' })
|
154
|
+
```
|
155
|
+
|
156
|
+
These script tags are automatically removed after execution, so they can be used to run one-off scripts in the browser. Pass `auto_remove: false` if you want to keep the script tag in the DOM.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
sse.execute_script(%(alert('Hello World!')), auto_remove: false)
|
135
160
|
```
|
136
161
|
|
137
162
|
#### `signals`
|
@@ -264,25 +289,25 @@ datastar.stream do |sse|
|
|
264
289
|
10.times do |i|
|
265
290
|
sleep 1
|
266
291
|
tpl = render_to_string('events/user', layout: false, locals: { name: "David #{i}" })
|
267
|
-
sse.
|
292
|
+
sse.patch_elements tpl
|
268
293
|
end
|
269
294
|
end
|
270
295
|
```
|
271
296
|
|
272
297
|
### Rendering Phlex components
|
273
298
|
|
274
|
-
`#
|
299
|
+
`#patch_elements` supports [Phlex](https://www.phlex.fun) component instances.
|
275
300
|
|
276
301
|
```ruby
|
277
|
-
sse.
|
302
|
+
sse.patch_elements(UserComponent.new(user: User.first))
|
278
303
|
```
|
279
304
|
|
280
305
|
### Rendering ViewComponent instances
|
281
306
|
|
282
|
-
`#
|
307
|
+
`#patch_elements` also works with [ViewComponent](https://viewcomponent.org) instances.
|
283
308
|
|
284
309
|
```ruby
|
285
|
-
sse.
|
310
|
+
sse.patch_elements(UserViewComponent.new(user: User.first))
|
286
311
|
```
|
287
312
|
|
288
313
|
### Rendering `#render_in(view_context)` interfaces
|
@@ -302,7 +327,7 @@ end
|
|
302
327
|
```
|
303
328
|
|
304
329
|
```ruby
|
305
|
-
sse.
|
330
|
+
sse.patch_elements MyComponent.new('Joe')
|
306
331
|
```
|
307
332
|
|
308
333
|
|
@@ -338,6 +363,12 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
338
363
|
|
339
364
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
340
365
|
|
366
|
+
### Building
|
367
|
+
|
368
|
+
To build `consts.rb` file from template, run Docker and run `make task build`
|
369
|
+
|
370
|
+
The template is located at `build/consts_ruby.gtpl`.
|
371
|
+
|
341
372
|
## Contributing
|
342
373
|
|
343
374
|
Bug reports and pull requests are welcome on GitHub at https://github.com/starfederation/datastar.
|
data/examples/test.ru
CHANGED
@@ -35,21 +35,17 @@ run do |env|
|
|
35
35
|
sse.signals['events'].each do |event|
|
36
36
|
type = event.delete('type')
|
37
37
|
case type
|
38
|
-
when '
|
39
|
-
arg = event.delete('signals')
|
40
|
-
sse.
|
41
|
-
when 'removeSignals'
|
42
|
-
arg = event.delete('paths')
|
43
|
-
sse.remove_signals(arg, event)
|
38
|
+
when 'patchSignals'
|
39
|
+
arg = event.delete('signals') || event.delete('signals-raw')
|
40
|
+
sse.patch_signals(arg, event)
|
44
41
|
when 'executeScript'
|
45
42
|
arg = event.delete('script')
|
46
43
|
sse.execute_script(arg, event)
|
47
|
-
when '
|
48
|
-
arg = event.delete('
|
49
|
-
sse.
|
50
|
-
|
51
|
-
|
52
|
-
sse.remove_fragments(arg, event)
|
44
|
+
when 'patchElements'
|
45
|
+
arg = event.delete('elements')
|
46
|
+
sse.patch_elements(arg, event)
|
47
|
+
else
|
48
|
+
raise "Unknown event type: #{type}"
|
53
49
|
end
|
54
50
|
end
|
55
51
|
end
|
data/lib/datastar/consts.rb
CHANGED
@@ -4,63 +4,54 @@
|
|
4
4
|
module Datastar
|
5
5
|
module Consts
|
6
6
|
DATASTAR_KEY = 'datastar'
|
7
|
-
VERSION = '1.0.0-
|
7
|
+
VERSION = '1.0.0-RC.13'
|
8
8
|
|
9
9
|
# The default duration for retrying SSE on connection reset. This is part of the underlying retry mechanism of SSE.
|
10
10
|
DEFAULT_SSE_RETRY_DURATION = 1000
|
11
11
|
|
12
|
-
# Should
|
13
|
-
|
12
|
+
# Should elements be patched using the ViewTransition API?
|
13
|
+
DEFAULT_ELEMENTS_USE_VIEW_TRANSITIONS = false
|
14
14
|
|
15
|
-
# Should a given set of signals
|
16
|
-
|
15
|
+
# Should a given set of signals patch if they are missing?
|
16
|
+
DEFAULT_PATCH_SIGNALS_ONLY_IF_MISSING = false
|
17
17
|
|
18
|
-
|
19
|
-
DEFAULT_EXECUTE_SCRIPT_AUTO_REMOVE = true
|
18
|
+
module ElementPatchMode
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
module FragmentMergeMode
|
25
|
-
|
26
|
-
# Morphs the fragment into the existing element using idiomorph.
|
27
|
-
MORPH = 'morph'
|
20
|
+
# Morphs the element into the existing element.
|
21
|
+
OUTER = 'outer';
|
28
22
|
|
29
23
|
# Replaces the inner HTML of the existing element.
|
30
|
-
INNER = 'inner'
|
24
|
+
INNER = 'inner';
|
31
25
|
|
32
|
-
#
|
33
|
-
|
26
|
+
# Removes the existing element.
|
27
|
+
REMOVE = 'remove';
|
34
28
|
|
35
|
-
#
|
36
|
-
|
29
|
+
# Replaces the existing element with the new element.
|
30
|
+
REPLACE = 'replace';
|
37
31
|
|
38
|
-
#
|
39
|
-
|
32
|
+
# Prepends the element inside to the existing element.
|
33
|
+
PREPEND = 'prepend';
|
40
34
|
|
41
|
-
#
|
42
|
-
|
35
|
+
# Appends the element inside the existing element.
|
36
|
+
APPEND = 'append';
|
43
37
|
|
44
|
-
# Inserts the
|
45
|
-
|
38
|
+
# Inserts the element before the existing element.
|
39
|
+
BEFORE = 'before';
|
46
40
|
|
47
|
-
#
|
48
|
-
|
41
|
+
# Inserts the element after the existing element.
|
42
|
+
AFTER = 'after';
|
49
43
|
end
|
50
44
|
|
51
|
-
|
52
|
-
|
45
|
+
|
46
|
+
# The mode in which an element is patched into the DOM.
|
47
|
+
DEFAULT_ELEMENT_PATCH_MODE = ElementPatchMode::OUTER
|
53
48
|
|
54
49
|
# Dataline literals.
|
55
50
|
SELECTOR_DATALINE_LITERAL = 'selector'
|
56
|
-
|
57
|
-
|
51
|
+
MODE_DATALINE_LITERAL = 'mode'
|
52
|
+
ELEMENTS_DATALINE_LITERAL = 'elements'
|
58
53
|
USE_VIEW_TRANSITION_DATALINE_LITERAL = 'useViewTransition'
|
59
54
|
SIGNALS_DATALINE_LITERAL = 'signals'
|
60
55
|
ONLY_IF_MISSING_DATALINE_LITERAL = 'onlyIfMissing'
|
61
|
-
PATHS_DATALINE_LITERAL = 'paths'
|
62
|
-
SCRIPT_DATALINE_LITERAL = 'script'
|
63
|
-
ATTRIBUTES_DATALINE_LITERAL = 'attributes'
|
64
|
-
AUTO_REMOVE_DATALINE_LITERAL = 'autoRemove'
|
65
56
|
end
|
66
57
|
end
|
data/lib/datastar/dispatcher.rb
CHANGED
@@ -10,20 +10,21 @@ module Datastar
|
|
10
10
|
# datastar = Datastar.new(request:, response:, view_context: self)
|
11
11
|
#
|
12
12
|
# # One-off fragment response
|
13
|
-
# datastar.
|
13
|
+
# datastar.patch_elements(template)
|
14
14
|
#
|
15
15
|
# # Streaming response with multiple messages
|
16
16
|
# datastar.stream do |sse|
|
17
|
-
# sse.
|
17
|
+
# sse.patch_elements(template)
|
18
18
|
# 10.times do |i|
|
19
19
|
# sleep 0.1
|
20
|
-
# sse.
|
20
|
+
# sse.patch_signals(count: i)
|
21
21
|
# end
|
22
22
|
# end
|
23
23
|
#
|
24
24
|
class Dispatcher
|
25
25
|
BLANK_BODY = [].freeze
|
26
26
|
SSE_CONTENT_TYPE = 'text/event-stream'
|
27
|
+
SSE_ACCEPT_EXP = /text\/event-stream/
|
27
28
|
HTTP_ACCEPT = 'HTTP_ACCEPT'
|
28
29
|
HTTP1 = 'HTTP/1.1'
|
29
30
|
|
@@ -72,7 +73,7 @@ module Datastar
|
|
72
73
|
# Check if the request accepts SSE responses
|
73
74
|
# @return [Boolean]
|
74
75
|
def sse?
|
75
|
-
@request.get_header(HTTP_ACCEPT)
|
76
|
+
!!(@request.get_header(HTTP_ACCEPT).to_s =~ SSE_ACCEPT_EXP)
|
76
77
|
end
|
77
78
|
|
78
79
|
# Register an on-connect callback
|
@@ -119,47 +120,48 @@ module Datastar
|
|
119
120
|
@signals ||= parse_signals(request).freeze
|
120
121
|
end
|
121
122
|
|
122
|
-
# Send one-off
|
123
|
-
# See https://data-star.dev/reference/sse_events#datastar-
|
123
|
+
# Send one-off elements to the UI
|
124
|
+
# See https://data-star.dev/reference/sse_events#datastar-patch-elements
|
124
125
|
# @example
|
125
126
|
#
|
126
|
-
# datastar.
|
127
|
+
# datastar.patch_elements(%(<div id="foo">\n<span>hello</span>\n</div>\n))
|
127
128
|
# # or a Phlex view object
|
128
|
-
# datastar.
|
129
|
+
# datastar.patch_elements(UserComponet.new)
|
129
130
|
#
|
130
|
-
# @param
|
131
|
+
# @param elements [String, #call(view_context: Object) => Object] the HTML elements or object
|
131
132
|
# @param options [Hash] the options to send with the message
|
132
|
-
def
|
133
|
+
def patch_elements(elements, options = BLANK_OPTIONS)
|
133
134
|
stream_no_heartbeat do |sse|
|
134
|
-
sse.
|
135
|
+
sse.patch_elements(elements, options)
|
135
136
|
end
|
136
137
|
end
|
137
138
|
|
138
|
-
# One-off remove
|
139
|
-
#
|
139
|
+
# One-off remove elements from the UI
|
140
|
+
# Sugar on top of patch-elements with mode: 'remove'
|
141
|
+
# See https://data-star.dev/reference/sse_events#datastar-patch-elements
|
140
142
|
# @example
|
141
143
|
#
|
142
|
-
# datastar.
|
144
|
+
# datastar.remove_elements('#users')
|
143
145
|
#
|
144
146
|
# @param selector [String] a CSS selector for the fragment to remove
|
145
147
|
# @param options [Hash] the options to send with the message
|
146
|
-
def
|
148
|
+
def remove_elements(selector, options = BLANK_OPTIONS)
|
147
149
|
stream_no_heartbeat do |sse|
|
148
|
-
sse.
|
150
|
+
sse.remove_elements(selector, options)
|
149
151
|
end
|
150
152
|
end
|
151
153
|
|
152
|
-
# One-off
|
153
|
-
# See https://data-star.dev/reference/sse_events#datastar-
|
154
|
+
# One-off patch signals in the UI
|
155
|
+
# See https://data-star.dev/reference/sse_events#datastar-patch-signals
|
154
156
|
# @example
|
155
157
|
#
|
156
|
-
# datastar.
|
158
|
+
# datastar.patch_signals(count: 1, toggle: true)
|
157
159
|
#
|
158
|
-
# @param signals [Hash] signals to merge
|
160
|
+
# @param signals [Hash, String] signals to merge
|
159
161
|
# @param options [Hash] the options to send with the message
|
160
|
-
def
|
162
|
+
def patch_signals(signals, options = BLANK_OPTIONS)
|
161
163
|
stream_no_heartbeat do |sse|
|
162
|
-
sse.
|
164
|
+
sse.patch_signals(signals, options)
|
163
165
|
end
|
164
166
|
end
|
165
167
|
|
@@ -209,9 +211,9 @@ module Datastar
|
|
209
211
|
#
|
210
212
|
# datastar.stream do |sse|
|
211
213
|
# total = 300
|
212
|
-
# sse.
|
214
|
+
# sse.patch_elements(%(<progress data-signal-progress="0" id="progress" max="#{total}" data-attr-value="$progress">0</progress>))
|
213
215
|
# total.times do |i|
|
214
|
-
# sse.
|
216
|
+
# sse.patch_signals(progress: i)
|
215
217
|
# end
|
216
218
|
# end
|
217
219
|
#
|
@@ -4,7 +4,7 @@ require 'json'
|
|
4
4
|
|
5
5
|
module Datastar
|
6
6
|
class ServerSentEventGenerator
|
7
|
-
MSG_END = "\n
|
7
|
+
MSG_END = "\n"
|
8
8
|
|
9
9
|
SSE_OPTION_MAPPING = {
|
10
10
|
'eventId' => 'id',
|
@@ -15,20 +15,12 @@ module Datastar
|
|
15
15
|
|
16
16
|
OPTION_DEFAULTS = {
|
17
17
|
'retry' => Consts::DEFAULT_SSE_RETRY_DURATION,
|
18
|
-
Consts::
|
19
|
-
Consts::
|
20
|
-
Consts::
|
21
|
-
Consts::ONLY_IF_MISSING_DATALINE_LITERAL => Consts::DEFAULT_MERGE_SIGNALS_ONLY_IF_MISSING,
|
18
|
+
Consts::MODE_DATALINE_LITERAL => Consts::DEFAULT_ELEMENT_PATCH_MODE,
|
19
|
+
Consts::USE_VIEW_TRANSITION_DATALINE_LITERAL => Consts::DEFAULT_ELEMENTS_USE_VIEW_TRANSITIONS,
|
20
|
+
Consts::ONLY_IF_MISSING_DATALINE_LITERAL => Consts::DEFAULT_PATCH_SIGNALS_ONLY_IF_MISSING,
|
22
21
|
}.freeze
|
23
22
|
|
24
|
-
|
25
|
-
# 'type' => 'module'
|
26
|
-
# }.freeze
|
27
|
-
ATTRIBUTE_DEFAULTS = Consts::DEFAULT_EXECUTE_SCRIPT_ATTRIBUTES
|
28
|
-
.split("\n")
|
29
|
-
.map { |attr| attr.split(' ') }
|
30
|
-
.to_h
|
31
|
-
.freeze
|
23
|
+
SIGNAL_SEPARATOR = '.'
|
32
24
|
|
33
25
|
attr_reader :signals
|
34
26
|
|
@@ -45,59 +37,71 @@ module Datastar
|
|
45
37
|
@stream << MSG_END
|
46
38
|
end
|
47
39
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
fragments.render_in(view_context)
|
53
|
-
elsif fragments.respond_to?(:call)
|
54
|
-
fragments.call(view_context:)
|
55
|
-
else
|
56
|
-
fragments.to_s
|
40
|
+
def patch_elements(elements, options = BLANK_OPTIONS)
|
41
|
+
elements = Array(elements).compact
|
42
|
+
rendered_elements = elements.map do |element|
|
43
|
+
render_element(element)
|
57
44
|
end
|
58
45
|
|
59
|
-
|
46
|
+
element_lines = rendered_elements.flat_map do |el|
|
47
|
+
el.to_s.split("\n")
|
48
|
+
end
|
60
49
|
|
61
|
-
buffer = +"event: datastar-
|
50
|
+
buffer = +"event: datastar-patch-elements\n"
|
62
51
|
build_options(options, buffer)
|
63
|
-
|
52
|
+
element_lines.each { |line| buffer << "data: #{Consts::ELEMENTS_DATALINE_LITERAL} #{line}\n" }
|
64
53
|
|
65
54
|
write(buffer)
|
66
55
|
end
|
67
56
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
57
|
+
def remove_elements(selector, options = BLANK_OPTIONS)
|
58
|
+
patch_elements(
|
59
|
+
nil,
|
60
|
+
options.merge(
|
61
|
+
Consts::MODE_DATALINE_LITERAL => Consts::ElementPatchMode::REMOVE,
|
62
|
+
selector:
|
63
|
+
)
|
64
|
+
)
|
73
65
|
end
|
74
66
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
buffer = +"event: datastar-merge-signals\n"
|
67
|
+
def patch_signals(signals, options = BLANK_OPTIONS)
|
68
|
+
buffer = +"event: datastar-patch-signals\n"
|
79
69
|
build_options(options, buffer)
|
80
|
-
|
70
|
+
case signals
|
71
|
+
when Hash
|
72
|
+
signals = JSON.dump(signals)
|
73
|
+
buffer << "data: signals #{signals}\n"
|
74
|
+
when String
|
75
|
+
multi_data_lines(signals, buffer, Consts::SIGNALS_DATALINE_LITERAL)
|
76
|
+
end
|
81
77
|
write(buffer)
|
82
78
|
end
|
83
79
|
|
84
80
|
def remove_signals(paths, options = BLANK_OPTIONS)
|
85
81
|
paths = [paths].flatten
|
82
|
+
signals = paths.each.with_object({}) do |path, acc|
|
83
|
+
parts = path.split(SIGNAL_SEPARATOR)
|
84
|
+
set_nested_value(acc, parts, nil)
|
85
|
+
end
|
86
86
|
|
87
|
-
|
88
|
-
build_options(options, buffer)
|
89
|
-
paths.each { |path| buffer << "data: paths #{path}\n" }
|
90
|
-
write(buffer)
|
87
|
+
patch_signals(signals, options)
|
91
88
|
end
|
92
89
|
|
93
90
|
def execute_script(script, options = BLANK_OPTIONS)
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
91
|
+
options = camelize_keys(options)
|
92
|
+
auto_remove = options.key?('autoRemove') ? options.delete('autoRemove') : true
|
93
|
+
attributes = options.delete('attributes') || BLANK_OPTIONS
|
94
|
+
script_tag = +"<script"
|
95
|
+
attributes.each do |k, v|
|
96
|
+
script_tag << %( #{camelize(k)}="#{v}")
|
99
97
|
end
|
100
|
-
|
98
|
+
script_tag << %( data-effect="el.remove()") if auto_remove
|
99
|
+
script_tag << ">#{script}</script>"
|
100
|
+
|
101
|
+
options[Consts::SELECTOR_DATALINE_LITERAL] = 'body'
|
102
|
+
options[Consts::MODE_DATALINE_LITERAL] = Consts::ElementPatchMode::APPEND
|
103
|
+
|
104
|
+
patch_elements(script_tag, options)
|
101
105
|
end
|
102
106
|
|
103
107
|
def redirect(url)
|
@@ -113,6 +117,18 @@ module Datastar
|
|
113
117
|
|
114
118
|
attr_reader :view_context, :stream
|
115
119
|
|
120
|
+
# Support Phlex components
|
121
|
+
# And Rails' #render_in interface
|
122
|
+
def render_element(element)
|
123
|
+
if element.respond_to?(:render_in)
|
124
|
+
element.render_in(view_context)
|
125
|
+
elsif element.respond_to?(:call)
|
126
|
+
element.call(view_context:)
|
127
|
+
else
|
128
|
+
element
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
116
132
|
def build_options(options, buffer)
|
117
133
|
options.each do |k, v|
|
118
134
|
k = camelize(k)
|
@@ -121,8 +137,13 @@ module Datastar
|
|
121
137
|
buffer << "#{sse_key}: #{v}\n" unless v == default_value
|
122
138
|
elsif v.is_a?(Hash)
|
123
139
|
v.each do |kk, vv|
|
124
|
-
|
125
|
-
|
140
|
+
buffer << "data: #{k} #{kk} #{vv}\n"
|
141
|
+
end
|
142
|
+
elsif v.is_a?(Array)
|
143
|
+
if k == Consts::SELECTOR_DATALINE_LITERAL
|
144
|
+
buffer << "data: #{k} #{v.join(', ')}\n"
|
145
|
+
else
|
146
|
+
buffer << "data: #{k} #{v.join(' ')}\n"
|
126
147
|
end
|
127
148
|
else
|
128
149
|
default_value = OPTION_DEFAULTS[k]
|
@@ -131,8 +152,34 @@ module Datastar
|
|
131
152
|
end
|
132
153
|
end
|
133
154
|
|
155
|
+
def camelize_keys(options)
|
156
|
+
options.each.with_object({}) do |(key, value), acc|
|
157
|
+
value = camelize_keys(value) if value.is_a?(Hash)
|
158
|
+
acc[camelize(key)] = value
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
134
162
|
def camelize(str)
|
135
163
|
str.to_s.split('_').map.with_index { |word, i| i == 0 ? word : word.capitalize }.join
|
136
164
|
end
|
165
|
+
|
166
|
+
# Take a string, split it by newlines,
|
167
|
+
# and write each line as a separate data line
|
168
|
+
def multi_data_lines(data, buffer, key)
|
169
|
+
lines = data.to_s.split("\n")
|
170
|
+
lines.each do |line|
|
171
|
+
buffer << "data: #{key} #{line}\n"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def set_nested_value(hash, path, value)
|
176
|
+
# Navigate to the parent hash using all but the last segment
|
177
|
+
parent = path[0...-1].reduce(hash) do |current_hash, key|
|
178
|
+
current_hash[key] ||= {}
|
179
|
+
end
|
180
|
+
|
181
|
+
# Set the final key to the value
|
182
|
+
parent[path.last] = value
|
183
|
+
end
|
137
184
|
end
|
138
185
|
end
|
data/lib/datastar/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: datastar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ismael Celis
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rack
|
@@ -23,6 +23,34 @@ dependencies:
|
|
23
23
|
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: 3.1.14
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: json
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: logger
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
26
54
|
email:
|
27
55
|
- ismaelct@gmail.com
|
28
56
|
executables: []
|
@@ -64,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
92
|
- !ruby/object:Gem::Version
|
65
93
|
version: '0'
|
66
94
|
requirements: []
|
67
|
-
rubygems_version: 3.6.
|
95
|
+
rubygems_version: 3.6.9
|
68
96
|
specification_version: 4
|
69
97
|
summary: Ruby SDK for Datastar. Rack-compatible.
|
70
98
|
test_files: []
|