1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

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
This commit is contained in:
David Heinemeier Hansson 2005-09-12 05:26:25 +00:00
parent c0e84b811e
commit 2fe8610673
3 changed files with 173 additions and 0 deletions

View file

@ -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]

View file

@ -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}" }

View file

@ -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