Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
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...
commit 389d15ef139e50696b274f2d61dd309ba2632877 1 parent 7a152ab
@josevalim josevalim authored
View
1  actionpack/lib/action_controller.rb
@@ -31,6 +31,7 @@ module ActionController
autoload :Rescue
autoload :Responder
autoload :SessionManagement
+ autoload :Streaming
autoload :Testing
autoload :UrlFor
end
View
1  actionpack/lib/action_controller/base.rb
@@ -199,6 +199,7 @@ def self.without_modules(*modules)
Flash,
RequestForgeryProtection,
ForceSSL,
+ Streaming,
DataStreaming,
RecordIdentifier,
HttpAuthentication::Basic::ControllerMethods,
View
57 actionpack/lib/action_controller/metal/streaming.rb
@@ -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 added a note

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

@josevalim Owner

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 added a note

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ 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 added a note

options[:stream] ||= self.stream

@josevalim Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ 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
+
View
1  actionpack/lib/action_view/context.rb
@@ -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.
View
62 actionpack/test/controller/new_base/render_streaming_test.rb
@@ -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
Please sign in to comment.
Something went wrong with that request. Please try again.