turbo-rails 2.0.5 → 2.0.7
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/README.md +53 -6
- data/app/assets/javascripts/turbo.js +150 -121
- data/app/assets/javascripts/turbo.min.js +6 -6
- data/app/assets/javascripts/turbo.min.js.map +1 -1
- data/app/channels/turbo/streams/broadcasts.rb +15 -3
- data/app/channels/turbo/streams_channel.rb +15 -15
- data/app/controllers/turbo/frames/frame_request.rb +2 -2
- data/app/helpers/turbo/drive_helper.rb +2 -2
- data/app/helpers/turbo/streams/action_helper.rb +3 -2
- data/app/helpers/turbo/streams_helper.rb +6 -0
- data/app/models/concerns/turbo/broadcastable.rb +19 -11
- data/config/routes.rb +3 -3
- data/lib/tasks/turbo_tasks.rake +0 -22
- data/lib/turbo/engine.rb +21 -6
- data/lib/turbo/version.rb +1 -1
- metadata +5 -5
- data/lib/install/turbo_needs_redis.rb +0 -20
@@ -75,8 +75,15 @@ module Turbo::Streams::Broadcasts
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def broadcast_action_later_to(*streamables, action:, target: nil, targets: nil, attributes: {}, **rendering)
|
78
|
-
|
79
|
-
|
78
|
+
streamables.flatten!
|
79
|
+
streamables.compact_blank!
|
80
|
+
|
81
|
+
if streamables.present?
|
82
|
+
target = convert_to_turbo_stream_dom_id(target)
|
83
|
+
targets = convert_to_turbo_stream_dom_id(targets, include_selector: true)
|
84
|
+
Turbo::Streams::ActionBroadcastJob.perform_later \
|
85
|
+
stream_name_from(streamables), action: action, target: target, targets: targets, attributes: attributes, **rendering
|
86
|
+
end
|
80
87
|
end
|
81
88
|
|
82
89
|
def broadcast_render_to(*streamables, **rendering)
|
@@ -88,7 +95,12 @@ module Turbo::Streams::Broadcasts
|
|
88
95
|
end
|
89
96
|
|
90
97
|
def broadcast_stream_to(*streamables, content:)
|
91
|
-
|
98
|
+
streamables.flatten!
|
99
|
+
streamables.compact_blank!
|
100
|
+
|
101
|
+
if streamables.present?
|
102
|
+
ActionCable.server.broadcast stream_name_from(streamables), content
|
103
|
+
end
|
92
104
|
end
|
93
105
|
|
94
106
|
def refresh_debouncer_for(*streamables, request_id: nil) # :nodoc:
|
@@ -9,27 +9,27 @@
|
|
9
9
|
# helper modules like <tt>Turbo::Streams::StreamName</tt>:
|
10
10
|
#
|
11
11
|
# class CustomChannel < ActionCable::Channel::Base
|
12
|
-
#
|
13
|
-
#
|
12
|
+
# extend Turbo::Streams::Broadcasts, Turbo::Streams::StreamName
|
13
|
+
# include Turbo::Streams::StreamName::ClassMethods
|
14
14
|
#
|
15
|
-
#
|
16
|
-
#
|
15
|
+
# def subscribed
|
16
|
+
# if (stream_name = verified_stream_name_from_params).present? &&
|
17
17
|
# subscription_allowed?
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
18
|
+
# stream_from stream_name
|
19
|
+
# else
|
20
|
+
# reject
|
21
|
+
# end
|
22
|
+
# end
|
23
23
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
24
|
+
# def subscription_allowed?
|
25
|
+
# # ...
|
26
|
+
# end
|
27
27
|
# end
|
28
28
|
#
|
29
|
-
#
|
30
|
-
#
|
29
|
+
# This channel can be connected to a web page using <tt>:channel</tt> option in
|
30
|
+
# <tt>turbo_stream_from</tt> helper:
|
31
31
|
#
|
32
|
-
#
|
32
|
+
# <%= turbo_stream_from 'room', channel: CustomChannel %>
|
33
33
|
#
|
34
34
|
class Turbo::StreamsChannel < ActionCable::Channel::Base
|
35
35
|
extend Turbo::Streams::Broadcasts, Turbo::Streams::StreamName
|
@@ -24,7 +24,7 @@ module Turbo::Frames::FrameRequest
|
|
24
24
|
layout -> { "turbo_rails/frame" if turbo_frame_request? }
|
25
25
|
etag { :frame if turbo_frame_request? }
|
26
26
|
|
27
|
-
helper_method :turbo_frame_request_id
|
27
|
+
helper_method :turbo_frame_request?, :turbo_frame_request_id
|
28
28
|
end
|
29
29
|
|
30
30
|
private
|
@@ -33,6 +33,6 @@ module Turbo::Frames::FrameRequest
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def turbo_frame_request_id
|
36
|
-
request
|
36
|
+
request.headers["Turbo-Frame"]
|
37
37
|
end
|
38
38
|
end
|
@@ -51,7 +51,7 @@ module Turbo::DriveHelper
|
|
51
51
|
# Configure how to handle page refreshes. A page refresh happens when
|
52
52
|
# Turbo loads the current page again with a *replace* visit:
|
53
53
|
#
|
54
|
-
#
|
54
|
+
# ==== Parameters:
|
55
55
|
#
|
56
56
|
# * <tt>method</tt> - Method to update the +<body>+ of the page
|
57
57
|
# during a page refresh. It can be one of:
|
@@ -64,7 +64,7 @@ module Turbo::DriveHelper
|
|
64
64
|
# * +reset:+: Resets scroll to the top, left corner. This is the default.
|
65
65
|
# * +preserve:+: Keeps the scroll.
|
66
66
|
#
|
67
|
-
#
|
67
|
+
# ==== Example Usage:
|
68
68
|
#
|
69
69
|
# turbo_refreshes_with(method: :morph, scroll: :preserve)
|
70
70
|
def turbo_refreshes_with(method: :replace, scroll: :reset)
|
@@ -44,8 +44,9 @@ module Turbo::Streams::ActionHelper
|
|
44
44
|
|
45
45
|
private
|
46
46
|
def convert_to_turbo_stream_dom_id(target, include_selector: false)
|
47
|
-
|
48
|
-
|
47
|
+
target_array = Array.wrap(target)
|
48
|
+
if target_array.any? { |value| value.respond_to?(:to_key) }
|
49
|
+
"#{"#" if include_selector}#{ActionView::RecordIdentifier.dom_id(*target_array)}"
|
49
50
|
else
|
50
51
|
target
|
51
52
|
end
|
@@ -48,7 +48,13 @@ module Turbo::StreamsHelper
|
|
48
48
|
# It is also possible to pass additional parameters to the channel by passing them through `data` attributes:
|
49
49
|
#
|
50
50
|
# <%= turbo_stream_from "room", channel: RoomChannel, data: {room_name: "room #1"} %>
|
51
|
+
#
|
52
|
+
# Raises an +ArgumentError+ if all streamables are blank
|
53
|
+
#
|
54
|
+
# <%= turbo_stream_from("") %> # => ArgumentError: streamables can't be blank
|
55
|
+
# <%= turbo_stream_from("", nil) %> # => ArgumentError: streamables can't be blank
|
51
56
|
def turbo_stream_from(*streamables, **attributes)
|
57
|
+
raise ArgumentError, "streamables can't be blank" unless streamables.any?(&:present?)
|
52
58
|
attributes[:channel] = attributes[:channel]&.to_s || "Turbo::StreamsChannel"
|
53
59
|
attributes[:"signed-stream-name"] = Turbo::StreamsChannel.signed_stream_name(streamables)
|
54
60
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Turbo streams can be
|
1
|
+
# Turbo streams can be broadcasted directly from models that include this module (this is automatically done for Active Records).
|
2
2
|
# This makes it convenient to execute both synchronous and asynchronous updates, and render directly from callbacks in models
|
3
3
|
# or from controllers or jobs that act on those models. Here's an example:
|
4
4
|
#
|
@@ -79,19 +79,19 @@
|
|
79
79
|
#
|
80
80
|
# == Page refreshes
|
81
81
|
#
|
82
|
-
# You can broadcast "page refresh" stream actions. This will make subscribed clients reload the
|
82
|
+
# You can broadcast "page refresh" stream actions. This will make subscribed clients reload the
|
83
83
|
# page. For pages that configure morphing and scroll preservation, this will translate into smooth
|
84
84
|
# updates when it only updates the content that changed.
|
85
|
-
|
85
|
+
#
|
86
86
|
# This approach is an alternative to fine-grained stream actions targeting specific DOM elements. It
|
87
87
|
# offers good fidelity with a much simpler programming model. As a tradeoff, the fidelity you can reach
|
88
88
|
# is often not as high as with targeted stream actions since it renders the entire page again.
|
89
89
|
#
|
90
|
-
# The +
|
90
|
+
# The +broadcasts_refreshes+ class method configures the model to broadcast a "page refresh" on creates,
|
91
91
|
# updates, and destroys to a stream name derived at runtime by the <tt>stream</tt> symbol invocation. Examples
|
92
92
|
#
|
93
93
|
# class Board < ApplicationRecord
|
94
|
-
#
|
94
|
+
# broadcasts_refreshes
|
95
95
|
# end
|
96
96
|
#
|
97
97
|
# In this example, when a board is created, updated, or destroyed, a Turbo Stream for a
|
@@ -104,16 +104,16 @@
|
|
104
104
|
# belongs_to :board, touch: true # +Board+ will trigger a page refresh on column changes
|
105
105
|
# end
|
106
106
|
#
|
107
|
-
# You can also specify the streamable declaratively by passing a symbol to the +
|
107
|
+
# You can also specify the streamable declaratively by passing a symbol to the +broadcasts_refreshes_to+ method:
|
108
108
|
#
|
109
109
|
# class Column < ApplicationRecord
|
110
110
|
# belongs_to :board
|
111
|
-
#
|
111
|
+
# broadcasts_refreshes_to :board
|
112
112
|
# end
|
113
113
|
#
|
114
|
-
# For more granular control, you can also broadcast a "page refresh" to a stream name derived
|
114
|
+
# For more granular control, you can also broadcast a "page refresh" to a stream name derived
|
115
115
|
# from the passed <tt>streamables</tt> by using the instance-level methods <tt>broadcast_refresh_to</tt> or
|
116
|
-
# <tt>broadcast_refresh_later_to</tt>. These methods are particularly useful when you want to trigger
|
116
|
+
# <tt>broadcast_refresh_later_to</tt>. These methods are particularly useful when you want to trigger
|
117
117
|
# a page refresh for more specific scenarios. Example:
|
118
118
|
#
|
119
119
|
# class Clearance < ApplicationRecord
|
@@ -128,11 +128,11 @@
|
|
128
128
|
# end
|
129
129
|
# end
|
130
130
|
#
|
131
|
-
# In this example, a "page refresh" is broadcast to the stream named "identity:<identity-id>:clearances"
|
131
|
+
# In this example, a "page refresh" is broadcast to the stream named "identity:<identity-id>:clearances"
|
132
132
|
# after a new clearance is created. All clients subscribed to this stream will refresh the page to reflect
|
133
133
|
# the changes.
|
134
134
|
#
|
135
|
-
# When broadcasting page refreshes, Turbo will automatically debounce multiple calls in a row to only broadcast the last one.
|
135
|
+
# When broadcasting page refreshes, Turbo will automatically debounce multiple calls in a row to only broadcast the last one.
|
136
136
|
# This is meant for scenarios where you process records in mass. Because of the nature of such signals, it makes no sense to
|
137
137
|
# broadcast them repeatedly and individually.
|
138
138
|
# == Suppressing broadcasts
|
@@ -260,6 +260,10 @@ module Turbo::Broadcastable
|
|
260
260
|
# # Sends <turbo-stream action="replace" target="clearance_5"><template><div id="clearance_5">Other partial</div></template></turbo-stream>
|
261
261
|
# # to the stream named "identity:2:clearances"
|
262
262
|
# clearance.broadcast_replace_to examiner.identity, :clearances, partial: "clearances/other_partial", locals: { a: 1 }
|
263
|
+
#
|
264
|
+
# # Sends <turbo-stream action="replace" method="morph" target="clearance_5"><template><div id="clearance_5">Other partial</div></template></turbo-stream>
|
265
|
+
# # to the stream named "identity:2:clearances"
|
266
|
+
# clearance.broadcast_replace_to examiner.identity, :clearance, attributes: { method: :morph }, partial: "clearances/other_partial", locals: { a: 1 }
|
263
267
|
def broadcast_replace_to(*streamables, **rendering)
|
264
268
|
Turbo::StreamsChannel.broadcast_replace_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
265
269
|
end
|
@@ -279,6 +283,10 @@ module Turbo::Broadcastable
|
|
279
283
|
# # Sends <turbo-stream action="update" target="clearance_5"><template><div id="clearance_5">Other partial</div></template></turbo-stream>
|
280
284
|
# # to the stream named "identity:2:clearances"
|
281
285
|
# clearance.broadcast_update_to examiner.identity, :clearances, partial: "clearances/other_partial", locals: { a: 1 }
|
286
|
+
#
|
287
|
+
# # sends <turbo-stream action="update" method="morph" target="clearance_5"><template><div id="clearance_5">Other partial</div></template></turbo-stream>
|
288
|
+
# # to the stream named "identity:2:clearances"
|
289
|
+
# # clearance.broadcast_update_to examiner.identity, :clearances, attributes: { method: :morph }, partial: "clearances/other_partial", locals: { a: 1 }
|
282
290
|
def broadcast_update_to(*streamables, **rendering)
|
283
291
|
Turbo::StreamsChannel.broadcast_update_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
|
284
292
|
end
|
data/config/routes.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
Rails.application.routes.draw do
|
2
|
-
get "recede_historical_location"
|
3
|
-
get "resume_historical_location"
|
4
|
-
get "refresh_historical_location"
|
2
|
+
get "recede_historical_location", to: "turbo/native/navigation#recede", as: :turbo_recede_historical_location
|
3
|
+
get "resume_historical_location", to: "turbo/native/navigation#resume", as: :turbo_resume_historical_location
|
4
|
+
get "refresh_historical_location", to: "turbo/native/navigation#refresh", as: :turbo_refresh_historical_location
|
5
5
|
end if Turbo.draw_routes
|
data/lib/tasks/turbo_tasks.rake
CHANGED
@@ -5,20 +5,6 @@ module Turbo
|
|
5
5
|
system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{File.expand_path("../install/#{path}.rb", __dir__)}"
|
6
6
|
end
|
7
7
|
|
8
|
-
def redis_installed?
|
9
|
-
Gem.win_platform? ?
|
10
|
-
system('where redis-server > NUL 2>&1') :
|
11
|
-
system('which redis-server > /dev/null')
|
12
|
-
end
|
13
|
-
|
14
|
-
def switch_on_redis_if_available
|
15
|
-
if redis_installed?
|
16
|
-
Rake::Task["turbo:install:redis"].invoke
|
17
|
-
else
|
18
|
-
puts "Run turbo:install:redis to switch on Redis and use it in development for turbo streams"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
8
|
def using_bun?
|
23
9
|
Rails.root.join("bun.config.js").exist?
|
24
10
|
end
|
@@ -43,24 +29,16 @@ namespace :turbo do
|
|
43
29
|
desc "Install Turbo into the app with asset pipeline"
|
44
30
|
task :importmap do
|
45
31
|
Turbo::Tasks.run_turbo_install_template "turbo_with_importmap"
|
46
|
-
Turbo::Tasks.switch_on_redis_if_available
|
47
32
|
end
|
48
33
|
|
49
34
|
desc "Install Turbo into the app with webpacker"
|
50
35
|
task :node do
|
51
36
|
Turbo::Tasks.run_turbo_install_template "turbo_with_node"
|
52
|
-
Turbo::Tasks.switch_on_redis_if_available
|
53
37
|
end
|
54
38
|
|
55
39
|
desc "Install Turbo into the app with bun"
|
56
40
|
task :bun do
|
57
41
|
Turbo::Tasks.run_turbo_install_template "turbo_with_bun"
|
58
|
-
Turbo::Tasks.switch_on_redis_if_available
|
59
|
-
end
|
60
|
-
|
61
|
-
desc "Switch on Redis and use it in development"
|
62
|
-
task :redis do
|
63
|
-
Turbo::Tasks.run_turbo_install_template "turbo_needs_redis"
|
64
42
|
end
|
65
43
|
end
|
66
44
|
end
|
data/lib/turbo/engine.rb
CHANGED
@@ -15,8 +15,25 @@ module Turbo
|
|
15
15
|
#{root}/app/jobs
|
16
16
|
)
|
17
17
|
|
18
|
+
# If the parent application does not use Action Cable, app/channels cannot
|
19
|
+
# be eager loaded, because it references the ActionCable constant.
|
20
|
+
#
|
21
|
+
# When turbo-rails depends on Rails 7 or above, the entire block can be
|
22
|
+
# reduced to
|
23
|
+
#
|
24
|
+
# unless defined?(ActionCable)
|
25
|
+
# Rails.autoloaders.once.do_not_eager_load("#{root}/app/channels")
|
26
|
+
# end
|
27
|
+
#
|
18
28
|
initializer "turbo.no_action_cable", before: :set_eager_load_paths do
|
19
|
-
|
29
|
+
unless defined?(ActionCable)
|
30
|
+
if Rails.autoloaders.zeitwerk_enabled?
|
31
|
+
Rails.autoloaders.once.do_not_eager_load("#{root}/app/channels")
|
32
|
+
else
|
33
|
+
# This else branch only runs in Rails 6.x + classic mode.
|
34
|
+
config.eager_load_paths.delete("#{root}/app/channels")
|
35
|
+
end
|
36
|
+
end
|
20
37
|
end
|
21
38
|
|
22
39
|
# If you don't want to precompile Turbo's assets (eg. because you're using webpack),
|
@@ -63,11 +80,9 @@ module Turbo
|
|
63
80
|
end
|
64
81
|
|
65
82
|
initializer "turbo.renderer" do
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
turbo_streams_html
|
70
|
-
end
|
83
|
+
ActionController::Renderers.add :turbo_stream do |turbo_streams_html, options|
|
84
|
+
self.content_type = Mime[:turbo_stream] if media_type.nil?
|
85
|
+
turbo_streams_html
|
71
86
|
end
|
72
87
|
end
|
73
88
|
|
data/lib/turbo/version.rb
CHANGED
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: 2.0.
|
4
|
+
version: 2.0.7
|
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: 2024-
|
13
|
+
date: 2024-09-12 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activejob
|
@@ -93,7 +93,6 @@ files:
|
|
93
93
|
- app/models/turbo/thread_debouncer.rb
|
94
94
|
- app/views/layouts/turbo_rails/frame.html.erb
|
95
95
|
- config/routes.rb
|
96
|
-
- lib/install/turbo_needs_redis.rb
|
97
96
|
- lib/install/turbo_with_bun.rb
|
98
97
|
- lib/install/turbo_with_importmap.rb
|
99
98
|
- lib/install/turbo_with_node.rb
|
@@ -107,7 +106,8 @@ files:
|
|
107
106
|
homepage: https://github.com/hotwired/turbo-rails
|
108
107
|
licenses:
|
109
108
|
- MIT
|
110
|
-
metadata:
|
109
|
+
metadata:
|
110
|
+
changelog_uri: https://github.com/hotwired/turbo-rails/releases
|
111
111
|
post_install_message:
|
112
112
|
rdoc_options: []
|
113
113
|
require_paths:
|
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
requirements: []
|
126
|
-
rubygems_version: 3.
|
126
|
+
rubygems_version: 3.5.16
|
127
127
|
signing_key:
|
128
128
|
specification_version: 4
|
129
129
|
summary: The speed of a single-page web application without having to write any JavaScript.
|
@@ -1,20 +0,0 @@
|
|
1
|
-
if (cable_config_path = Rails.root.join("config/cable.yml")).exist?
|
2
|
-
say "Enable redis in bundle"
|
3
|
-
|
4
|
-
gemfile_content = File.read(Rails.root.join("Gemfile"))
|
5
|
-
pattern = /gem ['"]redis['"]/
|
6
|
-
|
7
|
-
if gemfile_content.match?(pattern)
|
8
|
-
uncomment_lines "Gemfile", pattern
|
9
|
-
else
|
10
|
-
append_file "Gemfile", "\n# Use Redis for Action Cable"
|
11
|
-
gem 'redis', '~> 4.0'
|
12
|
-
end
|
13
|
-
|
14
|
-
run_bundle
|
15
|
-
|
16
|
-
say "Switch development cable to use redis"
|
17
|
-
gsub_file cable_config_path.to_s, /development:\n\s+adapter: async/, "development:\n adapter: redis\n url: redis://localhost:6379/1"
|
18
|
-
else
|
19
|
-
say 'ActionCable config file (config/cable.yml) is missing. Uncomment "gem \'redis\'" in your Gemfile and create config/cable.yml to use the Turbo Streams broadcast feature.'
|
20
|
-
end
|