Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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]
  • Loading branch information...
commit 1f5b360466c3494267cc9aad08a19d1ace4763d8 1 parent 6947e3a
@joeljunstrom joeljunstrom authored rafaelfranca committed
View
19 actionpack/lib/action_view/partial_iteration.rb
@@ -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
41 actionview/lib/action_view/renderer/partial_renderer.rb
@@ -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 @@ def setup(context, options, block)
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 @@ def find_template(path, locals)
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 @@ def collection_with_template
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 @@ def collection_without_template
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 @@ def merge_prefix_into_object_path(prefix, object_path)
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 @@ def retrieve_variable(path, as)
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
18 actionview/test/actionpack/controller/render_test.rb
@@ -536,6 +536,14 @@ def partial_collection_with_as
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 @@ def test_partial_collection_with_as
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
1  actionview/test/fixtures/actionpack/test/_customer_iteration.erb
@@ -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
1  actionview/test/fixtures/actionpack/test/_customer_iteration_with_as.erb
@@ -0,0 +1 @@
+<%= client_iteration.size %>-<%= client_iteration.index %>: <%= client.name %><%= '-first' if client_iteration.first? %><%= '-last' if client_iteration.last? %>
View
31 actionview/test/template/partial_iteration_test.rb
@@ -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
2  actionview/test/template/render_test.rb
@@ -256,7 +256,7 @@ def test_render_partial_collection_as_by_symbol
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
Please sign in to comment.
Something went wrong with that request. Please try again.