turbo-rails 1.5.0 → 2.0.0.pre.beta.1
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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/turbo.js +1498 -685
- data/app/assets/javascripts/turbo.min.js +9 -5
- data/app/assets/javascripts/turbo.min.js.map +1 -1
- data/app/channels/turbo/streams/broadcasts.rb +18 -4
- data/app/controllers/concerns/turbo/request_id_tracking.rb +12 -0
- data/app/helpers/turbo/drive_helper.rb +8 -0
- data/app/helpers/turbo/frames_helper.rb +4 -1
- data/app/helpers/turbo/streams/action_helper.rb +5 -1
- data/app/jobs/turbo/streams/action_broadcast_job.rb +2 -2
- data/app/jobs/turbo/streams/broadcast_stream_job.rb +7 -0
- data/app/models/concerns/turbo/broadcastable.rb +85 -20
- data/app/models/turbo/debouncer.rb +24 -0
- data/app/models/turbo/thread_debouncer.rb +28 -0
- data/config/routes.rb +0 -1
- data/lib/turbo/engine.rb +6 -0
- data/lib/turbo/version.rb +1 -1
- data/lib/turbo-rails.rb +9 -0
- metadata +8 -4
@@ -33,9 +33,14 @@ module Turbo::Streams::Broadcasts
|
|
33
33
|
broadcast_action_to(*streamables, action: :prepend, **opts)
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
36
|
+
def broadcast_refresh_to(*streamables, **opts)
|
37
|
+
broadcast_stream_to(*streamables, content: turbo_stream_refresh_tag)
|
38
|
+
end
|
39
|
+
|
40
|
+
def broadcast_action_to(*streamables, action:, target: nil, targets: nil, attributes: {}, **rendering)
|
37
41
|
broadcast_stream_to(*streamables, content: turbo_stream_action_tag(action, target: target, targets: targets, template:
|
38
|
-
rendering.delete(:content) || rendering.delete(:html) || (rendering.any? ? render_format(:html, **rendering) : nil)
|
42
|
+
rendering.delete(:content) || rendering.delete(:html) || (rendering[:render] != false && rendering.any? ? render_format(:html, **rendering) : nil),
|
43
|
+
**attributes
|
39
44
|
))
|
40
45
|
end
|
41
46
|
|
@@ -63,9 +68,15 @@ module Turbo::Streams::Broadcasts
|
|
63
68
|
broadcast_action_later_to(*streamables, action: :prepend, **opts)
|
64
69
|
end
|
65
70
|
|
66
|
-
def
|
71
|
+
def broadcast_refresh_later_to(*streamables, request_id: Turbo.current_request_id, **opts)
|
72
|
+
refresh_debouncer_for(*streamables, request_id: request_id).debounce do
|
73
|
+
Turbo::Streams::BroadcastStreamJob.perform_later stream_name_from(streamables), content: turbo_stream_refresh_tag(request_id: request_id, **opts)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def broadcast_action_later_to(*streamables, action:, target: nil, targets: nil, attributes: {}, **rendering)
|
67
78
|
Turbo::Streams::ActionBroadcastJob.perform_later \
|
68
|
-
stream_name_from(streamables), action: action, target: target, targets: targets, **rendering
|
79
|
+
stream_name_from(streamables), action: action, target: target, targets: targets, attributes: attributes, **rendering
|
69
80
|
end
|
70
81
|
|
71
82
|
def broadcast_render_to(*streamables, **rendering)
|
@@ -80,6 +91,9 @@ module Turbo::Streams::Broadcasts
|
|
80
91
|
ActionCable.server.broadcast stream_name_from(streamables), content
|
81
92
|
end
|
82
93
|
|
94
|
+
def refresh_debouncer_for(*streamables, request_id: nil) # :nodoc:
|
95
|
+
Turbo::ThreadDebouncer.for("turbo-refresh-debouncer-#{stream_name_from(streamables.including(request_id))}")
|
96
|
+
end
|
83
97
|
|
84
98
|
private
|
85
99
|
def render_format(format, **rendering)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Turbo::RequestIdTracking
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
around_action :turbo_tracking_request_id
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
def turbo_tracking_request_id(&block)
|
10
|
+
Turbo.with_request_id(request.headers["X-Turbo-Request-Id"], &block)
|
11
|
+
end
|
12
|
+
end
|
@@ -26,4 +26,12 @@ module Turbo::DriveHelper
|
|
26
26
|
def turbo_page_requires_reload
|
27
27
|
provide :head, tag.meta(name: "turbo-visit-control", content: "reload")
|
28
28
|
end
|
29
|
+
|
30
|
+
def turbo_refreshes_with(method: :replace, scroll: :reset)
|
31
|
+
raise ArgumentError, "Invalid refresh option '#{method}'" unless method.in?(%i[ replace morph ])
|
32
|
+
raise ArgumentError, "Invalid scroll option '#{scroll}'" unless scroll.in?(%i[ reset preserve ])
|
33
|
+
|
34
|
+
provide :head, tag.meta(name: "turbo-refresh-method", content: method)
|
35
|
+
provide :head, tag.meta(name: "turbo-refresh-scroll", content: scroll)
|
36
|
+
end
|
29
37
|
end
|
@@ -24,6 +24,9 @@ module Turbo::FramesHelper
|
|
24
24
|
# <% end %>
|
25
25
|
# # => <turbo-frame id="tray"><div>My tray frame!</div></turbo-frame>
|
26
26
|
#
|
27
|
+
# <%= turbo_frame_tag [user_id, "tray"], src: tray_path(tray) %>
|
28
|
+
# # => <turbo-frame id="1_tray" src="http://example.com/trays/1"></turbo-frame>
|
29
|
+
#
|
27
30
|
# The `turbo_frame_tag` helper will convert the arguments it receives to their
|
28
31
|
# `dom_id` if applicable to easily generate unique ids for Turbo Frames:
|
29
32
|
#
|
@@ -36,7 +39,7 @@ module Turbo::FramesHelper
|
|
36
39
|
# <%= turbo_frame_tag(Article.find(1), Comment.new) %>
|
37
40
|
# # => <turbo-frame id="article_1_new_comment"></turbo-frame>
|
38
41
|
def turbo_frame_tag(*ids, src: nil, target: nil, **attributes, &block)
|
39
|
-
id = ids.first.respond_to?(:to_key) ? ActionView::RecordIdentifier.dom_id(*ids) : ids.
|
42
|
+
id = ids.first.respond_to?(:to_key) ? ActionView::RecordIdentifier.dom_id(*ids) : ids.join('_')
|
40
43
|
src = url_for(src) if src.present?
|
41
44
|
|
42
45
|
tag.turbo_frame(**attributes.merge(id: id, src: src, target: target).compact, &block)
|
@@ -24,7 +24,7 @@ module Turbo::Streams::ActionHelper
|
|
24
24
|
# # => <turbo-stream action="remove" target="special_message_1"></turbo-stream>
|
25
25
|
#
|
26
26
|
def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil, **attributes)
|
27
|
-
template = action.to_sym
|
27
|
+
template = action.to_sym.in?(%i[ remove refresh ]) ? "" : tag.template(template.to_s.html_safe)
|
28
28
|
|
29
29
|
if target = convert_to_turbo_stream_dom_id(target)
|
30
30
|
tag.turbo_stream(template, **attributes, action: action, target: target)
|
@@ -35,6 +35,10 @@ module Turbo::Streams::ActionHelper
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
def turbo_stream_refresh_tag(request_id: Turbo.current_request_id, **attributes)
|
39
|
+
turbo_stream_action_tag(:refresh, **{ "request-id": request_id }.compact, **attributes)
|
40
|
+
end
|
41
|
+
|
38
42
|
private
|
39
43
|
def convert_to_turbo_stream_dom_id(target, include_selector: false)
|
40
44
|
if Array(target).any? { |value| value.respond_to?(:to_key) }
|
@@ -2,7 +2,7 @@
|
|
2
2
|
class Turbo::Streams::ActionBroadcastJob < ActiveJob::Base
|
3
3
|
discard_on ActiveJob::DeserializationError
|
4
4
|
|
5
|
-
def perform(stream, action:, target:, **rendering)
|
6
|
-
Turbo::StreamsChannel.broadcast_action_to stream, action: action, target: target, **rendering
|
5
|
+
def perform(stream, action:, target:, attributes: {}, **rendering)
|
6
|
+
Turbo::StreamsChannel.broadcast_action_to stream, action: action, target: target, attributes: attributes, **rendering
|
7
7
|
end
|
8
8
|
end
|
@@ -75,9 +75,32 @@
|
|
75
75
|
# In addition to the four basic actions, you can also use <tt>broadcast_render</tt>,
|
76
76
|
# <tt>broadcast_render_to</tt> <tt>broadcast_render_later</tt>, and <tt>broadcast_render_later_to</tt>
|
77
77
|
# to render a turbo stream template with multiple actions.
|
78
|
+
#
|
79
|
+
# == Suppressing broadcasts
|
80
|
+
#
|
81
|
+
# Sometimes, you need to disable broadcasts in certain scenarios. You can use <tt>.suppressing_turbo_broadcasts</tt> to create
|
82
|
+
# execution contexts where broadcasts are disabled:
|
83
|
+
#
|
84
|
+
# class Message < ApplicationRecord
|
85
|
+
# after_create_commit :update_message
|
86
|
+
#
|
87
|
+
# private
|
88
|
+
# def update_message
|
89
|
+
# broadcast_replace_to(user, :message, target: "message", renderable: MessageComponent.new)
|
90
|
+
# end
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# Message.suppressing_turbo_broadcasts do
|
94
|
+
# Message.create!(board: board) # This won't broadcast the replace action
|
95
|
+
# end
|
78
96
|
module Turbo::Broadcastable
|
79
97
|
extend ActiveSupport::Concern
|
80
98
|
|
99
|
+
included do
|
100
|
+
thread_mattr_accessor :suppressed_turbo_broadcasts, instance_accessor: false
|
101
|
+
delegate :suppressed_turbo_broadcasts?, to: "self.class"
|
102
|
+
end
|
103
|
+
|
81
104
|
module ClassMethods
|
82
105
|
# Configures the model to broadcast creates, updates, and destroys to a stream name derived at runtime by the
|
83
106
|
# <tt>stream</tt> symbol invocation. By default, the creates are appended to a dom id target name derived from
|
@@ -112,10 +135,34 @@ module Turbo::Broadcastable
|
|
112
135
|
after_destroy_commit -> { broadcast_remove }
|
113
136
|
end
|
114
137
|
|
138
|
+
# Configures the model to broadcast a "page refresh" on creates, updates, and destroys to a stream
|
139
|
+
# name derived at runtime by the <tt>stream</tt> symbol invocation.
|
140
|
+
def broadcasts_refreshes_to(stream)
|
141
|
+
after_commit -> { broadcast_refresh_later_to(stream.try(:call, self) || send(stream)) }
|
142
|
+
end
|
143
|
+
|
144
|
+
# Same as <tt>#broadcasts_refreshes_to</tt>, but the designated stream for page refreshes is automatically set to
|
145
|
+
# the current model.
|
146
|
+
def broadcasts_refreshes
|
147
|
+
after_commit -> { broadcast_refresh_later }
|
148
|
+
end
|
149
|
+
|
115
150
|
# All default targets will use the return of this method. Overwrite if you want something else than <tt>model_name.plural</tt>.
|
116
151
|
def broadcast_target_default
|
117
152
|
model_name.plural
|
118
153
|
end
|
154
|
+
|
155
|
+
# Executes +block+ preventing both synchronous and asynchronous broadcasts from this model.
|
156
|
+
def suppressing_turbo_broadcasts(&block)
|
157
|
+
original, self.suppressed_turbo_broadcasts = self.suppressed_turbo_broadcasts, true
|
158
|
+
yield
|
159
|
+
ensure
|
160
|
+
self.suppressed_turbo_broadcasts = original
|
161
|
+
end
|
162
|
+
|
163
|
+
def suppressed_turbo_broadcasts?
|
164
|
+
suppressed_turbo_broadcasts
|
165
|
+
end
|
119
166
|
end
|
120
167
|
|
121
168
|
# Remove this broadcastable model from the dom for subscribers of the stream name identified by the passed streamables.
|
@@ -124,7 +171,7 @@ module Turbo::Broadcastable
|
|
124
171
|
# # Sends <turbo-stream action="remove" target="clearance_5"></turbo-stream> to the stream named "identity:2:clearances"
|
125
172
|
# clearance.broadcast_remove_to examiner.identity, :clearances
|
126
173
|
def broadcast_remove_to(*streamables, target: self)
|
127
|
-
Turbo::StreamsChannel.broadcast_remove_to(*streamables, target: target)
|
174
|
+
Turbo::StreamsChannel.broadcast_remove_to(*streamables, target: target) unless suppressed_turbo_broadcasts?
|
128
175
|
end
|
129
176
|
|
130
177
|
# Same as <tt>#broadcast_remove_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -143,7 +190,7 @@ module Turbo::Broadcastable
|
|
143
190
|
# # to the stream named "identity:2:clearances"
|
144
191
|
# clearance.broadcast_replace_to examiner.identity, :clearances, partial: "clearances/other_partial", locals: { a: 1 }
|
145
192
|
def broadcast_replace_to(*streamables, **rendering)
|
146
|
-
Turbo::StreamsChannel.broadcast_replace_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering))
|
193
|
+
Turbo::StreamsChannel.broadcast_replace_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
147
194
|
end
|
148
195
|
|
149
196
|
# Same as <tt>#broadcast_replace_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -162,7 +209,7 @@ module Turbo::Broadcastable
|
|
162
209
|
# # to the stream named "identity:2:clearances"
|
163
210
|
# clearance.broadcast_update_to examiner.identity, :clearances, partial: "clearances/other_partial", locals: { a: 1 }
|
164
211
|
def broadcast_update_to(*streamables, **rendering)
|
165
|
-
Turbo::StreamsChannel.broadcast_update_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering))
|
212
|
+
Turbo::StreamsChannel.broadcast_update_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
166
213
|
end
|
167
214
|
|
168
215
|
# Same as <tt>#broadcast_update_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -215,7 +262,7 @@ module Turbo::Broadcastable
|
|
215
262
|
# clearance.broadcast_append_to examiner.identity, :clearances, target: "clearances",
|
216
263
|
# partial: "clearances/other_partial", locals: { a: 1 }
|
217
264
|
def broadcast_append_to(*streamables, target: broadcast_target_default, **rendering)
|
218
|
-
Turbo::StreamsChannel.broadcast_append_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering))
|
265
|
+
Turbo::StreamsChannel.broadcast_append_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
219
266
|
end
|
220
267
|
|
221
268
|
# Same as <tt>#broadcast_append_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -236,7 +283,7 @@ module Turbo::Broadcastable
|
|
236
283
|
# clearance.broadcast_prepend_to examiner.identity, :clearances, target: "clearances",
|
237
284
|
# partial: "clearances/other_partial", locals: { a: 1 }
|
238
285
|
def broadcast_prepend_to(*streamables, target: broadcast_target_default, **rendering)
|
239
|
-
Turbo::StreamsChannel.broadcast_prepend_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering))
|
286
|
+
Turbo::StreamsChannel.broadcast_prepend_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
240
287
|
end
|
241
288
|
|
242
289
|
# Same as <tt>#broadcast_prepend_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -244,24 +291,32 @@ module Turbo::Broadcastable
|
|
244
291
|
broadcast_prepend_to self, target: target, **rendering
|
245
292
|
end
|
246
293
|
|
294
|
+
def broadcast_refresh_to(*streamables)
|
295
|
+
Turbo::StreamsChannel.broadcast_refresh_to *streamables unless suppressed_turbo_broadcasts?
|
296
|
+
end
|
297
|
+
|
298
|
+
def broadcast_refresh
|
299
|
+
broadcast_refresh_to self
|
300
|
+
end
|
301
|
+
|
247
302
|
# Broadcast a named <tt>action</tt>, allowing for dynamic dispatch, instead of using the concrete action methods. Examples:
|
248
303
|
#
|
249
304
|
# # Sends <turbo-stream action="prepend" target="clearances"><template><div id="clearance_5">My Clearance</div></template></turbo-stream>
|
250
305
|
# # to the stream named "identity:2:clearances"
|
251
306
|
# clearance.broadcast_action_to examiner.identity, :clearances, action: :prepend, target: "clearances"
|
252
|
-
def broadcast_action_to(*streamables, action:, target: broadcast_target_default, **rendering)
|
253
|
-
Turbo::StreamsChannel.broadcast_action_to(*streamables, action: action, target: target, **broadcast_rendering_with_defaults(rendering))
|
307
|
+
def broadcast_action_to(*streamables, action:, target: broadcast_target_default, attributes: {}, **rendering)
|
308
|
+
Turbo::StreamsChannel.broadcast_action_to(*streamables, action: action, target: target, attributes: attributes, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
254
309
|
end
|
255
310
|
|
256
311
|
# Same as <tt>#broadcast_action_to</tt>, but the designated stream is automatically set to the current model.
|
257
|
-
def broadcast_action(action, target: broadcast_target_default, **rendering)
|
258
|
-
broadcast_action_to self, action: action, target: target, **rendering
|
312
|
+
def broadcast_action(action, target: broadcast_target_default, attributes: {}, **rendering)
|
313
|
+
broadcast_action_to self, action: action, target: target, attributes: attributes, **rendering
|
259
314
|
end
|
260
315
|
|
261
316
|
|
262
317
|
# Same as <tt>broadcast_replace_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
263
318
|
def broadcast_replace_later_to(*streamables, **rendering)
|
264
|
-
Turbo::StreamsChannel.broadcast_replace_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering))
|
319
|
+
Turbo::StreamsChannel.broadcast_replace_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
265
320
|
end
|
266
321
|
|
267
322
|
# Same as <tt>#broadcast_replace_later_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -271,7 +326,7 @@ module Turbo::Broadcastable
|
|
271
326
|
|
272
327
|
# Same as <tt>broadcast_update_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
273
328
|
def broadcast_update_later_to(*streamables, **rendering)
|
274
|
-
Turbo::StreamsChannel.broadcast_update_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering))
|
329
|
+
Turbo::StreamsChannel.broadcast_update_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
275
330
|
end
|
276
331
|
|
277
332
|
# Same as <tt>#broadcast_update_later_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -281,7 +336,7 @@ module Turbo::Broadcastable
|
|
281
336
|
|
282
337
|
# Same as <tt>broadcast_append_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
283
338
|
def broadcast_append_later_to(*streamables, target: broadcast_target_default, **rendering)
|
284
|
-
Turbo::StreamsChannel.broadcast_append_later_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering))
|
339
|
+
Turbo::StreamsChannel.broadcast_append_later_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
285
340
|
end
|
286
341
|
|
287
342
|
# Same as <tt>#broadcast_append_later_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -291,7 +346,7 @@ module Turbo::Broadcastable
|
|
291
346
|
|
292
347
|
# Same as <tt>broadcast_prepend_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
293
348
|
def broadcast_prepend_later_to(*streamables, target: broadcast_target_default, **rendering)
|
294
|
-
Turbo::StreamsChannel.broadcast_prepend_later_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering))
|
349
|
+
Turbo::StreamsChannel.broadcast_prepend_later_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
295
350
|
end
|
296
351
|
|
297
352
|
# Same as <tt>#broadcast_prepend_later_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -299,14 +354,22 @@ module Turbo::Broadcastable
|
|
299
354
|
broadcast_prepend_later_to self, target: target, **rendering
|
300
355
|
end
|
301
356
|
|
357
|
+
def broadcast_refresh_later_to(*streamables)
|
358
|
+
Turbo::StreamsChannel.broadcast_refresh_later_to(*streamables, request_id: Turbo.current_request_id) unless suppressed_turbo_broadcasts?
|
359
|
+
end
|
360
|
+
|
361
|
+
def broadcast_refresh_later
|
362
|
+
broadcast_refresh_later_to self
|
363
|
+
end
|
364
|
+
|
302
365
|
# Same as <tt>broadcast_action_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
303
|
-
def broadcast_action_later_to(*streamables, action:, target: broadcast_target_default, **rendering)
|
304
|
-
Turbo::StreamsChannel.broadcast_action_later_to(*streamables, action: action, target: target, **broadcast_rendering_with_defaults(rendering))
|
366
|
+
def broadcast_action_later_to(*streamables, action:, target: broadcast_target_default, attributes: {}, **rendering)
|
367
|
+
Turbo::StreamsChannel.broadcast_action_later_to(*streamables, action: action, target: target, attributes: attributes, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
305
368
|
end
|
306
369
|
|
307
370
|
# Same as <tt>#broadcast_action_later_to</tt>, but the designated stream is automatically set to the current model.
|
308
|
-
def broadcast_action_later(action:, target: broadcast_target_default, **rendering)
|
309
|
-
broadcast_action_later_to self, action: action, target: target, **rendering
|
371
|
+
def broadcast_action_later(action:, target: broadcast_target_default, attributes: {}, **rendering)
|
372
|
+
broadcast_action_later_to self, action: action, target: target, attributes: attributes, **rendering
|
310
373
|
end
|
311
374
|
|
312
375
|
# Render a turbo stream template with this broadcastable model passed as the local variable. Example:
|
@@ -337,7 +400,7 @@ module Turbo::Broadcastable
|
|
337
400
|
# desireable for model callbacks, certainly not if those callbacks are inside of a transaction. Most of the time you should
|
338
401
|
# be using `broadcast_render_later_to`, unless you specifically know why synchronous rendering is needed.
|
339
402
|
def broadcast_render_to(*streamables, **rendering)
|
340
|
-
Turbo::StreamsChannel.broadcast_render_to(*streamables, **broadcast_rendering_with_defaults(rendering))
|
403
|
+
Turbo::StreamsChannel.broadcast_render_to(*streamables, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
341
404
|
end
|
342
405
|
|
343
406
|
# Same as <tt>broadcast_action_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
@@ -348,7 +411,7 @@ module Turbo::Broadcastable
|
|
348
411
|
# Same as <tt>broadcast_render_later</tt> but run with the added option of naming the stream using the passed
|
349
412
|
# <tt>streamables</tt>.
|
350
413
|
def broadcast_render_later_to(*streamables, **rendering)
|
351
|
-
Turbo::StreamsChannel.broadcast_render_later_to(*streamables, **broadcast_rendering_with_defaults(rendering))
|
414
|
+
Turbo::StreamsChannel.broadcast_render_later_to(*streamables, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
352
415
|
end
|
353
416
|
|
354
417
|
|
@@ -361,12 +424,14 @@ module Turbo::Broadcastable
|
|
361
424
|
options.tap do |o|
|
362
425
|
# Add the current instance into the locals with the element name (which is the un-namespaced name)
|
363
426
|
# as the key. This parallels how the ActionView::ObjectRenderer would create a local variable.
|
364
|
-
o[:locals] = (o[:locals] || {}).reverse_merge!(model_name.element.to_sym => self)
|
427
|
+
o[:locals] = (o[:locals] || {}).reverse_merge!(model_name.element.to_sym => self, request_id: Turbo.current_request_id).compact
|
365
428
|
|
366
429
|
if o[:html] || o[:partial]
|
367
430
|
return o
|
368
431
|
elsif o[:template] || o[:renderable]
|
369
432
|
o[:layout] = false
|
433
|
+
elsif o[:render] == false
|
434
|
+
return o
|
370
435
|
else
|
371
436
|
# if none of these options are passed in, it will set a partial from #to_partial_path
|
372
437
|
o[:partial] ||= to_partial_path
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Turbo::Debouncer
|
2
|
+
attr_reader :delay, :scheduled_task
|
3
|
+
|
4
|
+
DEFAULT_DELAY = 0.5
|
5
|
+
|
6
|
+
def initialize(delay: DEFAULT_DELAY)
|
7
|
+
@delay = delay
|
8
|
+
@scheduled_task = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def debounce(&block)
|
12
|
+
scheduled_task&.cancel unless scheduled_task&.complete?
|
13
|
+
@scheduled_task = Concurrent::ScheduledTask.execute(delay, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def wait
|
17
|
+
scheduled_task&.wait(wait_timeout)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def wait_timeout
|
22
|
+
delay + 1
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# A decorated debouncer that will store instances in the current thread clearing them
|
2
|
+
# after the debounced logic triggers.
|
3
|
+
class Turbo::ThreadDebouncer
|
4
|
+
delegate :wait, to: :debouncer
|
5
|
+
|
6
|
+
def self.for(key, delay: Turbo::Debouncer::DEFAULT_DELAY)
|
7
|
+
Thread.current[key] ||= new(key, Thread.current, delay: delay)
|
8
|
+
end
|
9
|
+
|
10
|
+
private_class_method :new
|
11
|
+
|
12
|
+
def initialize(key, thread, delay: )
|
13
|
+
@key = key
|
14
|
+
@debouncer = Turbo::Debouncer.new(delay: delay)
|
15
|
+
@thread = thread
|
16
|
+
end
|
17
|
+
|
18
|
+
def debounce
|
19
|
+
debouncer.debounce do
|
20
|
+
yield.tap do
|
21
|
+
thread[key] = nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
attr_reader :key, :debouncer, :thread
|
28
|
+
end
|
data/config/routes.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# FIXME: Offer flag to opt out of these native routes
|
2
1
|
Rails.application.routes.draw do
|
3
2
|
get "recede_historical_location" => "turbo/native/navigation#recede", as: :turbo_recede_historical_location
|
4
3
|
get "resume_historical_location" => "turbo/native/navigation#resume", as: :turbo_resume_historical_location
|
data/lib/turbo/engine.rb
CHANGED
@@ -46,6 +46,12 @@ module Turbo
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
initializer "turbo.request_id_tracking" do
|
50
|
+
ActiveSupport.on_load(:action_controller) do
|
51
|
+
include Turbo::RequestIdTracking
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
49
55
|
initializer "turbo.broadcastable" do
|
50
56
|
ActiveSupport.on_load(:active_record) do
|
51
57
|
include Turbo::Broadcastable
|
data/lib/turbo/version.rb
CHANGED
data/lib/turbo-rails.rb
CHANGED
@@ -5,6 +5,8 @@ module Turbo
|
|
5
5
|
|
6
6
|
mattr_accessor :draw_routes, default: true
|
7
7
|
|
8
|
+
thread_mattr_accessor :current_request_id
|
9
|
+
|
8
10
|
class << self
|
9
11
|
attr_writer :signed_stream_verifier_key
|
10
12
|
|
@@ -15,5 +17,12 @@ module Turbo
|
|
15
17
|
def signed_stream_verifier_key
|
16
18
|
@signed_stream_verifier_key or raise ArgumentError, "Turbo requires a signed_stream_verifier_key"
|
17
19
|
end
|
20
|
+
|
21
|
+
def with_request_id(request_id)
|
22
|
+
old_request_id, self.current_request_id = self.current_request_id, request_id
|
23
|
+
yield
|
24
|
+
ensure
|
25
|
+
self.current_request_id = old_request_id
|
26
|
+
end
|
18
27
|
end
|
19
28
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbo-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0.pre.beta.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Stephenson
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-
|
13
|
+
date: 2023-11-23 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activejob
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- app/channels/turbo/streams/broadcasts.rb
|
70
70
|
- app/channels/turbo/streams/stream_name.rb
|
71
71
|
- app/channels/turbo/streams_channel.rb
|
72
|
+
- app/controllers/concerns/turbo/request_id_tracking.rb
|
72
73
|
- app/controllers/turbo/frames/frame_request.rb
|
73
74
|
- app/controllers/turbo/native/navigation.rb
|
74
75
|
- app/controllers/turbo/native/navigation_controller.rb
|
@@ -85,8 +86,11 @@ files:
|
|
85
86
|
- app/javascript/turbo/snakeize.js
|
86
87
|
- app/jobs/turbo/streams/action_broadcast_job.rb
|
87
88
|
- app/jobs/turbo/streams/broadcast_job.rb
|
89
|
+
- app/jobs/turbo/streams/broadcast_stream_job.rb
|
88
90
|
- app/models/concerns/turbo/broadcastable.rb
|
91
|
+
- app/models/turbo/debouncer.rb
|
89
92
|
- app/models/turbo/streams/tag_builder.rb
|
93
|
+
- app/models/turbo/thread_debouncer.rb
|
90
94
|
- app/views/layouts/turbo_rails/frame.html.erb
|
91
95
|
- config/routes.rb
|
92
96
|
- lib/install/turbo_needs_redis.rb
|
@@ -115,9 +119,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
115
119
|
version: 2.6.0
|
116
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
121
|
requirements:
|
118
|
-
- - "
|
122
|
+
- - ">"
|
119
123
|
- !ruby/object:Gem::Version
|
120
|
-
version:
|
124
|
+
version: 1.3.1
|
121
125
|
requirements: []
|
122
126
|
rubygems_version: 3.4.15
|
123
127
|
signing_key:
|