render_turbo_stream 4.1.6 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +28 -0
- data/app/controllers/render_turbo_stream/render_controller.rb +4 -0
- data/app/views/render_turbo_stream.turbo_stream.erb +14 -8
- data/app/views/render_turbo_stream_request_test.html.erb +11 -6
- data/lib/render_turbo_stream/channel_libs.rb +29 -23
- data/lib/render_turbo_stream/check_template.rb +2 -13
- data/lib/render_turbo_stream/controller_channel_helpers.rb +4 -4
- data/lib/render_turbo_stream/controller_helpers.rb +3 -3
- data/lib/render_turbo_stream/controller_libs.rb +25 -18
- data/lib/render_turbo_stream/libs.rb +13 -0
- data/lib/render_turbo_stream/version.rb +1 -1
- metadata +8 -8
- data/app/controllers/render_turbo_stream/render_turbo_stream_render_controller.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0baf30f6152a1308687add8e14fda8ae815bfa43e54f9b4ffc6d859accc5e5b
|
4
|
+
data.tar.gz: a61f84ab272df1447b3a4cd38ef5dcbf3702390e360cbb03d5f172f0056aa760
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f6b26a244628091a4385883b04d588d682f1da0144dd56a827e74cceb5d61df5bdeeb8eb72e041c9203995b25f1b62605fde0d5d4eadc611b3ef7a02dd8df54
|
7
|
+
data.tar.gz: bf8a7eaae4377b4a52f4200057f9dd4e8e8273fdee243f258cf1e65a63f935873d11a9686e73e1e353b54c5a05458781a6811c991f76650d1810da6dfa2b5e0a
|
data/README.md
CHANGED
@@ -170,6 +170,34 @@ If this config is set to true, Turbo::StreamsChannel is installed and a current
|
|
170
170
|
|
171
171
|
If an `if_success_redirect_to` argument is provided and the save action was successful, `turbo_stream_save` would send the partials by channel.
|
172
172
|
|
173
|
+
**target-ID: Avoid the same definition in multiple places**
|
174
|
+
|
175
|
+
Without this gem a turbo action would be wrapped within two frames, for example:
|
176
|
+
|
177
|
+
```haml
|
178
|
+
= turbo_stream.replace 'target-id' do
|
179
|
+
= render 'a partial'
|
180
|
+
|
181
|
+
```
|
182
|
+
|
183
|
+
and within a partial:
|
184
|
+
|
185
|
+
```haml
|
186
|
+
= turbo_frame_tag 'target-id' do
|
187
|
+
... content
|
188
|
+
```
|
189
|
+
|
190
|
+
The `turbo_frame_tag` would stay inside a partial in most cases. The reason is: On the first load, the target-id must be delivered up front, so that if the tag needs to be replaced later, turbo knows which part to replace.
|
191
|
+
|
192
|
+
This means that the target id must be defined in several places: inside a partial and at the place where the turbo action is defined.
|
193
|
+
|
194
|
+
In order to avoid this kind of tedious coding, the gem has a kind of fallback built in: If the argument `partial` is given, but the attribute `target_id` is not, the gem will get the target_id from the partial. The process is:
|
195
|
+
|
196
|
+
1. Render the partial with the provided locals
|
197
|
+
2. Grabs into the partial by Nokogiri and looks for a turbo_frame_tag and fetches the target_id.
|
198
|
+
3. If all that not is found it raises a exception
|
199
|
+
4. wraps the partial within the `turbo_stream.*` and sends this to the front.
|
200
|
+
|
173
201
|
# Request Testing
|
174
202
|
|
175
203
|
To test if the whole system works together, including javascript actions, so that finally a part reaches the surface, there is **Capybara** system testing. But it is a good practice to **break tests into smaller pieces**. So, there are helpers for **enabling the much faster request tests that are much easier to maintain**.
|
@@ -14,22 +14,28 @@
|
|
14
14
|
<% else %>
|
15
15
|
<% ctl = { partial: args[:partial], target_id: args[:target_id], action: args[:action] } %>
|
16
16
|
|
17
|
+
|
18
|
+
|
19
|
+
<% rendered_html = render(partial: args[:partial], locals: args[:locals]&.symbolize_keys, formats: [:html]) %>
|
20
|
+
<% unless args[:target].present? %>
|
21
|
+
<% args[:target] = RenderTurboStream::Libs.fetch_arguments_from_rendered_string(rendered_html)[:target] %>
|
22
|
+
<% unless args[:target].present? %>
|
23
|
+
<% raise 'No target specified by arguments and no target found inside the rendered partial' %>
|
24
|
+
<% end %>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
|
17
28
|
<% target_id = RenderTurboStream::Libs.target_to_target_id(args[:target]) %>
|
18
29
|
|
19
30
|
<% info = { target_id: args[:target_id], partial: args[:partial], locals: args[:locals] } %>
|
20
31
|
|
21
32
|
|
22
|
-
|
23
33
|
<% if args[:action].present? %>
|
24
34
|
<% Rails.logger.debug(" • render-turbo-stream #{args[:action].upcase} => #{info}") %>
|
25
35
|
<%= turbo_stream.send args[:action].to_sym, target_id do %>
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
<% end %>
|
30
|
-
<% else %>
|
31
|
-
<%= render args[:partial], locals: args[:locals]&.symbolize_keys %>
|
32
|
-
<% end %>
|
36
|
+
|
37
|
+
<%= rendered_html %>
|
38
|
+
|
33
39
|
<% end %>
|
34
40
|
|
35
41
|
<% else %>
|
@@ -10,15 +10,20 @@
|
|
10
10
|
<% rendered_partials.push(h) %>
|
11
11
|
<% else %>
|
12
12
|
|
13
|
-
<% wrap = RenderTurboStream::CheckTemplate.new(partial: s[:partial], action: s[:action]).add_turbo_frame_tag? %>
|
14
13
|
|
15
|
-
<% if wrap %>
|
16
|
-
<% html = turbo_frame_tag RenderTurboStream::Libs.target_to_target_id(s[:target]) do %>
|
17
|
-
<%= (render s[:partial], locals: s[:locals]&.symbolize_keys, formats: [:html]) %>
|
18
|
-
<% end %>
|
19
|
-
<% else %>
|
20
14
|
<% html = (render s[:partial], locals: s[:locals]&.symbolize_keys, formats: [:html]) %>
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
<% unless s[:target].present? %>
|
19
|
+
<% s[:target] = RenderTurboStream::Libs.fetch_arguments_from_rendered_string(html)[:target] %>
|
20
|
+
<% unless s[:target].present? %>
|
21
|
+
<% raise 'No target specified by arguments and no target found inside the rendered partial' %>
|
22
|
+
<% end %>
|
21
23
|
<% end %>
|
24
|
+
|
25
|
+
|
26
|
+
|
22
27
|
<% s[:target] = s[:target] %>
|
23
28
|
<% s.delete(:id) %>
|
24
29
|
<% rendered_partials.push({ html_response: html, type: 'stream-partial' }.merge(s)) %>
|
@@ -7,9 +7,10 @@ module RenderTurboStream
|
|
7
7
|
|
8
8
|
def render_to_channel(channel, target, action, controller_self, partial: nil, template: nil, locals: nil)
|
9
9
|
|
10
|
+
_locals = (locals ? locals : {})
|
11
|
+
|
10
12
|
# instance variables
|
11
13
|
|
12
|
-
target_id = RenderTurboStream::Libs.target_to_target_id(target)
|
13
14
|
check_template = RenderTurboStream::CheckTemplate.new(
|
14
15
|
partial: partial,
|
15
16
|
template: template,
|
@@ -19,35 +20,40 @@ module RenderTurboStream
|
|
19
20
|
|
20
21
|
# add instance_variables to locals
|
21
22
|
|
22
|
-
locals = {} unless locals # prevent error missing keys for nil
|
23
|
-
if check_template.add_turbo_frame_tag?
|
24
|
-
_layout = 'layouts/add_turbo_frame_tag'
|
25
|
-
_locals = locals.merge(
|
26
|
-
{
|
27
|
-
render_turbo_stream_partial: partial,
|
28
|
-
render_turbo_stream_target_id: target_id
|
29
|
-
}
|
30
|
-
)
|
31
|
-
else
|
32
|
-
_locals = locals
|
33
|
-
_layout = false
|
34
|
-
end
|
35
23
|
check_template.templates_instance_variables.each do |v|
|
36
24
|
_locals[:"render_turbo_stream_instance_variable_#{v}"] = controller_self.instance_variable_get(v.to_s)
|
37
25
|
end
|
38
26
|
|
27
|
+
# fetch target-id
|
28
|
+
|
29
|
+
target_id = RenderTurboStream::Libs.target_to_target_id(target)
|
30
|
+
if !target.present? || Rails.env.test?
|
31
|
+
if partial.present?
|
32
|
+
rendered_html = RenderController.render(partial: partial, locals: _locals, layout: false)
|
33
|
+
elsif template.present?
|
34
|
+
rendered_html = RenderController.render(template: template, locals: _locals, layout: false)
|
35
|
+
end
|
36
|
+
if !target.present?
|
37
|
+
r = RenderTurboStream::Libs.fetch_arguments_from_rendered_string(rendered_html)
|
38
|
+
target_id = r[:target_id]
|
39
|
+
target = r[:target]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
unless target.present? && target_id.present?
|
44
|
+
raise 'No target specified by arguments and no target found inside the rendered partial'
|
45
|
+
end
|
46
|
+
|
39
47
|
# add headers for test
|
40
48
|
|
41
49
|
if Rails.env.test?
|
42
|
-
html = RenderTurboStreamRenderController.render(partial: partial, locals: _locals, layout: _layout) if partial
|
43
|
-
html = RenderTurboStreamRenderController.render(template: template, locals: _locals, layout: _layout) if template
|
44
50
|
props = {
|
45
51
|
target: target,
|
46
52
|
action: action,
|
47
53
|
type: 'channel-partial',
|
48
54
|
locals: locals,
|
49
55
|
channel: channel,
|
50
|
-
html_response:
|
56
|
+
html_response: rendered_html.to_s
|
51
57
|
}
|
52
58
|
|
53
59
|
props[:partial] = partial if partial
|
@@ -66,23 +72,23 @@ module RenderTurboStream
|
|
66
72
|
|
67
73
|
# send
|
68
74
|
|
69
|
-
if partial
|
75
|
+
if partial.present?
|
70
76
|
Turbo::StreamsChannel.send(
|
71
77
|
"broadcast_#{action}_to",
|
72
78
|
channel.to_s,
|
73
79
|
target: target_id,
|
74
80
|
partial: partial,
|
75
81
|
locals: _locals&.symbolize_keys,
|
76
|
-
layout:
|
82
|
+
layout: false
|
77
83
|
)
|
78
|
-
elsif template
|
84
|
+
elsif template.present?
|
79
85
|
Turbo::StreamsChannel.send(
|
80
86
|
"broadcast_#{action}_to",
|
81
87
|
channel.to_s,
|
82
88
|
target: target_id,
|
83
89
|
template: template,
|
84
|
-
locals:
|
85
|
-
layout:
|
90
|
+
locals: _locals&.symbolize_keys,
|
91
|
+
layout: false
|
86
92
|
)
|
87
93
|
end
|
88
94
|
end
|
@@ -114,7 +120,7 @@ module RenderTurboStream
|
|
114
120
|
end
|
115
121
|
end
|
116
122
|
|
117
|
-
content =
|
123
|
+
content = RenderController.render(template: 'render_turbo_stream_command', layout: false, locals: { command: command, arguments: arguments })
|
118
124
|
|
119
125
|
Turbo::StreamsChannel.broadcast_stream_to(
|
120
126
|
channel,
|
@@ -23,12 +23,12 @@ module RenderTurboStream
|
|
23
23
|
if $render_turbo_stream_check_templates[@key]
|
24
24
|
@result = $render_turbo_stream_check_templates[@key]
|
25
25
|
else
|
26
|
-
@result = { instance_variables: []
|
26
|
+
@result = { instance_variables: [] }
|
27
27
|
fetch_nested_partials(1, partial: @partial_path, template: @template_path)
|
28
28
|
$render_turbo_stream_check_templates[@key] ||= @result
|
29
29
|
end
|
30
30
|
else
|
31
|
-
@result = { instance_variables: []
|
31
|
+
@result = { instance_variables: [] }
|
32
32
|
fetch_nested_partials(1, partial: @partial_path, template: @template_path)
|
33
33
|
end
|
34
34
|
end
|
@@ -37,10 +37,6 @@ module RenderTurboStream
|
|
37
37
|
@result[:instance_variables]
|
38
38
|
end
|
39
39
|
|
40
|
-
def add_turbo_frame_tag?
|
41
|
-
@result[:add_turbo_frame_tag]
|
42
|
-
end
|
43
|
-
|
44
40
|
private
|
45
41
|
|
46
42
|
def production?
|
@@ -67,13 +63,6 @@ module RenderTurboStream
|
|
67
63
|
end
|
68
64
|
end
|
69
65
|
|
70
|
-
conf = Rails.configuration.x.render_turbo_stream.auto_wrap_for_actions
|
71
|
-
if stack_level == 1 && conf.present? && conf.include?(@action.to_sym)
|
72
|
-
unless _code.match(/(turbo_frame_tag|turbo-frame)/)
|
73
|
-
@result[:add_turbo_frame_tag] = true
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
66
|
loop do
|
78
67
|
_matched_partial = _code.match((/\brender[ ](partial:|())(|[ ])("|')[a-z_\/]+("|')/))
|
79
68
|
_code = _code[(_code.index(_matched_partial.to_s) + 5)..-1]
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module RenderTurboStream
|
2
2
|
module ControllerChannelHelpers
|
3
3
|
|
4
|
-
def render_to_all(target_id, action = :replace, partial: nil, template: nil, locals: nil)
|
4
|
+
def render_to_all(target_id = nil, action = :replace, partial: nil, template: nil, locals: nil)
|
5
5
|
evaluate_instance_variables
|
6
6
|
render_to_channel(
|
7
7
|
'all',
|
@@ -13,7 +13,7 @@ module RenderTurboStream
|
|
13
13
|
)
|
14
14
|
end
|
15
15
|
|
16
|
-
def render_to_me(target_id, action = :replace, partial: nil, locals: nil)
|
16
|
+
def render_to_me(target_id = nil, action = :replace, partial: nil, locals: nil)
|
17
17
|
begin
|
18
18
|
u_id = helpers.current_user&.id
|
19
19
|
unless u_id.present?
|
@@ -34,7 +34,7 @@ module RenderTurboStream
|
|
34
34
|
)
|
35
35
|
end
|
36
36
|
|
37
|
-
def render_to_authenticated_group(group, target_id, action = :replace, partial: nil, locals: nil)
|
37
|
+
def render_to_authenticated_group(group, target_id = nil, action = :replace, partial: nil, locals: nil)
|
38
38
|
begin
|
39
39
|
u_id = helpers.current_user&.id
|
40
40
|
unless u_id.present?
|
@@ -55,7 +55,7 @@ module RenderTurboStream
|
|
55
55
|
)
|
56
56
|
end
|
57
57
|
|
58
|
-
def render_to_channel(channel, target_id, action, partial: nil, template: nil, locals: nil, instance_variables: true)
|
58
|
+
def render_to_channel(channel, target_id = nil, action = :replace, partial: nil, template: nil, locals: nil, instance_variables: true)
|
59
59
|
|
60
60
|
raise 'Arguments partial and template cannot both be specified' if partial && template
|
61
61
|
|
@@ -21,7 +21,7 @@ module RenderTurboStream
|
|
21
21
|
if_error_add: nil, # additional partials that should be rendered if save_action failed
|
22
22
|
add: [], # additional streams
|
23
23
|
|
24
|
-
if_success_notices: nil, # array of strings, override default generated flash generation in the case of success
|
24
|
+
if_success_notices: nil, # array of strings, or string, override default generated flash generation in the case of success
|
25
25
|
if_error_alerts: nil,
|
26
26
|
add_notices: nil, # array of strings
|
27
27
|
add_alerts: nil,
|
@@ -132,11 +132,11 @@ module RenderTurboStream
|
|
132
132
|
else
|
133
133
|
r[:target] = props[:target]
|
134
134
|
end
|
135
|
-
raise "Missing attribute :target in #{props}" if !props[:target].present?
|
136
135
|
r.delete(:target_id)
|
137
136
|
r[:action] = (props[:action].present? ? props[:action] : :replace)
|
138
137
|
r[:partial] = RenderTurboStream::Libs.partial_path(props[:target], props[:target_id], controller_path, props[:partial])
|
139
138
|
r[:type] = 'stream-partial'
|
139
|
+
|
140
140
|
ary.push(r)
|
141
141
|
elsif pr.is_a?(Array)
|
142
142
|
raise "array has to contain at least one element: #{pr}" unless pr.first.present?
|
@@ -157,7 +157,7 @@ module RenderTurboStream
|
|
157
157
|
|
158
158
|
# renders a partial to turbo_stream
|
159
159
|
|
160
|
-
def stream_partial(target_id, partial: nil, action: :replace, locals: {})
|
160
|
+
def stream_partial(target_id = nil, partial: nil, action: :replace, locals: {})
|
161
161
|
render_turbo_stream(
|
162
162
|
[
|
163
163
|
{
|
@@ -5,7 +5,7 @@ module RenderTurboStream
|
|
5
5
|
@save_action = save_action
|
6
6
|
end
|
7
7
|
|
8
|
-
def generate_flash(
|
8
|
+
def generate_flash(model_name, controller_action, if_success_notices, if_error_alerts, add_notices, add_alerts)
|
9
9
|
|
10
10
|
target_id = (Rails.configuration.x.render_turbo_stream.flash_target_id rescue nil)
|
11
11
|
raise "Missing configuration: config.x.render_turbo_stream.flash_target_id" unless target_id
|
@@ -14,26 +14,33 @@ module RenderTurboStream
|
|
14
14
|
turbo_action = Rails.configuration.x.render_turbo_stream.flash_turbo_action
|
15
15
|
raise "Missing configuration: configuration.x.render_turbo_stream.flash_turbo_action" unless turbo_action
|
16
16
|
|
17
|
-
|
18
17
|
if @save_action
|
19
18
|
notices = if if_success_notices
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
if if_success_notices.is_a?(String)
|
20
|
+
[if_success_notices]
|
21
|
+
else
|
22
|
+
if_success_notices
|
23
|
+
end
|
24
|
+
else
|
25
|
+
str = I18n.t(
|
26
|
+
"activerecord.render_turbo_stream_success.#{controller_action}"
|
27
|
+
)
|
28
|
+
[format(str, model_name: model_name)]
|
29
|
+
end
|
27
30
|
alerts = []
|
28
31
|
else
|
29
32
|
alerts = if if_error_alerts
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
if if_error_alerts.is_a?(String)
|
34
|
+
[if_error_alerts]
|
35
|
+
else
|
36
|
+
if_error_alerts
|
37
|
+
end
|
38
|
+
else
|
39
|
+
str = I18n.t(
|
40
|
+
"activerecord.render_turbo_stream_errors.#{controller_action}"
|
41
|
+
)
|
42
|
+
[format(str, model_name: model_name)]
|
43
|
+
end
|
37
44
|
notices = []
|
38
45
|
end
|
39
46
|
|
@@ -63,13 +70,13 @@ module RenderTurboStream
|
|
63
70
|
{ turbo_actions: turbo_actions, alerts: alerts, notices: notices }
|
64
71
|
end
|
65
72
|
|
66
|
-
def additional_actions(
|
73
|
+
def additional_actions(if_success_add, if_error_add, add)
|
67
74
|
|
68
75
|
(@save_action ? make_actions(if_success_add) : make_actions(if_error_add)) + make_actions(add)
|
69
76
|
|
70
77
|
end
|
71
78
|
|
72
|
-
def generate_action(
|
79
|
+
def generate_action(controller_path, target_id, action, partial, locals)
|
73
80
|
libs = RenderTurboStream::Libs
|
74
81
|
target = libs.target_id_to_target(target_id)
|
75
82
|
_partial = libs.partial_path(nil, target_id, controller_path, partial)
|
@@ -29,5 +29,18 @@ module RenderTurboStream
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
def self.fetch_arguments_from_rendered_string(rendered_string)
|
33
|
+
noko = Nokogiri::HTML(rendered_string)
|
34
|
+
frame = noko.at_css('turbo-frame')
|
35
|
+
if frame.present?
|
36
|
+
{
|
37
|
+
target_id: frame[:id],
|
38
|
+
target: target_id_to_target(frame[:id])
|
39
|
+
}
|
40
|
+
else
|
41
|
+
{}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
32
45
|
end
|
33
46
|
end
|
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: 4.
|
4
|
+
version: 4.2.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-05-
|
11
|
+
date: 2023-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -27,10 +27,10 @@ dependencies:
|
|
27
27
|
description: 'A set of helpers that allow a UNIFIED WORKFLOW for TurboStream and TurboStreams::Channel.
|
28
28
|
To avoid a heavy mix of view and logic, partials and templates can be rendered directly
|
29
29
|
from the controller. There is no need to write *.turbo_stream.* templates anymore.
|
30
|
-
Logic can stay on
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
Logic can stay on in the controller. Javascript actions can also be executed directly
|
31
|
+
from the controller. TESTING: To reduce the amount of hard-to-maintain system tests,
|
32
|
+
this gem allows detailed request tests. A DEMO PROJECT with all this built in, along
|
33
|
+
with system and request tests, is linked in the README.'
|
34
34
|
email:
|
35
35
|
- christian@sedlmair.ch
|
36
36
|
executables: []
|
@@ -40,7 +40,7 @@ files:
|
|
40
40
|
- README.md
|
41
41
|
- Rakefile
|
42
42
|
- app/controllers/render_turbo_stream/application_controller.rb
|
43
|
-
- app/controllers/render_turbo_stream/
|
43
|
+
- app/controllers/render_turbo_stream/render_controller.rb
|
44
44
|
- app/views/layouts/_add_turbo_frame_tag.html.erb
|
45
45
|
- app/views/render_turbo_stream.turbo_stream.erb
|
46
46
|
- app/views/render_turbo_stream_command.html.erb
|
@@ -86,5 +86,5 @@ requirements: []
|
|
86
86
|
rubygems_version: 3.4.12
|
87
87
|
signing_key:
|
88
88
|
specification_version: 4
|
89
|
-
summary:
|
89
|
+
summary: Makes working with Turbo fun and ensures quality.
|
90
90
|
test_files: []
|