diff options
author | Michael Koziarski <michael@koziarski.com> | 2008-03-03 04:01:35 +0000 |
---|---|---|
committer | Michael Koziarski <michael@koziarski.com> | 2008-03-03 04:01:35 +0000 |
commit | 51b6619d4e78d9948952dab4709274ab57f41206 (patch) | |
tree | bab7a4d988c9349f4d8241e38e09f30d62b0049b /actionpack/lib/action_view | |
parent | 10d9d0b6fe023df1be0d87ca95bb739bb7eb30ba (diff) | |
download | rails-51b6619d4e78d9948952dab4709274ab57f41206.tar.gz rails-51b6619d4e78d9948952dab4709274ab57f41206.tar.bz2 rails-51b6619d4e78d9948952dab4709274ab57f41206.zip |
Refactor partial rendering into a PartialTemplate class. [Pratik]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8976 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'actionpack/lib/action_view')
-rw-r--r-- | actionpack/lib/action_view/base.rb | 48 | ||||
-rw-r--r-- | actionpack/lib/action_view/partial_template.rb | 61 | ||||
-rw-r--r-- | actionpack/lib/action_view/partials.rb | 94 | ||||
-rw-r--r-- | actionpack/lib/action_view/template.rb | 22 | ||||
-rw-r--r-- | actionpack/lib/action_view/template_handler.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/template_handlers/compilable.rb | 4 |
6 files changed, 109 insertions, 122 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index f5346bd480..143428c6d9 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -292,24 +292,22 @@ If you are rendering a subtemplate, you must now use controller-like partial syn # Renders the template present at <tt>template_path</tt> (relative to the view_paths array). # The hash in <tt>local_assigns</tt> is made available as local variables. - def render(options = {}, old_local_assigns = {}, &block) #:nodoc: + def render(options = {}, local_assigns = {}, &block) #:nodoc: if options.is_a?(String) - render_file(options, true, old_local_assigns) + render_file(options, true, local_assigns) elsif options == :update update_page(&block) elsif options.is_a?(Hash) options = options.reverse_merge(:locals => {}, :use_full_path => true) - if options[:layout] - path, partial_name = partial_pieces(options.delete(:layout)) - + if partial_layout = options.delete(:layout) if block_given? wrap_content_for_layout capture(&block) do - concat(render(options.merge(:partial => "#{path}/#{partial_name}")), block.binding) + concat(render(options.merge(:partial => partial_layout)), block.binding) end else wrap_content_for_layout render(options) do - render(options.merge(:partial => "#{path}/#{partial_name}")) + render(options.merge(:partial => partial_layout)) end end elsif options[:file] @@ -325,17 +323,8 @@ If you are rendering a subtemplate, you must now use controller-like partial syn end end - # Renders the +template+ which is given as a string as either erb or builder depending on <tt>template_extension</tt>. - # The hash in <tt>local_assigns</tt> is made available as local variables. def render_template(template) #:nodoc: - handler = template.handler - @current_render_extension = template.extension - - if handler.compilable? - compile_and_render_template(handler, template) - else - handler.render(template.source, template.locals) - end + template.render end # Returns true is the file may be rendered implicitly. @@ -369,7 +358,7 @@ If you are rendering a subtemplate, you must now use controller-like partial syn end end - private + private def wrap_content_for_layout(content) original_content_for_layout = @content_for_layout @content_for_layout = content @@ -388,24 +377,11 @@ If you are rendering a subtemplate, you must now use controller-like partial syn def assign_variables_from_controller @assigns.each { |key, value| instance_variable_set("@#{key}", value) } end - - # Render the provided template with the given local assigns. If the template has not been rendered with the provided - # local assigns yet, or if the template has been updated on disk, then the template will be compiled to a method. - # - # Either, but not both, of template and file_path may be nil. If file_path is given, the template - # will only be read if it has to be compiled. - # - def compile_and_render_template(handler, template) #:nodoc: - # compile the given template, if necessary - handler.compile_template(template) - - # Get the method name for this template and run it - method_name = @@method_names[template.method_key] - evaluate_assigns - - send(method_name, template.locals) do |*name| - instance_variable_get "@content_for_#{name.first || 'layout'}" - end + + def execute(template) + send(template.method, template.locals) do |*names| + instance_variable_get "@content_for_#{names.first || 'layout'}" + end end end end diff --git a/actionpack/lib/action_view/partial_template.rb b/actionpack/lib/action_view/partial_template.rb new file mode 100644 index 0000000000..a470c915cd --- /dev/null +++ b/actionpack/lib/action_view/partial_template.rb @@ -0,0 +1,61 @@ +module ActionView #:nodoc: + class PartialTemplate < Template #:nodoc: + + attr_reader :variable_name, :object + + def initialize(view, partial_path, object = nil, locals = {}) + @path, @variable_name = extract_partial_name_and_path(view, partial_path) + super(view, @path, true, locals) + add_object_to_local_assigns!(object) + + # This is needed here in order to compile template with knowledge of 'counter' + initialize_counter + + # Prepare early. This is a performance optimization for partial collections + prepare! + end + + def render + ActionController::Base.benchmark("Rendered #{@path}", Logger::DEBUG, false) do + @handler.render(self) + end + end + + def render_member(object) + @locals[@counter_name] += 1 + @locals[:object] = @locals[@variable_name] = object + render + end + + private + + def add_object_to_local_assigns!(object) + @locals[:object] ||= + @locals[@variable_name] ||= + if object.is_a?(ActionView::Base::ObjectWrapper) + object.value + else + object + end || @view.controller.instance_variable_get("@#{variable_name}") + end + + def extract_partial_name_and_path(view, partial_path) + path, partial_name = partial_pieces(view, partial_path) + [File.join(path, "_#{partial_name}"), partial_name.split('/').last.split('.').first.to_sym] + end + + def partial_pieces(view, partial_path) + if partial_path.include?('/') + return File.dirname(partial_path), File.basename(partial_path) + else + return view.controller.class.controller_path, partial_path + end + end + + def initialize_counter + @counter_name ||= "#{@variable_name}_counter".to_sym + @locals[@counter_name] = 0 + end + + end +end diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index e2aa98a319..7f1963fb29 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -103,23 +103,11 @@ module ActionView # As you can see, the :locals hash is shared between both the partial and its layout. module Partials private - def render_partial(partial_path, object_assigns = nil, local_assigns = nil) #:nodoc: + def render_partial(partial_path, object_assigns = nil, local_assigns = {}) #:nodoc: case partial_path when String, Symbol, NilClass - path, partial_name = partial_pieces(partial_path) - full_partial_path = File.join(path, "_#{partial_name}") - object = extracting_object(partial_name, object_assigns) - local_assigns = local_assigns ? local_assigns.clone : {} - add_counter_to_local_assigns!(partial_name, local_assigns) - add_object_to_local_assigns!(partial_name, local_assigns, object) - - if logger && logger.debug? - ActionController::Base.benchmark("Rendered #{full_partial_path}", Logger::DEBUG, false) do - render(full_partial_path, local_assigns) - end - else - render(full_partial_path, local_assigns) - end + # Render the template + ActionView::PartialTemplate.new(self, partial_path, object_assigns, local_assigns).render when ActionView::Helpers::FormBuilder builder_partial_path = partial_path.class.to_s.demodulize.underscore.sub(/_builder$/, '') render_partial(builder_partial_path, object_assigns, (local_assigns || {}).merge(builder_partial_path.to_sym => partial_path)) @@ -132,75 +120,21 @@ module ActionView "" end else - render_partial( - ActionController::RecordIdentifier.partial_path(partial_path), - partial_path, local_assigns) + render_partial(ActionController::RecordIdentifier.partial_path(partial_path), partial_path, local_assigns) end end - def render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = nil) #:nodoc: - collection_of_partials = Array.new - counter_name = partial_counter_name(partial_name) + def render_partial_collection(partial_path, collection, partial_spacer_template = nil, local_assigns = {}) #:nodoc: + return " " if collection.empty? + local_assigns = local_assigns ? local_assigns.clone : {} - collection.each_with_index do |element, counter| - local_assigns[counter_name] = counter - collection_of_partials.push(render_partial(partial_name, element, local_assigns)) - end - - return " " if collection_of_partials.empty? - - if partial_spacer_template - spacer_path, spacer_name = partial_pieces(partial_spacer_template) - collection_of_partials.join(render("#{spacer_path}/_#{spacer_name}")) - else - collection_of_partials.join - end - end - - alias_method :render_collection_of_partials, :render_partial_collection - - def partial_pieces(partial_path) - if partial_path.include?('/') - return File.dirname(partial_path), File.basename(partial_path) - else - return controller.class.controller_path, partial_path - end - end - - def partial_counter_name(partial_name) - "#{partial_variable_name(partial_name)}_counter".intern - end - - def partial_variable_name(partial_name) - @@partial_variable_names ||= {} - @@partial_variable_names[partial_name] ||= - partial_name.split('/').last.split('.').first.intern - end - - def extracting_object(partial_name, object_assigns) - variable_name = partial_variable_name(partial_name) - if object_assigns.nil? - controller.instance_variable_get("@#{variable_name}") - else - object_assigns - end - end - - def add_counter_to_local_assigns!(partial_name, local_assigns) - counter_name = partial_counter_name(partial_name) - local_assigns[counter_name] = 1 unless local_assigns.has_key?(counter_name) - end - - def add_object_to_local_assigns!(partial_name, local_assigns, object) - variable_name = partial_variable_name(partial_name) - - local_assigns[:object] ||= - local_assigns[variable_name] ||= - if object.is_a?(ActionView::Base::ObjectWrapper) - object.value - else - object - end || controller.instance_variable_get("@#{variable_name}") + template = ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns) + + spacer = partial_spacer_template ? render(:partial => partial_spacer_template) : '' + + collection.map do |element| + template.render_member(element) + end.join(spacer) end end end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index a814d7b4e5..9f979e2e5d 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -2,7 +2,7 @@ module ActionView #:nodoc: class Template #:nodoc: attr_accessor :locals - attr_reader :handler, :path, :source, :extension, :filename, :path_without_extension + attr_reader :handler, :path, :source, :extension, :filename, :path_without_extension, :method def initialize(view, path_or_source, use_full_path, locals = {}, inline = false, inline_type = nil) @view = view @@ -19,6 +19,12 @@ module ActionView #:nodoc: @extension = inline_type end @locals = locals || {} + @handler = @view.class.handler_class_for_extension(@extension).new(@view) + end + + def render + prepare! + @handler.render(self) end def source @@ -29,13 +35,19 @@ module ActionView #:nodoc: @method_key ||= (@filename || @source) end - def handler - @handler ||= @view.class.handler_class_for_extension(@extension).new(@view) - end - def base_path_for_exception @finder.find_base_path_for("#{@path_without_extension}.#{@extension}") || @finder.view_paths.first end + + def prepare! + @view.send :evaluate_assigns + @view.current_render_extension = @extension + + if @handler.compilable? + @handler.compile_template(self) # compile the given template, if necessary + @method = @view.method_names[method_key] # Set the method name for this template and run it + end + end private diff --git a/actionpack/lib/action_view/template_handler.rb b/actionpack/lib/action_view/template_handler.rb index 687c0db4c6..ec407e3fb3 100644 --- a/actionpack/lib/action_view/template_handler.rb +++ b/actionpack/lib/action_view/template_handler.rb @@ -13,7 +13,7 @@ module ActionView @view = view end - def render(template, local_assigns) + def render(template) end def compile(template) diff --git a/actionpack/lib/action_view/template_handlers/compilable.rb b/actionpack/lib/action_view/template_handlers/compilable.rb index 110c90af0a..35c74f6c51 100644 --- a/actionpack/lib/action_view/template_handlers/compilable.rb +++ b/actionpack/lib/action_view/template_handlers/compilable.rb @@ -24,6 +24,10 @@ module ActionView true end end + + def render(template) + @view.send :execute, template + end # Compile and evaluate the template's code def compile_template(template) |