turbo-rails 0.5.9 → 0.6.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.
@@ -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