From 1f5b360466c3494267cc9aad08a19d1ace4763d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Junstr=C3=B6m?= Date: Sun, 16 Sep 2012 22:45:08 +0200 Subject: Added PartialIteration class used when rendering collections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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] --- actionpack/lib/action_view/partial_iteration.rb | 19 ++++++++++ .../lib/action_view/renderer/partial_renderer.rb | 41 +++++++++++++++------- .../test/actionpack/controller/render_test.rb | 18 ++++++++++ .../actionpack/test/_customer_iteration.erb | 1 + .../test/_customer_iteration_with_as.erb | 1 + actionview/test/template/partial_iteration_test.rb | 31 ++++++++++++++++ actionview/test/template/render_test.rb | 2 +- 7 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 actionpack/lib/action_view/partial_iteration.rb create mode 100644 actionview/test/fixtures/actionpack/test/_customer_iteration.erb create mode 100644 actionview/test/fixtures/actionpack/test/_customer_iteration_with_as.erb create mode 100644 actionview/test/template/partial_iteration_test.rb diff --git a/actionpack/lib/action_view/partial_iteration.rb b/actionpack/lib/action_view/partial_iteration.rb new file mode 100644 index 0000000000..5b33a40722 --- /dev/null +++ b/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 diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index 36f17f01fd..3963a73204 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/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 :as 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; " + diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb index cc65586c72..b3b51ae583 100644 --- a/actionview/test/actionpack/controller/render_test.rb +++ b/actionview/test/actionpack/controller/render_test.rb @@ -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 diff --git a/actionview/test/fixtures/actionpack/test/_customer_iteration.erb b/actionview/test/fixtures/actionpack/test/_customer_iteration.erb new file mode 100644 index 0000000000..fb530b04a7 --- /dev/null +++ b/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? %> \ No newline at end of file diff --git a/actionview/test/fixtures/actionpack/test/_customer_iteration_with_as.erb b/actionview/test/fixtures/actionpack/test/_customer_iteration_with_as.erb new file mode 100644 index 0000000000..57297d0564 --- /dev/null +++ b/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? %> \ No newline at end of file diff --git a/actionview/test/template/partial_iteration_test.rb b/actionview/test/template/partial_iteration_test.rb new file mode 100644 index 0000000000..3976c855ae --- /dev/null +++ b/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 diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb index a26f20d522..c13e59d82b 100644 --- a/actionview/test/template/render_test.rb +++ b/actionview/test/template/render_test.rb @@ -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 -- cgit v1.2.3 From 9830ebbeaf6f7faead9b4503ed9c9ab0a327df9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 16 Jul 2014 14:13:42 -0300 Subject: No need to have a file to PartialIteration class This class is only used on the PartialRenderer. --- actionpack/lib/action_view/partial_iteration.rb | 19 ------------------- .../lib/action_view/renderer/partial_renderer.rb | 18 +++++++++++++++++- actionview/test/template/partial_iteration_test.rb | 5 ++--- 3 files changed, 19 insertions(+), 23 deletions(-) delete mode 100644 actionpack/lib/action_view/partial_iteration.rb diff --git a/actionpack/lib/action_view/partial_iteration.rb b/actionpack/lib/action_view/partial_iteration.rb deleted file mode 100644 index 5b33a40722..0000000000 --- a/actionpack/lib/action_view/partial_iteration.rb +++ /dev/null @@ -1,19 +0,0 @@ -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 diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index 3963a73204..6b3c49aa79 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -1,7 +1,23 @@ require 'thread_safe' -require "action_view/partial_iteration" 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 + # = Action View Partials # # There's also a convenience method for rendering sub templates within the current controller that depends on a diff --git a/actionview/test/template/partial_iteration_test.rb b/actionview/test/template/partial_iteration_test.rb index 3976c855ae..af0dde96c3 100644 --- a/actionview/test/template/partial_iteration_test.rb +++ b/actionview/test/template/partial_iteration_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' -require 'action_view/partial_iteration' -class PartialIterationTest < ActiveSupport::TestCase +require 'action_view/renderer/partial_renderer' +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" @@ -27,5 +27,4 @@ class PartialIterationTest < ActiveSupport::TestCase iteration = ActionView::PartialIteration.new 3, 0 assert !iteration.last?, "not last when current is 0" end - end -- cgit v1.2.3 From 9290fc5ce213836f666a97e0e458d98e69a920ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 16 Jul 2014 14:28:39 -0300 Subject: Build only one PartialIteration object for loop --- .../lib/action_view/renderer/partial_renderer.rb | 29 ++++++++++++++-------- actionview/test/template/partial_iteration_test.rb | 13 ++++++---- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index 6b3c49aa79..bc0215b6c6 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -4,9 +4,9 @@ module ActionView class PartialIteration # :nodoc: attr_reader :size, :index - def initialize(size, index) + def initialize(size) @size = size - @index = index + @index = 0 end def first? @@ -16,6 +16,10 @@ module ActionView def last? index == size - 1 end + + def iterate! + @index += 1 + end end # = Action View Partials @@ -412,16 +416,16 @@ module ActionView layout = find_template(layout, @template_keys) end - index = -1 - @collection.map do |object| - index += 1 + partial_interation = PartialIteration.new(@collection.size) + locals[iteration] = partial_interation + @collection.map do |object| locals[as] = object - locals[counter] = index - locals[iteration] = PartialIteration.new(@collection.size, index) + locals[counter] = partial_interation.index content = template.render(view, locals) content = layout.render(view, locals) { content } if layout + partial_interation.iterate! content end end @@ -431,17 +435,20 @@ module ActionView cache = {} keys = @locals.keys - index = -1 + partial_interation = PartialIteration.new(@collection.size) + @collection.map do |object| - index += 1 + index = partial_interation.index path, as, counter, iteration = collection_data[index] locals[as] = object locals[counter] = index - locals[iteration] = PartialIteration.new(@collection.size, index) + locals[iteration] = partial_interation template = (cache[path] ||= find_template(path, keys + [as, counter])) - template.render(view, locals) + content = template.render(view, locals) + partial_interation.iterate! + content end end diff --git a/actionview/test/template/partial_iteration_test.rb b/actionview/test/template/partial_iteration_test.rb index af0dde96c3..695f9f1bef 100644 --- a/actionview/test/template/partial_iteration_test.rb +++ b/actionview/test/template/partial_iteration_test.rb @@ -3,28 +3,31 @@ require 'action_view/renderer/partial_renderer' class PartialIterationTest < ActiveSupport::TestCase def test_has_size_and_index - iteration = ActionView::PartialIteration.new 3, 0 + iteration = ActionView::PartialIteration.new 3 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 + iteration = ActionView::PartialIteration.new 3 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 + iteration = ActionView::PartialIteration.new 3 + iteration.iterate! 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 + iteration = ActionView::PartialIteration.new 3 + iteration.iterate! + iteration.iterate! 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 + iteration = ActionView::PartialIteration.new 3 assert !iteration.last?, "not last when current is 0" end end -- cgit v1.2.3 From 7dc0f3fc8d3160db3408ef5a20c43a7268c0cd8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 16 Jul 2014 14:38:07 -0300 Subject: Document the PartialIteration object --- actionview/lib/action_view/renderer/partial_renderer.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index bc0215b6c6..eb8db16796 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -1,23 +1,29 @@ require 'thread_safe' module ActionView - class PartialIteration # :nodoc: - attr_reader :size, :index + class PartialIteration + # The number of iterations that will be done by the partial. + attr_reader :size + + # The current iteration of the partial. + attr_reader :index def initialize(size) @size = size @index = 0 end + # Check if this is the first iteration of the partial. def first? index == 0 end + # Check if this is the last iteration of the partial. def last? index == size - 1 end - def iterate! + def iterate! # :nodoc: @index += 1 end end -- cgit v1.2.3 From 03d77504be20fbd170a25e948f259ad4047d0d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 16 Jul 2014 14:40:50 -0300 Subject: Add CHANGELOG entry for PartialIteration. Closes #7698. --- actionview/CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 3a55407491..f4a12360d7 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,13 @@ +* Add `PartialIteration` object 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 being iterated over, + the current `index` and two convenience methods `first?` and `last?`. + + *Joel Junström*, *Lucas Uyezu* + * Return an absolute instead of relative path from an asset url in the case of the `asset_host` proc returning nil -- cgit v1.2.3