Permalink
Browse files

Initial work on fibered layout.

  • Loading branch information...
josevalim committed Apr 15, 2011
1 parent c630750 commit fad214b9e1c0a66f8fecde48fbd8d122e5c51e33
@@ -44,6 +44,7 @@ module ActionView
autoload :AbstractRenderer
autoload :PartialRenderer
autoload :TemplateRenderer
+ autoload :FiberedTemplateRenderer
end
autoload_at "action_view/template/resolver" do
@@ -153,7 +153,7 @@ def cache_template_loading=(value)
end
end
- attr_accessor :_template
+ attr_accessor :_template, :_view_flow, :magic_medicine
attr_internal :request, :controller, :config, :assigns, :lookup_context
delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
@@ -181,8 +181,8 @@ def initialize(lookup_context = nil, assigns_for_first_render = {}, controller =
self.helpers = Module.new unless self.class.helpers
@_config = {}
- @_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil
+ @_view_flow = Flow.new
@output_buffer = nil
if @_controller = controller
@@ -195,10 +195,6 @@ def initialize(lookup_context = nil, assigns_for_first_render = {}, controller =
@_lookup_context.formats = formats if formats
end
- def store_content_for(key, value)
- @_content_for[key] = value
- end
-
def controller_path
@controller_path ||= controller && controller.controller_path
end
@@ -209,4 +205,49 @@ def controller_prefixes
ActiveSupport.run_load_hooks(:action_view, self)
end
+
+ class Flow
+ attr_reader :content
+
+ def initialize
+ @content = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
+ end
+
+ def get(key)
+ @content[key]
+ end
+
+ def set(key, value)
+ @content[key] = value
+ end
+
+ def append(key, value)
+ @content[key] << value
+ end
+ end
+
+ class FiberedFlow < Flow
+ def initialize(flow, fiber)
+ @content = flow.content
+ @fiber = fiber
+ end
+
+ def get(key)
+ return super if @content.key?(key)
+
+ begin
+ @waiting_for = key
+ Fiber.yield
+ ensure
+ @waiting_for = nil
+ end
+
+ super
+ end
+
+ def set(key, value)
+ super
+ @fiber.resume if @waiting_for == key
+ end
+ end
end
@@ -135,8 +135,8 @@ def capture(*args)
# for elements that will be fragment cached.
def content_for(name, content = nil, &block)
content = capture(&block) if block_given?
- @_content_for[name] << content if content
- @_content_for[name] unless content
+ result = @_view_flow.append(name, content) if content
+ result unless content
end
# content_for? simply checks whether any content has been captured yet using content_for
@@ -158,7 +158,7 @@ def content_for(name, content = nil, &block)
# </body>
# </html>
def content_for?(name)
- @_content_for[name].present?
+ @_view_flow.get(name).present?
end
# Use an alternate output buffer for the duration of the block.
@@ -0,0 +1,35 @@
+require 'action_view/renderer/template_renderer'
+require 'fiber'
+
+module ActionView
+ class FiberedTemplateRenderer < TemplateRenderer #:nodoc:
+ # Renders the given template. An string representing the layout can be
+ # supplied as well.
+ def render_template(template, layout_name = nil, locals = {}) #:nodoc:
+ view, locals = @view, locals || {}
+
+ final = nil
+ layout = layout_name && find_layout(layout_name, locals.keys)
+ yielder = lambda { |*name| view._layout_for(*name) }
+
+ instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
+ @fiber = Fiber.new do
+ final = if layout
+ layout.render(view, locals, &yielder)
+ else
+ view._layout_for
+ end
+ end
+
+ @view._view_flow = FiberedFlow.new(view._view_flow, @fiber)
+ @fiber.resume
+
+ content = template.render(view, locals, &yielder)
+ view._view_flow.set(:layout, content)
+ @fiber.resume while @fiber.alive?
+ end
+
+ final
+ end
+ end
+end
@@ -79,7 +79,7 @@ def render_partial
locals[as] = object
content = @template.render(view, locals) do |*name|
- view._layout_for(*name, &block)
+ view._block_layout_for(*name, &block)
end
content = layout.render(view, locals){ content } if layout
@@ -7,6 +7,7 @@ class TemplateRenderer < AbstractRenderer #:nodoc:
def render(options)
wrap_formats(options[:template] || options[:file]) do
template = determine_template(options)
+ freeze_formats(template.formats, true)
render_template(template, options[:layout], options[:locals])
end
end
@@ -31,7 +32,6 @@ def determine_template(options) #:nodoc:
# Renders the given template. An string representing the layout can be
# supplied as well.
def render_template(template, layout_name = nil, locals = {}) #:nodoc:
- freeze_formats(template.formats, true)
view, locals = @view, locals || {}
render_with_layout(layout_name, locals) do |layout|
@@ -47,7 +47,7 @@ def render_with_layout(path, locals) #:nodoc:
if layout
view = @view
- view.store_content_for(:layout, content)
+ view._view_flow.set(:layout, content)
layout.render(view, locals){ |*name| view._layout_for(*name) }
else
content
@@ -73,24 +73,38 @@ def render(options = {}, locals = {}, &block)
# Hello David
# </html>
#
- def _layout_for(*args, &block)
+ def _layout_for(*args)
+ name = args.first
+ name = :layout unless name.is_a?(Symbol)
+ @_view_flow.get(name).html_safe
+ end
+
+ # Returns the content from the Flow unless we have a block.
+ def _block_layout_for(*args, &block)
name = args.first
- if name.is_a?(Symbol)
- @_content_for[name].html_safe
- elsif block
+ if !name.is_a?(Symbol) && block
capture(*args, &block)
else
- @_content_for[:layout].html_safe
+ _layout_for(*args)
end
end
def _render_template(options) #:nodoc:
- _template_renderer.render(options)
+ if @magic_medicine
+ _fibered_template_renderer.render(options)
+ else
+ _template_renderer.render(options)
+ end
end
def _template_renderer #:nodoc:
@_template_renderer ||= TemplateRenderer.new(self)
end
+
+ def _fibered_template_renderer #:nodoc:
+ @_fibered_template_renderer ||= FiberedTemplateRenderer.new(self)
+ end
+
end
end
@@ -4,7 +4,7 @@ class CaptureHelperTest < ActionView::TestCase
def setup
super
@av = ActionView::Base.new
- @_content_for = Hash.new {|h,k| h[k] = "" }
+ @_view_flow = ActionView::Flow.new
end
def test_capture_captures_the_temporary_output_buffer_in_its_block
@@ -0,0 +1,27 @@
+# encoding: utf-8
+require 'abstract_unit'
+require 'controller/fake_models'
+
+class TestController < ActionController::Base
+end
+
+
+class FiberedTest < ActiveSupport::TestCase
+
+ def setup
+ view_paths = ActionController::Base.view_paths
+ @assigns = { :secret => 'in the sauce' }
+ @view = ActionView::Base.new(view_paths, @assigns)
+ @view.magic_medicine = true
+ @controller_view = TestController.new.view_context
+ end
+
+ def test_render_template
+ assert_equal "Hello world!", @view.render(:template => "test/hello_world")
+ end
+
+ def test_render_with_layout
+ assert_equal %(<title></title>\nHello world!\n),
+ @view.render(:template => "test/hello_world.erb", :layout => "layouts/yield")
+ end
+end if defined?(Fiber)

0 comments on commit fad214b

Please sign in to comment.