turbo-rails 1.5.0 → 2.0.0.pre.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|