diff options
author | Jeremy Kemper <jeremy@bitsweat.net> | 2009-03-13 02:36:00 -0700 |
---|---|---|
committer | Jeremy Kemper <jeremy@bitsweat.net> | 2009-03-13 02:36:15 -0700 |
commit | 7c1714cbd0b37d1fc5ca2ac3e08980943454d516 (patch) | |
tree | c94b454f753281d9d9e67d6f0de237fa76f5417b /actionpack | |
parent | 79b0b1a0ef926e91388e24bc77d3831f6d50b13d (diff) | |
download | rails-7c1714cbd0b37d1fc5ca2ac3e08980943454d516.tar.gz rails-7c1714cbd0b37d1fc5ca2ac3e08980943454d516.tar.bz2 rails-7c1714cbd0b37d1fc5ca2ac3e08980943454d516.zip |
Body parts: future rendering, threaded future, queued future, open-uri example
Diffstat (limited to 'actionpack')
-rw-r--r-- | actionpack/lib/action_view/base.rb | 10 | ||||
-rw-r--r-- | actionpack/lib/action_view/body_parts/future.rb | 26 | ||||
-rw-r--r-- | actionpack/lib/action_view/body_parts/open_uri.rb | 13 | ||||
-rw-r--r-- | actionpack/lib/action_view/body_parts/queued.rb | 21 | ||||
-rw-r--r-- | actionpack/lib/action_view/body_parts/threaded.rb | 21 | ||||
-rw-r--r-- | actionpack/test/template/body_parts_test.rb | 142 | ||||
-rw-r--r-- | actionpack/test/template/output_buffer_test.rb | 35 |
7 files changed, 231 insertions, 37 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index e19acc5c29..3bbd2ca530 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -288,12 +288,12 @@ module ActionView #:nodoc: # Access the current template being rendered. # Returns a ActionView::Template object. def template - @_current_render + Thread.current[:_current_render] end def template=(template) #:nodoc: @_first_render ||= template - @_current_render = template + Thread.current[:_current_render] = template end def with_template(current_template) @@ -303,6 +303,12 @@ module ActionView #:nodoc: self.template = last_template end + def punctuate_body!(part) + flush_output_buffer + response.body_parts << part + nil + end + private # Evaluates the local assigns and controller ivars, pushes them to the view. def _evaluate_assigns_and_ivars #:nodoc: diff --git a/actionpack/lib/action_view/body_parts/future.rb b/actionpack/lib/action_view/body_parts/future.rb new file mode 100644 index 0000000000..a5291c6e8c --- /dev/null +++ b/actionpack/lib/action_view/body_parts/future.rb @@ -0,0 +1,26 @@ +module ActionView + module BodyParts + class Future + def initialize(&block) + @block = block + @parts = [] + end + + def to_s + finish + body + end + + protected + def work + @block.call(@parts) + end + + def body + str = '' + @parts.each { |part| str << part.to_s } + str + end + end + end +end diff --git a/actionpack/lib/action_view/body_parts/open_uri.rb b/actionpack/lib/action_view/body_parts/open_uri.rb new file mode 100644 index 0000000000..8ebd17b4a1 --- /dev/null +++ b/actionpack/lib/action_view/body_parts/open_uri.rb @@ -0,0 +1,13 @@ +require 'action_view/body_parts/threaded' +require 'open-uri' + +module ActionView + module BodyParts + class OpenUri < Threaded + def initialize(url) + url = URI::Generic === url ? url : URI.parse(url) + super(true) { |parts| parts << url.read } + end + end + end +end diff --git a/actionpack/lib/action_view/body_parts/queued.rb b/actionpack/lib/action_view/body_parts/queued.rb new file mode 100644 index 0000000000..f8501f6a85 --- /dev/null +++ b/actionpack/lib/action_view/body_parts/queued.rb @@ -0,0 +1,21 @@ +require 'action_view/body_parts/future' + +module ActionView + module BodyParts + class Queued < Future + def initialize(job, &block) + super(&block) + enqueue(job) + end + + protected + def enqueue(job) + @receipt = submit(job) + end + + def finish + @parts << redeem(@receipt) + end + end + end +end diff --git a/actionpack/lib/action_view/body_parts/threaded.rb b/actionpack/lib/action_view/body_parts/threaded.rb new file mode 100644 index 0000000000..34800bf9c7 --- /dev/null +++ b/actionpack/lib/action_view/body_parts/threaded.rb @@ -0,0 +1,21 @@ +require 'action_view/body_parts/future' + +module ActionView + module BodyParts + class Threaded < Future + def initialize(concurrent = false, &block) + super(&block) + concurrent ? start : work + end + + protected + def start + @worker = Thread.new { work } + end + + def finish + @worker.join if @worker && @worker.alive? + end + end + end +end diff --git a/actionpack/test/template/body_parts_test.rb b/actionpack/test/template/body_parts_test.rb new file mode 100644 index 0000000000..d15c8808d9 --- /dev/null +++ b/actionpack/test/template/body_parts_test.rb @@ -0,0 +1,142 @@ +require 'abstract_unit' +require 'action_view/body_parts/queued' +require 'action_view/body_parts/open_uri' + +class OutputBufferTest < ActionController::TestCase + class TestController < ActionController::Base + def index + render :text => 'foo' + end + end + + tests TestController + + def test_flush_output_buffer + # Start with the default body parts + get :index + assert_equal ['foo'], @response.body_parts + assert_nil @response.template.output_buffer + + # Nil output buffer is skipped + @response.template.flush_output_buffer + assert_nil @response.template.output_buffer + assert_equal ['foo'], @response.body_parts + + # Empty output buffer is skipped + @response.template.output_buffer = '' + @response.template.flush_output_buffer + assert_equal '', @response.template.output_buffer + assert_equal ['foo'], @response.body_parts + + # Flushing appends the output buffer to the body parts + @response.template.output_buffer = 'bar' + @response.template.flush_output_buffer + assert_equal '', @response.template.output_buffer + assert_equal ['foo', 'bar'], @response.body_parts + end +end + +class QueuedPartTest < ActionController::TestCase + class SimpleQueued < ActionView::BodyParts::Queued + protected + def submit(job) + job + end + + def redeem(receipt) + receipt.to_s.reverse + end + end + + class TestController < ActionController::Base + def index + queued_render 'foo' + queued_render 'bar' + queued_render 'baz' + @performed_render = true + end + + def queued_render(job) + response.template.punctuate_body! SimpleQueued.new(job) + end + end + + tests TestController + + def test_queued_parts + get :index + assert_equal 'oofrabzab', @response.body + end +end + +class ThreadedPartTest < ActionController::TestCase + class TestController < ActionController::Base + def index + append_thread_id = lambda do |parts| + parts << Thread.current.object_id + parts << '::' + parts << Time.now.to_i + sleep 1 + end + + future_render &append_thread_id + response.body_parts << '-' + + future_render &append_thread_id + response.body_parts << '-' + + future_render do |parts| + parts << ActionView::BodyParts::Threaded.new(true, &append_thread_id) + parts << '-' + parts << ActionView::BodyParts::Threaded.new(true, &append_thread_id) + end + + @performed_render = true + end + + def future_render(&block) + response.template.punctuate_body! ActionView::BodyParts::Threaded.new(true, &block) + end + end + + tests TestController + + def test_concurrent_threaded_parts + get :index + + before = Time.now.to_i + thread_ids = @response.body.split('-').map { |part| part.split('::').first.to_i } + elapsed = Time.now.to_i - before + + assert_equal thread_ids.size, thread_ids.uniq.size + assert elapsed < 1.1 + end +end + +class OpenUriPartTest < ActionController::TestCase + class TestController < ActionController::Base + def index + render_url 'http://localhost/foo' + render_url 'http://localhost/bar' + render_url 'http://localhost/baz' + @performed_render = true + end + + def render_url(url) + url = URI.parse(url) + def url.read; path end + response.template.punctuate_body! ActionView::BodyParts::OpenUri.new(url) + end + end + + tests TestController + + def test_concurrent_open_uri_parts + get :index + + elapsed = Benchmark.ms do + assert_equal '/foo/bar/baz', @response.body + end + assert elapsed < 1.1 + end +end diff --git a/actionpack/test/template/output_buffer_test.rb b/actionpack/test/template/output_buffer_test.rb deleted file mode 100644 index 6d8eab63dc..0000000000 --- a/actionpack/test/template/output_buffer_test.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'abstract_unit' - -class OutputBufferTest < ActionController::TestCase - class TestController < ActionController::Base - def index - render :text => 'foo' - end - end - - tests TestController - - def test_flush_output_buffer - # Start with the default body parts - get :index - assert_equal ['foo'], @response.body_parts - assert_nil @response.template.output_buffer - - # Nil output buffer is skipped - @response.template.flush_output_buffer - assert_nil @response.template.output_buffer - assert_equal ['foo'], @response.body_parts - - # Empty output buffer is skipped - @response.template.output_buffer = '' - @response.template.flush_output_buffer - assert_equal '', @response.template.output_buffer - assert_equal ['foo'], @response.body_parts - - # Flushing appends the output buffer to the body parts - @response.template.output_buffer = 'bar' - @response.template.flush_output_buffer - assert_equal '', @response.template.output_buffer - assert_equal ['foo', 'bar'], @response.body_parts - end -end |