diff options
Diffstat (limited to 'actionpack/lib/action_view/render')
-rw-r--r-- | actionpack/lib/action_view/render/partials.rb | 140 | ||||
-rw-r--r-- | actionpack/lib/action_view/render/rendering.rb | 104 |
2 files changed, 112 insertions, 132 deletions
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index ccb14d513a..b7b14e9007 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -172,131 +172,89 @@ module ActionView module Partials extend ActiveSupport::Memoizable extend ActiveSupport::Concern - + included do - attr_accessor :_partial + attr_accessor :_partial + end + + module ClassMethods + def _partial_names + @_partial_names ||= ActiveSupport::ConcurrentHash.new + end end - def _render_partial_from_controller(*args) + def render_partial(options) @assigns_added = false - _render_partial(*args) + _render_partial(options) end - def _render_partial(options = {}) #:nodoc: + def _render_partial(options, &block) #:nodoc: options[:locals] ||= {} - case path = partial = options[:partial] - when *_array_like_objects - return _render_partial_collection(partial, options) - else - if partial.is_a?(ActionView::Helpers::FormBuilder) - path = partial.class.to_s.demodulize.underscore.sub(/_builder$/, '') - options[:locals].merge!(path.to_sym => partial) - elsif !partial.is_a?(String) - options[:object] = object = partial - path = ActionController::RecordIdentifier.partial_path(object, controller_path) - end - _, _, prefix, object = parts = partial_parts(path, options) - parts[1] = {:formats => parts[1]} - template = find_by_parts(*parts) - _render_partial_object(template, options, (object unless object == true)) + path = partial = options[:partial] + + if partial.respond_to?(:to_ary) + return _render_partial_collection(partial, options, &block) + elsif !partial.is_a?(String) + options[:object] = object = partial + path = _partial_path(object) end + + _render_partial_object(_pick_partial_template(path), options, &block) end private - def partial_parts(name, options) - segments = name.split("/") - parts = segments.pop.split(".") - - case parts.size - when 1 - parts - when 2, 3 - extension = parts.delete_at(1).to_sym - if formats.include?(extension) - self.formats.replace [extension] + def _partial_path(object) + self.class._partial_names[[controller.class, object.class]] ||= begin + name = object.class.model_name + if controller_path && controller_path.include?("/") + File.join(File.dirname(controller_path), name.partial_path) + else + name.partial_path end - parts.pop if parts.size == 2 end - - path = parts.join(".") - prefix = segments[0..-1].join("/") - prefix = prefix.blank? ? controller_path : prefix - parts = [path, formats, prefix] - parts.push options[:object] || true - end - - def _render_partial_with_block(layout, block, options) - @_proc_for_layout = block - concat(_render_partial(options.merge(:partial => layout))) - ensure - @_proc_for_layout = nil end - def _render_partial_with_layout(layout, options) - if layout - prefix = controller && !layout.include?("/") ? controller.controller_path : nil - layout = find_by_parts(layout, {:formats => formats}, prefix, true) - end - content = _render_partial(options) - return _render_content_with_layout(content, layout, options[:locals]) - end + def _render_partial_template(template, locals, object, options = {}, &block) + options[:_template] = template + locals[:object] = locals[template.variable_name] = object + locals[options[:as]] = object if options[:as] - def _array_like_objects - array_like = [Array] - if defined?(ActiveRecord) - array_like.push(ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope) - end - array_like + _render_single_template(template, locals, &block) end - def _render_partial_object(template, options, object = nil) + def _render_partial_object(template, options, &block) if options.key?(:collection) - _render_partial_collection(options.delete(:collection), options, template) + _render_partial_collection(options.delete(:collection), options, template, &block) else locals = (options[:locals] ||= {}) - object ||= locals[:object] || locals[template.variable_name] - - _set_locals(object, locals, template, options) - - options[:_template] = template - - _render_template(template, locals) - end - end + object = options[:object] || locals[:object] || locals[template.variable_name] - def _set_locals(object, locals, template, options) - locals[:object] = locals[template.variable_name] = object - locals[options[:as]] = object if options[:as] + _render_partial_template(template, locals, object, options, &block) + end end - def _render_partial_collection(collection, options = {}, passed_template = nil) #:nodoc: + def _render_partial_collection(collection, options = {}, template = nil, &block) #:nodoc: return nil if collection.blank? - - spacer = options[:spacer_template] ? _render_partial(:partial => options[:spacer_template]) : '' - locals = (options[:locals] ||= {}) - index, @_partial_path = 0, nil - collection.map do |object| - options[:_template] = template = passed_template || begin - _partial_path = - ActionController::RecordIdentifier.partial_path(object, controller_path) - template = _pick_partial_template(_partial_path) - end + if options.key?(:spacer_template) + spacer = _render_partial(:partial => options[:spacer_template]) + end - _set_locals(object, locals, template, options) - locals[template.counter_name] = index - + locals, index = options[:locals] || {}, 0 + + collection.map do |object| + tmp = template || _pick_partial_template(_partial_path(object)) + locals[tmp.counter_name] = index index += 1 - - _render_template(template, locals) + + _render_partial_template(tmp, locals, object, options, &block) end.join(spacer) end def _pick_partial_template(partial_path) #:nodoc: - prefix = controller_path unless partial_path.include?('/') + prefix = controller_path unless partial_path.include?(?/) find_by_parts(partial_path, {:formats => formats}, prefix, true) end - memoize :_pick_partial_template end end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index 162e38c484..9c25fab6bb 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -10,24 +10,25 @@ module ActionView # # 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. - def render(options = {}, local_assigns = {}, &block) #:nodoc: - local_assigns ||= {} - - @exempt_from_layout = true - + def render(options = {}, locals = {}, &block) #:nodoc: case options + when String, NilClass + _render_partial(:partial => options, :locals => locals || {}) when Hash - options[:locals] ||= {} layout = options[:layout] - - return _render_partial_with_layout(layout, options) if options.key?(:partial) - return _render_partial_with_block(layout, block, options) if block_given? - + + if block_given? + return concat(_render_partial(options.merge(:partial => layout), &block)) + elsif options.key?(:partial) + layout = _pick_partial_template(layout) if layout + return _render_content(_render_partial(options), layout, options[:locals]) + end + layout = find_by_parts(layout, {:formats => formats}) if layout - + if file = options[:file] template = find_by_parts(file, {:formats => formats}) - _render_template_with_layout(template, layout, :locals => options[:locals]) + _render_template(template, layout, :locals => options[:locals] || {}) elsif inline = options[:inline] _render_inline(inline, layout, options) elsif text = options[:text] @@ -35,35 +36,33 @@ module ActionView end when :update update_page(&block) - when String, NilClass - _render_partial(:partial => options, :locals => local_assigns) end end - - def _render_content_with_layout(content, layout, locals) + + def _render_content(content, layout, locals) return content unless layout - + locals ||= {} if controller && layout @_layout = layout.identifier logger.info("Rendering template within #{layout.identifier}") if logger end - + begin old_content, @_content_for[:layout] = @_content_for[:layout], content @cached_content_for_layout = @_content_for[:layout] - _render_template(layout, locals) + _render_single_template(layout, locals) ensure @_content_for[:layout] = old_content end end - # You can think of a layout as a method that is called with a block. This method - # returns the block that the layout is called with. If the user calls yield :some_name, - # the block, by default, returns content_for(:some_name). If the user calls yield, - # the default block returns content_for(:layout). + # You can think of a layout as a method that is called with a block. _layout_for + # returns the contents that are yielded to the layout. If the user calls yield + # :some_name, the block, by default, returns content_for(:some_name). If the user + # calls yield, the default block returns content_for(:layout). # # The user can override this default by passing a block to the layout. # @@ -92,15 +91,28 @@ module ActionView # In this case, the layout would receive the block passed into <tt>render :layout</tt>, # and the Struct specified in the layout would be passed into the block. The result # would be <html>Hello David</html>. - def layout_proc(name) - @_default_layout ||= proc { |*names| @_content_for[names.first || :layout] } - !@_content_for.key?(name) && @_proc_for_layout || @_default_layout + def _layout_for(names, &block) + with_output_buffer do + # This is due to the potentially ambiguous use of yield when + # a block is passed in to a template *and* there is a content_for() + # of the same name. Suggested solution: require explicit use of content_for + # in these ambiguous cases. + # + # We would be able to continue supporting yield in all non-ambiguous + # cases. Question: should we deprecate yield in favor of content_for + # and reserve yield for cases where there is a yield into a real block? + if @_content_for.key?(names.first) || !block_given? + return @_content_for[names.first || :layout] + else + return yield(names) + end + end end - def _render_template(template, local_assigns = {}) + def _render_single_template(template, locals = {}, &block) with_template(template) do - template.render(self, local_assigns) do |*names| - capture(*names, &layout_proc(names.first)) + template.render(self, locals) do |*names| + _layout_for(names, &block) end end rescue Exception => e @@ -115,32 +127,42 @@ module ActionView def _render_inline(inline, layout, options) handler = Template.handler_class_for_extension(options[:type] || "erb") template = Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {}) - content = _render_template(template, options[:locals] || {}) - layout ? _render_content_with_layout(content, layout, options[:locals]) : content + content = _render_single_template(template, options[:locals] || {}) + layout ? _render_content(content, layout, options[:locals]) : content end def _render_text(text, layout, options) - layout ? _render_content_with_layout(text, layout, options[:locals]) : text + layout ? _render_content(text, layout, options[:locals]) : text end - def _render_template_from_controller(*args) + # This is the API to render a ViewContext's template from a controller. + # + # Internal Options: + # _template:: The Template object to render + # _layout:: The layout, if any, to wrap the Template in + # _partial:: true if the template is a partial + def render_template(options) @assigns_added = nil - _render_template_with_layout(*args) + template, layout, partial = options.values_at(:_template, :_layout, :_partial) + _render_template(template, layout, options, partial) end - def _render_template_with_layout(template, layout = nil, options = {}, partial = false) - logger && logger.info("Rendering #{template.identifier}#{' (#{options[:status]})' if options[:status]}") + def _render_template(template, layout = nil, options = {}, partial = nil) + logger && logger.info do + msg = "Rendering #{template.identifier}" + msg << " (#{options[:status]})" if options[:status] + msg + end locals = options[:locals] || {} content = if partial - object = partial unless partial == true - _render_partial_object(template, options, object) + _render_partial_object(template, options) else - _render_template(template, locals) + _render_single_template(template, locals) end - - layout ? _render_content_with_layout(content, layout, locals) : content + + _render_content(content, layout, locals) end end end
\ No newline at end of file |