actionpack 1.12.3 → 1.12.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +35 -0
- data/README +4 -0
- data/Rakefile +8 -4
- data/filler +53 -0
- data/lib/action_controller/base.rb +102 -71
- data/lib/action_controller/caching.rb +3 -3
- data/lib/action_controller/cgi_process.rb +6 -5
- data/lib/action_controller/integration.rb +10 -6
- data/lib/action_controller/layout.rb +7 -5
- data/lib/action_controller/mime_responds.rb +7 -1
- data/lib/action_controller/pagination.rb +2 -2
- data/lib/action_controller/request.rb +11 -3
- data/lib/action_controller/routing.rb +7 -2
- data/lib/action_controller/streaming.rb +11 -6
- data/lib/action_controller/templates/scaffolds/layout.rhtml +1 -1
- data/lib/action_controller/verification.rb +1 -1
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view/base.rb +7 -4
- data/lib/action_view/helpers/capture_helper.rb +18 -16
- data/lib/action_view/helpers/java_script_macros_helper.rb +5 -4
- data/lib/action_view/helpers/prototype_helper.rb +4 -3
- data/lib/action_view/helpers/text_helper.rb +22 -22
- data/test/controller/filter_params_test.rb +42 -0
- data/test/controller/send_file_test.rb +15 -1
- data/test/template/compiled_templates_test.rb +134 -0
- metadata +7 -4
- data/test/template/compiled_templates_tests.rb +0 -63
@@ -27,7 +27,7 @@ module ActionController #:nodoc:
|
|
27
27
|
# that the header and footer are only mentioned in one place, like this:
|
28
28
|
#
|
29
29
|
# <!-- The header part of this layout -->
|
30
|
-
# <%=
|
30
|
+
# <%= yield %>
|
31
31
|
# <!-- The footer part of this layout -->
|
32
32
|
#
|
33
33
|
# And then you have content pages that look like this:
|
@@ -47,7 +47,7 @@ module ActionController #:nodoc:
|
|
47
47
|
# references that won't materialize before rendering time:
|
48
48
|
#
|
49
49
|
# <h1><%= @page_title %></h1>
|
50
|
-
# <%=
|
50
|
+
# <%= yield %>
|
51
51
|
#
|
52
52
|
# ...and content pages that fulfill these references _at_ rendering time:
|
53
53
|
#
|
@@ -159,10 +159,12 @@ module ActionController #:nodoc:
|
|
159
159
|
#
|
160
160
|
# As you can see, you pass the template as the first parameter, the status code as the second ("200" is OK), and the layout
|
161
161
|
# as the third.
|
162
|
+
#
|
163
|
+
# NOTE: The old notation for rendering the view from a layout was to expose the magic <tt>@content_for_layout</tt> instance
|
164
|
+
# variable. The preferred notation now is to use <tt>yield</tt>, as documented above.
|
162
165
|
module ClassMethods
|
163
|
-
# If a layout is specified, all
|
164
|
-
#
|
165
|
-
# <tt><%= @content_for_layout %></tt>. This layout can itself depend on instance variables assigned during action
|
166
|
+
# If a layout is specified, all rendered actions will have their result rendered
|
167
|
+
# when the layout<tt>yield</tt>'s. This layout can itself depend on instance variables assigned during action
|
166
168
|
# performance and have access to them as any normal template would.
|
167
169
|
def layout(template_name, conditions = {})
|
168
170
|
add_layout_conditions(conditions)
|
@@ -92,6 +92,12 @@ module ActionController #:nodoc:
|
|
92
92
|
# Note that you can define your own XML parameter parser which would allow you to describe multiple entities
|
93
93
|
# in a single request (i.e., by wrapping them all in a single root note), but if you just go with the flow
|
94
94
|
# and accept Rails' defaults, life will be much easier.
|
95
|
+
#
|
96
|
+
# If you need to use a MIME type which isn't supported by default, you can register your own handlers in
|
97
|
+
# environment.rb as follows.
|
98
|
+
#
|
99
|
+
# Mime::Type.register "image/jpg", :jpg
|
100
|
+
#
|
95
101
|
def respond_to(*types, &block)
|
96
102
|
raise ArgumentError, "respond_to takes either types or a block, never bot" unless types.any? ^ block
|
97
103
|
block ||= lambda { |responder| types.each { |type| responder.send(type) } }
|
@@ -160,4 +166,4 @@ module ActionController #:nodoc:
|
|
160
166
|
end
|
161
167
|
end
|
162
168
|
end
|
163
|
-
end
|
169
|
+
end
|
@@ -31,7 +31,7 @@ module ActionController
|
|
31
31
|
# instance variable, which is an ordered collection of model objects for the
|
32
32
|
# current page (at most 20, sorted by last name and first name), and a
|
33
33
|
# <tt>@person_pages</tt> Paginator instance. The current page is determined
|
34
|
-
# by the <tt
|
34
|
+
# by the <tt>params[:page]</tt> variable.
|
35
35
|
#
|
36
36
|
# ==== Pagination for a single action
|
37
37
|
#
|
@@ -47,7 +47,7 @@ module ActionController
|
|
47
47
|
# ==== Custom/"classic" pagination
|
48
48
|
#
|
49
49
|
# def list
|
50
|
-
# @person_pages = Paginator.new self, Person.count, 10,
|
50
|
+
# @person_pages = Paginator.new self, Person.count, 10, params[:page]
|
51
51
|
# @people = Person.find :all, :order => 'last_name, first_name',
|
52
52
|
# :limit => @person_pages.items_per_page,
|
53
53
|
# :offset => @person_pages.current.offset
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module ActionController
|
2
|
-
#
|
2
|
+
# Subclassing AbstractRequest makes these methods available to the request objects used in production and testing,
|
3
|
+
# CgiRequest and TestRequest
|
3
4
|
class AbstractRequest
|
4
5
|
cattr_accessor :relative_url_root
|
5
6
|
|
@@ -65,6 +66,7 @@ module ActionController
|
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
69
|
+
# Returns the accepted MIME type for the request
|
68
70
|
def accepts
|
69
71
|
@accepts ||=
|
70
72
|
if @env['HTTP_ACCEPT'].to_s.strip.empty?
|
@@ -202,15 +204,21 @@ module ActionController
|
|
202
204
|
host + port_string
|
203
205
|
end
|
204
206
|
|
205
|
-
def path_parameters=(parameters)
|
207
|
+
def path_parameters=(parameters) #:nodoc:
|
206
208
|
@path_parameters = parameters
|
207
209
|
@symbolized_path_parameters = @parameters = nil
|
208
210
|
end
|
209
211
|
|
210
|
-
|
212
|
+
# The same as <tt>path_parameters</tt> with explicitly symbolized keys
|
213
|
+
def symbolized_path_parameters
|
211
214
|
@symbolized_path_parameters ||= path_parameters.symbolize_keys
|
212
215
|
end
|
213
216
|
|
217
|
+
# Returns a hash with the parameters used to form the path of the request
|
218
|
+
#
|
219
|
+
# Example:
|
220
|
+
#
|
221
|
+
# {:action => 'my_action', :controller => 'my_controller'}
|
214
222
|
def path_parameters
|
215
223
|
@path_parameters ||= {}
|
216
224
|
end
|
@@ -218,7 +218,11 @@ module ActionController
|
|
218
218
|
expr = "::#{controller.split('/').collect {|c| c.camelize}.join('::')}Controller"
|
219
219
|
g.result :controller, expr, true
|
220
220
|
end
|
221
|
-
|
221
|
+
|
222
|
+
def file_kinds(kind)
|
223
|
+
((@file_kinds ||= []) << kind).uniq! || @file_kinds
|
224
|
+
end
|
225
|
+
|
222
226
|
def traverse_to_controller(segments, start_at = 0)
|
223
227
|
mod = ::Object
|
224
228
|
length = segments.length
|
@@ -228,6 +232,7 @@ module ActionController
|
|
228
232
|
return nil unless /\A[A-Za-z][A-Za-z\d_]*\Z/ =~ (segment = segments[index])
|
229
233
|
index += 1
|
230
234
|
|
235
|
+
file_kinds :app
|
231
236
|
mod_name = segment.camelize
|
232
237
|
controller_name = "#{mod_name}Controller"
|
233
238
|
path_suffix = File.join(segments[start_at..(index - 1)])
|
@@ -268,7 +273,7 @@ module ActionController
|
|
268
273
|
$LOAD_PATH.select do |base|
|
269
274
|
base = File.expand_path(base)
|
270
275
|
extended_root = File.expand_path(RAILS_ROOT)
|
271
|
-
base
|
276
|
+
base.match(/\A#{Regexp.escape(extended_root)}\/*#{file_kinds(:lib) * '|'}/) || base =~ %r{rails-[\d.]+/builtin}
|
272
277
|
end
|
273
278
|
else
|
274
279
|
$LOAD_PATH
|
@@ -14,7 +14,7 @@ module ActionController #:nodoc:
|
|
14
14
|
# it feasible to send even large files.
|
15
15
|
#
|
16
16
|
# Be careful to sanitize the path parameter if it coming from a web
|
17
|
-
# page. send_file(
|
17
|
+
# page. send_file(params[:path]) allows a malicious user to
|
18
18
|
# download any file on your server.
|
19
19
|
#
|
20
20
|
# Options:
|
@@ -28,6 +28,7 @@ module ActionController #:nodoc:
|
|
28
28
|
# or to read the entire file before sending (false). Defaults to true.
|
29
29
|
# * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
|
30
30
|
# Defaults to 4096.
|
31
|
+
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
|
31
32
|
#
|
32
33
|
# The default Content-Type and Content-Disposition headers are
|
33
34
|
# set to download arbitrary binary files in as many browsers as
|
@@ -37,9 +38,12 @@ module ActionController #:nodoc:
|
|
37
38
|
# Simple download:
|
38
39
|
# send_file '/path/to.zip'
|
39
40
|
#
|
40
|
-
# Show a JPEG in browser:
|
41
|
+
# Show a JPEG in the browser:
|
41
42
|
# send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
|
42
43
|
#
|
44
|
+
# Show a 404 page in the browser:
|
45
|
+
# send_file '/path/to/404.html, :type => 'text/html; charset=utf-8', :status => 404
|
46
|
+
#
|
43
47
|
# Read about the other Content-* HTTP headers if you'd like to
|
44
48
|
# provide the user with more information (such as Content-Description).
|
45
49
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
|
@@ -61,7 +65,7 @@ module ActionController #:nodoc:
|
|
61
65
|
@performed_render = false
|
62
66
|
|
63
67
|
if options[:stream]
|
64
|
-
render :text => Proc.new { |response, output|
|
68
|
+
render :status => options[:status], :text => Proc.new { |response, output|
|
65
69
|
logger.info "Streaming file #{path}" unless logger.nil?
|
66
70
|
len = options[:buffer_size] || 4096
|
67
71
|
File.open(path, 'rb') do |file|
|
@@ -81,7 +85,7 @@ module ActionController #:nodoc:
|
|
81
85
|
}
|
82
86
|
else
|
83
87
|
logger.info "Sending file #{path}" unless logger.nil?
|
84
|
-
File.open(path, 'rb') { |file| render :text => file.read }
|
88
|
+
File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read }
|
85
89
|
end
|
86
90
|
end
|
87
91
|
|
@@ -93,6 +97,7 @@ module ActionController #:nodoc:
|
|
93
97
|
# * <tt>:type</tt> - specifies an HTTP content type.
|
94
98
|
# Defaults to 'application/octet-stream'.
|
95
99
|
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
100
|
+
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
|
96
101
|
# Valid values are 'inline' and 'attachment' (default).
|
97
102
|
#
|
98
103
|
# Generic data download:
|
@@ -109,7 +114,7 @@ module ActionController #:nodoc:
|
|
109
114
|
logger.info "Sending data #{options[:filename]}" unless logger.nil?
|
110
115
|
send_file_headers! options.merge(:length => data.size)
|
111
116
|
@performed_render = false
|
112
|
-
render :text => data
|
117
|
+
render :status => options[:status], :text => data
|
113
118
|
end
|
114
119
|
|
115
120
|
private
|
@@ -139,4 +144,4 @@ module ActionController #:nodoc:
|
|
139
144
|
@headers['Cache-Control'] = 'private' if @headers['Cache-Control'] == 'no-cache'
|
140
145
|
end
|
141
146
|
end
|
142
|
-
end
|
147
|
+
end
|
@@ -37,7 +37,7 @@ module ActionController #:nodoc:
|
|
37
37
|
# is a hash consisting of the following key/value pairs:
|
38
38
|
#
|
39
39
|
# * <tt>:params</tt>: a single key or an array of keys that must
|
40
|
-
# be in the
|
40
|
+
# be in the <tt>params</tt> hash in order for the action(s) to be safely
|
41
41
|
# called.
|
42
42
|
# * <tt>:session</tt>: a single key or an array of keys that must
|
43
43
|
# be in the @session in order for the action(s) to be safely called.
|
data/lib/action_pack/version.rb
CHANGED
data/lib/action_view/base.rb
CHANGED
@@ -11,7 +11,7 @@ module ActionView #:nodoc:
|
|
11
11
|
#
|
12
12
|
# = ERb
|
13
13
|
#
|
14
|
-
# You trigger ERb by using embeddings such as <%
|
14
|
+
# You trigger ERb by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
|
15
15
|
# following loop for names:
|
16
16
|
#
|
17
17
|
# <b>Names of all the people</b>
|
@@ -19,12 +19,14 @@ module ActionView #:nodoc:
|
|
19
19
|
# Name: <%= person.name %><br/>
|
20
20
|
# <% end %>
|
21
21
|
#
|
22
|
-
# The loop is setup in regular embedding tags
|
22
|
+
# The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
|
23
23
|
# is not just a usage suggestion. Regular output functions like print or puts won't work with ERb templates. So this would be wrong:
|
24
24
|
#
|
25
25
|
# Hi, Mr. <% puts "Frodo" %>
|
26
26
|
#
|
27
|
-
#
|
27
|
+
# If you absolutely must write from within a function, you can use the TextHelper#concat
|
28
|
+
#
|
29
|
+
# <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
|
28
30
|
#
|
29
31
|
# == Using sub templates
|
30
32
|
#
|
@@ -425,7 +427,8 @@ module ActionView #:nodoc:
|
|
425
427
|
|
426
428
|
if @@compile_time[render_symbol] && supports_local_assigns?(render_symbol, local_assigns)
|
427
429
|
if file_name && !@@cache_template_loading
|
428
|
-
@@compile_time[render_symbol] < File.mtime(file_name)
|
430
|
+
@@compile_time[render_symbol] < File.mtime(file_name) || (File.symlink?(file_name) ?
|
431
|
+
@@compile_time[render_symbol] < File.lstat(file_name).mtime : false)
|
429
432
|
end
|
430
433
|
else
|
431
434
|
true
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ActionView
|
2
2
|
module Helpers
|
3
|
-
# Capture lets you extract parts of code
|
3
|
+
# Capture lets you extract parts of code which
|
4
4
|
# can be used in other points of the template or even layout file.
|
5
5
|
#
|
6
6
|
# == Capturing a block into an instance variable
|
@@ -8,12 +8,11 @@ module ActionView
|
|
8
8
|
# <% @script = capture do %>
|
9
9
|
# [some html...]
|
10
10
|
# <% end %>
|
11
|
-
#
|
12
11
|
#
|
13
12
|
# == Add javascript to header using content_for
|
14
13
|
#
|
15
|
-
# content_for("name") is a wrapper for capture which will
|
16
|
-
# fragment
|
14
|
+
# content_for("name") is a wrapper for capture which will
|
15
|
+
# make the fragment available by name to a yielding layout or template.
|
17
16
|
#
|
18
17
|
# layout.rhtml:
|
19
18
|
#
|
@@ -21,11 +20,11 @@ module ActionView
|
|
21
20
|
# <head>
|
22
21
|
# <title>layout with js</title>
|
23
22
|
# <script type="text/javascript">
|
24
|
-
#
|
25
|
-
#
|
23
|
+
# <%= yield :script %>
|
24
|
+
# </script>
|
26
25
|
# </head>
|
27
26
|
# <body>
|
28
|
-
# <%=
|
27
|
+
# <%= yield %>
|
29
28
|
# </body>
|
30
29
|
# </html>
|
31
30
|
#
|
@@ -69,13 +68,9 @@ module ActionView
|
|
69
68
|
end
|
70
69
|
end
|
71
70
|
|
72
|
-
#
|
73
|
-
#
|
74
|
-
# or in the layout.
|
75
|
-
#
|
76
|
-
# The name of the instance variable is content_for_<name>
|
77
|
-
# to stay consistent with @content_for_layout which is used
|
78
|
-
# by ActionView's layouts
|
71
|
+
# Calling content_for stores the block of markup for later use.
|
72
|
+
# Subsequently, you can make calls to it by name with <tt>yield</tt>
|
73
|
+
# in another template or in the layout.
|
79
74
|
#
|
80
75
|
# Example:
|
81
76
|
#
|
@@ -83,10 +78,17 @@ module ActionView
|
|
83
78
|
# alert('hello world')
|
84
79
|
# <% end %>
|
85
80
|
#
|
86
|
-
# You can use
|
81
|
+
# You can use yield :header anywhere in your templates.
|
82
|
+
#
|
83
|
+
# <%= yield :header %>
|
87
84
|
#
|
88
85
|
# NOTE: Beware that content_for is ignored in caches. So you shouldn't use it
|
89
|
-
# for elements that are going to be fragment cached.
|
86
|
+
# for elements that are going to be fragment cached.
|
87
|
+
#
|
88
|
+
# The deprecated way of accessing a content_for block was to use a instance variable
|
89
|
+
# named @@content_for_#{name_of_the_content_block}@. So <tt><%= content_for('footer') %></tt>
|
90
|
+
# would be avaiable as <tt><%= @content_for_footer %></tt>. The preferred notation now is
|
91
|
+
# <tt><%= yield :footer %></tt>.
|
90
92
|
def content_for(name, &block)
|
91
93
|
eval "@content_for_#{name} = (@content_for_#{name} || '') + capture(&block)"
|
92
94
|
end
|
@@ -27,6 +27,7 @@ module ActionView
|
|
27
27
|
# <tt>:url</tt>:: Specifies the url where the updated value should
|
28
28
|
# be sent after the user presses "ok".
|
29
29
|
#
|
30
|
+
#
|
30
31
|
# Addtional +options+ are:
|
31
32
|
# <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
|
32
33
|
# <tt>:cols</tt>:: Number of characters the text input should span (works for both INPUT and TEXTAREA)
|
@@ -122,10 +123,10 @@ module ActionView
|
|
122
123
|
# <tt>:on_show</tt>:: Like on_hide, only now the expression is called
|
123
124
|
# then the div is shown.
|
124
125
|
# <tt>:after_update_element</tt>:: A Javascript expression that is called when the
|
125
|
-
#
|
126
|
-
#
|
127
|
-
#
|
128
|
-
#
|
126
|
+
# user has selected one of the proposed values.
|
127
|
+
# The expression should take two variables: element and value.
|
128
|
+
# Element is a DOM element for the field, value
|
129
|
+
# is the value selected by the user.
|
129
130
|
# <tt>:select</tt>:: Pick the class of the element from which the value for
|
130
131
|
# insertion should be extracted. If this is not specified,
|
131
132
|
# the entire element is used.
|
@@ -143,7 +143,7 @@ module ActionView
|
|
143
143
|
# background instead of the regular reloading POST arrangement. Even
|
144
144
|
# though it's using JavaScript to serialize the form elements, the form
|
145
145
|
# submission will work just like a regular submission as viewed by the
|
146
|
-
# receiving side (all elements available in
|
146
|
+
# receiving side (all elements available in <tt>params</tt>). The options for
|
147
147
|
# specifying the target with :url and defining callbacks is the same as
|
148
148
|
# link_to_remote.
|
149
149
|
#
|
@@ -171,9 +171,10 @@ module ActionView
|
|
171
171
|
end
|
172
172
|
|
173
173
|
# Works like form_remote_tag, but uses form_for semantics.
|
174
|
-
def remote_form_for(object_name,
|
174
|
+
def remote_form_for(object_name, *args, &proc)
|
175
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
175
176
|
concat(form_remote_tag(options), proc.binding)
|
176
|
-
fields_for(object_name,
|
177
|
+
fields_for(object_name, *(args << options), &proc)
|
177
178
|
concat('</form>', proc.binding)
|
178
179
|
end
|
179
180
|
alias_method :form_remote_for, :remote_form_for
|
@@ -77,7 +77,7 @@ module ActionView
|
|
77
77
|
end
|
78
78
|
|
79
79
|
begin
|
80
|
-
require_library_or_gem "redcloth"
|
80
|
+
require_library_or_gem "redcloth" unless Object.const_defined?(:RedCloth)
|
81
81
|
|
82
82
|
# Returns the text with all the Textile codes turned into HTML-tags.
|
83
83
|
# <i>This method is only available if RedCloth can be required</i>.
|
@@ -104,7 +104,7 @@ module ActionView
|
|
104
104
|
end
|
105
105
|
|
106
106
|
begin
|
107
|
-
require_library_or_gem "bluecloth"
|
107
|
+
require_library_or_gem "bluecloth" unless Object.const_defined?(:BlueCloth)
|
108
108
|
|
109
109
|
# Returns the text with all the Markdown codes turned into HTML-tags.
|
110
110
|
# <i>This method is only available if BlueCloth can be required</i>.
|
@@ -116,7 +116,7 @@ module ActionView
|
|
116
116
|
end
|
117
117
|
|
118
118
|
# Returns +text+ transformed into HTML using very simple formatting rules
|
119
|
-
# Surrounds paragraphs with <tt
|
119
|
+
# Surrounds paragraphs with <tt><p></tt> tags, and converts line breaks into <tt><br/></tt>
|
120
120
|
# Two consecutive newlines(<tt>\n\n</tt>) are considered as a paragraph, one newline (<tt>\n</tt>) is
|
121
121
|
# considered a linebreak, three or more consecutive newlines are turned into two newlines
|
122
122
|
def simple_format(text)
|
@@ -129,7 +129,7 @@ module ActionView
|
|
129
129
|
end
|
130
130
|
|
131
131
|
# Turns all urls and email addresses into clickable links. The +link+ parameter can limit what should be linked.
|
132
|
-
# Options are
|
132
|
+
# Options are <tt>:all</tt> (default), <tt>:email_addresses</tt>, and <tt>:urls</tt>.
|
133
133
|
#
|
134
134
|
# Example:
|
135
135
|
# auto_link("Go to http://www.rubyonrails.com and say hello to david@loudthinking.com") =>
|
@@ -235,28 +235,28 @@ module ActionView
|
|
235
235
|
# array every time it is called. This can be used to alternate classes
|
236
236
|
# for table rows:
|
237
237
|
#
|
238
|
-
#
|
239
|
-
#
|
240
|
-
#
|
241
|
-
#
|
242
|
-
#
|
238
|
+
# <%- for item in @items do -%>
|
239
|
+
# <tr class="<%= cycle("even", "odd") %>">
|
240
|
+
# ... use item ...
|
241
|
+
# </tr>
|
242
|
+
# <%- end -%>
|
243
243
|
#
|
244
244
|
# You can use named cycles to prevent clashes in nested loops. You'll
|
245
245
|
# have to reset the inner cycle, manually:
|
246
246
|
#
|
247
|
-
#
|
248
|
-
#
|
249
|
-
#
|
250
|
-
#
|
251
|
-
#
|
252
|
-
#
|
253
|
-
#
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
257
|
-
#
|
258
|
-
#
|
259
|
-
#
|
247
|
+
# <%- for item in @items do -%>
|
248
|
+
# <tr class="<%= cycle("even", "odd", :name => "row_class")
|
249
|
+
# <td>
|
250
|
+
# <%- for value in item.values do -%>
|
251
|
+
# <span style="color:'<%= cycle("red", "green", "blue"
|
252
|
+
# :name => "colors") %>'">
|
253
|
+
# item
|
254
|
+
# </span>
|
255
|
+
# <%- end -%>
|
256
|
+
# <%- reset_cycle("colors") -%>
|
257
|
+
# </td>
|
258
|
+
# </tr>
|
259
|
+
# <%- end -%>
|
260
260
|
def cycle(first_value, *values)
|
261
261
|
if (values.last.instance_of? Hash)
|
262
262
|
params = values.pop
|