actionpack 2.1.2 → 2.2.2

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 (200) hide show
  1. data/CHANGELOG +223 -7
  2. data/README +6 -12
  3. data/Rakefile +11 -11
  4. data/lib/action_controller.rb +9 -9
  5. data/lib/action_controller/assertions/response_assertions.rb +29 -78
  6. data/lib/action_controller/assertions/routing_assertions.rb +33 -33
  7. data/lib/action_controller/assertions/selector_assertions.rb +9 -5
  8. data/lib/action_controller/base.rb +227 -161
  9. data/lib/action_controller/benchmarking.rb +37 -24
  10. data/lib/action_controller/caching/actions.rb +53 -21
  11. data/lib/action_controller/caching/fragments.rb +10 -36
  12. data/lib/action_controller/caching/sweeping.rb +3 -3
  13. data/lib/action_controller/cgi_ext/session.rb +2 -22
  14. data/lib/action_controller/cgi_process.rb +8 -46
  15. data/lib/action_controller/components.rb +4 -1
  16. data/lib/action_controller/cookies.rb +10 -0
  17. data/lib/action_controller/dispatcher.rb +49 -15
  18. data/lib/action_controller/filters.rb +48 -10
  19. data/lib/action_controller/headers.rb +16 -14
  20. data/lib/action_controller/helpers.rb +2 -2
  21. data/lib/action_controller/http_authentication.rb +1 -1
  22. data/lib/action_controller/integration.rb +57 -60
  23. data/lib/action_controller/layout.rb +27 -53
  24. data/lib/action_controller/mime_responds.rb +5 -1
  25. data/lib/action_controller/mime_type.rb +64 -42
  26. data/lib/action_controller/mime_types.rb +2 -1
  27. data/lib/action_controller/performance_test.rb +16 -0
  28. data/lib/action_controller/polymorphic_routes.rb +16 -9
  29. data/lib/action_controller/rack_process.rb +303 -0
  30. data/lib/action_controller/request.rb +205 -97
  31. data/lib/action_controller/request_forgery_protection.rb +2 -2
  32. data/lib/action_controller/request_profiler.rb +0 -0
  33. data/lib/action_controller/rescue.rb +20 -115
  34. data/lib/action_controller/resources.rb +186 -83
  35. data/lib/action_controller/response.rb +140 -26
  36. data/lib/action_controller/routing.rb +28 -30
  37. data/lib/action_controller/routing/builder.rb +45 -54
  38. data/lib/action_controller/routing/optimisations.rb +31 -21
  39. data/lib/action_controller/routing/recognition_optimisation.rb +33 -27
  40. data/lib/action_controller/routing/route.rb +162 -147
  41. data/lib/action_controller/routing/route_set.rb +8 -7
  42. data/lib/action_controller/routing/routing_ext.rb +4 -1
  43. data/lib/action_controller/routing/segments.rb +50 -21
  44. data/lib/action_controller/session/cookie_store.rb +3 -2
  45. data/lib/action_controller/session/drb_server.rb +7 -7
  46. data/lib/action_controller/session_management.rb +6 -2
  47. data/lib/action_controller/streaming.rb +15 -8
  48. data/lib/action_controller/templates/rescues/diagnostics.erb +2 -2
  49. data/lib/action_controller/templates/rescues/template_error.erb +2 -2
  50. data/lib/action_controller/test_case.rb +66 -2
  51. data/lib/action_controller/test_process.rb +71 -66
  52. data/lib/action_controller/translation.rb +13 -0
  53. data/lib/action_controller/url_rewriter.rb +90 -13
  54. data/lib/action_controller/vendor/html-scanner/html/node.rb +9 -2
  55. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +1 -1
  56. data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -2
  57. data/lib/action_controller/verification.rb +2 -2
  58. data/lib/action_pack/version.rb +1 -1
  59. data/lib/action_view.rb +19 -11
  60. data/lib/action_view/base.rb +184 -150
  61. data/lib/action_view/helpers.rb +38 -0
  62. data/lib/action_view/helpers/active_record_helper.rb +56 -27
  63. data/lib/action_view/helpers/asset_tag_helper.rb +356 -153
  64. data/lib/action_view/helpers/atom_feed_helper.rb +74 -19
  65. data/lib/action_view/helpers/benchmark_helper.rb +3 -3
  66. data/lib/action_view/helpers/cache_helper.rb +1 -2
  67. data/lib/action_view/helpers/capture_helper.rb +19 -44
  68. data/lib/action_view/helpers/date_helper.rb +486 -296
  69. data/lib/action_view/helpers/debug_helper.rb +20 -13
  70. data/lib/action_view/helpers/form_helper.rb +71 -30
  71. data/lib/action_view/helpers/form_options_helper.rb +15 -85
  72. data/lib/action_view/helpers/form_tag_helper.rb +61 -38
  73. data/lib/action_view/helpers/javascript_helper.rb +80 -89
  74. data/lib/action_view/helpers/number_helper.rb +179 -74
  75. data/lib/action_view/helpers/prototype_helper.rb +216 -201
  76. data/lib/action_view/helpers/record_tag_helper.rb +4 -5
  77. data/lib/action_view/helpers/sanitize_helper.rb +65 -33
  78. data/lib/action_view/helpers/scriptaculous_helper.rb +2 -2
  79. data/lib/action_view/helpers/tag_helper.rb +39 -22
  80. data/lib/action_view/helpers/text_helper.rb +212 -118
  81. data/lib/action_view/helpers/translation_helper.rb +21 -0
  82. data/lib/action_view/helpers/url_helper.rb +100 -58
  83. data/lib/action_view/inline_template.rb +13 -14
  84. data/lib/action_view/locale/en.yml +91 -0
  85. data/lib/action_view/partials.rb +100 -55
  86. data/lib/action_view/paths.rb +125 -0
  87. data/lib/action_view/renderable.rb +102 -0
  88. data/lib/action_view/renderable_partial.rb +48 -0
  89. data/lib/action_view/template.rb +90 -101
  90. data/lib/action_view/template_error.rb +11 -21
  91. data/lib/action_view/template_handler.rb +8 -28
  92. data/lib/action_view/template_handlers.rb +45 -0
  93. data/lib/action_view/template_handlers/builder.rb +5 -15
  94. data/lib/action_view/template_handlers/erb.rb +9 -6
  95. data/lib/action_view/template_handlers/rjs.rb +2 -17
  96. data/lib/action_view/test_case.rb +7 -4
  97. data/test/abstract_unit.rb +4 -1
  98. data/test/active_record_unit.rb +28 -30
  99. data/test/activerecord/render_partial_with_record_identification_test.rb +25 -12
  100. data/test/controller/action_pack_assertions_test.rb +8 -37
  101. data/test/controller/addresses_render_test.rb +0 -3
  102. data/test/controller/assert_select_test.rb +51 -24
  103. data/test/controller/base_test.rb +4 -4
  104. data/test/controller/caching_test.rb +136 -66
  105. data/test/controller/capture_test.rb +1 -21
  106. data/test/controller/cgi_test.rb +157 -10
  107. data/test/controller/components_test.rb +41 -25
  108. data/test/controller/content_type_test.rb +49 -17
  109. data/test/controller/cookie_test.rb +1 -1
  110. data/test/controller/deprecation/deprecated_base_methods_test.rb +0 -3
  111. data/test/controller/dispatcher_test.rb +9 -1
  112. data/test/controller/filter_params_test.rb +2 -2
  113. data/test/controller/filters_test.rb +13 -13
  114. data/test/controller/html-scanner/cdata_node_test.rb +15 -0
  115. data/test/controller/html-scanner/node_test.rb +21 -0
  116. data/test/controller/html-scanner/sanitizer_test.rb +14 -0
  117. data/test/controller/integration_test.rb +167 -6
  118. data/test/controller/layout_test.rb +11 -68
  119. data/test/controller/logging_test.rb +46 -0
  120. data/test/controller/mime_responds_test.rb +61 -59
  121. data/test/controller/mime_type_test.rb +6 -6
  122. data/test/controller/polymorphic_routes_test.rb +37 -2
  123. data/test/controller/rack_test.rb +323 -0
  124. data/test/controller/redirect_test.rb +72 -71
  125. data/test/controller/render_test.rb +1120 -108
  126. data/test/controller/request_forgery_protection_test.rb +66 -52
  127. data/test/controller/request_test.rb +103 -146
  128. data/test/controller/rescue_test.rb +20 -24
  129. data/test/controller/resources_test.rb +408 -25
  130. data/test/controller/routing_test.rb +1774 -1774
  131. data/test/controller/send_file_test.rb +0 -4
  132. data/test/controller/session/cookie_store_test.rb +53 -1
  133. data/test/controller/test_test.rb +15 -37
  134. data/test/controller/translation_test.rb +26 -0
  135. data/test/controller/url_rewriter_test.rb +27 -28
  136. data/test/controller/view_paths_test.rb +48 -47
  137. data/test/fixtures/_top_level_partial.html.erb +1 -0
  138. data/test/fixtures/_top_level_partial_only.erb +1 -0
  139. data/test/fixtures/developers/_developer.erb +1 -0
  140. data/test/fixtures/fun/games/_game.erb +1 -0
  141. data/test/fixtures/fun/serious/games/_game.erb +1 -0
  142. data/test/fixtures/functional_caching/formatted_fragment_cached.html.erb +3 -0
  143. data/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs +6 -0
  144. data/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder +5 -0
  145. data/test/fixtures/functional_caching/inline_fragment_cached.html.erb +2 -0
  146. data/test/fixtures/layouts/_column.html.erb +2 -0
  147. data/test/fixtures/projects/_project.erb +1 -0
  148. data/test/fixtures/public/javascripts/subdir/subdir.js +1 -0
  149. data/test/fixtures/public/stylesheets/subdir/subdir.css +1 -0
  150. data/test/fixtures/replies/_reply.erb +1 -0
  151. data/test/fixtures/test/_counter.html.erb +1 -0
  152. data/test/fixtures/test/_customer.erb +1 -1
  153. data/test/fixtures/test/_customer_with_var.erb +1 -0
  154. data/test/fixtures/test/_layout_for_block_with_args.html.erb +3 -0
  155. data/test/fixtures/test/_local_inspector.html.erb +1 -0
  156. data/test/fixtures/test/_partial_with_only_html_version.html.erb +1 -0
  157. data/test/fixtures/test/hello.builder +1 -1
  158. data/test/fixtures/test/hyphen-ated.erb +1 -0
  159. data/test/fixtures/test/implicit_content_type.atom.builder +2 -0
  160. data/test/fixtures/test/nested_layout.erb +3 -0
  161. data/test/fixtures/test/non_erb_block_content_for.builder +1 -1
  162. data/test/fixtures/test/sub_template_raise.html.erb +1 -0
  163. data/test/fixtures/test/template.erb +1 -0
  164. data/test/fixtures/test/using_layout_around_block_with_args.html.erb +1 -0
  165. data/test/template/active_record_helper_i18n_test.rb +46 -0
  166. data/test/template/active_record_helper_test.rb +24 -24
  167. data/test/template/asset_tag_helper_test.rb +161 -29
  168. data/test/template/atom_feed_helper_test.rb +114 -5
  169. data/test/template/compiled_templates_test.rb +59 -0
  170. data/test/template/date_helper_i18n_test.rb +113 -0
  171. data/test/template/date_helper_test.rb +403 -109
  172. data/test/template/form_helper_test.rb +213 -154
  173. data/test/template/form_options_helper_test.rb +249 -897
  174. data/test/template/form_tag_helper_test.rb +80 -32
  175. data/test/template/javascript_helper_test.rb +17 -18
  176. data/test/template/number_helper_i18n_test.rb +54 -0
  177. data/test/template/number_helper_test.rb +43 -13
  178. data/test/template/prototype_helper_test.rb +101 -84
  179. data/test/template/record_tag_helper_test.rb +24 -20
  180. data/test/template/render_test.rb +193 -0
  181. data/test/template/sanitize_helper_test.rb +3 -3
  182. data/test/template/tag_helper_test.rb +34 -14
  183. data/test/template/text_helper_test.rb +83 -9
  184. data/test/template/translation_helper_test.rb +28 -0
  185. data/test/template/url_helper_test.rb +55 -18
  186. metadata +57 -18
  187. data/lib/action_view/helpers/javascripts/controls.js +0 -963
  188. data/lib/action_view/helpers/javascripts/dragdrop.js +0 -972
  189. data/lib/action_view/helpers/javascripts/effects.js +0 -1120
  190. data/lib/action_view/helpers/javascripts/prototype.js +0 -4225
  191. data/lib/action_view/partial_template.rb +0 -70
  192. data/lib/action_view/template_finder.rb +0 -177
  193. data/lib/action_view/template_handlers/compilable.rb +0 -128
  194. data/test/controller/custom_handler_test.rb +0 -45
  195. data/test/controller/new_render_test.rb +0 -945
  196. data/test/fixtures/test/block_content_for.erb +0 -2
  197. data/test/fixtures/test/erb_content_for.erb +0 -2
  198. data/test/template/deprecated_erb_variable_test.rb +0 -9
  199. data/test/template/template_finder_test.rb +0 -73
  200. data/test/template/template_object_test.rb +0 -95
@@ -3,11 +3,6 @@ module ActionController #:nodoc:
3
3
  def self.included(base)
4
4
  base.extend(ClassMethods)
5
5
  base.class_eval do
6
- # NOTE: Can't use alias_method_chain here because +render_without_layout+ is already
7
- # defined as a publicly exposed method
8
- alias_method :render_with_no_layout, :render
9
- alias_method :render, :render_with_a_layout
10
-
11
6
  class << self
12
7
  alias_method_chain :inherited, :layout
13
8
  end
@@ -169,17 +164,17 @@ module ActionController #:nodoc:
169
164
  # performance and have access to them as any normal template would.
170
165
  def layout(template_name, conditions = {}, auto = false)
171
166
  add_layout_conditions(conditions)
172
- write_inheritable_attribute "layout", template_name
173
- write_inheritable_attribute "auto_layout", auto
167
+ write_inheritable_attribute(:layout, template_name)
168
+ write_inheritable_attribute(:auto_layout, auto)
174
169
  end
175
170
 
176
171
  def layout_conditions #:nodoc:
177
- @layout_conditions ||= read_inheritable_attribute("layout_conditions")
172
+ @layout_conditions ||= read_inheritable_attribute(:layout_conditions)
178
173
  end
179
174
 
180
175
  def default_layout(format) #:nodoc:
181
- layout = read_inheritable_attribute("layout")
182
- return layout unless read_inheritable_attribute("auto_layout")
176
+ layout = read_inheritable_attribute(:layout)
177
+ return layout unless read_inheritable_attribute(:auto_layout)
183
178
  @default_layout ||= {}
184
179
  @default_layout[format] ||= default_layout_with_format(format, layout)
185
180
  @default_layout[format]
@@ -199,7 +194,7 @@ module ActionController #:nodoc:
199
194
  end
200
195
 
201
196
  def add_layout_conditions(conditions)
202
- write_inheritable_hash "layout_conditions", normalize_conditions(conditions)
197
+ write_inheritable_hash(:layout_conditions, normalize_conditions(conditions))
203
198
  end
204
199
 
205
200
  def normalize_conditions(conditions)
@@ -221,10 +216,10 @@ module ActionController #:nodoc:
221
216
  # object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
222
217
  # weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
223
218
  def active_layout(passed_layout = nil)
224
- layout = passed_layout || self.class.default_layout(response.template.template_format)
219
+ layout = passed_layout || self.class.default_layout(default_template_format)
225
220
  active_layout = case layout
226
221
  when String then layout
227
- when Symbol then send!(layout)
222
+ when Symbol then __send__(layout)
228
223
  when Proc then layout.call(self)
229
224
  end
230
225
 
@@ -240,51 +235,24 @@ module ActionController #:nodoc:
240
235
  end
241
236
  end
242
237
 
243
- protected
244
- def render_with_a_layout(options = nil, extra_options = {}, &block) #:nodoc:
245
- template_with_options = options.is_a?(Hash)
246
-
247
- if (layout = pick_layout(template_with_options, options)) && apply_layout?(template_with_options, options)
248
- options = options.merge :layout => false if template_with_options
249
- logger.info("Rendering template within #{layout}") if logger
250
-
251
- content_for_layout = render_with_no_layout(options, extra_options, &block)
252
- erase_render_results
253
- add_variables_to_assigns
254
- @template.instance_variable_set("@content_for_layout", content_for_layout)
255
- response.layout = layout
256
- status = template_with_options ? options[:status] : nil
257
- render_for_text(@template.render_file(layout, true), status)
258
- else
259
- render_with_no_layout(options, extra_options, &block)
260
- end
261
- end
262
-
263
-
264
238
  private
265
- def apply_layout?(template_with_options, options)
266
- return false if options == :update
267
- template_with_options ? candidate_for_layout?(options) : !template_exempt_from_layout?
268
- end
269
-
270
239
  def candidate_for_layout?(options)
271
- (options.has_key?(:layout) && options[:layout] != false) ||
272
- options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing).compact.empty? &&
273
- !template_exempt_from_layout?(options[:template] || default_template_name(options[:action]))
240
+ options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty? &&
241
+ !@template.__send__(:_exempt_from_layout?, options[:template] || default_template_name(options[:action]))
274
242
  end
275
243
 
276
- def pick_layout(template_with_options, options)
277
- if template_with_options
278
- case layout = options[:layout]
279
- when FalseClass
280
- nil
281
- when NilClass, TrueClass
282
- active_layout if action_has_layout?
283
- else
284
- active_layout(layout)
244
+ def pick_layout(options)
245
+ if options.has_key?(:layout)
246
+ case layout = options.delete(:layout)
247
+ when FalseClass
248
+ nil
249
+ when NilClass, TrueClass
250
+ active_layout if action_has_layout? && !@template.__send__(:_exempt_from_layout?, default_template_name)
251
+ else
252
+ active_layout(layout)
285
253
  end
286
254
  else
287
- active_layout if action_has_layout?
255
+ active_layout if action_has_layout? && candidate_for_layout?(options)
288
256
  end
289
257
  end
290
258
 
@@ -304,7 +272,13 @@ module ActionController #:nodoc:
304
272
  end
305
273
 
306
274
  def layout_directory?(layout_name)
307
- @template.finder.find_template_extension_from_handler(File.join('layouts', layout_name))
275
+ @template.__send__(:_pick_template, "#{File.join('layouts', layout_name)}.#{@template.template_format}") ? true : false
276
+ rescue ActionView::MissingTemplate
277
+ false
278
+ end
279
+
280
+ def default_template_format
281
+ response.template.template_format
308
282
  end
309
283
  end
310
284
  end
@@ -114,7 +114,11 @@ module ActionController #:nodoc:
114
114
  @request = controller.request
115
115
  @response = controller.response
116
116
 
117
- @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
117
+ if ActionController::Base.use_accept_header
118
+ @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
119
+ else
120
+ @mime_type_priority = [@request.format]
121
+ end
118
122
 
119
123
  @order = []
120
124
  @responses = {}
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  module Mime
2
4
  SET = []
3
5
  EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
@@ -18,8 +20,20 @@ module Mime
18
20
  # end
19
21
  class Type
20
22
  @@html_types = Set.new [:html, :all]
23
+ cattr_reader :html_types
24
+
25
+ # These are the content types which browsers can generate without using ajax, flash, etc
26
+ # i.e. following a link, getting an image or posting a form. CSRF protection
27
+ # only needs to protect against these types.
28
+ @@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text]
29
+ cattr_reader :browser_generated_types
30
+
31
+
21
32
  @@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
22
- cattr_reader :html_types, :unverifiable_types
33
+ def self.unverifiable_types
34
+ ActiveSupport::Deprecation.warn("unverifiable_types is deprecated and has no effect", caller)
35
+ @@unverifiable_types
36
+ end
23
37
 
24
38
  # A simple helper class used in parsing the accept header
25
39
  class AcceptItem #:nodoc:
@@ -72,57 +86,61 @@ module Mime
72
86
  end
73
87
 
74
88
  def parse(accept_header)
75
- # keep track of creation order to keep the subsequent sort stable
76
- list = []
77
- accept_header.split(/,/).each_with_index do |header, index|
78
- params, q = header.split(/;\s*q=/)
79
- if params
80
- params.strip!
81
- list << AcceptItem.new(index, params, q) unless params.empty?
89
+ if accept_header !~ /,/
90
+ [Mime::Type.lookup(accept_header)]
91
+ else
92
+ # keep track of creation order to keep the subsequent sort stable
93
+ list = []
94
+ accept_header.split(/,/).each_with_index do |header, index|
95
+ params, q = header.split(/;\s*q=/)
96
+ if params
97
+ params.strip!
98
+ list << AcceptItem.new(index, params, q) unless params.empty?
99
+ end
82
100
  end
83
- end
84
- list.sort!
101
+ list.sort!
85
102
 
86
- # Take care of the broken text/xml entry by renaming or deleting it
87
- text_xml = list.index("text/xml")
88
- app_xml = list.index(Mime::XML.to_s)
103
+ # Take care of the broken text/xml entry by renaming or deleting it
104
+ text_xml = list.index("text/xml")
105
+ app_xml = list.index(Mime::XML.to_s)
89
106
 
90
- if text_xml && app_xml
91
- # set the q value to the max of the two
92
- list[app_xml].q = [list[text_xml].q, list[app_xml].q].max
107
+ if text_xml && app_xml
108
+ # set the q value to the max of the two
109
+ list[app_xml].q = [list[text_xml].q, list[app_xml].q].max
93
110
 
94
- # make sure app_xml is ahead of text_xml in the list
95
- if app_xml > text_xml
96
- list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
97
- app_xml, text_xml = text_xml, app_xml
98
- end
111
+ # make sure app_xml is ahead of text_xml in the list
112
+ if app_xml > text_xml
113
+ list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
114
+ app_xml, text_xml = text_xml, app_xml
115
+ end
99
116
 
100
- # delete text_xml from the list
101
- list.delete_at(text_xml)
117
+ # delete text_xml from the list
118
+ list.delete_at(text_xml)
102
119
 
103
- elsif text_xml
104
- list[text_xml].name = Mime::XML.to_s
105
- end
120
+ elsif text_xml
121
+ list[text_xml].name = Mime::XML.to_s
122
+ end
106
123
 
107
- # Look for more specific XML-based types and sort them ahead of app/xml
124
+ # Look for more specific XML-based types and sort them ahead of app/xml
108
125
 
109
- if app_xml
110
- idx = app_xml
111
- app_xml_type = list[app_xml]
126
+ if app_xml
127
+ idx = app_xml
128
+ app_xml_type = list[app_xml]
112
129
 
113
- while(idx < list.length)
114
- type = list[idx]
115
- break if type.q < app_xml_type.q
116
- if type.name =~ /\+xml$/
117
- list[app_xml], list[idx] = list[idx], list[app_xml]
118
- app_xml = idx
130
+ while(idx < list.length)
131
+ type = list[idx]
132
+ break if type.q < app_xml_type.q
133
+ if type.name =~ /\+xml$/
134
+ list[app_xml], list[idx] = list[idx], list[app_xml]
135
+ app_xml = idx
136
+ end
137
+ idx += 1
119
138
  end
120
- idx += 1
121
139
  end
122
- end
123
140
 
124
- list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
125
- list
141
+ list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
142
+ list
143
+ end
126
144
  end
127
145
  end
128
146
 
@@ -159,15 +177,19 @@ module Mime
159
177
  end
160
178
 
161
179
  # Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
162
- # ActionController::RequestForgerProtection.
180
+ # ActionController::RequestForgeryProtection.
163
181
  def verify_request?
164
- !@@unverifiable_types.include?(to_sym)
182
+ browser_generated?
165
183
  end
166
184
 
167
185
  def html?
168
186
  @@html_types.include?(to_sym) || @string =~ /html/
169
187
  end
170
188
 
189
+ def browser_generated?
190
+ @@browser_generated_types.include?(to_sym)
191
+ end
192
+
171
193
  private
172
194
  def method_missing(method, *args)
173
195
  if method.to_s =~ /(\w+)\?$/
@@ -17,4 +17,5 @@ Mime::Type.register "multipart/form-data", :multipart_form
17
17
  Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
18
18
 
19
19
  # http://www.ietf.org/rfc/rfc4627.txt
20
- Mime::Type.register "application/json", :json, %w( text/x-json )
20
+ # http://www.json.org/JSONRequest.html
21
+ Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
@@ -0,0 +1,16 @@
1
+ require 'action_controller/integration'
2
+ require 'active_support/testing/performance'
3
+ require 'active_support/testing/default'
4
+
5
+ module ActionController
6
+ # An integration test that runs a code profiler on your test methods.
7
+ # Profiling output for combinations of each test method, measurement, and
8
+ # output format are written to your tmp/performance directory.
9
+ #
10
+ # By default, process_time is measured and both flat and graph_html output
11
+ # formats are written, so you'll have two output files per test method.
12
+ class PerformanceTest < ActionController::IntegrationTest
13
+ include ActiveSupport::Testing::Performance
14
+ include ActiveSupport::Testing::Default
15
+ end
16
+ end
@@ -73,7 +73,8 @@ module ActionController
73
73
  #
74
74
  def polymorphic_url(record_or_hash_or_array, options = {})
75
75
  if record_or_hash_or_array.kind_of?(Array)
76
- record_or_hash_or_array = record_or_hash_or_array.dup
76
+ record_or_hash_or_array = record_or_hash_or_array.compact
77
+ record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
77
78
  end
78
79
 
79
80
  record = extract_record(record_or_hash_or_array)
@@ -102,7 +103,13 @@ module ActionController
102
103
  args << format if format
103
104
 
104
105
  named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
105
- send!(named_route, *args)
106
+
107
+ url_options = options.except(:action, :routing_type, :format)
108
+ unless url_options.empty?
109
+ args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
110
+ end
111
+
112
+ __send__(named_route, *args)
106
113
  end
107
114
 
108
115
  # Returns the path component of a URL for the given record. It uses
@@ -114,19 +121,19 @@ module ActionController
114
121
 
115
122
  %w(edit new formatted).each do |action|
116
123
  module_eval <<-EOT, __FILE__, __LINE__
117
- def #{action}_polymorphic_url(record_or_hash)
118
- polymorphic_url(record_or_hash, :action => "#{action}")
124
+ def #{action}_polymorphic_url(record_or_hash, options = {})
125
+ polymorphic_url(record_or_hash, options.merge(:action => "#{action}"))
119
126
  end
120
127
 
121
- def #{action}_polymorphic_path(record_or_hash)
122
- polymorphic_url(record_or_hash, :action => "#{action}", :routing_type => :path)
128
+ def #{action}_polymorphic_path(record_or_hash, options = {})
129
+ polymorphic_url(record_or_hash, options.merge(:action => "#{action}", :routing_type => :path))
123
130
  end
124
131
  EOT
125
132
  end
126
133
 
127
134
  private
128
135
  def action_prefix(options)
129
- options[:action] ? "#{options[:action]}_" : ""
136
+ options[:action] ? "#{options[:action]}_" : options[:format] ? "formatted_" : ""
130
137
  end
131
138
 
132
139
  def routing_type(options)
@@ -143,7 +150,7 @@ module ActionController
143
150
  if parent.is_a?(Symbol) || parent.is_a?(String)
144
151
  string << "#{parent}_"
145
152
  else
146
- string << "#{RecordIdentifier.send!("singular_class_name", parent)}_"
153
+ string << "#{RecordIdentifier.__send__("singular_class_name", parent)}_"
147
154
  end
148
155
  end
149
156
  end
@@ -151,7 +158,7 @@ module ActionController
151
158
  if record.is_a?(Symbol) || record.is_a?(String)
152
159
  route << "#{record}_"
153
160
  else
154
- route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_"
161
+ route << "#{RecordIdentifier.__send__("#{inflection}_class_name", record)}_"
155
162
  end
156
163
 
157
164
  action_prefix(options) + namespace + route + routing_type(options).to_s
@@ -0,0 +1,303 @@
1
+ require 'action_controller/cgi_ext'
2
+ require 'action_controller/session/cookie_store'
3
+
4
+ module ActionController #:nodoc:
5
+ class RackRequest < AbstractRequest #:nodoc:
6
+ attr_accessor :session_options
7
+ attr_reader :cgi
8
+
9
+ class SessionFixationAttempt < StandardError #:nodoc:
10
+ end
11
+
12
+ DEFAULT_SESSION_OPTIONS = {
13
+ :database_manager => CGI::Session::CookieStore, # store data in cookie
14
+ :prefix => "ruby_sess.", # prefix session file names
15
+ :session_path => "/", # available to all paths in app
16
+ :session_key => "_session_id",
17
+ :cookie_only => true,
18
+ :session_http_only=> true
19
+ }
20
+
21
+ def initialize(env, session_options = DEFAULT_SESSION_OPTIONS)
22
+ @session_options = session_options
23
+ @env = env
24
+ @cgi = CGIWrapper.new(self)
25
+ super()
26
+ end
27
+
28
+ %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_INFO
29
+ PATH_TRANSLATED REMOTE_HOST
30
+ REMOTE_IDENT REMOTE_USER SCRIPT_NAME
31
+ SERVER_NAME SERVER_PROTOCOL
32
+
33
+ HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
34
+ HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
35
+ HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
36
+ define_method(env.sub(/^HTTP_/n, '').downcase) do
37
+ @env[env]
38
+ end
39
+ end
40
+
41
+ def query_string
42
+ qs = super
43
+ if !qs.blank?
44
+ qs
45
+ else
46
+ @env['QUERY_STRING']
47
+ end
48
+ end
49
+
50
+ def body_stream #:nodoc:
51
+ @env['rack.input']
52
+ end
53
+
54
+ def key?(key)
55
+ @env.key?(key)
56
+ end
57
+
58
+ def cookies
59
+ return {} unless @env["HTTP_COOKIE"]
60
+
61
+ unless @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"]
62
+ @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"]
63
+ @env["rack.request.cookie_hash"] = CGI::Cookie::parse(@env["rack.request.cookie_string"])
64
+ end
65
+
66
+ @env["rack.request.cookie_hash"]
67
+ end
68
+
69
+ def server_port
70
+ @env['SERVER_PORT'].to_i
71
+ end
72
+
73
+ def server_software
74
+ @env['SERVER_SOFTWARE'].split("/").first
75
+ end
76
+
77
+ def session
78
+ unless defined?(@session)
79
+ if @session_options == false
80
+ @session = Hash.new
81
+ else
82
+ stale_session_check! do
83
+ if cookie_only? && query_parameters[session_options_with_string_keys['session_key']]
84
+ raise SessionFixationAttempt
85
+ end
86
+ case value = session_options_with_string_keys['new_session']
87
+ when true
88
+ @session = new_session
89
+ when false
90
+ begin
91
+ @session = CGI::Session.new(@cgi, session_options_with_string_keys)
92
+ # CGI::Session raises ArgumentError if 'new_session' == false
93
+ # and no session cookie or query param is present.
94
+ rescue ArgumentError
95
+ @session = Hash.new
96
+ end
97
+ when nil
98
+ @session = CGI::Session.new(@cgi, session_options_with_string_keys)
99
+ else
100
+ raise ArgumentError, "Invalid new_session option: #{value}"
101
+ end
102
+ @session['__valid_session']
103
+ end
104
+ end
105
+ end
106
+ @session
107
+ end
108
+
109
+ def reset_session
110
+ @session.delete if defined?(@session) && @session.is_a?(CGI::Session)
111
+ @session = new_session
112
+ end
113
+
114
+ private
115
+ # Delete an old session if it exists then create a new one.
116
+ def new_session
117
+ if @session_options == false
118
+ Hash.new
119
+ else
120
+ CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
121
+ CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
122
+ end
123
+ end
124
+
125
+ def cookie_only?
126
+ session_options_with_string_keys['cookie_only']
127
+ end
128
+
129
+ def stale_session_check!
130
+ yield
131
+ rescue ArgumentError => argument_error
132
+ if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
133
+ begin
134
+ # Note that the regexp does not allow $1 to end with a ':'
135
+ $1.constantize
136
+ rescue LoadError, NameError => const_error
137
+ raise ActionController::SessionRestoreError, <<-end_msg
138
+ Session contains objects whose class definition isn\'t available.
139
+ Remember to require the classes for all objects kept in the session.
140
+ (Original exception: #{const_error.message} [#{const_error.class}])
141
+ end_msg
142
+ end
143
+
144
+ retry
145
+ else
146
+ raise
147
+ end
148
+ end
149
+
150
+ def session_options_with_string_keys
151
+ @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys
152
+ end
153
+ end
154
+
155
+ class RackResponse < AbstractResponse #:nodoc:
156
+ def initialize(request)
157
+ @cgi = request.cgi
158
+ @writer = lambda { |x| @body << x }
159
+ @block = nil
160
+ super()
161
+ end
162
+
163
+ # Retrieve status from instance variable if has already been delete
164
+ def status
165
+ @status || super
166
+ end
167
+
168
+ def out(output = $stdout, &block)
169
+ # Nasty hack because CGI sessions are closed after the normal
170
+ # prepare! statement
171
+ set_cookies!
172
+
173
+ @block = block
174
+ @status = headers.delete("Status")
175
+ if [204, 304].include?(status.to_i)
176
+ headers.delete("Content-Type")
177
+ [status, headers.to_hash, []]
178
+ else
179
+ [status, headers.to_hash, self]
180
+ end
181
+ end
182
+ alias to_a out
183
+
184
+ def each(&callback)
185
+ if @body.respond_to?(:call)
186
+ @writer = lambda { |x| callback.call(x) }
187
+ @body.call(self, self)
188
+ elsif @body.is_a?(String)
189
+ @body.each_line(&callback)
190
+ else
191
+ @body.each(&callback)
192
+ end
193
+
194
+ @writer = callback
195
+ @block.call(self) if @block
196
+ end
197
+
198
+ def write(str)
199
+ @writer.call str.to_s
200
+ str
201
+ end
202
+
203
+ def close
204
+ @body.close if @body.respond_to?(:close)
205
+ end
206
+
207
+ def empty?
208
+ @block == nil && @body.empty?
209
+ end
210
+
211
+ def prepare!
212
+ super
213
+
214
+ convert_language!
215
+ convert_expires!
216
+ set_status!
217
+ # set_cookies!
218
+ end
219
+
220
+ private
221
+ def convert_language!
222
+ headers["Content-Language"] = headers.delete("language") if headers["language"]
223
+ end
224
+
225
+ def convert_expires!
226
+ headers["Expires"] = headers.delete("") if headers["expires"]
227
+ end
228
+
229
+ def convert_content_type!
230
+ super
231
+ headers['Content-Type'] = headers.delete('type') || "text/html"
232
+ headers['Content-Type'] += "; charset=" + headers.delete('charset') if headers['charset']
233
+ end
234
+
235
+ def set_content_length!
236
+ super
237
+ headers["Content-Length"] = headers["Content-Length"].to_s if headers["Content-Length"]
238
+ end
239
+
240
+ def set_status!
241
+ self.status ||= "200 OK"
242
+ end
243
+
244
+ def set_cookies!
245
+ # Convert 'cookie' header to 'Set-Cookie' headers.
246
+ # Because Set-Cookie header can appear more the once in the response body,
247
+ # we store it in a line break separated string that will be translated to
248
+ # multiple Set-Cookie header by the handler.
249
+ if cookie = headers.delete('cookie')
250
+ cookies = []
251
+
252
+ case cookie
253
+ when Array then cookie.each { |c| cookies << c.to_s }
254
+ when Hash then cookie.each { |_, c| cookies << c.to_s }
255
+ else cookies << cookie.to_s
256
+ end
257
+
258
+ @cgi.output_cookies.each { |c| cookies << c.to_s } if @cgi.output_cookies
259
+
260
+ headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].flatten.compact
261
+ end
262
+ end
263
+ end
264
+
265
+ class CGIWrapper < ::CGI
266
+ attr_reader :output_cookies
267
+
268
+ def initialize(request, *args)
269
+ @request = request
270
+ @args = *args
271
+ @input = request.body
272
+
273
+ super *args
274
+ end
275
+
276
+ def params
277
+ @params ||= @request.params
278
+ end
279
+
280
+ def cookies
281
+ @request.cookies
282
+ end
283
+
284
+ def query_string
285
+ @request.query_string
286
+ end
287
+
288
+ # Used to wrap the normal args variable used inside CGI.
289
+ def args
290
+ @args
291
+ end
292
+
293
+ # Used to wrap the normal env_table variable used inside CGI.
294
+ def env_table
295
+ @request.env
296
+ end
297
+
298
+ # Used to wrap the normal stdinput variable used inside CGI.
299
+ def stdinput
300
+ @input
301
+ end
302
+ end
303
+ end