render_turbo_stream 1.2.2 → 1.3.0
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 +34 -8
- data/app/views/render_turbo_stream.html.erb +16 -0
- data/app/views/render_turbo_stream.turbo_stream.erb +41 -0
- data/lib/render_turbo_stream/test/request_helpers.rb +12 -3
- data/lib/render_turbo_stream/version.rb +1 -1
- data/lib/render_turbo_stream.rb +25 -35
- metadata +10 -10
- data/app/views/render_turbo_stream_partials.html.erb +0 -12
- data/app/views/render_turbo_stream_partials.turbo_stream.erb +0 -52
- data/app/views/render_turbo_stream_redirect.turbo_stream.erb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7737e1e5b1a6b397c22634b0a7efe6ee3f5d550a041fc667323c3b91dc5bf31b
|
4
|
+
data.tar.gz: a254e95ff504444aec88dfb25bfeb24c30f10447b2f47bfe7ff40ef57bb3ed0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d1c9a165d5471062752980dc3af0c9cd87908d16e37f000953aceb78c23f458ec5bd6585ec571068572479c7b088b9d6a442f5f993f05f03a8f46d6cc4cf518
|
7
|
+
data.tar.gz: 6db4979439d1399e40f1c786f8f4c78de5828aa4c310810bd4950928acb41cf8db6662ba238c7776337161d76d9139b180d8737bd042329854d8e4f3d366c690
|
data/README.md
CHANGED
@@ -4,6 +4,8 @@ Defining templates like `(create|update).turbo_stream.haml` annoyed me.
|
|
4
4
|
|
5
5
|
Working consistently with turbo_stream means shooting lots of partials from the backend to the frontend. This always requires the same attributes: the path to the partial, the html-id that turbo_stream points to, and maybe some locals. This gem serialises that: Partials can be controlled directly from the controller. It sets the status, generates a flash message, handles redirection, pushes it all to the front and comes with predefined helpers for enabling request-specs.
|
6
6
|
|
7
|
+
And build it dynamically: Most [turbo_power](https://github.com/marcoroth/turbo_power) commands, such as adding a css class to an html element or pushing a state to the browser history, work directly from the controller. Since turbo allows it, custom javascript functions are also possible.
|
8
|
+
|
7
9
|
An overview of how we design a rails-7 application with turbo
|
8
10
|
is [published on dev.to](https://dev.to/chmich/rails-7-vite-wrapping-up-1pia).
|
9
11
|
|
@@ -62,10 +64,10 @@ def update
|
|
62
64
|
end
|
63
65
|
```
|
64
66
|
|
65
|
-
This will set a status, generate a flash message and perform `
|
67
|
+
This will set a status, generate a flash message and perform `render_turbo_stream`, which could result in something like this:
|
66
68
|
|
67
69
|
```ruby
|
68
|
-
|
70
|
+
render_turbo_stream(
|
69
71
|
[
|
70
72
|
{
|
71
73
|
id: 'form',
|
@@ -80,7 +82,7 @@ stream_partials(
|
|
80
82
|
)
|
81
83
|
```
|
82
84
|
|
83
|
-
The `stream_partial` method is nothing else than a singular of `
|
85
|
+
The `stream_partial` method is nothing else than a singular of `render_turbo_stream`.
|
84
86
|
|
85
87
|
**locals**: The hash for locals goes through a `symbolize_keys`, so you need to use locals in used partials like this: `locals[:message]`.
|
86
88
|
|
@@ -93,13 +95,39 @@ stream_partial(
|
|
93
95
|
)
|
94
96
|
```
|
95
97
|
|
98
|
+
## More options
|
99
|
+
|
100
|
+
`render_turbo_stream` interprets a hash as a partial to be sent by `turbo_stream` and an array as a command to be sent. This should allow you to perform most actions from, for example, [turbo_power](https://github.com/marcoroth/turbo_power). An example of rendering a partial and adding a css class to an html element would look like this:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
render_turbo_stream(
|
104
|
+
[
|
105
|
+
{
|
106
|
+
id: 'form',
|
107
|
+
partial: 'form'
|
108
|
+
},
|
109
|
+
[
|
110
|
+
:add_css_class,
|
111
|
+
'#colored-element',
|
112
|
+
'red'
|
113
|
+
]
|
114
|
+
]
|
115
|
+
)
|
116
|
+
```
|
117
|
+
|
118
|
+
which is done under the hood, for the array: `= turbo_stream.send args.first, *(args[1..-1])`
|
119
|
+
|
120
|
+
|
96
121
|
## Testing Scenarios
|
97
122
|
|
98
123
|
For system testing there is Capybara. Its the only way to check if frontend and backend work together. But its a good practice to break tests into smaller pieces. The much larger number of tests we will write on the much faster request tests, examples here in rspec.
|
99
124
|
|
100
125
|
If the request format is not `turbo_stream`, which is the case on request specs, the method responds in a special html
|
101
|
-
that contains the medadata that is interesting for our tests and is parsed by included test helpers.
|
102
|
-
|
126
|
+
that contains the medadata that is interesting for our tests and is parsed by included test helpers.
|
127
|
+
|
128
|
+
There is a helper for writing the test: In the debugger, within the test, check the output of `partials_log`.
|
129
|
+
|
130
|
+
Test scenarios are:
|
103
131
|
|
104
132
|
**The fastest**
|
105
133
|
|
@@ -143,9 +171,7 @@ expect(partial_response('articles/form', id: 'form', css: '.field_with_errors',
|
|
143
171
|
|
144
172
|
**More detailed**
|
145
173
|
|
146
|
-
Consider a controller action that should respond in 2 flashes
|
147
|
-
|
148
|
-
Helper for writing the test: On Debugger, inside the test, check the output of `partials_log`.
|
174
|
+
Consider a controller action that should respond in 2 flashes:
|
149
175
|
|
150
176
|
```ruby
|
151
177
|
expect(partials_count).to eq(1)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<% rendered_partials = [] %>
|
2
|
+
|
3
|
+
<% streams.each do |s| %>
|
4
|
+
|
5
|
+
<% if s.is_a?(Array) %>
|
6
|
+
<% rendered_partials.push({ command: "turbo_stream.#{s.first}, #{s[1..-1].join(', ')}", method: s.first, attributes: s[1..-1] }) %>
|
7
|
+
<% else %>
|
8
|
+
<% html = (render s[:partial], locals: s[:locals]&.symbolize_keys, formats: [:html]) %>
|
9
|
+
<% rendered_partials.push({ html_response: html }.merge(s)) %>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<%= content_tag :div, rendered_partials.to_json, id: 'rendered-partials' %>
|
16
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<% error = false %>
|
2
|
+
<% control = [] %>
|
3
|
+
<% streams = local_assigns[:streams] %>
|
4
|
+
<% streams.each do |args| %>
|
5
|
+
|
6
|
+
|
7
|
+
<% if args.is_a?(Array) %>
|
8
|
+
|
9
|
+
<% ctl = "turbo_stream.#{args.first}, #{args[1..-1].join(', ')}" %>
|
10
|
+
<% Rails.logger.error(" RENDER TURBO STREAM => #{ctl}") %>
|
11
|
+
<%= turbo_stream.send args.first, *(args[1..-1]) %>
|
12
|
+
|
13
|
+
|
14
|
+
<% else %>
|
15
|
+
<% ctl = { partial: args[:partial], id: args[:id], action: args[:action] } %>
|
16
|
+
<% info = { id: args[:id], partial: args[:partial], locals: args[:locals] } %>
|
17
|
+
<% if !args[:action].present? %>
|
18
|
+
<% Rails.logger.error(" ERROR RENDER TURBO STREAM => MISSING ACTION => #{args}") %>
|
19
|
+
<% error = true %>
|
20
|
+
|
21
|
+
<% elsif args[:action].present? %>
|
22
|
+
<% Rails.logger.debug(" • render-turbo-stream #{args[:action].upcase} => #{info}") %>
|
23
|
+
<%= turbo_stream.send args[:action].to_sym, args[:id] do %>
|
24
|
+
<%= render args[:partial], locals: args[:locals]&.symbolize_keys %>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<% else %>
|
28
|
+
<% Rails.logger.error(" ERROR RENDER TURBO STREAM => NOTHING DONE! => #{args}") %>
|
29
|
+
<% end %>
|
30
|
+
<% end %>
|
31
|
+
|
32
|
+
<% control.push(ctl) %>
|
33
|
+
|
34
|
+
<% end %>
|
35
|
+
|
36
|
+
<% if error %>
|
37
|
+
<% Rails.logger.error(" RENDER TURBO STREAM HAD ERRORS, REPEATING WHOLE ARRAY: ") %>
|
38
|
+
<% control.each do |c| %>
|
39
|
+
<% Rails.logger.error(" #{c}") %>
|
40
|
+
<% end %>
|
41
|
+
<% end %>
|
@@ -34,9 +34,18 @@ module RenderTurboStream
|
|
34
34
|
|
35
35
|
# Returns the path to which turbo_stream.redirect_to would be applied.
|
36
36
|
def turbo_redirect_to
|
37
|
-
|
38
|
-
|
39
|
-
r
|
37
|
+
resps = RenderTurboStream::Libs.all_responses(response)
|
38
|
+
url = nil
|
39
|
+
resps.each do |r|
|
40
|
+
if r['method'] == 'redirect_to'
|
41
|
+
if url
|
42
|
+
url = 'ERROR: REDIRECT CALLED MORE THAN ONCE'
|
43
|
+
else
|
44
|
+
url = r['attributes'].first
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
url
|
40
49
|
end
|
41
50
|
|
42
51
|
def partial_response_count(partial, id: nil, total: 1, &block)
|
data/lib/render_turbo_stream.rb
CHANGED
@@ -126,52 +126,42 @@ module RenderTurboStream
|
|
126
126
|
response.status = 302
|
127
127
|
flash[:alert] = flash_alerts
|
128
128
|
flash[:notice] = flash_notices
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
129
|
+
render_turbo_stream([
|
130
|
+
[
|
131
|
+
:redirect_to,
|
132
|
+
redirect_on_success_to
|
133
|
+
]
|
134
|
+
])
|
134
135
|
else
|
135
136
|
flash.now[:alert] = flash_alerts
|
136
137
|
flash.now[:notice] = flash_notices
|
137
|
-
|
138
|
+
render_turbo_stream(streams)
|
138
139
|
end
|
139
140
|
end
|
140
141
|
|
141
|
-
def
|
142
|
+
def render_turbo_stream(array)
|
142
143
|
|
143
144
|
ary = []
|
144
|
-
array.
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
145
|
+
array.each do |pr|
|
146
|
+
if pr.is_a?(Hash)
|
147
|
+
props = pr.symbolize_keys
|
148
|
+
part = (props[:partial].present? ? props[:partial] : props[:id]).gsub('-', '_')
|
149
|
+
partial = (part.to_s.include?('/') ? part : [controller_path, part].join('/'))
|
150
|
+
r = props
|
151
|
+
r[:action] = (props[:action].present? ? props[:action] : :replace)
|
152
|
+
r[:partial] = partial
|
153
|
+
ary.push(r)
|
154
|
+
elsif pr.is_a?(Array)
|
155
|
+
ary.push(pr)
|
156
|
+
else
|
157
|
+
raise "ERROR render_turbo_stream invalid type: Only hash or array allowed"
|
158
|
+
end
|
152
159
|
end
|
153
160
|
|
154
161
|
if request.format.to_sym == :turbo_stream
|
155
|
-
render template: '
|
162
|
+
render template: 'render_turbo_stream', locals: { streams: ary }, layout: false, formats: :turbo_stream
|
156
163
|
else
|
157
|
-
|
158
|
-
check_ids = {}
|
159
|
-
ary.each do |a|
|
160
|
-
if a[:action] == 'replace'
|
161
|
-
check_ids[a[:id]] ||= 0
|
162
|
-
check_ids[a[:id]] += 1
|
163
|
-
throw "More than one partial rendered to ID «#{a[:id]}» by replace" if check_ids[a[:id]] >= 2
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
check = {}
|
168
|
-
ary.each do |a|
|
169
|
-
b = a.dup
|
170
|
-
b.delete(:id)
|
171
|
-
check[a[:id]] = b
|
172
|
-
end
|
173
|
-
# render json: check
|
174
|
-
render template: 'render_turbo_stream_partials', locals: { check: check, streams: ary }, layout: false, formats: :html
|
164
|
+
render template: 'render_turbo_stream', locals: { streams: ary }, layout: false, formats: :html
|
175
165
|
end
|
176
166
|
end
|
177
167
|
|
@@ -181,7 +171,7 @@ module RenderTurboStream
|
|
181
171
|
action: :replace,
|
182
172
|
locals: {}
|
183
173
|
)
|
184
|
-
|
174
|
+
render_turbo_stream(
|
185
175
|
[
|
186
176
|
{
|
187
177
|
id: id,
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: render_turbo_stream
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- christian
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -24,10 +24,11 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 7.0.4.3
|
27
|
-
description:
|
27
|
+
description: Writing views like (create|update).turbo_stream.haml annoyed me. This
|
28
28
|
gem handles translated flash messages, sets status, and renders partials through
|
29
|
-
turbo-stream. You can control
|
30
|
-
|
29
|
+
turbo-stream. You can control it directly from the controller. With the turbo_power
|
30
|
+
gem you can also handle predefined actions like redirects and much more. Includes
|
31
|
+
helpers for enabling request testing.
|
31
32
|
email:
|
32
33
|
- christian@sedlmair.ch
|
33
34
|
executables: []
|
@@ -37,9 +38,8 @@ files:
|
|
37
38
|
- README.md
|
38
39
|
- Rakefile
|
39
40
|
- app/controllers/render_turbo_stream/application_controller.rb
|
40
|
-
- app/views/
|
41
|
-
- app/views/
|
42
|
-
- app/views/render_turbo_stream_redirect.turbo_stream.erb
|
41
|
+
- app/views/render_turbo_stream.html.erb
|
42
|
+
- app/views/render_turbo_stream.turbo_stream.erb
|
43
43
|
- lib/render_turbo_stream.rb
|
44
44
|
- lib/render_turbo_stream/engine.rb
|
45
45
|
- lib/render_turbo_stream/railtie.rb
|
@@ -72,6 +72,6 @@ requirements: []
|
|
72
72
|
rubygems_version: 3.4.12
|
73
73
|
signing_key:
|
74
74
|
specification_version: 4
|
75
|
-
summary: Render partials by turbo-stream directly
|
76
|
-
included.
|
75
|
+
summary: Render partials or performing javascript actions by turbo-stream directly
|
76
|
+
from the controller. Test helpers included.
|
77
77
|
test_files: []
|
@@ -1,12 +0,0 @@
|
|
1
|
-
<% rendered_partials = [] %>
|
2
|
-
|
3
|
-
<% streams.each do |s| %>
|
4
|
-
|
5
|
-
<% html = (render s[:partial], locals: s[:locals]&.symbolize_keys, formats: [:html]) %>
|
6
|
-
|
7
|
-
<% rendered_partials.push({ html_response: html }.merge(s)) %>
|
8
|
-
|
9
|
-
<% end %>
|
10
|
-
|
11
|
-
<%= content_tag :div, rendered_partials.to_json, id: 'rendered-partials' %>
|
12
|
-
|
@@ -1,52 +0,0 @@
|
|
1
|
-
<% error = false %>
|
2
|
-
<% control = [] %>
|
3
|
-
<% local_assigns[:streams].each do |s| %>
|
4
|
-
<% ctl = { partial: s[:partial], id: s[:id] } %>
|
5
|
-
<% info = { id: s[:id], partial: s[:partial], locals: s[:locals] } %>
|
6
|
-
|
7
|
-
<% if !s[:id] %>
|
8
|
-
<% Rails.logger.error(" ERROR RENDER TURBO STREAM => MISSING ID => #{s}") %>
|
9
|
-
<% error = true %>
|
10
|
-
|
11
|
-
<% elsif !s[:partial].present? %>
|
12
|
-
<% Rails.logger.error(" ERROR RENDER TURBO STREAM => MISSING PARTIAL => #{s}") %>
|
13
|
-
<% error = true %>
|
14
|
-
<% ctl[:partial] = nil %>
|
15
|
-
|
16
|
-
<% elsif s[:action].to_sym == :replace %>
|
17
|
-
<% ctl[:action] = :replace %>
|
18
|
-
<% Rails.logger.debug(" • render-turbo-stream REPLACE => #{info}") %>
|
19
|
-
<%= turbo_stream.replace s[:id] do %>
|
20
|
-
<%= render s[:partial], locals: s[:locals]&.symbolize_keys %>
|
21
|
-
<% end %>
|
22
|
-
|
23
|
-
<% elsif s[:action].to_sym == :prepend %>
|
24
|
-
<% ctl[:action] = :prepend %>
|
25
|
-
<% Rails.logger.debug(" • render-turbo-stream PREPEND => #{info}") %>
|
26
|
-
<%= turbo_stream.prepend s[:id] do %>
|
27
|
-
<%= render s[:partial], locals: s[:locals]&.symbolize_keys %>
|
28
|
-
<% end %>
|
29
|
-
|
30
|
-
<% elsif s[:action].to_sym == :append %>
|
31
|
-
<% ctl[:action] = :append %>
|
32
|
-
<% Rails.logger.debug(" • render-turbo-stream APPEND => #{info}") %>
|
33
|
-
<%= turbo_stream.prepend s[:id] do %>
|
34
|
-
<%= render s[:partial], locals: s[:locals]&.symbolize_keys %>
|
35
|
-
<% end %>
|
36
|
-
|
37
|
-
<% else %>
|
38
|
-
<% ctl[:action] = :UNKNOWN %>
|
39
|
-
<% Rails.logger.fatal(" ERROR render-turbo-stream => UNKNOWN ACTION => #{s}") %>
|
40
|
-
<% error = true %>
|
41
|
-
<% end %>
|
42
|
-
|
43
|
-
<% control.push(ctl) %>
|
44
|
-
|
45
|
-
<% end %>
|
46
|
-
|
47
|
-
<% if error %>
|
48
|
-
<% Rails.logger.error(" RENDER TURBO STREAM HAD ERRORS, REPEATING WHOLE ARRAY: ") %>
|
49
|
-
<% control.each do |c| %>
|
50
|
-
<% Rails.logger.error(" #{c}") %>
|
51
|
-
<% end %>
|
52
|
-
<% end %>
|
@@ -1 +0,0 @@
|
|
1
|
-
<%= turbo_stream.redirect_to local_assigns[:url] %>
|