ruby-on-railsrubyasset-pipelinecontent-for

Ruby on Rails: provide vs content_for


I came across the view helper function "provide" today. By looking into its manual I am still confused on how it is different from "content_for".

provide(name, content = nil, &block)

The same as content_for but when used with streaming flushes straight back to the layout. In other words, if you want to concatenate several times to the same buffer when rendering a given template, you should use content_for, if not, use provide to tell the layout to stop looking for more contents.

Question 1: this is quite abstract to me - could anyone flesh it out by giving a demonstrative example?

Question 2: working with asset pipeline, which performs better and why?

Thanks!


Solution

  • First of all, what is streaming? Why would you use it?

    Streaming is alternate method of rendering pages top-down (outside-in). The default rendering behavior is inside-out. Streaming must be enabled in your controller:

    class MyController
      def action
        render stream: true # Streaming enabled
      end
    end
    

    According to the documentation:

    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.

    So, if you're not using streaming, is there still a difference?

    Yes.

    The difference is a template can define multiple content blocks by calling content_for multiple times. Doing so will concatenate the blocks and pass that to the layout:

    # layout.html.erb
    <div class="heading"><%= yield :surprise %></div>
    <div class="body">
       <p><%= yield %></p>
       <p>But it's not very interesting...</p>
    </div>
    
    # template.html.erb
    <%= content_for :surprise, "Hello" %>
    I've got your content!
    <%= content_for :surprise, ", World!" %>
    
    # Generated HTML
    <div class="heading">Hello, World!</div>
    <div class="body">
       <p>I've got your content!</p>
       <p>But it's not very interesting...</p>
    </div>
    

    Since provide doesn't continue searching the provided template, only the block passed to the first provide call will be sent to the template:

    # layout.html.erb
    <div class="heading"><%= yield :title %></div>
    
    # template.html.erb
    <%= provide :title, "Foo" %>
    <%= provide :title, "bar" %>
    
    # Generated HTML
    <div class="heading">Foo</div>