aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view/renderer/partial_renderer.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_view/renderer/partial_renderer.rb')
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb94
1 files changed, 55 insertions, 39 deletions
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index a351fbc04f..15cb9d0f76 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -12,8 +12,7 @@ module ActionView
#
# <%= render :partial => "account" %>
#
- # This would render "advertiser/_account.html.erb" and pass the instance variable @account in as a local variable
- # +account+ to the template for display.
+ # This would render "advertiser/_account.html.erb".
#
# In another template for Advertiser#buy, we could have:
#
@@ -28,32 +27,24 @@ module ActionView
#
# == The :as and :object options
#
- # By default <tt>ActionView::Partials::PartialRenderer</tt> has its object in a local variable with the same
- # name as the template. So, given
+ # By default <tt>ActionView::PartialRenderer</tt> doesn't have any local variables.
+ # The <tt>:object</tt> option can be used to pass an object to the partial. For instance:
#
- # <%= render :partial => "contract" %>
+ # <%= render :partial => "account", :object => @buyer %>
#
- # within contract we'll get <tt>@contract</tt> in the local variable +contract+, as if we had written
+ # would provide the +@buyer+ object to the partial, available under the local variable +account+ and is
+ # equivalent to:
#
- # <%= render :partial => "contract", :locals => { :contract => @contract } %>
+ # <%= render :partial => "account", :locals => { :account => @buyer } %>
#
# With the <tt>:as</tt> option we can specify a different name for said local variable. For example, if we
- # wanted it to be +agreement+ instead of +contract+ we'd do:
- #
- # <%= render :partial => "contract", :as => 'agreement' %>
+ # wanted it to be +user+ instead of +account+ we'd do:
#
- # The <tt>:object</tt> option can be used to directly specify which object is rendered into the partial;
- # useful when the template's object is elsewhere, in a different ivar or in a local variable for instance.
+ # <%= render :partial => "account", :object => @buyer, :as => 'user' %>
#
- # Revisiting a previous example we could have written this code:
+ # This is equivalent to
#
- # <%= render :partial => "account", :object => @buyer %>
- #
- # <% @advertisements.each do |ad| %>
- # <%= render :partial => "ad", :object => ad %>
- # <% end %>
- #
- # The <tt>:object</tt> and <tt>:as</tt> options can be used together.
+ # <%= render :partial => "account", :locals => { :user => @buyer } %>
#
# == Rendering a collection of partials
#
@@ -215,27 +206,25 @@ module ActionView
# <%- end -%>
# <% end %>
class PartialRenderer < AbstractRenderer #:nodoc:
- PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
+ PARTIAL_NAMES = Hash.new { |h,k| h[k] = {} }
def initialize(*)
super
- @partial_names = PARTIAL_NAMES[@lookup_context.prefixes.first]
+ @context_prefix = @lookup_context.prefixes.first
+ @partial_names = PARTIAL_NAMES[@context_prefix]
end
def render(context, options, block)
setup(context, options, block)
+ identifier = (@template = find_partial) ? @template.identifier : @path
- wrap_formats(@path) do
- identifier = ((@template = find_partial) ? @template.identifier : @path)
-
- if @collection
- instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
- render_collection
- end
- else
- instrument(:partial, :identifier => identifier) do
- render_partial
- end
+ if @collection
+ instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
+ render_collection
+ end
+ else
+ instrument(:partial, :identifier => identifier) do
+ render_partial
end
end
end
@@ -279,6 +268,7 @@ module ActionView
@options = options
@locals = options[:locals] || {}
@block = block
+ @details = extract_details(options)
if String === partial
@object = options[:object]
@@ -301,6 +291,13 @@ module ActionView
paths.map! { |path| retrieve_variable(path).unshift(path) }
end
+ if String === partial && @variable.to_s !~ /^[a-z_][a-zA-Z_0-9]*$/
+ raise ArgumentError.new("The partial name (#{partial}) is not a valid Ruby identifier; " +
+ "make sure your partial name starts with a letter or underscore, " +
+ "and is followed by any combinations of letters, numbers, or underscores.")
+ end
+
+ extract_format(@path, @details)
self
end
@@ -328,7 +325,7 @@ module ActionView
def find_template(path=@path, locals=@locals.keys)
prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
- @lookup_context.find_template(path, prefixes, true, locals)
+ @lookup_context.find_template(path, prefixes, true, locals, @details)
end
def collection_with_template
@@ -364,13 +361,32 @@ module ActionView
end
def partial_path(object = @object)
- @partial_names[object.class.name] ||= begin
- object = object.to_model if object.respond_to?(:to_model)
+ object = object.to_model if object.respond_to?(:to_model)
+
+ path = if object.respond_to?(:to_partial_path)
+ object.to_partial_path
+ else
+ ActiveSupport::Deprecation.warn "ActiveModel-compatible objects whose classes return a #model_name that responds to #partial_path are deprecated. Please respond to #to_partial_path directly instead."
+ object.class.model_name.partial_path
+ end
- object.class.model_name.partial_path.dup.tap do |partial|
- path = @lookup_context.prefixes.first
- partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
+ @partial_names[path] ||= path.dup.tap do |object_path|
+ merge_prefix_into_object_path(@context_prefix, object_path)
+ end
+ end
+
+ def merge_prefix_into_object_path(prefix, object_path)
+ if prefix.include?(?/) && object_path.include?(?/)
+ overlap = []
+ prefix_array = File.dirname(prefix).split('/')
+ object_path_array = object_path.split('/')[0..-3] # skip model dir & partial
+
+ prefix_array.each_with_index do |dir, index|
+ overlap << dir if dir == object_path_array[index]
end
+
+ object_path.gsub!(/^#{overlap.join('/')}\//,'')
+ object_path.insert(0, "#{File.dirname(prefix)}/")
end
end