Skip to content

Commit

Permalink
Added TextHelper#cycle to cycle over an array of values on each hit (…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
dhh committed Sep 12, 2005
1 parent c0e84b8 commit 2fe8610
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 0 deletions.
2 changes: 2 additions & 0 deletions actionpack/CHANGELOG
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
82 changes: 82 additions & 0 deletions actionpack/lib/action_view/helpers/text_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,91 @@ def sanitize(html)

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}" }
Expand Down
89 changes: 89 additions & 0 deletions actionpack/test/template/text_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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!")
Expand Down Expand Up @@ -137,4 +143,87 @@ def test_sanitize_javascript_href
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

0 comments on commit 2fe8610

Please sign in to comment.