diff options
author | David Heinemeier Hansson <david@loudthinking.com> | 2005-09-12 05:26:25 +0000 |
---|---|---|
committer | David Heinemeier Hansson <david@loudthinking.com> | 2005-09-12 05:26:25 +0000 |
commit | 2fe8610673e7445860830aad5e6ad49a1c99f2c5 (patch) | |
tree | 82085e882383bc3d236a0a754b57daf182cb2c46 | |
parent | c0e84b811eed956d5e2ed5d10104ca1c70f3e1c1 (diff) | |
download | rails-2fe8610673e7445860830aad5e6ad49a1c99f2c5.tar.gz rails-2fe8610673e7445860830aad5e6ad49a1c99f2c5.tar.bz2 rails-2fe8610673e7445860830aad5e6ad49a1c99f2c5.zip |
Added TextHelper#cycle to cycle over an array of values on each hit (useful for alternating row colors etc) #2154 [dave-ml@dribin.org]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2213 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
-rw-r--r-- | actionpack/CHANGELOG | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/text_helper.rb | 82 | ||||
-rw-r--r-- | actionpack/test/template/text_helper_test.rb | 89 |
3 files changed, 173 insertions, 0 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 0e0b47f584..16d70e5bf7 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Added TextHelper#cycle to cycle over an array of values on each hit (useful for alternating row colors etc) #2154 [dave-ml@dribin.org] + * Ensure that request.path never returns nil. Closes #1675 [Nicholas Seckar] * Add ability to specify Route Regexps for controllers. Closes #1917. [Sebastian Kanthak] diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 217cf6c9f8..413f32532d 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -189,9 +189,91 @@ module ActionView html end + + # Returns a Cycle object whose to_s value cycles through items of an + # array every time it is called. This can be used to alternate classes + # for table rows: + # + # <%- for item in @items do -%> + # <tr class="<%= cycle("even", "odd") %>"> + # ... use item ... + # </tr> + # <%- end -%> + # + # You can use named cycles to prevent clashes in nested loops. You'll + # have to reset the inner cycle, manually: + # + # <%- for item in @items do -%> + # <tr class="<%= cycle("even", "odd", :name => "row_class") + # <td> + # <%- for value in item.values do -%> + # <span style="color:'<%= cycle("red", "green", "blue" + # :name => "colors") %>'"> + # item + # </span> + # <%- end -%> + # <%- reset_cycle("colors") -%> + # </td> + # </tr> + # <%- end -%> + def cycle(first_value, *values) + if (values.last.instance_of? Hash) + params = values.pop + name = params[:name] + else + name = "default" + end + values.unshift(first_value) + + cycle = get_cycle(name) + if (cycle.nil? || cycle.values != values) + cycle = set_cycle(name, Cycle.new(*values)) + end + return cycle.to_s + end + + # Resets a cycle so that it starts from the first element in the array + # the next time it is used. + def reset_cycle(name = "default") + cycle = get_cycle(name) + return if cycle.nil? + cycle.reset + end + + class Cycle + attr_reader :values + + def initialize(first_value, *values) + @values = values.unshift(first_value) + reset + end + + def reset + @index = 0 + end + + def to_s + value = @values[@index].to_s + @index = (@index + 1) % @values.size + return value + end + end private + # The cycle helpers need to store the cycles in a place that is + # guaranteed to be reset every time a page is rendered, so it + # uses an instance variable of ActionView::Base. + def get_cycle(name) + @_cycles = Hash.new if @_cycles.nil? + return @_cycles[name] + end + + def set_cycle(name, cycle_object) + @_cycles = Hash.new if @_cycles.nil? + @_cycles[name] = cycle_object + end + # Returns a version of the text that's safe to use in a regular expression without triggering engine features. def escape_regexp(text) text.gsub(/([\\|?+*\/\)\(])/) { |m| "\\#{$1}" } diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 9b704711e1..ed0f3ce91c 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -8,6 +8,12 @@ class TextHelperTest < Test::Unit::TestCase include ActionView::Helpers::TextHelper include ActionView::Helpers::TagHelper + def setup + # This simulates the fact that instance variables are reset every time + # a view is rendered. The cycle helper depends on this behavior. + @_cycles = nil if (defined? @_cycles) + end + def test_simple_format assert_equal "<p>crazy\n<br /> cross\n<br /> platform linebreaks</p>", simple_format("crazy\r\n cross\r platform linebreaks") assert_equal "<p>A paragraph</p>\n\n<p>and another one!</p>", simple_format("A paragraph\n\nand another one!") @@ -137,4 +143,87 @@ class TextHelperTest < Test::Unit::TestCase assert_equal %{href="javascript:bang" <a name='hello'>foo</a>, <span>bar</span>}, result end + def test_cycle_class + value = Cycle.new("one", 2, "3") + assert_equal("one", value.to_s) + assert_equal("2", value.to_s) + assert_equal("3", value.to_s) + assert_equal("one", value.to_s) + value.reset + assert_equal("one", value.to_s) + assert_equal("2", value.to_s) + assert_equal("3", value.to_s) + end + + def test_cycle_class_with_no_arguments + assert_raise(ArgumentError) { value = Cycle.new() } + end + + def test_cycle + assert_equal("one", cycle("one", 2, "3")) + assert_equal("2", cycle("one", 2, "3")) + assert_equal("3", cycle("one", 2, "3")) + assert_equal("one", cycle("one", 2, "3")) + assert_equal("2", cycle("one", 2, "3")) + assert_equal("3", cycle("one", 2, "3")) + end + + def test_cycle_with_no_arguments + assert_raise(ArgumentError) { value = cycle() } + end + + def test_cycle_resets_with_new_values + assert_equal("even", cycle("even", "odd")) + assert_equal("odd", cycle("even", "odd")) + assert_equal("even", cycle("even", "odd")) + assert_equal("1", cycle(1, 2, 3)) + assert_equal("2", cycle(1, 2, 3)) + assert_equal("3", cycle(1, 2, 3)) + assert_equal("1", cycle(1, 2, 3)) + end + + def test_named_cycles + assert_equal("1", cycle(1, 2, 3, :name => "numbers")) + assert_equal("red", cycle("red", "blue", :name => "colors")) + assert_equal("2", cycle(1, 2, 3, :name => "numbers")) + assert_equal("blue", cycle("red", "blue", :name => "colors")) + assert_equal("3", cycle(1, 2, 3, :name => "numbers")) + assert_equal("red", cycle("red", "blue", :name => "colors")) + end + + def test_default_named_cycle + assert_equal("1", cycle(1, 2, 3)) + assert_equal("2", cycle(1, 2, 3, :name => "default")) + assert_equal("3", cycle(1, 2, 3)) + end + + def test_reset_cycle + assert_equal("1", cycle(1, 2, 3)) + assert_equal("2", cycle(1, 2, 3)) + reset_cycle + assert_equal("1", cycle(1, 2, 3)) + end + + def test_reset_unknown_cycle + reset_cycle("colors") + end + + def test_recet_named_cycle + assert_equal("1", cycle(1, 2, 3, :name => "numbers")) + assert_equal("red", cycle("red", "blue", :name => "colors")) + reset_cycle("numbers") + assert_equal("1", cycle(1, 2, 3, :name => "numbers")) + assert_equal("blue", cycle("red", "blue", :name => "colors")) + assert_equal("2", cycle(1, 2, 3, :name => "numbers")) + assert_equal("red", cycle("red", "blue", :name => "colors")) + end + + def test_cycle_no_instance_variable_clashes + @cycles = %w{Specialized Fuji Giant} + assert_equal("red", cycle("red", "blue")) + assert_equal("blue", cycle("red", "blue")) + assert_equal("red", cycle("red", "blue")) + assert_equal(%w{Specialized Fuji Giant}, @cycles) + end + end |