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
data/CHANGELOG CHANGED
@@ -1,4 +1,13 @@
1
- *2.3.3 (July 20, 2009)*
1
+ *2.3.4 (September 4, 2009)*
2
+
3
+ * Sanitize multibyte strings before escaping them with escape_once. CVE-2009-3009
4
+
5
+ * Introduce grouped_collection_select helper. #1249 [Dan Codeape, Erik Ostrom]
6
+
7
+ * Ruby 1.9: fix Content-Length for multibyte send_data streaming. #2661 [Sava Chankov]
8
+
9
+
10
+ *2.3.3 (July 12, 2009)*
2
11
 
3
12
  * Fixed that TestResponse.cookies was returning cookies unescaped #1867 [Doug McInnes]
4
13
 
@@ -7,6 +16,8 @@
7
16
 
8
17
  * Fixed that redirection would just log the options, not the final url (which lead to "Redirected to #<Post:0x23150b8>") [DHH]
9
18
 
19
+ * Don't check authenticity tokens for any AJAX requests [Ross Kaffenberger/Bryan Helmkamp]
20
+
10
21
  * Added ability to pass in :public => true to fresh_when, stale?, and expires_in to make the request proxy cachable #2095 [Gregg Pollack]
11
22
 
12
23
  * Fixed that passing a custom form builder would be forwarded to nested fields_for calls #2023 [Eloy Duran/Nate Wiger]
data/Rakefile CHANGED
@@ -29,7 +29,7 @@ Rake::TestTask.new(:test_action_pack) do |t|
29
29
 
30
30
  # make sure we include the tests in alphabetical order as on some systems
31
31
  # this will not happen automatically and the tests (as a whole) will error
32
- t.test_files = Dir.glob( "test/[cft]*/**/*_test.rb" ).sort
32
+ t.test_files = Dir.glob( "test/[cftv]*/**/*_test.rb" ).sort
33
33
 
34
34
  t.verbose = true
35
35
  #t.warning = true
@@ -79,7 +79,7 @@ spec = Gem::Specification.new do |s|
79
79
  s.has_rdoc = true
80
80
  s.requirements << 'none'
81
81
 
82
- s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD)
82
+ s.add_dependency('activesupport', '= 2.3.4' + PKG_BUILD)
83
83
  s.add_dependency('rack', '~> 1.0.0')
84
84
 
85
85
  s.require_path = 'lib'
@@ -149,11 +149,12 @@ end
149
149
 
150
150
  desc "Publish the release files to RubyForge."
151
151
  task :release => [ :package ] do
152
- `rubyforge login`
152
+ require 'rubyforge'
153
+ require 'rake/contrib/rubyforgepublisher'
153
154
 
154
- for ext in %w( gem tgz zip )
155
- release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
156
- puts release_command
157
- system(release_command)
158
- end
155
+ packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
156
+
157
+ rubyforge = RubyForge.new
158
+ rubyforge.login
159
+ rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
159
160
  end
@@ -64,7 +64,9 @@ module ActionController
64
64
  # Support partial arguments for hash redirections
65
65
  if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash)
66
66
  if options.all? {|(key, value)| @response.redirected_to[key] == value}
67
- ::ActiveSupport::Deprecation.warn("Using assert_redirected_to with partial hash arguments is deprecated. Specify the full set arguments instead", caller)
67
+ callstack = caller.dup
68
+ callstack.slice!(0, 2)
69
+ ::ActiveSupport::Deprecation.warn("Using assert_redirected_to with partial hash arguments is deprecated. Specify the full set arguments instead", callstack)
68
70
  return true
69
71
  end
70
72
  end
@@ -493,7 +493,12 @@ module ActionController #:nodoc:
493
493
  filtered_parameters[key] = filter_parameters(value)
494
494
  elsif value.is_a?(Array)
495
495
  filtered_parameters[key] = value.collect do |item|
496
- filter_parameters(item)
496
+ case item
497
+ when Hash, Array
498
+ filter_parameters(item)
499
+ else
500
+ item
501
+ end
497
502
  end
498
503
  elsif block_given?
499
504
  key = key.dup
@@ -814,7 +819,6 @@ module ActionController #:nodoc:
814
819
  # render :text => proc { |response, output|
815
820
  # 10_000_000.times do |i|
816
821
  # output.write("This is line #{i}\n")
817
- # output.flush
818
822
  # end
819
823
  # }
820
824
  #
@@ -51,7 +51,7 @@ module ActionController #:nodoc:
51
51
  protected
52
52
  # Returns the cookie container, which operates as described above.
53
53
  def cookies
54
- CookieJar.new(self)
54
+ @cookies ||= CookieJar.new(self)
55
55
  end
56
56
  end
57
57
 
@@ -2,13 +2,12 @@ module ActionController
2
2
  # Dispatches requests to the appropriate controller and takes care of
3
3
  # reloading the app after each request when Dependencies.load? is true.
4
4
  class Dispatcher
5
+ @@cache_classes = true
6
+
5
7
  class << self
6
8
  def define_dispatcher_callbacks(cache_classes)
9
+ @@cache_classes = cache_classes
7
10
  unless cache_classes
8
- unless self.middleware.include?(Reloader)
9
- self.middleware.insert_after(Failsafe, Reloader)
10
- end
11
-
12
11
  ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
13
12
  end
14
13
 
@@ -79,7 +78,7 @@ module ActionController
79
78
  # DEPRECATE: Remove arguments, since they are only used by CGI
80
79
  def initialize(output = $stdout, request = nil, response = nil)
81
80
  @output = output
82
- @app = @@middleware.build(lambda { |env| self.dup._call(env) })
81
+ build_middleware_stack if @@cache_classes
83
82
  end
84
83
 
85
84
  def dispatch
@@ -103,7 +102,18 @@ module ActionController
103
102
  end
104
103
 
105
104
  def call(env)
106
- @app.call(env)
105
+ if @@cache_classes
106
+ @app.call(env)
107
+ else
108
+ Reloader.run do
109
+ # When class reloading is turned on, we will want to rebuild the
110
+ # middleware stack every time we process a request. If we don't
111
+ # rebuild the middleware stack, then the stack may contain references
112
+ # to old classes metal classes, which will b0rk class reloading.
113
+ build_middleware_stack
114
+ @app.call(env)
115
+ end
116
+ end
107
117
  end
108
118
 
109
119
  def _call(env)
@@ -114,5 +124,10 @@ module ActionController
114
124
  def flush_logger
115
125
  Base.logger.flush
116
126
  end
127
+
128
+ private
129
+ def build_middleware_stack
130
+ @app = @@middleware.build(lambda { |env| self.dup._call(env) })
131
+ end
117
132
  end
118
133
  end
@@ -139,7 +139,7 @@ module ActionController
139
139
  end
140
140
 
141
141
  def decode_credentials(request)
142
- ActiveSupport::Base64.decode64(authorization(request).split.last || '')
142
+ ActiveSupport::Base64.decode64(authorization(request).split(' ', 2).last || '')
143
143
  end
144
144
 
145
145
  def encode_credentials(user_name, password)
@@ -195,9 +195,10 @@ module ActionController
195
195
  return false unless password
196
196
 
197
197
  method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
198
+ uri = credentials[:uri][0,1] == '/' ? request.request_uri : request.url
198
199
 
199
200
  [true, false].any? do |password_is_ha1|
200
- expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1)
201
+ expected = expected_response(method, uri, credentials, password, password_is_ha1)
201
202
  expected == credentials[:response]
202
203
  end
203
204
  end
@@ -47,6 +47,8 @@ module ActionController
47
47
  false
48
48
  end
49
49
  rescue Exception => e # YAML, XML or Ruby code block errors
50
+ logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
51
+
50
52
  raise
51
53
  { "body" => request.raw_post,
52
54
  "content_type" => request.content_type,
@@ -67,5 +69,9 @@ module ActionController
67
69
 
68
70
  nil
69
71
  end
72
+
73
+ def logger
74
+ defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
75
+ end
70
76
  end
71
77
  end
@@ -1,14 +1,21 @@
1
+ require 'thread'
2
+
1
3
  module ActionController
2
4
  class Reloader
5
+ @@default_lock = Mutex.new
6
+ cattr_accessor :default_lock
7
+
3
8
  class BodyWrapper
4
- def initialize(body)
9
+ def initialize(body, lock)
5
10
  @body = body
11
+ @lock = lock
6
12
  end
7
13
 
8
14
  def close
9
15
  @body.close if @body.respond_to?(:close)
10
16
  ensure
11
17
  Dispatcher.cleanup_application
18
+ @lock.unlock
12
19
  end
13
20
 
14
21
  def method_missing(*args, &block)
@@ -20,26 +27,28 @@ module ActionController
20
27
  end
21
28
  end
22
29
 
23
- def initialize(app)
24
- @app = app
25
- end
26
-
27
- def call(env)
28
- Dispatcher.reload_application
29
- status, headers, body = @app.call(env)
30
- # We do not want to call 'cleanup_application' in an ensure block
31
- # because the returned Rack response body may lazily generate its data. This
32
- # is for example the case if one calls
33
- #
34
- # render :text => lambda { ... code here which refers to application models ... }
35
- #
36
- # in an ActionController.
37
- #
38
- # Instead, we will want to cleanup the application code after the request is
39
- # completely finished. So we wrap the body in a BodyWrapper class so that
40
- # when the Rack handler calls #close during the end of the request, we get to
41
- # run our cleanup code.
42
- [status, headers, BodyWrapper.new(body)]
30
+ def self.run(lock = @@default_lock)
31
+ lock.lock
32
+ begin
33
+ Dispatcher.reload_application
34
+ status, headers, body = yield
35
+ # We do not want to call 'cleanup_application' in an ensure block
36
+ # because the returned Rack response body may lazily generate its data. This
37
+ # is for example the case if one calls
38
+ #
39
+ # render :text => lambda { ... code here which refers to application models ... }
40
+ #
41
+ # in an ActionController.
42
+ #
43
+ # Instead, we will want to cleanup the application code after the request is
44
+ # completely finished. So we wrap the body in a BodyWrapper class so that
45
+ # when the Rack handler calls #close during the end of the request, we get to
46
+ # run our cleanup code.
47
+ [status, headers, BodyWrapper.new(body, lock)]
48
+ rescue Exception
49
+ lock.unlock
50
+ raise
51
+ end
43
52
  end
44
53
  end
45
54
  end
@@ -81,12 +81,13 @@ module ActionController #:nodoc:
81
81
 
82
82
  # Returns true or false if a request is verified. Checks:
83
83
  #
84
- # * is the format restricted? By default, only HTML and AJAX requests are checked.
84
+ # * is the format restricted? By default, only HTML requests are checked.
85
85
  # * is it a GET request? Gets should be safe and idempotent
86
86
  # * Does the form_authenticity_token match the given token value from the params?
87
87
  def verified_request?
88
88
  !protect_against_forgery? ||
89
89
  request.method == :get ||
90
+ request.xhr? ||
90
91
  !verifiable_request_format? ||
91
92
  form_authenticity_token == params[request_forgery_protection_token]
92
93
  end
@@ -317,9 +317,10 @@ module ActionController
317
317
  # notes.resources :attachments
318
318
  # end
319
319
  #
320
- # * <tt>:path_names</tt> - Specify different names for the 'new' and 'edit' actions. For example:
320
+ # * <tt>:path_names</tt> - Specify different path names for the actions. For example:
321
321
  # # new_products_path == '/productos/nuevo'
322
- # map.resources :products, :as => 'productos', :path_names => { :new => 'nuevo', :edit => 'editar' }
322
+ # # bids_product_path(1) == '/productos/1/licitacoes'
323
+ # map.resources :products, :as => 'productos', :member => { :bids => :get }, :path_names => { :new => 'nuevo', :bids => 'licitacoes' }
323
324
  #
324
325
  # You can also set default action names from an environment, like this:
325
326
  # config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' }
@@ -525,16 +526,16 @@ module ActionController
525
526
  resource = Resource.new(entities, options)
526
527
 
527
528
  with_options :controller => resource.controller do |map|
528
- map_collection_actions(map, resource)
529
- map_default_collection_actions(map, resource)
530
- map_new_actions(map, resource)
531
- map_member_actions(map, resource)
532
-
533
529
  map_associations(resource, options)
534
530
 
535
531
  if block_given?
536
532
  with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
537
533
  end
534
+
535
+ map_collection_actions(map, resource)
536
+ map_default_collection_actions(map, resource)
537
+ map_new_actions(map, resource)
538
+ map_member_actions(map, resource)
538
539
  end
539
540
  end
540
541
 
@@ -542,16 +543,16 @@ module ActionController
542
543
  resource = SingletonResource.new(entities, options)
543
544
 
544
545
  with_options :controller => resource.controller do |map|
545
- map_collection_actions(map, resource)
546
- map_new_actions(map, resource)
547
- map_member_actions(map, resource)
548
- map_default_singleton_actions(map, resource)
549
-
550
546
  map_associations(resource, options)
551
547
 
552
548
  if block_given?
553
549
  with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
554
550
  end
551
+
552
+ map_collection_actions(map, resource)
553
+ map_new_actions(map, resource)
554
+ map_member_actions(map, resource)
555
+ map_default_singleton_actions(map, resource)
555
556
  end
556
557
  end
557
558
 
@@ -586,7 +587,10 @@ module ActionController
586
587
  resource.collection_methods.each do |method, actions|
587
588
  actions.each do |action|
588
589
  [method].flatten.each do |m|
589
- map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
590
+ action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
591
+ action_path ||= action
592
+
593
+ map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
590
594
  end
591
595
  end
592
596
  end
@@ -166,6 +166,12 @@ module ActionController # :nodoc:
166
166
  str
167
167
  end
168
168
 
169
+ def flush #:nodoc:
170
+ ActiveSupport::Deprecation.warn(
171
+ 'Calling output.flush is no longer needed for streaming output ' +
172
+ 'because ActionController::Response automatically handles it', caller)
173
+ end
174
+
169
175
  def set_cookie(key, value)
170
176
  if value.has_key?(:http_only)
171
177
  ActiveSupport::Deprecation.warn(
@@ -271,6 +271,9 @@ module ActionController
271
271
 
272
272
  ALLOWED_REQUIREMENTS_FOR_OPTIMISATION = [:controller, :action].to_set
273
273
 
274
+ mattr_accessor :generate_best_match
275
+ self.generate_best_match = true
276
+
274
277
  # The root paths which may contain controller files
275
278
  mattr_accessor :controller_paths
276
279
  self.controller_paths = []
@@ -405,11 +405,14 @@ module ActionController
405
405
  end
406
406
 
407
407
  # don't use the recalled keys when determining which routes to check
408
- routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }]
408
+ future_routes, deprecated_routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }]
409
+ routes = Routing.generate_best_match ? deprecated_routes : future_routes
409
410
 
410
- routes.each do |route|
411
+ routes.each_with_index do |route, index|
411
412
  results = route.__send__(method, options, merged, expire_on)
412
- return results if results && (!results.is_a?(Array) || results.first)
413
+ if results && (!results.is_a?(Array) || results.first)
414
+ return results
415
+ end
413
416
  end
414
417
  end
415
418
 
@@ -448,7 +451,10 @@ module ActionController
448
451
  @routes_by_controller ||= Hash.new do |controller_hash, controller|
449
452
  controller_hash[controller] = Hash.new do |action_hash, action|
450
453
  action_hash[action] = Hash.new do |key_hash, keys|
451
- key_hash[keys] = routes_for_controller_and_action_and_keys(controller, action, keys)
454
+ key_hash[keys] = [
455
+ routes_for_controller_and_action_and_keys(controller, action, keys),
456
+ deprecated_routes_for_controller_and_action_and_keys(controller, action, keys)
457
+ ]
452
458
  end
453
459
  end
454
460
  end
@@ -460,10 +466,11 @@ module ActionController
460
466
  merged = options if expire_on[:controller]
461
467
  action = merged[:action] || 'index'
462
468
 
463
- routes_by_controller[controller][action][merged.keys]
469
+ routes_by_controller[controller][action][merged.keys][1]
464
470
  end
465
471
 
466
472
  def routes_for_controller_and_action(controller, action)
473
+ ActiveSupport::Deprecation.warn "routes_for_controller_and_action() has been deprecated. Please use routes_for()"
467
474
  selected = routes.select do |route|
468
475
  route.matches_controller_and_action? controller, action
469
476
  end
@@ -471,6 +478,12 @@ module ActionController
471
478
  end
472
479
 
473
480
  def routes_for_controller_and_action_and_keys(controller, action, keys)
481
+ routes.select do |route|
482
+ route.matches_controller_and_action? controller, action
483
+ end
484
+ end
485
+
486
+ def deprecated_routes_for_controller_and_action_and_keys(controller, action, keys)
474
487
  selected = routes.select do |route|
475
488
  route.matches_controller_and_action? controller, action
476
489
  end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/string/bytesize'
2
+
1
3
  module ActionController #:nodoc:
2
4
  # Methods for sending arbitrary data and for streaming files to the browser,
3
5
  # instead of rendering.
@@ -137,7 +139,7 @@ module ActionController #:nodoc:
137
139
  # instead. See ActionController::Base#render for more information.
138
140
  def send_data(data, options = {}) #:doc:
139
141
  logger.info "Sending data #{options[:filename]}" if logger
140
- send_file_headers! options.merge(:length => data.size)
142
+ send_file_headers! options.merge(:length => data.bytesize)
141
143
  @performed_render = false
142
144
  render :status => options[:status], :text => data
143
145
  end