actionpack 2.0.5 → 2.1.0

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 (210) hide show
  1. data/CHANGELOG +149 -7
  2. data/MIT-LICENSE +1 -1
  3. data/README +1 -1
  4. data/Rakefile +5 -6
  5. data/lib/action_controller.rb +2 -2
  6. data/lib/action_controller/assertions/model_assertions.rb +2 -1
  7. data/lib/action_controller/assertions/response_assertions.rb +4 -2
  8. data/lib/action_controller/assertions/routing_assertions.rb +3 -3
  9. data/lib/action_controller/assertions/selector_assertions.rb +30 -27
  10. data/lib/action_controller/assertions/tag_assertions.rb +3 -3
  11. data/lib/action_controller/base.rb +103 -129
  12. data/lib/action_controller/benchmarking.rb +3 -3
  13. data/lib/action_controller/caching.rb +41 -652
  14. data/lib/action_controller/caching/actions.rb +144 -0
  15. data/lib/action_controller/caching/fragments.rb +138 -0
  16. data/lib/action_controller/caching/pages.rb +154 -0
  17. data/lib/action_controller/caching/sql_cache.rb +18 -0
  18. data/lib/action_controller/caching/sweeping.rb +97 -0
  19. data/lib/action_controller/cgi_ext/cookie.rb +27 -23
  20. data/lib/action_controller/cgi_ext/stdinput.rb +1 -0
  21. data/lib/action_controller/cgi_process.rb +6 -4
  22. data/lib/action_controller/components.rb +7 -6
  23. data/lib/action_controller/cookies.rb +31 -19
  24. data/lib/action_controller/dispatcher.rb +51 -84
  25. data/lib/action_controller/filters.rb +295 -421
  26. data/lib/action_controller/flash.rb +1 -6
  27. data/lib/action_controller/headers.rb +31 -0
  28. data/lib/action_controller/helpers.rb +26 -9
  29. data/lib/action_controller/http_authentication.rb +1 -1
  30. data/lib/action_controller/integration.rb +65 -13
  31. data/lib/action_controller/layout.rb +24 -39
  32. data/lib/action_controller/mime_responds.rb +7 -3
  33. data/lib/action_controller/mime_type.rb +25 -9
  34. data/lib/action_controller/mime_types.rb +1 -1
  35. data/lib/action_controller/polymorphic_routes.rb +32 -17
  36. data/lib/action_controller/record_identifier.rb +10 -4
  37. data/lib/action_controller/request.rb +46 -30
  38. data/lib/action_controller/request_forgery_protection.rb +10 -9
  39. data/lib/action_controller/request_profiler.rb +29 -8
  40. data/lib/action_controller/rescue.rb +24 -24
  41. data/lib/action_controller/resources.rb +66 -23
  42. data/lib/action_controller/response.rb +2 -2
  43. data/lib/action_controller/routing.rb +113 -1229
  44. data/lib/action_controller/routing/builder.rb +204 -0
  45. data/lib/action_controller/{routing_optimisation.rb → routing/optimisations.rb} +13 -12
  46. data/lib/action_controller/routing/recognition_optimisation.rb +158 -0
  47. data/lib/action_controller/routing/route.rb +240 -0
  48. data/lib/action_controller/routing/route_set.rb +435 -0
  49. data/lib/action_controller/routing/routing_ext.rb +46 -0
  50. data/lib/action_controller/routing/segments.rb +283 -0
  51. data/lib/action_controller/session/active_record_store.rb +13 -8
  52. data/lib/action_controller/session/cookie_store.rb +20 -17
  53. data/lib/action_controller/session_management.rb +10 -3
  54. data/lib/action_controller/streaming.rb +45 -31
  55. data/lib/action_controller/test_case.rb +33 -23
  56. data/lib/action_controller/test_process.rb +39 -35
  57. data/lib/action_controller/url_rewriter.rb +18 -12
  58. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +1 -1
  59. data/lib/action_pack.rb +1 -1
  60. data/lib/action_pack/version.rb +2 -2
  61. data/lib/action_view.rb +11 -3
  62. data/lib/action_view/base.rb +73 -390
  63. data/lib/action_view/helpers/active_record_helper.rb +83 -62
  64. data/lib/action_view/helpers/asset_tag_helper.rb +101 -44
  65. data/lib/action_view/helpers/atom_feed_helper.rb +35 -7
  66. data/lib/action_view/helpers/benchmark_helper.rb +5 -3
  67. data/lib/action_view/helpers/cache_helper.rb +3 -2
  68. data/lib/action_view/helpers/capture_helper.rb +1 -2
  69. data/lib/action_view/helpers/date_helper.rb +104 -82
  70. data/lib/action_view/helpers/form_helper.rb +148 -75
  71. data/lib/action_view/helpers/form_options_helper.rb +44 -23
  72. data/lib/action_view/helpers/form_tag_helper.rb +22 -13
  73. data/lib/action_view/helpers/javascripts/controls.js +1 -1
  74. data/lib/action_view/helpers/javascripts/dragdrop.js +1 -1
  75. data/lib/action_view/helpers/javascripts/effects.js +1 -1
  76. data/lib/action_view/helpers/number_helper.rb +10 -3
  77. data/lib/action_view/helpers/prototype_helper.rb +61 -29
  78. data/lib/action_view/helpers/record_tag_helper.rb +3 -3
  79. data/lib/action_view/helpers/sanitize_helper.rb +23 -17
  80. data/lib/action_view/helpers/scriptaculous_helper.rb +86 -60
  81. data/lib/action_view/helpers/text_helper.rb +153 -125
  82. data/lib/action_view/helpers/url_helper.rb +83 -28
  83. data/lib/action_view/inline_template.rb +20 -0
  84. data/lib/action_view/partial_template.rb +70 -0
  85. data/lib/action_view/partials.rb +31 -73
  86. data/lib/action_view/template.rb +127 -0
  87. data/lib/action_view/template_error.rb +8 -7
  88. data/lib/action_view/template_finder.rb +177 -0
  89. data/lib/action_view/template_handler.rb +18 -1
  90. data/lib/action_view/template_handlers/builder.rb +10 -2
  91. data/lib/action_view/template_handlers/compilable.rb +128 -0
  92. data/lib/action_view/template_handlers/erb.rb +37 -2
  93. data/lib/action_view/template_handlers/rjs.rb +14 -1
  94. data/lib/action_view/test_case.rb +58 -0
  95. data/test/abstract_unit.rb +1 -1
  96. data/test/active_record_unit.rb +3 -6
  97. data/test/activerecord/active_record_store_test.rb +1 -2
  98. data/test/activerecord/render_partial_with_record_identification_test.rb +158 -41
  99. data/test/adv_attr_test.rb +20 -0
  100. data/test/controller/action_pack_assertions_test.rb +16 -19
  101. data/test/controller/addresses_render_test.rb +1 -1
  102. data/test/controller/assert_select_test.rb +13 -6
  103. data/test/controller/base_test.rb +48 -2
  104. data/test/controller/benchmark_test.rb +1 -2
  105. data/test/controller/caching_test.rb +282 -21
  106. data/test/controller/capture_test.rb +1 -1
  107. data/test/controller/cgi_test.rb +1 -1
  108. data/test/controller/components_test.rb +1 -1
  109. data/test/controller/content_type_test.rb +2 -2
  110. data/test/controller/cookie_test.rb +13 -2
  111. data/test/controller/custom_handler_test.rb +14 -10
  112. data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -1
  113. data/test/controller/dispatcher_test.rb +31 -49
  114. data/test/controller/fake_controllers.rb +17 -0
  115. data/test/controller/fake_models.rb +6 -0
  116. data/test/controller/filter_params_test.rb +14 -8
  117. data/test/controller/filters_test.rb +44 -16
  118. data/test/controller/flash_test.rb +2 -2
  119. data/test/controller/header_test.rb +14 -0
  120. data/test/controller/helper_test.rb +19 -15
  121. data/test/controller/html-scanner/document_test.rb +1 -2
  122. data/test/controller/html-scanner/node_test.rb +1 -2
  123. data/test/controller/html-scanner/sanitizer_test.rb +8 -5
  124. data/test/controller/html-scanner/tag_node_test.rb +1 -2
  125. data/test/controller/html-scanner/text_node_test.rb +2 -3
  126. data/test/controller/html-scanner/tokenizer_test.rb +8 -2
  127. data/test/controller/http_authentication_test.rb +1 -1
  128. data/test/controller/integration_test.rb +14 -16
  129. data/test/controller/integration_upload_test.rb +43 -0
  130. data/test/controller/layout_test.rb +26 -6
  131. data/test/controller/mime_responds_test.rb +39 -7
  132. data/test/controller/mime_type_test.rb +29 -5
  133. data/test/controller/new_render_test.rb +105 -34
  134. data/test/controller/polymorphic_routes_test.rb +32 -20
  135. data/test/controller/record_identifier_test.rb +38 -2
  136. data/test/controller/redirect_test.rb +21 -1
  137. data/test/controller/render_test.rb +59 -15
  138. data/test/controller/request_forgery_protection_test.rb +92 -5
  139. data/test/controller/request_test.rb +64 -6
  140. data/test/controller/rescue_test.rb +22 -6
  141. data/test/controller/resources_test.rb +102 -14
  142. data/test/controller/routing_test.rb +231 -19
  143. data/test/controller/selector_test.rb +2 -2
  144. data/test/controller/send_file_test.rb +14 -3
  145. data/test/controller/session/cookie_store_test.rb +16 -4
  146. data/test/controller/session/mem_cache_store_test.rb +3 -4
  147. data/test/controller/session_fixation_test.rb +1 -1
  148. data/test/controller/session_management_test.rb +23 -1
  149. data/test/controller/test_test.rb +39 -18
  150. data/test/controller/url_rewriter_test.rb +35 -1
  151. data/test/controller/verification_test.rb +1 -1
  152. data/test/controller/view_paths_test.rb +15 -12
  153. data/test/controller/webservice_test.rb +48 -3
  154. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  155. data/test/fixtures/company.rb +1 -0
  156. data/test/fixtures/customers/_customer.html.erb +1 -0
  157. data/test/fixtures/db_definitions/sqlite.sql +6 -0
  158. data/test/fixtures/functional_caching/_partial.erb +3 -0
  159. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  160. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  161. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  162. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  163. data/test/fixtures/mascot.rb +3 -0
  164. data/test/fixtures/mascots.yml +4 -0
  165. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  166. data/test/fixtures/multipart/boundary_problem_file +10 -0
  167. data/test/fixtures/public/javascripts/application.js +1 -0
  168. data/test/fixtures/public/javascripts/controls.js +1 -0
  169. data/test/fixtures/public/javascripts/dragdrop.js +1 -0
  170. data/test/fixtures/public/javascripts/effects.js +1 -0
  171. data/test/fixtures/public/javascripts/prototype.js +1 -0
  172. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  173. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  174. data/test/fixtures/reply.rb +1 -0
  175. data/test/fixtures/shared.html.erb +1 -0
  176. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  177. data/test/fixtures/test/_customer_counter.erb +1 -0
  178. data/test/fixtures/test/_form.erb +1 -0
  179. data/test/fixtures/test/_labelling_form.erb +1 -0
  180. data/test/fixtures/test/_raise.html.erb +1 -0
  181. data/test/fixtures/test/greeting.js.rjs +1 -0
  182. data/test/fixtures/topics/_topic.html.erb +1 -0
  183. data/test/template/active_record_helper_test.rb +25 -8
  184. data/test/template/asset_tag_helper_test.rb +100 -17
  185. data/test/template/atom_feed_helper_test.rb +29 -1
  186. data/test/template/benchmark_helper_test.rb +10 -22
  187. data/test/template/date_helper_test.rb +455 -153
  188. data/test/template/erb_util_test.rb +10 -42
  189. data/test/template/form_helper_test.rb +192 -66
  190. data/test/template/form_options_helper_test.rb +19 -8
  191. data/test/template/form_tag_helper_test.rb +11 -8
  192. data/test/template/javascript_helper_test.rb +3 -9
  193. data/test/template/number_helper_test.rb +6 -3
  194. data/test/template/prototype_helper_test.rb +27 -40
  195. data/test/template/record_tag_helper_test.rb +54 -0
  196. data/test/template/sanitize_helper_test.rb +5 -6
  197. data/test/template/scriptaculous_helper_test.rb +7 -13
  198. data/test/template/tag_helper_test.rb +3 -6
  199. data/test/template/template_finder_test.rb +73 -0
  200. data/test/template/template_object_test.rb +95 -0
  201. data/test/template/test_test.rb +56 -0
  202. data/test/template/text_helper_test.rb +46 -33
  203. data/test/template/url_helper_test.rb +8 -10
  204. metadata +65 -12
  205. data/lib/action_view/compiled_templates.rb +0 -69
  206. data/test/action_view_test.rb +0 -44
  207. data/test/activerecord/fixtures_test.rb +0 -24
  208. data/test/controller/fragment_store_setting_test.rb +0 -47
  209. data/test/template/compiled_templates_test.rb +0 -197
  210. data/test/template/deprecate_ivars_test.rb +0 -51
@@ -28,7 +28,6 @@ module ActionController #:nodoc:
28
28
  base.class_eval do
29
29
  include InstanceMethods
30
30
  alias_method_chain :assign_shortcuts, :flash
31
- alias_method_chain :process_cleanup, :flash
32
31
  alias_method_chain :reset_session, :flash
33
32
  end
34
33
  end
@@ -166,11 +165,7 @@ module ActionController #:nodoc:
166
165
  def assign_shortcuts_with_flash(request, response) #:nodoc:
167
166
  assign_shortcuts_without_flash(request, response)
168
167
  flash(:refresh)
169
- end
170
-
171
- def process_cleanup_with_flash
172
- flash.sweep if @_session
173
- process_cleanup_without_flash
168
+ flash.sweep if @_session && !component_request?
174
169
  end
175
170
  end
176
171
  end
@@ -0,0 +1,31 @@
1
+ module ActionController
2
+ module Http
3
+ class Headers < ::Hash
4
+
5
+ def initialize(constructor = {})
6
+ if constructor.is_a?(Hash)
7
+ super()
8
+ update(constructor)
9
+ else
10
+ super(constructor)
11
+ end
12
+ end
13
+
14
+ def [](header_name)
15
+ if include?(header_name)
16
+ super
17
+ else
18
+ super(normalize_header(header_name))
19
+ end
20
+ end
21
+
22
+
23
+ private
24
+ # Takes an HTTP header name and returns it in the
25
+ # format
26
+ def normalize_header(header_name)
27
+ "HTTP_#{header_name.upcase.gsub(/-/, '_')}"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -20,7 +20,7 @@ module ActionController #:nodoc:
20
20
  end
21
21
 
22
22
  # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+,
23
- # +numbers+ and +ActiveRecord+ objects, to name a few. These helpers are available to all templates
23
+ # +numbers+ and Active Record objects, to name a few. These helpers are available to all templates
24
24
  # by default.
25
25
  #
26
26
  # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
@@ -32,7 +32,7 @@ module ActionController #:nodoc:
32
32
  # controller which inherits from it.
33
33
  #
34
34
  # ==== Examples
35
- # The +to_s+ method from the +Time+ class can be wrapped in a helper method to display a custom message if
35
+ # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if
36
36
  # the Time object is blank:
37
37
  #
38
38
  # module FormattedTimeHelper
@@ -41,7 +41,7 @@ module ActionController #:nodoc:
41
41
  # end
42
42
  # end
43
43
  #
44
- # +FormattedTimeHelper+ can now be included in a controller, using the +helper+ class method:
44
+ # FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
45
45
  #
46
46
  # class EventsController < ActionController::Base
47
47
  # helper FormattedTimeHelper
@@ -74,22 +74,22 @@ module ActionController #:nodoc:
74
74
 
75
75
  # The +helper+ class method can take a series of helper module names, a block, or both.
76
76
  #
77
- # * <tt>*args</tt>: One or more +Modules+, +Strings+ or +Symbols+, or the special symbol <tt>:all</tt>.
77
+ # * <tt>*args</tt>: One or more modules, strings or symbols, or the special symbol <tt>:all</tt>.
78
78
  # * <tt>&block</tt>: A block defining helper methods.
79
79
  #
80
80
  # ==== Examples
81
- # When the argument is a +String+ or +Symbol+, the method will provide the "_helper" suffix, require the file
81
+ # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
82
82
  # and include the module in the template class. The second form illustrates how to include custom helpers
83
83
  # when working with namespaced controllers, or other cases where the file containing the helper definition is not
84
84
  # in one of Rails' standard load paths:
85
85
  # helper :foo # => requires 'foo_helper' and includes FooHelper
86
86
  # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
87
87
  #
88
- # When the argument is a +Module+, it will be included directly in the template class.
88
+ # When the argument is a module it will be included directly in the template class.
89
89
  # helper FooHelper # => includes FooHelper
90
90
  #
91
91
  # When the argument is the symbol <tt>:all</tt>, the controller will include all helpers from
92
- # <tt>app/helpers/**/*.rb</tt> under +RAILS_ROOT+.
92
+ # <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT.
93
93
  # helper :all
94
94
  #
95
95
  # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
@@ -143,11 +143,19 @@ module ActionController #:nodoc:
143
143
  # Declare a controller method as a helper. For example, the following
144
144
  # makes the +current_user+ controller method available to the view:
145
145
  # class ApplicationController < ActionController::Base
146
- # helper_method :current_user
146
+ # helper_method :current_user, :logged_in?
147
+ #
147
148
  # def current_user
148
- # @current_user ||= User.find(session[:user])
149
+ # @current_user ||= User.find_by_id(session[:user])
149
150
  # end
151
+ #
152
+ # def logged_in?
153
+ # current_user != nil
154
+ # end
150
155
  # end
156
+ #
157
+ # In a view:
158
+ # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
151
159
  def helper_method(*methods)
152
160
  methods.flatten.each do |method|
153
161
  master_helper_module.module_eval <<-end_eval
@@ -167,6 +175,15 @@ module ActionController #:nodoc:
167
175
  attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
168
176
  end
169
177
 
178
+ # Provides a proxy to access helpers methods from outside the view.
179
+ def helpers
180
+ unless @helper_proxy
181
+ @helper_proxy = ActionView::Base.new
182
+ @helper_proxy.extend master_helper_module
183
+ else
184
+ @helper_proxy
185
+ end
186
+ end
170
187
 
171
188
  private
172
189
  def default_helper_module!
@@ -70,7 +70,7 @@ module ActionController
70
70
  #
71
71
  # On shared hosts, Apache sometimes doesn't pass authentication headers to
72
72
  # FCGI instances. If your environment matches this description and you cannot
73
- # authenticate, try this rule in public/.htaccess (replace the plain one):
73
+ # authenticate, try this rule in your Apache setup:
74
74
  #
75
75
  # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
76
76
  module Basic
@@ -55,7 +55,10 @@ module ActionController
55
55
  # A running counter of the number of requests processed.
56
56
  attr_accessor :request_count
57
57
 
58
- # Create and initialize a new +Session+ instance.
58
+ class MultiPartNeededException < Exception
59
+ end
60
+
61
+ # Create and initialize a new Session instance.
59
62
  def initialize
60
63
  reset!
61
64
  end
@@ -133,25 +136,25 @@ module ActionController
133
136
  end
134
137
 
135
138
  # Performs a GET request, following any subsequent redirect.
136
- # See #request_via_redirect() for more information.
139
+ # See +request_via_redirect+ for more information.
137
140
  def get_via_redirect(path, parameters = nil, headers = nil)
138
141
  request_via_redirect(:get, path, parameters, headers)
139
142
  end
140
143
 
141
144
  # Performs a POST request, following any subsequent redirect.
142
- # See #request_via_redirect() for more information.
145
+ # See +request_via_redirect+ for more information.
143
146
  def post_via_redirect(path, parameters = nil, headers = nil)
144
147
  request_via_redirect(:post, path, parameters, headers)
145
148
  end
146
149
 
147
150
  # Performs a PUT request, following any subsequent redirect.
148
- # See #request_via_redirect() for more information.
151
+ # See +request_via_redirect+ for more information.
149
152
  def put_via_redirect(path, parameters = nil, headers = nil)
150
153
  request_via_redirect(:put, path, parameters, headers)
151
154
  end
152
155
 
153
156
  # Performs a DELETE request, following any subsequent redirect.
154
- # See #request_via_redirect() for more information.
157
+ # See +request_via_redirect+ for more information.
155
158
  def delete_via_redirect(path, parameters = nil, headers = nil)
156
159
  request_via_redirect(:delete, path, parameters, headers)
157
160
  end
@@ -163,12 +166,12 @@ module ActionController
163
166
 
164
167
  # Performs a GET request with the given parameters. The parameters may
165
168
  # be +nil+, a Hash, or a string that is appropriately encoded
166
- # (application/x-www-form-urlencoded or multipart/form-data). The headers
167
- # should be a hash. The keys will automatically be upcased, with the
169
+ # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
170
+ # The headers should be a hash. The keys will automatically be upcased, with the
168
171
  # prefix 'HTTP_' added if needed.
169
172
  #
170
- # You can also perform POST, PUT, DELETE, and HEAD requests with #post,
171
- # #put, #delete, and #head.
173
+ # You can also perform POST, PUT, DELETE, and HEAD requests with +post+,
174
+ # +put+, +delete+, and +head+.
172
175
  def get(path, parameters = nil, headers = nil)
173
176
  process :get, path, parameters, headers
174
177
  end
@@ -225,6 +228,8 @@ module ActionController
225
228
 
226
229
  super
227
230
 
231
+ stdinput.set_encoding(Encoding::BINARY) if stdinput.respond_to?(:set_encoding)
232
+ stdinput.force_encoding(Encoding::BINARY) if stdinput.respond_to?(:force_encoding)
228
233
  @stdinput = stdinput.is_a?(IO) ? stdinput : StringIO.new(stdinput || '')
229
234
  end
230
235
  end
@@ -294,15 +299,19 @@ module ActionController
294
299
 
295
300
  parse_result
296
301
  return status
302
+ rescue MultiPartNeededException
303
+ boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
304
+ status = process(method, path, multipart_body(parameters, boundary), (headers || {}).merge({"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"}))
305
+ return status
297
306
  end
298
307
 
299
308
  # Parses the result of the response and extracts the various values,
300
309
  # like cookies, status, headers, etc.
301
310
  def parse_result
302
- headers, result_body = @result.split(/\r\n\r\n/, 2)
311
+ response_headers, result_body = @result.split(/\r\n\r\n/, 2)
303
312
 
304
313
  @headers = Hash.new { |h,k| h[k] = [] }
305
- headers.each_line do |line|
314
+ response_headers.to_s.each_line do |line|
306
315
  key, value = line.strip.split(/:\s*/, 2)
307
316
  @headers[key.downcase] << value
308
317
  end
@@ -312,7 +321,7 @@ module ActionController
312
321
  @cookies[name] = value
313
322
  end
314
323
 
315
- @status, @status_message = @headers["status"].first.split(/ /)
324
+ @status, @status_message = @headers["status"].first.to_s.split(/ /)
316
325
  @status = @status.to_i
317
326
  end
318
327
 
@@ -342,7 +351,9 @@ module ActionController
342
351
  # Convert the given parameters to a request string. The parameters may
343
352
  # be a string, +nil+, or a Hash.
344
353
  def requestify(parameters, prefix=nil)
345
- if Hash === parameters
354
+ if TestUploadedFile === parameters
355
+ raise MultiPartNeededException
356
+ elsif Hash === parameters
346
357
  return nil if parameters.empty?
347
358
  parameters.map { |k,v| requestify(v, name_with_prefix(prefix, k)) }.join("&")
348
359
  elsif Array === parameters
@@ -353,6 +364,47 @@ module ActionController
353
364
  "#{CGI.escape(prefix)}=#{CGI.escape(parameters.to_s)}"
354
365
  end
355
366
  end
367
+
368
+ def multipart_requestify(params, first=true)
369
+ returning Hash.new do |p|
370
+ params.each do |key, value|
371
+ k = first ? CGI.escape(key.to_s) : "[#{CGI.escape(key.to_s)}]"
372
+ if Hash === value
373
+ multipart_requestify(value, false).each do |subkey, subvalue|
374
+ p[k + subkey] = subvalue
375
+ end
376
+ else
377
+ p[k] = value
378
+ end
379
+ end
380
+ end
381
+ end
382
+
383
+ def multipart_body(params, boundary)
384
+ multipart_requestify(params).map do |key, value|
385
+ if value.respond_to?(:original_filename)
386
+ File.open(value.path) do |f|
387
+ f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
388
+
389
+ <<-EOF
390
+ --#{boundary}\r
391
+ Content-Disposition: form-data; name="#{key}"; filename="#{CGI.escape(value.original_filename)}"\r
392
+ Content-Type: #{value.content_type}\r
393
+ Content-Length: #{File.stat(value.path).size}\r
394
+ \r
395
+ #{f.read}\r
396
+ EOF
397
+ end
398
+ else
399
+ <<-EOF
400
+ --#{boundary}\r
401
+ Content-Disposition: form-data; name="#{key}"\r
402
+ \r
403
+ #{value}\r
404
+ EOF
405
+ end
406
+ end.join("")+"--#{boundary}--\r"
407
+ end
356
408
  end
357
409
 
358
410
  # A module used to extend ActionController::Base, so that integration tests
@@ -65,15 +65,15 @@ module ActionController #:nodoc:
65
65
  # == Automatic layout assignment
66
66
  #
67
67
  # If there is a template in <tt>app/views/layouts/</tt> with the same name as the current controller then it will be automatically
68
- # set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named
68
+ # set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named
69
69
  # <tt>app/views/layouts/weblog.erb</tt> or <tt>app/views/layouts/weblog.builder</tt> exists then it will be automatically set as
70
70
  # the layout for your WeblogController. You can create a layout with the name <tt>application.erb</tt> or <tt>application.builder</tt>
71
- # and this will be set as the default controller if there is no layout with the same name as the current controller and there is
71
+ # and this will be set as the default controller if there is no layout with the same name as the current controller and there is
72
72
  # no layout explicitly assigned with the +layout+ method. Nested controllers use the same folder structure for automatic layout.
73
73
  # assignment. So an Admin::WeblogController will look for a template named <tt>app/views/layouts/admin/weblog.erb</tt>.
74
74
  # Setting a layout explicitly will always override the automatic behaviour for the controller where the layout is set.
75
75
  # Explicitly setting the layout in a parent class, though, will not override the child class's layout assignment if the child
76
- # class has a layout with the same name.
76
+ # class has a layout with the same name.
77
77
  #
78
78
  # == Inheritance for layouts
79
79
  #
@@ -113,7 +113,7 @@ module ActionController #:nodoc:
113
113
  # logged_in? ? "writer_layout" : "reader_layout"
114
114
  # end
115
115
  #
116
- # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
116
+ # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
117
117
  # is logged in or not.
118
118
  #
119
119
  # If you want to use an inline method, such as a proc, do something like this:
@@ -132,24 +132,24 @@ module ActionController #:nodoc:
132
132
  # == Conditional layouts
133
133
  #
134
134
  # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
135
- # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
135
+ # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
136
136
  # <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
137
137
  #
138
138
  # class WeblogController < ActionController::Base
139
139
  # layout "weblog_standard", :except => :rss
140
- #
140
+ #
141
141
  # # ...
142
142
  #
143
143
  # end
144
144
  #
145
- # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout
145
+ # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout
146
146
  # around the rendered view.
147
147
  #
148
- # Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
148
+ # Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
149
149
  # #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
150
150
  #
151
151
  # == Using a different layout in the action render call
152
- #
152
+ #
153
153
  # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
154
154
  # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
155
155
  # You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
@@ -176,21 +176,19 @@ module ActionController #:nodoc:
176
176
  def layout_conditions #:nodoc:
177
177
  @layout_conditions ||= read_inheritable_attribute("layout_conditions")
178
178
  end
179
-
179
+
180
180
  def default_layout(format) #:nodoc:
181
- layout = read_inheritable_attribute("layout")
181
+ layout = read_inheritable_attribute("layout")
182
182
  return layout unless read_inheritable_attribute("auto_layout")
183
183
  @default_layout ||= {}
184
184
  @default_layout[format] ||= default_layout_with_format(format, layout)
185
185
  @default_layout[format]
186
186
  end
187
-
187
+
188
188
  def layout_list #:nodoc:
189
- view_paths.collect do |path|
190
- Dir["#{path}/layouts/**/*"]
191
- end.flatten
189
+ Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] }
192
190
  end
193
-
191
+
194
192
  private
195
193
  def inherited_with_layout(child)
196
194
  inherited_without_layout(child)
@@ -207,13 +205,7 @@ module ActionController #:nodoc:
207
205
  def normalize_conditions(conditions)
208
206
  conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})}
209
207
  end
210
-
211
- def layout_directory_exists_cache
212
- @@layout_directory_exists_cache ||= Hash.new do |h, dirname|
213
- h[dirname] = File.directory? dirname
214
- end
215
- end
216
-
208
+
217
209
  def default_layout_with_format(format, layout)
218
210
  list = layout_list
219
211
  if list.grep(%r{layouts/#{layout}\.#{format}(\.[a-z][0-9a-z]*)+$}).empty?
@@ -235,7 +227,7 @@ module ActionController #:nodoc:
235
227
  when Symbol then send!(layout)
236
228
  when Proc then layout.call(self)
237
229
  end
238
-
230
+
239
231
  # Explicitly passed layout names with slashes are looked up relative to the template root,
240
232
  # but auto-discovered layouts derived from a nested controller will contain a slash, though be relative
241
233
  # to the 'layouts' directory so we have to check the file system to infer which case the layout name came from.
@@ -249,16 +241,14 @@ module ActionController #:nodoc:
249
241
  end
250
242
 
251
243
  protected
252
- def render_with_a_layout(options = nil, &block) #:nodoc:
244
+ def render_with_a_layout(options = nil, extra_options = {}, &block) #:nodoc:
253
245
  template_with_options = options.is_a?(Hash)
254
-
255
- if apply_layout?(template_with_options, options) && (layout = pick_layout(template_with_options, options))
256
- assert_existence_of_template_file(layout)
257
246
 
247
+ if (layout = pick_layout(template_with_options, options)) && apply_layout?(template_with_options, options)
258
248
  options = options.merge :layout => false if template_with_options
259
249
  logger.info("Rendering template within #{layout}") if logger
260
250
 
261
- content_for_layout = render_with_no_layout(options, &block)
251
+ content_for_layout = render_with_no_layout(options, extra_options, &block)
262
252
  erase_render_results
263
253
  add_variables_to_assigns
264
254
  @template.instance_variable_set("@content_for_layout", content_for_layout)
@@ -266,7 +256,7 @@ module ActionController #:nodoc:
266
256
  status = template_with_options ? options[:status] : nil
267
257
  render_for_text(@template.render_file(layout, true), status)
268
258
  else
269
- render_with_no_layout(options, &block)
259
+ render_with_no_layout(options, extra_options, &block)
270
260
  end
271
261
  end
272
262
 
@@ -278,7 +268,7 @@ module ActionController #:nodoc:
278
268
  end
279
269
 
280
270
  def candidate_for_layout?(options)
281
- (options.has_key?(:layout) && options[:layout] != false) ||
271
+ (options.has_key?(:layout) && options[:layout] != false) ||
282
272
  options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing).compact.empty? &&
283
273
  !template_exempt_from_layout?(options[:template] || default_template_name(options[:action]))
284
274
  end
@@ -304,7 +294,7 @@ module ActionController #:nodoc:
304
294
  when only = conditions[:only]
305
295
  only.include?(action_name)
306
296
  when except = conditions[:except]
307
- !except.include?(action_name)
297
+ !except.include?(action_name)
308
298
  else
309
299
  true
310
300
  end
@@ -312,14 +302,9 @@ module ActionController #:nodoc:
312
302
  true
313
303
  end
314
304
  end
315
-
316
- # Does a layout directory for this class exist?
317
- # we cache this info in a class level hash
305
+
318
306
  def layout_directory?(layout_name)
319
- view_paths.find do |path|
320
- next unless template_path = Dir[File.join(path, 'layouts', layout_name) + ".*"].first
321
- self.class.send!(:layout_directory_exists_cache)[File.dirname(template_path)]
322
- end
307
+ @template.finder.find_template_extension_from_handler(File.join('layouts', layout_name))
323
308
  end
324
309
  end
325
310
  end