template_streaming 0.0.11 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +9 -0
 - data/README.markdown +177 -88
 - data/Rakefile +0 -21
 - data/lib/template_streaming.rb +184 -99
 - data/lib/template_streaming/autoflushing.rb +88 -0
 - data/lib/template_streaming/caching.rb +68 -0
 - data/lib/template_streaming/error_recovery.rb +199 -85
 - data/lib/template_streaming/new_relic.rb +555 -0
 - data/lib/template_streaming/templates/errors.erb +37 -0
 - data/lib/template_streaming/version.rb +1 -1
 - data/rails/init.rb +3 -0
 - data/spec/autoflushing_spec.rb +75 -0
 - data/spec/caching_spec.rb +126 -0
 - data/spec/error_recovery_spec.rb +261 -0
 - data/spec/spec_helper.rb +13 -0
 - data/spec/support/streaming_app.rb +135 -0
 - data/spec/template_streaming_spec.rb +926 -0
 - metadata +55 -27
 
    
        data/CHANGELOG
    CHANGED
    
    | 
         @@ -1,3 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            == 0.1.0 2011-05-16
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
             * Replace prelayouts with :stream => true option to #render.
         
     | 
| 
      
 4 
     | 
    
         
            +
             * Controller-level #stream to stream selected actions.
         
     | 
| 
      
 5 
     | 
    
         
            +
             * Inject errors during rendering to end of body.
         
     | 
| 
      
 6 
     | 
    
         
            +
             * Autoflushing.
         
     | 
| 
      
 7 
     | 
    
         
            +
             * Fix page and action caching for streamed responses.
         
     | 
| 
      
 8 
     | 
    
         
            +
             * New Relic support.
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       1 
10 
     | 
    
         
             
            == 0.0.11 2010-05-10
         
     | 
| 
       2 
11 
     | 
    
         | 
| 
       3 
12 
     | 
    
         
             
             * Ensure authenticity token is initialized properly.
         
     | 
    
        data/README.markdown
    CHANGED
    
    | 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Template Streaming
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            Progressive rendering for Rails.
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            ## Background
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
         @@ -8,33 +8,45 @@ A typical Rails client-side profile looks something like this: 
     | 
|
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            ![Typical Rails Profile][slow-profile]
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 11 
     | 
    
         
            +
            This is highly suboptimal. Many resources, such as external stylesheets, are
         
     | 
| 
      
 12 
     | 
    
         
            +
            completely static and could be loaded by the client while it's waiting for the
         
     | 
| 
      
 13 
     | 
    
         
            +
            server response.
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
            The trick is to  
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
            page to become interactive.
         
     | 
| 
      
 15 
     | 
    
         
            +
            The trick is to *stream* the response--flushing the markup for the static
         
     | 
| 
      
 16 
     | 
    
         
            +
            resources to the client before it has rendered the rest of the page. In
         
     | 
| 
      
 17 
     | 
    
         
            +
            addition to being able to render styles and images earlier, the browser can
         
     | 
| 
      
 18 
     | 
    
         
            +
            download javascripts, making the page responsive to input events sooner.
         
     | 
| 
       20 
19 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
            The  
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
            the  
     | 
| 
       24 
     | 
    
         
            -
            simply flush the rendering buffer from a helper method.
         
     | 
| 
      
 20 
     | 
    
         
            +
            The main barrier to this in Rails is that layouts are rendered before the
         
     | 
| 
      
 21 
     | 
    
         
            +
            content of the page. The control flow must thus be altered to render the page in
         
     | 
| 
      
 22 
     | 
    
         
            +
            the order the client needs to receive it - layout first.
         
     | 
| 
       25 
23 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
            Template Streaming circumvents the template rendering order by introducing
         
     | 
| 
       29 
     | 
    
         
            -
            *prelayouts*. A prelayout wraps a layout, and is rendered *before* the layout
         
     | 
| 
       30 
     | 
    
         
            -
            and its content. By using the provided `flush` helper prior to yielding in the
         
     | 
| 
       31 
     | 
    
         
            -
            prelayout, one can now output content early in the rendering process, giving
         
     | 
| 
       32 
     | 
    
         
            -
            profiles that look more like:
         
     | 
| 
      
 24 
     | 
    
         
            +
            With streaming, your profiles can look more like this:
         
     | 
| 
       33 
25 
     | 
    
         | 
| 
       34 
26 
     | 
    
         
             
            ![Progressive Rendering Profile][fast-profile]
         
     | 
| 
       35 
27 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
            [slow-profile]:  
     | 
| 
       37 
     | 
    
         
            -
            [fast-profile]:  
     | 
| 
      
 28 
     | 
    
         
            +
            [slow-profile]: https://github.com/oggy/template_streaming/raw/master/doc/slow-profile.png
         
     | 
| 
      
 29 
     | 
    
         
            +
            [fast-profile]: https://github.com/oggy/template_streaming/raw/master/doc/fast-profile.png
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            ## How
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            Just add the `template_streaming` gem to your application, and add a `stream`
         
     | 
| 
      
 34 
     | 
    
         
            +
            call for the actions you'd like to stream. For example, to stream just the
         
     | 
| 
      
 35 
     | 
    
         
            +
            `index` action of your `HomeController`, it would look like this:
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                class HomeController
         
     | 
| 
      
 38 
     | 
    
         
            +
                  stream :only => :index
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def index
         
     | 
| 
      
 41 
     | 
    
         
            +
                    ...
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
            To stream everything, just add `stream` to your `ApplicationController`.
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
            Now you may pepper `flush` calls strategically throughout your views to force a
         
     | 
| 
      
 48 
     | 
    
         
            +
            flush, such as just after the stylesheet and javascript tags. `flush` may occur
         
     | 
| 
      
 49 
     | 
    
         
            +
            in both templates and their layouts.
         
     | 
| 
       38 
50 
     | 
    
         | 
| 
       39 
51 
     | 
    
         
             
            ## API
         
     | 
| 
       40 
52 
     | 
    
         | 
| 
         @@ -48,9 +60,12 @@ This has several implications: 
     | 
|
| 
       48 
60 
     | 
    
         
             
             * Anything that needs to inspect or modify the body should be moved to a
         
     | 
| 
       49 
61 
     | 
    
         
             
               middleware.
         
     | 
| 
       50 
62 
     | 
    
         
             
             * Modifications to cookies (this includes the flash and session if using the
         
     | 
| 
       51 
     | 
    
         
            -
               cookie store 
     | 
| 
       52 
     | 
    
         
            -
              
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
      
 63 
     | 
    
         
            +
               cookie store) must not be made in the view. In fact, these objects will be
         
     | 
| 
      
 64 
     | 
    
         
            +
               frozen when streaming.
         
     | 
| 
      
 65 
     | 
    
         
            +
             * An exception during rendering cannot result in a 500 response, as the headers
         
     | 
| 
      
 66 
     | 
    
         
            +
               will have already been sent. Instead, the innermost partial which contains an
         
     | 
| 
      
 67 
     | 
    
         
            +
               error will simply render nothing, and error information is injected into the
         
     | 
| 
      
 68 
     | 
    
         
            +
               foot of the page in development mode.
         
     | 
| 
       54 
69 
     | 
    
         | 
| 
       55 
70 
     | 
    
         
             
            ### Helpers
         
     | 
| 
       56 
71 
     | 
    
         | 
| 
         @@ -58,32 +73,76 @@ This has several implications: 
     | 
|
| 
       58 
73 
     | 
    
         
             
                client immediately.
         
     | 
| 
       59 
74 
     | 
    
         
             
             * `push(data)` - send the given data to the client immediately.
         
     | 
| 
       60 
75 
     | 
    
         | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
      
 76 
     | 
    
         
            +
            ## Support
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
            Template Streaming currently only supports Rails 2.3.11. Rails 3.0 support is
         
     | 
| 
      
 79 
     | 
    
         
            +
            planned in the near future. Rails 3.1 will ship with support for streaming. This
         
     | 
| 
      
 80 
     | 
    
         
            +
            gem will be updated to meet the API of Rails 3.1 as it evolves, to help you
         
     | 
| 
      
 81 
     | 
    
         
            +
            migrate.
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
            Streaming also requires a web server that does not buffer Rack responses. It has
         
     | 
| 
      
 84 
     | 
    
         
            +
            been tested **successfully** with [Passenger][passenger], [Unicorn][unicorn],
         
     | 
| 
      
 85 
     | 
    
         
            +
            and [Mongrel][mongrel]. Note that Unicorn requires the `:tcp_nopush => false`
         
     | 
| 
      
 86 
     | 
    
         
            +
            configuration option. [Thin][thin] is only supported if the
         
     | 
| 
      
 87 
     | 
    
         
            +
            [Event Machine Flush][event-machine-flush] gem is installed. WEBrick does
         
     | 
| 
      
 88 
     | 
    
         
            +
            **not** support streaming. [Please send me][contact] your experiences with other
         
     | 
| 
       66 
89 
     | 
    
         
             
            web servers!
         
     | 
| 
       67 
90 
     | 
    
         | 
| 
       68 
     | 
    
         
            -
            [mongrel]: http://github.com/fauna/mongrel
         
     | 
| 
       69 
91 
     | 
    
         
             
            [passenger]: http://www.modrails.com
         
     | 
| 
       70 
     | 
    
         
            -
            [ 
     | 
| 
       71 
     | 
    
         
            -
            [ 
     | 
| 
      
 92 
     | 
    
         
            +
            [unicorn]: http://unicorn.bogomips.org/
         
     | 
| 
      
 93 
     | 
    
         
            +
            [mongrel]: https://github.com/fauna/mongrel
         
     | 
| 
      
 94 
     | 
    
         
            +
            [thin]: https://github.com/macournoyer/thin
         
     | 
| 
      
 95 
     | 
    
         
            +
            [event-machine-flush]: https://github.com/oggy/event_machine_flush
         
     | 
| 
       72 
96 
     | 
    
         
             
            [contact]: mailto:george.ogata@gmail.com
         
     | 
| 
       73 
97 
     | 
    
         | 
| 
       74 
98 
     | 
    
         
             
            ### Controller
         
     | 
| 
       75 
99 
     | 
    
         | 
| 
       76 
     | 
    
         
            -
              
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
      
 100 
     | 
    
         
            +
            Class methods:
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
             * `stream` - stream responses for these actions. Takes `:only` or `:except`
         
     | 
| 
      
 103 
     | 
    
         
            +
               options, like `before_filter`.
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
             * `when_streaming_template` - registers a callback to be called during `render`
         
     | 
| 
      
 106 
     | 
    
         
            +
               when rendering progressively. This is before the body is rendered, or any
         
     | 
| 
      
 107 
     | 
    
         
            +
               data is sent to the client.
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
            Instance methods:
         
     | 
| 
       79 
110 
     | 
    
         | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
      
 111 
     | 
    
         
            +
             * `render` has been modified to accept a `:stream` option. If true, the
         
     | 
| 
      
 112 
     | 
    
         
            +
               response will be streamed, otherwise it won't. This overrides the setting set
         
     | 
| 
      
 113 
     | 
    
         
            +
               by the `stream` method above.
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
            ### Error Recovery
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
            As mentioned above, headers are sent to the client before view rendering starts,
         
     | 
| 
      
 118 
     | 
    
         
            +
            which means it's not possible to send an error response in the event of an
         
     | 
| 
      
 119 
     | 
    
         
            +
            uncaught exception. Instead, the innermost template which raised the error
         
     | 
| 
      
 120 
     | 
    
         
            +
            simply renders nothing. This has the added advantage of minimizing the impact on
         
     | 
| 
      
 121 
     | 
    
         
            +
            your visitors, as the rest of the page will render fine.
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
            When an error is swallowed like this, it is passed to an error hander callback,
         
     | 
| 
      
 124 
     | 
    
         
            +
            which you can set as follows.
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                TemplateStreaming.on_streaming_error do |controller, exception|
         
     | 
| 
      
 127 
     | 
    
         
            +
                  ...
         
     | 
| 
      
 128 
     | 
    
         
            +
                end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
            This is where you should hook in your error notification system. Errors are also
         
     | 
| 
      
 131 
     | 
    
         
            +
            logged to the application log.
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
            In addition, in development mode, error information is injected into the foot of
         
     | 
| 
      
 134 
     | 
    
         
            +
            the page. This is presented over the top of the rendered page, so the result
         
     | 
| 
      
 135 
     | 
    
         
            +
            looks much like when not streaming.
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
            ## Streaming Templates Effectively
         
     | 
| 
       81 
138 
     | 
    
         | 
| 
       82 
139 
     | 
    
         
             
            Conventional wisdom says to put your external stylesheets in the HEAD of your
         
     | 
| 
       83 
140 
     | 
    
         
             
            page, and your external javascripts at the bottom of the BODY (markup in
         
     | 
| 
       84 
141 
     | 
    
         
             
            [HAML][haml]):
         
     | 
| 
       85 
142 
     | 
    
         | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
      
 143 
     | 
    
         
            +
            [haml]: http://haml-lang.com
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
            ### `app/views/layouts/application.html.haml`
         
     | 
| 
       87 
146 
     | 
    
         | 
| 
       88 
147 
     | 
    
         
             
                !!! 5
         
     | 
| 
       89 
148 
     | 
    
         
             
                %html
         
     | 
| 
         @@ -91,65 +150,101 @@ page, and your external javascripts at the bottom of the BODY (markup in 
     | 
|
| 
       91 
150 
     | 
    
         
             
                    = stylesheet_link_tag 'one'
         
     | 
| 
       92 
151 
     | 
    
         
             
                    = stylesheet_link_tag 'two'
         
     | 
| 
       93 
152 
     | 
    
         
             
                 - flush
         
     | 
| 
       94 
     | 
    
         
            -
                  
     | 
| 
      
 153 
     | 
    
         
            +
                 %body
         
     | 
| 
      
 154 
     | 
    
         
            +
                   = yield
         
     | 
| 
      
 155 
     | 
    
         
            +
                   = javascript_include_tag 'one'
         
     | 
| 
      
 156 
     | 
    
         
            +
                   = javascript_include_tag 'two'
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
            When streaming, however, you can do better: put the javascripts at the top of
         
     | 
| 
      
 159 
     | 
    
         
            +
            the page too, and fetch them *asynchronously*. This can be done by appending a
         
     | 
| 
      
 160 
     | 
    
         
            +
            script tag to the HEAD of the page in a small piece of inline javascript:
         
     | 
| 
       95 
161 
     | 
    
         | 
| 
       96 
162 
     | 
    
         
             
            ### `app/views/layouts/application.html.haml`
         
     | 
| 
       97 
163 
     | 
    
         | 
| 
      
 164 
     | 
    
         
            +
                !!! 5
         
     | 
| 
      
 165 
     | 
    
         
            +
                %html
         
     | 
| 
      
 166 
     | 
    
         
            +
                  %head
         
     | 
| 
      
 167 
     | 
    
         
            +
                    = stylesheet_link_tag 'one'
         
     | 
| 
      
 168 
     | 
    
         
            +
                    = stylesheet_link_tag 'two'
         
     | 
| 
      
 169 
     | 
    
         
            +
                    = javascript_tag do
         
     | 
| 
      
 170 
     | 
    
         
            +
                      = File.read(Rails.public_path + '/javascripts/get_script.js')
         
     | 
| 
      
 171 
     | 
    
         
            +
                      $.getScript('#{javascript_path('jquery')}');
         
     | 
| 
      
 172 
     | 
    
         
            +
                      $.getScript('#{javascript_path('application')}');
         
     | 
| 
       98 
173 
     | 
    
         
             
                %body
         
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
      
 174 
     | 
    
         
            +
                    - flush
         
     | 
| 
      
 175 
     | 
    
         
            +
                    = yield
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
            ### `public/javascripts/get_script.js`
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                //
         
     | 
| 
      
 180 
     | 
    
         
            +
                // Credit: Sam Cole [https://gist.github.com/364746]
         
     | 
| 
      
 181 
     | 
    
         
            +
                //
         
     | 
| 
      
 182 
     | 
    
         
            +
                window.$ = {
         
     | 
| 
      
 183 
     | 
    
         
            +
                  getScript: function(script_src, callback) {
         
     | 
| 
      
 184 
     | 
    
         
            +
                    var done = false;
         
     | 
| 
      
 185 
     | 
    
         
            +
                    var head = document.getElementsByTagName("head")[0] || document.documentElement;
         
     | 
| 
      
 186 
     | 
    
         
            +
                    var script = document.createElement("script");
         
     | 
| 
      
 187 
     | 
    
         
            +
                    script.src = script_src;
         
     | 
| 
      
 188 
     | 
    
         
            +
                    script.onload = script.onreadystatechange = function() {
         
     | 
| 
      
 189 
     | 
    
         
            +
                      if ( !done && (!this.readyState ||
         
     | 
| 
      
 190 
     | 
    
         
            +
                          this.readyState === "loaded" || this.readyState === "complete") ) {
         
     | 
| 
      
 191 
     | 
    
         
            +
                        if(callback) callback();
         
     | 
| 
       102 
192 
     | 
    
         | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
      
 193 
     | 
    
         
            +
                        // Handle memory leak in IE
         
     | 
| 
      
 194 
     | 
    
         
            +
                        script.onload = script.onreadystatechange = null;
         
     | 
| 
      
 195 
     | 
    
         
            +
                        if ( head && script.parentNode ) {
         
     | 
| 
      
 196 
     | 
    
         
            +
                          head.removeChild( script );
         
     | 
| 
      
 197 
     | 
    
         
            +
                        }
         
     | 
| 
       108 
198 
     | 
    
         | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
             
     | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
      
 199 
     | 
    
         
            +
                        done = true;
         
     | 
| 
      
 200 
     | 
    
         
            +
                      }
         
     | 
| 
      
 201 
     | 
    
         
            +
                    };
         
     | 
| 
      
 202 
     | 
    
         
            +
                    head.insertBefore( script, head.firstChild );
         
     | 
| 
      
 203 
     | 
    
         
            +
                  }
         
     | 
| 
      
 204 
     | 
    
         
            +
                };
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
            If you have inline javascript that depends on the fetched scripts, you'll need
         
     | 
| 
      
 207 
     | 
    
         
            +
            to delay its execution until the scripts have been run. You can do this by
         
     | 
| 
      
 208 
     | 
    
         
            +
            wrapping the javascript in a function, with a guard which will delay execution
         
     | 
| 
      
 209 
     | 
    
         
            +
            until the script is loaded, unless the script has already been loaded. Example:
         
     | 
| 
       114 
210 
     | 
    
         | 
| 
       115 
     | 
    
         
            -
            ###  
     | 
| 
      
 211 
     | 
    
         
            +
            ### Layout
         
     | 
| 
       116 
212 
     | 
    
         | 
| 
       117 
213 
     | 
    
         
             
                !!! 5
         
     | 
| 
       118 
214 
     | 
    
         
             
                %html
         
     | 
| 
       119 
215 
     | 
    
         
             
                  %head
         
     | 
| 
       120 
     | 
    
         
            -
                    = define_get_script
         
     | 
| 
       121 
216 
     | 
    
         
             
                    = stylesheet_link_tag 'one'
         
     | 
| 
       122 
217 
     | 
    
         
             
                    = stylesheet_link_tag 'two'
         
     | 
| 
       123 
     | 
    
         
            -
                    =  
     | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
      
 218 
     | 
    
         
            +
                    = javascript_tag do
         
     | 
| 
      
 219 
     | 
    
         
            +
                      = File.read(Rails.public_path + '/javascripts/get_script.js')
         
     | 
| 
      
 220 
     | 
    
         
            +
                      $.getScript('#{javascript_path('jquery')}', function() {
         
     | 
| 
      
 221 
     | 
    
         
            +
                        window.script_loaded = 1;
         
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
      
 223 
     | 
    
         
            +
                        // If the inline code has been loaded (but not yet run), run it
         
     | 
| 
      
 224 
     | 
    
         
            +
                        // now. Otherwise, it will be run immediately when it's available.
         
     | 
| 
      
 225 
     | 
    
         
            +
                        if (window.inline)
         
     | 
| 
      
 226 
     | 
    
         
            +
                          inline();
         
     | 
| 
      
 227 
     | 
    
         
            +
                      });
         
     | 
| 
       130 
228 
     | 
    
         
             
                %body
         
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
      
 229 
     | 
    
         
            +
                    - flush
         
     | 
| 
      
 230 
     | 
    
         
            +
                    = yield
         
     | 
| 
       132 
231 
     | 
    
         | 
| 
       133 
     | 
    
         
            -
            ###  
     | 
| 
      
 232 
     | 
    
         
            +
            ### View
         
     | 
| 
       134 
233 
     | 
    
         | 
| 
       135 
     | 
    
         
            -
                 
     | 
| 
       136 
     | 
    
         
            -
                   
     | 
| 
       137 
     | 
    
         
            -
                     
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
                    end
         
     | 
| 
       140 
     | 
    
         
            -
                  end
         
     | 
| 
      
 234 
     | 
    
         
            +
                - javascript_tag do
         
     | 
| 
      
 235 
     | 
    
         
            +
                  window.inline() {
         
     | 
| 
      
 236 
     | 
    
         
            +
                    // ... inline javascript code ...
         
     | 
| 
      
 237 
     | 
    
         
            +
                  }
         
     | 
| 
       141 
238 
     | 
    
         | 
| 
       142 
     | 
    
         
            -
                   
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
                     
     | 
| 
       146 
     | 
    
         
            -
                  end
         
     | 
| 
       147 
     | 
    
         
            -
                end
         
     | 
| 
      
 239 
     | 
    
         
            +
                  // If the script is already loaded, run it now. Otherwise, the callback
         
     | 
| 
      
 240 
     | 
    
         
            +
                  // above will run it after the script is loaded.
         
     | 
| 
      
 241 
     | 
    
         
            +
                  if (window.script_loaded)
         
     | 
| 
      
 242 
     | 
    
         
            +
                    inline();
         
     | 
| 
       148 
243 
     | 
    
         | 
| 
       149 
     | 
    
         
            -
            ### `public/javascripts/get_script.js`
         
     | 
| 
      
 244 
     | 
    
         
            +
            ### In `public/javascripts/get_script.js`
         
     | 
| 
       150 
245 
     | 
    
         | 
| 
       151 
246 
     | 
    
         
             
                //
         
     | 
| 
       152 
     | 
    
         
            -
                //  
     | 
| 
      
 247 
     | 
    
         
            +
                // Credit: Sam Cole [https://gist.github.com/364746]
         
     | 
| 
       153 
248 
     | 
    
         
             
                //
         
     | 
| 
       154 
249 
     | 
    
         
             
                window.$ = {
         
     | 
| 
       155 
250 
     | 
    
         
             
                  getScript: function(script_src, callback) {
         
     | 
| 
         @@ -175,20 +270,14 @@ script asynchronously, and then appends the script tag to the HEAD. 
     | 
|
| 
       175 
270 
     | 
    
         
             
                  }
         
     | 
| 
       176 
271 
     | 
    
         
             
                };
         
     | 
| 
       177 
272 
     | 
    
         | 
| 
       178 
     | 
    
         
            -
             
     | 
| 
       179 
     | 
    
         
            -
             
     | 
| 
       180 
     | 
    
         
            -
            [haml]: http://haml-lang.com
         
     | 
| 
       181 
     | 
    
         
            -
            [stefanov]: http://www.yuiblog.com/blog/2008/07/22/non-blocking-scripts
         
     | 
| 
       182 
     | 
    
         
            -
            [get-script]: http://gist.github.com/364746
         
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
       184 
     | 
    
         
            -
            ## Note on Patches/Pull Requests
         
     | 
| 
      
 273 
     | 
    
         
            +
            ## Contributing
         
     | 
| 
       185 
274 
     | 
    
         | 
| 
       186 
     | 
    
         
            -
             * Bug reports 
     | 
| 
       187 
     | 
    
         
            -
             * Source 
     | 
| 
      
 275 
     | 
    
         
            +
             * [Bug reports](https://github.com/oggy/template_streaming/issues)
         
     | 
| 
      
 276 
     | 
    
         
            +
             * [Source](https://github.com/oggy/template_streaming)
         
     | 
| 
       188 
277 
     | 
    
         
             
             * Patches: Fork on Github, send pull request.
         
     | 
| 
       189 
     | 
    
         
            -
               *  
     | 
| 
      
 278 
     | 
    
         
            +
               * Include tests where practical.
         
     | 
| 
       190 
279 
     | 
    
         
             
               * Leave the version alone, or bump it in a separate commit.
         
     | 
| 
       191 
280 
     | 
    
         | 
| 
       192 
281 
     | 
    
         
             
            ## Copyright
         
     | 
| 
       193 
282 
     | 
    
         | 
| 
       194 
     | 
    
         
            -
            Copyright (c)  
     | 
| 
      
 283 
     | 
    
         
            +
            Copyright (c) George Ogata. See LICENSE for details.
         
     | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -1,22 +1 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            gem 'ritual'
         
     | 
| 
       2 
1 
     | 
    
         
             
            require 'ritual'
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
            spec_task :spec do |t|
         
     | 
| 
       5 
     | 
    
         
            -
              t.libs << 'lib' << 'spec'
         
     | 
| 
       6 
     | 
    
         
            -
              t.spec_files = FileList['spec/**/*_spec.rb']
         
     | 
| 
       7 
     | 
    
         
            -
            end
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
            spec_task :rcov do |t|
         
     | 
| 
       10 
     | 
    
         
            -
              t.libs << 'lib' << 'spec'
         
     | 
| 
       11 
     | 
    
         
            -
              t.pattern = 'spec/**/*_spec.rb'
         
     | 
| 
       12 
     | 
    
         
            -
              t.rcov = true
         
     | 
| 
       13 
     | 
    
         
            -
            end
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
            rdoc_task do |t|
         
     | 
| 
       16 
     | 
    
         
            -
              t.rdoc_dir = 'rdoc'
         
     | 
| 
       17 
     | 
    
         
            -
              t.title = "Template Streaming #{version}"
         
     | 
| 
       18 
     | 
    
         
            -
              t.rdoc_files.include('README*')
         
     | 
| 
       19 
     | 
    
         
            -
              t.rdoc_files.include('lib/**/*.rb')
         
     | 
| 
       20 
     | 
    
         
            -
            end
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
            task :default => :spec
         
     | 
    
        data/lib/template_streaming.rb
    CHANGED
    
    | 
         @@ -5,43 +5,17 @@ module TemplateStreaming 
     | 
|
| 
       5 
5 
     | 
    
         
             
                    send "#{key}=", value
         
     | 
| 
       6 
6 
     | 
    
         
             
                  end
         
     | 
| 
       7 
7 
     | 
    
         
             
                end
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
                #
         
     | 
| 
       10 
     | 
    
         
            -
                # If true, always reference the flash before returning from the
         
     | 
| 
       11 
     | 
    
         
            -
                # action when rendering progressively.
         
     | 
| 
       12 
     | 
    
         
            -
                #
         
     | 
| 
       13 
     | 
    
         
            -
                # This is required for the flash to work with progressive
         
     | 
| 
       14 
     | 
    
         
            -
                # rendering, but unlike standard Rails behavior, will cause the
         
     | 
| 
       15 
     | 
    
         
            -
                # flash to be swept even if it's never referenced in the
         
     | 
| 
       16 
     | 
    
         
            -
                # views. This usually isn't an issue, as flash messages are
         
     | 
| 
       17 
     | 
    
         
            -
                # typically rendered in the layout, causing a reference anyway.
         
     | 
| 
       18 
     | 
    
         
            -
                #
         
     | 
| 
       19 
     | 
    
         
            -
                # Default: true.
         
     | 
| 
       20 
     | 
    
         
            -
                #
         
     | 
| 
       21 
     | 
    
         
            -
                attr_accessor :autosweep_flash
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                #
         
     | 
| 
       24 
     | 
    
         
            -
                # If true, always set the authenticity token before returning from
         
     | 
| 
       25 
     | 
    
         
            -
                # the action when rendering progressively.
         
     | 
| 
       26 
     | 
    
         
            -
                #
         
     | 
| 
       27 
     | 
    
         
            -
                # This is required for the authenticity token to work with
         
     | 
| 
       28 
     | 
    
         
            -
                # progressive rendering, but unlike standard Rails behavior, will
         
     | 
| 
       29 
     | 
    
         
            -
                # cause the token to be set (and thus the session updated) even if
         
     | 
| 
       30 
     | 
    
         
            -
                # it's never referenced in views.
         
     | 
| 
       31 
     | 
    
         
            -
                #
         
     | 
| 
       32 
     | 
    
         
            -
                # Default: true.
         
     | 
| 
       33 
     | 
    
         
            -
                #
         
     | 
| 
       34 
     | 
    
         
            -
                attr_accessor :set_authenticity_token
         
     | 
| 
       35 
8 
     | 
    
         
             
              end
         
     | 
| 
       36 
9 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
               
     | 
| 
       38 
     | 
    
         
            -
              self.set_authenticity_token = true
         
     | 
| 
      
 10 
     | 
    
         
            +
              STREAMING_KEY = 'template_streaming.streaming'.freeze
         
     | 
| 
       39 
11 
     | 
    
         | 
| 
       40 
12 
     | 
    
         
             
              module Controller
         
     | 
| 
       41 
13 
     | 
    
         
             
                def self.included(base)
         
     | 
| 
       42 
14 
     | 
    
         
             
                  base.class_eval do
         
     | 
| 
      
 15 
     | 
    
         
            +
                    extend ClassMethods
         
     | 
| 
       43 
16 
     | 
    
         
             
                    alias_method_chain :render, :template_streaming
         
     | 
| 
       44 
17 
     | 
    
         
             
                    alias_method_chain :render_to_string, :template_streaming
         
     | 
| 
      
 18 
     | 
    
         
            +
                    alias_method_chain :flash, :template_streaming
         
     | 
| 
       45 
19 
     | 
    
         
             
                    helper_method :flush, :push
         
     | 
| 
       46 
20 
     | 
    
         | 
| 
       47 
21 
     | 
    
         
             
                    include ActiveSupport::Callbacks
         
     | 
| 
         @@ -49,11 +23,39 @@ module TemplateStreaming 
     | 
|
| 
       49 
23 
     | 
    
         
             
                  end
         
     | 
| 
       50 
24 
     | 
    
         
             
                end
         
     | 
| 
       51 
25 
     | 
    
         | 
| 
      
 26 
     | 
    
         
            +
                module ClassMethods
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def stream(options={})
         
     | 
| 
      
 28 
     | 
    
         
            +
                    before_filter :action_streams, options
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def action_streams
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @action_streams = true
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def action_streams?
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @action_streams
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       52 
40 
     | 
    
         
             
                def render_with_template_streaming(*args, &block)
         
     | 
| 
       53 
     | 
    
         
            -
                   
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
      
 41 
     | 
    
         
            +
                  options = args.first { |a| a.is_a?(Hash) }
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if options && options.size == 1 && options.key?(:stream)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    # Need to set the default values, since the standard #render won't.
         
     | 
| 
      
 44 
     | 
    
         
            +
                    options[:template] = default_template
         
     | 
| 
      
 45 
     | 
    
         
            +
                    options[:layout] = true
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                  push_render_stack_frame do |stack_height|
         
     | 
| 
      
 48 
     | 
    
         
            +
                    if start_streaming_template?(stack_height, *args)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      @streaming_template = true
         
     | 
| 
      
 50 
     | 
    
         
            +
                      @template.streaming_template = true
         
     | 
| 
       55 
51 
     | 
    
         
             
                      @performed_render = true
         
     | 
| 
       56 
     | 
    
         
            -
                      @streaming_body = StreamingBody.new( 
     | 
| 
      
 52 
     | 
    
         
            +
                      @streaming_body = StreamingBody.new(template_streaming_threshold) do
         
     | 
| 
      
 53 
     | 
    
         
            +
                        cookies.freeze
         
     | 
| 
      
 54 
     | 
    
         
            +
                        if self.class.session_store.sent_with_headers?
         
     | 
| 
      
 55 
     | 
    
         
            +
                          session.freeze
         
     | 
| 
      
 56 
     | 
    
         
            +
                          flash.freeze
         
     | 
| 
      
 57 
     | 
    
         
            +
                        end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
       57 
59 
     | 
    
         
             
                        @performed_render = false
         
     | 
| 
       58 
60 
     | 
    
         
             
                        last_piece = render_without_template_streaming(*args, &block)
         
     | 
| 
       59 
61 
     | 
    
         
             
                        # The original render will clobber our response.body, so
         
     | 
| 
         @@ -62,33 +64,44 @@ module TemplateStreaming 
     | 
|
| 
       62 
64 
     | 
    
         
             
                      end
         
     | 
| 
       63 
65 
     | 
    
         
             
                      response.body = @streaming_body
         
     | 
| 
       64 
66 
     | 
    
         
             
                      response.prepare!
         
     | 
| 
       65 
     | 
    
         
            -
                       
     | 
| 
       66 
     | 
    
         
            -
                      form_authenticity_token if TemplateStreaming.set_authenticity_token
         
     | 
| 
       67 
     | 
    
         
            -
                      run_callbacks :when_streaming_template
         
     | 
| 
      
 67 
     | 
    
         
            +
                      form_authenticity_token  # generate now
         
     | 
| 
       68 
68 
     | 
    
         | 
| 
       69 
     | 
    
         
            -
                      # Normally,  
     | 
| 
       70 
     | 
    
         
            -
                      # means  
     | 
| 
       71 
     | 
    
         
            -
                      #  
     | 
| 
       72 
     | 
    
         
            -
                      #  
     | 
| 
       73 
     | 
    
         
            -
                      # twice, obliterating its contents.
         
     | 
| 
      
 69 
     | 
    
         
            +
                      # Normally, the flash is swept on first reference. This
         
     | 
| 
      
 70 
     | 
    
         
            +
                      # means we need to ensure it's referenced before the session
         
     | 
| 
      
 71 
     | 
    
         
            +
                      # is persisted. In the case of the cookie store, that's when
         
     | 
| 
      
 72 
     | 
    
         
            +
                      # the headers are sent, so we force a reference now.
         
     | 
| 
       74 
73 
     | 
    
         
             
                      #
         
     | 
| 
       75 
     | 
    
         
            -
                      #  
     | 
| 
       76 
     | 
    
         
            -
                      #  
     | 
| 
       77 
     | 
    
         
            -
                       
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                       
     | 
| 
      
 74 
     | 
    
         
            +
                      # But alas, that's not all. @_flash is removed after
         
     | 
| 
      
 75 
     | 
    
         
            +
                      # #perform_action, which means calling #flash in the view
         
     | 
| 
      
 76 
     | 
    
         
            +
                      # would cause the flash to be referenced again, sweeping the
         
     | 
| 
      
 77 
     | 
    
         
            +
                      # flash a second time. To prevent this, we preserve the
         
     | 
| 
      
 78 
     | 
    
         
            +
                      # flash in a separate ivar, and patch #flash to return this
         
     | 
| 
      
 79 
     | 
    
         
            +
                      # if we're streaming.
         
     | 
| 
      
 80 
     | 
    
         
            +
                      #
         
     | 
| 
      
 81 
     | 
    
         
            +
                      flash  # ensure sweep
         
     | 
| 
      
 82 
     | 
    
         
            +
                      @template_streaming_flash = @_flash
         
     | 
| 
      
 83 
     | 
    
         
            +
                      request.env[STREAMING_KEY] = true
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                      run_callbacks :when_streaming_template
         
     | 
| 
       80 
86 
     | 
    
         
             
                    else
         
     | 
| 
       81 
87 
     | 
    
         
             
                      render_without_template_streaming(*args, &block)
         
     | 
| 
       82 
88 
     | 
    
         
             
                    end
         
     | 
| 
       83 
89 
     | 
    
         
             
                  end
         
     | 
| 
       84 
90 
     | 
    
         
             
                end
         
     | 
| 
       85 
91 
     | 
    
         | 
| 
      
 92 
     | 
    
         
            +
                # Mark the case when it's a layout for a toplevel render. This is
         
     | 
| 
      
 93 
     | 
    
         
            +
                # done here, as it's called after the option wrangling in
         
     | 
| 
      
 94 
     | 
    
         
            +
                # AC::Base#render, and nowhere else.
         
     | 
| 
      
 95 
     | 
    
         
            +
                def pick_layout(options)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  result = super
         
     | 
| 
      
 97 
     | 
    
         
            +
                  options[:toplevel_render_with_layout] = true if result
         
     | 
| 
      
 98 
     | 
    
         
            +
                  result
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
       86 
101 
     | 
    
         
             
                # Override to ensure calling render_to_string from a helper
         
     | 
| 
       87 
102 
     | 
    
         
             
                # doesn't trigger template streaming.
         
     | 
| 
       88 
103 
     | 
    
         
             
                def render_to_string_with_template_streaming(*args, &block) # :nodoc
         
     | 
| 
       89 
     | 
    
         
            -
                   
     | 
| 
       90 
     | 
    
         
            -
                  # top-level.
         
     | 
| 
       91 
     | 
    
         
            -
                  with_template_streaming_condition do
         
     | 
| 
      
 104 
     | 
    
         
            +
                  push_render_stack_frame do
         
     | 
| 
       92 
105 
     | 
    
         
             
                    render_to_string_without_template_streaming(*args, &block)
         
     | 
| 
       93 
106 
     | 
    
         
             
                  end
         
     | 
| 
       94 
107 
     | 
    
         
             
                end
         
     | 
| 
         @@ -98,7 +111,7 @@ module TemplateStreaming 
     | 
|
| 
       98 
111 
     | 
    
         
             
                # immediately.
         
     | 
| 
       99 
112 
     | 
    
         
             
                #
         
     | 
| 
       100 
113 
     | 
    
         
             
                def flush
         
     | 
| 
       101 
     | 
    
         
            -
                   
     | 
| 
      
 114 
     | 
    
         
            +
                  if @streaming_body && !@template.output_buffer.nil?
         
     | 
| 
       102 
115 
     | 
    
         
             
                    push @template.output_buffer.slice!(0..-1)
         
     | 
| 
       103 
116 
     | 
    
         
             
                  end
         
     | 
| 
       104 
117 
     | 
    
         
             
                end
         
     | 
| 
         @@ -107,35 +120,50 @@ module TemplateStreaming 
     | 
|
| 
       107 
120 
     | 
    
         
             
                # Push the given data to the client immediately.
         
     | 
| 
       108 
121 
     | 
    
         
             
                #
         
     | 
| 
       109 
122 
     | 
    
         
             
                def push(data)
         
     | 
| 
       110 
     | 
    
         
            -
                  @streaming_body 
     | 
| 
       111 
     | 
    
         
            -
             
     | 
| 
      
 123 
     | 
    
         
            +
                  if @streaming_body
         
     | 
| 
      
 124 
     | 
    
         
            +
                    @streaming_body.push(data)
         
     | 
| 
      
 125 
     | 
    
         
            +
                    flush_thin
         
     | 
| 
      
 126 
     | 
    
         
            +
                  end
         
     | 
| 
       112 
127 
     | 
    
         
             
                end
         
     | 
| 
       113 
128 
     | 
    
         | 
| 
       114 
129 
     | 
    
         
             
                def template_streaming_flash # :nodoc:
         
     | 
| 
       115 
130 
     | 
    
         
             
                  @template_streaming_flash
         
     | 
| 
       116 
131 
     | 
    
         
             
                end
         
     | 
| 
       117 
132 
     | 
    
         | 
| 
      
 133 
     | 
    
         
            +
                def streaming_template?
         
     | 
| 
      
 134 
     | 
    
         
            +
                  @streaming_template
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
       118 
137 
     | 
    
         
             
                private # --------------------------------------------------------
         
     | 
| 
       119 
138 
     | 
    
         | 
| 
       120 
     | 
    
         
            -
                 
     | 
| 
       121 
     | 
    
         
            -
                # Yield true if we should intercept this render call, false
         
     | 
| 
       122 
     | 
    
         
            -
                # otherwise.
         
     | 
| 
       123 
     | 
    
         
            -
                #
         
     | 
| 
       124 
     | 
    
         
            -
                def with_template_streaming_condition(*args)
         
     | 
| 
      
 139 
     | 
    
         
            +
                def push_render_stack_frame
         
     | 
| 
       125 
140 
     | 
    
         
             
                  @render_stack_height ||= 0
         
     | 
| 
       126 
141 
     | 
    
         
             
                  @render_stack_height += 1
         
     | 
| 
       127 
142 
     | 
    
         
             
                  begin
         
     | 
| 
       128 
     | 
    
         
            -
                     
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
      
 143 
     | 
    
         
            +
                    yield @render_stack_height
         
     | 
| 
      
 144 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 145 
     | 
    
         
            +
                    @render_stack_height -= 1
         
     | 
| 
      
 146 
     | 
    
         
            +
                  end
         
     | 
| 
      
 147 
     | 
    
         
            +
                end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                def start_streaming_template?(render_stack_height, *render_args)
         
     | 
| 
      
 150 
     | 
    
         
            +
                  render_stack_height == 1 or
         
     | 
| 
      
 151 
     | 
    
         
            +
                    return false
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                  return false if rendered_action_cache
         
     | 
| 
       131 
154 
     | 
    
         | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
      
 155 
     | 
    
         
            +
                  (render_options = render_args.last).is_a?(Hash) or
         
     | 
| 
      
 156 
     | 
    
         
            +
                    render_options = {}
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                  if !(UNSTREAMABLE_KEYS & render_options.keys).empty? || render_args.first == :update
         
     | 
| 
      
 159 
     | 
    
         
            +
                    false
         
     | 
| 
      
 160 
     | 
    
         
            +
                  else
         
     | 
| 
      
 161 
     | 
    
         
            +
                    explicit_option = render_options[:stream]
         
     | 
| 
      
 162 
     | 
    
         
            +
                    if explicit_option.nil?
         
     | 
| 
      
 163 
     | 
    
         
            +
                      action_streams?
         
     | 
| 
       134 
164 
     | 
    
         
             
                    else
         
     | 
| 
       135 
     | 
    
         
            -
                       
     | 
| 
      
 165 
     | 
    
         
            +
                      explicit_option
         
     | 
| 
       136 
166 
     | 
    
         
             
                    end
         
     | 
| 
       137 
     | 
    
         
            -
                  ensure
         
     | 
| 
       138 
     | 
    
         
            -
                    @render_stack_height -= 1
         
     | 
| 
       139 
167 
     | 
    
         
             
                  end
         
     | 
| 
       140 
168 
     | 
    
         
             
                end
         
     | 
| 
       141 
169 
     | 
    
         | 
| 
         @@ -145,7 +173,7 @@ module TemplateStreaming 
     | 
|
| 
       145 
173 
     | 
    
         
             
                # The number of bytes that must be received by the client before
         
     | 
| 
       146 
174 
     | 
    
         
             
                # anything will be rendered.
         
     | 
| 
       147 
175 
     | 
    
         
             
                #
         
     | 
| 
       148 
     | 
    
         
            -
                def  
     | 
| 
      
 176 
     | 
    
         
            +
                def template_streaming_threshold
         
     | 
| 
       149 
177 
     | 
    
         
             
                  content_type = response.header['Content-type']
         
     | 
| 
       150 
178 
     | 
    
         
             
                  content_type.nil? || content_type =~ %r'\Atext/html' or
         
     | 
| 
       151 
179 
     | 
    
         
             
                    return 0
         
     | 
| 
         @@ -170,6 +198,15 @@ module TemplateStreaming 
     | 
|
| 
       170 
198 
     | 
    
         
             
                  connection = request.env['template_streaming.thin_connection'] and
         
     | 
| 
       171 
199 
     | 
    
         
             
                    EventMachineFlush.flush(connection)
         
     | 
| 
       172 
200 
     | 
    
         
             
                end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                def flash_with_template_streaming # :nodoc:
         
     | 
| 
      
 203 
     | 
    
         
            +
                  if defined?(@template_streaming_flash)
         
     | 
| 
      
 204 
     | 
    
         
            +
                    # Flash has been swept - don't use the standard #flash or it'll sweep again.
         
     | 
| 
      
 205 
     | 
    
         
            +
                    @template_streaming_flash
         
     | 
| 
      
 206 
     | 
    
         
            +
                  else
         
     | 
| 
      
 207 
     | 
    
         
            +
                    flash_without_template_streaming
         
     | 
| 
      
 208 
     | 
    
         
            +
                  end
         
     | 
| 
      
 209 
     | 
    
         
            +
                end
         
     | 
| 
       173 
210 
     | 
    
         
             
              end
         
     | 
| 
       174 
211 
     | 
    
         | 
| 
       175 
212 
     | 
    
         
             
              # Only prepare once.
         
     | 
| 
         @@ -196,53 +233,83 @@ module TemplateStreaming 
     | 
|
| 
       196 
233 
     | 
    
         | 
| 
       197 
234 
     | 
    
         
             
              module View
         
     | 
| 
       198 
235 
     | 
    
         
             
                def self.included(base)
         
     | 
| 
      
 236 
     | 
    
         
            +
                  base.alias_method_chain :render, :template_streaming
         
     | 
| 
       199 
237 
     | 
    
         
             
                  base.alias_method_chain :_render_with_layout, :template_streaming
         
     | 
| 
       200 
     | 
    
         
            -
                  base.alias_method_chain :flash, :template_streaming
         
     | 
| 
       201 
238 
     | 
    
         
             
                end
         
     | 
| 
       202 
239 
     | 
    
         | 
| 
       203 
     | 
    
         
            -
                def  
     | 
| 
       204 
     | 
    
         
            -
                   
     | 
| 
       205 
     | 
    
         
            -
             
     | 
| 
      
 240 
     | 
    
         
            +
                def render_with_template_streaming(*args, &block)
         
     | 
| 
      
 241 
     | 
    
         
            +
                  options = args.first
         
     | 
| 
      
 242 
     | 
    
         
            +
                  if streaming_template? && options.is_a?(Hash)
         
     | 
| 
      
 243 
     | 
    
         
            +
                    # These branches exist to handle the case where AC::Base#render calls
         
     | 
| 
      
 244 
     | 
    
         
            +
                    # AV::Base#render for rendering a partial with a layout. AC::Base
         
     | 
| 
      
 245 
     | 
    
         
            +
                    # renders the partial then the layout separately, but we need to render
         
     | 
| 
      
 246 
     | 
    
         
            +
                    # them together, in the reverse order (layout first). We do this by
         
     | 
| 
      
 247 
     | 
    
         
            +
                    # standard-rendering the layout with a block that renders the partial.
         
     | 
| 
      
 248 
     | 
    
         
            +
                    if options[:toplevel_render_with_layout] && (partial = options[:partial])
         
     | 
| 
      
 249 
     | 
    
         
            +
                      # Don't render yet - we need to do the layout first.
         
     | 
| 
      
 250 
     | 
    
         
            +
                      options.delete(:toplevel_render_with_layout)
         
     | 
| 
      
 251 
     | 
    
         
            +
                      return DeferredPartialRender.new(args)
         
     | 
| 
      
 252 
     | 
    
         
            +
                    elsif options[:text].is_a?(DeferredPartialRender)
         
     | 
| 
      
 253 
     | 
    
         
            +
                      render = options.delete(:text)
         
     | 
| 
      
 254 
     | 
    
         
            +
                      # We patch the case of rendering :partial with :layout while
         
     | 
| 
      
 255 
     | 
    
         
            +
                      # streaming in _render_with_layout.
         
     | 
| 
      
 256 
     | 
    
         
            +
                      return render(render.args.first.merge(:layout => options[:layout]))
         
     | 
| 
      
 257 
     | 
    
         
            +
                    end
         
     | 
| 
       206 
258 
     | 
    
         
             
                  end
         
     | 
| 
      
 259 
     | 
    
         
            +
                  render_without_template_streaming(*args, &block)
         
     | 
| 
      
 260 
     | 
    
         
            +
                end
         
     | 
| 
      
 261 
     | 
    
         
            +
             
     | 
| 
      
 262 
     | 
    
         
            +
                DeferredPartialRender = Struct.new(:args)
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
                attr_writer :streaming_template
         
     | 
| 
      
 265 
     | 
    
         
            +
             
     | 
| 
      
 266 
     | 
    
         
            +
                def streaming_template?
         
     | 
| 
      
 267 
     | 
    
         
            +
                  @streaming_template
         
     | 
| 
       207 
268 
     | 
    
         
             
                end
         
     | 
| 
       208 
269 
     | 
    
         | 
| 
       209 
     | 
    
         
            -
                def  
     | 
| 
       210 
     | 
    
         
            -
                  if  
     | 
| 
      
 270 
     | 
    
         
            +
                def _render_with_layout_with_template_streaming(options, local_assigns, &block)
         
     | 
| 
      
 271 
     | 
    
         
            +
                  if !streaming_template?
         
     | 
| 
      
 272 
     | 
    
         
            +
                    _render_with_layout_without_template_streaming(options, local_assigns, &block)
         
     | 
| 
      
 273 
     | 
    
         
            +
                  elsif block_given?
         
     | 
| 
      
 274 
     | 
    
         
            +
                    # The standard method doesn't properly restore @_proc_for_layout. Do it ourselves.
         
     | 
| 
      
 275 
     | 
    
         
            +
                    original_proc_for_layout = @_proc_for_layout
         
     | 
| 
       211 
276 
     | 
    
         
             
                    begin
         
     | 
| 
       212 
     | 
    
         
            -
                       
     | 
| 
       213 
     | 
    
         
            -
                        # nil out @_proc_for_layout else rendering with the layout will call it again.
         
     | 
| 
       214 
     | 
    
         
            -
                        @_proc_for_layout, original_proc_for_layout = nil, @_proc_for_layout
         
     | 
| 
       215 
     | 
    
         
            -
                        begin
         
     | 
| 
       216 
     | 
    
         
            -
                          block.call
         
     | 
| 
       217 
     | 
    
         
            -
                        ensure
         
     | 
| 
       218 
     | 
    
         
            -
                          @_proc_for_layout = original_proc_for_layout
         
     | 
| 
       219 
     | 
    
         
            -
                        end
         
     | 
| 
       220 
     | 
    
         
            -
                      end
         
     | 
| 
       221 
     | 
    
         
            -
                      render(:file => prelayout, :locals => locals)
         
     | 
| 
      
 277 
     | 
    
         
            +
                      _render_with_layout_without_template_streaming(options, local_assigns, &block)
         
     | 
| 
       222 
278 
     | 
    
         
             
                    ensure
         
     | 
| 
       223 
     | 
    
         
            -
                      @_proc_for_layout =  
     | 
| 
      
 279 
     | 
    
         
            +
                      @_proc_for_layout = original_proc_for_layout
         
     | 
| 
      
 280 
     | 
    
         
            +
                    end
         
     | 
| 
      
 281 
     | 
    
         
            +
                  elsif options[:layout].is_a?(ActionView::Template)
         
     | 
| 
      
 282 
     | 
    
         
            +
                    # Toplevel render call, from the controller.
         
     | 
| 
      
 283 
     | 
    
         
            +
                    layout = options.delete(:layout)
         
     | 
| 
      
 284 
     | 
    
         
            +
                    with_render_proc_for_layout(options) do
         
     | 
| 
      
 285 
     | 
    
         
            +
                      render(options.merge(:file => layout.path_without_format_and_extension))
         
     | 
| 
       224 
286 
     | 
    
         
             
                    end
         
     | 
| 
       225 
287 
     | 
    
         
             
                  else
         
     | 
| 
       226 
     | 
    
         
            -
                     
     | 
| 
      
 288 
     | 
    
         
            +
                    layout = options.delete(:layout)
         
     | 
| 
      
 289 
     | 
    
         
            +
                    with_render_proc_for_layout(options) do
         
     | 
| 
      
 290 
     | 
    
         
            +
                      if (options[:inline] || options[:file] || options[:text])
         
     | 
| 
      
 291 
     | 
    
         
            +
                        render(:file => layout, :locals => local_assigns)
         
     | 
| 
      
 292 
     | 
    
         
            +
                      else
         
     | 
| 
      
 293 
     | 
    
         
            +
                        render(options.merge(:partial => layout))
         
     | 
| 
      
 294 
     | 
    
         
            +
                      end
         
     | 
| 
      
 295 
     | 
    
         
            +
                    end
         
     | 
| 
       227 
296 
     | 
    
         
             
                  end
         
     | 
| 
       228 
297 
     | 
    
         
             
                end
         
     | 
| 
       229 
298 
     | 
    
         | 
| 
       230 
     | 
    
         
            -
                def  
     | 
| 
       231 
     | 
    
         
            -
                   
     | 
| 
       232 
     | 
    
         
            -
             
     | 
| 
       233 
     | 
    
         
            -
             
     | 
| 
       234 
     | 
    
         
            -
             
     | 
| 
       235 
     | 
    
         
            -
             
     | 
| 
       236 
     | 
    
         
            -
             
     | 
| 
       237 
     | 
    
         
            -
             
     | 
| 
       238 
     | 
    
         
            -
             
     | 
| 
       239 
     | 
    
         
            -
                   
     | 
| 
       240 
     | 
    
         
            -
             
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
       242 
     | 
    
         
            -
             
     | 
| 
       243 
     | 
    
         
            -
             
     | 
| 
       244 
     | 
    
         
            -
                  # Override ActionView::Base#flash to prevent a double-sweep.
         
     | 
| 
       245 
     | 
    
         
            -
                  controller.instance_eval { @template_streaming_flash || flash }
         
     | 
| 
      
 299 
     | 
    
         
            +
                def with_render_proc_for_layout(options)
         
     | 
| 
      
 300 
     | 
    
         
            +
                  original_proc_for_layout = @_proc_for_layout
         
     | 
| 
      
 301 
     | 
    
         
            +
                  @_proc_for_layout = lambda do |*args|
         
     | 
| 
      
 302 
     | 
    
         
            +
                    if args.empty?
         
     | 
| 
      
 303 
     | 
    
         
            +
                      render(options)
         
     | 
| 
      
 304 
     | 
    
         
            +
                    else
         
     | 
| 
      
 305 
     | 
    
         
            +
                      instance_variable_get(:"@content_for_#{args.first}")
         
     | 
| 
      
 306 
     | 
    
         
            +
                    end
         
     | 
| 
      
 307 
     | 
    
         
            +
                  end
         
     | 
| 
      
 308 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 309 
     | 
    
         
            +
                    yield
         
     | 
| 
      
 310 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 311 
     | 
    
         
            +
                    @_proc_for_layout = original_proc_for_layout
         
     | 
| 
      
 312 
     | 
    
         
            +
                  end
         
     | 
| 
       246 
313 
     | 
    
         
             
                end
         
     | 
| 
       247 
314 
     | 
    
         
             
              end
         
     | 
| 
       248 
315 
     | 
    
         | 
| 
         @@ -275,10 +342,24 @@ module TemplateStreaming 
     | 
|
| 
       275 
342 
     | 
    
         
             
                end
         
     | 
| 
       276 
343 
     | 
    
         
             
              end
         
     | 
| 
       277 
344 
     | 
    
         | 
| 
      
 345 
     | 
    
         
            +
              module AbstractSessionStoreExtension
         
     | 
| 
      
 346 
     | 
    
         
            +
                def sent_with_headers?
         
     | 
| 
      
 347 
     | 
    
         
            +
                  false
         
     | 
| 
      
 348 
     | 
    
         
            +
                end
         
     | 
| 
      
 349 
     | 
    
         
            +
              end
         
     | 
| 
      
 350 
     | 
    
         
            +
             
     | 
| 
      
 351 
     | 
    
         
            +
              module CookieSessionStoreExtension
         
     | 
| 
      
 352 
     | 
    
         
            +
                def sent_with_headers?
         
     | 
| 
      
 353 
     | 
    
         
            +
                  true
         
     | 
| 
      
 354 
     | 
    
         
            +
                end
         
     | 
| 
      
 355 
     | 
    
         
            +
              end
         
     | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
       278 
357 
     | 
    
         
             
              ActionView::Base.send :include, View
         
     | 
| 
       279 
358 
     | 
    
         
             
              ActionController::Base.send :include, Controller
         
     | 
| 
       280 
359 
     | 
    
         
             
              ActionController::Response.send :include, Response
         
     | 
| 
       281 
360 
     | 
    
         
             
              ActionController::Dispatcher.middleware.insert 0, Rack::Chunked
         
     | 
| 
      
 361 
     | 
    
         
            +
              ActionController::Session::AbstractStore.extend AbstractSessionStoreExtension
         
     | 
| 
      
 362 
     | 
    
         
            +
              ActionController::Session::CookieStore.extend CookieSessionStoreExtension
         
     | 
| 
       282 
363 
     | 
    
         
             
            end
         
     | 
| 
       283 
364 
     | 
    
         | 
| 
       284 
365 
     | 
    
         
             
            # Please let there be a better way to do this...
         
     | 
| 
         @@ -311,3 +392,7 @@ if defined?(Thin) 
     | 
|
| 
       311 
392 
     | 
    
         
             
                end
         
     | 
| 
       312 
393 
     | 
    
         
             
              end
         
     | 
| 
       313 
394 
     | 
    
         
             
            end
         
     | 
| 
      
 395 
     | 
    
         
            +
             
     | 
| 
      
 396 
     | 
    
         
            +
            require 'template_streaming/error_recovery'
         
     | 
| 
      
 397 
     | 
    
         
            +
            require 'template_streaming/caching'
         
     | 
| 
      
 398 
     | 
    
         
            +
            require 'template_streaming/autoflushing'
         
     |