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,24 @@
1
+ require 'cgi'
2
+
3
+ module ActionController
4
+ module CgiExt
5
+ # Publicize the CGI's internal input stream so we can lazy-read
6
+ # request.body. Make it writable so we don't have to play $stdin games.
7
+ module Stdinput
8
+ def self.included(base)
9
+ base.class_eval do
10
+ remove_method :stdinput
11
+ attr_accessor :stdinput
12
+ end
13
+
14
+ base.alias_method_chain :initialize, :stdinput
15
+ end
16
+
17
+ def initialize_with_stdinput(type = nil, stdinput = $stdin)
18
+ @stdinput = stdinput
19
+ @stdinput.set_encoding(Encoding::BINARY) if @stdinput.respond_to?(:set_encoding)
20
+ initialize_without_stdinput(type || 'query')
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,223 @@
1
+ require 'action_controller/cgi_ext'
2
+ require 'action_controller/session/cookie_store'
3
+
4
+ module ActionController #:nodoc:
5
+ class Base
6
+ # Process a request extracted from a CGI object and return a response. Pass false as <tt>session_options</tt> to disable
7
+ # sessions (large performance increase if sessions are not needed). The <tt>session_options</tt> are the same as for CGI::Session:
8
+ #
9
+ # * <tt>:database_manager</tt> - standard options are CGI::Session::FileStore, CGI::Session::MemoryStore, and CGI::Session::PStore
10
+ # (default). Additionally, there is CGI::Session::DRbStore and CGI::Session::ActiveRecordStore. Read more about these in
11
+ # lib/action_controller/session.
12
+ # * <tt>:session_key</tt> - the parameter name used for the session id. Defaults to '_session_id'.
13
+ # * <tt>:session_id</tt> - the session id to use. If not provided, then it is retrieved from the +session_key+ cookie, or
14
+ # automatically generated for a new session.
15
+ # * <tt>:new_session</tt> - if true, force creation of a new session. If not set, a new session is only created if none currently
16
+ # exists. If false, a new session is never created, and if none currently exists and the +session_id+ option is not set,
17
+ # an ArgumentError is raised.
18
+ # * <tt>:session_expires</tt> - the time the current session expires, as a Time object. If not set, the session will continue
19
+ # indefinitely.
20
+ # * <tt>:session_domain</tt> - the hostname domain for which this session is valid. If not set, defaults to the hostname of the
21
+ # server.
22
+ # * <tt>:session_secure</tt> - if +true+, this session will only work over HTTPS.
23
+ # * <tt>:session_path</tt> - the path for which this session applies. Defaults to the directory of the CGI script.
24
+ # * <tt>:cookie_only</tt> - if +true+ (the default), session IDs will only be accepted from cookies and not from
25
+ # the query string or POST parameters. This protects against session fixation attacks.
26
+ def self.process_cgi(cgi = CGI.new, session_options = {})
27
+ new.process_cgi(cgi, session_options)
28
+ end
29
+
30
+ def process_cgi(cgi, session_options = {}) #:nodoc:
31
+ process(CgiRequest.new(cgi, session_options), CgiResponse.new(cgi)).out
32
+ end
33
+ end
34
+
35
+ class CgiRequest < AbstractRequest #:nodoc:
36
+ attr_accessor :cgi, :session_options
37
+ class SessionFixationAttempt < StandardError #:nodoc:
38
+ end
39
+
40
+ DEFAULT_SESSION_OPTIONS = {
41
+ :database_manager => CGI::Session::CookieStore, # store data in cookie
42
+ :prefix => "ruby_sess.", # prefix session file names
43
+ :session_path => "/", # available to all paths in app
44
+ :session_key => "_session_id",
45
+ :cookie_only => true
46
+ } unless const_defined?(:DEFAULT_SESSION_OPTIONS)
47
+
48
+ def initialize(cgi, session_options = {})
49
+ @cgi = cgi
50
+ @session_options = session_options
51
+ @env = @cgi.send!(:env_table)
52
+ super()
53
+ end
54
+
55
+ def query_string
56
+ qs = @cgi.query_string if @cgi.respond_to?(:query_string)
57
+ if !qs.blank?
58
+ qs
59
+ else
60
+ super
61
+ end
62
+ end
63
+
64
+ # The request body is an IO input stream. If the RAW_POST_DATA environment
65
+ # variable is already set, wrap it in a StringIO.
66
+ def body
67
+ if raw_post = env['RAW_POST_DATA']
68
+ raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
69
+ StringIO.new(raw_post)
70
+ else
71
+ @cgi.stdinput
72
+ end
73
+ end
74
+
75
+ def query_parameters
76
+ @query_parameters ||= self.class.parse_query_parameters(query_string)
77
+ end
78
+
79
+ def request_parameters
80
+ @request_parameters ||= parse_formatted_request_parameters
81
+ end
82
+
83
+ def cookies
84
+ @cgi.cookies.freeze
85
+ end
86
+
87
+ def host_with_port_without_standard_port_handling
88
+ if forwarded = env["HTTP_X_FORWARDED_HOST"]
89
+ forwarded.split(/,\s?/).last
90
+ elsif http_host = env['HTTP_HOST']
91
+ http_host
92
+ elsif server_name = env['SERVER_NAME']
93
+ server_name
94
+ else
95
+ "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
96
+ end
97
+ end
98
+
99
+ def host
100
+ host_with_port_without_standard_port_handling.sub(/:\d+$/, '')
101
+ end
102
+
103
+ def port
104
+ if host_with_port_without_standard_port_handling =~ /:(\d+)$/
105
+ $1.to_i
106
+ else
107
+ standard_port
108
+ end
109
+ end
110
+
111
+ def session
112
+ unless defined?(@session)
113
+ if @session_options == false
114
+ @session = Hash.new
115
+ else
116
+ stale_session_check! do
117
+ if cookie_only? && query_parameters[session_options_with_string_keys['session_key']]
118
+ raise SessionFixationAttempt
119
+ end
120
+ case value = session_options_with_string_keys['new_session']
121
+ when true
122
+ @session = new_session
123
+ when false
124
+ begin
125
+ @session = CGI::Session.new(@cgi, session_options_with_string_keys)
126
+ # CGI::Session raises ArgumentError if 'new_session' == false
127
+ # and no session cookie or query param is present.
128
+ rescue ArgumentError
129
+ @session = Hash.new
130
+ end
131
+ when nil
132
+ @session = CGI::Session.new(@cgi, session_options_with_string_keys)
133
+ else
134
+ raise ArgumentError, "Invalid new_session option: #{value}"
135
+ end
136
+ @session['__valid_session']
137
+ end
138
+ end
139
+ end
140
+ @session
141
+ end
142
+
143
+ def reset_session
144
+ @session.delete if defined?(@session) && @session.is_a?(CGI::Session)
145
+ @session = new_session
146
+ end
147
+
148
+ def method_missing(method_id, *arguments)
149
+ @cgi.send!(method_id, *arguments) rescue super
150
+ end
151
+
152
+ private
153
+ # Delete an old session if it exists then create a new one.
154
+ def new_session
155
+ if @session_options == false
156
+ Hash.new
157
+ else
158
+ CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
159
+ CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
160
+ end
161
+ end
162
+
163
+ def cookie_only?
164
+ session_options_with_string_keys['cookie_only']
165
+ end
166
+
167
+ def stale_session_check!
168
+ yield
169
+ rescue ArgumentError => argument_error
170
+ if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
171
+ begin
172
+ # Note that the regexp does not allow $1 to end with a ':'
173
+ $1.constantize
174
+ rescue LoadError, NameError => const_error
175
+ raise ActionController::SessionRestoreError, <<-end_msg
176
+ Session contains objects whose class definition isn\'t available.
177
+ Remember to require the classes for all objects kept in the session.
178
+ (Original exception: #{const_error.message} [#{const_error.class}])
179
+ end_msg
180
+ end
181
+
182
+ retry
183
+ else
184
+ raise
185
+ end
186
+ end
187
+
188
+ def session_options_with_string_keys
189
+ @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys
190
+ end
191
+ end
192
+
193
+ class CgiResponse < AbstractResponse #:nodoc:
194
+ def initialize(cgi)
195
+ @cgi = cgi
196
+ super()
197
+ end
198
+
199
+ def out(output = $stdout)
200
+ output.binmode if output.respond_to?(:binmode)
201
+ output.sync = false if output.respond_to?(:sync=)
202
+
203
+ begin
204
+ output.write(@cgi.header(@headers))
205
+
206
+ if @cgi.send!(:env_table)['REQUEST_METHOD'] == 'HEAD'
207
+ return
208
+ elsif @body.respond_to?(:call)
209
+ # Flush the output now in case the @body Proc uses
210
+ # #syswrite.
211
+ output.flush if output.respond_to?(:flush)
212
+ @body.call(self, output)
213
+ else
214
+ output.write(@body)
215
+ end
216
+
217
+ output.flush if output.respond_to?(:flush)
218
+ rescue Errno::EPIPE, Errno::ECONNRESET
219
+ # lost connection to parent process, ignore output
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,166 @@
1
+ module ActionController #:nodoc:
2
+ # Components allow you to call other actions for their rendered response while executing another action. You can either delegate
3
+ # the entire response rendering or you can mix a partial response in with your other content.
4
+ #
5
+ # class WeblogController < ActionController::Base
6
+ # # Performs a method and then lets hello_world output its render
7
+ # def delegate_action
8
+ # do_other_stuff_before_hello_world
9
+ # render_component :controller => "greeter", :action => "hello_world", :params => { :person => "david" }
10
+ # end
11
+ # end
12
+ #
13
+ # class GreeterController < ActionController::Base
14
+ # def hello_world
15
+ # render :text => "#{params[:person]} says, Hello World!"
16
+ # end
17
+ # end
18
+ #
19
+ # The same can be done in a view to do a partial rendering:
20
+ #
21
+ # Let's see a greeting:
22
+ # <%= render_component :controller => "greeter", :action => "hello_world" %>
23
+ #
24
+ # It is also possible to specify the controller as a class constant, bypassing the inflector
25
+ # code to compute the controller class at runtime:
26
+ #
27
+ # <%= render_component :controller => GreeterController, :action => "hello_world" %>
28
+ #
29
+ # == When to use components
30
+ #
31
+ # Components should be used with care. They're significantly slower than simply splitting reusable parts into partials and
32
+ # conceptually more complicated. Don't use components as a way of separating concerns inside a single application. Instead,
33
+ # reserve components to those rare cases where you truly have reusable view and controller elements that can be employed
34
+ # across many applications at once.
35
+ #
36
+ # So to repeat: Components are a special-purpose approach that can often be replaced with better use of partials and filters.
37
+ module Components
38
+ def self.included(base) #:nodoc:
39
+ base.class_eval do
40
+ include InstanceMethods
41
+ extend ClassMethods
42
+ helper HelperMethods
43
+
44
+ # If this controller was instantiated to process a component request,
45
+ # +parent_controller+ points to the instantiator of this controller.
46
+ attr_accessor :parent_controller
47
+
48
+ alias_method_chain :process_cleanup, :components
49
+ alias_method_chain :set_session_options, :components
50
+ alias_method_chain :flash, :components
51
+
52
+ alias_method :component_request?, :parent_controller
53
+ end
54
+ end
55
+
56
+ module ClassMethods
57
+ # Track parent controller to identify component requests
58
+ def process_with_components(request, response, parent_controller = nil) #:nodoc:
59
+ controller = new
60
+ controller.parent_controller = parent_controller
61
+ controller.process(request, response)
62
+ end
63
+ end
64
+
65
+ module HelperMethods
66
+ def render_component(options)
67
+ @controller.send!(:render_component_as_string, options)
68
+ end
69
+ end
70
+
71
+ module InstanceMethods
72
+ # Extracts the action_name from the request parameters and performs that action.
73
+ def process_with_components(request, response, method = :perform_action, *arguments) #:nodoc:
74
+ flash.discard if component_request?
75
+ process_without_components(request, response, method, *arguments)
76
+ end
77
+
78
+ protected
79
+ # Renders the component specified as the response for the current method
80
+ def render_component(options) #:doc:
81
+ component_logging(options) do
82
+ render_for_text(component_response(options, true).body, response.headers["Status"])
83
+ end
84
+ end
85
+
86
+ # Returns the component response as a string
87
+ def render_component_as_string(options) #:doc:
88
+ component_logging(options) do
89
+ response = component_response(options, false)
90
+
91
+ if redirected = response.redirected_to
92
+ render_component_as_string(redirected)
93
+ else
94
+ response.body
95
+ end
96
+ end
97
+ end
98
+
99
+ def flash_with_components(refresh = false) #:nodoc:
100
+ if !defined?(@_flash) || refresh
101
+ @_flash =
102
+ if defined?(@parent_controller)
103
+ @parent_controller.flash
104
+ else
105
+ flash_without_components
106
+ end
107
+ end
108
+ @_flash
109
+ end
110
+
111
+ private
112
+ def component_response(options, reuse_response)
113
+ klass = component_class(options)
114
+ request = request_for_component(klass.controller_name, options)
115
+ new_response = reuse_response ? response : response.dup
116
+
117
+ klass.process_with_components(request, new_response, self)
118
+ end
119
+
120
+ # determine the controller class for the component request
121
+ def component_class(options)
122
+ if controller = options[:controller]
123
+ controller.is_a?(Class) ? controller : "#{controller.camelize}Controller".constantize
124
+ else
125
+ self.class
126
+ end
127
+ end
128
+
129
+ # Create a new request object based on the current request.
130
+ # The new request inherits the session from the current request,
131
+ # bypassing any session options set for the component controller's class
132
+ def request_for_component(controller_name, options)
133
+ new_request = request.dup
134
+ new_request.session = request.session
135
+
136
+ new_request.instance_variable_set(
137
+ :@parameters,
138
+ (options[:params] || {}).with_indifferent_access.update(
139
+ "controller" => controller_name, "action" => options[:action], "id" => options[:id]
140
+ )
141
+ )
142
+
143
+ new_request
144
+ end
145
+
146
+ def component_logging(options)
147
+ if logger
148
+ logger.info "Start rendering component (#{options.inspect}): "
149
+ result = yield
150
+ logger.info "\n\nEnd of component rendering"
151
+ result
152
+ else
153
+ yield
154
+ end
155
+ end
156
+
157
+ def set_session_options_with_components(request)
158
+ set_session_options_without_components(request) unless component_request?
159
+ end
160
+
161
+ def process_cleanup_with_components
162
+ process_cleanup_without_components unless component_request?
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,96 @@
1
+ module ActionController #:nodoc:
2
+ # Cookies are read and written through ActionController#cookies.
3
+ #
4
+ # The cookies being read are the ones received along with the request, the cookies
5
+ # being written will be sent out with the response. Reading a cookie does not get
6
+ # the cookie object itself back, just the value it holds.
7
+ #
8
+ # Examples for writing:
9
+ #
10
+ # # Sets a simple session cookie.
11
+ # cookies[:user_name] = "david"
12
+ #
13
+ # # Sets a cookie that expires in 1 hour.
14
+ # cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
15
+ #
16
+ # Examples for reading:
17
+ #
18
+ # cookies[:user_name] # => "david"
19
+ # cookies.size # => 2
20
+ #
21
+ # Example for deleting:
22
+ #
23
+ # cookies.delete :user_name
24
+ #
25
+ # The option symbols for setting cookies are:
26
+ #
27
+ # * <tt>:value</tt> - The cookie's value or list of values (as an array).
28
+ # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
29
+ # of the application.
30
+ # * <tt>:domain</tt> - The domain for which this cookie applies.
31
+ # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
32
+ # * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
33
+ # Default is +false+.
34
+ # * <tt>:http_only</tt> - Whether this cookie is accessible via scripting or
35
+ # only HTTP. Defaults to +false+.
36
+ module Cookies
37
+ def self.included(base)
38
+ base.helper_method :cookies
39
+ end
40
+
41
+ protected
42
+ # Returns the cookie container, which operates as described above.
43
+ def cookies
44
+ CookieJar.new(self)
45
+ end
46
+ end
47
+
48
+ class CookieJar < Hash #:nodoc:
49
+ def initialize(controller)
50
+ @controller, @cookies = controller, controller.request.cookies
51
+ super()
52
+ update(@cookies)
53
+ end
54
+
55
+ # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
56
+ def [](name)
57
+ cookie = @cookies[name.to_s]
58
+ if cookie && cookie.respond_to?(:value)
59
+ cookie.size > 1 ? cookie.value : cookie.value[0]
60
+ end
61
+ end
62
+
63
+ # Sets the cookie named +name+. The second argument may be the very cookie
64
+ # value, or a hash of options as documented above.
65
+ def []=(name, options)
66
+ if options.is_a?(Hash)
67
+ options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
68
+ options["name"] = name.to_s
69
+ else
70
+ options = { "name" => name.to_s, "value" => options }
71
+ end
72
+
73
+ set_cookie(options)
74
+ end
75
+
76
+ # Removes the cookie on the client machine by setting the value to an empty string
77
+ # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
78
+ # an options hash to delete cookies with extra data such as a <tt>:path</tt>.
79
+ def delete(name, options = {})
80
+ options.stringify_keys!
81
+ set_cookie(options.merge("name" => name.to_s, "value" => "", "expires" => Time.at(0)))
82
+ end
83
+
84
+ private
85
+ # Builds a CGI::Cookie object and adds the cookie to the response headers.
86
+ #
87
+ # The path of the cookie defaults to "/" if there's none in +options+, and
88
+ # everything is passed to the CGI::Cookie constructor.
89
+ def set_cookie(options) #:doc:
90
+ options["path"] = "/" unless options["path"]
91
+ cookie = CGI::Cookie.new(options)
92
+ @controller.logger.info "Cookie set: #{cookie}" unless @controller.logger.nil?
93
+ @controller.response.headers["cookie"] << cookie
94
+ end
95
+ end
96
+ end