turbo-rails 0.5.9 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,6 +13,18 @@ module Turbo::Streams::Broadcasts
13
13
  broadcast_action_to *streamables, action: :replace, target: target, **rendering
14
14
  end
15
15
 
16
+ def broadcast_update_to(*streamables, target:, **rendering)
17
+ broadcast_action_to *streamables, action: :update, target: target, **rendering
18
+ end
19
+
20
+ def broadcast_before_to(*streamables, target:, **rendering)
21
+ broadcast_action_to *streamables, action: :before, target: target, **rendering
22
+ end
23
+
24
+ def broadcast_after_to(*streamables, target:, **rendering)
25
+ broadcast_action_to *streamables, action: :after, target: target, **rendering
26
+ end
27
+
16
28
  def broadcast_append_to(*streamables, target:, **rendering)
17
29
  broadcast_action_to *streamables, action: :append, target: target, **rendering
18
30
  end
@@ -32,6 +44,18 @@ module Turbo::Streams::Broadcasts
32
44
  broadcast_action_later_to *streamables, action: :replace, target: target, **rendering
33
45
  end
34
46
 
47
+ def broadcast_update_later_to(*streamables, target:, **rendering)
48
+ broadcast_action_later_to *streamables, action: :update, target: target, **rendering
49
+ end
50
+
51
+ def broadcast_before_later_to(*streamables, target:, **rendering)
52
+ broadcast_action_later_to *streamables, action: :before, target: target, **rendering
53
+ end
54
+
55
+ def broadcast_after_later_to(*streamables, target:, **rendering)
56
+ broadcast_action_later_to *streamables, action: :after, target: target, **rendering
57
+ end
58
+
35
59
  def broadcast_append_later_to(*streamables, target:, **rendering)
36
60
  broadcast_action_later_to *streamables, action: :append, target: target, **rendering
37
61
  end
@@ -16,12 +16,16 @@ module Turbo::FramesHelper
16
16
  # <%= turbo_frame_tag "tray", target: "other_tray" %>
17
17
  # # => <turbo-frame id="tray" target="other_tray"></turbo-frame>
18
18
  #
19
+ # <%= turbo_frame_tag "tray", src: tray_path(tray), loading: "lazy" %>
20
+ # # => <turbo-frame id="tray" src="http://example.com/trays/1" loading="lazy"></turbo-frame>
21
+ #
19
22
  # <%= turbo_frame_tag "tray" do %>
20
23
  # <div>My tray frame!</div>
21
24
  # <% end %>
22
25
  # # => <turbo-frame id="tray"><div>My tray frame!</div></turbo-frame>
23
26
  def turbo_frame_tag(id, src: nil, target: nil, **attributes, &block)
24
27
  id = id.respond_to?(:to_key) ? dom_id(id) : id
28
+ src = url_for(src) if src.present?
25
29
 
26
30
  tag.turbo_frame(**attributes.merge(id: id, src: src, target: target).compact, &block)
27
31
  end
@@ -6,11 +6,20 @@ module Turbo::Streams::ActionHelper
6
6
  #
7
7
  # turbo_stream_action_tag "replace", target: "message_1", template: %(<div id="message_1">Hello!</div>)
8
8
  # # => <turbo-stream action="replace" target="message_1"><template><div id="message_1">Hello!</div></template></turbo-stream>
9
- def turbo_stream_action_tag(action, target:, template: nil)
10
- target = convert_to_turbo_stream_dom_id(target)
9
+ #
10
+ # turbo_stream_action_tag "replace", targets: "message_1", template: %(<div id="message_1">Hello!</div>)
11
+ # # => <turbo-stream action="replace" targets="message_1"><template><div id="message_1">Hello!</div></template></turbo-stream>
12
+ def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil)
11
13
  template = action.to_sym == :remove ? "" : "<template>#{template}</template>"
12
14
 
13
- %(<turbo-stream action="#{action}" target="#{target}">#{template}</turbo-stream>).html_safe
15
+ if target
16
+ target = convert_to_turbo_stream_dom_id(target)
17
+ %(<turbo-stream action="#{action}" target="#{target}">#{template}</turbo-stream>).html_safe
18
+ elsif targets
19
+ %(<turbo-stream action="#{action}" targets="#{targets}">#{template}</turbo-stream>).html_safe
20
+ else
21
+ raise ArgumentError, "target or targets must be supplied"
22
+ end
14
23
  end
15
24
 
16
25
  private
@@ -1,5 +1,5 @@
1
1
  module Turbo::StreamsHelper
2
- # Returns a new <tt>Turbo::Streams::TagBuilder</tt> object that accepts stream actions and renders them them as
2
+ # Returns a new <tt>Turbo::Streams::TagBuilder</tt> object that accepts stream actions and renders them as
3
3
  # the template tags needed to send across the wire. This object is automatically yielded to turbo_stream.erb templates.
4
4
  #
5
5
  # When responding to HTTP requests, controllers can declare `turbo_stream` format response templates in that same
@@ -39,7 +39,9 @@ module Turbo::StreamsHelper
39
39
  # The example above will process all turbo streams sent to a stream name like <tt>account:5:entries</tt>
40
40
  # (when Current.account.id = 5). Updates to this stream can be sent like
41
41
  # <tt>entry.broadcast_append_to entry.account, :entries, target: "entries"</tt>.
42
- def turbo_stream_from(*streamables)
43
- tag.turbo_cable_stream_source channel: "Turbo::StreamsChannel", "signed-stream-name": Turbo::StreamsChannel.signed_stream_name(streamables)
42
+ def turbo_stream_from(*streamables, **attributes)
43
+ attributes[:channel] = "Turbo::StreamsChannel"
44
+ attributes[:"signed-stream-name"] = Turbo::StreamsChannel.signed_stream_name(streamables)
45
+ tag.turbo_cable_stream_source(**attributes)
44
46
  end
45
47
  end
@@ -1,15 +1,18 @@
1
1
  let consumer
2
2
 
3
3
  export async function getConsumer() {
4
- if (consumer) return consumer
5
- const { createConsumer } = await import("@rails/actioncable/src")
6
- return setConsumer(createConsumer())
4
+ return consumer || setConsumer(createConsumer().then(setConsumer))
7
5
  }
8
6
 
9
7
  export function setConsumer(newConsumer) {
10
8
  return consumer = newConsumer
11
9
  }
12
10
 
11
+ export async function createConsumer() {
12
+ const { createConsumer } = await import(/* webpackChunkName: "actioncable" */ "@rails/actioncable/src")
13
+ return createConsumer()
14
+ }
15
+
13
16
  export async function subscribeTo(channel, mixin) {
14
17
  const { subscriptions } = await getConsumer()
15
18
  return subscriptions.create(channel, mixin)
@@ -1,5 +1,7 @@
1
1
  # The job that powers all the <tt>broadcast_$action_later</tt> broadcasts available in <tt>Turbo::Streams::Broadcasts</tt>.
2
2
  class Turbo::Streams::ActionBroadcastJob < ActiveJob::Base
3
+ discard_on ActiveJob::DeserializationError
4
+
3
5
  def perform(stream, action:, target:, **rendering)
4
6
  Turbo::StreamsChannel.broadcast_action_to stream, action: action, target: target, **rendering
5
7
  end
@@ -1,6 +1,8 @@
1
1
  # The job that powers the <tt>broadcast_render_later_to</tt> available in <tt>Turbo::Streams::Broadcasts</tt> for rendering
2
2
  # turbo stream templates.
3
3
  class Turbo::Streams::BroadcastJob < ActiveJob::Base
4
+ discard_on ActiveJob::DeserializationError
5
+
4
6
  def perform(stream, **rendering)
5
7
  Turbo::StreamsChannel.broadcast_render_to stream, **rendering
6
8
  end
@@ -39,7 +39,7 @@ module Turbo::Broadcastable
39
39
  module ClassMethods
40
40
  # Configures the model to broadcast creates, updates, and destroys to a stream name derived at runtime by the
41
41
  # <tt>stream</tt> symbol invocation. By default, the creates are appended to a dom id target name derived from
42
- # the model's plural name. The insertion can also be made to be a prepend by overwriting <tt>insertion</tt> and
42
+ # the model's plural name. The insertion can also be made to be a prepend by overwriting <tt>inserts_by</tt> and
43
43
  # the target dom id overwritten by passing <tt>target</tt>. Examples:
44
44
  #
45
45
  # class Message < ApplicationRecord
@@ -52,14 +52,14 @@ module Turbo::Broadcastable
52
52
  # broadcasts_to ->(message) { [ message.board, :messages ] }, inserts_by: :prepend, target: "board_messages"
53
53
  # end
54
54
  def broadcasts_to(stream, inserts_by: :append, target: broadcast_target_default)
55
- after_create_commit -> { broadcast_action_later_to stream.try(:call, self) || send(stream), action: inserts_by, target: target }
55
+ after_create_commit -> { broadcast_action_later_to stream.try(:call, self) || send(stream), action: inserts_by, target: target.try(:call, self) || target }
56
56
  after_update_commit -> { broadcast_replace_later_to stream.try(:call, self) || send(stream) }
57
57
  after_destroy_commit -> { broadcast_remove_to stream.try(:call, self) || send(stream) }
58
58
  end
59
59
 
60
60
  # Same as <tt>#broadcasts_to</tt>, but the designated stream is automatically set to the current model.
61
61
  def broadcasts(inserts_by: :append, target: broadcast_target_default)
62
- after_create_commit -> { broadcast_action_later action: inserts_by, target: target }
62
+ after_create_commit -> { broadcast_action_later action: inserts_by, target: target.try(:call, self) || target }
63
63
  after_update_commit -> { broadcast_replace_later }
64
64
  after_destroy_commit -> { broadcast_remove }
65
65
  end
@@ -75,8 +75,8 @@ module Turbo::Broadcastable
75
75
  #
76
76
  # # Sends <turbo-stream action="remove" target="clearance_5"></turbo-stream> to the stream named "identity:2:clearances"
77
77
  # clearance.broadcast_remove_to examiner.identity, :clearances
78
- def broadcast_remove_to(*streamables)
79
- Turbo::StreamsChannel.broadcast_remove_to *streamables, target: self
78
+ def broadcast_remove_to(*streamables, target: self)
79
+ Turbo::StreamsChannel.broadcast_remove_to *streamables, target: target
80
80
  end
81
81
 
82
82
  # Same as <tt>#broadcast_remove_to</tt>, but the designated stream is automatically set to the current model.
@@ -103,6 +103,57 @@ module Turbo::Broadcastable
103
103
  broadcast_replace_to self, **rendering
104
104
  end
105
105
 
106
+ # Update this broadcastable model in the dom for subscribers of the stream name identified by the passed
107
+ # <tt>streamables</tt>. The rendering parameters can be set by appending named arguments to the call. Examples:
108
+ #
109
+ # # Sends <turbo-stream action="update" target="clearance_5"><template><div id="clearance_5">My Clearance</div></template></turbo-stream>
110
+ # # to the stream named "identity:2:clearances"
111
+ # clearance.broadcast_update_to examiner.identity, :clearances
112
+ #
113
+ # # Sends <turbo-stream action="update" target="clearance_5"><template><div id="clearance_5">Other partial</div></template></turbo-stream>
114
+ # # to the stream named "identity:2:clearances"
115
+ # clearance.broadcast_update_to examiner.identity, :clearances, partial: "clearances/other_partial", locals: { a: 1 }
116
+ def broadcast_update_to(*streamables, **rendering)
117
+ Turbo::StreamsChannel.broadcast_update_to *streamables, target: self, **broadcast_rendering_with_defaults(rendering)
118
+ end
119
+
120
+ # Same as <tt>#broadcast_update_to</tt>, but the designated stream is automatically set to the current model.
121
+ def broadcast_update(**rendering)
122
+ broadcast_update_to self, **rendering
123
+ end
124
+
125
+ # Insert a rendering of this broadcastable model before the target identified by it's dom id passed as <tt>target</tt>
126
+ # for subscribers of the stream name identified by the passed <tt>streamables</tt>. The rendering parameters can be set by
127
+ # appending named arguments to the call. Examples:
128
+ #
129
+ # # Sends <turbo-stream action="before" target="clearance_5"><template><div id="clearance_4">My Clearance</div></template></turbo-stream>
130
+ # # to the stream named "identity:2:clearances"
131
+ # clearance.broadcast_before_to examiner.identity, :clearances, target: "clearance_5"
132
+ #
133
+ # # Sends <turbo-stream action="before" target="clearance_5"><template><div id="clearance_4">Other partial</div></template></turbo-stream>
134
+ # # to the stream named "identity:2:clearances"
135
+ # clearance.broadcast_before_to examiner.identity, :clearances, target: "clearance_5",
136
+ # partial: "clearances/other_partial", locals: { a: 1 }
137
+ def broadcast_before_to(*streamables, target:, **rendering)
138
+ Turbo::StreamsChannel.broadcast_before_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
139
+ end
140
+
141
+ # Insert a rendering of this broadcastable model after the target identified by it's dom id passed as <tt>target</tt>
142
+ # for subscribers of the stream name identified by the passed <tt>streamables</tt>. The rendering parameters can be set by
143
+ # appending named arguments to the call. Examples:
144
+ #
145
+ # # Sends <turbo-stream action="after" target="clearance_5"><template><div id="clearance_6">My Clearance</div></template></turbo-stream>
146
+ # # to the stream named "identity:2:clearances"
147
+ # clearance.broadcast_after_to examiner.identity, :clearances, target: "clearance_5"
148
+ #
149
+ # # Sends <turbo-stream action="after" target="clearance_5"><template><div id="clearance_6">Other partial</div></template></turbo-stream>
150
+ # # to the stream named "identity:2:clearances"
151
+ # clearance.broadcast_after_to examiner.identity, :clearances, target: "clearance_5",
152
+ # partial: "clearances/other_partial", locals: { a: 1 }
153
+ def broadcast_after_to(*streamables, target:, **rendering)
154
+ Turbo::StreamsChannel.broadcast_after_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
155
+ end
156
+
106
157
  # Append a rendering of this broadcastable model to the target identified by it's dom id passed as <tt>target</tt>
107
158
  # for subscribers of the stream name identified by the passed <tt>streamables</tt>. The rendering parameters can be set by
108
159
  # appending named arguments to the call. Examples:
@@ -140,7 +191,7 @@ module Turbo::Broadcastable
140
191
  Turbo::StreamsChannel.broadcast_prepend_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
141
192
  end
142
193
 
143
- # Same as <tt>#broadcast_append_to</tt>, but the designated stream is automatically set to the current model.
194
+ # Same as <tt>#broadcast_prepend_to</tt>, but the designated stream is automatically set to the current model.
144
195
  def broadcast_prepend(target: broadcast_target_default, **rendering)
145
196
  broadcast_prepend_to self, target: target, **rendering
146
197
  end
@@ -170,6 +221,16 @@ module Turbo::Broadcastable
170
221
  broadcast_replace_later_to self, **rendering
171
222
  end
172
223
 
224
+ # Same as <tt>broadcast_update_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
225
+ def broadcast_update_later_to(*streamables, **rendering)
226
+ Turbo::StreamsChannel.broadcast_update_later_to *streamables, target: self, **broadcast_rendering_with_defaults(rendering)
227
+ end
228
+
229
+ # Same as <tt>#broadcast_update_later_to</tt>, but the designated stream is automatically set to the current model.
230
+ def broadcast_update_later(**rendering)
231
+ broadcast_update_later_to self, **rendering
232
+ end
233
+
173
234
  # Same as <tt>broadcast_append_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
174
235
  def broadcast_append_later_to(*streamables, target: broadcast_target_default, **rendering)
175
236
  Turbo::StreamsChannel.broadcast_append_later_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
@@ -190,7 +251,7 @@ module Turbo::Broadcastable
190
251
  broadcast_prepend_later_to self, target: target, **rendering
191
252
  end
192
253
 
193
- # Same as <tt>broadcast_action_later_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
254
+ # Same as <tt>broadcast_action_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
194
255
  def broadcast_action_later_to(*streamables, action:, target: broadcast_target_default, **rendering)
195
256
  Turbo::StreamsChannel.broadcast_action_later_to(*streamables, action: action, target: target, **broadcast_rendering_with_defaults(rendering))
196
257
  end
@@ -219,7 +280,7 @@ module Turbo::Broadcastable
219
280
  broadcast_render_later_to self, **rendering
220
281
  end
221
282
 
222
- # Same as <tt>broadcast_prepend_to</tt> but run with the added option of naming the stream using the passed
283
+ # Same as <tt>broadcast_render_later</tt> but run with the added option of naming the stream using the passed
223
284
  # <tt>streamables</tt>.
224
285
  def broadcast_render_later_to(*streamables, **rendering)
225
286
  Turbo::StreamsChannel.broadcast_render_later_to *streamables, **broadcast_rendering_with_defaults(rendering)
@@ -233,7 +294,9 @@ module Turbo::Broadcastable
233
294
 
234
295
  def broadcast_rendering_with_defaults(options)
235
296
  options.tap do |o|
236
- o[:locals] = (o[:locals] || {}).reverse_merge!(model_name.singular.to_sym => self)
297
+ # Add the current instance into the locals with the element name (which is the un-namespaced name)
298
+ # as the key. This parallels how the ActionView::ObjectRenderer would create a local variable.
299
+ o[:locals] = (o[:locals] || {}).reverse_merge!(model_name.element.to_sym => self)
237
300
  o[:partial] ||= to_partial_path
238
301
  end
239
302
  end
@@ -40,6 +40,16 @@ class Turbo::Streams::TagBuilder
40
40
  action :remove, target, allow_inferred_rendering: false
41
41
  end
42
42
 
43
+ # Removes the <tt>targets</tt> from the dom. The targets can either be a CSS selector string or an object that responds to
44
+ # <tt>to_key</tt>, which is then called and passed through <tt>ActionView::RecordIdentifier.dom_id</tt> (all Active Records
45
+ # do). Examples:
46
+ #
47
+ # <%= turbo_stream.remove_all ".clearance_item" %>
48
+ # <%= turbo_stream.remove_all clearance %>
49
+ def remove_all(targets)
50
+ action_all :remove, targets, allow_inferred_rendering: false
51
+ end
52
+
43
53
  # Replace the <tt>target</tt> in the dom with the either the <tt>content</tt> passed in, a rendering result determined
44
54
  # by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the target as a record. Examples:
45
55
  #
@@ -53,6 +63,71 @@ class Turbo::Streams::TagBuilder
53
63
  action :replace, target, content, **rendering, &block
54
64
  end
55
65
 
66
+ # Replace the <tt>targets</tt> in the dom with the either the <tt>content</tt> passed in, a rendering result determined
67
+ # by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the target as a record. Examples:
68
+ #
69
+ # <%= turbo_stream.replace_all ".clearance_item", "<div class='clearance_item'>Replace the dom target identified by the class clearance_item</div>" %>
70
+ # <%= turbo_stream.replace_all clearance %>
71
+ # <%= turbo_stream.replace_all clearance, partial: "clearances/clearance", locals: { title: "Hello" } %>
72
+ # <%= turbo_stream.replace_all ".clearance_item" do %>
73
+ # <div class='.clearance_item'>Replace the dom target identified by the class clearance_item</div>
74
+ # <% end %>
75
+ def replace_all(targets, content = nil, **rendering, &block)
76
+ action_all :replace, targets, content, **rendering, &block
77
+ end
78
+
79
+ # Insert the <tt>content</tt> passed in, a rendering result determined by the <tt>rendering</tt> keyword arguments,
80
+ # the content in the block, or the rendering of the target as a record before the <tt>target</tt> in the dom. Examples:
81
+ #
82
+ # <%= turbo_stream.before "clearance_5", "<div id='clearance_4'>Insert before the dom target identified by clearance_5</div>" %>
83
+ # <%= turbo_stream.before clearance %>
84
+ # <%= turbo_stream.before clearance, partial: "clearances/clearance", locals: { title: "Hello" } %>
85
+ # <%= turbo_stream.before "clearance_5" do %>
86
+ # <div id='clearance_4'>Insert before the dom target identified by clearance_5</div>
87
+ # <% end %>
88
+ def before(target, content = nil, **rendering, &block)
89
+ action :before, target, content, **rendering, &block
90
+ end
91
+
92
+ # Insert the <tt>content</tt> passed in, a rendering result determined by the <tt>rendering</tt> keyword arguments,
93
+ # the content in the block, or the rendering of the target as a record before the <tt>targets</tt> in the dom. Examples:
94
+ #
95
+ # <%= turbo_stream.before_all ".clearance_item", "<div class='clearance_item'>Insert before the dom target identified by the class clearance_item</div>" %>
96
+ # <%= turbo_stream.before_all clearance %>
97
+ # <%= turbo_stream.before_all clearance, partial: "clearances/clearance", locals: { title: "Hello" } %>
98
+ # <%= turbo_stream.before_all ".clearance_item" do %>
99
+ # <div class='clearance_item'>Insert before the dom target identified by clearance_item</div>
100
+ # <% end %>
101
+ def before_all(targets, content = nil, **rendering, &block)
102
+ action_all :before, targets, content, **rendering, &block
103
+ end
104
+
105
+ # Insert the <tt>content</tt> passed in, a rendering result determined by the <tt>rendering</tt> keyword arguments,
106
+ # the content in the block, or the rendering of the target as a record after the <tt>target</tt> in the dom. Examples:
107
+ #
108
+ # <%= turbo_stream.after "clearance_5", "<div id='clearance_6'>Insert after the dom target identified by clearance_5</div>" %>
109
+ # <%= turbo_stream.after clearance %>
110
+ # <%= turbo_stream.after clearance, partial: "clearances/clearance", locals: { title: "Hello" } %>
111
+ # <%= turbo_stream.after "clearance_5" do %>
112
+ # <div id='clearance_6'>Insert after the dom target identified by clearance_5</div>
113
+ # <% end %>
114
+ def after(target, content = nil, **rendering, &block)
115
+ action :after, target, content, **rendering, &block
116
+ end
117
+
118
+ # Insert the <tt>content</tt> passed in, a rendering result determined by the <tt>rendering</tt> keyword arguments,
119
+ # the content in the block, or the rendering of the target as a record after the <tt>targets</tt> in the dom. Examples:
120
+ #
121
+ # <%= turbo_stream.after_all ".clearance_item", "<div class='clearance_item'>Insert after the dom target identified by the class clearance_item</div>" %>
122
+ # <%= turbo_stream.after_all clearance %>
123
+ # <%= turbo_stream.after_all clearance, partial: "clearances/clearance", locals: { title: "Hello" } %>
124
+ # <%= turbo_stream.after_all "clearance_item" do %>
125
+ # <div class='clearance_item'>Insert after the dom target identified by the class clearance_item</div>
126
+ # <% end %>
127
+ def after_all(targets, content = nil, **rendering, &block)
128
+ action_all :after, targets, content, **rendering, &block
129
+ end
130
+
56
131
  # Update the <tt>target</tt> in the dom with the either the <tt>content</tt> passed in or a rendering result determined
57
132
  # by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the target as a record. Examples:
58
133
  #
@@ -66,6 +141,19 @@ class Turbo::Streams::TagBuilder
66
141
  action :update, target, content, **rendering, &block
67
142
  end
68
143
 
144
+ # Update the <tt>targets</tt> in the dom with the either the <tt>content</tt> passed in or a rendering result determined
145
+ # by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the targets as a record. Examples:
146
+ #
147
+ # <%= turbo_stream.update_all "clearance_item", "Update the content of the dom target identified by the class clearance_item" %>
148
+ # <%= turbo_stream.update_all clearance %>
149
+ # <%= turbo_stream.update_all clearance, partial: "clearances/new_clearance", locals: { title: "Hello" } %>
150
+ # <%= turbo_stream.update_all "clearance_item" do %>
151
+ # Update the content of the dom target identified by the class clearance_item
152
+ # <% end %>
153
+ def update_all(targets, content = nil, **rendering, &block)
154
+ action_all :update, targets, content, **rendering, &block
155
+ end
156
+
69
157
  # Append to the target in the dom identified with <tt>target</tt> either the <tt>content</tt> passed in or a
70
158
  # rendering result determined by the <tt>rendering</tt> keyword arguments, the content in the block,
71
159
  # or the rendering of the content as a record. Examples:
@@ -80,6 +168,20 @@ class Turbo::Streams::TagBuilder
80
168
  action :append, target, content, **rendering, &block
81
169
  end
82
170
 
171
+ # Append to the targets in the dom identified with <tt>targets</tt> either the <tt>content</tt> passed in or a
172
+ # rendering result determined by the <tt>rendering</tt> keyword arguments, the content in the block,
173
+ # or the rendering of the content as a record. Examples:
174
+ #
175
+ # <%= turbo_stream.append_all ".clearances", "<div class='clearance_item'>Append this to .clearance_group</div>" %>
176
+ # <%= turbo_stream.append_all ".clearances", clearance %>
177
+ # <%= turbo_stream.append_all ".clearances", partial: "clearances/new_clearance", locals: { clearance: clearance } %>
178
+ # <%= turbo_stream.append_all ".clearances" do %>
179
+ # <div id='clearance_item'>Append this to .clearances</div>
180
+ # <% end %>
181
+ def append_all(targets, content = nil, **rendering, &block)
182
+ action_all :append, targets, content, **rendering, &block
183
+ end
184
+
83
185
  # Prepend to the target in the dom identified with <tt>target</tt> either the <tt>content</tt> passed in or a
84
186
  # rendering result determined by the <tt>rendering</tt> keyword arguments or the content in the block,
85
187
  # or the rendering of the content as a record. Examples:
@@ -94,20 +196,34 @@ class Turbo::Streams::TagBuilder
94
196
  action :prepend, target, content, **rendering, &block
95
197
  end
96
198
 
97
- # Send an action of the type <tt>name</tt>. Options described in the concrete methods.
199
+ # Prepend to the targets in the dom identified with <tt>targets</tt> either the <tt>content</tt> passed in or a
200
+ # rendering result determined by the <tt>rendering</tt> keyword arguments or the content in the block,
201
+ # or the rendering of the content as a record. Examples:
202
+ #
203
+ # <%= turbo_stream.prepend_all ".clearances", "<div class='clearance_item'>Prepend this to .clearances</div>" %>
204
+ # <%= turbo_stream.prepend_all ".clearances", clearance %>
205
+ # <%= turbo_stream.prepend_all ".clearances", partial: "clearances/new_clearance", locals: { clearance: clearance } %>
206
+ # <%= turbo_stream.prepend_all ".clearances" do %>
207
+ # <div class='clearance_item'>Prepend this to .clearances</div>
208
+ # <% end %>
209
+ def prepend_all(targets, content = nil, **rendering, &block)
210
+ action_all :prepend, targets, content, **rendering, &block
211
+ end
212
+
213
+ # Send an action of the type <tt>name</tt> to <tt>target</tt>. Options described in the concrete methods.
98
214
  def action(name, target, content = nil, allow_inferred_rendering: true, **rendering, &block)
99
215
  target_name = extract_target_name_from(target)
216
+ template = render_template(target, content, allow_inferred_rendering: allow_inferred_rendering, **rendering, &block)
100
217
 
101
- case
102
- when content
103
- turbo_stream_action_tag name, target: target_name, template: (render_record(content) if allow_inferred_rendering) || content
104
- when block_given?
105
- turbo_stream_action_tag name, target: target_name, template: @view_context.capture(&block)
106
- when rendering.any?
107
- turbo_stream_action_tag name, target: target_name, template: @view_context.render(formats: [ :html ], **rendering)
108
- else
109
- turbo_stream_action_tag name, target: target_name, template: (render_record(target) if allow_inferred_rendering)
110
- end
218
+ turbo_stream_action_tag name, target: target_name, template: template
219
+ end
220
+
221
+ # Send an action of the type <tt>name</tt> to <tt>targets</tt>. Options described in the concrete methods.
222
+ def action_all(name, targets, content = nil, allow_inferred_rendering: true, **rendering, &block)
223
+ targets_name = extract_target_name_from(targets)
224
+ template = render_template(targets, content, allow_inferred_rendering: allow_inferred_rendering, **rendering, &block)
225
+
226
+ turbo_stream_action_tag name, targets: targets_name, template: template
111
227
  end
112
228
 
113
229
  private
@@ -119,6 +235,19 @@ class Turbo::Streams::TagBuilder
119
235
  end
120
236
  end
121
237
 
238
+ def render_template(target, content = nil, allow_inferred_rendering: true, **rendering, &block)
239
+ case
240
+ when content
241
+ allow_inferred_rendering ? (render_record(content) || content) : content
242
+ when block_given?
243
+ @view_context.capture(&block)
244
+ when rendering.any?
245
+ @view_context.render(formats: [ :html ], **rendering)
246
+ else
247
+ render_record(target) if allow_inferred_rendering
248
+ end
249
+ end
250
+
122
251
  def render_record(possible_record)
123
252
  if possible_record.respond_to?(:to_partial_path)
124
253
  record = possible_record