1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Initial work on fibered layout.

This commit is contained in:
José Valim 2011-04-16 01:09:05 +02:00
parent c630750fa5
commit fad214b9e1
9 changed files with 137 additions and 19 deletions

View file

@ -44,6 +44,7 @@ module ActionView
autoload :AbstractRenderer autoload :AbstractRenderer
autoload :PartialRenderer autoload :PartialRenderer
autoload :TemplateRenderer autoload :TemplateRenderer
autoload :FiberedTemplateRenderer
end end
autoload_at "action_view/template/resolver" do autoload_at "action_view/template/resolver" do

View file

@ -153,7 +153,7 @@ module ActionView #:nodoc:
end end
end end
attr_accessor :_template attr_accessor :_template, :_view_flow, :magic_medicine
attr_internal :request, :controller, :config, :assigns, :lookup_context attr_internal :request, :controller, :config, :assigns, :lookup_context
delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
@ -181,8 +181,8 @@ module ActionView #:nodoc:
self.helpers = Module.new unless self.class.helpers self.helpers = Module.new unless self.class.helpers
@_config = {} @_config = {}
@_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil @_virtual_path = nil
@_view_flow = Flow.new
@output_buffer = nil @output_buffer = nil
if @_controller = controller if @_controller = controller
@ -195,10 +195,6 @@ module ActionView #:nodoc:
@_lookup_context.formats = formats if formats @_lookup_context.formats = formats if formats
end end
def store_content_for(key, value)
@_content_for[key] = value
end
def controller_path def controller_path
@controller_path ||= controller && controller.controller_path @controller_path ||= controller && controller.controller_path
end end
@ -209,4 +205,49 @@ module ActionView #:nodoc:
ActiveSupport.run_load_hooks(:action_view, self) ActiveSupport.run_load_hooks(:action_view, self)
end 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 end

View file

@ -135,8 +135,8 @@ module ActionView
# for elements that will be fragment cached. # for elements that will be fragment cached.
def content_for(name, content = nil, &block) def content_for(name, content = nil, &block)
content = capture(&block) if block_given? content = capture(&block) if block_given?
@_content_for[name] << content if content result = @_view_flow.append(name, content) if content
@_content_for[name] unless content result unless content
end end
# content_for? simply checks whether any content has been captured yet using content_for # content_for? simply checks whether any content has been captured yet using content_for
@ -158,7 +158,7 @@ module ActionView
# </body> # </body>
# </html> # </html>
def content_for?(name) def content_for?(name)
@_content_for[name].present? @_view_flow.get(name).present?
end end
# Use an alternate output buffer for the duration of the block. # Use an alternate output buffer for the duration of the block.

View file

@ -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

View file

@ -79,7 +79,7 @@ module ActionView
locals[as] = object locals[as] = object
content = @template.render(view, locals) do |*name| content = @template.render(view, locals) do |*name|
view._layout_for(*name, &block) view._block_layout_for(*name, &block)
end end
content = layout.render(view, locals){ content } if layout content = layout.render(view, locals){ content } if layout

View file

@ -7,6 +7,7 @@ module ActionView
def render(options) def render(options)
wrap_formats(options[:template] || options[:file]) do wrap_formats(options[:template] || options[:file]) do
template = determine_template(options) template = determine_template(options)
freeze_formats(template.formats, true)
render_template(template, options[:layout], options[:locals]) render_template(template, options[:layout], options[:locals])
end end
end end
@ -31,7 +32,6 @@ module ActionView
# Renders the given template. An string representing the layout can be # Renders the given template. An string representing the layout can be
# supplied as well. # supplied as well.
def render_template(template, layout_name = nil, locals = {}) #:nodoc: def render_template(template, layout_name = nil, locals = {}) #:nodoc:
freeze_formats(template.formats, true)
view, locals = @view, locals || {} view, locals = @view, locals || {}
render_with_layout(layout_name, locals) do |layout| render_with_layout(layout_name, locals) do |layout|
@ -47,7 +47,7 @@ module ActionView
if layout if layout
view = @view view = @view
view.store_content_for(:layout, content) view._view_flow.set(:layout, content)
layout.render(view, locals){ |*name| view._layout_for(*name) } layout.render(view, locals){ |*name| view._layout_for(*name) }
else else
content content

View file

@ -73,24 +73,38 @@ module ActionView
# Hello David # Hello David
# </html> # </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 name = args.first
if name.is_a?(Symbol) if !name.is_a?(Symbol) && block
@_content_for[name].html_safe
elsif block
capture(*args, &block) capture(*args, &block)
else else
@_content_for[:layout].html_safe _layout_for(*args)
end end
end end
def _render_template(options) #:nodoc: def _render_template(options) #:nodoc:
_template_renderer.render(options) if @magic_medicine
_fibered_template_renderer.render(options)
else
_template_renderer.render(options)
end
end end
def _template_renderer #:nodoc: def _template_renderer #:nodoc:
@_template_renderer ||= TemplateRenderer.new(self) @_template_renderer ||= TemplateRenderer.new(self)
end end
def _fibered_template_renderer #:nodoc:
@_fibered_template_renderer ||= FiberedTemplateRenderer.new(self)
end
end end
end end

View file

@ -4,7 +4,7 @@ class CaptureHelperTest < ActionView::TestCase
def setup def setup
super super
@av = ActionView::Base.new @av = ActionView::Base.new
@_content_for = Hash.new {|h,k| h[k] = "" } @_view_flow = ActionView::Flow.new
end end
def test_capture_captures_the_temporary_output_buffer_in_its_block def test_capture_captures_the_temporary_output_buffer_in_its_block

View file

@ -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)