turbo-rails 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- # Provides the broadcast actions in synchronous and asynchrous form for the <tt>Turbo::StreamsChannel</tt>.
1
+ # Provides the broadcast actions in synchronous and asynchronous form for the <tt>Turbo::StreamsChannel</tt>.
2
2
  # See <tt>Turbo::Broadcastable</tt> for the user-facing API that invokes these methods with most of the paperwork filled out already.
3
3
  #
4
4
  # Can be used directly using something like <tt>Turbo::StreamsChannel.broadcast_remove_to :entries, target: 1</tt>.
@@ -5,7 +5,7 @@
5
5
  # When that header is detected by the controller, we substitute our own minimal layout in place of the
6
6
  # application-supplied layout (since we're only working on an in-page frame, thus can skip the weight of the layout). We
7
7
  # use a minimal layout, rather than avoid the layout entirely, so that it's still possible to render content into the
8
- # <tt>head<tt>.
8
+ # <tt>head</tt>.
9
9
  #
10
10
  # Accordingly, we ensure that the etag for the page is changed, such that a cache for a minimal-layout request isn't
11
11
  # served on a normal request and vice versa.
@@ -15,29 +15,32 @@ module Turbo::Native::Navigation
15
15
  request.user_agent.to_s.match?(/Turbo Native/)
16
16
  end
17
17
 
18
- # Tell the Turbo Native app to dismiss a modal (if presented) or pop a screen off of the navigation stack.
18
+ # Tell the Turbo Native app to dismiss a modal (if presented) or pop a screen off of the navigation stack. Otherwise redirect to the given URL if Turbo Native is not present.
19
19
  def recede_or_redirect_to(url, **options)
20
20
  turbo_native_action_or_redirect url, :recede, :to, options
21
21
  end
22
22
 
23
- # Tell the Turbo Native app to ignore this navigation.
23
+ # Tell the Turbo Native app to ignore this navigation, otherwise redirect to the given URL if Turbo Native is not present.
24
24
  def resume_or_redirect_to(url, **options)
25
25
  turbo_native_action_or_redirect url, :resume, :to, options
26
26
  end
27
27
 
28
- # Tell the Turbo Native app to refresh the current screen.
28
+ # Tell the Turbo Native app to refresh the current screen, otherwise redirect to the given URL if Turbo Native is not present.
29
29
  def refresh_or_redirect_to(url, **options)
30
30
  turbo_native_action_or_redirect url, :refresh, :to, options
31
31
  end
32
32
 
33
+ # Same as <tt>recede_or_redirect_to</tt> but redirects to the previous page or provided fallback location if the Turbo Native app is not present.
33
34
  def recede_or_redirect_back_or_to(url, **options)
34
35
  turbo_native_action_or_redirect url, :recede, :back, options
35
36
  end
36
37
 
38
+ # Same as <tt>resume_or_redirect_to</tt> but redirects to the previous page or provided fallback location if the Turbo Native app is not present.
37
39
  def resume_or_redirect_back_or_to(url, **options)
38
40
  turbo_native_action_or_redirect url, :resume, :back, options
39
41
  end
40
42
 
43
+ # Same as <tt>refresh_or_redirect_to</tt> but redirects to the previous page or provided fallback location if the Turbo Native app is not present.
41
44
  def refresh_or_redirect_back_or_to(url, **options)
42
45
  turbo_native_action_or_redirect url, :refresh, :back, options
43
46
  end
@@ -1,22 +1,21 @@
1
+ # Helpers to configure Turbo Drive via meta directives. They come in two
2
+ # variants:
3
+ #
4
+ # The recommended option is to include +yield :head+ in the +<head>+ section
5
+ # of the layout. Then you can use the helpers in any view.
6
+ #
7
+ # ==== Example
8
+ #
9
+ # # app/views/application.html.erb
10
+ # <html><head><%= yield :head %></head><body><%= yield %></html>
11
+ #
12
+ # # app/views/trays/index.html.erb
13
+ # <% turbo_exempts_page_from_cache %>
14
+ # <p>Page that shouldn't be cached by Turbo</p>
15
+ #
16
+ # Alternatively, you can use the +_tag+ variant of the helpers to only get the
17
+ # HTML for the meta directive.
1
18
  module Turbo::DriveHelper
2
- # Helpers to configure Turbo Drive via meta directives. They come in two
3
- # variants:
4
- #
5
- # The recommended option is to include +yield :head+ in the +<head>+ section
6
- # of the layout. Then you can use the helpers in any view.
7
- #
8
- # ==== Example
9
- #
10
- # # app/views/application.html.erb
11
- # <html><head><%= yield :head %></head><body><%= yield %></html>
12
- #
13
- # # app/views/trays/index.html.erb
14
- # <% turbo_exempts_page_from_cache %>
15
- # <p>Page that shouldn't be cached by Turbo</p>
16
- #
17
- # Alternatively, you can use the +_tag+ variant of the helpers to only get the
18
- # HTML for the meta directive.
19
-
20
19
  # Pages that are more likely than not to be a cache miss can skip turbo cache to avoid visual jitter.
21
20
  # Cannot be used along with +turbo_exempts_page_from_preview+.
22
21
  def turbo_exempts_page_from_cache
@@ -2,7 +2,7 @@ module Turbo::FramesHelper
2
2
  # Returns a frame tag that can either be used simply to encapsulate frame content or as a lazy-loading container that starts empty but
3
3
  # fetches the URL supplied in the +src+ attribute.
4
4
  #
5
- # === Examples
5
+ # ==== Examples
6
6
  #
7
7
  # <%= turbo_frame_tag "tray", src: tray_path(tray) %>
8
8
  # # => <turbo-frame id="tray" src="http://example.com/trays/1"></turbo-frame>
@@ -27,17 +27,14 @@ module Turbo::FramesHelper
27
27
  # <%= turbo_frame_tag [user_id, "tray"], src: tray_path(tray) %>
28
28
  # # => <turbo-frame id="1_tray" src="http://example.com/trays/1"></turbo-frame>
29
29
  #
30
- # The `turbo_frame_tag` helper will convert the arguments it receives to their
31
- # `dom_id` if applicable to easily generate unique ids for Turbo Frames:
30
+ # The +turbo_frame_tag+ helper will convert the arguments it receives to their
31
+ # +dom_id+ if applicable to easily generate unique ids for Turbo Frames:
32
32
  #
33
33
  # <%= turbo_frame_tag(Article.find(1)) %>
34
34
  # # => <turbo-frame id="article_1"></turbo-frame>
35
35
  #
36
36
  # <%= turbo_frame_tag(Article.find(1), "comments") %>
37
- # # => <turbo-frame id="article_1_comments"></turbo-frame>
38
- #
39
- # <%= turbo_frame_tag(Article.find(1), Comment.new) %>
40
- # # => <turbo-frame id="article_1_new_comment"></turbo-frame>
37
+ # # => <turbo-frame id="comments_article_1"></turbo-frame>
41
38
  def turbo_frame_tag(*ids, src: nil, target: nil, **attributes, &block)
42
39
  id = ids.first.respond_to?(:to_key) ? ActionView::RecordIdentifier.dom_id(*ids) : ids.join('_')
43
40
  src = url_for(src) if src.present?
@@ -22,7 +22,6 @@ module Turbo::Streams::ActionHelper
22
22
  # message = Message.find(1)
23
23
  # turbo_stream_action_tag "remove", target: [message, :special]
24
24
  # # => <turbo-stream action="remove" target="special_message_1"></turbo-stream>
25
- #
26
25
  def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil, **attributes)
27
26
  template = action.to_sym.in?(%i[ remove refresh ]) ? "" : tag.template(template.to_s.html_safe)
28
27
 
@@ -35,6 +34,10 @@ module Turbo::Streams::ActionHelper
35
34
  end
36
35
  end
37
36
 
37
+ # Creates a `turbo-stream` tag with an `action="refresh"` attribute. Example:
38
+ #
39
+ # turbo_stream_refresh_tag
40
+ # # => <turbo-stream action="refresh"></turbo-stream>
38
41
  def turbo_stream_refresh_tag(request_id: Turbo.current_request_id, **attributes)
39
42
  turbo_stream_action_tag(:refresh, **{ "request-id": request_id }.compact, **attributes)
40
43
  end
@@ -48,7 +48,6 @@ 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
51
  def turbo_stream_from(*streamables, **attributes)
53
52
  attributes[:channel] = attributes[:channel]&.to_s || "Turbo::StreamsChannel"
54
53
  attributes[:"signed-stream-name"] = Turbo::StreamsChannel.signed_stream_name(streamables)
@@ -14,8 +14,8 @@
14
14
  # end
15
15
  # end
16
16
  #
17
- # This is an example from [HEY](https://hey.com), and the clearance is the model that drives
18
- # [the screener](https://hey.com/features/the-screener/), which gives users the power to deny first-time senders (petitioners)
17
+ # This is an example from {HEY}[https://hey.com], and the clearance is the model that drives
18
+ # {the screener}[https://hey.com/features/the-screener/], which gives users the power to deny first-time senders (petitioners)
19
19
  # access to their attention (as the examiner). When a new clearance is created upon receipt of an email from a first-time
20
20
  # sender, that'll trigger the call to broadcast_later, which in turn invokes <tt>broadcast_prepend_later_to</tt>.
21
21
  #
@@ -27,7 +27,7 @@
27
27
  # (which is derived by default from the plural model name of the model, but can be overwritten).
28
28
  #
29
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. Example:
30
+ # you do this by passing the +html:+ option to any broadcast method that accepts the **rendering argument. Example:
31
31
  #
32
32
  # class Message < ApplicationRecord
33
33
  # belongs_to :user
@@ -40,8 +40,8 @@
40
40
  # end
41
41
  # end
42
42
  #
43
- # If you want to render a template instead of a partial, e.g. ('messages/index' or 'messages/show'), you can use the `template:` option.
44
- # Again, only to any broadcast method that accepts the `**rendering` argument. Example:
43
+ # If you want to render a template instead of a partial, e.g. ('messages/index' or 'messages/show'), you can use the +template:+ option.
44
+ # Again, only to any broadcast method that accepts the +**rendering+ argument. Example:
45
45
  #
46
46
  # class Message < ApplicationRecord
47
47
  # belongs_to :user
@@ -54,7 +54,7 @@
54
54
  # end
55
55
  # end
56
56
  #
57
- # If you want to render a renderable object you can use the `renderable:` option.
57
+ # If you want to render a renderable object you can use the +renderable:+ option.
58
58
  #
59
59
  # class Message < ApplicationRecord
60
60
  # belongs_to :user
@@ -67,15 +67,74 @@
67
67
  # end
68
68
  # end
69
69
  #
70
- # There are four basic actions you can broadcast: <tt>remove</tt>, <tt>replace</tt>, <tt>append</tt>, and
71
- # <tt>prepend</tt>. As a rule, you should use the <tt>_later</tt> versions of everything except for remove when broadcasting
70
+ # There are seven basic actions you can broadcast: <tt>after</tt>, <tt>append</tt>, <tt>before</tt>,
71
+ # <tt>prepend</tt>, <tt>remove</tt>, <tt>replace</tt>, and
72
+ # <tt>update</tt>. As a rule, you should use the <tt>_later</tt> versions of everything except for remove when broadcasting
72
73
  # within a real-time path, like a controller or model, since all those updates require a rendering step, which can slow down
73
74
  # execution. You don't need to do this for remove, since only the dom id for the model is used.
74
75
  #
75
- # In addition to the four basic actions, you can also use <tt>broadcast_render</tt>,
76
+ # In addition to the seven basic actions, you can also use <tt>broadcast_render</tt>,
76
77
  # <tt>broadcast_render_to</tt> <tt>broadcast_render_later</tt>, and <tt>broadcast_render_later_to</tt>
77
78
  # to render a turbo stream template with multiple actions.
78
79
  #
80
+ # == Page refreshes
81
+ #
82
+ # You can broadcast "page refresh" stream actions. This will make subscribed clients reload the
83
+ # page. For pages that configure morphing and scroll preservation, this will translate into smooth
84
+ # updates when it only updates the content that changed.
85
+
86
+ # This approach is an alternative to fine-grained stream actions targeting specific DOM elements. It
87
+ # offers good fidelity with a much simpler programming model. As a tradeoff, the fidelity you can reach
88
+ # is often not as high as with targeted stream actions since it renders the entire page again.
89
+ #
90
+ # The +broadcast_refreshes+ class method configures the model to broadcast a "page refresh" on creates,
91
+ # updates, and destroys to a stream name derived at runtime by the <tt>stream</tt> symbol invocation. Examples
92
+ #
93
+ # class Board < ApplicationRecord
94
+ # broadcast_refreshes
95
+ # end
96
+ #
97
+ # In this example, when a board is created, updated, or destroyed, a Turbo Stream for a
98
+ # page refresh will be broadcasted to all clients subscribed to the "boards" stream.
99
+ #
100
+ # This works great in hierarchical structures, where the child record touches parent records automatically
101
+ # to invalidate the cache:
102
+ #
103
+ # class Column < ApplicationRecord
104
+ # belongs_to :board, touch: true # +Board+ will trigger a page refresh on column changes
105
+ # end
106
+ #
107
+ # You can also specify the streamable declaratively by passing a symbol to the +broadcast_refreshes_to+ method:
108
+ #
109
+ # class Column < ApplicationRecord
110
+ # belongs_to :board
111
+ # broadcast_refreshes_to :board
112
+ # end
113
+ #
114
+ # For more granular control, you can also broadcast a "page refresh" to a stream name derived
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
117
+ # a page refresh for more specific scenarios. Example:
118
+ #
119
+ # class Clearance < ApplicationRecord
120
+ # belongs_to :petitioner, class_name: "Contact"
121
+ # belongs_to :examiner, class_name: "User"
122
+ #
123
+ # after_create_commit :broadcast_refresh_later
124
+ #
125
+ # private
126
+ # def broadcast_refresh_later
127
+ # broadcast_refresh_later_to examiner.identity, :clearances
128
+ # end
129
+ # end
130
+ #
131
+ # In this example, a "page refresh" is broadcast to the stream named "identity:<identity-id>:clearances"
132
+ # after a new clearance is created. All clients subscribed to this stream will refresh the page to reflect
133
+ # the changes.
134
+ #
135
+ # When broadcasting page refreshes, Turbo will automatically debounce multiple calls in a row to only broadcast the last one.
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
+ # broadcast them repeatedly and individually.
79
138
  # == Suppressing broadcasts
80
139
  #
81
140
  # Sometimes, you need to disable broadcasts in certain scenarios. You can use <tt>.suppressing_turbo_broadcasts</tt> to create
@@ -136,7 +195,17 @@ module Turbo::Broadcastable
136
195
  end
137
196
 
138
197
  # Configures the model to broadcast a "page refresh" on creates, updates, and destroys to a stream
139
- # name derived at runtime by the <tt>stream</tt> symbol invocation.
198
+ # name derived at runtime by the <tt>stream</tt> symbol invocation. Examples:
199
+ #
200
+ # class Message < ApplicationRecord
201
+ # belongs_to :board
202
+ # broadcasts_refreshes_to :board
203
+ # end
204
+ #
205
+ # class Message < ApplicationRecord
206
+ # belongs_to :board
207
+ # broadcasts_refreshes_to ->(message) { [ message.board, :messages ] }
208
+ # end
140
209
  def broadcasts_refreshes_to(stream)
141
210
  after_commit -> { broadcast_refresh_later_to(stream.try(:call, self) || send(stream)) }
142
211
  end
@@ -293,10 +362,15 @@ module Turbo::Broadcastable
293
362
  broadcast_prepend_to self, target: target, **rendering
294
363
  end
295
364
 
365
+ # Broadcast a "page refresh" to the stream name identified by the passed <tt>streamables</tt>. Example:
366
+ #
367
+ # # Sends <turbo-stream action="refresh"></turbo-stream> to the stream named "identity:2:clearances"
368
+ # clearance.broadcast_refresh_to examiner.identity, :clearances
296
369
  def broadcast_refresh_to(*streamables)
297
370
  Turbo::StreamsChannel.broadcast_refresh_to(*streamables) unless suppressed_turbo_broadcasts?
298
371
  end
299
372
 
373
+ # Same as <tt>#broadcast_refresh_to</tt>, but the designated stream is automatically set to the current model.
300
374
  def broadcast_refresh
301
375
  broadcast_refresh_to self
302
376
  end
@@ -315,7 +389,6 @@ module Turbo::Broadcastable
315
389
  broadcast_action_to self, action: action, target: target, attributes: attributes, **rendering
316
390
  end
317
391
 
318
-
319
392
  # Same as <tt>broadcast_replace_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
320
393
  def broadcast_replace_later_to(*streamables, **rendering)
321
394
  Turbo::StreamsChannel.broadcast_replace_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
@@ -356,10 +429,12 @@ module Turbo::Broadcastable
356
429
  broadcast_prepend_later_to self, target: target, **rendering
357
430
  end
358
431
 
432
+ # Same as <tt>broadcast_refresh_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
359
433
  def broadcast_refresh_later_to(*streamables)
360
434
  Turbo::StreamsChannel.broadcast_refresh_later_to(*streamables, request_id: Turbo.current_request_id) unless suppressed_turbo_broadcasts?
361
435
  end
362
436
 
437
+ # Same as <tt>#broadcast_refresh_later_to</tt>, but the designated stream is automatically set to the current model.
363
438
  def broadcast_refresh_later
364
439
  broadcast_refresh_later_to self
365
440
  end
@@ -390,7 +465,7 @@ module Turbo::Broadcastable
390
465
  #
391
466
  # Note that rendering inline via this method will cause template rendering to happen synchronously. That is usually not
392
467
  # desireable for model callbacks, certainly not if those callbacks are inside of a transaction. Most of the time you should
393
- # be using `broadcast_render_later`, unless you specifically know why synchronous rendering is needed.
468
+ # be using +broadcast_render_later+, unless you specifically know why synchronous rendering is needed.
394
469
  def broadcast_render(**rendering)
395
470
  broadcast_render_to self, **rendering
396
471
  end
@@ -400,12 +475,12 @@ module Turbo::Broadcastable
400
475
  #
401
476
  # Note that rendering inline via this method will cause template rendering to happen synchronously. That is usually not
402
477
  # desireable for model callbacks, certainly not if those callbacks are inside of a transaction. Most of the time you should
403
- # be using `broadcast_render_later_to`, unless you specifically know why synchronous rendering is needed.
478
+ # be using +broadcast_render_later_to+, unless you specifically know why synchronous rendering is needed.
404
479
  def broadcast_render_to(*streamables, **rendering)
405
480
  Turbo::StreamsChannel.broadcast_render_to(*streamables, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
406
481
  end
407
482
 
408
- # Same as <tt>broadcast_action_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
483
+ # Same as <tt>broadcast_render_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
409
484
  def broadcast_render_later(**rendering)
410
485
  broadcast_render_later_to self, **rendering
411
486
  end
@@ -416,7 +491,6 @@ module Turbo::Broadcastable
416
491
  Turbo::StreamsChannel.broadcast_render_later_to(*streamables, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
417
492
  end
418
493
 
419
-
420
494
  private
421
495
  def broadcast_target_default
422
496
  self.class.broadcast_target_default
@@ -11,14 +11,14 @@ module Turbo
11
11
 
12
12
  # Asserts that `<turbo-stream>` elements were broadcast over Action Cable
13
13
  #
14
- # === Arguments
14
+ # ==== Arguments
15
15
  #
16
16
  # * <tt>stream_name_or_object</tt> the objects used to generate the
17
17
  # channel Action Cable name, or the name itself
18
18
  # * <tt>&block</tt> optional block executed before the
19
19
  # assertion
20
20
  #
21
- # === Options
21
+ # ==== Options
22
22
  #
23
23
  # * <tt>count:</tt> the number of `<turbo-stream>` elements that are
24
24
  # expected to be broadcast
@@ -70,7 +70,7 @@ module Turbo
70
70
 
71
71
  # Asserts that no `<turbo-stream>` elements were broadcast over Action Cable
72
72
  #
73
- # === Arguments
73
+ # ==== Arguments
74
74
  #
75
75
  # * <tt>stream_name_or_object</tt> the objects used to generate the
76
76
  # channel Action Cable name, or the name itself
@@ -113,7 +113,7 @@ module Turbo
113
113
 
114
114
  # Captures any `<turbo-stream>` elements that were broadcast over Action Cable
115
115
  #
116
- # === Arguments
116
+ # ==== Arguments
117
117
  #
118
118
  # * <tt>stream_name_or_object</tt> the objects used to generate the
119
119
  # channel Action Cable name, or the name itself
data/lib/turbo/engine.rb CHANGED
@@ -84,6 +84,7 @@ module Turbo
84
84
  require "turbo/broadcastable/test_helper"
85
85
 
86
86
  include Turbo::TestAssertions
87
+ include Turbo::Broadcastable::TestHelper
87
88
  end
88
89
 
89
90
  ActiveSupport.on_load(:action_dispatch_integration_test) do
@@ -4,7 +4,7 @@ module Turbo
4
4
  # Assert that the Turbo Stream request's response body's HTML contains a
5
5
  # `<turbo-stream>` element.
6
6
  #
7
- # === Options
7
+ # ==== Options
8
8
  #
9
9
  # * <tt>:status</tt> [Integer, Symbol] the HTTP response status
10
10
  # * <tt>:action</tt> [String] matches the element's <tt>[action]</tt>
@@ -47,7 +47,7 @@ module Turbo
47
47
  # Assert that the Turbo Stream request's response body's HTML does not
48
48
  # contain a `<turbo-stream>` element.
49
49
  #
50
- # === Options
50
+ # ==== Options
51
51
  #
52
52
  # * <tt>:status</tt> [Integer, Symbol] the HTTP response status
53
53
  # * <tt>:action</tt> [String] matches the element's <tt>[action]</tt>
@@ -10,7 +10,7 @@ module Turbo
10
10
  # Assert that the rendered fragment of HTML contains a `<turbo-stream>`
11
11
  # element.
12
12
  #
13
- # === Options
13
+ # ==== Options
14
14
  #
15
15
  # * <tt>:action</tt> [String] matches the element's <tt>[action]</tt>
16
16
  # attribute
@@ -55,7 +55,7 @@ module Turbo
55
55
  # Assert that the rendered fragment of HTML does not contain a `<turbo-stream>`
56
56
  # element.
57
57
  #
58
- # === Options
58
+ # ==== Options
59
59
  #
60
60
  # * <tt>:action</tt> [String] matches the element's <tt>[action]</tt>
61
61
  # attribute
data/lib/turbo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Turbo
2
- VERSION = "2.0.1"
2
+ VERSION = "2.0.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.2
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-02-08 00:00:00.000000000 Z
13
+ date: 2024-02-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activejob