diff options
Diffstat (limited to 'actionpack/lib')
5 files changed, 66 insertions, 29 deletions
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index e88e5aefc0..1c63fb2d14 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -155,7 +155,7 @@ module AbstractController options[:partial] = action_name end - if (options.keys & [:partial, :file, :template]).empty? + if (options.keys & [:partial, :file, :template, :once]).empty? options[:prefix] ||= _prefix end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index adbb6bc626..0cd5d9d437 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -10,6 +10,7 @@ module ActionView # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those. # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller. # * <tt>:text</tt> - Renders the text passed in out. + # * <tt>:once</tt> - Receives :template paths and ensures they are rendered just once. # # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter # as the locals hash. @@ -20,6 +21,8 @@ module ActionView _render_partial(options.merge(:partial => options[:layout]), &block) elsif options.key?(:partial) _render_partial(options) + elsif options.key?(:once) + _render_once(options) else _render_template(options) end @@ -88,6 +91,10 @@ module ActionView end end + def _render_once(options) #:nodoc: + _template_renderer.render_once(options) + end + def _render_template(options) #:nodoc: _template_renderer.render(options) end diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb index f9fa63ce7f..77cfa51dff 100644 --- a/actionpack/lib/action_view/renderer/abstract_renderer.rb +++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb @@ -14,12 +14,6 @@ module ActionView raise NotImplementedError end - # Contains the logic that actually renders the layout. - def render_layout(layout, locals, &block) #:nodoc: - view = @view - layout.render(view, locals){ |*name| view._layout_for(*name, &block) } - end - # Checks if the given path contains a format and if so, change # the lookup context to take this new format into account. def wrap_formats(value) @@ -32,5 +26,11 @@ module ActionView yield end end + + protected + + def instrument(name, options={}) + ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield } + end end end
\ No newline at end of file diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index 3be1702f9e..eff425687b 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -2,7 +2,6 @@ require 'action_view/renderer/abstract_renderer' module ActionView class PartialRenderer < AbstractRenderer #:nodoc: - N = ::ActiveSupport::Notifications PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} } def initialize(view) @@ -46,11 +45,11 @@ module ActionView identifier = ((@template = find_partial) ? @template.identifier : @path) if @collection - N.instrument("render_collection.action_view", :identifier => identifier || "collection", :count => @collection.size) do + instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do render_collection end else - N.instrument("render_partial.action_view", :identifier => identifier) do + instrument(:partial, :identifier => identifier) do render_partial end end @@ -83,7 +82,7 @@ module ActionView view._layout_for(*name, &block) end - content = render_layout(layout, locals){ content } if layout + content = layout.render(view, locals){ content } if layout content end diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index 3acc68dcac..9f9df15347 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -1,26 +1,51 @@ +require 'set' +require 'active_support/core_ext/object/try' +require 'active_support/core_ext/array/wrap' require 'action_view/renderer/abstract_renderer' module ActionView class TemplateRenderer < AbstractRenderer #:nodoc: + attr_reader :rendered + + def initialize(view) + super + @rendered = Set.new + end + def render(options) wrap_formats(options[:template] || options[:file]) do template = determine_template(options) - lookup_context.freeze_formats(template.formats, true) - render_template(template, options[:layout], options) + render_template(template, options[:layout], options[:locals]) + end + end + + def render_once(options) + paths, locals = options[:once], options[:locals] || {} + layout, keys, prefix = options[:layout], locals.keys, options[:prefix] + + raise "render :once expects a String or an Array to be given" unless paths + + render_with_layout(layout, locals) do + contents = [] + Array.wrap(paths).each do |path| + template = find_template(path, prefix, false, keys) + contents << render_template(template, nil, locals) if @rendered.add?(template) + end + contents.join("\n") end end # Determine the template to be rendered using the given options. def determine_template(options) #:nodoc: - keys = (options[:locals] ||= {}).keys + keys = options[:locals].try(:keys) || [] - if options.key?(:inline) - handler = Template.handler_class_for_extension(options[:type] || "erb") - Template.new(options[:inline], "inline template", handler, { :locals => keys }) - elsif options.key?(:text) + if options.key?(:text) Template::Text.new(options[:text], formats.try(:first)) elsif options.key?(:file) with_fallbacks { find_template(options[:file], options[:prefix], false, keys) } + elsif options.key?(:inline) + handler = Template.handler_class_for_extension(options[:type] || "erb") + Template.new(options[:inline], "inline template", handler, { :locals => keys }) elsif options.key?(:template) options[:template].respond_to?(:render) ? options[:template] : find_template(options[:template], options[:prefix], false, keys) @@ -29,20 +54,26 @@ module ActionView # Renders the given template. An string representing the layout can be # supplied as well. - def render_template(template, layout = nil, options = {}) #:nodoc: - view, locals = @view, options[:locals] || {} - layout = find_layout(layout, locals.keys) if layout + def render_template(template, layout_name = nil, locals = {}) #:nodoc: + lookup_context.freeze_formats(template.formats, true) + view, locals = @view, locals || {} - ActiveSupport::Notifications.instrument("render_template.action_view", - :identifier => template.identifier, :layout => layout.try(:virtual_path)) do - - content = template.render(view, locals) { |*name| view._layout_for(*name) } - - if layout - view.store_content_for(:layout, content) - content = render_layout(layout, locals) + render_with_layout(layout_name, locals) do |layout| + instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do + template.render(view, locals) { |*name| view._layout_for(*name) } end + end + end + + def render_with_layout(path, locals) #:nodoc: + layout = path && find_layout(path, locals.keys) + content = yield(layout) + if layout + view = @view + view.store_content_for(:layout, content) + layout.render(view, locals){ |*name| view._layout_for(*name) } + else content end end |