eactionpack 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. data/CHANGELOG +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +469 -0
  4. data/RUNNING_UNIT_TESTS +24 -0
  5. data/Rakefile +146 -0
  6. data/install.rb +30 -0
  7. data/lib/action_controller.rb +79 -0
  8. data/lib/action_controller/assertions.rb +69 -0
  9. data/lib/action_controller/assertions/dom_assertions.rb +39 -0
  10. data/lib/action_controller/assertions/model_assertions.rb +20 -0
  11. data/lib/action_controller/assertions/response_assertions.rb +172 -0
  12. data/lib/action_controller/assertions/routing_assertions.rb +146 -0
  13. data/lib/action_controller/assertions/selector_assertions.rb +491 -0
  14. data/lib/action_controller/assertions/tag_assertions.rb +130 -0
  15. data/lib/action_controller/base.rb +1288 -0
  16. data/lib/action_controller/benchmarking.rb +94 -0
  17. data/lib/action_controller/caching.rb +72 -0
  18. data/lib/action_controller/caching/actions.rb +144 -0
  19. data/lib/action_controller/caching/fragments.rb +138 -0
  20. data/lib/action_controller/caching/pages.rb +154 -0
  21. data/lib/action_controller/caching/sql_cache.rb +18 -0
  22. data/lib/action_controller/caching/sweeping.rb +97 -0
  23. data/lib/action_controller/cgi_ext.rb +16 -0
  24. data/lib/action_controller/cgi_ext/cookie.rb +110 -0
  25. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  26. data/lib/action_controller/cgi_ext/session.rb +73 -0
  27. data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
  28. data/lib/action_controller/cgi_process.rb +223 -0
  29. data/lib/action_controller/components.rb +166 -0
  30. data/lib/action_controller/cookies.rb +96 -0
  31. data/lib/action_controller/dispatcher.rb +162 -0
  32. data/lib/action_controller/filters.rb +642 -0
  33. data/lib/action_controller/flash.rb +172 -0
  34. data/lib/action_controller/headers.rb +31 -0
  35. data/lib/action_controller/helpers.rb +221 -0
  36. data/lib/action_controller/http_authentication.rb +124 -0
  37. data/lib/action_controller/integration.rb +634 -0
  38. data/lib/action_controller/layout.rb +309 -0
  39. data/lib/action_controller/mime_responds.rb +173 -0
  40. data/lib/action_controller/mime_type.rb +186 -0
  41. data/lib/action_controller/mime_types.rb +20 -0
  42. data/lib/action_controller/polymorphic_routes.rb +191 -0
  43. data/lib/action_controller/record_identifier.rb +102 -0
  44. data/lib/action_controller/request.rb +764 -0
  45. data/lib/action_controller/request_forgery_protection.rb +140 -0
  46. data/lib/action_controller/request_profiler.rb +169 -0
  47. data/lib/action_controller/rescue.rb +258 -0
  48. data/lib/action_controller/resources.rb +572 -0
  49. data/lib/action_controller/response.rb +76 -0
  50. data/lib/action_controller/routing.rb +387 -0
  51. data/lib/action_controller/routing/builder.rb +203 -0
  52. data/lib/action_controller/routing/optimisations.rb +120 -0
  53. data/lib/action_controller/routing/recognition_optimisation.rb +162 -0
  54. data/lib/action_controller/routing/route.rb +240 -0
  55. data/lib/action_controller/routing/route_set.rb +436 -0
  56. data/lib/action_controller/routing/routing_ext.rb +46 -0
  57. data/lib/action_controller/routing/segments.rb +283 -0
  58. data/lib/action_controller/session/active_record_store.rb +340 -0
  59. data/lib/action_controller/session/cookie_store.rb +166 -0
  60. data/lib/action_controller/session/drb_server.rb +32 -0
  61. data/lib/action_controller/session/drb_store.rb +35 -0
  62. data/lib/action_controller/session/mem_cache_store.rb +98 -0
  63. data/lib/action_controller/session_management.rb +158 -0
  64. data/lib/action_controller/status_codes.rb +88 -0
  65. data/lib/action_controller/streaming.rb +155 -0
  66. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  67. data/lib/action_controller/templates/rescues/_trace.erb +26 -0
  68. data/lib/action_controller/templates/rescues/diagnostics.erb +11 -0
  69. data/lib/action_controller/templates/rescues/layout.erb +29 -0
  70. data/lib/action_controller/templates/rescues/missing_template.erb +2 -0
  71. data/lib/action_controller/templates/rescues/routing_error.erb +10 -0
  72. data/lib/action_controller/templates/rescues/template_error.erb +21 -0
  73. data/lib/action_controller/templates/rescues/unknown_action.erb +2 -0
  74. data/lib/action_controller/test_case.rb +83 -0
  75. data/lib/action_controller/test_process.rb +526 -0
  76. data/lib/action_controller/url_rewriter.rb +142 -0
  77. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  78. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  79. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  80. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  81. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  82. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  83. data/lib/action_controller/verification.rb +130 -0
  84. data/lib/action_pack.rb +24 -0
  85. data/lib/action_pack/version.rb +9 -0
  86. data/lib/action_view.rb +44 -0
  87. data/lib/action_view/base.rb +335 -0
  88. data/lib/action_view/helpers/active_record_helper.rb +276 -0
  89. data/lib/action_view/helpers/asset_tag_helper.rb +599 -0
  90. data/lib/action_view/helpers/atom_feed_helper.rb +143 -0
  91. data/lib/action_view/helpers/benchmark_helper.rb +33 -0
  92. data/lib/action_view/helpers/cache_helper.rb +40 -0
  93. data/lib/action_view/helpers/capture_helper.rb +161 -0
  94. data/lib/action_view/helpers/date_helper.rb +711 -0
  95. data/lib/action_view/helpers/debug_helper.rb +31 -0
  96. data/lib/action_view/helpers/form_helper.rb +767 -0
  97. data/lib/action_view/helpers/form_options_helper.rb +458 -0
  98. data/lib/action_view/helpers/form_tag_helper.rb +458 -0
  99. data/lib/action_view/helpers/javascript_helper.rb +148 -0
  100. data/lib/action_view/helpers/number_helper.rb +186 -0
  101. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  102. data/lib/action_view/helpers/record_tag_helper.rb +59 -0
  103. data/lib/action_view/helpers/sanitize_helper.rb +229 -0
  104. data/lib/action_view/helpers/tag_helper.rb +134 -0
  105. data/lib/action_view/helpers/text_helper.rb +507 -0
  106. data/lib/action_view/helpers/url_helper.rb +573 -0
  107. data/lib/action_view/inline_template.rb +20 -0
  108. data/lib/action_view/partial_template.rb +70 -0
  109. data/lib/action_view/partials.rb +158 -0
  110. data/lib/action_view/template.rb +125 -0
  111. data/lib/action_view/template_error.rb +110 -0
  112. data/lib/action_view/template_finder.rb +176 -0
  113. data/lib/action_view/template_handler.rb +34 -0
  114. data/lib/action_view/template_handlers/builder.rb +27 -0
  115. data/lib/action_view/template_handlers/compilable.rb +128 -0
  116. data/lib/action_view/template_handlers/erb.rb +56 -0
  117. data/lib/action_view/test_case.rb +58 -0
  118. data/lib/actionpack.rb +1 -0
  119. data/test/abstract_unit.rb +36 -0
  120. data/test/active_record_unit.rb +105 -0
  121. data/test/activerecord/active_record_store_test.rb +141 -0
  122. data/test/activerecord/render_partial_with_record_identification_test.rb +191 -0
  123. data/test/adv_attr_test.rb +20 -0
  124. data/test/controller/action_pack_assertions_test.rb +543 -0
  125. data/test/controller/addresses_render_test.rb +43 -0
  126. data/test/controller/assert_select_test.rb +331 -0
  127. data/test/controller/base_test.rb +219 -0
  128. data/test/controller/benchmark_test.rb +32 -0
  129. data/test/controller/caching_test.rb +581 -0
  130. data/test/controller/capture_test.rb +89 -0
  131. data/test/controller/cgi_test.rb +116 -0
  132. data/test/controller/components_test.rb +140 -0
  133. data/test/controller/content_type_test.rb +139 -0
  134. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  135. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  136. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  137. data/test/controller/cookie_test.rb +146 -0
  138. data/test/controller/custom_handler_test.rb +45 -0
  139. data/test/controller/deprecation/deprecated_base_methods_test.rb +37 -0
  140. data/test/controller/dispatcher_test.rb +105 -0
  141. data/test/controller/fake_controllers.rb +33 -0
  142. data/test/controller/fake_models.rb +11 -0
  143. data/test/controller/filter_params_test.rb +49 -0
  144. data/test/controller/filters_test.rb +881 -0
  145. data/test/controller/flash_test.rb +146 -0
  146. data/test/controller/header_test.rb +14 -0
  147. data/test/controller/helper_test.rb +210 -0
  148. data/test/controller/html-scanner/cdata_node_test.rb +15 -0
  149. data/test/controller/html-scanner/document_test.rb +148 -0
  150. data/test/controller/html-scanner/node_test.rb +89 -0
  151. data/test/controller/html-scanner/sanitizer_test.rb +269 -0
  152. data/test/controller/html-scanner/tag_node_test.rb +238 -0
  153. data/test/controller/html-scanner/text_node_test.rb +50 -0
  154. data/test/controller/html-scanner/tokenizer_test.rb +131 -0
  155. data/test/controller/http_authentication_test.rb +54 -0
  156. data/test/controller/integration_test.rb +252 -0
  157. data/test/controller/integration_upload_test.rb +43 -0
  158. data/test/controller/layout_test.rb +255 -0
  159. data/test/controller/mime_responds_test.rb +514 -0
  160. data/test/controller/mime_type_test.rb +84 -0
  161. data/test/controller/new_render_test.rb +843 -0
  162. data/test/controller/polymorphic_routes_test.rb +174 -0
  163. data/test/controller/record_identifier_test.rb +139 -0
  164. data/test/controller/redirect_test.rb +289 -0
  165. data/test/controller/render_test.rb +484 -0
  166. data/test/controller/request_forgery_protection_test.rb +305 -0
  167. data/test/controller/request_test.rb +928 -0
  168. data/test/controller/rescue_test.rb +517 -0
  169. data/test/controller/resources_test.rb +873 -0
  170. data/test/controller/routing_test.rb +2464 -0
  171. data/test/controller/selector_test.rb +628 -0
  172. data/test/controller/send_file_test.rb +138 -0
  173. data/test/controller/session/cookie_store_test.rb +258 -0
  174. data/test/controller/session/mem_cache_store_test.rb +181 -0
  175. data/test/controller/session_fixation_test.rb +89 -0
  176. data/test/controller/session_management_test.rb +178 -0
  177. data/test/controller/test_test.rb +695 -0
  178. data/test/controller/url_rewriter_test.rb +310 -0
  179. data/test/controller/verification_test.rb +270 -0
  180. data/test/controller/view_paths_test.rb +140 -0
  181. data/test/controller/webservice_test.rb +229 -0
  182. data/test/fixtures/addresses/list.erb +1 -0
  183. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  184. data/test/fixtures/companies.yml +24 -0
  185. data/test/fixtures/company.rb +10 -0
  186. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  187. data/test/fixtures/content_type/render_default_for_js.js.erb +1 -0
  188. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  189. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  190. data/test/fixtures/customers/_customer.html.erb +1 -0
  191. data/test/fixtures/db_definitions/sqlite.sql +49 -0
  192. data/test/fixtures/developer.rb +9 -0
  193. data/test/fixtures/developers.yml +21 -0
  194. data/test/fixtures/developers_projects.yml +13 -0
  195. data/test/fixtures/fun/games/hello_world.erb +1 -0
  196. data/test/fixtures/functional_caching/_partial.erb +3 -0
  197. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  198. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  199. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  200. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  201. data/test/fixtures/helpers/abc_helper.rb +5 -0
  202. data/test/fixtures/helpers/fun/games_helper.rb +3 -0
  203. data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
  204. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  205. data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
  206. data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
  207. data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
  208. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  209. data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
  210. data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
  211. data/test/fixtures/layouts/block_with_layout.erb +3 -0
  212. data/test/fixtures/layouts/builder.builder +3 -0
  213. data/test/fixtures/layouts/partial_with_layout.erb +3 -0
  214. data/test/fixtures/layouts/standard.erb +1 -0
  215. data/test/fixtures/layouts/talk_from_action.erb +2 -0
  216. data/test/fixtures/layouts/yield.erb +2 -0
  217. data/test/fixtures/mascot.rb +3 -0
  218. data/test/fixtures/mascots.yml +4 -0
  219. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  220. data/test/fixtures/multipart/binary_file +0 -0
  221. data/test/fixtures/multipart/boundary_problem_file +10 -0
  222. data/test/fixtures/multipart/bracketed_param +5 -0
  223. data/test/fixtures/multipart/large_text_file +10 -0
  224. data/test/fixtures/multipart/mixed_files +0 -0
  225. data/test/fixtures/multipart/mona_lisa.jpg +0 -0
  226. data/test/fixtures/multipart/single_parameter +5 -0
  227. data/test/fixtures/multipart/text_file +10 -0
  228. data/test/fixtures/override/test/hello_world.erb +1 -0
  229. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  230. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  231. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  232. data/test/fixtures/post_test/post/index.html.erb +1 -0
  233. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  234. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  235. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  236. data/test/fixtures/project.rb +3 -0
  237. data/test/fixtures/projects.yml +7 -0
  238. data/test/fixtures/public/404.html +1 -0
  239. data/test/fixtures/public/500.html +1 -0
  240. data/test/fixtures/public/images/rails.png +0 -0
  241. data/test/fixtures/public/javascripts/application.js +1 -0
  242. data/test/fixtures/public/javascripts/bank.js +1 -0
  243. data/test/fixtures/public/javascripts/robber.js +1 -0
  244. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  245. data/test/fixtures/public/stylesheets/bank.css +1 -0
  246. data/test/fixtures/public/stylesheets/robber.css +1 -0
  247. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  248. data/test/fixtures/replies.yml +15 -0
  249. data/test/fixtures/reply.rb +7 -0
  250. data/test/fixtures/respond_to/all_types_with_layout.html.erb +1 -0
  251. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  252. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  253. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  254. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  255. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  256. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  257. data/test/fixtures/respond_to/using_defaults.html.erb +1 -0
  258. data/test/fixtures/respond_to/using_defaults.js.rjs +1 -0
  259. data/test/fixtures/respond_to/using_defaults.xml.builder +1 -0
  260. data/test/fixtures/respond_to/using_defaults_with_type_list.html.erb +1 -0
  261. data/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs +1 -0
  262. data/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder +1 -0
  263. data/test/fixtures/scope/test/modgreet.erb +1 -0
  264. data/test/fixtures/shared.html.erb +1 -0
  265. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  266. data/test/fixtures/test/_customer.erb +1 -0
  267. data/test/fixtures/test/_customer_counter.erb +1 -0
  268. data/test/fixtures/test/_customer_greeting.erb +1 -0
  269. data/test/fixtures/test/_form.erb +1 -0
  270. data/test/fixtures/test/_hash_greeting.erb +1 -0
  271. data/test/fixtures/test/_hash_object.erb +2 -0
  272. data/test/fixtures/test/_hello.builder +1 -0
  273. data/test/fixtures/test/_labelling_form.erb +1 -0
  274. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  275. data/test/fixtures/test/_partial.erb +1 -0
  276. data/test/fixtures/test/_partial.html.erb +1 -0
  277. data/test/fixtures/test/_partial.js.erb +1 -0
  278. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  279. data/test/fixtures/test/_partial_only.erb +1 -0
  280. data/test/fixtures/test/_person.erb +2 -0
  281. data/test/fixtures/test/_raise.html.erb +1 -0
  282. data/test/fixtures/test/action_talk_to_layout.erb +2 -0
  283. data/test/fixtures/test/block_content_for.erb +2 -0
  284. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  285. data/test/fixtures/test/capturing.erb +4 -0
  286. data/test/fixtures/test/content_for.erb +2 -0
  287. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  288. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  289. data/test/fixtures/test/delete_with_js.rjs +2 -0
  290. data/test/fixtures/test/dot.directory/render_file_with_ivar.erb +1 -0
  291. data/test/fixtures/test/enum_rjs_test.rjs +6 -0
  292. data/test/fixtures/test/erb_content_for.erb +2 -0
  293. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  294. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  295. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  296. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  297. data/test/fixtures/test/greeting.erb +1 -0
  298. data/test/fixtures/test/greeting.js.rjs +1 -0
  299. data/test/fixtures/test/hello.builder +4 -0
  300. data/test/fixtures/test/hello_world.erb +1 -0
  301. data/test/fixtures/test/hello_world_container.builder +3 -0
  302. data/test/fixtures/test/hello_world_from_rxml.builder +4 -0
  303. data/test/fixtures/test/hello_world_with_layout_false.erb +1 -0
  304. data/test/fixtures/test/hello_xml_world.builder +11 -0
  305. data/test/fixtures/test/list.erb +1 -0
  306. data/test/fixtures/test/non_erb_block_content_for.builder +4 -0
  307. data/test/fixtures/test/potential_conflicts.erb +4 -0
  308. data/test/fixtures/test/render_file_from_template.html.erb +1 -0
  309. data/test/fixtures/test/render_file_with_ivar.erb +1 -0
  310. data/test/fixtures/test/render_file_with_locals.erb +1 -0
  311. data/test/fixtures/test/render_to_string_test.erb +1 -0
  312. data/test/fixtures/test/update_element_with_capture.erb +9 -0
  313. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  314. data/test/fixtures/topic.rb +3 -0
  315. data/test/fixtures/topics.yml +22 -0
  316. data/test/fixtures/topics/_topic.html.erb +1 -0
  317. data/test/template/active_record_helper_test.rb +268 -0
  318. data/test/template/asset_tag_helper_test.rb +514 -0
  319. data/test/template/atom_feed_helper_test.rb +179 -0
  320. data/test/template/benchmark_helper_test.rb +60 -0
  321. data/test/template/date_helper_test.rb +1791 -0
  322. data/test/template/deprecated_erb_variable_test.rb +9 -0
  323. data/test/template/erb_util_test.rb +24 -0
  324. data/test/template/form_helper_test.rb +885 -0
  325. data/test/template/form_options_helper_test.rb +1333 -0
  326. data/test/template/form_tag_helper_test.rb +272 -0
  327. data/test/template/javascript_helper_test.rb +73 -0
  328. data/test/template/number_helper_test.rb +97 -0
  329. data/test/template/record_tag_helper_test.rb +54 -0
  330. data/test/template/sanitize_helper_test.rb +48 -0
  331. data/test/template/tag_helper_test.rb +77 -0
  332. data/test/template/template_finder_test.rb +73 -0
  333. data/test/template/template_object_test.rb +95 -0
  334. data/test/template/test_test.rb +56 -0
  335. data/test/template/text_helper_test.rb +367 -0
  336. data/test/template/url_helper_test.rb +544 -0
  337. data/test/testing_sandbox.rb +15 -0
  338. metadata +469 -0
@@ -0,0 +1,154 @@
1
+ require 'fileutils'
2
+ require 'uri'
3
+
4
+ module ActionController #:nodoc:
5
+ module Caching
6
+ # Page caching is an approach to caching where the entire action output of is stored as a HTML file that the web server
7
+ # can serve without going through Action Pack. This is the fastest way to cache your content as opposed to going dynamically
8
+ # through the process of generating the content. Unfortunately, this incredible speed-up is only available to stateless pages
9
+ # where all visitors are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are
10
+ # a great fit for this approach, but account-based systems where people log in and manipulate their own data are often less
11
+ # likely candidates.
12
+ #
13
+ # Specifying which actions to cache is done through the <tt>caches_page</tt> class method:
14
+ #
15
+ # class WeblogController < ActionController::Base
16
+ # caches_page :show, :new
17
+ # end
18
+ #
19
+ # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>,
20
+ # which match the URLs used to trigger the dynamic generation. This is how the web server is able
21
+ # pick up a cache file when it exists and otherwise let the request pass on to Action Pack to generate it.
22
+ #
23
+ # Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache
24
+ # is not restored before another hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
25
+ #
26
+ # class WeblogController < ActionController::Base
27
+ # def update
28
+ # List.update(params[:list][:id], params[:list])
29
+ # expire_page :action => "show", :id => params[:list][:id]
30
+ # redirect_to :action => "show", :id => params[:list][:id]
31
+ # end
32
+ # end
33
+ #
34
+ # Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be
35
+ # expired.
36
+ #
37
+ # == Setting the cache directory
38
+ #
39
+ # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
40
+ # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing
41
+ # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
42
+ # web server to look in the new location for cached files.
43
+ #
44
+ # == Setting the cache extension
45
+ #
46
+ # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
47
+ # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
48
+ # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
49
+ # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
50
+ module Pages
51
+ def self.included(base) #:nodoc:
52
+ base.extend(ClassMethods)
53
+ base.class_eval do
54
+ @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
55
+ cattr_accessor :page_cache_directory
56
+
57
+ @@page_cache_extension = '.html'
58
+ cattr_accessor :page_cache_extension
59
+ end
60
+ end
61
+
62
+ module ClassMethods
63
+ # Expires the page that was cached with the +path+ as a key. Example:
64
+ # expire_page "/lists/show"
65
+ def expire_page(path)
66
+ return unless perform_caching
67
+
68
+ benchmark "Expired page: #{page_cache_file(path)}" do
69
+ File.delete(page_cache_path(path)) if File.exist?(page_cache_path(path))
70
+ end
71
+ end
72
+
73
+ # Manually cache the +content+ in the key determined by +path+. Example:
74
+ # cache_page "I'm the cached content", "/lists/show"
75
+ def cache_page(content, path)
76
+ return unless perform_caching
77
+
78
+ benchmark "Cached page: #{page_cache_file(path)}" do
79
+ FileUtils.makedirs(File.dirname(page_cache_path(path)))
80
+ File.open(page_cache_path(path), "wb+") { |f| f.write(content) }
81
+ end
82
+ end
83
+
84
+ # Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that
85
+ # matches the triggering url.
86
+ #
87
+ # Usage:
88
+ #
89
+ # # cache the index action
90
+ # caches_page :index
91
+ #
92
+ # # cache the index action except for JSON requests
93
+ # caches_page :index, :if => Proc.new { |c| !c.request.format.json? }
94
+ def caches_page(*actions)
95
+ return unless perform_caching
96
+ options = actions.extract_options!
97
+ after_filter({:only => actions}.merge(options)) { |c| c.cache_page }
98
+ end
99
+
100
+ private
101
+ def page_cache_file(path)
102
+ name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/'))
103
+ name << page_cache_extension unless (name.split('/').last || name).include? '.'
104
+ return name
105
+ end
106
+
107
+ def page_cache_path(path)
108
+ page_cache_directory + page_cache_file(path)
109
+ end
110
+ end
111
+
112
+ # Expires the page that was cached with the +options+ as a key. Example:
113
+ # expire_page :controller => "lists", :action => "show"
114
+ def expire_page(options = {})
115
+ return unless perform_caching
116
+
117
+ if options.is_a?(Hash)
118
+ if options[:action].is_a?(Array)
119
+ options[:action].dup.each do |action|
120
+ self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :action => action)))
121
+ end
122
+ else
123
+ self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true)))
124
+ end
125
+ else
126
+ self.class.expire_page(options)
127
+ end
128
+ end
129
+
130
+ # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used
131
+ # If no options are provided, the requested url is used. Example:
132
+ # cache_page "I'm the cached content", :controller => "lists", :action => "show"
133
+ def cache_page(content = nil, options = nil)
134
+ return unless perform_caching && caching_allowed
135
+
136
+ path = case options
137
+ when Hash
138
+ url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :format => params[:format]))
139
+ when String
140
+ options
141
+ else
142
+ request.path
143
+ end
144
+
145
+ self.class.cache_page(content || response.body, path)
146
+ end
147
+
148
+ private
149
+ def caching_allowed
150
+ request.get? && response.headers['Status'].to_i == 200
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,18 @@
1
+ module ActionController #:nodoc:
2
+ module Caching
3
+ module SqlCache
4
+ def self.included(base) #:nodoc:
5
+ if defined?(ActiveRecord) && ActiveRecord::Base.respond_to?(:cache)
6
+ base.alias_method_chain :perform_action, :caching
7
+ end
8
+ end
9
+
10
+ protected
11
+ def perform_action_with_caching
12
+ ActiveRecord::Base.cache do
13
+ perform_action_without_caching
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,97 @@
1
+ module ActionController #:nodoc:
2
+ module Caching
3
+ # Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
4
+ # They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
5
+ #
6
+ # class ListSweeper < ActionController::Caching::Sweeper
7
+ # observe List, Item
8
+ #
9
+ # def after_save(record)
10
+ # list = record.is_a?(List) ? record : record.list
11
+ # expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
12
+ # expire_action(:controller => "lists", :action => "all")
13
+ # list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
14
+ # end
15
+ # end
16
+ #
17
+ # The sweeper is assigned in the controllers that wish to have its job performed using the <tt>cache_sweeper</tt> class method:
18
+ #
19
+ # class ListsController < ApplicationController
20
+ # caches_action :index, :show, :public, :feed
21
+ # cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
22
+ # end
23
+ #
24
+ # In the example above, four actions are cached and three actions are responsible for expiring those caches.
25
+ #
26
+ # You can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:
27
+ #
28
+ # class ListsController < ApplicationController
29
+ # caches_action :index, :show, :public, :feed
30
+ # cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
31
+ # end
32
+ module Sweeping
33
+ def self.included(base) #:nodoc:
34
+ base.extend(ClassMethods)
35
+ end
36
+
37
+ module ClassMethods #:nodoc:
38
+ def cache_sweeper(*sweepers)
39
+ configuration = sweepers.extract_options!
40
+
41
+ sweepers.each do |sweeper|
42
+ ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
43
+ sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
44
+
45
+ if sweeper_instance.is_a?(Sweeper)
46
+ around_filter(sweeper_instance, :only => configuration[:only])
47
+ else
48
+ after_filter(sweeper_instance, :only => configuration[:only])
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
56
+ class Sweeper < ActiveRecord::Observer #:nodoc:
57
+ attr_accessor :controller
58
+
59
+ def before(controller)
60
+ self.controller = controller
61
+ callback(:before) if controller.perform_caching
62
+ end
63
+
64
+ def after(controller)
65
+ callback(:after) if controller.perform_caching
66
+ # Clean up, so that the controller can be collected after this request
67
+ self.controller = nil
68
+ end
69
+
70
+ protected
71
+ # gets the action cache path for the given options.
72
+ def action_path_for(options)
73
+ ActionController::Caching::Actions::ActionCachePath.path_for(controller, options)
74
+ end
75
+
76
+ # Retrieve instance variables set in the controller.
77
+ def assigns(key)
78
+ controller.instance_variable_get("@#{key}")
79
+ end
80
+
81
+ private
82
+ def callback(timing)
83
+ controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
84
+ action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
85
+
86
+ send!(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
87
+ send!(action_callback_method_name) if respond_to?(action_callback_method_name, true)
88
+ end
89
+
90
+ def method_missing(method, *arguments)
91
+ return if @controller.nil?
92
+ @controller.send!(method, *arguments)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,16 @@
1
+ require 'action_controller/cgi_ext/stdinput'
2
+ require 'action_controller/cgi_ext/query_extension'
3
+ require 'action_controller/cgi_ext/cookie'
4
+ require 'action_controller/cgi_ext/session'
5
+
6
+ class CGI #:nodoc:
7
+ include ActionController::CgiExt::Stdinput
8
+
9
+ class << self
10
+ alias :escapeHTML_fail_on_nil :escapeHTML
11
+
12
+ def escapeHTML(string)
13
+ escapeHTML_fail_on_nil(string) unless string.nil?
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,110 @@
1
+ CGI.module_eval { remove_const "Cookie" }
2
+
3
+ # TODO: document how this differs from stdlib CGI::Cookie
4
+ class CGI #:nodoc:
5
+ class Cookie < DelegateClass(Array)
6
+ attr_accessor :name, :value, :path, :domain, :expires
7
+ attr_reader :secure, :http_only
8
+
9
+ # Creates a new CGI::Cookie object.
10
+ #
11
+ # The contents of the cookie can be specified as a +name+ and one
12
+ # or more +value+ arguments. Alternatively, the contents can
13
+ # be specified as a single hash argument. The possible keywords of
14
+ # this hash are as follows:
15
+ #
16
+ # * <tt>:name</tt> - The name of the cookie. Required.
17
+ # * <tt>:value</tt> - The cookie's value or list of values.
18
+ # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the
19
+ # base directory of the CGI script.
20
+ # * <tt>:domain</tt> - The domain for which this cookie applies.
21
+ # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
22
+ # * <tt>:secure</tt> - Whether this cookie is a secure cookie or not (defaults to
23
+ # +false+). Secure cookies are only transmitted to HTTPS servers.
24
+ # * <tt>:http_only</tt> - Whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP.
25
+ # More details in http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx. Defaults to +false+.
26
+ #
27
+ # These keywords correspond to attributes of the cookie object.
28
+ def initialize(name = '', *value)
29
+ if name.kind_of?(String)
30
+ @name = name
31
+ @value = Array(value)
32
+ @domain = nil
33
+ @expires = nil
34
+ @secure = false
35
+ @http_only = false
36
+ @path = nil
37
+ else
38
+ @name = name['name']
39
+ @value = (name['value'].kind_of?(String) ? [name['value']] : Array(name['value'])).delete_if(&:blank?)
40
+ @domain = name['domain']
41
+ @expires = name['expires']
42
+ @secure = name['secure'] || false
43
+ @http_only = name['http_only'] || false
44
+ @path = name['path']
45
+ end
46
+
47
+ raise ArgumentError, "`name' required" unless @name
48
+
49
+ # simple support for IE
50
+ unless @path
51
+ %r|^(.*/)|.match(ENV['SCRIPT_NAME'])
52
+ @path = ($1 or '')
53
+ end
54
+
55
+ super(@value)
56
+ end
57
+
58
+ # Sets whether the Cookie is a secure cookie or not.
59
+ def secure=(val)
60
+ @secure = val == true
61
+ end
62
+
63
+ # Sets whether the Cookie is an HTTP only cookie or not.
64
+ def http_only=(val)
65
+ @http_only = val == true
66
+ end
67
+
68
+ # Converts the Cookie to its string representation.
69
+ def to_s
70
+ buf = ''
71
+ buf << @name << '='
72
+ buf << (@value.kind_of?(String) ? CGI::escape(@value) : @value.collect{|v| CGI::escape(v) }.join("&"))
73
+ buf << '; domain=' << @domain if @domain
74
+ buf << '; path=' << @path if @path
75
+ buf << '; expires=' << CGI::rfc1123_date(@expires) if @expires
76
+ buf << '; secure' if @secure
77
+ buf << '; HttpOnly' if @http_only
78
+ buf
79
+ end
80
+
81
+ # FIXME: work around broken 1.8.7 DelegateClass#respond_to?
82
+ def respond_to?(method, include_private = false)
83
+ return true if super(method)
84
+ return __getobj__.respond_to?(method, include_private)
85
+ end
86
+
87
+ # Parses a raw cookie string into a hash of <tt>cookie-name => cookie-object</tt>
88
+ # pairs.
89
+ #
90
+ # cookies = CGI::Cookie::parse("raw_cookie_string")
91
+ # # => { "name1" => cookie1, "name2" => cookie2, ... }
92
+ #
93
+ def self.parse(raw_cookie)
94
+ cookies = Hash.new([])
95
+
96
+ if raw_cookie
97
+ raw_cookie.split(/;\s?/).each do |pairs|
98
+ name, value = pairs.split('=',2)
99
+ next unless name and value
100
+ name = CGI::unescape(name)
101
+ unless cookies.has_key?(name)
102
+ cookies[name] = new(name, CGI::unescape(value))
103
+ end
104
+ end
105
+ end
106
+
107
+ cookies
108
+ end
109
+ end # class Cookie
110
+ end
@@ -0,0 +1,22 @@
1
+ require 'cgi'
2
+
3
+ class CGI #:nodoc:
4
+ module QueryExtension
5
+ # Remove the old initialize_query method before redefining it.
6
+ remove_method :initialize_query
7
+
8
+ # Neuter CGI parameter parsing.
9
+ def initialize_query
10
+ # Fix some strange request environments.
11
+ env_table['REQUEST_METHOD'] ||= 'GET'
12
+
13
+ # POST assumes missing Content-Type is application/x-www-form-urlencoded.
14
+ if env_table['CONTENT_TYPE'].blank? && env_table['REQUEST_METHOD'] == 'POST'
15
+ env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
16
+ end
17
+
18
+ @cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
19
+ @params = {}
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,73 @@
1
+ require 'digest/md5'
2
+ require 'cgi/session'
3
+ require 'cgi/session/pstore'
4
+
5
+ class CGI #:nodoc:
6
+ # * Expose the CGI instance to session stores.
7
+ # * Don't require 'digest/md5' whenever a new session id is generated.
8
+ class Session #:nodoc:
9
+ begin
10
+ require 'securerandom'
11
+
12
+ # Generate a 32-character unique id using SecureRandom.
13
+ # This is used to generate session ids but may be reused elsewhere.
14
+ def self.generate_unique_id(constant = nil)
15
+ SecureRandom.hex(16)
16
+ end
17
+ rescue LoadError
18
+ # Generate an 32-character unique id based on a hash of the current time,
19
+ # a random number, the process id, and a constant string. This is used
20
+ # to generate session ids but may be reused elsewhere.
21
+ def self.generate_unique_id(constant = 'foobar')
22
+ md5 = Digest::MD5.new
23
+ now = Time.now
24
+ md5 << now.to_s
25
+ md5 << String(now.usec)
26
+ md5 << String(rand(0))
27
+ md5 << String($$)
28
+ md5 << constant
29
+ md5.hexdigest
30
+ end
31
+ end
32
+
33
+ # Make the CGI instance available to session stores.
34
+ attr_reader :cgi
35
+ attr_reader :dbman
36
+ alias_method :initialize_without_cgi_reader, :initialize
37
+ def initialize(cgi, options = {})
38
+ @cgi = cgi
39
+ initialize_without_cgi_reader(cgi, options)
40
+ end
41
+
42
+ private
43
+ # Create a new session id.
44
+ def create_new_id
45
+ @new_session = true
46
+ self.class.generate_unique_id
47
+ end
48
+
49
+ # * Don't require 'digest/md5' whenever a new session is started.
50
+ class PStore #:nodoc:
51
+ def initialize(session, option={})
52
+ dir = option['tmpdir'] || Dir::tmpdir
53
+ prefix = option['prefix'] || ''
54
+ id = session.session_id
55
+ md5 = Digest::MD5.hexdigest(id)[0,16]
56
+ path = dir+"/"+prefix+md5
57
+ path.untaint
58
+ if File::exist?(path)
59
+ @hash = nil
60
+ else
61
+ unless session.new_session
62
+ raise CGI::Session::NoSession, "uninitialized session"
63
+ end
64
+ @hash = {}
65
+ end
66
+ @p = ::PStore.new(path)
67
+ @p.transaction do |p|
68
+ File.chmod(0600, p.path)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end