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:
parent
c630750fa5
commit
fad214b9e1
9 changed files with 137 additions and 19 deletions
|
@ -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 @@ module ActionView #:nodoc:
|
|||
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 @@ module ActionView #:nodoc:
|
|||
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 @@ module ActionView #:nodoc:
|
|||
@_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 @@ module ActionView #:nodoc:
|
|||
|
||||
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 @@ module ActionView
|
|||
# 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 @@ module ActionView
|
|||
# </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 @@ module ActionView
|
|||
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 @@ module ActionView
|
|||
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 @@ module ActionView
|
|||
# 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 @@ module ActionView
|
|||
|
||||
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 @@ module ActionView
|
|||
# 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
|
||||
|
|
27
actionpack/test/template/fibered_render_test.rb
Normal file
27
actionpack/test/template/fibered_render_test.rb
Normal 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)
|
Loading…
Reference in a new issue