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.rb120
1 files changed, 89 insertions, 31 deletions
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 54a0ba96ff..c5d5540510 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -32,7 +32,7 @@ module ActionView
#
# <%= render :partial => "account", :object => @buyer %>
#
- # would provide the +@buyer+ object to the partial, available under the local variable +account+ and is
+ # would provide the <tt>@buyer</tt> object to the partial, available under the local variable +account+ and is
# equivalent to:
#
# <%= render :partial => "account", :locals => { :account => @buyer } %>
@@ -82,16 +82,17 @@ module ActionView
#
# This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from.
#
- # == Rendering objects with the RecordIdentifier
+ # == Rendering objects that respond to `to_partial_path`
#
- # Instead of explicitly naming the location of a partial, you can also let the RecordIdentifier do the work if
- # you're following its conventions for RecordIdentifier#partial_path. Examples:
+ # Instead of explicitly naming the location of a partial, you can also let PartialRenderer do the work
+ # and pick the proper path by checking `to_partial_path` method.
#
- # # @account is an Account instance, so it uses the RecordIdentifier to replace
+ # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
# # <%= render :partial => "accounts/account", :locals => { :account => @account} %>
# <%= render :partial => @account %>
#
- # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
+ # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
+ # # that's why we can replace:
# # <%= render :partial => "posts/post", :collection => @posts %>
# <%= render :partial => @posts %>
#
@@ -106,13 +107,14 @@ module ActionView
# # Instead of <%= render :partial => "account", :locals => { :account => @buyer } %>
# <%= render "account", :account => @buyer %>
#
- # # @account is an Account instance, so it uses the RecordIdentifier to replace
- # # <%= render :partial => "accounts/account", :locals => { :account => @account } %>
- # <%= render(@account) %>
+ # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
+ # # <%= render :partial => "accounts/account", :locals => { :account => @account} %>
+ # <%= render @account %>
#
- # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
+ # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
+ # # that's why we can replace:
# # <%= render :partial => "posts/post", :collection => @posts %>
- # <%= render(@posts) %>
+ # <%= render @posts %>
#
# == Rendering partials with layouts
#
@@ -156,6 +158,47 @@ module ActionView
# Name: <%= user.name %>
# </div>
#
+ # If a collection is given, the layout will be rendered once for each item in
+ # the collection. Just think these two snippets have the same output:
+ #
+ # <%# app/views/users/_user.html.erb %>
+ # Name: <%= user.name %>
+ #
+ # <%# app/views/users/index.html.erb %>
+ # <%# This does not use layouts %>
+ # <ul>
+ # <% users.each do |user| -%>
+ # <li>
+ # <%= render :partial => "user", :locals => { :user => user } %>
+ # </li>
+ # <% end -%>
+ # </ul>
+ #
+ # <%# app/views/users/_li_layout.html.erb %>
+ # <li>
+ # <%= yield %>
+ # </li>
+ #
+ # <%# app/views/users/index.html.erb %>
+ # <ul>
+ # <%= render :partial => "user", :layout => "li_layout", :collection => users %>
+ # </ul>
+ #
+ # Given two users whose names are Alice and Bob, these snippets return:
+ #
+ # <ul>
+ # <li>
+ # Name: Alice
+ # </li>
+ # <li>
+ # Name: Bob
+ # </li>
+ # </ul>
+ #
+ # The current object being rendered, as well as the object_counter, will be
+ # available as local variables inside the layout template under the same names
+ # as available in the partial.
+ #
# You can also apply a layout to a block within any template:
#
# <%# app/views/users/_chief.html.erb &>
@@ -205,19 +248,26 @@ module ActionView
# Deadline: <%= user.deadline %>
# <%- end -%>
# <% end %>
- class PartialRenderer < AbstractRenderer #:nodoc:
- PARTIAL_NAMES = Hash.new { |h,k| h[k] = {} }
+ class PartialRenderer < AbstractRenderer
+ PREFIXED_PARTIAL_NAMES = Hash.new { |h,k| h[k] = {} }
def initialize(*)
super
@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
+ @lookup_context.rendered_format ||= begin
+ if @template && @template.formats.present?
+ @template.formats.first
+ else
+ formats.first
+ end
+ end
+
if @collection
instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
render_collection
@@ -245,7 +295,7 @@ module ActionView
object, as = @object, @variable
if !block && (layout = @options[:layout])
- layout = find_template(layout)
+ layout = find_template(layout, @locals.keys + [@variable])
end
object ||= locals[as]
@@ -297,7 +347,6 @@ module ActionView
"and is followed by any combinations of letters, numbers, or underscores.")
end
- extract_format(@path, @details)
self
end
@@ -332,12 +381,19 @@ module ActionView
segments, locals, template = [], @locals, @template
as, counter = @variable, @variable_counter
+ if layout = @options[:layout]
+ layout = find_template(layout, @locals.keys + [@variable, @variable_counter])
+ end
+
locals[counter] = -1
@collection.each do |object|
locals[counter] += 1
locals[as] = object
- segments << template.render(@view, locals)
+
+ content = template.render(@view, locals)
+ content = layout.render(@view, locals) { content } if layout
+ segments << content
end
segments
@@ -366,37 +422,39 @@ module ActionView
path = if object.respond_to?(:to_partial_path)
object.to_partial_path
else
- klass = object.class
- if klass.respond_to?(:model_name)
- 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."
- klass.model_name.partial_path
- else
- raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object that returns a valid partial path.")
- end
+ raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
end
- @partial_names[path] ||= path.dup.tap do |object_path|
- merge_prefix_into_object_path(@context_prefix, object_path)
+ if @view.prefix_partial_path_with_controller_namespace
+ prefixed_partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
+ else
+ path
end
end
+ def prefixed_partial_names
+ @prefixed_partial_names ||= PREFIXED_PARTIAL_NAMES[@context_prefix]
+ end
+
def merge_prefix_into_object_path(prefix, object_path)
if prefix.include?(?/) && object_path.include?(?/)
- overlap = []
+ prefixes = []
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]
+ break if dir == object_path_array[index]
+ prefixes << dir
end
- object_path.gsub!(/^#{overlap.join('/')}\//,'')
- object_path.insert(0, "#{File.dirname(prefix)}/")
+ (prefixes << object_path).join("/")
+ else
+ object_path
end
end
def retrieve_variable(path)
- variable = @options[:as].try(:to_sym) || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ variable = @options.fetch(:as) { path[%r'_?(\w+)(\.\w+)*$', 1] }.try(:to_sym)
variable_counter = :"#{variable}_counter" if @collection
[variable, variable_counter]
end