Skip to content
This repository
tag: v3.1.0.rc1
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 204 lines (197 sloc) 7.407 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/output_safety'

module ActionView
  # = Action View Capture Helper
  module Helpers
    # CaptureHelper exposes methods to let you extract generated markup which
    # can be used in other parts of a template or layout file.
    #
    # It provides a method to capture blocks into variables through capture and
    # a way to capture a block of markup for use in a layout through content_for.
    module CaptureHelper
      # The capture method allows you to extract part of a template into a
      # variable. You can then use this variable anywhere in your templates or layout.
      #
      # ==== Examples
      # The capture method can be used in ERB templates...
      #
      # <% @greeting = capture do %>
      # Welcome to my shiny new web page! The date and time is
      # <%= Time.now %>
      # <% end %>
      #
      # ...and Builder (RXML) templates.
      #
      # @timestamp = capture do
      # "The current timestamp is #{Time.now}."
      # end
      #
      # You can then use that variable anywhere else. For example:
      #
      # <html>
      # <head><title><%= @greeting %></title></head>
      # <body>
      # <b><%= @greeting %></b>
      # </body></html>
      #
      def capture(*args)
        value = nil
        buffer = with_output_buffer { value = yield(*args) }
        if string = buffer.presence || value and string.is_a?(String)
          ERB::Util.html_escape string
        end
      end

      # Calling content_for stores a block of markup in an identifier for later use.
      # You can make subsequent calls to the stored content in other templates, helper modules
      # or the layout by passing the identifier as an argument to <tt>content_for</tt>.
      #
      # Note: <tt>yield</tt> can still be used to retrieve the stored content, but calling
      # <tt>yield</tt> doesn't work in helper modules, while <tt>content_for</tt> does.
      #
      # ==== Examples
      #
      # <% content_for :not_authorized do %>
      # alert('You are not authorized to do that!')
      # <% end %>
      #
      # You can then use <tt>content_for :not_authorized</tt> anywhere in your templates.
      #
      # <%= content_for :not_authorized if current_user.nil? %>
      #
      # This is equivalent to:
      #
      # <%= yield :not_authorized if current_user.nil? %>
      #
      # <tt>content_for</tt>, however, can also be used in helper modules.
      #
      # module StorageHelper
      # def stored_content
      # content_for(:storage) || "Your storage is empty"
      # end
      # end
      #
      # This helper works just like normal helpers.
      #
      # <%= stored_content %>
      #
      # You can use the <tt>yield</tt> syntax alongside an existing call to <tt>yield</tt> in a layout. For example:
      #
      # <%# This is the layout %>
      # <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
      # <head>
      # <title>My Website</title>
      # <%= yield :script %>
      # </head>
      # <body>
      # <%= yield %>
      # </body>
      # </html>
      #
      # And now, we'll create a view that has a <tt>content_for</tt> call that
      # creates the <tt>script</tt> identifier.
      #
      # <%# This is our view %>
      # Please login!
      #
      # <% content_for :script do %>
      # <script type="text/javascript">alert('You are not authorized to view this page!')</script>
      # <% end %>
      #
      # Then, in another view, you could to do something like this:
      #
      # <%= link_to 'Logout', :action => 'logout', :remote => true %>
      #
      # <% content_for :script do %>
      # <%= javascript_include_tag :defaults %>
      # <% end %>
      #
      # That will place +script+ tags for your default set of JavaScript files on the page;
      # this technique is useful if you'll only be using these scripts in a few views.
      #
      # Note that content_for concatenates the blocks it is given for a particular
      # identifier in order. For example:
      #
      # <% content_for :navigation do %>
      # <li><%= link_to 'Home', :action => 'index' %></li>
      # <% end %>
      #
      # <%# Add some other content, or use a different template: %>
      #
      # <% content_for :navigation do %>
      # <li><%= link_to 'Login', :action => 'login' %></li>
      # <% end %>
      #
      # Then, in another template or layout, this code would render both links in order:
      #
      # <ul><%= content_for :navigation %></ul>
      #
      # Lastly, simple content can be passed as a parameter:
      #
      # <% content_for :script, javascript_include_tag(:defaults) %>
      #
      # WARNING: content_for is ignored in caches. So you shouldn't use it
      # for elements that will be fragment cached.
      def content_for(name, content = nil, &block)
        content = capture(&block) if block_given?
        if content
          @view_flow.append(name, content)
          nil
        else
          @view_flow.get(name)
        end
      end

      # 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.
      def provide(name, content = nil, &block)
        content = capture(&block) if block_given?
        result = @view_flow.append!(name, content) if content
        result unless content
      end

      # content_for? simply checks whether any content has been captured yet using content_for
      # Useful to render parts of your layout differently based on what is in your views.
      #
      # ==== Examples
      #
      # Perhaps you will use different css in you layout if no content_for :right_column
      #
      # <%# This is the layout %>
      # <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
      # <head>
      # <title>My Website</title>
      # <%= yield :script %>
      # </head>
      # <body class="<%= content_for?(:right_col) ? 'one-column' : 'two-column' %>">
      # <%= yield %>
      # <%= yield :right_col %>
      # </body>
      # </html>
      def content_for?(name)
        @view_flow.get(name).present?
      end

      # Use an alternate output buffer for the duration of the block.
      # Defaults to a new empty string.
      def with_output_buffer(buf = nil) #:nodoc:
        unless buf
          buf = ActionView::OutputBuffer.new
          buf.force_encoding(output_buffer.encoding) if output_buffer.respond_to?(:encoding) && buf.respond_to?(:force_encoding)
        end
        self.output_buffer, old_buffer = buf, output_buffer
        yield
        output_buffer
      ensure
        self.output_buffer = old_buffer
      end

      # Add the output buffer to the response body and start a new one.
      def flush_output_buffer #:nodoc:
        if output_buffer && !output_buffer.empty?
          response.body_parts << output_buffer
          self.output_buffer = output_buffer[0,0]
          nil
        end
      end
    end
  end
end
Something went wrong with that request. Please try again.