Permalink
Browse files

Body... wanna *stream* my body? Body... such a thrill my body!

Added stream as class level method to make it explicit when to stream.
Render also accepts :stream as option.
  • Loading branch information...
josevalim committed Apr 18, 2011
1 parent 7a152ab commit 389d15ef139e50696b274f2d61dd309ba2632877
@@ -31,6 +31,7 @@ module ActionController
autoload :Rescue
autoload :Responder
autoload :SessionManagement
+ autoload :Streaming
autoload :Testing
autoload :UrlFor
end
@@ -199,6 +199,7 @@ def self.without_modules(*modules)
Flash,
RequestForgeryProtection,
ForceSSL,
+ Streaming,
DataStreaming,
RecordIdentifier,
HttpAuthentication::Basic::ControllerMethods,
@@ -0,0 +1,57 @@
+require 'active_support/core_ext/file/path'
+require 'rack/chunked'
+
+module ActionController #:nodoc:
+ # Methods for sending streaming templates back to the client.
+ module Streaming
+ extend ActiveSupport::Concern
@foca

foca Apr 18, 2011

Contributor

Why extend with ActiveSupport::Concern if you're not using included anywhere else in this class? What am I missing?

@josevalim

josevalim Apr 18, 2011

Contributor

See ClassMethods below. Besides, Concern also tracks included modules so the included hook for AbstractController::Rendering is executed in the included target and not in Streaming.

@foca

foca Apr 18, 2011

Contributor

Ah! Didn't know that last bit. Interesting…

+
+ include AbstractController::Rendering
+ attr_internal :stream
+
+ module ClassMethods
+ # Render streaming templates. It accepts :only, :except, :if and :unless as options
+ # to specify when to stream, as in ActionController filters.
+ def stream(options={})
+ if defined?(Fiber)
+ before_filter :_stream_filter, options
+ else
+ raise "You cannot use streaming if Fiber is not available."
+ end
+ end
+ end
+
+ protected
+
+ # Mark following render calls as streaming.
+ def _stream_filter #:nodoc:
+ self.stream = true
+ end
+
+ # Consider the stream option when normalazing options.
+ def _normalize_options(options) #:nodoc:
+ super
+ options[:stream] = self.stream unless options.key?(:stream)
@jodosha

jodosha Apr 18, 2011

options[:stream] ||= self.stream

@josevalim

josevalim Apr 18, 2011

Contributor

Nops. If I set stream to true at the class level, I may want to turn it off when I call render with render :stream => false. :) In your case, it would always return true, the current case gives higher priority to options.

+ end
+
+ # Set proper cache control and transfer encoding when streaming
+ def _process_options(options) #:nodoc:
+ super
+ if options[:stream]
+ headers["Cache-Control"] ||= "no-cache"
+ headers["Transfer-Encoding"] = "chunked"
+ headers.delete("Content-Length")
+ end
+ end
+
+ # Call render_to_body if we are streaming instead of usual +render+.
+ def _render_template(options) #:nodoc:
+ if options.delete(:stream)
+ Rack::Chunked::Body.new view_context.render_body(options)
+ else
+ super
+ end
+ end
+ end
+end
+
@@ -2,6 +2,7 @@ module ActionView
module CompiledTemplates #:nodoc:
# holds compiled template code
end
+
# = Action View Context
#
# Action View contexts are supplied to Action Controller to render template.
@@ -0,0 +1,62 @@
+require 'abstract_unit'
+
+module RenderStreaming
+ class BasicController < ActionController::Base
+ self.view_paths = [ActionView::FixtureResolver.new(
+ "render_streaming/basic/hello_world.html.erb" => "Hello world",
+ "layouts/application.html.erb" => "<%= yield %>, I'm here!"
+ )]
+
+ layout "application"
+ stream :only => :hello_world
+
+ def hello_world
+ end
+
+ def explicit
+ render :action => "hello_world", :stream => true
+ end
+
+ def no_layout
+ render :action => "hello_world", :stream => true, :layout => false
+ end
+
+ def explicit_cache
+ headers["Cache-Control"] = "private"
+ render :action => "hello_world", :stream => true
+ end
+ end
+
+ class StreamingTest < Rack::TestCase
+ test "rendering with streaming enabled at the class level" do
+ get "/render_streaming/basic/hello_world"
+ assert_body "b\r\nHello world\r\nb\r\n, I'm here!\r\n0\r\n\r\n"
+ assert_streaming!
+ end
+
+ test "rendering with streaming given to render" do
+ get "/render_streaming/basic/explicit"
+ assert_body "b\r\nHello world\r\nb\r\n, I'm here!\r\n0\r\n\r\n"
+ assert_streaming!
+ end
+
+ test "rendering with streaming do not override explicit cache control given to render" do
+ get "/render_streaming/basic/explicit_cache"
+ assert_body "b\r\nHello world\r\nb\r\n, I'm here!\r\n0\r\n\r\n"
+ assert_streaming! "private"
+ end
+
+ test "rendering with streaming no layout" do
+ get "/render_streaming/basic/no_layout"
+ assert_body "b\r\nHello world\r\n0\r\n\r\n"
+ assert_streaming!
+ end
+
+ def assert_streaming!(cache="no-cache")
+ assert_status 200
+ assert_equal nil, headers["Content-Length"]
+ assert_equal "chunked", headers["Transfer-Encoding"]
+ assert_equal cache, headers["Cache-Control"]
+ end
+ end if defined?(Fiber)
+end

0 comments on commit 389d15e

Please sign in to comment.