Skip to content
This repository
tree: dfc15e122a
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 157 lines (146 sloc) 5.593 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
module ActionView
  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 RHTML (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, &block)
        # execute the block
        begin
          buffer = eval(ActionView::Base.erb_variable, block.binding)
        rescue
          buffer = nil
        end
        
        if buffer.nil?
          capture_block(*args, &block).to_s
        else
          capture_erb_with_buffer(buffer, *args, &block).to_s
        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 or the layout
      # by passing the identifier as an argument to <tt>yield</tt>.
      #
      # ==== Examples
      #
      # <% content_for :not_authorized do %>
      # alert('You are not authorized to do that!')
      # <% end %>
      #
      # You can then use <tt>yield :not_authorized</tt> anywhere in your templates.
      #
      # <%= yield :not_authorized if current_user.nil? %>
      #
      # You can also use this 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 content_for 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_remote 'Logout', :action => 'logout' %>
      #
      # <% content_for :script do %>
      # <%= javascript_include_tag :defaults %>
      # <% end %>
      #
      # That will place <script> tags for Prototype, Scriptaculous, and application.js (if it exists)
      # on the page; this technique is useful if you'll only be using these scripts in a few views.
      #
      # Also, 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><%= yield :navigation %></ul>
      #
      # WARNING: content_for is ignored in caches. So you shouldn't use it
      # for elements that will be fragment cached.
      #
      # The deprecated way of accessing a content_for block is to use an instance variable
      # named <tt>@content_for_#{name_of_the_content_block}</tt>. So <tt><%= content_for :footer %></tt>
      # would be avaiable as <tt><%= @content_for_footer %></tt>. The preferred usage is now
      # <tt><%= yield :footer %></tt>.
      def content_for(name, content = nil, &block)
        eval "@content_for_#{name} = (@content_for_#{name} || '') + capture(&block)"
      end

      private
        def capture_block(*args, &block)
          block.call(*args)
        end
      
        def capture_erb(*args, &block)
          buffer = eval(ActionView::Base.erb_variable, block.binding)
          capture_erb_with_buffer(buffer, *args, &block)
        end
      
        def capture_erb_with_buffer(buffer, *args, &block)
          pos = buffer.length
          block.call(*args)
        
          # extract the block
          data = buffer[pos..-1]
        
          # replace it in the original with empty string
          buffer[pos..-1] = ''
        
          data
        end
      
        def erb_content_for(name, &block)
          eval "@content_for_#{name} = (@content_for_#{name} || '') + capture_erb(&block)"
        end
      
        def block_content_for(name, &block)
          eval "@content_for_#{name} = (@content_for_#{name} || '') + capture_block(&block)"
        end
    end
  end
end
Something went wrong with that request. Please try again.