actionpack 2.3.3 → 2.3.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.

Files changed (50) hide show
  1. data/CHANGELOG +12 -1
  2. data/Rakefile +9 -8
  3. data/lib/action_controller/assertions/response_assertions.rb +3 -1
  4. data/lib/action_controller/base.rb +6 -2
  5. data/lib/action_controller/cookies.rb +1 -1
  6. data/lib/action_controller/dispatcher.rb +21 -6
  7. data/lib/action_controller/http_authentication.rb +3 -2
  8. data/lib/action_controller/params_parser.rb +6 -0
  9. data/lib/action_controller/reloader.rb +30 -21
  10. data/lib/action_controller/request_forgery_protection.rb +2 -1
  11. data/lib/action_controller/resources.rb +17 -13
  12. data/lib/action_controller/response.rb +6 -0
  13. data/lib/action_controller/routing.rb +3 -0
  14. data/lib/action_controller/routing/route_set.rb +18 -5
  15. data/lib/action_controller/streaming.rb +3 -1
  16. data/lib/action_controller/url_rewriter.rb +1 -1
  17. data/lib/action_pack/version.rb +1 -1
  18. data/lib/action_view/helpers/atom_feed_helper.rb +1 -1
  19. data/lib/action_view/helpers/form_helper.rb +3 -2
  20. data/lib/action_view/helpers/form_options_helper.rb +69 -1
  21. data/lib/action_view/helpers/tag_helper.rb +1 -1
  22. data/lib/action_view/helpers/text_helper.rb +20 -11
  23. data/lib/action_view/helpers/url_helper.rb +1 -1
  24. data/lib/action_view/locale/en.yml +4 -0
  25. data/test/abstract_unit.rb +16 -0
  26. data/test/controller/caching_test.rb +1 -1
  27. data/test/controller/cookie_test.rb +6 -0
  28. data/test/controller/dispatcher_test.rb +50 -11
  29. data/test/controller/filter_params_test.rb +2 -1
  30. data/test/controller/http_basic_authentication_test.rb +25 -0
  31. data/test/controller/http_digest_authentication_test.rb +29 -6
  32. data/test/controller/rack_test.rb +18 -1
  33. data/test/controller/redirect_test.rb +1 -1
  34. data/test/controller/reloader_test.rb +47 -20
  35. data/test/controller/request/json_params_parsing_test.rb +24 -4
  36. data/test/controller/request/xml_params_parsing_test.rb +15 -0
  37. data/test/controller/request_forgery_protection_test.rb +6 -5
  38. data/test/controller/resources_test.rb +44 -0
  39. data/test/controller/routing_test.rb +7 -2
  40. data/test/controller/send_file_test.rb +11 -1
  41. data/test/controller/url_rewriter_test.rb +29 -3
  42. data/test/fixtures/public/absolute/test.css +23 -0
  43. data/test/fixtures/public/absolute/test.js +63 -0
  44. data/test/template/atom_feed_helper_test.rb +29 -0
  45. data/test/template/form_helper_test.rb +26 -0
  46. data/test/template/form_options_helper_i18n_test.rb +27 -0
  47. data/test/template/form_options_helper_test.rb +34 -0
  48. data/test/template/text_helper_test.rb +23 -0
  49. data/test/template/url_helper_test.rb +8 -0
  50. metadata +10 -94
@@ -1,6 +1,6 @@
1
1
  require 'abstract_unit'
2
2
 
3
- class BaseRackTest < Test::Unit::TestCase
3
+ class BaseRackTest < ActiveSupport::TestCase
4
4
  def setup
5
5
  @env = {
6
6
  "HTTP_MAX_FORWARDS" => "10",
@@ -261,6 +261,23 @@ class RackResponseTest < BaseRackTest
261
261
  body.each { |part| parts << part }
262
262
  assert_equal ["0", "1", "2", "3", "4"], parts
263
263
  end
264
+
265
+ def test_streaming_block_with_flush_is_deprecated
266
+ @response.body = Proc.new do |response, output|
267
+ 5.times do |n|
268
+ output.write(n)
269
+ output.flush
270
+ end
271
+ end
272
+
273
+ assert_deprecated(/output.flush is no longer needed/) do
274
+ @response.prepare!
275
+ status, headers, body = @response.to_a
276
+
277
+ parts = []
278
+ body.each { |part| parts << part }
279
+ end
280
+ end
264
281
  end
265
282
 
266
283
  class RackResponseHeadersTest < BaseRackTest
@@ -236,7 +236,7 @@ class RedirectTest < ActionController::TestCase
236
236
  def test_redirect_with_partial_params
237
237
  get :module_redirect
238
238
 
239
- assert_deprecated do
239
+ assert_deprecated(/test_redirect_with_partial_params/) do
240
240
  assert_redirected_to :action => 'hello_world'
241
241
  end
242
242
  end
@@ -22,35 +22,62 @@ class ReloaderTests < ActiveSupport::TestCase
22
22
  end
23
23
  end
24
24
 
25
- def setup_and_return_body(app = lambda { })
25
+ def setup
26
+ @lock = Mutex.new
27
+ end
28
+
29
+ def test_it_reloads_the_application_before_yielding
26
30
  Dispatcher.expects(:reload_application)
27
- reloader = Reloader.new(app)
28
- headers, status, body = reloader.call({ })
29
- body
31
+ Reloader.run(@lock) do
32
+ [200, { "Content-Type" => "text/html" }, [""]]
33
+ end
30
34
  end
31
35
 
32
- def test_it_reloads_the_application_before_the_request
36
+ def test_it_locks_before_yielding
37
+ lock = DummyMutex.new
33
38
  Dispatcher.expects(:reload_application)
34
- reloader = Reloader.new(lambda {
39
+ Reloader.run(lock) do
40
+ assert lock.locked?
35
41
  [200, { "Content-Type" => "text/html" }, [""]]
36
- })
37
- reloader.call({ })
42
+ end
43
+ assert lock.locked?
44
+ end
45
+
46
+ def test_it_unlocks_upon_calling_close_on_body
47
+ lock = DummyMutex.new
48
+ Dispatcher.expects(:reload_application)
49
+ headers, status, body = Reloader.run(lock) do
50
+ [200, { "Content-Type" => "text/html" }, [""]]
51
+ end
52
+ body.close
53
+ assert !lock.locked?
54
+ end
55
+
56
+ def test_it_unlocks_if_app_object_raises_exception
57
+ lock = DummyMutex.new
58
+ Dispatcher.expects(:reload_application)
59
+ assert_raise(RuntimeError) do
60
+ Reloader.run(lock) do
61
+ raise "oh no!"
62
+ end
63
+ end
64
+ assert !lock.locked?
38
65
  end
39
66
 
40
67
  def test_returned_body_object_always_responds_to_close
41
- body = setup_and_return_body(lambda {
68
+ status, headers, body = Reloader.run(@lock) do
42
69
  [200, { "Content-Type" => "text/html" }, [""]]
43
- })
70
+ end
44
71
  assert body.respond_to?(:close)
45
72
  end
46
73
 
47
74
  def test_returned_body_object_behaves_like_underlying_object
48
- body = setup_and_return_body(lambda {
75
+ status, headers, body = Reloader.run(@lock) do
49
76
  b = MyBody.new
50
77
  b << "hello"
51
78
  b << "world"
52
79
  [200, { "Content-Type" => "text/html" }, b]
53
- })
80
+ end
54
81
  assert_equal 2, body.size
55
82
  assert_equal "hello", body[0]
56
83
  assert_equal "world", body[1]
@@ -60,20 +87,20 @@ class ReloaderTests < ActiveSupport::TestCase
60
87
 
61
88
  def test_it_calls_close_on_underlying_object_when_close_is_called_on_body
62
89
  close_called = false
63
- body = setup_and_return_body(lambda {
90
+ status, headers, body = Reloader.run(@lock) do
64
91
  b = MyBody.new do
65
92
  close_called = true
66
93
  end
67
94
  [200, { "Content-Type" => "text/html" }, b]
68
- })
95
+ end
69
96
  body.close
70
97
  assert close_called
71
98
  end
72
99
 
73
100
  def test_returned_body_object_responds_to_all_methods_supported_by_underlying_object
74
- body = setup_and_return_body(lambda {
101
+ status, headers, body = Reloader.run(@lock) do
75
102
  [200, { "Content-Type" => "text/html" }, MyBody.new]
76
- })
103
+ end
77
104
  assert body.respond_to?(:size)
78
105
  assert body.respond_to?(:each)
79
106
  assert body.respond_to?(:foo)
@@ -82,16 +109,16 @@ class ReloaderTests < ActiveSupport::TestCase
82
109
 
83
110
  def test_it_doesnt_clean_up_the_application_after_call
84
111
  Dispatcher.expects(:cleanup_application).never
85
- body = setup_and_return_body(lambda {
112
+ status, headers, body = Reloader.run(@lock) do
86
113
  [200, { "Content-Type" => "text/html" }, MyBody.new]
87
- })
114
+ end
88
115
  end
89
116
 
90
117
  def test_it_cleans_up_the_application_when_close_is_called_on_body
91
118
  Dispatcher.expects(:cleanup_application)
92
- body = setup_and_return_body(lambda {
119
+ status, headers, body = Reloader.run(@lock) do
93
120
  [200, { "Content-Type" => "text/html" }, MyBody.new]
94
- })
121
+ end
95
122
  body.close
96
123
  end
97
124
  end
@@ -30,16 +30,36 @@ class JsonParamsParsingTest < ActionController::IntegrationTest
30
30
  )
31
31
  end
32
32
 
33
+ test "logs error if parsing unsuccessful" do
34
+ with_test_routing do
35
+ begin
36
+ $stderr = StringIO.new
37
+ json = "[\"person]\": {\"name\": \"David\"}}"
38
+ post "/parse", json, {'CONTENT_TYPE' => 'application/json'}
39
+ assert_response :error
40
+ $stderr.rewind && err = $stderr.read
41
+ assert err =~ /Error occurred while parsing request parameters/
42
+ ensure
43
+ $stderr = STDERR
44
+ end
45
+ end
46
+ end
47
+
33
48
  private
34
49
  def assert_parses(expected, actual, headers = {})
50
+ with_test_routing do
51
+ post "/parse", actual, headers
52
+ assert_response :ok
53
+ assert_equal(expected, TestController.last_request_parameters)
54
+ end
55
+ end
56
+
57
+ def with_test_routing
35
58
  with_routing do |set|
36
59
  set.draw do |map|
37
60
  map.connect ':action', :controller => "json_params_parsing_test/test"
38
61
  end
39
-
40
- post "/parse", actual, headers
41
- assert_response :ok
42
- assert_equal(expected, TestController.last_request_parameters)
62
+ yield
43
63
  end
44
64
  end
45
65
  end
@@ -38,6 +38,21 @@ class XmlParamsParsingTest < ActionController::IntegrationTest
38
38
  end
39
39
  end
40
40
 
41
+ test "logs error if parsing unsuccessful" do
42
+ with_test_routing do
43
+ begin
44
+ $stderr = StringIO.new
45
+ xml = "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{ActiveSupport::Base64.encode64('ABC')}</avatar></pineapple>"
46
+ post "/parse", xml, default_headers
47
+ assert_response :error
48
+ $stderr.rewind && err = $stderr.read
49
+ assert err =~ /Error occurred while parsing request parameters/
50
+ ensure
51
+ $stderr = STDERR
52
+ end
53
+ end
54
+ end
55
+
41
56
  test "parses multiple files" do
42
57
  xml = <<-end_body
43
58
  <person>
@@ -151,14 +151,10 @@ module RequestForgeryProtectionTests
151
151
  delete :index, :format => 'xml'
152
152
  end
153
153
  end
154
-
154
+
155
155
  def test_should_allow_xhr_post_without_token
156
156
  assert_nothing_raised { xhr :post, :index }
157
157
  end
158
- def test_should_not_allow_xhr_post_with_html_without_token
159
- @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
160
- assert_raise(ActionController::InvalidAuthenticityToken) { xhr :post, :index }
161
- end
162
158
 
163
159
  def test_should_allow_xhr_put_without_token
164
160
  assert_nothing_raised { xhr :put, :index }
@@ -168,6 +164,11 @@ module RequestForgeryProtectionTests
168
164
  assert_nothing_raised { xhr :delete, :index }
169
165
  end
170
166
 
167
+ def test_should_allow_xhr_post_with_encoded_form_content_type_without_token
168
+ @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
169
+ assert_nothing_raised { xhr :post, :index }
170
+ end
171
+
171
172
  def test_should_allow_post_with_token
172
173
  post :index, :authenticity_token => @token
173
174
  assert_response :success
@@ -76,6 +76,50 @@ class ResourcesTest < ActionController::TestCase
76
76
  end
77
77
  end
78
78
 
79
+ def test_override_paths_for_member_and_collection_methods
80
+ collection_methods = { 'rss' => :get, 'reorder' => :post, 'csv' => :post }
81
+ member_methods = { 'rss' => :get, :atom => :get, :upload => :post, :fix => :post }
82
+ path_names = {:new => 'nuevo', 'rss' => 'canal', :fix => 'corrigir' }
83
+
84
+ with_restful_routing :messages,
85
+ :collection => collection_methods,
86
+ :member => member_methods,
87
+ :path_names => path_names do
88
+
89
+ assert_restful_routes_for :messages,
90
+ :collection => collection_methods,
91
+ :member => member_methods,
92
+ :path_names => path_names do |options|
93
+ member_methods.each do |action, method|
94
+ assert_recognizes(options.merge(:action => action.to_s, :id => '1'),
95
+ :path => "/messages/1/#{path_names[action] || action}",
96
+ :method => method)
97
+ end
98
+
99
+ collection_methods.each do |action, method|
100
+ assert_recognizes(options.merge(:action => action),
101
+ :path => "/messages/#{path_names[action] || action}",
102
+ :method => method)
103
+ end
104
+ end
105
+
106
+ assert_restful_named_routes_for :messages,
107
+ :collection => collection_methods,
108
+ :member => member_methods,
109
+ :path_names => path_names do |options|
110
+
111
+ collection_methods.keys.each do |action|
112
+ assert_named_route "/messages/#{path_names[action] || action}", "#{action}_messages_path", :action => action
113
+ end
114
+
115
+ member_methods.keys.each do |action|
116
+ assert_named_route "/messages/1/#{path_names[action] || action}", "#{action}_message_path", :action => action, :id => "1"
117
+ end
118
+
119
+ end
120
+ end
121
+ end
122
+
79
123
  def test_override_paths_for_default_restful_actions
80
124
  resource = ActionController::Resources::Resource.new(:messages,
81
125
  :path_names => {:new => 'nuevo', :edit => 'editar'})
@@ -1,5 +1,6 @@
1
1
  require 'abstract_unit'
2
2
  require 'controller/fake_controllers'
3
+ require 'action_controller/routing/route_set'
3
4
 
4
5
  class MilestonesController < ActionController::Base
5
6
  def index() head :ok end
@@ -742,7 +743,7 @@ class MockController
742
743
  end
743
744
  end
744
745
 
745
- class LegacyRouteSetTests < Test::Unit::TestCase
746
+ class LegacyRouteSetTests < ActiveSupport::TestCase
746
747
  attr_reader :rs
747
748
 
748
749
  def setup
@@ -758,6 +759,10 @@ class LegacyRouteSetTests < Test::Unit::TestCase
758
759
  @rs.clear!
759
760
  end
760
761
 
762
+ def test_routes_for_controller_and_action_deprecated
763
+ assert_deprecated { @rs.routes_for_controller_and_action("controller", "action") }
764
+ end
765
+
761
766
  def test_default_setup
762
767
  @rs.draw {|m| m.connect ':controller/:action/:id' }
763
768
  assert_equal({:controller => "content", :action => 'index'}, rs.recognize_path("/content"))
@@ -1605,7 +1610,7 @@ class RouteTest < Test::Unit::TestCase
1605
1610
  end
1606
1611
  end
1607
1612
 
1608
- class RouteSetTest < Test::Unit::TestCase
1613
+ class RouteSetTest < ActiveSupport::TestCase
1609
1614
  def set
1610
1615
  @set ||= ROUTING::RouteSet.new
1611
1616
  end
@@ -1,9 +1,10 @@
1
+ # encoding: utf-8
1
2
  require 'abstract_unit'
2
3
 
3
4
  module TestFileUtils
4
5
  def file_name() File.basename(__FILE__) end
5
6
  def file_path() File.expand_path(__FILE__) end
6
- def file_data() File.open(file_path, 'rb') { |f| f.read } end
7
+ def file_data() @data ||= File.open(file_path, 'rb') { |f| f.read } end
7
8
  end
8
9
 
9
10
  class SendFileController < ActionController::Base
@@ -15,6 +16,7 @@ class SendFileController < ActionController::Base
15
16
 
16
17
  def file() send_file(file_path, options) end
17
18
  def data() send_data(file_data, options) end
19
+ def multibyte_text_data() send_data("Кирилица\n祝您好運", options) end
18
20
 
19
21
  def rescue_action(e) raise end
20
22
  end
@@ -49,6 +51,7 @@ class SendFileTest < ActionController::TestCase
49
51
  require 'stringio'
50
52
  output = StringIO.new
51
53
  output.binmode
54
+ output.string.force_encoding(file_data.encoding) if output.string.respond_to?(:force_encoding)
52
55
  assert_nothing_raised { response.body.call(response, output) }
53
56
  assert_equal file_data, output.string
54
57
  end
@@ -158,4 +161,11 @@ class SendFileTest < ActionController::TestCase
158
161
  assert_equal ActionController::Base::DEFAULT_RENDER_STATUS_CODE, @response.status
159
162
  end
160
163
  end
164
+
165
+ def test_send_data_content_length_header
166
+ @controller.headers = {}
167
+ @controller.options = { :type => :text, :filename => 'file_with_utf8_text' }
168
+ process('multibyte_text_data')
169
+ assert_equal '29', @controller.headers['Content-Length']
170
+ end
161
171
  end
@@ -46,6 +46,20 @@ class UrlRewriterTests < ActionController::TestCase
46
46
  )
47
47
  end
48
48
 
49
+ def test_anchor_should_call_to_param
50
+ assert_equal(
51
+ 'http://test.host/c/a/i#anchor',
52
+ @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anchor'))
53
+ )
54
+ end
55
+
56
+ def test_anchor_should_be_cgi_escaped
57
+ assert_equal(
58
+ 'http://test.host/c/a/i#anc%2Fhor',
59
+ @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anc/hor'))
60
+ )
61
+ end
62
+
49
63
  def test_overwrite_params
50
64
  @params[:controller] = 'hi'
51
65
  @params[:action] = 'bye'
@@ -110,6 +124,18 @@ class UrlWriterTests < ActionController::TestCase
110
124
  )
111
125
  end
112
126
 
127
+ def test_anchor_should_call_to_param
128
+ assert_equal('/c/a#anchor',
129
+ W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anchor'))
130
+ )
131
+ end
132
+
133
+ def test_anchor_should_be_cgi_escaped
134
+ assert_equal('/c/a#anc%2Fhor',
135
+ W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anc/hor'))
136
+ )
137
+ end
138
+
113
139
  def test_default_host
114
140
  add_host!
115
141
  assert_equal('http://www.basecamphq.com/c/a/i',
@@ -304,7 +330,7 @@ class UrlWriterTests < ActionController::TestCase
304
330
  def test_named_routes_with_nil_keys
305
331
  ActionController::Routing::Routes.clear!
306
332
  ActionController::Routing::Routes.draw do |map|
307
- map.main '', :controller => 'posts'
333
+ map.main '', :controller => 'posts', :format => nil
308
334
  map.resources :posts
309
335
  map.connect ':controller/:action/:id'
310
336
  end
@@ -314,9 +340,9 @@ class UrlWriterTests < ActionController::TestCase
314
340
 
315
341
  controller = kls.new
316
342
  params = {:action => :index, :controller => :posts, :format => :xml}
317
- assert_equal("http://www.basecamphq.com/posts.xml", controller.send(:url_for, params))
343
+ assert_equal("http://www.basecamphq.com/posts.xml", controller.send(:url_for, params))
318
344
  params[:format] = nil
319
- assert_equal("http://www.basecamphq.com/", controller.send(:url_for, params))
345
+ assert_equal("http://www.basecamphq.com/", controller.send(:url_for, params))
320
346
  ensure
321
347
  ActionController::Routing::Routes.load!
322
348
  end
@@ -0,0 +1,23 @@
1
+ /* bank.css */
2
+
3
+ /* robber.css */
4
+
5
+ /* version.1.0.css */
6
+
7
+ /* bank.css */
8
+
9
+ /* bank.css */
10
+
11
+ /* robber.css */
12
+
13
+ /* version.1.0.css */
14
+
15
+ /* bank.css */
16
+
17
+ /* robber.css */
18
+
19
+ /* version.1.0.css */
20
+
21
+ /* robber.css */
22
+
23
+ /* version.1.0.css */