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 code for use in a layout through content_for. module CaptureHelper # The capture method allows you to extract a part of the template into a # variable. You can then use this value 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 the content as a variable anywhere else. For # example: # # # <%= @greeting %> # # <%= @greeting %> # # 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 the block of markup in an identifier for later use. # You can make subsequent calls to the stored content in another template or in the layout # by calling it by name with yield. # # ==== Examples # # <% content_for("authorized") do %> # alert('You are not authorized for that!') # <% end %> # # You can then use yield :authorized anywhere in your templates. # # <%= yield :authorized if current_user == nil %> # # You can also use these variables in a layout. For example: # # # # # My Website # <%= yield :script %> # # # <%= yield %> # # # # And now we'll create a view that has a content_for call that # creates the script identifier. # # # Please login! # # <% content_for("script") do %> # # <% end %> # # Then in another view you may want to do something like this: # # <%= link_to_remote 'Logout', :action => 'logout' %> # # <% content_for("script") do %> # <%= javascript_include_tag :defaults %> # <% end %> # # That will include Prototype and Scriptaculous into the page; this technique # is useful if you'll only be using these scripts on a few views. # # NOTE: Beware that content_for is ignored in caches. So you shouldn't use it # for elements that are going to be fragment cached. # # The deprecated way of accessing a content_for block was to use a instance variable # named @@content_for_#{name_of_the_content_block}@. So <%= content_for('footer') %> # would be avaiable as <%= @content_for_footer %>. The preferred notation now is # <%= yield :footer %>. 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