# frozen_string_literal: true require "rack/chunked" module ActionController # :nodoc: # Allows views to be streamed back to the client as they are rendered. # # By default, Rails renders views by first rendering the template # and then the layout. The response is sent to the client after the whole # template is rendered, all queries are made, and the layout is processed. # # Streaming inverts the rendering flow by rendering the layout first and # streaming each part of the layout as they are processed. This allows the # header of the HTML (which is usually in the layout) to be streamed back # to client very quickly, allowing JavaScripts and stylesheets to be loaded # earlier than usual. # # This approach was introduced in Rails 3.1 and is still improving. Several # Rack middlewares may not work and you need to be careful when streaming. # Those points are going to be addressed soon. # # In order to use streaming, you will need to use a Ruby version that # supports fibers (fibers are supported since version 1.9.2 of the main # Ruby implementation). # # Streaming can be added to a given template easily, all you need to do is # to pass the +:stream+ option. # # class PostsController # def index # @posts = Post.all # render stream: true # end # end # # == When to use streaming # # Streaming may be considered to be overkill for lightweight actions like # +new+ or +edit+. The real benefit of streaming is on expensive actions # that, for example, do a lot of queries on the database. # # In such actions, you want to delay queries execution as much as you can. # For example, imagine the following +dashboard+ action: # # def dashboard # @posts = Post.all # @pages = Page.all # @articles = Article.all # end # # Most of the queries here are happening in the controller. In order to benefit # from streaming you would want to rewrite it as: # # def dashboard # # Allow lazy execution of the queries # @posts = Post.all # @pages = Page.all # @articles = Article.all # render stream: true # end # # Notice that +:stream+ only works with templates. Rendering +:json+ # or +:xml+ with +:stream+ won't work. # # == Communication between layout and template # # When streaming, rendering happens top-down instead of inside-out. # Rails starts with the layout, and the template is rendered later, # when its +yield+ is reached. # # This means that, if your application currently relies on instance # variables set in the template to be used in the layout, they won't # work once you move to streaming. The proper way to communicate # between layout and template, regardless of whether you use streaming # or not, is by using +content_for+, +provide+, and +yield+. # # Take a simple example where the layout expects the template to tell # which title to use: # # #
yield :title
in your layout
# and you want to use streaming, you would have to render the whole template
# (and eventually trigger all queries) before streaming the title and all
# assets, which kills the purpose of streaming. For this purpose, you can use
# a helper called +provide+ that does the same as +content_for+ but tells the
# layout to stop searching for other entries and continue rendering.
#
# For instance, the template above using +provide+ would be:
#
# <%= provide :title, "Main" %>
# Hello
# <%= content_for :title, " page" %>
#
# Giving:
#
#
#