Module: Turbo::Broadcastable

Extended by:
ActiveSupport::Concern
Defined in:
app/models/concerns/turbo/broadcastable.rb

Overview

Turbo streams can be broadcast directly from models that include this module (this is automatically done for Active Records). This makes it convenient to execute both synchronous and asynchronous updates, and render directly from callbacks in models or from controllers or jobs that act on those models. Here's an example:

class Clearance < ApplicationRecord
  belongs_to :petitioner, class_name: "Contact"
  belongs_to :examiner,   class_name: "User"

  after_create_commit :broadcast_later

  private
    def broadcast_later
      broadcast_prepend_later_to examiner.identity, :clearances
    end
end

This is an example from [HEY](hey.com), and the clearance is the model that drives [the screener](hey.com/features/the-screener/), which gives users the power to deny first-time senders (petitioners) access to their attention (as the examiner). When a new clearance is created upon receipt of an email from a first-time sender, that'll trigger the call to broadcast_later, which in turn invokes broadcast_prepend_later_to.

That method enqueues a Turbo::Streams::ActionBroadcastJob for the prepend, which will render the partial for clearance (it knows which by calling Clearance#to_partial_path, which in this case returns clearances/_clearance.html.erb), send that to all users that have subscribed to updates (using turbo_stream_from(examiner.identity, :clearances) in a view) using the Turbo::StreamsChannel under the stream name derived from [ examiner.identity, :clearances ], and finally prepend the result of that partial rendering to the target identified with the dom id “clearances” (which is derived by default from the plural model name of the model, but can be overwritten).

There are four basic actions you can broadcast: remove, replace, append, and prepend. As a rule, you should use the _later versions of everything except for remove when broadcasting within a real-time path, like a controller or model, since all those updates require a rendering step, which can slow down execution. You don't need to do this for remove, since only the dom id for the model is used.

In addition to the four basic actions, you can also use broadcast_render_later or broadcast_render_later_to to render a turbo stream template with multiple actions.

Defined Under Namespace

Modules: ClassMethods

Instance Method Summary collapse

Instance Method Details

#broadcast_action(action, target: broadcast_target_default, **rendering) ⇒ Object

Same as #broadcast_action_to, but the designated stream is automatically set to the current model.


153
154
155
# File 'app/models/concerns/turbo/broadcastable.rb', line 153

def broadcast_action(action, target: broadcast_target_default, **rendering)
  broadcast_action_to self, action: action, target: target, **rendering
end

#broadcast_action_later(action:, target: broadcast_target_default, **rendering) ⇒ Object

Same as #broadcast_action_later_to, but the designated stream is automatically set to the current model.


194
195
196
# File 'app/models/concerns/turbo/broadcastable.rb', line 194

def broadcast_action_later(action:, target: broadcast_target_default, **rendering)
  broadcast_action_later_to self, action: action, target: target, **rendering
end

#broadcast_action_later_to(*streamables, action:, target: broadcast_target_default, **rendering) ⇒ Object

Same as broadcast_action_later_to but run asynchronously via a Turbo::Streams::BroadcastJob.


189
190
191
# File 'app/models/concerns/turbo/broadcastable.rb', line 189

def broadcast_action_later_to(*streamables, action:, target: broadcast_target_default, **rendering)
  Turbo::StreamsChannel.broadcast_action_later_to(*streamables, action: action, target: target, **broadcast_rendering_with_defaults(rendering))
end

#broadcast_action_to(*streamables, action:, target: broadcast_target_default, **rendering) ⇒ Object

Broadcast a named action, allowing for dynamic dispatch, instead of using the concrete action methods. Examples:

# Sends <turbo-stream action="prepend" target="clearances"><template><div id="clearance_5">My Clearance</div></template></turbo-stream>
# to the stream named "identity:2:clearances"
clearance.broadcast_action_to examiner.identity, :clearances, action: :prepend, target: "clearances"

148
149
150
# File 'app/models/concerns/turbo/broadcastable.rb', line 148

def broadcast_action_to(*streamables, action:, target: broadcast_target_default, **rendering)
  Turbo::StreamsChannel.broadcast_action_to(*streamables, action: action, target: target, **broadcast_rendering_with_defaults(rendering))
end

#broadcast_append(target: broadcast_target_default, **rendering) ⇒ Object

Same as #broadcast_append_to, but the designated stream is automatically set to the current model.


118
119
120
# File 'app/models/concerns/turbo/broadcastable.rb', line 118

def broadcast_append(target: broadcast_target_default, **rendering)
  broadcast_append_to self, target: target, **rendering
end

#broadcast_append_later(target: broadcast_target_default, **rendering) ⇒ Object

Same as #broadcast_append_later_to, but the designated stream is automatically set to the current model.


174
175
176
# File 'app/models/concerns/turbo/broadcastable.rb', line 174

def broadcast_append_later(target: broadcast_target_default, **rendering)
  broadcast_append_later_to self, target: target, **rendering
end

#broadcast_append_later_to(*streamables, target: broadcast_target_default, **rendering) ⇒ Object

Same as broadcast_append_to but run asynchronously via a Turbo::Streams::BroadcastJob.


169
170
171
# File 'app/models/concerns/turbo/broadcastable.rb', line 169

def broadcast_append_later_to(*streamables, target: broadcast_target_default, **rendering)
  Turbo::StreamsChannel.broadcast_append_later_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
end

#broadcast_append_to(*streamables, target: broadcast_target_default, **rendering) ⇒ Object

Append a rendering of this broadcastable model to the target identified by it's dom id passed as target for subscribers of the stream name identified by the passed streamables. The rendering parameters can be set by appending named arguments to the call. Examples:

# Sends <turbo-stream action="append" target="clearances"><template><div id="clearance_5">My Clearance</div></template></turbo-stream>
# to the stream named "identity:2:clearances"
clearance.broadcast_append_to examiner.identity, :clearances, target: "clearances"

# Sends <turbo-stream action="append" target="clearances"><template><div id="clearance_5">Other partial</div></template></turbo-stream>
# to the stream named "identity:2:clearances"
clearance.broadcast_append_to examiner.identity, :clearances, target: "clearances",
  partial: "clearances/other_partial", locals: { a: 1 }

113
114
115
# File 'app/models/concerns/turbo/broadcastable.rb', line 113

def broadcast_append_to(*streamables, target: broadcast_target_default, **rendering)
  Turbo::StreamsChannel.broadcast_append_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
end

#broadcast_prepend(target: broadcast_target_default, **rendering) ⇒ Object

Same as #broadcast_append_to, but the designated stream is automatically set to the current model.


139
140
141
# File 'app/models/concerns/turbo/broadcastable.rb', line 139

def broadcast_prepend(target: broadcast_target_default, **rendering)
  broadcast_prepend_to self, target: target, **rendering
end

#broadcast_prepend_later(target: broadcast_target_default, **rendering) ⇒ Object

Same as #broadcast_prepend_later_to, but the designated stream is automatically set to the current model.


184
185
186
# File 'app/models/concerns/turbo/broadcastable.rb', line 184

def broadcast_prepend_later(target: broadcast_target_default, **rendering)
  broadcast_prepend_later_to self, target: target, **rendering
end

#broadcast_prepend_later_to(*streamables, target: broadcast_target_default, **rendering) ⇒ Object

Same as broadcast_prepend_to but run asynchronously via a Turbo::Streams::BroadcastJob.


179
180
181
# File 'app/models/concerns/turbo/broadcastable.rb', line 179

def broadcast_prepend_later_to(*streamables, target: broadcast_target_default, **rendering)
  Turbo::StreamsChannel.broadcast_prepend_later_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
end

#broadcast_prepend_to(*streamables, target: broadcast_target_default, **rendering) ⇒ Object

Prepend a rendering of this broadcastable model to the target identified by it's dom id passed as target for subscribers of the stream name identified by the passed streamables. The rendering parameters can be set by appending named arguments to the call. Examples:

# Sends <turbo-stream action="prepend" target="clearances"><template><div id="clearance_5">My Clearance</div></template></turbo-stream>
# to the stream named "identity:2:clearances"
clearance.broadcast_prepend_to examiner.identity, :clearances, target: "clearances"

# Sends <turbo-stream action="prepend" target="clearances"><template><div id="clearance_5">Other partial</div></template></turbo-stream>
# to the stream named "identity:2:clearances"
clearance.broadcast_prepend_to examiner.identity, :clearances, target: "clearances",
  partial: "clearances/other_partial", locals: { a: 1 }

134
135
136
# File 'app/models/concerns/turbo/broadcastable.rb', line 134

def broadcast_prepend_to(*streamables, target: broadcast_target_default, **rendering)
  Turbo::StreamsChannel.broadcast_prepend_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
end

#broadcast_removeObject

Same as #broadcast_remove_to, but the designated stream is automatically set to the current model.


78
79
80
# File 'app/models/concerns/turbo/broadcastable.rb', line 78

def broadcast_remove
  broadcast_remove_to self
end

#broadcast_remove_to(*streamables) ⇒ Object

Remove this broadcastable model from the dom for subscribers of the stream name identified by the passed streamables. Example:

# Sends <turbo-stream action="remove" target="clearance_5"></turbo-stream> to the stream named "identity:2:clearances"
clearance.broadcast_remove_to examiner.identity, :clearances

73
74
75
# File 'app/models/concerns/turbo/broadcastable.rb', line 73

def broadcast_remove_to(*streamables)
  Turbo::StreamsChannel.broadcast_remove_to *streamables, target: self
end

#broadcast_render_later(**rendering) ⇒ Object

Render a turbo stream template asynchronously with this broadcastable model passed as the local variable using a Turbo::Streams::BroadcastJob. Example:

# Template: entries/_entry.turbo_stream.erb
<%= turbo_stream.remove entry %>

<%= turbo_stream.append "entries", entry if entry.active? %>

Sends:

<turbo-stream action="remove" target="entry_5"></turbo-stream>
<turbo-stream action="append" target="entries"><template><div id="entry_5">My Entry</div></template></turbo-stream>

…to the stream named “entry:5”


213
214
215
# File 'app/models/concerns/turbo/broadcastable.rb', line 213

def broadcast_render_later(**rendering)
  broadcast_render_later_to self, **rendering
end

#broadcast_render_later_to(*streamables, **rendering) ⇒ Object

Same as broadcast_prepend_to but run with the added option of naming the stream using the passed streamables.


219
220
221
# File 'app/models/concerns/turbo/broadcastable.rb', line 219

def broadcast_render_later_to(*streamables, **rendering)
  Turbo::StreamsChannel.broadcast_render_later_to *streamables, **broadcast_rendering_with_defaults(rendering)
end

#broadcast_replace(**rendering) ⇒ Object

Same as #broadcast_replace_to, but the designated stream is automatically set to the current model.


97
98
99
# File 'app/models/concerns/turbo/broadcastable.rb', line 97

def broadcast_replace(**rendering)
  broadcast_replace_to self, **rendering
end

#broadcast_replace_later(**rendering) ⇒ Object

Same as #broadcast_replace_later_to, but the designated stream is automatically set to the current model.


164
165
166
# File 'app/models/concerns/turbo/broadcastable.rb', line 164

def broadcast_replace_later(**rendering)
  broadcast_replace_later_to self, **rendering
end

#broadcast_replace_later_to(*streamables, **rendering) ⇒ Object

Same as broadcast_replace_to but run asynchronously via a Turbo::Streams::BroadcastJob.


159
160
161
# File 'app/models/concerns/turbo/broadcastable.rb', line 159

def broadcast_replace_later_to(*streamables, **rendering)
  Turbo::StreamsChannel.broadcast_replace_later_to *streamables, target: self, **broadcast_rendering_with_defaults(rendering)
end

#broadcast_replace_to(*streamables, **rendering) ⇒ Object

Replace this broadcastable model in the dom for subscribers of the stream name identified by the passed streamables. The rendering parameters can be set by appending named arguments to the call. Examples:

# Sends <turbo-stream action="replace" target="clearance_5"><template><div id="clearance_5">My Clearance</div></template></turbo-stream>
# to the stream named "identity:2:clearances"
clearance.broadcast_replace_to examiner.identity, :clearances

# Sends <turbo-stream action="replace" target="clearance_5"><template><div id="clearance_5">Other partial</div></template></turbo-stream>
# to the stream named "identity:2:clearances"
clearance.broadcast_replace_to examiner.identity, :clearances, partial: "clearances/other_partial", locals: { a: 1 }

92
93
94
# File 'app/models/concerns/turbo/broadcastable.rb', line 92

def broadcast_replace_to(*streamables, **rendering)
  Turbo::StreamsChannel.broadcast_replace_to *streamables, target: self, **broadcast_rendering_with_defaults(rendering)
end