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)
         
     |