aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view/renderer
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@gmail.com>2010-10-10 11:03:18 +0200
committerJosé Valim <jose.valim@gmail.com>2010-10-10 12:43:26 +0200
commitb88f4ca93bcaef9a6bfd21d95acc8f432a3c8e5c (patch)
tree94ca53a9d78f749b18ab6b325afc54a1c991f1ca /actionpack/lib/action_view/renderer
parentab2f2b22a6d0656f719c294d40e35d21c752ba8c (diff)
downloadrails-b88f4ca93bcaef9a6bfd21d95acc8f432a3c8e5c.tar.gz
rails-b88f4ca93bcaef9a6bfd21d95acc8f432a3c8e5c.tar.bz2
rails-b88f4ca93bcaef9a6bfd21d95acc8f432a3c8e5c.zip
Clean up the house before moving in the new furniture.
This commit moves all the template rendering logic that was hanging around AV::Base to renderer objects.
Diffstat (limited to 'actionpack/lib/action_view/renderer')
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb36
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb167
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb66
3 files changed, 269 insertions, 0 deletions
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
new file mode 100644
index 0000000000..f9fa63ce7f
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -0,0 +1,36 @@
+module ActionView
+ class AbstractRenderer #:nodoc:
+ attr_reader :vew, :lookup_context
+
+ delegate :find_template, :template_exists?, :with_fallbacks, :update_details,
+ :with_layout_format, :formats, :to => :lookup_context
+
+ def initialize(view)
+ @view = view
+ @lookup_context = view.lookup_context
+ end
+
+ def render
+ 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)
+ return yield unless value.is_a?(String)
+ @@formats_regexp ||= /\.(#{Mime::SET.symbols.join('|')})$/
+
+ if value.sub!(@@formats_regexp, "")
+ update_details(:formats => [$1.to_sym]){ yield }
+ else
+ yield
+ end
+ 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
new file mode 100644
index 0000000000..3be1702f9e
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -0,0 +1,167 @@
+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)
+ super
+ @partial_names = PARTIAL_NAMES[@view.controller.class.name]
+ end
+
+ def setup(options, block)
+ partial = options[:partial]
+
+ @options = options
+ @locals = options[:locals] || {}
+ @block = block
+
+ if String === partial
+ @object = options[:object]
+ @path = partial
+ @collection = collection
+ else
+ @object = partial
+
+ if @collection = collection_from_object || collection
+ paths = @collection_data = @collection.map { |o| partial_path(o) }
+ @path = paths.uniq.size == 1 ? paths.first : nil
+ else
+ @path = partial_path
+ end
+ end
+
+ if @path
+ @variable, @variable_counter = retrieve_variable(@path)
+ else
+ paths.map! { |path| retrieve_variable(path).unshift(path) }
+ end
+
+ self
+ end
+
+ def render
+ wrap_formats(@path) do
+ identifier = ((@template = find_partial) ? @template.identifier : @path)
+
+ if @collection
+ N.instrument("render_collection.action_view", :identifier => identifier || "collection", :count => @collection.size) do
+ render_collection
+ end
+ else
+ N.instrument("render_partial.action_view", :identifier => identifier) do
+ render_partial
+ end
+ end
+ end
+ end
+
+ def render_collection
+ return nil if @collection.blank?
+
+ if @options.key?(:spacer_template)
+ spacer = find_template(@options[:spacer_template]).render(@view, @locals)
+ end
+
+ result = @template ? collection_with_template : collection_without_template
+ result.join(spacer).html_safe
+ end
+
+ def render_partial
+ locals, view, block = @locals, @view, @block
+ object, as = @object, @variable
+
+ if !block && (layout = @options[:layout])
+ layout = find_template(layout)
+ end
+
+ object ||= locals[as]
+ locals[as] = object
+
+ content = @template.render(view, locals) do |*name|
+ view._layout_for(*name, &block)
+ end
+
+ content = render_layout(layout, locals){ content } if layout
+ content
+ end
+
+ private
+
+ def collection
+ if @options.key?(:collection)
+ @options[:collection] || []
+ end
+ end
+
+ def collection_from_object
+ if @object.respond_to?(:to_ary)
+ @object
+ end
+ end
+
+ def find_partial
+ if path = @path
+ locals = @locals.keys
+ locals << @variable
+ locals << @variable_counter if @collection
+ find_template(path, locals)
+ end
+ end
+
+ def find_template(path=@path, locals=@locals.keys)
+ prefix = @view.controller_path unless path.include?(?/)
+ @lookup_context.find_template(path, prefix, true, locals)
+ end
+
+ def collection_with_template
+ segments, locals, template = [], @locals, @template
+ as, counter = @variable, @variable_counter
+
+ locals[counter] = -1
+
+ @collection.each do |object|
+ locals[counter] += 1
+ locals[as] = object
+ segments << template.render(@view, locals)
+ end
+
+ segments
+ end
+
+ def collection_without_template
+ segments, locals, collection_data = [], @locals, @collection_data
+ index, template, cache = -1, nil, {}
+ keys = @locals.keys
+
+ @collection.each_with_index do |object, i|
+ path, *data = collection_data[i]
+ template = (cache[path] ||= find_template(path, keys + data))
+ locals[data[0]] = object
+ locals[data[1]] = (index += 1)
+ segments << template.render(@view, locals)
+ end
+
+ @template = template
+ segments
+ end
+
+ def partial_path(object = @object)
+ @partial_names[object.class.name] ||= begin
+ object = object.to_model if object.respond_to?(:to_model)
+
+ object.class.model_name.partial_path.dup.tap do |partial|
+ path = @view.controller_path
+ partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
+ end
+ end
+ end
+
+ def retrieve_variable(path)
+ variable = @options[:as] || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ variable_counter = :"#{variable}_counter" if @collection
+ [variable, variable_counter]
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
new file mode 100644
index 0000000000..3acc68dcac
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -0,0 +1,66 @@
+require 'action_view/renderer/abstract_renderer'
+
+module ActionView
+ class TemplateRenderer < AbstractRenderer #:nodoc:
+ 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)
+ end
+ end
+
+ # Determine the template to be rendered using the given options.
+ def determine_template(options) #:nodoc:
+ keys = (options[:locals] ||= {}).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)
+ 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?(:template)
+ options[:template].respond_to?(:render) ?
+ options[:template] : find_template(options[:template], options[:prefix], false, keys)
+ end
+ end
+
+ # 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
+
+ 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)
+ end
+
+ content
+ end
+ end
+
+ # This is the method which actually finds the layout using details in the lookup
+ # context object. If no layout is found, it checks if at least a layout with
+ # the given name exists across all details before raising the error.
+ def find_layout(layout, keys)
+ begin
+ with_layout_format do
+ layout =~ /^\// ?
+ with_fallbacks { find_template(layout, nil, false, keys) } : find_template(layout, nil, false, keys)
+ end
+ rescue ActionView::MissingTemplate => e
+ update_details(:formats => nil) do
+ raise unless template_exists?(layout)
+ end
+ end
+ end
+ end
+end \ No newline at end of file