Added PartialIteration class used when rendering collections

The iteration object is available as the local variable
"template_name_iteration" when rendering partials with collections.

It gives access to the +size+ of the collection beeing iterated over,
the current +index+ and two convinicence methods +first?+ and +last?+

"template_name_counter" variable is kept but is deprecated.

[Joel Junström + Lucas Uyezu]
This commit is contained in:
Joel Junström 2012-09-16 22:45:08 +02:00 committed by Rafael Mendonça França
parent 6947e3a2b5
commit 1f5b360466
7 changed files with 99 additions and 14 deletions

View File

@ -0,0 +1,19 @@
module ActionView
class PartialIteration # :nodoc:
attr_reader :size, :index
def initialize(size, index)
@size = size
@index = index
end
def first?
index == 0
end
def last?
index == size - 1
end
end
end

View File

@ -1,4 +1,5 @@
require 'thread_safe'
require "action_view/partial_iteration"
module ActionView
# = Action View Partials
@ -56,8 +57,12 @@ module ActionView
# <%= render partial: "ad", collection: @advertisements %>
#
# This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An
# iteration counter will automatically be made available to the template with a name of the form
# +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
# iteration object will automatically be made available to the template with a name of the form
# +partial_name_iteration+. The iteration object has knowledge about which index the current object has in
# the collection and the total size of the collection. The iteration object also has two convenience methods,
# +first?+ and +last?+. In the case of the example above, the template would be fed +ad_iteration+.
# For backwards compatibility the +partial_name_counter+ is still present and is mapped to the iteration's
# +index+ method.
#
# The <tt>:as</tt> option may be used when rendering partials.
#
@ -352,7 +357,7 @@ module ActionView
end
if @path
@variable, @variable_counter = retrieve_variable(@path, as)
@variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as)
@template_keys = retrieve_template_keys
else
paths.map! { |path| retrieve_variable(path, as).unshift(path) }
@ -385,7 +390,7 @@ module ActionView
def collection_with_template
view, locals, template = @view, @locals, @template
as, counter = @variable, @variable_counter
as, counter, iteration = @variable, @variable_counter, @variable_iteration
if layout = @options[:layout]
layout = find_template(layout, @template_keys)
@ -393,8 +398,11 @@ module ActionView
index = -1
@collection.map do |object|
locals[as] = object
locals[counter] = (index += 1)
index += 1
locals[as] = object
locals[counter] = index
locals[iteration] = PartialIteration.new(@collection.size, index)
content = template.render(view, locals)
content = layout.render(view, locals) { content } if layout
@ -410,10 +418,11 @@ module ActionView
index = -1
@collection.map do |object|
index += 1
path, as, counter = collection_data[index]
path, as, counter, iteration = collection_data[index]
locals[as] = object
locals[counter] = index
locals[as] = object
locals[counter] = index
locals[iteration] = PartialIteration.new(@collection.size, index)
template = (cache[path] ||= find_template(path, keys + [as, counter]))
template.render(view, locals)
@ -466,8 +475,11 @@ module ActionView
def retrieve_template_keys
keys = @locals.keys
keys << @variable if @object || @collection
keys << @variable_counter if @collection
keys << @variable if @object || @collection
if @collection
keys << @variable_counter
keys << @variable_iteration
end
keys
end
@ -477,8 +489,11 @@ module ActionView
raise_invalid_identifier(path) unless base =~ /\A_?([a-z]\w*)(\.\w+)*\z/
$1.to_sym
end
variable_counter = :"#{variable}_counter" if @collection
[variable, variable_counter]
if @collection
variable_counter = :"#{variable}_counter"
variable_iteration = :"#{variable}_iteration"
end
[variable, variable_counter, variable_iteration]
end
IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " +

View File

@ -536,6 +536,14 @@ class TestController < ApplicationController
render :partial => "customer_with_var", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :customer
end
def partial_collection_with_iteration
render partial: "customer_iteration", collection: [ Customer.new("david"), Customer.new("mary"), Customer.new('christine') ]
end
def partial_collection_with_as_and_iteration
render partial: "customer_iteration_with_as", collection: [ Customer.new("david"), Customer.new("mary"), Customer.new('christine') ], as: :client
end
def partial_collection_with_counter
render :partial => "customer_counter", :collection => [ Customer.new("david"), Customer.new("mary") ]
end
@ -1237,6 +1245,16 @@ class RenderTest < ActionController::TestCase
assert_equal "david david davidmary mary mary", @response.body
end
def test_partial_collection_with_iteration
get :partial_collection_with_iteration
assert_equal "3-0: david-first3-1: mary3-2: christine-last", @response.body
end
def test_partial_collection_with_as_and_iteration
get :partial_collection_with_as_and_iteration
assert_equal "3-0: david-first3-1: mary3-2: christine-last", @response.body
end
def test_partial_collection_with_counter
get :partial_collection_with_counter
assert_equal "david0mary1", @response.body

View File

@ -0,0 +1 @@
<%= customer_iteration_iteration.size %>-<%= customer_iteration_iteration.index %>: <%= customer_iteration.name %><%= '-first' if customer_iteration_iteration.first? %><%= '-last' if customer_iteration_iteration.last? %>

View File

@ -0,0 +1 @@
<%= client_iteration.size %>-<%= client_iteration.index %>: <%= client.name %><%= '-first' if client_iteration.first? %><%= '-last' if client_iteration.last? %>

View File

@ -0,0 +1,31 @@
require 'abstract_unit'
require 'action_view/partial_iteration'
class PartialIterationTest < ActiveSupport::TestCase
def test_has_size_and_index
iteration = ActionView::PartialIteration.new 3, 0
assert_equal 0, iteration.index, "should be at the first index"
assert_equal 3, iteration.size, "should have the size"
end
def test_first_is_true_when_current_is_at_the_first_index
iteration = ActionView::PartialIteration.new 3, 0
assert iteration.first?, "first when current is 0"
end
def test_first_is_false_unless_current_is_at_the_first_index
iteration = ActionView::PartialIteration.new 3, 1
assert !iteration.first?, "not first when current is 1"
end
def test_last_is_true_when_current_is_at_the_last_index
iteration = ActionView::PartialIteration.new 3, 2
assert iteration.last?, "last when current is 2"
end
def test_last_is_false_unless_current_is_at_the_last_index
iteration = ActionView::PartialIteration.new 3, 0
assert !iteration.last?, "not last when current is 0"
end
end

View File

@ -256,7 +256,7 @@ module RenderTestCases
end
def test_render_partial_collection_without_as
assert_equal "local_inspector,local_inspector_counter",
assert_equal "local_inspector,local_inspector_counter,local_inspector_iteration",
@view.render(:partial => "test/local_inspector", :collection => [ Customer.new("mary") ])
end