turbo-rails 0.5.9 → 0.5.10

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.
@@ -13,6 +13,14 @@ module Turbo::Streams::Broadcasts
13
13
  broadcast_action_to *streamables, action: :replace, target: target, **rendering
14
14
  end
15
15
 
16
+ def broadcast_before_to(*streamables, target:, **rendering)
17
+ broadcast_action_to *streamables, action: :before, target: target, **rendering
18
+ end
19
+
20
+ def broadcast_after_to(*streamables, target:, **rendering)
21
+ broadcast_action_to *streamables, action: :after, target: target, **rendering
22
+ end
23
+
16
24
  def broadcast_append_to(*streamables, target:, **rendering)
17
25
  broadcast_action_to *streamables, action: :append, target: target, **rendering
18
26
  end
@@ -32,6 +40,14 @@ module Turbo::Streams::Broadcasts
32
40
  broadcast_action_later_to *streamables, action: :replace, target: target, **rendering
33
41
  end
34
42
 
43
+ def broadcast_before_later_to(*streamables, target:, **rendering)
44
+ broadcast_action_later_to *streamables, action: :before, target: target, **rendering
45
+ end
46
+
47
+ def broadcast_after_later_to(*streamables, target:, **rendering)
48
+ broadcast_action_later_to *streamables, action: :after, target: target, **rendering
49
+ end
50
+
35
51
  def broadcast_append_later_to(*streamables, target:, **rendering)
36
52
  broadcast_action_later_to *streamables, action: :append, target: target, **rendering
37
53
  end
@@ -16,12 +16,16 @@ module Turbo::FramesHelper
16
16
  # <%= turbo_frame_tag "tray", target: "other_tray" %>
17
17
  # # => <turbo-frame id="tray" target="other_tray"></turbo-frame>
18
18
  #
19
+ # <%= turbo_frame_tag "tray", src: tray_path(tray), loading: "lazy" %>
20
+ # # => <turbo-frame id="tray" src="http://example.com/trays/1" loading="lazy"></turbo-frame>
21
+ #
19
22
  # <%= turbo_frame_tag "tray" do %>
20
23
  # <div>My tray frame!</div>
21
24
  # <% end %>
22
25
  # # => <turbo-frame id="tray"><div>My tray frame!</div></turbo-frame>
23
26
  def turbo_frame_tag(id, src: nil, target: nil, **attributes, &block)
24
27
  id = id.respond_to?(:to_key) ? dom_id(id) : id
28
+ src = url_for(src) if src.present?
25
29
 
26
30
  tag.turbo_frame(**attributes.merge(id: id, src: src, target: target).compact, &block)
27
31
  end
@@ -1,5 +1,5 @@
1
1
  module Turbo::StreamsHelper
2
- # Returns a new <tt>Turbo::Streams::TagBuilder</tt> object that accepts stream actions and renders them them as
2
+ # Returns a new <tt>Turbo::Streams::TagBuilder</tt> object that accepts stream actions and renders them as
3
3
  # the template tags needed to send across the wire. This object is automatically yielded to turbo_stream.erb templates.
4
4
  #
5
5
  # When responding to HTTP requests, controllers can declare `turbo_stream` format response templates in that same
@@ -39,7 +39,9 @@ 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
- def turbo_stream_from(*streamables)
43
- tag.turbo_cable_stream_source channel: "Turbo::StreamsChannel", "signed-stream-name": Turbo::StreamsChannel.signed_stream_name(streamables)
42
+ def turbo_stream_from(*streamables, **attributes)
43
+ attributes[:channel] = "Turbo::StreamsChannel"
44
+ attributes[:"signed-stream-name"] = Turbo::StreamsChannel.signed_stream_name(streamables)
45
+ tag.turbo_cable_stream_source(**attributes)
44
46
  end
45
47
  end
@@ -1,15 +1,18 @@
1
1
  let consumer
2
2
 
3
3
  export async function getConsumer() {
4
- if (consumer) return consumer
5
- const { createConsumer } = await import("@rails/actioncable/src")
6
- return setConsumer(createConsumer())
4
+ return consumer || setConsumer(createConsumer().then(setConsumer))
7
5
  }
8
6
 
9
7
  export function setConsumer(newConsumer) {
10
8
  return consumer = newConsumer
11
9
  }
12
10
 
11
+ export async function createConsumer() {
12
+ const { createConsumer } = await import(/* webpackChunkName: "actioncable" */ "@rails/actioncable/src")
13
+ return createConsumer()
14
+ }
15
+
13
16
  export async function subscribeTo(channel, mixin) {
14
17
  const { subscriptions } = await getConsumer()
15
18
  return subscriptions.create(channel, mixin)
@@ -1,5 +1,7 @@
1
1
  # The job that powers all the <tt>broadcast_$action_later</tt> broadcasts available in <tt>Turbo::Streams::Broadcasts</tt>.
2
2
  class Turbo::Streams::ActionBroadcastJob < ActiveJob::Base
3
+ discard_on ActiveJob::DeserializationError
4
+
3
5
  def perform(stream, action:, target:, **rendering)
4
6
  Turbo::StreamsChannel.broadcast_action_to stream, action: action, target: target, **rendering
5
7
  end
@@ -1,6 +1,8 @@
1
1
  # The job that powers the <tt>broadcast_render_later_to</tt> available in <tt>Turbo::Streams::Broadcasts</tt> for rendering
2
2
  # turbo stream templates.
3
3
  class Turbo::Streams::BroadcastJob < ActiveJob::Base
4
+ discard_on ActiveJob::DeserializationError
5
+
4
6
  def perform(stream, **rendering)
5
7
  Turbo::StreamsChannel.broadcast_render_to stream, **rendering
6
8
  end
@@ -39,7 +39,7 @@ module Turbo::Broadcastable
39
39
  module ClassMethods
40
40
  # Configures the model to broadcast creates, updates, and destroys to a stream name derived at runtime by the
41
41
  # <tt>stream</tt> symbol invocation. By default, the creates are appended to a dom id target name derived from
42
- # the model's plural name. The insertion can also be made to be a prepend by overwriting <tt>insertion</tt> and
42
+ # the model's plural name. The insertion can also be made to be a prepend by overwriting <tt>inserts_by</tt> and
43
43
  # the target dom id overwritten by passing <tt>target</tt>. Examples:
44
44
  #
45
45
  # class Message < ApplicationRecord
@@ -52,14 +52,14 @@ module Turbo::Broadcastable
52
52
  # broadcasts_to ->(message) { [ message.board, :messages ] }, inserts_by: :prepend, target: "board_messages"
53
53
  # end
54
54
  def broadcasts_to(stream, inserts_by: :append, target: broadcast_target_default)
55
- after_create_commit -> { broadcast_action_later_to stream.try(:call, self) || send(stream), action: inserts_by, target: target }
55
+ after_create_commit -> { broadcast_action_later_to stream.try(:call, self) || send(stream), action: inserts_by, target: target.try(:call, self) || target }
56
56
  after_update_commit -> { broadcast_replace_later_to stream.try(:call, self) || send(stream) }
57
57
  after_destroy_commit -> { broadcast_remove_to stream.try(:call, self) || send(stream) }
58
58
  end
59
59
 
60
60
  # Same as <tt>#broadcasts_to</tt>, but the designated stream is automatically set to the current model.
61
61
  def broadcasts(inserts_by: :append, target: broadcast_target_default)
62
- after_create_commit -> { broadcast_action_later action: inserts_by, target: target }
62
+ after_create_commit -> { broadcast_action_later action: inserts_by, target: target.try(:call, self) || target }
63
63
  after_update_commit -> { broadcast_replace_later }
64
64
  after_destroy_commit -> { broadcast_remove }
65
65
  end
@@ -103,6 +103,38 @@ module Turbo::Broadcastable
103
103
  broadcast_replace_to self, **rendering
104
104
  end
105
105
 
106
+ # Insert a rendering of this broadcastable model before the target identified by it's dom id passed as <tt>target</tt>
107
+ # for subscribers of the stream name identified by the passed <tt>streamables</tt>. The rendering parameters can be set by
108
+ # appending named arguments to the call. Examples:
109
+ #
110
+ # # Sends <turbo-stream action="before" target="clearance_5"><template><div id="clearance_4">My Clearance</div></template></turbo-stream>
111
+ # # to the stream named "identity:2:clearances"
112
+ # clearance.broadcast_before_to examiner.identity, :clearances, target: "clearance_5"
113
+ #
114
+ # # Sends <turbo-stream action="before" target="clearance_5"><template><div id="clearance_4">Other partial</div></template></turbo-stream>
115
+ # # to the stream named "identity:2:clearances"
116
+ # clearance.broadcast_before_to examiner.identity, :clearances, target: "clearance_5",
117
+ # partial: "clearances/other_partial", locals: { a: 1 }
118
+ def broadcast_before_to(*streamables, target:, **rendering)
119
+ Turbo::StreamsChannel.broadcast_before_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
120
+ end
121
+
122
+ # Insert a rendering of this broadcastable model after the target identified by it's dom id passed as <tt>target</tt>
123
+ # for subscribers of the stream name identified by the passed <tt>streamables</tt>. The rendering parameters can be set by
124
+ # appending named arguments to the call. Examples:
125
+ #
126
+ # # Sends <turbo-stream action="after" target="clearance_5"><template><div id="clearance_6">My Clearance</div></template></turbo-stream>
127
+ # # to the stream named "identity:2:clearances"
128
+ # clearance.broadcast_after_to examiner.identity, :clearances, target: "clearance_5"
129
+ #
130
+ # # Sends <turbo-stream action="after" target="clearance_5"><template><div id="clearance_6">Other partial</div></template></turbo-stream>
131
+ # # to the stream named "identity:2:clearances"
132
+ # clearance.broadcast_after_to examiner.identity, :clearances, target: "clearance_5",
133
+ # partial: "clearances/other_partial", locals: { a: 1 }
134
+ def broadcast_after_to(*streamables, target:, **rendering)
135
+ Turbo::StreamsChannel.broadcast_after_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
136
+ end
137
+
106
138
  # Append a rendering of this broadcastable model to the target identified by it's dom id passed as <tt>target</tt>
107
139
  # for subscribers of the stream name identified by the passed <tt>streamables</tt>. The rendering parameters can be set by
108
140
  # appending named arguments to the call. Examples:
@@ -140,7 +172,7 @@ module Turbo::Broadcastable
140
172
  Turbo::StreamsChannel.broadcast_prepend_to *streamables, target: target, **broadcast_rendering_with_defaults(rendering)
141
173
  end
142
174
 
143
- # Same as <tt>#broadcast_append_to</tt>, but the designated stream is automatically set to the current model.
175
+ # Same as <tt>#broadcast_prepend_to</tt>, but the designated stream is automatically set to the current model.
144
176
  def broadcast_prepend(target: broadcast_target_default, **rendering)
145
177
  broadcast_prepend_to self, target: target, **rendering
146
178
  end
@@ -190,7 +222,7 @@ module Turbo::Broadcastable
190
222
  broadcast_prepend_later_to self, target: target, **rendering
191
223
  end
192
224
 
193
- # Same as <tt>broadcast_action_later_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
225
+ # Same as <tt>broadcast_action_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
194
226
  def broadcast_action_later_to(*streamables, action:, target: broadcast_target_default, **rendering)
195
227
  Turbo::StreamsChannel.broadcast_action_later_to(*streamables, action: action, target: target, **broadcast_rendering_with_defaults(rendering))
196
228
  end
@@ -219,7 +251,7 @@ module Turbo::Broadcastable
219
251
  broadcast_render_later_to self, **rendering
220
252
  end
221
253
 
222
- # Same as <tt>broadcast_prepend_to</tt> but run with the added option of naming the stream using the passed
254
+ # Same as <tt>broadcast_render_later</tt> but run with the added option of naming the stream using the passed
223
255
  # <tt>streamables</tt>.
224
256
  def broadcast_render_later_to(*streamables, **rendering)
225
257
  Turbo::StreamsChannel.broadcast_render_later_to *streamables, **broadcast_rendering_with_defaults(rendering)
@@ -233,7 +265,7 @@ module Turbo::Broadcastable
233
265
 
234
266
  def broadcast_rendering_with_defaults(options)
235
267
  options.tap do |o|
236
- o[:locals] = (o[:locals] || {}).reverse_merge!(model_name.singular.to_sym => self)
268
+ o[:object] ||= self
237
269
  o[:partial] ||= to_partial_path
238
270
  end
239
271
  end
@@ -53,6 +53,32 @@ class Turbo::Streams::TagBuilder
53
53
  action :replace, target, content, **rendering, &block
54
54
  end
55
55
 
56
+ # Insert the <tt>content</tt> passed in, a rendering result determined by the <tt>rendering</tt> keyword arguments,
57
+ # the content in the block, or the rendering of the target as a record before the <tt>target</tt> in the dom. Examples:
58
+ #
59
+ # <%= turbo_stream.before "clearance_5", "<div id='clearance_4'>Insert before the dom target identified by clearance_5</div>" %>
60
+ # <%= turbo_stream.before clearance %>
61
+ # <%= turbo_stream.before clearance, partial: "clearances/clearance", locals: { title: "Hello" } %>
62
+ # <%= turbo_stream.before "clearance_5" do %>
63
+ # <div id='clearance_4'>Insert before the dom target identified by clearance_5</div>
64
+ # <% end %>
65
+ def before(target, content = nil, **rendering, &block)
66
+ action :before, target, content, **rendering, &block
67
+ end
68
+
69
+ # Insert the <tt>content</tt> passed in, a rendering result determined by the <tt>rendering</tt> keyword arguments,
70
+ # the content in the block, or the rendering of the target as a record after the <tt>target</tt> in the dom. Examples:
71
+ #
72
+ # <%= turbo_stream.after "clearance_5", "<div id='clearance_6'>Insert after the dom target identified by clearance_5</div>" %>
73
+ # <%= turbo_stream.after clearance %>
74
+ # <%= turbo_stream.after clearance, partial: "clearances/clearance", locals: { title: "Hello" } %>
75
+ # <%= turbo_stream.after "clearance_5" do %>
76
+ # <div id='clearance_6'>Insert after the dom target identified by clearance_5</div>
77
+ # <% end %>
78
+ def after(target, content = nil, **rendering, &block)
79
+ action :after, target, content, **rendering, &block
80
+ end
81
+
56
82
  # Update the <tt>target</tt> in the dom with the either the <tt>content</tt> passed in or a rendering result determined
57
83
  # by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the target as a record. Examples:
58
84
  #
@@ -19,12 +19,7 @@ if APPLICATION_LAYOUT_PATH.exist?
19
19
  end
20
20
  else
21
21
  say "Default application.html.erb is missing!", :red
22
-
23
- if APPLICATION_LAYOUT_PATH.read =~ /stimulus/
24
- say %( Add <%= javascript_include_tag("turbo", type: "module-shim") %> and <%= yield :head %> within the <head> tag after Stimulus includes in your custom layout.)
25
- else
26
- say %( Add <%= javascript_include_tag("turbo", type: "module") %> and <%= yield :head %> within the <head> tag in your custom layout.)
27
- end
22
+ say %( Add <%= javascript_include_tag("turbo", type: "module-shim") %> and <%= yield :head %> within the <head> tag after Stimulus includes in your custom layout.)
28
23
  end
29
24
 
30
25
  say "Enable redis in bundle"
@@ -1,16 +1,17 @@
1
1
  # Some Rails versions use commonJS(require) others use ESM(import).
2
2
  TURBOLINKS_REGEX = /(import .* from "turbolinks".*\n|require\("turbolinks"\).*\n)/.freeze
3
+ ACTIVE_STORAGE_REGEX = /(import.*ActiveStorage|require.*@rails\/activestorage.*)/.freeze
3
4
 
4
5
  abort "❌ Webpacker not found. Exiting." unless defined?(Webpacker::Engine)
5
6
 
6
7
  say "Install Turbo"
7
8
  run "yarn add @hotwired/turbo-rails"
8
- insert_into_file "#{Webpacker.config.source_entry_path}/application.js", "import \"@hotwired/turbo-rails\"\n", before: /import.*ActiveStorage/
9
+ insert_into_file "#{Webpacker.config.source_entry_path}/application.js", "import \"@hotwired/turbo-rails\"\n", before: ACTIVE_STORAGE_REGEX
9
10
 
10
11
  say "Remove Turbolinks"
11
- gsub_file 'Gemfile', /gem 'turbolinks'.*/, ''
12
- run "bin/bundle", capture: true
13
- run "bin/yarn remove turbolinks"
12
+ run "#{RbConfig.ruby} bin/bundle remove turbolinks"
13
+ run "#{RbConfig.ruby} bin/bundle", capture: true
14
+ run "#{RbConfig.ruby} bin/yarn remove turbolinks"
14
15
  gsub_file "#{Webpacker.config.source_entry_path}/application.js", TURBOLINKS_REGEX, ''
15
16
  gsub_file "#{Webpacker.config.source_entry_path}/application.js", /Turbolinks.start.*\n/, ''
16
17
 
data/lib/turbo/engine.rb CHANGED
@@ -16,13 +16,17 @@ module Turbo
16
16
  #{root}/app/jobs
17
17
  )
18
18
 
19
+ initializer "turbo.no_action_cable" do
20
+ Rails.autoloaders.once.do_not_eager_load(Dir["#{root}/app/channels/turbo/*_channel.rb"]) unless defined?(ActionCable)
21
+ end
22
+
19
23
  initializer "turbo.assets" do
20
24
  if Rails.application.config.respond_to?(:assets)
21
25
  Rails.application.config.assets.precompile += %w( turbo )
22
26
  end
23
27
  end
24
28
 
25
- initializer "turbo.helpers" do
29
+ initializer "turbo.helpers", before: :load_config_initializers do
26
30
  ActiveSupport.on_load(:action_controller_base) do
27
31
  include Turbo::Streams::TurboStreamsTagBuilder, Turbo::Frames::FrameRequest, Turbo::Native::Navigation
28
32
  helper Turbo::Engine.helpers
@@ -7,14 +7,13 @@ module Turbo
7
7
  delegate :dom_id, :dom_class, to: ActionView::RecordIdentifier
8
8
  end
9
9
 
10
- def assert_turbo_stream(action:, target: nil, &block)
11
- assert_response :ok
10
+ def assert_turbo_stream(action:, target: nil, status: :ok, &block)
11
+ assert_response status
12
12
  assert_equal Mime[:turbo_stream], response.media_type
13
13
  assert_select %(turbo-stream[action="#{action}"][target="#{target.respond_to?(:to_key) ? dom_id(target) : target}"]), count: 1, &block
14
14
  end
15
15
 
16
16
  def assert_no_turbo_stream(action:, target: nil)
17
- assert_response :ok
18
17
  assert_equal Mime[:turbo_stream], response.media_type
19
18
  assert_select %(turbo-stream[action="#{action}"][target="#{target.respond_to?(:to_key) ? dom_id(target) : target}"]), count: 0
20
19
  end
data/lib/turbo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Turbo
2
- VERSION = "0.5.9"
2
+ VERSION = "0.5.10"
3
3
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.9
4
+ version: 0.5.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Stephenson
8
8
  - Javan Mahkmali
9
9
  - David Heinemeier Hansson
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-01-30 00:00:00.000000000 Z
13
+ date: 2021-06-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
@@ -26,7 +26,7 @@ dependencies:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
28
  version: 6.0.0
29
- description:
29
+ description:
30
30
  email: david@loudthinking.com
31
31
  executables: []
32
32
  extensions: []
@@ -67,7 +67,7 @@ homepage: https://github.com/hotwired/turbo-rails
67
67
  licenses:
68
68
  - MIT
69
69
  metadata: {}
70
- post_install_message:
70
+ post_install_message:
71
71
  rdoc_options: []
72
72
  require_paths:
73
73
  - lib
@@ -82,8 +82,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
84
  requirements: []
85
- rubygems_version: 3.1.2
86
- signing_key:
85
+ rubygems_version: 3.1.4
86
+ signing_key:
87
87
  specification_version: 4
88
88
  summary: The speed of a single-page web application without having to write any JavaScript.
89
89
  test_files: []