turbo-rails 0.7.11 → 1.3.0
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 +46 -16
- data/app/assets/javascripts/turbo.js +1330 -574
- data/app/assets/javascripts/turbo.min.js +25 -0
- data/app/assets/javascripts/turbo.min.js.map +1 -0
- data/app/channels/turbo/streams/broadcasts.rb +17 -17
- data/app/channels/turbo/streams/stream_name.rb +7 -0
- data/app/channels/turbo/streams_channel.rb +30 -2
- data/app/controllers/turbo/frames/frame_request.rb +5 -1
- data/app/helpers/turbo/drive_helper.rb +16 -3
- data/app/helpers/turbo/frames_helper.rb +14 -2
- data/app/helpers/turbo/streams/action_helper.rb +14 -8
- data/app/helpers/turbo/streams_helper.rb +12 -1
- data/app/javascript/turbo/cable_stream_source_element.js +2 -1
- data/app/javascript/turbo/fetch_requests.js +19 -0
- data/app/javascript/turbo/index.js +4 -0
- data/app/javascript/turbo/snakeize.js +31 -0
- data/app/models/concerns/turbo/broadcastable.rb +67 -26
- data/app/models/turbo/streams/tag_builder.rb +4 -4
- data/lib/install/turbo_needs_redis.rb +12 -1
- data/lib/install/turbo_with_importmap.rb +1 -1
- data/lib/tasks/turbo_tasks.rake +4 -2
- data/lib/turbo/engine.rb +15 -5
- data/lib/turbo/test_assertions.rb +10 -4
- data/lib/turbo/version.rb +1 -1
- metadata +40 -8
@@ -6,61 +6,61 @@ module Turbo::Streams::Broadcasts
|
|
6
6
|
include Turbo::Streams::ActionHelper
|
7
7
|
|
8
8
|
def broadcast_remove_to(*streamables, **opts)
|
9
|
-
broadcast_action_to
|
9
|
+
broadcast_action_to(*streamables, action: :remove, **opts)
|
10
10
|
end
|
11
11
|
|
12
12
|
def broadcast_replace_to(*streamables, **opts)
|
13
|
-
broadcast_action_to
|
13
|
+
broadcast_action_to(*streamables, action: :replace, **opts)
|
14
14
|
end
|
15
15
|
|
16
16
|
def broadcast_update_to(*streamables, **opts)
|
17
|
-
broadcast_action_to
|
17
|
+
broadcast_action_to(*streamables, action: :update, **opts)
|
18
18
|
end
|
19
19
|
|
20
20
|
def broadcast_before_to(*streamables, **opts)
|
21
|
-
broadcast_action_to
|
21
|
+
broadcast_action_to(*streamables, action: :before, **opts)
|
22
22
|
end
|
23
23
|
|
24
24
|
def broadcast_after_to(*streamables, **opts)
|
25
|
-
broadcast_action_to
|
25
|
+
broadcast_action_to(*streamables, action: :after, **opts)
|
26
26
|
end
|
27
27
|
|
28
28
|
def broadcast_append_to(*streamables, **opts)
|
29
|
-
broadcast_action_to
|
29
|
+
broadcast_action_to(*streamables, action: :append, **opts)
|
30
30
|
end
|
31
31
|
|
32
32
|
def broadcast_prepend_to(*streamables, **opts)
|
33
|
-
broadcast_action_to
|
33
|
+
broadcast_action_to(*streamables, action: :prepend, **opts)
|
34
34
|
end
|
35
35
|
|
36
36
|
def broadcast_action_to(*streamables, action:, target: nil, targets: nil, **rendering)
|
37
|
-
broadcast_stream_to
|
38
|
-
rendering.delete(:content) || (rendering.any? ? render_format(:html, **rendering) : nil)
|
39
|
-
)
|
37
|
+
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)
|
39
|
+
))
|
40
40
|
end
|
41
41
|
|
42
42
|
def broadcast_replace_later_to(*streamables, **opts)
|
43
|
-
broadcast_action_later_to
|
43
|
+
broadcast_action_later_to(*streamables, action: :replace, **opts)
|
44
44
|
end
|
45
45
|
|
46
46
|
def broadcast_update_later_to(*streamables, **opts)
|
47
|
-
broadcast_action_later_to
|
47
|
+
broadcast_action_later_to(*streamables, action: :update, **opts)
|
48
48
|
end
|
49
49
|
|
50
50
|
def broadcast_before_later_to(*streamables, **opts)
|
51
|
-
broadcast_action_later_to
|
51
|
+
broadcast_action_later_to(*streamables, action: :before, **opts)
|
52
52
|
end
|
53
53
|
|
54
54
|
def broadcast_after_later_to(*streamables, **opts)
|
55
|
-
broadcast_action_later_to
|
55
|
+
broadcast_action_later_to(*streamables, action: :after, **opts)
|
56
56
|
end
|
57
57
|
|
58
58
|
def broadcast_append_later_to(*streamables, **opts)
|
59
|
-
broadcast_action_later_to
|
59
|
+
broadcast_action_later_to(*streamables, action: :append, **opts)
|
60
60
|
end
|
61
61
|
|
62
62
|
def broadcast_prepend_later_to(*streamables, **opts)
|
63
|
-
broadcast_action_later_to
|
63
|
+
broadcast_action_later_to(*streamables, action: :prepend, **opts)
|
64
64
|
end
|
65
65
|
|
66
66
|
def broadcast_action_later_to(*streamables, action:, target: nil, targets: nil, **rendering)
|
@@ -69,7 +69,7 @@ module Turbo::Streams::Broadcasts
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def broadcast_render_to(*streamables, **rendering)
|
72
|
-
broadcast_stream_to
|
72
|
+
broadcast_stream_to(*streamables, content: render_format(:turbo_stream, **rendering))
|
73
73
|
end
|
74
74
|
|
75
75
|
def broadcast_render_later_to(*streamables, **rendering)
|
@@ -13,6 +13,13 @@ module Turbo::Streams::StreamName
|
|
13
13
|
Turbo.signed_stream_verifier.generate stream_name_from(streamables)
|
14
14
|
end
|
15
15
|
|
16
|
+
module ClassMethods
|
17
|
+
# Can be used by custom turbo stream channels to obtain signed stream name from <tt>params</tt>
|
18
|
+
def verified_stream_name_from_params
|
19
|
+
self.class.verified_stream_name(params[:signed_stream_name])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
16
23
|
private
|
17
24
|
def stream_name_from(streamables)
|
18
25
|
if streamables.is_a?(Array)
|
@@ -4,12 +4,40 @@
|
|
4
4
|
# into signed stream name using <tt>Turbo::Streams::StreamName#signed_stream_name</tt>. This is automatically done
|
5
5
|
# using the view helper <tt>Turbo::StreamsHelper#turbo_stream_from(*streamables)</tt>.
|
6
6
|
# If the signed stream name cannot be verified, the subscription is rejected.
|
7
|
+
#
|
8
|
+
# In case if custom behavior is desired, one can create their own channel and re-use some of the primitives from
|
9
|
+
# helper modules like <tt>Turbo::Streams::StreamName</tt>:
|
10
|
+
#
|
11
|
+
# class CustomChannel < ActionCable::Channel::Base
|
12
|
+
# extend Turbo::Streams::Broadcasts, Turbo::Streams::StreamName
|
13
|
+
# include Turbo::Streams::StreamName::ClassMethods
|
14
|
+
#
|
15
|
+
# def subscribed
|
16
|
+
# if (stream_name = verified_stream_name_from_params).present? &&
|
17
|
+
# subscription_allowed?
|
18
|
+
# stream_from stream_name
|
19
|
+
# else
|
20
|
+
# reject
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def subscription_allowed?
|
25
|
+
# # ...
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# This channel can be connected to a web page using <tt>:channel</tt> option in
|
30
|
+
# <tt>turbo_stream_from</tt> helper:
|
31
|
+
#
|
32
|
+
# <%= turbo_stream_from 'room', channel: CustomChannel %>
|
33
|
+
#
|
7
34
|
class Turbo::StreamsChannel < ActionCable::Channel::Base
|
8
35
|
extend Turbo::Streams::Broadcasts, Turbo::Streams::StreamName
|
36
|
+
include Turbo::Streams::StreamName::ClassMethods
|
9
37
|
|
10
38
|
def subscribed
|
11
|
-
if
|
12
|
-
stream_from
|
39
|
+
if stream_name = verified_stream_name_from_params
|
40
|
+
stream_from stream_name
|
13
41
|
else
|
14
42
|
reject
|
15
43
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module Turbo::DriveHelper
|
2
|
-
#
|
3
|
-
# Note: This requires a +yield :head+ provision in the application layout.
|
2
|
+
# Note: These helpers require a +yield :head+ provision in the layout.
|
4
3
|
#
|
5
4
|
# ==== Example
|
6
5
|
#
|
@@ -10,7 +9,21 @@ module Turbo::DriveHelper
|
|
10
9
|
# # app/views/trays/index.html.erb
|
11
10
|
# <% turbo_exempts_page_from_cache %>
|
12
11
|
# <p>Page that shouldn't be cached by Turbo</p>
|
12
|
+
|
13
|
+
# Pages that are more likely than not to be a cache miss can skip turbo cache to avoid visual jitter.
|
14
|
+
# Cannot be used along with +turbo_exempts_page_from_preview+.
|
13
15
|
def turbo_exempts_page_from_cache
|
14
|
-
provide :head,
|
16
|
+
provide :head, tag.meta(name: "turbo-cache-control", content: "no-cache")
|
17
|
+
end
|
18
|
+
|
19
|
+
# Specify that a cached version of the page should not be shown as a preview during an application visit.
|
20
|
+
# Cannot be used along with +turbo_exempts_page_from_cache+.
|
21
|
+
def turbo_exempts_page_from_preview
|
22
|
+
provide :head, tag.meta(name: "turbo-cache-control", content: "no-preview")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Force the page, when loaded by Turbo, to be cause a full page reload.
|
26
|
+
def turbo_page_requires_reload
|
27
|
+
provide :head, tag.meta(name: "turbo-visit-control", content: "reload")
|
15
28
|
end
|
16
29
|
end
|
@@ -23,8 +23,20 @@ module Turbo::FramesHelper
|
|
23
23
|
# <div>My tray frame!</div>
|
24
24
|
# <% end %>
|
25
25
|
# # => <turbo-frame id="tray"><div>My tray frame!</div></turbo-frame>
|
26
|
-
|
27
|
-
|
26
|
+
#
|
27
|
+
# The `turbo_frame_tag` helper will convert the arguments it receives to their
|
28
|
+
# `dom_id` if applicable to easily generate unique ids for Turbo Frames:
|
29
|
+
#
|
30
|
+
# <%= turbo_frame_tag(Article.find(1)) %>
|
31
|
+
# # => <turbo-frame id="article_1"></turbo-frame>
|
32
|
+
#
|
33
|
+
# <%= turbo_frame_tag(Article.find(1), "comments") %>
|
34
|
+
# # => <turbo-frame id="article_1_comments"></turbo-frame>
|
35
|
+
#
|
36
|
+
# <%= turbo_frame_tag(Article.find(1), Comment.new) %>
|
37
|
+
# # => <turbo-frame id="article_1_new_comment"></turbo-frame>
|
38
|
+
def turbo_frame_tag(*ids, src: nil, target: nil, **attributes, &block)
|
39
|
+
id = ids.map { |id| id.respond_to?(:to_key) ? ActionView::RecordIdentifier.dom_id(id) : id }.join("_")
|
28
40
|
src = url_for(src) if src.present?
|
29
41
|
|
30
42
|
tag.turbo_frame(**attributes.merge(id: id, src: src, target: target).compact, &block)
|
@@ -1,4 +1,6 @@
|
|
1
1
|
module Turbo::Streams::ActionHelper
|
2
|
+
include ActionView::Helpers::TagHelper
|
3
|
+
|
2
4
|
# Creates a `turbo-stream` tag according to the passed parameters. Examples:
|
3
5
|
#
|
4
6
|
# turbo_stream_action_tag "remove", target: "message_1"
|
@@ -9,20 +11,24 @@ module Turbo::Streams::ActionHelper
|
|
9
11
|
#
|
10
12
|
# turbo_stream_action_tag "replace", targets: "message_1", template: %(<div id="message_1">Hello!</div>)
|
11
13
|
# # => <turbo-stream action="replace" targets="message_1"><template><div id="message_1">Hello!</div></template></turbo-stream>
|
12
|
-
def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil)
|
13
|
-
template = action.to_sym == :remove ? "" :
|
14
|
+
def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil, **attributes)
|
15
|
+
template = action.to_sym == :remove ? "" : tag.template(template.to_s.html_safe)
|
14
16
|
|
15
17
|
if target = convert_to_turbo_stream_dom_id(target)
|
16
|
-
|
17
|
-
elsif targets = convert_to_turbo_stream_dom_id(targets)
|
18
|
-
|
18
|
+
tag.turbo_stream(template, **attributes.merge(action: action, target: target))
|
19
|
+
elsif targets = convert_to_turbo_stream_dom_id(targets, include_selector: true)
|
20
|
+
tag.turbo_stream(template, **attributes.merge(action: action, targets: targets))
|
19
21
|
else
|
20
|
-
|
22
|
+
tag.turbo_stream(template, **attributes.merge(action: action))
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
26
|
private
|
25
|
-
def convert_to_turbo_stream_dom_id(target)
|
26
|
-
target.respond_to?(:to_key)
|
27
|
+
def convert_to_turbo_stream_dom_id(target, include_selector: false)
|
28
|
+
if target.respond_to?(:to_key)
|
29
|
+
[ ("#" if include_selector), ActionView::RecordIdentifier.dom_id(target) ].compact.join
|
30
|
+
else
|
31
|
+
target
|
32
|
+
end
|
27
33
|
end
|
28
34
|
end
|
@@ -39,9 +39,20 @@ module Turbo::StreamsHelper
|
|
39
39
|
# The example above will process all turbo streams sent to a stream name like <tt>account:5:entries</tt>
|
40
40
|
# (when Current.account.id = 5). Updates to this stream can be sent like
|
41
41
|
# <tt>entry.broadcast_append_to entry.account, :entries, target: "entries"</tt>.
|
42
|
+
#
|
43
|
+
# Custom channel class name can be passed using <tt>:channel</tt> option (either as a String
|
44
|
+
# or a class name):
|
45
|
+
#
|
46
|
+
# <%= turbo_stream_from "room", channel: RoomChannel %>
|
47
|
+
#
|
48
|
+
# It is also possible to pass additional parameters to the channel by passing them through `data` attributes:
|
49
|
+
#
|
50
|
+
# <%= turbo_stream_from "room", channel: RoomChannel, data: {room_name: "room #1"} %>
|
51
|
+
#
|
42
52
|
def turbo_stream_from(*streamables, **attributes)
|
43
|
-
attributes[:channel] = "Turbo::StreamsChannel"
|
53
|
+
attributes[:channel] = attributes[:channel]&.to_s || "Turbo::StreamsChannel"
|
44
54
|
attributes[:"signed-stream-name"] = Turbo::StreamsChannel.signed_stream_name(streamables)
|
55
|
+
|
45
56
|
tag.turbo_cable_stream_source(**attributes)
|
46
57
|
end
|
47
58
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { connectStreamSource, disconnectStreamSource } from "@hotwired/turbo"
|
2
2
|
import { subscribeTo } from "./cable"
|
3
|
+
import snakeize from "./snakeize"
|
3
4
|
|
4
5
|
class TurboCableStreamSourceElement extends HTMLElement {
|
5
6
|
async connectedCallback() {
|
@@ -20,7 +21,7 @@ class TurboCableStreamSourceElement extends HTMLElement {
|
|
20
21
|
get channel() {
|
21
22
|
const channel = this.getAttribute("channel")
|
22
23
|
const signed_stream_name = this.getAttribute("signed-stream-name")
|
23
|
-
return { channel, signed_stream_name }
|
24
|
+
return { channel, signed_stream_name, ...snakeize({ ...this.dataset }) }
|
24
25
|
}
|
25
26
|
}
|
26
27
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
export function encodeMethodIntoRequestBody(event) {
|
2
|
+
if (event.target instanceof HTMLFormElement) {
|
3
|
+
const { target: form, detail: { fetchOptions } } = event
|
4
|
+
|
5
|
+
form.addEventListener("turbo:submit-start", ({ detail: { formSubmission: { submitter } } }) => {
|
6
|
+
const method = (submitter && submitter.formMethod) || (fetchOptions.body && fetchOptions.body.get("_method")) || form.getAttribute("method")
|
7
|
+
|
8
|
+
if (!/get/i.test(method)) {
|
9
|
+
if (/post/i.test(method)) {
|
10
|
+
fetchOptions.body.delete("_method")
|
11
|
+
} else {
|
12
|
+
fetchOptions.body.set("_method", method)
|
13
|
+
}
|
14
|
+
|
15
|
+
fetchOptions.method = "post"
|
16
|
+
}
|
17
|
+
}, { once: true })
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
// Based on https://github.com/nathan7/snakeize
|
2
|
+
//
|
3
|
+
// This software is released under the MIT license:
|
4
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
// this software and associated documentation files (the "Software"), to deal in
|
6
|
+
// the Software without restriction, including without limitation the rights to
|
7
|
+
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
// the Software, and to permit persons to whom the Software is furnished to do so,
|
9
|
+
// subject to the following conditions:
|
10
|
+
|
11
|
+
// The above copyright notice and this permission notice shall be included in all
|
12
|
+
// copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
16
|
+
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
17
|
+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
18
|
+
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
|
+
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
export default function walk (obj) {
|
21
|
+
if (!obj || typeof obj !== 'object') return obj;
|
22
|
+
if (obj instanceof Date || obj instanceof RegExp) return obj;
|
23
|
+
if (Array.isArray(obj)) return obj.map(walk);
|
24
|
+
return Object.keys(obj).reduce(function (acc, key) {
|
25
|
+
var camel = key[0].toLowerCase() + key.slice(1).replace(/([A-Z]+)/g, function (m, x) {
|
26
|
+
return '_' + x.toLowerCase();
|
27
|
+
});
|
28
|
+
acc[camel] = walk(obj[key]);
|
29
|
+
return acc;
|
30
|
+
}, {});
|
31
|
+
};
|
@@ -26,13 +26,28 @@
|
|
26
26
|
# and finally prepend the result of that partial rendering to the target identified with the dom id "clearances"
|
27
27
|
# (which is derived by default from the plural model name of the model, but can be overwritten).
|
28
28
|
#
|
29
|
+
# You can also choose to render html instead of a partial inside of a broadcast
|
30
|
+
# you do this by passing the html: option to any broadcast method that accepts the **rendering argument
|
31
|
+
#
|
32
|
+
# class Message < ApplicationRecord
|
33
|
+
# belongs_to :user
|
34
|
+
#
|
35
|
+
# after_create_commit :update_message_count
|
36
|
+
#
|
37
|
+
# private
|
38
|
+
# def update_message_count
|
39
|
+
# broadcast_update_to(user, :messages, target: "message-count", html: "<p> #{user.messages.count} </p>")
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
29
43
|
# There are four basic actions you can broadcast: <tt>remove</tt>, <tt>replace</tt>, <tt>append</tt>, and
|
30
44
|
# <tt>prepend</tt>. As a rule, you should use the <tt>_later</tt> versions of everything except for remove when broadcasting
|
31
45
|
# within a real-time path, like a controller or model, since all those updates require a rendering step, which can slow down
|
32
46
|
# execution. You don't need to do this for remove, since only the dom id for the model is used.
|
33
47
|
#
|
34
|
-
# In addition to the four basic actions, you can also use <tt>
|
35
|
-
# <tt>
|
48
|
+
# In addition to the four basic actions, you can also use <tt>broadcast_render</tt>,
|
49
|
+
# <tt>broadcast_render_to</tt> <tt>broadcast_render_later</tt>, and <tt>broadcast_render_later_to</tt>
|
50
|
+
# to render a turbo stream template with multiple actions.
|
36
51
|
module Turbo::Broadcastable
|
37
52
|
extend ActiveSupport::Concern
|
38
53
|
|
@@ -51,16 +66,22 @@ module Turbo::Broadcastable
|
|
51
66
|
# belongs_to :board
|
52
67
|
# broadcasts_to ->(message) { [ message.board, :messages ] }, inserts_by: :prepend, target: "board_messages"
|
53
68
|
# end
|
54
|
-
|
55
|
-
|
56
|
-
|
69
|
+
#
|
70
|
+
# class Message < ApplicationRecord
|
71
|
+
# belongs_to :board
|
72
|
+
# broadcasts_to ->(message) { [ message.board, :messages ] }, partial: "messages/custom_message"
|
73
|
+
# end
|
74
|
+
def broadcasts_to(stream, inserts_by: :append, target: broadcast_target_default, **rendering)
|
75
|
+
after_create_commit -> { broadcast_action_later_to stream.try(:call, self) || send(stream), action: inserts_by, target: target.try(:call, self) || target, **rendering }
|
76
|
+
after_update_commit -> { broadcast_replace_later_to stream.try(:call, self) || send(stream), **rendering }
|
57
77
|
after_destroy_commit -> { broadcast_remove_to stream.try(:call, self) || send(stream) }
|
58
78
|
end
|
59
79
|
|
60
|
-
# Same as <tt>#broadcasts_to</tt>, but the designated stream is automatically set to
|
61
|
-
|
62
|
-
|
63
|
-
|
80
|
+
# Same as <tt>#broadcasts_to</tt>, but the designated stream for updates and destroys is automatically set to
|
81
|
+
# the current model, for creates - to the model plural name, which can be overriden by passing <tt>stream</tt>.
|
82
|
+
def broadcasts(stream = model_name.plural, inserts_by: :append, target: broadcast_target_default, **rendering)
|
83
|
+
after_create_commit -> { broadcast_action_later_to stream, action: inserts_by, target: target.try(:call, self) || target, **rendering }
|
84
|
+
after_update_commit -> { broadcast_replace_later **rendering }
|
64
85
|
after_destroy_commit -> { broadcast_remove }
|
65
86
|
end
|
66
87
|
|
@@ -76,7 +97,7 @@ module Turbo::Broadcastable
|
|
76
97
|
# # Sends <turbo-stream action="remove" target="clearance_5"></turbo-stream> to the stream named "identity:2:clearances"
|
77
98
|
# clearance.broadcast_remove_to examiner.identity, :clearances
|
78
99
|
def broadcast_remove_to(*streamables, target: self)
|
79
|
-
Turbo::StreamsChannel.broadcast_remove_to
|
100
|
+
Turbo::StreamsChannel.broadcast_remove_to(*streamables, target: target)
|
80
101
|
end
|
81
102
|
|
82
103
|
# Same as <tt>#broadcast_remove_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -95,7 +116,7 @@ module Turbo::Broadcastable
|
|
95
116
|
# # to the stream named "identity:2:clearances"
|
96
117
|
# clearance.broadcast_replace_to examiner.identity, :clearances, partial: "clearances/other_partial", locals: { a: 1 }
|
97
118
|
def broadcast_replace_to(*streamables, **rendering)
|
98
|
-
Turbo::StreamsChannel.broadcast_replace_to
|
119
|
+
Turbo::StreamsChannel.broadcast_replace_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering))
|
99
120
|
end
|
100
121
|
|
101
122
|
# Same as <tt>#broadcast_replace_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -114,7 +135,7 @@ module Turbo::Broadcastable
|
|
114
135
|
# # to the stream named "identity:2:clearances"
|
115
136
|
# clearance.broadcast_update_to examiner.identity, :clearances, partial: "clearances/other_partial", locals: { a: 1 }
|
116
137
|
def broadcast_update_to(*streamables, **rendering)
|
117
|
-
Turbo::StreamsChannel.broadcast_update_to
|
138
|
+
Turbo::StreamsChannel.broadcast_update_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering))
|
118
139
|
end
|
119
140
|
|
120
141
|
# Same as <tt>#broadcast_update_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -135,7 +156,7 @@ module Turbo::Broadcastable
|
|
135
156
|
# clearance.broadcast_before_to examiner.identity, :clearances, target: "clearance_5",
|
136
157
|
# partial: "clearances/other_partial", locals: { a: 1 }
|
137
158
|
def broadcast_before_to(*streamables, target:, **rendering)
|
138
|
-
Turbo::StreamsChannel.broadcast_before_to
|
159
|
+
Turbo::StreamsChannel.broadcast_before_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering))
|
139
160
|
end
|
140
161
|
|
141
162
|
# Insert a rendering of this broadcastable model after the target identified by it's dom id passed as <tt>target</tt>
|
@@ -151,7 +172,7 @@ module Turbo::Broadcastable
|
|
151
172
|
# clearance.broadcast_after_to examiner.identity, :clearances, target: "clearance_5",
|
152
173
|
# partial: "clearances/other_partial", locals: { a: 1 }
|
153
174
|
def broadcast_after_to(*streamables, target:, **rendering)
|
154
|
-
Turbo::StreamsChannel.broadcast_after_to
|
175
|
+
Turbo::StreamsChannel.broadcast_after_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering))
|
155
176
|
end
|
156
177
|
|
157
178
|
# Append a rendering of this broadcastable model to the target identified by it's dom id passed as <tt>target</tt>
|
@@ -167,7 +188,7 @@ module Turbo::Broadcastable
|
|
167
188
|
# clearance.broadcast_append_to examiner.identity, :clearances, target: "clearances",
|
168
189
|
# partial: "clearances/other_partial", locals: { a: 1 }
|
169
190
|
def broadcast_append_to(*streamables, target: broadcast_target_default, **rendering)
|
170
|
-
Turbo::StreamsChannel.broadcast_append_to
|
191
|
+
Turbo::StreamsChannel.broadcast_append_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering))
|
171
192
|
end
|
172
193
|
|
173
194
|
# Same as <tt>#broadcast_append_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -188,7 +209,7 @@ module Turbo::Broadcastable
|
|
188
209
|
# clearance.broadcast_prepend_to examiner.identity, :clearances, target: "clearances",
|
189
210
|
# partial: "clearances/other_partial", locals: { a: 1 }
|
190
211
|
def broadcast_prepend_to(*streamables, target: broadcast_target_default, **rendering)
|
191
|
-
Turbo::StreamsChannel.broadcast_prepend_to
|
212
|
+
Turbo::StreamsChannel.broadcast_prepend_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering))
|
192
213
|
end
|
193
214
|
|
194
215
|
# Same as <tt>#broadcast_prepend_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -213,7 +234,7 @@ module Turbo::Broadcastable
|
|
213
234
|
|
214
235
|
# Same as <tt>broadcast_replace_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
215
236
|
def broadcast_replace_later_to(*streamables, **rendering)
|
216
|
-
Turbo::StreamsChannel.broadcast_replace_later_to
|
237
|
+
Turbo::StreamsChannel.broadcast_replace_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering))
|
217
238
|
end
|
218
239
|
|
219
240
|
# Same as <tt>#broadcast_replace_later_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -223,7 +244,7 @@ module Turbo::Broadcastable
|
|
223
244
|
|
224
245
|
# Same as <tt>broadcast_update_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
225
246
|
def broadcast_update_later_to(*streamables, **rendering)
|
226
|
-
Turbo::StreamsChannel.broadcast_update_later_to
|
247
|
+
Turbo::StreamsChannel.broadcast_update_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering))
|
227
248
|
end
|
228
249
|
|
229
250
|
# Same as <tt>#broadcast_update_later_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -233,7 +254,7 @@ module Turbo::Broadcastable
|
|
233
254
|
|
234
255
|
# Same as <tt>broadcast_append_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
235
256
|
def broadcast_append_later_to(*streamables, target: broadcast_target_default, **rendering)
|
236
|
-
Turbo::StreamsChannel.broadcast_append_later_to
|
257
|
+
Turbo::StreamsChannel.broadcast_append_later_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering))
|
237
258
|
end
|
238
259
|
|
239
260
|
# Same as <tt>#broadcast_append_later_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -243,7 +264,7 @@ module Turbo::Broadcastable
|
|
243
264
|
|
244
265
|
# Same as <tt>broadcast_prepend_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
245
266
|
def broadcast_prepend_later_to(*streamables, target: broadcast_target_default, **rendering)
|
246
|
-
Turbo::StreamsChannel.broadcast_prepend_later_to
|
267
|
+
Turbo::StreamsChannel.broadcast_prepend_later_to(*streamables, target: target, **broadcast_rendering_with_defaults(rendering))
|
247
268
|
end
|
248
269
|
|
249
270
|
# Same as <tt>#broadcast_prepend_later_to</tt>, but the designated stream is automatically set to the current model.
|
@@ -261,9 +282,7 @@ module Turbo::Broadcastable
|
|
261
282
|
broadcast_action_later_to self, action: action, target: target, **rendering
|
262
283
|
end
|
263
284
|
|
264
|
-
|
265
|
-
# Render a turbo stream template asynchronously with this broadcastable model passed as the local variable using a
|
266
|
-
# <tt>Turbo::Streams::BroadcastJob</tt>. Example:
|
285
|
+
# Render a turbo stream template with this broadcastable model passed as the local variable. Example:
|
267
286
|
#
|
268
287
|
# # Template: entries/_entry.turbo_stream.erb
|
269
288
|
# <%= turbo_stream.remove entry %>
|
@@ -275,7 +294,26 @@ module Turbo::Broadcastable
|
|
275
294
|
# <turbo-stream action="remove" target="entry_5"></turbo-stream>
|
276
295
|
# <turbo-stream action="append" target="entries"><template><div id="entry_5">My Entry</div></template></turbo-stream>
|
277
296
|
#
|
278
|
-
# ...to the stream named "entry:5"
|
297
|
+
# ...to the stream named "entry:5".
|
298
|
+
#
|
299
|
+
# Note that rendering inline via this method will cause template rendering to happen synchronously. That is usually not
|
300
|
+
# desireable for model callbacks, certainly not if those callbacks are inside of a transaction. Most of the time you should
|
301
|
+
# be using `broadcast_render_later`, unless you specifically know why synchronous rendering is needed.
|
302
|
+
def broadcast_render(**rendering)
|
303
|
+
broadcast_render_to self, **rendering
|
304
|
+
end
|
305
|
+
|
306
|
+
# Same as <tt>broadcast_render</tt> but run with the added option of naming the stream using the passed
|
307
|
+
# <tt>streamables</tt>.
|
308
|
+
#
|
309
|
+
# Note that rendering inline via this method will cause template rendering to happen synchronously. That is usually not
|
310
|
+
# desireable for model callbacks, certainly not if those callbacks are inside of a transaction. Most of the time you should
|
311
|
+
# be using `broadcast_render_later_to`, unless you specifically know why synchronous rendering is needed.
|
312
|
+
def broadcast_render_to(*streamables, **rendering)
|
313
|
+
Turbo::StreamsChannel.broadcast_render_to(*streamables, **broadcast_rendering_with_defaults(rendering))
|
314
|
+
end
|
315
|
+
|
316
|
+
# Same as <tt>broadcast_action_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
|
279
317
|
def broadcast_render_later(**rendering)
|
280
318
|
broadcast_render_later_to self, **rendering
|
281
319
|
end
|
@@ -283,7 +321,7 @@ module Turbo::Broadcastable
|
|
283
321
|
# Same as <tt>broadcast_render_later</tt> but run with the added option of naming the stream using the passed
|
284
322
|
# <tt>streamables</tt>.
|
285
323
|
def broadcast_render_later_to(*streamables, **rendering)
|
286
|
-
Turbo::StreamsChannel.broadcast_render_later_to
|
324
|
+
Turbo::StreamsChannel.broadcast_render_later_to(*streamables, **broadcast_rendering_with_defaults(rendering))
|
287
325
|
end
|
288
326
|
|
289
327
|
|
@@ -297,7 +335,10 @@ module Turbo::Broadcastable
|
|
297
335
|
# Add the current instance into the locals with the element name (which is the un-namespaced name)
|
298
336
|
# as the key. This parallels how the ActionView::ObjectRenderer would create a local variable.
|
299
337
|
o[:locals] = (o[:locals] || {}).reverse_merge!(model_name.element.to_sym => self)
|
300
|
-
|
338
|
+
# if the html option is passed in it will skip setting a partial from #to_partial_path
|
339
|
+
unless o.include?(:html)
|
340
|
+
o[:partial] ||= to_partial_path
|
341
|
+
end
|
301
342
|
end
|
302
343
|
end
|
303
344
|
end
|
@@ -50,7 +50,7 @@ class Turbo::Streams::TagBuilder
|
|
50
50
|
action_all :remove, targets, allow_inferred_rendering: false
|
51
51
|
end
|
52
52
|
|
53
|
-
# Replace the <tt>target</tt> in the dom with
|
53
|
+
# Replace the <tt>target</tt> in the dom with either the <tt>content</tt> passed in, a rendering result determined
|
54
54
|
# by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the target as a record. Examples:
|
55
55
|
#
|
56
56
|
# <%= turbo_stream.replace "clearance_5", "<div id='clearance_5'>Replace the dom target identified by clearance_5</div>" %>
|
@@ -63,7 +63,7 @@ class Turbo::Streams::TagBuilder
|
|
63
63
|
action :replace, target, content, **rendering, &block
|
64
64
|
end
|
65
65
|
|
66
|
-
# Replace the <tt>targets</tt> in the dom with
|
66
|
+
# Replace the <tt>targets</tt> in the dom with either the <tt>content</tt> passed in, a rendering result determined
|
67
67
|
# by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the target as a record. Examples:
|
68
68
|
#
|
69
69
|
# <%= turbo_stream.replace_all ".clearance_item", "<div class='clearance_item'>Replace the dom target identified by the class clearance_item</div>" %>
|
@@ -128,7 +128,7 @@ class Turbo::Streams::TagBuilder
|
|
128
128
|
action_all :after, targets, content, **rendering, &block
|
129
129
|
end
|
130
130
|
|
131
|
-
# Update the <tt>target</tt> in the dom with
|
131
|
+
# Update the <tt>target</tt> in the dom with either the <tt>content</tt> passed in or a rendering result determined
|
132
132
|
# by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the target as a record. Examples:
|
133
133
|
#
|
134
134
|
# <%= turbo_stream.update "clearance_5", "Update the content of the dom target identified by clearance_5" %>
|
@@ -141,7 +141,7 @@ class Turbo::Streams::TagBuilder
|
|
141
141
|
action :update, target, content, **rendering, &block
|
142
142
|
end
|
143
143
|
|
144
|
-
# Update the <tt>targets</tt> in the dom with
|
144
|
+
# Update the <tt>targets</tt> in the dom with either the <tt>content</tt> passed in or a rendering result determined
|
145
145
|
# by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the targets as a record. Examples:
|
146
146
|
#
|
147
147
|
# <%= turbo_stream.update_all "clearance_item", "Update the content of the dom target identified by the class clearance_item" %>
|
@@ -1,6 +1,17 @@
|
|
1
1
|
if (cable_config_path = Rails.root.join("config/cable.yml")).exist?
|
2
2
|
say "Enable redis in bundle"
|
3
|
-
|
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
|
4
15
|
|
5
16
|
say "Switch development cable to use redis"
|
6
17
|
gsub_file cable_config_path.to_s, /development:\n\s+adapter: async/, "development:\n adapter: redis\n url: redis://localhost:6379/1"
|
@@ -2,4 +2,4 @@ say "Import Turbo"
|
|
2
2
|
append_to_file "app/javascript/application.js", %(import "@hotwired/turbo-rails"\n)
|
3
3
|
|
4
4
|
say "Pin Turbo"
|
5
|
-
append_to_file "config/importmap.rb", %(pin "@hotwired/turbo-rails", to: "turbo.js"\n)
|
5
|
+
append_to_file "config/importmap.rb", %(pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true\n)
|