actionpack 2.3.8 → 2.3.9.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (35) hide show
  1. data/CHANGELOG +5 -0
  2. data/Rakefile +1 -1
  3. data/lib/action_controller/caching/fragments.rb +1 -1
  4. data/lib/action_controller/integration.rb +16 -5
  5. data/lib/action_controller/request.rb +2 -2
  6. data/lib/action_controller/rescue.rb +2 -2
  7. data/lib/action_controller/resources.rb +1 -1
  8. data/lib/action_controller/routing.rb +1 -1
  9. data/lib/action_controller/routing/route.rb +3 -3
  10. data/lib/action_controller/session/abstract_store.rb +152 -56
  11. data/lib/action_controller/session/cookie_store.rb +51 -16
  12. data/lib/action_controller/session/mem_cache_store.rb +9 -0
  13. data/lib/action_controller/test_process.rb +1 -1
  14. data/lib/action_controller/url_rewriter.rb +9 -1
  15. data/lib/action_pack/version.rb +1 -1
  16. data/lib/action_view/helpers/form_helper.rb +3 -3
  17. data/lib/action_view/helpers/form_options_helper.rb +1 -1
  18. data/lib/action_view/helpers/form_tag_helper.rb +1 -1
  19. data/lib/action_view/helpers/prototype_helper.rb +3 -3
  20. data/lib/action_view/helpers/text_helper.rb +27 -17
  21. data/lib/action_view/locale/en.yml +14 -14
  22. data/lib/action_view/template.rb +2 -2
  23. data/test/abstract_unit.rb +17 -0
  24. data/test/activerecord/active_record_store_test.rb +65 -18
  25. data/test/controller/integration_test.rb +26 -0
  26. data/test/controller/request/multipart_params_parsing_test.rb +15 -0
  27. data/test/controller/rescue_test.rb +5 -6
  28. data/test/controller/session/cookie_store_test.rb +80 -2
  29. data/test/controller/session/mem_cache_store_test.rb +65 -2
  30. data/test/controller/url_rewriter_test.rb +9 -3
  31. data/test/fixtures/session_autoload_test/session_autoload_test/foo.rb +10 -0
  32. data/test/template/form_helper_test.rb +17 -0
  33. data/test/template/form_options_helper_test.rb +4 -0
  34. data/test/template/text_helper_test.rb +47 -18
  35. metadata +14 -9
@@ -266,6 +266,14 @@ class IntegrationProcessTest < ActionController::IntegrationTest
266
266
  render :text => "foo(1i): #{params[:"foo(1i)"]}, foo(2i): #{params[:"foo(2i)"]}, filesize: #{params[:file].size}", :status => 200
267
267
  end
268
268
 
269
+ def multipart_post_with_nested_params
270
+ render :text => "foo: #{params[:foo][0]}, #{params[:foo][1]}; [filesize: #{params[:file_list][0][:content].size}, filesize: #{params[:file_list][1][:content].size}]", :status => 200
271
+ end
272
+
273
+ def multipart_post_with_multiparameter_complex_params
274
+ render :text => "foo(1i): #{params[:"foo(1i)"]}, foo(2i): #{params[:"foo(2i)"]}, [filesize: #{params[:file_list][0][:content].size}, filesize: #{params[:file_list][1][:content].size}]", :status => 200
275
+ end
276
+
269
277
  def post
270
278
  render :text => "Created", :status => 201
271
279
  end
@@ -405,6 +413,24 @@ class IntegrationProcessTest < ActionController::IntegrationTest
405
413
  end
406
414
  end
407
415
 
416
+ def test_multipart_post_with_nested_params
417
+ with_test_route_set do
418
+ post '/multipart_post_with_nested_params', :"foo" => ['a', 'b'], :file_list => [{:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}, {:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}]
419
+
420
+ assert_equal 200, status
421
+ assert_equal "foo: a, b; [filesize: 159528, filesize: 159528]", response.body
422
+ end
423
+ end
424
+
425
+ def test_multipart_post_with_multiparameter_complex_attribute_parameters
426
+ with_test_route_set do
427
+ post '/multipart_post_with_multiparameter_complex_params', :"foo(1i)" => "bar", :"foo(2i)" => "baz", :file_list => [{:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}, {:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}]
428
+
429
+ assert_equal 200, status
430
+ assert_equal "foo(1i): bar, foo(2i): baz, [filesize: 159528, filesize: 159528]", response.body
431
+ end
432
+ end
433
+
408
434
  def test_head
409
435
  with_test_route_set do
410
436
  head '/get'
@@ -14,6 +14,10 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest
14
14
  def read
15
15
  render :text => "File: #{params[:uploaded_data].read}"
16
16
  end
17
+
18
+ def read_complex
19
+ render :text => "File: #{params[:level0][:level1][0][:file_data].read}"
20
+ end
17
21
  end
18
22
 
19
23
  FIXTURE_PATH = File.dirname(__FILE__) + '/../../fixtures/multipart'
@@ -133,6 +137,17 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest
133
137
  end
134
138
  end
135
139
 
140
+ test "uploads and reads file in complex parameter" do
141
+ with_test_routing do
142
+ post '/read_complex',
143
+ :level0 => {
144
+ :level1 => [ { :file_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain") }
145
+ ]
146
+ }
147
+ assert_equal "File: Hello", response.body
148
+ end
149
+ end
150
+
136
151
  private
137
152
  def fixture(name)
138
153
  File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
@@ -281,12 +281,11 @@ class RescueControllerTest < ActionController::TestCase
281
281
  end
282
282
 
283
283
  def test_local_request_when_remote_addr_is_localhost
284
- @controller.expects(:request).returns(@request).at_least(4)
285
- with_remote_addr '127.0.0.1' do
286
- assert @controller.send(:local_request?)
287
- end
288
- with_remote_addr '::1' do
289
- assert @controller.send(:local_request?)
284
+ @controller.expects(:request).returns(@request).at_least(10)
285
+ ['127.0.0.1', '127.0.0.127', '::1', '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1%0'].each do |ip_address|
286
+ with_remote_addr ip_address do
287
+ assert @controller.send(:local_request?)
288
+ end
290
289
  end
291
290
  end
292
291
 
@@ -34,6 +34,15 @@ class CookieStoreTest < ActionController::IntegrationTest
34
34
  render :text => "foo: #{session[:foo].inspect}; id: #{request.session_options[:id]}"
35
35
  end
36
36
 
37
+ def get_session_id_only
38
+ render :text => "id: #{request.session_options[:id]}"
39
+ end
40
+
41
+ def call_session_clear
42
+ session.clear
43
+ head :ok
44
+ end
45
+
37
46
  def call_reset_session
38
47
  reset_session
39
48
  head :ok
@@ -44,6 +53,12 @@ class CookieStoreTest < ActionController::IntegrationTest
44
53
  head :ok
45
54
  end
46
55
 
56
+ def set_session_value_and_cookie
57
+ cookies["foo"] = "bar"
58
+ session[:foo] = "bar"
59
+ render :text => Rack::Utils.escape(Verifier.generate(session.to_hash))
60
+ end
61
+
47
62
  def rescue_action(e) raise end
48
63
  end
49
64
 
@@ -96,7 +111,7 @@ class CookieStoreTest < ActionController::IntegrationTest
96
111
  with_test_route_set do
97
112
  get '/set_session_value'
98
113
  assert_response :success
99
- assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
114
+ assert_equal ["_myapp_session=#{response.body}; path=/; HttpOnly"],
100
115
  headers['Set-Cookie']
101
116
  end
102
117
  end
@@ -121,6 +136,10 @@ class CookieStoreTest < ActionController::IntegrationTest
121
136
  get '/get_session_id'
122
137
  assert_response :success
123
138
  assert_equal "foo: \"bar\"; id: #{session_id}", response.body
139
+
140
+ get '/get_session_id_only'
141
+ assert_response :success
142
+ assert_equal "id: #{session_id}", response.body, "should be able to read session id without accessing the session hash"
124
143
  end
125
144
  end
126
145
 
@@ -164,7 +183,7 @@ class CookieStoreTest < ActionController::IntegrationTest
164
183
  get '/set_session_value'
165
184
  assert_response :success
166
185
  session_payload = response.body
167
- assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
186
+ assert_equal ["_myapp_session=#{response.body}; path=/; HttpOnly"],
168
187
  headers['Set-Cookie']
169
188
 
170
189
  get '/call_reset_session'
@@ -178,6 +197,57 @@ class CookieStoreTest < ActionController::IntegrationTest
178
197
  end
179
198
  end
180
199
 
200
+ def test_setting_session_value_after_session_clear
201
+ with_test_route_set do
202
+ get '/set_session_value'
203
+ assert_response :success
204
+ session_payload = response.body
205
+ assert_equal ["_myapp_session=#{response.body}; path=/; HttpOnly"],
206
+ headers['Set-Cookie']
207
+
208
+ get '/call_session_clear'
209
+ assert_response :success
210
+
211
+ get '/get_session_value'
212
+ assert_response :success
213
+ assert_equal 'foo: nil', response.body
214
+ end
215
+ end
216
+
217
+ def test_getting_from_nonexistent_session
218
+ with_test_route_set do
219
+ get '/get_session_value'
220
+ assert_response :success
221
+ assert_equal 'foo: nil', response.body
222
+ assert_nil headers['Set-Cookie'], "should only create session on write, not read"
223
+ end
224
+ end
225
+
226
+ # {:foo=>#<SessionAutoloadTest::Foo bar:"baz">, :session_id=>"ce8b0752a6ab7c7af3cdb8a80e6b9e46"}
227
+ SignedSerializedCookie = "BAh7BzoIZm9vbzodU2Vzc2lvbkF1dG9sb2FkVGVzdDo6Rm9vBjoJQGJhciIIYmF6Og9zZXNzaW9uX2lkIiVjZThiMDc1MmE2YWI3YzdhZjNjZGI4YTgwZTZiOWU0Ng==--2bf3af1ae8bd4e52b9ac2099258ace0c380e601c"
228
+
229
+ def test_deserializes_unloaded_classes_on_get_id
230
+ with_test_route_set do
231
+ with_autoload_path "session_autoload_test" do
232
+ cookies[SessionKey] = SignedSerializedCookie
233
+ get '/get_session_id_only'
234
+ assert_response :success
235
+ assert_equal 'id: ce8b0752a6ab7c7af3cdb8a80e6b9e46', response.body, "should auto-load unloaded class"
236
+ end
237
+ end
238
+ end
239
+
240
+ def test_deserializes_unloaded_classes_on_get_value
241
+ with_test_route_set do
242
+ with_autoload_path "session_autoload_test" do
243
+ cookies[SessionKey] = SignedSerializedCookie
244
+ get '/get_session_value'
245
+ assert_response :success
246
+ assert_equal 'foo: #<SessionAutoloadTest::Foo bar:"baz">', response.body, "should auto-load unloaded class"
247
+ end
248
+ end
249
+ end
250
+
181
251
  def test_persistent_session_id
182
252
  with_test_route_set do
183
253
  cookies[SessionKey] = SignedBar
@@ -193,6 +263,14 @@ class CookieStoreTest < ActionController::IntegrationTest
193
263
  end
194
264
  end
195
265
 
266
+ def test_setting_session_value_and_cookie
267
+ with_test_route_set do
268
+ get '/set_session_value_and_cookie'
269
+ assert_response :success
270
+ assert_equal({"_myapp_session" => response.body, "foo" => "bar"}, cookies)
271
+ end
272
+ end
273
+
196
274
  private
197
275
  def with_test_route_set
198
276
  with_routing do |set|
@@ -12,12 +12,16 @@ class MemCacheStoreTest < ActionController::IntegrationTest
12
12
  head :ok
13
13
  end
14
14
 
15
+ def set_serialized_session_value
16
+ session[:foo] = SessionAutoloadTest::Foo.new
17
+ head :ok
18
+ end
19
+
15
20
  def get_session_value
16
21
  render :text => "foo: #{session[:foo].inspect}"
17
22
  end
18
23
 
19
24
  def get_session_id
20
- session[:foo]
21
25
  render :text => "#{request.session_options[:id]}"
22
26
  end
23
27
 
@@ -82,6 +86,34 @@ class MemCacheStoreTest < ActionController::IntegrationTest
82
86
  end
83
87
  end
84
88
 
89
+ def test_getting_session_value_after_session_reset
90
+ with_test_route_set do
91
+ get '/set_session_value'
92
+ assert_response :success
93
+ assert cookies['_session_id']
94
+ session_id = cookies["_session_id"]
95
+
96
+ get '/call_reset_session'
97
+ assert_response :success
98
+ assert_not_equal [], headers['Set-Cookie']
99
+
100
+ cookies["_session_id"] = session_id # replace our new session_id with our old, pre-reset session_id
101
+
102
+ get '/get_session_value'
103
+ assert_response :success
104
+ assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from memcached"
105
+ end
106
+ end
107
+
108
+ def test_getting_from_nonexistent_session
109
+ with_test_route_set do
110
+ get '/get_session_value'
111
+ assert_response :success
112
+ assert_equal 'foo: nil', response.body
113
+ assert_nil cookies['_session_id'], "should only create session on write, not read"
114
+ end
115
+ end
116
+
85
117
  def test_getting_session_id
86
118
  with_test_route_set do
87
119
  get '/set_session_value'
@@ -91,7 +123,38 @@ class MemCacheStoreTest < ActionController::IntegrationTest
91
123
 
92
124
  get '/get_session_id'
93
125
  assert_response :success
94
- assert_equal session_id, response.body
126
+ assert_equal session_id, response.body, "should be able to read session id without accessing the session hash"
127
+ end
128
+ end
129
+
130
+ def test_doesnt_write_session_cookie_if_session_id_is_already_exists
131
+ with_test_route_set do
132
+ get '/set_session_value'
133
+ assert_response :success
134
+ assert cookies['_session_id']
135
+
136
+ get '/get_session_value'
137
+ assert_response :success
138
+ assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists"
139
+ end
140
+ end
141
+
142
+ def test_deserializes_unloaded_class
143
+ with_test_route_set do
144
+ with_autoload_path "session_autoload_test" do
145
+ get '/set_serialized_session_value'
146
+ assert_response :success
147
+ assert cookies['_session_id']
148
+ end
149
+ with_autoload_path "session_autoload_test" do
150
+ get '/get_session_id'
151
+ assert_response :success
152
+ end
153
+ with_autoload_path "session_autoload_test" do
154
+ get '/get_session_value'
155
+ assert_response :success
156
+ assert_equal 'foo: #<SessionAutoloadTest::Foo bar:"baz">', response.body, "should auto-load unloaded class"
157
+ end
95
158
  end
96
159
  end
97
160
 
@@ -134,9 +134,15 @@ class UrlWriterTests < ActionController::TestCase
134
134
  )
135
135
  end
136
136
 
137
- def test_anchor_should_be_cgi_escaped
138
- assert_equal('/c/a#anc%2Fhor',
139
- W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anc/hor'))
137
+ def test_anchor_should_escape_unsafe_pchar
138
+ assert_equal('/c/a#%23anchor',
139
+ W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('#anchor'))
140
+ )
141
+ end
142
+
143
+ def test_anchor_should_not_escape_safe_pchar
144
+ assert_equal('/c/a#name=user&email=user@domain.com',
145
+ W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('name=user&email=user@domain.com'))
140
146
  )
141
147
  end
142
148
 
@@ -0,0 +1,10 @@
1
+ module SessionAutoloadTest
2
+ class Foo
3
+ def initialize(bar='baz')
4
+ @bar = bar
5
+ end
6
+ def inspect
7
+ "#<#{self.class} bar:#{@bar.inspect}>"
8
+ end
9
+ end
10
+ end
@@ -91,6 +91,16 @@ end
91
91
  class FormHelperTest < ActionView::TestCase
92
92
  tests ActionView::Helpers::FormHelper
93
93
 
94
+ class Developer
95
+ def name_before_type_cast
96
+ "David"
97
+ end
98
+
99
+ def name
100
+ "Santiago"
101
+ end
102
+ end
103
+
94
104
  def setup
95
105
  super
96
106
 
@@ -256,6 +266,13 @@ class FormHelperTest < ActionView::TestCase
256
266
  assert_equal object_name, "post[]"
257
267
  end
258
268
 
269
+ def test_text_field_from_a_user_defined_method
270
+ @developer = Developer.new
271
+ assert_dom_equal(
272
+ '<input id="developer_name" name="developer[name]" size="30" type="text" value="Santiago" />', text_field("developer", "name")
273
+ )
274
+ end
275
+
259
276
  def test_hidden_field
260
277
  assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="Hello World" />',
261
278
  hidden_field("post", "title")
@@ -280,6 +280,10 @@ class FormOptionsHelperTest < ActionView::TestCase
280
280
  opts
281
281
  end
282
282
 
283
+ def test_time_zone_options_returns_html_safe_string
284
+ assert time_zone_options_for_select.html_safe?
285
+ end
286
+
283
287
  def test_select
284
288
  @post = Post.new
285
289
  @post.category = "<mus>"
@@ -296,6 +296,7 @@ class TextHelperTest < ActionView::TestCase
296
296
  assert_equal %(<p>Link #{link_result_with_options}</p>), auto_link("<p>Link #{link_raw}</p>", :all, {:target => "_blank"})
297
297
  assert_equal %(Go to #{link_result}.), auto_link(%(Go to #{link_raw}.))
298
298
  assert_equal %(<p>Go to #{link_result}, then say hello to #{email_result}.</p>), auto_link(%(<p>Go to #{link_raw}, then say hello to #{email_raw}.</p>))
299
+ assert_equal %(#{link_result} #{link_result}), auto_link(%(#{link_result} #{link_raw}))
299
300
 
300
301
  email2_raw = '+david@loudthinking.com'
301
302
  email2_result = %{<a href="mailto:#{email2_raw}">#{email2_raw}</a>}
@@ -368,24 +369,38 @@ class TextHelperTest < ActionView::TestCase
368
369
  end
369
370
 
370
371
  def test_auto_link_other_protocols
371
- silence_warnings do
372
- begin
373
- old_re_value = ActionView::Helpers::TextHelper::AUTO_LINK_RE
374
- ActionView::Helpers::TextHelper.const_set :AUTO_LINK_RE, %r{(ftp://)[^\s<]+}
375
- link_raw = 'ftp://example.com/file.txt'
376
- link_result = generate_result(link_raw)
377
- assert_equal %(Download #{link_result}), auto_link("Download #{link_raw}")
378
- ensure
379
- ActionView::Helpers::TextHelper.const_set :AUTO_LINK_RE, old_re_value
380
- end
381
- end
372
+ ftp_raw = 'ftp://example.com/file.txt'
373
+ assert_equal %(Download #{generate_result(ftp_raw)}), auto_link("Download #{ftp_raw}")
374
+
375
+ file_scheme = 'file:///home/username/RomeoAndJuliet.pdf'
376
+ z39_scheme = 'z39.50r://host:696/db'
377
+ chrome_scheme = 'chrome://package/section/path'
378
+ view_source = 'view-source:http://en.wikipedia.org/wiki/URI_scheme'
379
+ assert_equal generate_result(z39_scheme), auto_link(z39_scheme)
380
+ assert_equal generate_result(chrome_scheme), auto_link(chrome_scheme)
381
+ assert_equal generate_result(view_source), auto_link(view_source)
382
382
  end
383
383
 
384
384
  def test_auto_link_already_linked
385
385
  linked1 = generate_result('Ruby On Rails', 'http://www.rubyonrails.com')
386
- linked2 = generate_result('www.rubyonrails.com', 'http://www.rubyonrails.com')
386
+ linked2 = %('<a href="http://www.example.com">www.example.com</a>')
387
+ linked3 = %('<a href="http://www.example.com" rel="nofollow">www.example.com</a>')
388
+ linked4 = %('<a href="http://www.example.com"><b>www.example.com</b></a>')
389
+ linked5 = %('<a href="#close">close</a> <a href="http://www.example.com"><b>www.example.com</b></a>')
387
390
  assert_equal linked1, auto_link(linked1)
388
391
  assert_equal linked2, auto_link(linked2)
392
+ assert_equal linked3, auto_link(linked3)
393
+ assert_equal linked4, auto_link(linked4)
394
+ assert_equal linked5, auto_link(linked5)
395
+
396
+ linked_email = %Q(<a href="mailto:david@loudthinking.com">Mail me</a>)
397
+ assert_equal linked_email, auto_link(linked_email)
398
+ end
399
+
400
+ def test_auto_link_within_tags
401
+ link_raw = 'http://www.rubyonrails.org/images/rails.png'
402
+ link_result = %Q(<img src="#{link_raw}" />)
403
+ assert_equal link_result, auto_link(link_result)
389
404
  end
390
405
 
391
406
  def test_auto_link_with_brackets
@@ -405,12 +420,6 @@ class TextHelperTest < ActionView::TestCase
405
420
  assert_equal "{link: #{link3_result}}", auto_link("{link: #{link3_raw}}")
406
421
  end
407
422
 
408
- def test_auto_link_in_tags
409
- link_raw = 'http://www.rubyonrails.org/images/rails.png'
410
- link_result = %Q(<img src="#{link_raw}" />)
411
- assert_equal link_result, auto_link(link_result)
412
- end
413
-
414
423
  def test_auto_link_at_eol
415
424
  url1 = "http://api.rubyonrails.com/Foo.html"
416
425
  url2 = "http://www.ruby-doc.org/core/Bar.html"
@@ -424,12 +433,32 @@ class TextHelperTest < ActionView::TestCase
424
433
 
425
434
  assert_equal %(<p><a href="#{url}">#{url[0...7]}...</a><br /><a href="mailto:#{email}">#{email[0...7]}...</a><br /></p>), auto_link("<p>#{url}<br />#{email}<br /></p>") { |url| truncate(url, :length => 10) }
426
435
  end
436
+
437
+ def test_auto_link_with_block_with_html
438
+ pic = "http://example.com/pic.png"
439
+ url = "http://example.com/album?a&b=c"
440
+
441
+ assert_equal %(My pic: <a href="#{pic}"><img src="#{pic}" width="160px"></a> -- full album here #{generate_result(url)}), auto_link("My pic: #{pic} -- full album here #{url}") { |link|
442
+ if link =~ /\.(jpg|gif|png|bmp|tif)$/i
443
+ raw %(<img src="#{link}" width="160px">)
444
+ else
445
+ link
446
+ end
447
+ }
448
+ end
427
449
 
428
450
  def test_auto_link_with_options_hash
429
451
  assert_dom_equal 'Welcome to my new blog at <a href="http://www.myblog.com/" class="menu" target="_blank">http://www.myblog.com/</a>. Please e-mail me at <a href="mailto:me@email.com" class="menu" target="_blank">me@email.com</a>.',
430
452
  auto_link("Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.",
431
453
  :link => :all, :html => { :class => "menu", :target => "_blank" })
432
454
  end
455
+
456
+ def test_auto_link_with_multiple_trailing_punctuations
457
+ url = "http://youtube.com"
458
+ url_result = generate_result(url)
459
+ assert_equal url_result, auto_link(url)
460
+ assert_equal "(link: #{url_result}).", auto_link("(link: #{url}).")
461
+ end
433
462
 
434
463
  def test_cycle_class
435
464
  value = Cycle.new("one", 2, "3")