-
Notifications
You must be signed in to change notification settings - Fork 330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for page refreshes and broadcasting #499
Merged
Merged
Changes from 3 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
a9e011d
Support for page refreshes and broadcasting
jorgemanrubia 6b94462
Upgrade terser and its rollup plugin to support private method's syntax
jorgemanrubia ddeeeb8
Build latest turbo version from the page-refreshes branch in hotwired…
jorgemanrubia a669409
Update with last turbo build
jorgemanrubia 8fcc54b
Debounce jobs to broadcast page refresh signals
jorgemanrubia 9d3fdeb
Build latest turbo version from the page-refreshes branch in hotwired…
jorgemanrubia a49f6a6
Update with last changes
jorgemanrubia d86dc96
Commit js changes
afcapel 57671f9
Build with latest turbo master
jorgemanrubia 00b2b24
Make wait noop if scheduled_task is nil
afcapel b385276
Build with latest turbo master
jorgemanrubia 1d6b6d3
Build with latest turbo/main
afcapel ec710c7
Don't include JS changes in this branch
afcapel 3199f7a
Use released Turbo version
afcapel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module Turbo::RequestIdTracking | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
around_action :turbo_tracking_request_id | ||
end | ||
|
||
private | ||
def turbo_tracking_request_id(&block) | ||
Turbo.with_request_id(request.headers["X-Turbo-Request-Id"], &block) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
class Turbo::Streams::BroadcastStreamJob < ActiveJob::Base | ||
discard_on ActiveJob::DeserializationError | ||
|
||
def perform(stream, content:) | ||
Turbo::StreamsChannel.broadcast_stream_to(stream, content: content) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -75,9 +75,32 @@ | |||||
# In addition to the four basic actions, you can also use <tt>broadcast_render</tt>, | ||||||
# <tt>broadcast_render_to</tt> <tt>broadcast_render_later</tt>, and <tt>broadcast_render_later_to</tt> | ||||||
# to render a turbo stream template with multiple actions. | ||||||
# | ||||||
# == Suppressing broadcasts | ||||||
# | ||||||
# Sometimes, you need to disable broadcasts in certain scenarios. You can use <tt>.suppressing_turbo_broadcasts</tt> to create | ||||||
# execution contexts where broadcasts are disabled: | ||||||
# | ||||||
# class Message < ApplicationRecord | ||||||
# after_create_commit :update_message | ||||||
# | ||||||
# private | ||||||
# def update_message | ||||||
# broadcast_replace_to(user, :message, target: "message", renderable: MessageComponent.new) | ||||||
# end | ||||||
# end | ||||||
# | ||||||
# Message.suppressing_turbo_broadcasts do | ||||||
# Message.create!(board: board) # This won't broadcast the replace action | ||||||
# end | ||||||
module Turbo::Broadcastable | ||||||
extend ActiveSupport::Concern | ||||||
|
||||||
included do | ||||||
thread_mattr_accessor :suppressed_turbo_broadcasts, instance_accessor: false | ||||||
delegate :suppressed_turbo_broadcasts?, to: "self.class" | ||||||
end | ||||||
|
||||||
module ClassMethods | ||||||
# Configures the model to broadcast creates, updates, and destroys to a stream name derived at runtime by the | ||||||
# <tt>stream</tt> symbol invocation. By default, the creates are appended to a dom id target name derived from | ||||||
|
@@ -112,10 +135,34 @@ def broadcasts(stream = model_name.plural, inserts_by: :append, target: broadcas | |||||
after_destroy_commit -> { broadcast_remove } | ||||||
end | ||||||
|
||||||
# Configures the model to broadcast a "page refresh" on creates, updates, and destroys to a stream | ||||||
# name derived at runtime by the <tt>stream</tt> symbol invocation. | ||||||
def broadcasts_refreshes_to(stream) | ||||||
after_commit -> { broadcast_refresh_later_to(stream.try(:call, self) || send(stream)) } | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcasts_refreshes_to</tt>, but the designated stream for page refreshes is automatically set to | ||||||
# the current model. | ||||||
def broadcasts_refreshes | ||||||
after_commit -> { broadcast_refresh_later } | ||||||
end | ||||||
|
||||||
# All default targets will use the return of this method. Overwrite if you want something else than <tt>model_name.plural</tt>. | ||||||
def broadcast_target_default | ||||||
model_name.plural | ||||||
end | ||||||
|
||||||
# Executes +block+ preventing both synchronous and asynchronous broadcasts from this model. | ||||||
def suppressing_turbo_broadcasts(&block) | ||||||
original, self.suppressed_turbo_broadcasts = self.suppressed_turbo_broadcasts, true | ||||||
yield | ||||||
ensure | ||||||
self.suppressed_turbo_broadcasts = original | ||||||
end | ||||||
|
||||||
def suppressed_turbo_broadcasts? | ||||||
suppressed_turbo_broadcasts | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
end | ||||||
end | ||||||
|
||||||
# Remove this broadcastable model from the dom for subscribers of the stream name identified by the passed streamables. | ||||||
|
@@ -124,7 +171,7 @@ def broadcast_target_default | |||||
# # Sends <turbo-stream action="remove" target="clearance_5"></turbo-stream> to the stream named "identity:2:clearances" | ||||||
# clearance.broadcast_remove_to examiner.identity, :clearances | ||||||
def broadcast_remove_to(*streamables, target: self) | ||||||
Turbo::StreamsChannel.broadcast_remove_to(*streamables, target: target) | ||||||
Turbo::StreamsChannel.broadcast_remove_to(*streamables, target: target) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_remove_to</tt>, but the designated stream is automatically set to the current model. | ||||||
|
@@ -143,7 +190,7 @@ def broadcast_remove | |||||
# # to the stream named "identity:2:clearances" | ||||||
# clearance.broadcast_replace_to examiner.identity, :clearances, partial: "clearances/other_partial", locals: { a: 1 } | ||||||
def broadcast_replace_to(*streamables, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_replace_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) | ||||||
Turbo::StreamsChannel.broadcast_replace_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_replace_to</tt>, but the designated stream is automatically set to the current model. | ||||||
|
@@ -162,7 +209,7 @@ def broadcast_replace(**rendering) | |||||
# # to the stream named "identity:2:clearances" | ||||||
# clearance.broadcast_update_to examiner.identity, :clearances, partial: "clearances/other_partial", locals: { a: 1 } | ||||||
def broadcast_update_to(*streamables, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_update_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) | ||||||
Turbo::StreamsChannel.broadcast_update_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_update_to</tt>, but the designated stream is automatically set to the current model. | ||||||
|
@@ -215,7 +262,7 @@ def broadcast_after_to(*streamables, target:, **rendering) | |||||
# clearance.broadcast_append_to examiner.identity, :clearances, target: "clearances", | ||||||
# partial: "clearances/other_partial", locals: { a: 1 } | ||||||
def broadcast_append_to(*streamables, target: broadcast_target_default, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_append_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering)) | ||||||
Turbo::StreamsChannel.broadcast_append_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_append_to</tt>, but the designated stream is automatically set to the current model. | ||||||
|
@@ -236,21 +283,29 @@ def broadcast_append(target: broadcast_target_default, **rendering) | |||||
# clearance.broadcast_prepend_to examiner.identity, :clearances, target: "clearances", | ||||||
# partial: "clearances/other_partial", locals: { a: 1 } | ||||||
def broadcast_prepend_to(*streamables, target: broadcast_target_default, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_prepend_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering)) | ||||||
Turbo::StreamsChannel.broadcast_prepend_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_prepend_to</tt>, but the designated stream is automatically set to the current model. | ||||||
def broadcast_prepend(target: broadcast_target_default, **rendering) | ||||||
broadcast_prepend_to self, target: target, **rendering | ||||||
end | ||||||
|
||||||
def broadcast_refresh_to(*streamables) | ||||||
Turbo::StreamsChannel.broadcast_refresh_to *streamables unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
def broadcast_refresh | ||||||
broadcast_refresh_to self | ||||||
end | ||||||
|
||||||
# Broadcast a named <tt>action</tt>, 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" | ||||||
def broadcast_action_to(*streamables, action:, target: broadcast_target_default, attributes: {}, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_action_to(*streamables, action: action, target: target, attributes: attributes, **broadcast_rendering_with_defaults(rendering)) | ||||||
Turbo::StreamsChannel.broadcast_action_to(*streamables, action: action, target: target, attributes: attributes, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_action_to</tt>, but the designated stream is automatically set to the current model. | ||||||
|
@@ -261,7 +316,7 @@ def broadcast_action(action, target: broadcast_target_default, attributes: {}, * | |||||
|
||||||
# Same as <tt>broadcast_replace_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>. | ||||||
def broadcast_replace_later_to(*streamables, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_replace_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) | ||||||
Turbo::StreamsChannel.broadcast_replace_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_replace_later_to</tt>, but the designated stream is automatically set to the current model. | ||||||
|
@@ -271,7 +326,7 @@ def broadcast_replace_later(**rendering) | |||||
|
||||||
# Same as <tt>broadcast_update_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>. | ||||||
def broadcast_update_later_to(*streamables, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_update_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) | ||||||
Turbo::StreamsChannel.broadcast_update_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_update_later_to</tt>, but the designated stream is automatically set to the current model. | ||||||
|
@@ -281,7 +336,7 @@ def broadcast_update_later(**rendering) | |||||
|
||||||
# Same as <tt>broadcast_append_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>. | ||||||
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)) | ||||||
Turbo::StreamsChannel.broadcast_append_later_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_append_later_to</tt>, but the designated stream is automatically set to the current model. | ||||||
|
@@ -291,17 +346,25 @@ def broadcast_append_later(target: broadcast_target_default, **rendering) | |||||
|
||||||
# Same as <tt>broadcast_prepend_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>. | ||||||
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)) | ||||||
Turbo::StreamsChannel.broadcast_prepend_later_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_prepend_later_to</tt>, but the designated stream is automatically set to the current model. | ||||||
def broadcast_prepend_later(target: broadcast_target_default, **rendering) | ||||||
broadcast_prepend_later_to self, target: target, **rendering | ||||||
end | ||||||
|
||||||
def broadcast_refresh_later_to(*streamables, target: broadcast_target_default, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_refresh_later_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering).merge(request_id: Turbo.current_request_id)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
def broadcast_refresh_later(target: broadcast_target_default, **rendering) | ||||||
broadcast_refresh_later_to self, target: target, **rendering | ||||||
end | ||||||
|
||||||
# Same as <tt>broadcast_action_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>. | ||||||
def broadcast_action_later_to(*streamables, action:, target: broadcast_target_default, attributes: {}, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_action_later_to(*streamables, action: action, target: target, attributes: attributes, **broadcast_rendering_with_defaults(rendering)) | ||||||
Turbo::StreamsChannel.broadcast_action_later_to(*streamables, action: action, target: target, attributes: attributes, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>#broadcast_action_later_to</tt>, but the designated stream is automatically set to the current model. | ||||||
|
@@ -337,7 +400,7 @@ def broadcast_render(**rendering) | |||||
# desireable for model callbacks, certainly not if those callbacks are inside of a transaction. Most of the time you should | ||||||
# be using `broadcast_render_later_to`, unless you specifically know why synchronous rendering is needed. | ||||||
def broadcast_render_to(*streamables, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_render_to(*streamables, **broadcast_rendering_with_defaults(rendering)) | ||||||
Turbo::StreamsChannel.broadcast_render_to(*streamables, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
# Same as <tt>broadcast_action_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>. | ||||||
|
@@ -348,7 +411,7 @@ def broadcast_render_later(**rendering) | |||||
# Same as <tt>broadcast_render_later</tt> but run with the added option of naming the stream using the passed | ||||||
# <tt>streamables</tt>. | ||||||
def broadcast_render_later_to(*streamables, **rendering) | ||||||
Turbo::StreamsChannel.broadcast_render_later_to(*streamables, **broadcast_rendering_with_defaults(rendering)) | ||||||
Turbo::StreamsChannel.broadcast_render_later_to(*streamables, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? | ||||||
end | ||||||
|
||||||
|
||||||
|
@@ -361,7 +424,7 @@ def broadcast_rendering_with_defaults(options) | |||||
options.tap do |o| | ||||||
# Add the current instance into the locals with the element name (which is the un-namespaced name) | ||||||
# as the key. This parallels how the ActionView::ObjectRenderer would create a local variable. | ||||||
o[:locals] = (o[:locals] || {}).reverse_merge!(model_name.element.to_sym => self) | ||||||
o[:locals] = (o[:locals] || {}).reverse_merge!(model_name.element.to_sym => self, request_id: Turbo.current_request_id).compact | ||||||
|
||||||
if o[:html] || o[:partial] | ||||||
return o | ||||||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
require "test_helper" | ||
require "action_cable" | ||
|
||
class Turbo::CurrentRequestIdTest < ActiveSupport::TestCase | ||
test "sets the current request id for a block of code" do | ||
assert_nil Turbo.current_request_id | ||
|
||
result = Turbo.with_request_id("123") do | ||
assert_equal "123", Turbo.current_request_id | ||
:the_result | ||
end | ||
|
||
assert_equal :the_result, result | ||
assert_nil Turbo.current_request_id | ||
end | ||
|
||
test "raised errors will raise and clear the current request id" do | ||
assert_nil Turbo.current_request_id | ||
|
||
assert_raise "Some error" do | ||
Turbo.with_request_id("123") do | ||
raise "Some error" | ||
end | ||
end | ||
|
||
assert_nil Turbo.current_request_id | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class RequestIdsController < ApplicationController | ||
def show | ||
render json: { turbo_frame_request_id: Turbo.current_request_id } | ||
end | ||
end |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, this will only send broadcast refreshes to the current model's channel, right? Shouldn't it be closer to the broadcasts method and accept the stream argument or default to the model's plural name when creating the model (see #295)? Otherwise, it will send a broadcast to a channel that no one will ever be listening on (because the model is being created), right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sent a PR: #521