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