aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view/helpers/capture_helper.rb
blob: 1d6ad560046e5501bb4a78d2d013dc9572cc1e74 (plain) (blame)
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
module ActionView
  module Helpers
    # Capture lets you extract parts of code which
    # can be used in other points of the template or even layout file.
    #
    # == Capturing a block into an instance variable
    #
    #   <% @script = capture do %>
    #     [some html...]
    #   <% end %>
    #
    # == Add javascript to header using content_for
    #
    # content_for("name") is a wrapper for capture which will 
    # make the fragment available by name to a yielding layout or template.
    #
    # layout.rhtml:
    #
    #   <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    #   <head>
    #	    <title>layout with js</title>
    #	    <script type="text/javascript">
    #	      <%= yield :script %>
    #     </script>
    #   </head>
    #   <body>
    #     <%= yield %>
    #   </body>
    #   </html>
    #
    # view.rhtml
    #   
    #   This page shows an alert box!
    #
    #   <% content_for("script") do %>
    #     alert('hello world')
    #   <% end %>
    #
    #   Normal view text
    module CaptureHelper
      # Capture allows you to extract a part of the template into an 
      # instance variable. You can use this instance variable anywhere
      # in your templates and even in your layout. 
      # 
      # Example of capture being used in a .rhtml page:
      # 
      #   <% @greeting = capture do %>
      #     Welcome To my shiny new web page!
      #   <% end %>
      #
      # Example of capture being used in a .rxml page:
      # 
      #   @greeting = capture do
      #     'Welcome To my shiny new web page!'
      #   end
      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 for later use.
      # Subsequently, you can make calls to it by name with <tt>yield</tt>
      # in another template or in the layout. 
      # 
      # Example:
      # 
      #   <% content_for("header") do %>
      #     alert('hello world')
      #   <% end %>
      #
      # You can use yield :header anywhere in your templates.
      #
      #   <%= yield :header %>
      #
      # 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 <tt><%= content_for('footer') %></tt>
      # would be avaiable as <tt><%= @content_for_footer %></tt>. The preferred notation now is
      # <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