diff options
author | José Valim <jose.valim@gmail.com> | 2011-04-16 01:09:05 +0200 |
---|---|---|
committer | José Valim <jose.valim@gmail.com> | 2011-04-16 01:10:13 +0200 |
commit | fad214b9e1c0a66f8fecde48fbd8d122e5c51e33 (patch) | |
tree | 3ca51e274e0992c460e28c81f501398757669899 /actionpack/lib/action_view | |
parent | c630750fa59e248fb5af96f850392333e341ccd7 (diff) | |
download | rails-fad214b9e1c0a66f8fecde48fbd8d122e5c51e33.tar.gz rails-fad214b9e1c0a66f8fecde48fbd8d122e5c51e33.tar.bz2 rails-fad214b9e1c0a66f8fecde48fbd8d122e5c51e33.zip |
Initial work on fibered layout.
Diffstat (limited to 'actionpack/lib/action_view')
6 files changed, 108 insertions, 18 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 7488127e35..10a523eeac 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -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 diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 3808e231f1..f8b5605ed9 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -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. diff --git a/actionpack/lib/action_view/renderer/fibered_template_renderer.rb b/actionpack/lib/action_view/renderer/fibered_template_renderer.rb new file mode 100644 index 0000000000..45f48cab76 --- /dev/null +++ b/actionpack/lib/action_view/renderer/fibered_template_renderer.rb @@ -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 diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index 94c0a8a8fb..180afc27ac 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -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 diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index 439a661dd3..10d8cc3b87 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -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 diff --git a/actionpack/lib/action_view/rendering.rb b/actionpack/lib/action_view/rendering.rb index 30c083a2bf..e3568e70e5 100644 --- a/actionpack/lib/action_view/rendering.rb +++ b/actionpack/lib/action_view/rendering.rb @@ -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 |