turbo-rails 0.7.11 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|