actionpack 1.13.6 → 2.0.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 (317) hide show
  1. data/CHANGELOG +1400 -20
  2. data/MIT-LICENSE +1 -1
  3. data/README +5 -5
  4. data/RUNNING_UNIT_TESTS +4 -5
  5. data/Rakefile +5 -6
  6. data/install.rb +2 -2
  7. data/lib/action_controller.rb +11 -15
  8. data/lib/action_controller/assertions.rb +12 -25
  9. data/lib/action_controller/assertions/dom_assertions.rb +18 -4
  10. data/lib/action_controller/assertions/model_assertions.rb +8 -1
  11. data/lib/action_controller/assertions/response_assertions.rb +35 -12
  12. data/lib/action_controller/assertions/routing_assertions.rb +56 -12
  13. data/lib/action_controller/assertions/selector_assertions.rb +105 -38
  14. data/lib/action_controller/assertions/tag_assertions.rb +28 -15
  15. data/lib/action_controller/base.rb +318 -250
  16. data/lib/action_controller/benchmarking.rb +33 -29
  17. data/lib/action_controller/caching.rb +130 -64
  18. data/lib/action_controller/cgi_ext.rb +16 -0
  19. data/lib/action_controller/cgi_ext/{cookie_performance_fix.rb → cookie.rb} +25 -40
  20. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  21. data/lib/action_controller/cgi_ext/session.rb +73 -0
  22. data/lib/action_controller/cgi_ext/stdinput.rb +23 -0
  23. data/lib/action_controller/cgi_process.rb +34 -57
  24. data/lib/action_controller/components.rb +19 -36
  25. data/lib/action_controller/cookies.rb +10 -9
  26. data/lib/action_controller/dispatcher.rb +195 -0
  27. data/lib/action_controller/filters.rb +35 -34
  28. data/lib/action_controller/flash.rb +30 -35
  29. data/lib/action_controller/helpers.rb +121 -47
  30. data/lib/action_controller/http_authentication.rb +126 -0
  31. data/lib/action_controller/integration.rb +105 -101
  32. data/lib/action_controller/layout.rb +59 -47
  33. data/lib/action_controller/mime_responds.rb +57 -68
  34. data/lib/action_controller/mime_type.rb +43 -80
  35. data/lib/action_controller/mime_types.rb +20 -0
  36. data/lib/action_controller/polymorphic_routes.rb +88 -0
  37. data/lib/action_controller/record_identifier.rb +91 -0
  38. data/lib/action_controller/request.rb +553 -88
  39. data/lib/action_controller/request_forgery_protection.rb +126 -0
  40. data/lib/action_controller/request_profiler.rb +138 -0
  41. data/lib/action_controller/rescue.rb +185 -69
  42. data/lib/action_controller/resources.rb +211 -172
  43. data/lib/action_controller/response.rb +49 -8
  44. data/lib/action_controller/routing.rb +359 -236
  45. data/lib/action_controller/routing_optimisation.rb +119 -0
  46. data/lib/action_controller/session/active_record_store.rb +3 -2
  47. data/lib/action_controller/session/cookie_store.rb +161 -0
  48. data/lib/action_controller/session/mem_cache_store.rb +9 -16
  49. data/lib/action_controller/session_management.rb +17 -8
  50. data/lib/action_controller/streaming.rb +6 -3
  51. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  52. data/lib/action_controller/templates/rescues/{_trace.rhtml → _trace.erb} +0 -0
  53. data/lib/action_controller/templates/rescues/{diagnostics.rhtml → diagnostics.erb} +2 -2
  54. data/lib/action_controller/templates/rescues/{layout.rhtml → layout.erb} +0 -0
  55. data/lib/action_controller/templates/rescues/{missing_template.rhtml → missing_template.erb} +0 -0
  56. data/lib/action_controller/templates/rescues/{routing_error.rhtml → routing_error.erb} +0 -0
  57. data/lib/action_controller/templates/rescues/{template_error.rhtml → template_error.erb} +2 -2
  58. data/lib/action_controller/templates/rescues/{unknown_action.rhtml → unknown_action.erb} +0 -0
  59. data/lib/action_controller/test_case.rb +53 -0
  60. data/lib/action_controller/test_process.rb +59 -46
  61. data/lib/action_controller/url_rewriter.rb +48 -24
  62. data/lib/action_controller/vendor/html-scanner/html/document.rb +7 -4
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +11 -6
  65. data/lib/action_controller/verification.rb +27 -21
  66. data/lib/action_pack.rb +1 -1
  67. data/lib/action_pack/version.rb +4 -4
  68. data/lib/action_view.rb +2 -3
  69. data/lib/action_view/base.rb +218 -63
  70. data/lib/action_view/compiled_templates.rb +1 -2
  71. data/lib/action_view/helpers/active_record_helper.rb +35 -17
  72. data/lib/action_view/helpers/asset_tag_helper.rb +395 -87
  73. data/lib/action_view/helpers/atom_feed_helper.rb +111 -0
  74. data/lib/action_view/helpers/benchmark_helper.rb +12 -5
  75. data/lib/action_view/helpers/cache_helper.rb +29 -0
  76. data/lib/action_view/helpers/capture_helper.rb +97 -63
  77. data/lib/action_view/helpers/date_helper.rb +295 -35
  78. data/lib/action_view/helpers/debug_helper.rb +6 -2
  79. data/lib/action_view/helpers/form_helper.rb +354 -111
  80. data/lib/action_view/helpers/form_options_helper.rb +171 -109
  81. data/lib/action_view/helpers/form_tag_helper.rb +332 -76
  82. data/lib/action_view/helpers/javascript_helper.rb +35 -11
  83. data/lib/action_view/helpers/javascripts/controls.js +484 -354
  84. data/lib/action_view/helpers/javascripts/dragdrop.js +88 -58
  85. data/lib/action_view/helpers/javascripts/effects.js +396 -364
  86. data/lib/action_view/helpers/javascripts/prototype.js +2817 -1107
  87. data/lib/action_view/helpers/number_helper.rb +84 -60
  88. data/lib/action_view/helpers/prototype_helper.rb +419 -43
  89. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  90. data/lib/action_view/helpers/record_tag_helper.rb +59 -0
  91. data/lib/action_view/helpers/sanitize_helper.rb +223 -0
  92. data/lib/action_view/helpers/scriptaculous_helper.rb +63 -4
  93. data/lib/action_view/helpers/tag_helper.rb +69 -39
  94. data/lib/action_view/helpers/text_helper.rb +221 -148
  95. data/lib/action_view/helpers/url_helper.rb +283 -165
  96. data/lib/action_view/partials.rb +134 -62
  97. data/lib/action_view/template_error.rb +4 -12
  98. data/lib/actionpack.rb +1 -0
  99. data/test/abstract_unit.rb +21 -1
  100. data/test/action_view_test.rb +26 -0
  101. data/test/active_record_unit.rb +12 -20
  102. data/test/activerecord/active_record_store_test.rb +2 -2
  103. data/test/activerecord/render_partial_with_record_identification_test.rb +74 -0
  104. data/test/controller/action_pack_assertions_test.rb +21 -152
  105. data/test/controller/addresses_render_test.rb +2 -7
  106. data/test/controller/assert_select_test.rb +120 -14
  107. data/test/controller/base_test.rb +11 -13
  108. data/test/controller/caching_test.rb +125 -5
  109. data/test/controller/capture_test.rb +23 -16
  110. data/test/controller/cgi_test.rb +66 -391
  111. data/test/controller/components_test.rb +31 -42
  112. data/test/controller/content_type_test.rb +1 -1
  113. data/test/controller/cookie_test.rb +42 -14
  114. data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -42
  115. data/test/controller/dispatcher_test.rb +123 -0
  116. data/test/controller/fake_models.rb +5 -0
  117. data/test/controller/filters_test.rb +44 -7
  118. data/test/controller/flash_test.rb +46 -2
  119. data/test/controller/fragment_store_setting_test.rb +10 -8
  120. data/test/controller/helper_test.rb +19 -2
  121. data/test/controller/html-scanner/document_test.rb +124 -0
  122. data/test/controller/html-scanner/node_test.rb +69 -0
  123. data/test/controller/html-scanner/sanitizer_test.rb +250 -0
  124. data/test/controller/html-scanner/tag_node_test.rb +239 -0
  125. data/test/controller/html-scanner/text_node_test.rb +51 -0
  126. data/test/controller/html-scanner/tokenizer_test.rb +125 -0
  127. data/test/controller/http_authentication_test.rb +54 -0
  128. data/test/controller/integration_test.rb +12 -26
  129. data/test/controller/layout_test.rb +64 -12
  130. data/test/controller/mime_responds_test.rb +193 -38
  131. data/test/controller/mime_type_test.rb +30 -8
  132. data/test/controller/new_render_test.rb +104 -22
  133. data/test/controller/polymorphic_routes_test.rb +98 -0
  134. data/test/controller/record_identifier_test.rb +103 -0
  135. data/test/controller/redirect_test.rb +120 -18
  136. data/test/controller/render_test.rb +195 -45
  137. data/test/controller/request_forgery_protection_test.rb +217 -0
  138. data/test/controller/request_test.rb +545 -27
  139. data/test/controller/rescue_test.rb +501 -0
  140. data/test/controller/resources_test.rb +258 -132
  141. data/test/controller/routing_test.rb +502 -106
  142. data/test/controller/selector_test.rb +5 -5
  143. data/test/controller/send_file_test.rb +17 -7
  144. data/test/controller/session/cookie_store_test.rb +246 -0
  145. data/test/controller/session/mem_cache_store_test.rb +182 -0
  146. data/test/controller/session_fixation_test.rb +8 -11
  147. data/test/controller/session_management_test.rb +7 -7
  148. data/test/controller/test_test.rb +150 -38
  149. data/test/controller/url_rewriter_test.rb +87 -12
  150. data/test/controller/verification_test.rb +11 -0
  151. data/test/controller/view_paths_test.rb +137 -0
  152. data/test/controller/webservice_test.rb +11 -75
  153. data/test/fixtures/addresses/{list.rhtml → list.erb} +0 -0
  154. data/test/fixtures/db_definitions/sqlite.sql +2 -1
  155. data/test/fixtures/developer.rb +2 -0
  156. data/test/fixtures/fun/games/{hello_world.rhtml → hello_world.erb} +0 -0
  157. data/test/fixtures/helpers/fun/pdf_helper.rb +1 -1
  158. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  159. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  160. data/test/fixtures/layouts/{builder.rxml → builder.builder} +0 -0
  161. data/test/fixtures/layouts/{standard.rhtml → standard.erb} +0 -0
  162. data/test/fixtures/layouts/{talk_from_action.rhtml → talk_from_action.erb} +0 -0
  163. data/test/fixtures/layouts/{yield.rhtml → yield.erb} +0 -0
  164. data/test/fixtures/multipart/binary_file +0 -0
  165. data/test/fixtures/multipart/bracketed_param +5 -0
  166. data/test/fixtures/override/test/hello_world.erb +1 -0
  167. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  168. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  169. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  170. data/test/fixtures/post_test/post/index.html.erb +1 -0
  171. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  172. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  173. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  174. data/test/fixtures/public/404.html +1 -0
  175. data/test/fixtures/public/500.html +1 -0
  176. data/test/fixtures/public/javascripts/application.js +0 -1
  177. data/test/fixtures/public/javascripts/bank.js +1 -0
  178. data/test/fixtures/public/javascripts/robber.js +1 -0
  179. data/test/fixtures/public/stylesheets/bank.css +1 -0
  180. data/test/fixtures/public/stylesheets/robber.css +1 -0
  181. data/test/fixtures/replies.yml +2 -0
  182. data/test/fixtures/reply.rb +2 -1
  183. data/test/fixtures/respond_to/{all_types_with_layout.rhtml → all_types_with_layout.html.erb} +0 -0
  184. data/test/fixtures/respond_to/{all_types_with_layout.rjs → all_types_with_layout.js.rjs} +0 -0
  185. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  186. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  187. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  188. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  189. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  190. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  191. data/test/fixtures/respond_to/{using_defaults.rhtml → using_defaults.html.erb} +0 -0
  192. data/test/fixtures/respond_to/{using_defaults.rjs → using_defaults.js.rjs} +0 -0
  193. data/test/fixtures/respond_to/{using_defaults.rxml → using_defaults.xml.builder} +0 -0
  194. data/test/fixtures/respond_to/{using_defaults_with_type_list.rhtml → using_defaults_with_type_list.html.erb} +0 -0
  195. data/test/fixtures/respond_to/{using_defaults_with_type_list.rjs → using_defaults_with_type_list.js.rjs} +0 -0
  196. data/test/fixtures/respond_to/{using_defaults_with_type_list.rxml → using_defaults_with_type_list.xml.builder} +0 -0
  197. data/test/fixtures/scope/test/{modgreet.rhtml → modgreet.erb} +0 -0
  198. data/test/fixtures/test/{_customer.rhtml → _customer.erb} +0 -0
  199. data/test/fixtures/test/{_customer_greeting.rhtml → _customer_greeting.erb} +0 -0
  200. data/test/fixtures/test/_hash_greeting.erb +1 -0
  201. data/test/fixtures/test/_hash_object.erb +2 -0
  202. data/test/fixtures/test/{_hello.rxml → _hello.builder} +0 -0
  203. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  204. data/test/fixtures/test/_partial.erb +1 -0
  205. data/test/fixtures/test/_partial.html.erb +1 -0
  206. data/test/fixtures/test/_partial.js.erb +1 -0
  207. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  208. data/test/fixtures/test/{_partial_only.rhtml → _partial_only.erb} +0 -0
  209. data/test/fixtures/test/{_person.rhtml → _person.erb} +0 -0
  210. data/test/fixtures/test/{action_talk_to_layout.rhtml → action_talk_to_layout.erb} +0 -0
  211. data/test/fixtures/test/{block_content_for.rhtml → block_content_for.erb} +0 -0
  212. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  213. data/test/fixtures/test/{capturing.rhtml → capturing.erb} +0 -0
  214. data/test/fixtures/test/{content_for.rhtml → content_for.erb} +0 -0
  215. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  216. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  217. data/test/fixtures/test/dot.directory/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
  218. data/test/fixtures/test/{erb_content_for.rhtml → erb_content_for.erb} +0 -0
  219. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  220. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  221. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  222. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  223. data/test/fixtures/test/{greeting.rhtml → greeting.erb} +0 -0
  224. data/test/fixtures/test/{hello.rxml → hello.builder} +0 -0
  225. data/test/fixtures/test/{hello_world.rxml → hello_world.builder} +0 -0
  226. data/test/fixtures/test/{hello_world.rhtml → hello_world.erb} +0 -0
  227. data/test/fixtures/test/{hello_world_container.rxml → hello_world_container.builder} +0 -0
  228. data/test/fixtures/test/{hello_world_with_layout_false.rhtml → hello_world_with_layout_false.erb} +0 -0
  229. data/test/fixtures/test/{hello_xml_world.rxml → hello_xml_world.builder} +0 -0
  230. data/test/fixtures/test/list.erb +1 -0
  231. data/test/fixtures/test/{non_erb_block_content_for.rxml → non_erb_block_content_for.builder} +0 -0
  232. data/test/fixtures/test/{potential_conflicts.rhtml → potential_conflicts.erb} +0 -0
  233. data/test/fixtures/test/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
  234. data/test/fixtures/test/{render_file_with_locals.rhtml → render_file_with_locals.erb} +0 -0
  235. data/test/fixtures/test/{render_to_string_test.rhtml → render_to_string_test.erb} +0 -0
  236. data/test/fixtures/test/{update_element_with_capture.rhtml → update_element_with_capture.erb} +0 -0
  237. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  238. data/test/fixtures/topic.rb +1 -1
  239. data/test/template/active_record_helper_test.rb +67 -20
  240. data/test/template/asset_tag_helper_test.rb +222 -54
  241. data/test/template/atom_feed_helper_test.rb +101 -0
  242. data/test/template/benchmark_helper_test.rb +2 -2
  243. data/test/template/compiled_templates_test.rb +76 -32
  244. data/test/template/date_helper_test.rb +125 -9
  245. data/test/template/form_helper_test.rb +326 -33
  246. data/test/template/form_options_helper_test.rb +822 -15
  247. data/test/template/form_tag_helper_test.rb +96 -30
  248. data/test/template/javascript_helper_test.rb +61 -13
  249. data/test/template/number_helper_test.rb +12 -11
  250. data/test/template/prototype_helper_test.rb +185 -24
  251. data/test/template/sanitize_helper_test.rb +49 -0
  252. data/test/template/scriptaculous_helper_test.rb +9 -3
  253. data/test/template/tag_helper_test.rb +13 -2
  254. data/test/template/text_helper_test.rb +38 -52
  255. data/test/template/url_helper_test.rb +216 -46
  256. metadata +144 -116
  257. data/examples/.htaccess +0 -24
  258. data/examples/address_book/index.rhtml +0 -33
  259. data/examples/address_book/layout.rhtml +0 -8
  260. data/examples/address_book_controller.cgi +0 -9
  261. data/examples/address_book_controller.fcgi +0 -6
  262. data/examples/address_book_controller.rb +0 -52
  263. data/examples/address_book_controller.rbx +0 -4
  264. data/examples/benchmark.rb +0 -52
  265. data/examples/benchmark_with_ar.fcgi +0 -89
  266. data/examples/blog_controller.cgi +0 -53
  267. data/examples/debate/index.rhtml +0 -14
  268. data/examples/debate/new_topic.rhtml +0 -22
  269. data/examples/debate/topic.rhtml +0 -32
  270. data/examples/debate_controller.cgi +0 -57
  271. data/lib/action_controller/assertions/deprecated_assertions.rb +0 -228
  272. data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -36
  273. data/lib/action_controller/cgi_ext/cgi_methods.rb +0 -211
  274. data/lib/action_controller/cgi_ext/pstore_performance_fix.rb +0 -30
  275. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +0 -95
  276. data/lib/action_controller/cgi_ext/session_performance_fix.rb +0 -30
  277. data/lib/action_controller/deprecated_dependencies.rb +0 -65
  278. data/lib/action_controller/deprecated_redirects.rb +0 -17
  279. data/lib/action_controller/deprecated_request_methods.rb +0 -34
  280. data/lib/action_controller/macros/auto_complete.rb +0 -53
  281. data/lib/action_controller/macros/in_place_editing.rb +0 -33
  282. data/lib/action_controller/pagination.rb +0 -408
  283. data/lib/action_controller/scaffolding.rb +0 -189
  284. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -44
  285. data/lib/action_controller/templates/scaffolds/edit.rhtml +0 -7
  286. data/lib/action_controller/templates/scaffolds/layout.rhtml +0 -69
  287. data/lib/action_controller/templates/scaffolds/list.rhtml +0 -27
  288. data/lib/action_controller/templates/scaffolds/new.rhtml +0 -6
  289. data/lib/action_controller/templates/scaffolds/show.rhtml +0 -9
  290. data/lib/action_controller/vendor/xml_node.rb +0 -97
  291. data/lib/action_view/helpers/deprecated_helper.rb +0 -37
  292. data/lib/action_view/helpers/java_script_macros_helper.rb +0 -233
  293. data/lib/action_view/helpers/pagination_helper.rb +0 -86
  294. data/test/activerecord/active_record_assertions_test.rb +0 -92
  295. data/test/activerecord/pagination_test.rb +0 -165
  296. data/test/controller/deprecated_instance_variables_test.rb +0 -48
  297. data/test/controller/raw_post_test.rb +0 -68
  298. data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +0 -1
  299. data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +0 -1
  300. data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +0 -1
  301. data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +0 -1
  302. data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +0 -1
  303. data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +0 -1
  304. data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +0 -1
  305. data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +0 -1
  306. data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +0 -1
  307. data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +0 -1
  308. data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +0 -1
  309. data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +0 -1
  310. data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +0 -1
  311. data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +0 -1
  312. data/test/fixtures/respond_to/layouts/standard.rhtml +0 -1
  313. data/test/fixtures/test/_hash_object.rhtml +0 -1
  314. data/test/fixtures/test/list.rhtml +0 -1
  315. data/test/template/deprecated_helper_test.rb +0 -36
  316. data/test/template/deprecated_instance_variables_test.rb +0 -43
  317. data/test/template/java_script_macros_helper_test.rb +0 -109
@@ -40,40 +40,44 @@ module ActionController #:nodoc:
40
40
  end
41
41
  end
42
42
 
43
- def render_with_benchmark(options = nil, deprecated_status = nil, &block)
44
- unless logger
45
- render_without_benchmark(options, deprecated_status, &block)
46
- else
47
- db_runtime = ActiveRecord::Base.connection.reset_runtime if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
43
+ protected
44
+ def render_with_benchmark(options = nil, deprecated_status = nil, &block)
45
+ unless logger
46
+ render_without_benchmark(options, &block)
47
+ else
48
+ db_runtime = ActiveRecord::Base.connection.reset_runtime if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
49
+
50
+ render_output = nil
51
+ @rendering_runtime = Benchmark::measure{ render_output = render_without_benchmark(options, &block) }.real
48
52
 
49
- render_output = nil
50
- @rendering_runtime = Benchmark::measure{ render_output = render_without_benchmark(options, deprecated_status, &block) }.real
53
+ if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
54
+ @db_rt_before_render = db_runtime
55
+ @db_rt_after_render = ActiveRecord::Base.connection.reset_runtime
56
+ @rendering_runtime -= @db_rt_after_render
57
+ end
51
58
 
52
- if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
53
- @db_rt_before_render = db_runtime
54
- @db_rt_after_render = ActiveRecord::Base.connection.reset_runtime
55
- @rendering_runtime -= @db_rt_after_render
59
+ render_output
56
60
  end
61
+ end
57
62
 
58
- render_output
59
- end
60
- end
63
+ private
64
+ def perform_action_with_benchmark
65
+ unless logger
66
+ perform_action_without_benchmark
67
+ else
68
+ runtime = [ Benchmark::measure{ perform_action_without_benchmark }.real, 0.0001 ].max
61
69
 
62
- def perform_action_with_benchmark
63
- unless logger
64
- perform_action_without_benchmark
65
- else
66
- runtime = [Benchmark::measure{ perform_action_without_benchmark }.real, 0.0001].max
67
- log_message = "Completed in #{sprintf("%.5f", runtime)} (#{(1 / runtime).floor} reqs/sec)"
68
- log_message << rendering_runtime(runtime) if defined?(@rendering_runtime)
69
- log_message << active_record_runtime(runtime) if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
70
- log_message << " | #{headers["Status"]}"
71
- log_message << " [#{complete_request_uri rescue "unknown"}]"
72
- logger.info(log_message)
70
+ log_message = "Completed in #{sprintf("%.5f", runtime)} (#{(1 / runtime).floor} reqs/sec)"
71
+ log_message << rendering_runtime(runtime) if defined?(@rendering_runtime)
72
+ log_message << active_record_runtime(runtime) if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
73
+ log_message << " | #{headers["Status"]}"
74
+ log_message << " [#{complete_request_uri rescue "unknown"}]"
75
+
76
+ logger.info(log_message)
77
+ response.headers["X-Runtime"] = sprintf("%.5f", runtime)
78
+ end
73
79
  end
74
- end
75
-
76
- private
80
+
77
81
  def rendering_runtime(runtime)
78
82
  " | Rendering: #{sprintf("%.5f", @rendering_runtime)} (#{sprintf("%d", (@rendering_runtime * 100) / runtime)}%)"
79
83
  end
@@ -86,4 +90,4 @@ module ActionController #:nodoc:
86
90
  " | DB: #{sprintf("%.5f", db_runtime)} (#{sprintf("%d", db_percentage)}%)"
87
91
  end
88
92
  end
89
- end
93
+ end
@@ -11,9 +11,13 @@ module ActionController #:nodoc:
11
11
  # Note: To turn off all caching and sweeping, set Base.perform_caching = false.
12
12
  module Caching
13
13
  def self.included(base) #:nodoc:
14
- base.send(:include, Pages, Actions, Fragments, Sweeping)
15
-
16
14
  base.class_eval do
15
+ include Pages, Actions, Fragments
16
+
17
+ if defined? ActiveRecord
18
+ include Sweeping, SqlCache
19
+ end
20
+
17
21
  @@perform_caching = true
18
22
  cattr_accessor :perform_caching
19
23
  end
@@ -96,14 +100,13 @@ module ActionController #:nodoc:
96
100
  # matches the triggering url.
97
101
  def caches_page(*actions)
98
102
  return unless perform_caching
99
- actions.each do |action|
100
- class_eval "after_filter { |c| c.cache_page if c.action_name == '#{action}' }"
101
- end
103
+ actions = actions.map(&:to_s)
104
+ after_filter { |c| c.cache_page if actions.include?(c.action_name) }
102
105
  end
103
106
 
104
107
  private
105
108
  def page_cache_file(path)
106
- name = ((path.empty? || path == "/") ? "/index" : URI.unescape(path))
109
+ name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/'))
107
110
  name << page_cache_extension unless (name.split('/').last || name).include? '.'
108
111
  return name
109
112
  end
@@ -117,21 +120,36 @@ module ActionController #:nodoc:
117
120
  # expire_page :controller => "lists", :action => "show"
118
121
  def expire_page(options = {})
119
122
  return unless perform_caching
120
- if options[:action].is_a?(Array)
121
- options[:action].dup.each do |action|
122
- self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :action => action)))
123
+
124
+ if options.is_a?(Hash)
125
+ if options[:action].is_a?(Array)
126
+ options[:action].dup.each do |action|
127
+ self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :action => action)))
128
+ end
129
+ else
130
+ self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true)))
123
131
  end
124
132
  else
125
- self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true)))
133
+ self.class.expire_page(options)
126
134
  end
127
135
  end
128
136
 
129
137
  # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used
130
- # If no options are provided, the current +options+ for this action is used. Example:
138
+ # If no options are provided, the requested url is used. Example:
131
139
  # cache_page "I'm the cached content", :controller => "lists", :action => "show"
132
- def cache_page(content = nil, options = {})
140
+ def cache_page(content = nil, options = nil)
133
141
  return unless perform_caching && caching_allowed
134
- self.class.cache_page(content || response.body, url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :format => params[:format])))
142
+
143
+ path = case options
144
+ when Hash
145
+ url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :format => params[:format]))
146
+ when String
147
+ options
148
+ else
149
+ request.path
150
+ end
151
+
152
+ self.class.cache_page(content || response.body, path)
135
153
  end
136
154
 
137
155
  private
@@ -161,17 +179,26 @@ module ActionController #:nodoc:
161
179
  # Different representations of the same resource, e.g. <tt>http://david.somewhere.com/lists</tt> and <tt>http://david.somewhere.com/lists.xml</tt>
162
180
  # are treated like separate requests and so are cached separately. Keep in mind when expiring an action cache that <tt>:action => 'lists'</tt> is not the same
163
181
  # as <tt>:action => 'list', :format => :xml</tt>.
182
+ #
183
+ # You can set modify the default action cache path by passing a :cache_path option. This will be passed directly to ActionCachePath.path_for. This is handy
184
+ # for actions with multiple possible routes that should be cached differently. If a block is given, it is called with the current controller instance.
185
+ #
186
+ # class ListsController < ApplicationController
187
+ # before_filter :authenticate, :except => :public
188
+ # caches_page :public
189
+ # caches_action :show, :cache_path => { :project => 1 }
190
+ # caches_action :show, :cache_path => Proc.new { |controller|
191
+ # controller.params[:user_id] ?
192
+ # controller.send(:user_list_url, c.params[:user_id], c.params[:id]) :
193
+ # controller.send(:list_url, c.params[:id]) }
194
+ # end
164
195
  module Actions
165
196
  def self.included(base) #:nodoc:
166
197
  base.extend(ClassMethods)
167
- base.class_eval do
168
- attr_accessor :rendered_action_cache, :action_cache_path
169
- alias_method_chain :protected_instance_variables, :action_caching
170
- end
171
- end
172
-
173
- def protected_instance_variables_with_action_caching
174
- protected_instance_variables_without_action_caching + %w(@action_cache_path)
198
+ base.class_eval do
199
+ attr_accessor :rendered_action_cache, :action_cache_path
200
+ alias_method_chain :protected_instance_variables, :action_caching
201
+ end
175
202
  end
176
203
 
177
204
  module ClassMethods
@@ -179,12 +206,14 @@ module ActionController #:nodoc:
179
206
  # See ActionController::Caching::Actions for details.
180
207
  def caches_action(*actions)
181
208
  return unless perform_caching
182
- action_cache_filter = ActionCacheFilter.new(*actions)
183
- before_filter action_cache_filter
184
- after_filter action_cache_filter
209
+ around_filter(ActionCacheFilter.new(*actions))
185
210
  end
186
211
  end
187
212
 
213
+ def protected_instance_variables_with_action_caching
214
+ protected_instance_variables_without_action_caching + %w(@action_cache_path)
215
+ end
216
+
188
217
  def expire_action(options = {})
189
218
  return unless perform_caching
190
219
  if options[:action].is_a?(Array)
@@ -197,17 +226,18 @@ module ActionController #:nodoc:
197
226
  end
198
227
 
199
228
  class ActionCacheFilter #:nodoc:
200
- def initialize(*actions)
229
+ def initialize(*actions, &block)
230
+ @options = actions.extract_options!
201
231
  @actions = Set.new actions
202
232
  end
203
233
 
204
234
  def before(controller)
205
- return unless @actions.include?(controller.action_name.to_sym)
206
- cache_path = ActionCachePath.new(controller, {})
235
+ return unless @actions.include?(controller.action_name.intern)
236
+ cache_path = ActionCachePath.new(controller, path_options_for(controller, @options))
207
237
  if cache = controller.read_fragment(cache_path.path)
208
238
  controller.rendered_action_cache = true
209
239
  set_content_type!(controller, cache_path.extension)
210
- controller.send(:render_text, cache)
240
+ controller.send!(:render_for_text, cache)
211
241
  false
212
242
  else
213
243
  controller.action_cache_path = cache_path
@@ -215,15 +245,22 @@ module ActionController #:nodoc:
215
245
  end
216
246
 
217
247
  def after(controller)
218
- return if !@actions.include?(controller.action_name.to_sym) || controller.rendered_action_cache
248
+ return if !@actions.include?(controller.action_name.intern) || controller.rendered_action_cache || !caching_allowed(controller)
219
249
  controller.write_fragment(controller.action_cache_path.path, controller.response.body)
220
250
  end
221
-
251
+
222
252
  private
223
253
  def set_content_type!(controller, extension)
224
- controller.response.content_type = Mime::EXTENSION_LOOKUP[extension].to_s if extension
254
+ controller.response.content_type = Mime::Type.lookup_by_extension(extension).to_s if extension
255
+ end
256
+
257
+ def path_options_for(controller, options)
258
+ ((path_options = options[:cache_path]).respond_to?(:call) ? path_options.call(controller) : path_options) || {}
259
+ end
260
+
261
+ def caching_allowed(controller)
262
+ controller.request.get? && controller.response.headers['Status'].to_i == 200
225
263
  end
226
-
227
264
  end
228
265
 
229
266
  class ActionCachePath
@@ -253,7 +290,7 @@ module ActionController #:nodoc:
253
290
  end
254
291
 
255
292
  def extract_extension(file_path)
256
- # Don't want just what comes after the last '.' to accomodate multi part extensions
293
+ # Don't want just what comes after the last '.' to accommodate multi part extensions
257
294
  # such as tar.gz.
258
295
  file_path[/^[^.]+\.(.+)$/, 1]
259
296
  end
@@ -270,25 +307,28 @@ module ActionController #:nodoc:
270
307
  # <%= render :partial => "topic", :collection => Topic.find(:all) %>
271
308
  # <% end %>
272
309
  #
273
- # This cache will bind to the name of action that called it. So you would be able to invalidate it using
274
- # <tt>expire_fragment(:controller => "topics", :action => "list")</tt> -- if that was the controller/action used. This is not too helpful
275
- # if you need to cache multiple fragments per action or if the action itself is cached using <tt>caches_action</tt>. So instead we should
276
- # qualify the name of the action used with something like:
310
+ # This cache will bind to the name of the action that called it, so if this code was part of the view for the topics/list action, you would
311
+ # be able to invalidate it using <tt>expire_fragment(:controller => "topics", :action => "list")</tt>.
312
+ #
313
+ # This default behavior is of limited use if you need to cache multiple fragments per action or if the action itself is cached using
314
+ # <tt>caches_action</tt>, so we also have the option to qualify the name of the cached fragment with something like:
277
315
  #
278
316
  # <% cache(:action => "list", :action_suffix => "all_topics") do %>
279
317
  #
280
- # That would result in a name such as "/topics/list/all_topics", which wouldn't conflict with any action cache and neither with another
281
- # fragment using a different suffix. Note that the URL doesn't have to really exist or be callable. We're just using the url_for system
282
- # to generate unique cache names that we can refer to later for expirations. The expiration call for this example would be
283
- # <tt>expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics")</tt>.
318
+ # That would result in a name such as "/topics/list/all_topics", avoiding conflicts with the action cache and with any fragments that use a
319
+ # different suffix. Note that the URL doesn't have to really exist or be callable - the url_for system is just used to generate unique
320
+ # cache names that we can refer to when we need to expire the cache.
321
+ #
322
+ # The expiration call for this example is:
323
+ #
324
+ # expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics")
284
325
  #
285
326
  # == Fragment stores
286
327
  #
287
- # In order to use the fragment caching, you need to designate where the caches should be stored. This is done by assigning a fragment store
288
- # of which there are four different kinds:
328
+ # By default, cached fragments are stored in memory. The available store options are:
289
329
  #
290
- # * FileStore: Keeps the fragments on disk in the +cache_path+, which works well for all types of environments and shares the fragments for
291
- # all the web server processes running off the same application directory.
330
+ # * FileStore: Keeps the fragments on disk in the +cache_path+, which works well for all types of environments and allows all
331
+ # processes running from the same application directory to access the cached content.
292
332
  # * MemoryStore: Keeps the fragments in memory, which is fine for WEBrick and for FCGI (if you don't care that each FCGI process holds its
293
333
  # own fragment store). It's not suitable for CGI as the process is thrown away at the end of each request. It can potentially also take
294
334
  # up a lot of memory since each process keeps all the caches in memory.
@@ -310,6 +350,7 @@ module ActionController #:nodoc:
310
350
  @@fragment_cache_store = MemoryStore.new
311
351
  cattr_reader :fragment_cache_store
312
352
 
353
+ # Defines the storage option for cached fragments
313
354
  def self.fragment_cache_store=(store_option)
314
355
  store, *parameters = *([ store_option ].flatten)
315
356
  @@fragment_cache_store = if store.is_a?(Symbol)
@@ -323,6 +364,9 @@ module ActionController #:nodoc:
323
364
  end
324
365
  end
325
366
 
367
+ # Given a name (as described in <tt>expire_fragment</tt>), returns a key suitable for use in reading,
368
+ # writing, or expiring a cached fragment. If the name is a hash, the generated name is the return
369
+ # value of url_for on that hash (without the protocol).
326
370
  def fragment_cache_key(name)
327
371
  name.is_a?(Hash) ? url_for(name).split("://").last : name
328
372
  end
@@ -331,7 +375,7 @@ module ActionController #:nodoc:
331
375
  def cache_erb_fragment(block, name = {}, options = nil)
332
376
  unless perform_caching then block.call; return end
333
377
 
334
- buffer = eval("_erbout", block.binding)
378
+ buffer = eval(ActionView::Base.erb_variable, block.binding)
335
379
 
336
380
  if cache = read_fragment(name, options)
337
381
  buffer.concat(cache)
@@ -342,6 +386,7 @@ module ActionController #:nodoc:
342
386
  end
343
387
  end
344
388
 
389
+ # Writes <tt>content</tt> to the location signified by <tt>name</tt> (see <tt>expire_fragment</tt> for acceptable formats)
345
390
  def write_fragment(name, content, options = nil)
346
391
  return unless perform_caching
347
392
 
@@ -352,6 +397,7 @@ module ActionController #:nodoc:
352
397
  content
353
398
  end
354
399
 
400
+ # Reads a cached fragment from the location signified by <tt>name</tt> (see <tt>expire_fragment</tt> for acceptable formats)
355
401
  def read_fragment(name, options = nil)
356
402
  return unless perform_caching
357
403
 
@@ -368,8 +414,8 @@ module ActionController #:nodoc:
368
414
  # %r{pages/\d*/notes}
369
415
  # Ensure you do not specify start and finish in the regex (^$) because
370
416
  # the actual filename matched looks like ./cache/filename/path.cache
371
- # Regexp expiration is not supported on caches which can't iterate over
372
- # all keys, such as memcached.
417
+ # Regexp expiration is only supported on caches that can iterate over
418
+ # all keys (unlike memcached).
373
419
  def expire_fragment(name, options = nil)
374
420
  return unless perform_caching
375
421
 
@@ -386,12 +432,6 @@ module ActionController #:nodoc:
386
432
  end
387
433
  end
388
434
 
389
- # Deprecated -- just call expire_fragment with a regular expression
390
- def expire_matched_fragments(matcher = /.*/, options = nil) #:nodoc:
391
- expire_fragment(matcher, options)
392
- end
393
- deprecate :expire_matched_fragments => :expire_fragment
394
-
395
435
 
396
436
  class UnthreadedMemoryStore #:nodoc:
397
437
  def initialize #:nodoc:
@@ -438,7 +478,7 @@ module ActionController #:nodoc:
438
478
  super
439
479
  if ActionController::Base.allow_concurrency
440
480
  @mutex = Mutex.new
441
- MemoryStore.send(:include, ThreadSafety)
481
+ MemoryStore.module_eval { include ThreadSafety }
442
482
  end
443
483
  end
444
484
  end
@@ -453,6 +493,8 @@ module ActionController #:nodoc:
453
493
  end
454
494
  end
455
495
 
496
+ begin
497
+ require_library_or_gem 'memcache'
456
498
  class MemCacheStore < MemoryStore #:nodoc:
457
499
  attr_reader :addresses
458
500
 
@@ -464,6 +506,9 @@ module ActionController #:nodoc:
464
506
  @data = MemCache.new(*addresses)
465
507
  end
466
508
  end
509
+ rescue LoadError
510
+ # MemCache wasn't available so neither can the store be
511
+ end
467
512
 
468
513
  class UnthreadedFileStore #:nodoc:
469
514
  attr_reader :cache_path
@@ -528,7 +573,7 @@ module ActionController #:nodoc:
528
573
  super(cache_path)
529
574
  if ActionController::Base.allow_concurrency
530
575
  @mutex = Mutex.new
531
- FileStore.send(:include, ThreadSafety)
576
+ FileStore.module_eval { include ThreadSafety }
532
577
  end
533
578
  end
534
579
  end
@@ -564,7 +609,7 @@ module ActionController #:nodoc:
564
609
  module ClassMethods #:nodoc:
565
610
  def cache_sweeper(*sweepers)
566
611
  return unless perform_caching
567
- configuration = sweepers.last.is_a?(Hash) ? sweepers.pop : {}
612
+ configuration = sweepers.extract_options!
568
613
  sweepers.each do |sweeper|
569
614
  ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
570
615
  sweeper_instance = Object.const_get(Inflector.classify(sweeper)).instance
@@ -583,10 +628,6 @@ module ActionController #:nodoc:
583
628
  class Sweeper < ActiveRecord::Observer #:nodoc:
584
629
  attr_accessor :controller
585
630
 
586
- # ActiveRecord::Observer will mark this class as reloadable even though it should not be.
587
- # However, subclasses of ActionController::Caching::Sweeper should be Reloadable
588
- include Reloadable::Deprecated
589
-
590
631
  def before(controller)
591
632
  self.controller = controller
592
633
  callback(:before)
@@ -598,20 +639,45 @@ module ActionController #:nodoc:
598
639
  self.controller = nil
599
640
  end
600
641
 
642
+ protected
643
+ # gets the action cache path for the given options.
644
+ def action_path_for(options)
645
+ ActionController::Caching::Actions::ActionCachePath.path_for(controller, options)
646
+ end
647
+
648
+ # Retrieve instance variables set in the controller.
649
+ def assigns(key)
650
+ controller.instance_variable_get("@#{key}")
651
+ end
652
+
601
653
  private
602
654
  def callback(timing)
603
655
  controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
604
656
  action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
605
657
 
606
- send(controller_callback_method_name) if respond_to?(controller_callback_method_name)
607
- send(action_callback_method_name) if respond_to?(action_callback_method_name)
658
+ send!(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
659
+ send!(action_callback_method_name) if respond_to?(action_callback_method_name, true)
608
660
  end
609
661
 
610
662
  def method_missing(method, *arguments)
611
663
  return if @controller.nil?
612
- @controller.send(method, *arguments)
664
+ @controller.send!(method, *arguments)
613
665
  end
614
666
  end
615
667
  end
668
+
669
+ module SqlCache
670
+ def self.included(base) #:nodoc:
671
+ if defined?(ActiveRecord) && ActiveRecord::Base.respond_to?(:cache)
672
+ base.alias_method_chain :perform_action, :caching
673
+ end
674
+ end
675
+
676
+ def perform_action_with_caching
677
+ ActiveRecord::Base.cache do
678
+ perform_action_without_caching
679
+ end
680
+ end
681
+ end
616
682
  end
617
683
  end