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,46 @@
1
+
2
+ class Object
3
+ def to_param
4
+ to_s
5
+ end
6
+ end
7
+
8
+ class TrueClass
9
+ def to_param
10
+ self
11
+ end
12
+ end
13
+
14
+ class FalseClass
15
+ def to_param
16
+ self
17
+ end
18
+ end
19
+
20
+ class NilClass
21
+ def to_param
22
+ self
23
+ end
24
+ end
25
+
26
+ class Regexp #:nodoc:
27
+ def number_of_captures
28
+ Regexp.new("|#{source}").match('').captures.length
29
+ end
30
+
31
+ class << self
32
+ def optionalize(pattern)
33
+ case unoptionalize(pattern)
34
+ when /\A(.|\(.*\))\Z/ then "#{pattern}?"
35
+ else "(?:#{pattern})?"
36
+ end
37
+ end
38
+
39
+ def unoptionalize(pattern)
40
+ [/\A\(\?:(.*)\)\?\Z/, /\A(.|\(.*\))\?\Z/].each do |regexp|
41
+ return $1 if regexp =~ pattern
42
+ end
43
+ return pattern
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,283 @@
1
+ module ActionController
2
+ module Routing
3
+ class Segment #:nodoc:
4
+ RESERVED_PCHAR = ':@&=+$,;'
5
+ UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze
6
+
7
+ attr_accessor :is_optional
8
+ alias_method :optional?, :is_optional
9
+
10
+ def initialize
11
+ self.is_optional = false
12
+ end
13
+
14
+ def extraction_code
15
+ nil
16
+ end
17
+
18
+ # Continue generating string for the prior segments.
19
+ def continue_string_structure(prior_segments)
20
+ if prior_segments.empty?
21
+ interpolation_statement(prior_segments)
22
+ else
23
+ new_priors = prior_segments[0..-2]
24
+ prior_segments.last.string_structure(new_priors)
25
+ end
26
+ end
27
+
28
+ def interpolation_chunk
29
+ URI.escape(value, UNSAFE_PCHAR)
30
+ end
31
+
32
+ # Return a string interpolation statement for this segment and those before it.
33
+ def interpolation_statement(prior_segments)
34
+ chunks = prior_segments.collect { |s| s.interpolation_chunk }
35
+ chunks << interpolation_chunk
36
+ "\"#{chunks * ''}\"#{all_optionals_available_condition(prior_segments)}"
37
+ end
38
+
39
+ def string_structure(prior_segments)
40
+ optional? ? continue_string_structure(prior_segments) : interpolation_statement(prior_segments)
41
+ end
42
+
43
+ # Return an if condition that is true if all the prior segments can be generated.
44
+ # If there are no optional segments before this one, then nil is returned.
45
+ def all_optionals_available_condition(prior_segments)
46
+ optional_locals = prior_segments.collect { |s| s.local_name if s.optional? && s.respond_to?(:local_name) }.compact
47
+ optional_locals.empty? ? nil : " if #{optional_locals * ' && '}"
48
+ end
49
+
50
+ # Recognition
51
+
52
+ def match_extraction(next_capture)
53
+ nil
54
+ end
55
+
56
+ # Warning
57
+
58
+ # Returns true if this segment is optional? because of a default. If so, then
59
+ # no warning will be emitted regarding this segment.
60
+ def optionality_implied?
61
+ false
62
+ end
63
+ end
64
+
65
+ class StaticSegment < Segment #:nodoc:
66
+ attr_accessor :value, :raw
67
+ alias_method :raw?, :raw
68
+
69
+ def initialize(value = nil)
70
+ super()
71
+ self.value = value
72
+ end
73
+
74
+ def interpolation_chunk
75
+ raw? ? value : super
76
+ end
77
+
78
+ def regexp_chunk
79
+ chunk = Regexp.escape(value)
80
+ optional? ? Regexp.optionalize(chunk) : chunk
81
+ end
82
+
83
+ def build_pattern(pattern)
84
+ escaped = Regexp.escape(value)
85
+ if optional? && ! pattern.empty?
86
+ "(?:#{Regexp.optionalize escaped}\\Z|#{escaped}#{Regexp.unoptionalize pattern})"
87
+ elsif optional?
88
+ Regexp.optionalize escaped
89
+ else
90
+ escaped + pattern
91
+ end
92
+ end
93
+
94
+ def to_s
95
+ value
96
+ end
97
+ end
98
+
99
+ class DividerSegment < StaticSegment #:nodoc:
100
+ def initialize(value = nil)
101
+ super(value)
102
+ self.raw = true
103
+ self.is_optional = true
104
+ end
105
+
106
+ def optionality_implied?
107
+ true
108
+ end
109
+ end
110
+
111
+ class DynamicSegment < Segment #:nodoc:
112
+ attr_accessor :key, :default, :regexp
113
+
114
+ def initialize(key = nil, options = {})
115
+ super()
116
+ self.key = key
117
+ self.default = options[:default] if options.key? :default
118
+ self.is_optional = true if options[:optional] || options.key?(:default)
119
+ end
120
+
121
+ def to_s
122
+ ":#{key}"
123
+ end
124
+
125
+ # The local variable name that the value of this segment will be extracted to.
126
+ def local_name
127
+ "#{key}_value"
128
+ end
129
+
130
+ def extract_value
131
+ "#{local_name} = hash[:#{key}] && hash[:#{key}].to_param #{"|| #{default.inspect}" if default}"
132
+ end
133
+ def value_check
134
+ if default # Then we know it won't be nil
135
+ "#{value_regexp.inspect} =~ #{local_name}" if regexp
136
+ elsif optional?
137
+ # If we have a regexp check that the value is not given, or that it matches.
138
+ # If we have no regexp, return nil since we do not require a condition.
139
+ "#{local_name}.nil? || #{value_regexp.inspect} =~ #{local_name}" if regexp
140
+ else # Then it must be present, and if we have a regexp, it must match too.
141
+ "#{local_name} #{"&& #{value_regexp.inspect} =~ #{local_name}" if regexp}"
142
+ end
143
+ end
144
+ def expiry_statement
145
+ "expired, hash = true, options if !expired && expire_on[:#{key}]"
146
+ end
147
+
148
+ def extraction_code
149
+ s = extract_value
150
+ vc = value_check
151
+ s << "\nreturn [nil,nil] unless #{vc}" if vc
152
+ s << "\n#{expiry_statement}"
153
+ end
154
+
155
+ def interpolation_chunk(value_code = "#{local_name}")
156
+ "\#{URI.escape(#{value_code}.to_s, ActionController::Routing::Segment::UNSAFE_PCHAR)}"
157
+ end
158
+
159
+ def string_structure(prior_segments)
160
+ if optional? # We have a conditional to do...
161
+ # If we should not appear in the url, just write the code for the prior
162
+ # segments. This occurs if our value is the default value, or, if we are
163
+ # optional, if we have nil as our value.
164
+ "if #{local_name} == #{default.inspect}\n" +
165
+ continue_string_structure(prior_segments) +
166
+ "\nelse\n" + # Otherwise, write the code up to here
167
+ "#{interpolation_statement(prior_segments)}\nend"
168
+ else
169
+ interpolation_statement(prior_segments)
170
+ end
171
+ end
172
+
173
+ def value_regexp
174
+ Regexp.new "\\A#{regexp.to_s}\\Z" if regexp
175
+ end
176
+
177
+ def regexp_chunk
178
+ if regexp
179
+ if regexp_has_modifiers?
180
+ "(#{regexp.to_s})"
181
+ else
182
+ "(#{regexp.source})"
183
+ end
184
+ else
185
+ "([^#{Routing::SEPARATORS.join}]+)"
186
+ end
187
+ end
188
+
189
+ def build_pattern(pattern)
190
+ chunk = regexp_chunk
191
+ chunk = "(#{chunk})" if Regexp.new(chunk).number_of_captures == 0
192
+ pattern = "#{chunk}#{pattern}"
193
+ optional? ? Regexp.optionalize(pattern) : pattern
194
+ end
195
+
196
+ def match_extraction(next_capture)
197
+ # All non code-related keys (such as :id, :slug) are URI-unescaped as
198
+ # path parameters.
199
+ default_value = default ? default.inspect : nil
200
+ %[
201
+ value = if (m = match[#{next_capture}])
202
+ URI.unescape(m)
203
+ else
204
+ #{default_value}
205
+ end
206
+ params[:#{key}] = value if value
207
+ ]
208
+ end
209
+
210
+ def optionality_implied?
211
+ [:action, :id].include? key
212
+ end
213
+
214
+ def regexp_has_modifiers?
215
+ regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0
216
+ end
217
+
218
+ end
219
+
220
+ class ControllerSegment < DynamicSegment #:nodoc:
221
+ def regexp_chunk
222
+ possible_names = Routing.possible_controllers.collect { |name| Regexp.escape name }
223
+ "(?i-:(#{(regexp || Regexp.union(*possible_names)).source}))"
224
+ end
225
+
226
+ # Don't URI.escape the controller name since it may contain slashes.
227
+ def interpolation_chunk(value_code = "#{local_name}")
228
+ "\#{#{value_code}.to_s}"
229
+ end
230
+
231
+ # Make sure controller names like Admin/Content are correctly normalized to
232
+ # admin/content
233
+ def extract_value
234
+ "#{local_name} = (hash[:#{key}] #{"|| #{default.inspect}" if default}).downcase"
235
+ end
236
+
237
+ def match_extraction(next_capture)
238
+ if default
239
+ "params[:#{key}] = match[#{next_capture}] ? match[#{next_capture}].downcase : '#{default}'"
240
+ else
241
+ "params[:#{key}] = match[#{next_capture}].downcase if match[#{next_capture}]"
242
+ end
243
+ end
244
+ end
245
+
246
+ class PathSegment < DynamicSegment #:nodoc:
247
+ def interpolation_chunk(value_code = "#{local_name}")
248
+ "\#{#{value_code}}"
249
+ end
250
+
251
+ def extract_value
252
+ "#{local_name} = hash[:#{key}] && Array(hash[:#{key}]).collect { |path_component| URI.escape(path_component.to_param, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}"
253
+ end
254
+
255
+ def default
256
+ ''
257
+ end
258
+
259
+ def default=(path)
260
+ raise RoutingError, "paths cannot have non-empty default values" unless path.blank?
261
+ end
262
+
263
+ def match_extraction(next_capture)
264
+ "params[:#{key}] = PathSegment::Result.new_escaped((match[#{next_capture}]#{" || " + default.inspect if default}).split('/'))#{" if match[" + next_capture + "]" if !default}"
265
+ end
266
+
267
+ def regexp_chunk
268
+ regexp || "(.*)"
269
+ end
270
+
271
+ def optionality_implied?
272
+ true
273
+ end
274
+
275
+ class Result < ::Array #:nodoc:
276
+ def to_s() join '/' end
277
+ def self.new_escaped(strings)
278
+ new strings.collect {|str| URI.unescape str}
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
@@ -0,0 +1,340 @@
1
+ require 'cgi'
2
+ require 'cgi/session'
3
+ require 'digest/md5'
4
+
5
+ class CGI
6
+ class Session
7
+ attr_reader :data
8
+
9
+ # Return this session's underlying Session instance. Useful for the DB-backed session stores.
10
+ def model
11
+ @dbman.model if @dbman
12
+ end
13
+
14
+
15
+ # A session store backed by an Active Record class. A default class is
16
+ # provided, but any object duck-typing to an Active Record Session class
17
+ # with text +session_id+ and +data+ attributes is sufficient.
18
+ #
19
+ # The default assumes a +sessions+ tables with columns:
20
+ # +id+ (numeric primary key),
21
+ # +session_id+ (text, or longtext if your session data exceeds 65K), and
22
+ # +data+ (text or longtext; careful if your session data exceeds 65KB).
23
+ # The +session_id+ column should always be indexed for speedy lookups.
24
+ # Session data is marshaled to the +data+ column in Base64 format.
25
+ # If the data you write is larger than the column's size limit,
26
+ # ActionController::SessionOverflowError will be raised.
27
+ #
28
+ # You may configure the table name, primary key, and data column.
29
+ # For example, at the end of <tt>config/environment.rb</tt>:
30
+ # CGI::Session::ActiveRecordStore::Session.table_name = 'legacy_session_table'
31
+ # CGI::Session::ActiveRecordStore::Session.primary_key = 'session_id'
32
+ # CGI::Session::ActiveRecordStore::Session.data_column_name = 'legacy_session_data'
33
+ # Note that setting the primary key to the +session_id+ frees you from
34
+ # having a separate +id+ column if you don't want it. However, you must
35
+ # set <tt>session.model.id = session.session_id</tt> by hand! A before filter
36
+ # on ApplicationController is a good place.
37
+ #
38
+ # Since the default class is a simple Active Record, you get timestamps
39
+ # for free if you add +created_at+ and +updated_at+ datetime columns to
40
+ # the +sessions+ table, making periodic session expiration a snap.
41
+ #
42
+ # You may provide your own session class implementation, whether a
43
+ # feature-packed Active Record or a bare-metal high-performance SQL
44
+ # store, by setting
45
+ # CGI::Session::ActiveRecordStore.session_class = MySessionClass
46
+ # You must implement these methods:
47
+ # self.find_by_session_id(session_id)
48
+ # initialize(hash_of_session_id_and_data)
49
+ # attr_reader :session_id
50
+ # attr_accessor :data
51
+ # save
52
+ # destroy
53
+ #
54
+ # The example SqlBypass class is a generic SQL session store. You may
55
+ # use it as a basis for high-performance database-specific stores.
56
+ class ActiveRecordStore
57
+ # The default Active Record class.
58
+ class Session < ActiveRecord::Base
59
+ # Customizable data column name. Defaults to 'data'.
60
+ cattr_accessor :data_column_name
61
+ self.data_column_name = 'data'
62
+
63
+ before_save :marshal_data!
64
+ before_save :raise_on_session_data_overflow!
65
+
66
+ class << self
67
+ # Don't try to reload ARStore::Session in dev mode.
68
+ def reloadable? #:nodoc:
69
+ false
70
+ end
71
+
72
+ def data_column_size_limit
73
+ @data_column_size_limit ||= columns_hash[@@data_column_name].limit
74
+ end
75
+
76
+ # Hook to set up sessid compatibility.
77
+ def find_by_session_id(session_id)
78
+ setup_sessid_compatibility!
79
+ find_by_session_id(session_id)
80
+ end
81
+
82
+ def marshal(data) ActiveSupport::Base64.encode64(Marshal.dump(data)) if data end
83
+ def unmarshal(data) Marshal.load(ActiveSupport::Base64.decode64(data)) if data end
84
+
85
+ def create_table!
86
+ connection.execute <<-end_sql
87
+ CREATE TABLE #{table_name} (
88
+ id INTEGER PRIMARY KEY,
89
+ #{connection.quote_column_name('session_id')} TEXT UNIQUE,
90
+ #{connection.quote_column_name(@@data_column_name)} TEXT(255)
91
+ )
92
+ end_sql
93
+ end
94
+
95
+ def drop_table!
96
+ connection.execute "DROP TABLE #{table_name}"
97
+ end
98
+
99
+ private
100
+ # Compatibility with tables using sessid instead of session_id.
101
+ def setup_sessid_compatibility!
102
+ # Reset column info since it may be stale.
103
+ reset_column_information
104
+ if columns_hash['sessid']
105
+ def self.find_by_session_id(*args)
106
+ find_by_sessid(*args)
107
+ end
108
+
109
+ define_method(:session_id) { sessid }
110
+ define_method(:session_id=) { |session_id| self.sessid = session_id }
111
+ else
112
+ def self.find_by_session_id(session_id)
113
+ find :first, :conditions => ["session_id #{attribute_condition(session_id)}", session_id]
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ # Lazy-unmarshal session state.
120
+ def data
121
+ @data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
122
+ end
123
+
124
+ attr_writer :data
125
+
126
+ # Has the session been loaded yet?
127
+ def loaded?
128
+ !! @data
129
+ end
130
+
131
+ private
132
+
133
+ def marshal_data!
134
+ return false if !loaded?
135
+ write_attribute(@@data_column_name, self.class.marshal(self.data))
136
+ end
137
+
138
+ # Ensures that the data about to be stored in the database is not
139
+ # larger than the data storage column. Raises
140
+ # ActionController::SessionOverflowError.
141
+ def raise_on_session_data_overflow!
142
+ return false if !loaded?
143
+ limit = self.class.data_column_size_limit
144
+ if loaded? and limit and read_attribute(@@data_column_name).size > limit
145
+ raise ActionController::SessionOverflowError
146
+ end
147
+ end
148
+ end
149
+
150
+ # A barebones session store which duck-types with the default session
151
+ # store but bypasses Active Record and issues SQL directly. This is
152
+ # an example session model class meant as a basis for your own classes.
153
+ #
154
+ # The database connection, table name, and session id and data columns
155
+ # are configurable class attributes. Marshaling and unmarshaling
156
+ # are implemented as class methods that you may override. By default,
157
+ # marshaling data is
158
+ #
159
+ # ActiveSupport::Base64.encode64(Marshal.dump(data))
160
+ #
161
+ # and unmarshaling data is
162
+ #
163
+ # Marshal.load(ActiveSupport::Base64.decode64(data))
164
+ #
165
+ # This marshaling behavior is intended to store the widest range of
166
+ # binary session data in a +text+ column. For higher performance,
167
+ # store in a +blob+ column instead and forgo the Base64 encoding.
168
+ class SqlBypass
169
+ # Use the ActiveRecord::Base.connection by default.
170
+ cattr_accessor :connection
171
+
172
+ # The table name defaults to 'sessions'.
173
+ cattr_accessor :table_name
174
+ @@table_name = 'sessions'
175
+
176
+ # The session id field defaults to 'session_id'.
177
+ cattr_accessor :session_id_column
178
+ @@session_id_column = 'session_id'
179
+
180
+ # The data field defaults to 'data'.
181
+ cattr_accessor :data_column
182
+ @@data_column = 'data'
183
+
184
+ class << self
185
+
186
+ def connection
187
+ @@connection ||= ActiveRecord::Base.connection
188
+ end
189
+
190
+ # Look up a session by id and unmarshal its data if found.
191
+ def find_by_session_id(session_id)
192
+ if record = @@connection.select_one("SELECT * FROM #{@@table_name} WHERE #{@@session_id_column}=#{@@connection.quote(session_id)}")
193
+ new(:session_id => session_id, :marshaled_data => record['data'])
194
+ end
195
+ end
196
+
197
+ def marshal(data) ActiveSupport::Base64.encode64(Marshal.dump(data)) if data end
198
+ def unmarshal(data) Marshal.load(ActiveSupport::Base64.decode64(data)) if data end
199
+
200
+ def create_table!
201
+ @@connection.execute <<-end_sql
202
+ CREATE TABLE #{table_name} (
203
+ id INTEGER PRIMARY KEY,
204
+ #{@@connection.quote_column_name(session_id_column)} TEXT UNIQUE,
205
+ #{@@connection.quote_column_name(data_column)} TEXT
206
+ )
207
+ end_sql
208
+ end
209
+
210
+ def drop_table!
211
+ @@connection.execute "DROP TABLE #{table_name}"
212
+ end
213
+ end
214
+
215
+ attr_reader :session_id
216
+ attr_writer :data
217
+
218
+ # Look for normal and marshaled data, self.find_by_session_id's way of
219
+ # telling us to postpone unmarshaling until the data is requested.
220
+ # We need to handle a normal data attribute in case of a new record.
221
+ def initialize(attributes)
222
+ @session_id, @data, @marshaled_data = attributes[:session_id], attributes[:data], attributes[:marshaled_data]
223
+ @new_record = @marshaled_data.nil?
224
+ end
225
+
226
+ def new_record?
227
+ @new_record
228
+ end
229
+
230
+ # Lazy-unmarshal session state.
231
+ def data
232
+ unless @data
233
+ if @marshaled_data
234
+ @data, @marshaled_data = self.class.unmarshal(@marshaled_data) || {}, nil
235
+ else
236
+ @data = {}
237
+ end
238
+ end
239
+ @data
240
+ end
241
+
242
+ def loaded?
243
+ !! @data
244
+ end
245
+
246
+ def save
247
+ return false if !loaded?
248
+ marshaled_data = self.class.marshal(data)
249
+
250
+ if @new_record
251
+ @new_record = false
252
+ @@connection.update <<-end_sql, 'Create session'
253
+ INSERT INTO #{@@table_name} (
254
+ #{@@connection.quote_column_name(@@session_id_column)},
255
+ #{@@connection.quote_column_name(@@data_column)} )
256
+ VALUES (
257
+ #{@@connection.quote(session_id)},
258
+ #{@@connection.quote(marshaled_data)} )
259
+ end_sql
260
+ else
261
+ @@connection.update <<-end_sql, 'Update session'
262
+ UPDATE #{@@table_name}
263
+ SET #{@@connection.quote_column_name(@@data_column)}=#{@@connection.quote(marshaled_data)}
264
+ WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
265
+ end_sql
266
+ end
267
+ end
268
+
269
+ def destroy
270
+ unless @new_record
271
+ @@connection.delete <<-end_sql, 'Destroy session'
272
+ DELETE FROM #{@@table_name}
273
+ WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
274
+ end_sql
275
+ end
276
+ end
277
+ end
278
+
279
+
280
+ # The class used for session storage. Defaults to
281
+ # CGI::Session::ActiveRecordStore::Session.
282
+ cattr_accessor :session_class
283
+ self.session_class = Session
284
+
285
+ # Find or instantiate a session given a CGI::Session.
286
+ def initialize(session, option = nil)
287
+ session_id = session.session_id
288
+ unless @session = ActiveRecord::Base.silence { @@session_class.find_by_session_id(session_id) }
289
+ unless session.new_session
290
+ raise CGI::Session::NoSession, 'uninitialized session'
291
+ end
292
+ @session = @@session_class.new(:session_id => session_id, :data => {})
293
+ # session saving can be lazy again, because of improved component implementation
294
+ # therefore next line gets commented out:
295
+ # @session.save
296
+ end
297
+ end
298
+
299
+ # Access the underlying session model.
300
+ def model
301
+ @session
302
+ end
303
+
304
+ # Restore session state. The session model handles unmarshaling.
305
+ def restore
306
+ if @session
307
+ @session.data
308
+ end
309
+ end
310
+
311
+ # Save session store.
312
+ def update
313
+ if @session
314
+ ActiveRecord::Base.silence { @session.save }
315
+ end
316
+ end
317
+
318
+ # Save and close the session store.
319
+ def close
320
+ if @session
321
+ update
322
+ @session = nil
323
+ end
324
+ end
325
+
326
+ # Delete and close the session store.
327
+ def delete
328
+ if @session
329
+ ActiveRecord::Base.silence { @session.destroy }
330
+ @session = nil
331
+ end
332
+ end
333
+
334
+ protected
335
+ def logger
336
+ ActionController::Base.logger rescue nil
337
+ end
338
+ end
339
+ end
340
+ end