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,162 @@
1
+ module ActionController
2
+ # Dispatches requests to the appropriate controller and takes care of
3
+ # reloading the app after each request when Dependencies.load? is true.
4
+ class Dispatcher
5
+ @@guard = Mutex.new
6
+
7
+ class << self
8
+ def define_dispatcher_callbacks(cache_classes)
9
+ unless cache_classes
10
+ # Development mode callbacks
11
+ before_dispatch :reload_application
12
+ after_dispatch :cleanup_application
13
+ end
14
+
15
+ # Common callbacks
16
+ to_prepare :load_application_controller do
17
+ begin
18
+ require_dependency 'application' unless defined?(::ApplicationController)
19
+ rescue LoadError => error
20
+ raise unless error.message =~ /application\.rb/
21
+ end
22
+ end
23
+
24
+ if defined?(ActiveRecord)
25
+ before_dispatch { ActiveRecord::Base.verify_active_connections! }
26
+ to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
27
+ end
28
+
29
+ after_dispatch :flush_logger if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:flush)
30
+ end
31
+
32
+ # Backward-compatible class method takes CGI-specific args. Deprecated
33
+ # in favor of Dispatcher.new(output, request, response).dispatch.
34
+ def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
35
+ new(output).dispatch_cgi(cgi, session_options)
36
+ end
37
+
38
+ # Add a preparation callback. Preparation callbacks are run before every
39
+ # request in development mode, and before the first request in production
40
+ # mode.
41
+ #
42
+ # An optional identifier may be supplied for the callback. If provided,
43
+ # to_prepare may be called again with the same identifier to replace the
44
+ # existing callback. Passing an identifier is a suggested practice if the
45
+ # code adding a preparation block may be reloaded.
46
+ def to_prepare(identifier = nil, &block)
47
+ @prepare_dispatch_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
48
+ callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
49
+ @prepare_dispatch_callbacks | callback
50
+ end
51
+
52
+ # If the block raises, send status code as a last-ditch response.
53
+ def failsafe_response(fallback_output, status, originating_exception = nil)
54
+ yield
55
+ rescue Exception => exception
56
+ begin
57
+ log_failsafe_exception(status, originating_exception || exception)
58
+ body = failsafe_response_body(status)
59
+ fallback_output.write "Status: #{status}\r\nContent-Type: text/html\r\n\r\n#{body}"
60
+ nil
61
+ rescue Exception => failsafe_error # Logger or IO errors
62
+ $stderr.puts "Error during failsafe response: #{failsafe_error}"
63
+ $stderr.puts "(originally #{originating_exception})" if originating_exception
64
+ end
65
+ end
66
+
67
+ private
68
+ def failsafe_response_body(status)
69
+ error_path = "#{error_file_path}/#{status.to_s[0..3]}.html"
70
+
71
+ if File.exist?(error_path)
72
+ File.read(error_path)
73
+ else
74
+ "<html><body><h1>#{status}</h1></body></html>"
75
+ end
76
+ end
77
+
78
+ def log_failsafe_exception(status, exception)
79
+ message = "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: #{status}\n"
80
+ message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception
81
+ failsafe_logger.fatal message
82
+ end
83
+
84
+ def failsafe_logger
85
+ if defined?(::RAILS_DEFAULT_LOGGER) && !::RAILS_DEFAULT_LOGGER.nil?
86
+ ::RAILS_DEFAULT_LOGGER
87
+ else
88
+ Logger.new($stderr)
89
+ end
90
+ end
91
+ end
92
+
93
+ cattr_accessor :error_file_path
94
+ self.error_file_path = Rails.public_path if defined?(Rails.public_path)
95
+
96
+ include ActiveSupport::Callbacks
97
+ define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
98
+
99
+ def initialize(output, request = nil, response = nil)
100
+ @output, @request, @response = output, request, response
101
+ end
102
+
103
+ def dispatch
104
+ @@guard.synchronize do
105
+ begin
106
+ run_callbacks :before_dispatch
107
+ handle_request
108
+ rescue Exception => exception
109
+ failsafe_rescue exception
110
+ ensure
111
+ run_callbacks :after_dispatch, :enumerator => :reverse_each
112
+ end
113
+ end
114
+ end
115
+
116
+ def dispatch_cgi(cgi, session_options)
117
+ if cgi ||= self.class.failsafe_response(@output, '400 Bad Request') { CGI.new }
118
+ @request = CgiRequest.new(cgi, session_options)
119
+ @response = CgiResponse.new(cgi)
120
+ dispatch
121
+ end
122
+ rescue Exception => exception
123
+ failsafe_rescue exception
124
+ end
125
+
126
+ def reload_application
127
+ # Run prepare callbacks before every request in development mode
128
+ run_callbacks :prepare_dispatch
129
+
130
+ Routing::Routes.reload
131
+ ActionView::TemplateFinder.reload! unless ActionView::Base.cache_template_loading
132
+ end
133
+
134
+ # Cleanup the application by clearing out loaded classes so they can
135
+ # be reloaded on the next request without restarting the server.
136
+ def cleanup_application
137
+ ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
138
+ ActiveSupport::Dependencies.clear
139
+ ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
140
+ end
141
+
142
+ def flush_logger
143
+ RAILS_DEFAULT_LOGGER.flush
144
+ end
145
+
146
+ protected
147
+ def handle_request
148
+ @controller = Routing::Routes.recognize(@request)
149
+ @controller.process(@request, @response).out(@output)
150
+ end
151
+
152
+ def failsafe_rescue(exception)
153
+ self.class.failsafe_response(@output, '500 Internal Server Error', exception) do
154
+ if @controller ||= defined?(::ApplicationController) ? ::ApplicationController : Base
155
+ @controller.process_with_exception(@request, @response, exception).out(@output)
156
+ else
157
+ raise exception
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,642 @@
1
+ module ActionController #:nodoc:
2
+ module Filters #:nodoc:
3
+ def self.included(base)
4
+ base.class_eval do
5
+ extend ClassMethods
6
+ include ActionController::Filters::InstanceMethods
7
+ end
8
+ end
9
+
10
+ class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
11
+ def append_filter_to_chain(filters, filter_type, &block)
12
+ pos = find_filter_append_position(filters, filter_type)
13
+ update_filter_chain(filters, filter_type, pos, &block)
14
+ end
15
+
16
+ def prepend_filter_to_chain(filters, filter_type, &block)
17
+ pos = find_filter_prepend_position(filters, filter_type)
18
+ update_filter_chain(filters, filter_type, pos, &block)
19
+ end
20
+
21
+ def create_filters(filters, filter_type, &block)
22
+ filters, conditions = extract_options(filters, &block)
23
+ filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) }
24
+ filters
25
+ end
26
+
27
+ def skip_filter_in_chain(*filters, &test)
28
+ filters, conditions = extract_options(filters)
29
+ filters.each do |filter|
30
+ if callback = find(filter) then delete(callback) end
31
+ end if conditions.empty?
32
+ update_filter_in_chain(filters, :skip => conditions, &test)
33
+ end
34
+
35
+ private
36
+ def update_filter_chain(filters, filter_type, pos, &block)
37
+ new_filters = create_filters(filters, filter_type, &block)
38
+ insert(pos, new_filters).flatten!
39
+ end
40
+
41
+ def find_filter_append_position(filters, filter_type)
42
+ # appending an after filter puts it at the end of the call chain
43
+ # before and around filters go before the first after filter in the chain
44
+ unless filter_type == :after
45
+ each_with_index do |f,i|
46
+ return i if f.after?
47
+ end
48
+ end
49
+ return -1
50
+ end
51
+
52
+ def find_filter_prepend_position(filters, filter_type)
53
+ # prepending a before or around filter puts it at the front of the call chain
54
+ # after filters go before the first after filter in the chain
55
+ if filter_type == :after
56
+ each_with_index do |f,i|
57
+ return i if f.after?
58
+ end
59
+ return -1
60
+ end
61
+ return 0
62
+ end
63
+
64
+ def find_or_create_filter(filter, filter_type, options = {})
65
+ update_filter_in_chain([filter], options)
66
+
67
+ if found_filter = find(filter) { |f| f.type == filter_type }
68
+ found_filter
69
+ else
70
+ filter_kind = case
71
+ when filter.respond_to?(:before) && filter_type == :before
72
+ :before
73
+ when filter.respond_to?(:after) && filter_type == :after
74
+ :after
75
+ else
76
+ :filter
77
+ end
78
+
79
+ case filter_type
80
+ when :before
81
+ BeforeFilter.new(filter_kind, filter, options)
82
+ when :after
83
+ AfterFilter.new(filter_kind, filter, options)
84
+ else
85
+ AroundFilter.new(filter_kind, filter, options)
86
+ end
87
+ end
88
+ end
89
+
90
+ def update_filter_in_chain(filters, options, &test)
91
+ filters.map! { |f| block_given? ? find(f, &test) : find(f) }
92
+ filters.compact!
93
+
94
+ map! do |filter|
95
+ if filters.include?(filter)
96
+ new_filter = filter.dup
97
+ new_filter.options.merge!(options)
98
+ new_filter
99
+ else
100
+ filter
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ class Filter < ActiveSupport::Callbacks::Callback #:nodoc:
107
+ def before?
108
+ self.class == BeforeFilter
109
+ end
110
+
111
+ def after?
112
+ self.class == AfterFilter
113
+ end
114
+
115
+ def around?
116
+ self.class == AroundFilter
117
+ end
118
+
119
+ private
120
+ def should_not_skip?(controller)
121
+ if options[:skip]
122
+ !included_in_action?(controller, options[:skip])
123
+ else
124
+ true
125
+ end
126
+ end
127
+
128
+ def included_in_action?(controller, options)
129
+ if options[:only]
130
+ Array(options[:only]).map(&:to_s).include?(controller.action_name)
131
+ elsif options[:except]
132
+ !Array(options[:except]).map(&:to_s).include?(controller.action_name)
133
+ else
134
+ true
135
+ end
136
+ end
137
+
138
+ def should_run_callback?(controller)
139
+ should_not_skip?(controller) && included_in_action?(controller, options) && super
140
+ end
141
+ end
142
+
143
+ class AroundFilter < Filter #:nodoc:
144
+ def type
145
+ :around
146
+ end
147
+
148
+ def call(controller, &block)
149
+ if should_run_callback?(controller)
150
+ method = filter_responds_to_before_and_after? ? around_proc : self.method
151
+
152
+ # For around_filter do |controller, action|
153
+ if method.is_a?(Proc) && method.arity == 2
154
+ evaluate_method(method, controller, block)
155
+ else
156
+ evaluate_method(method, controller, &block)
157
+ end
158
+ else
159
+ block.call
160
+ end
161
+ end
162
+
163
+ private
164
+ def filter_responds_to_before_and_after?
165
+ method.respond_to?(:before) && method.respond_to?(:after)
166
+ end
167
+
168
+ def around_proc
169
+ Proc.new do |controller, action|
170
+ method.before(controller)
171
+
172
+ if controller.send!(:performed?)
173
+ controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
174
+ else
175
+ begin
176
+ action.call
177
+ ensure
178
+ method.after(controller)
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+ class BeforeFilter < Filter #:nodoc:
186
+ def type
187
+ :before
188
+ end
189
+
190
+ def call(controller, &block)
191
+ super
192
+ if controller.send!(:performed?)
193
+ controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
194
+ end
195
+ end
196
+ end
197
+
198
+ class AfterFilter < Filter #:nodoc:
199
+ def type
200
+ :after
201
+ end
202
+ end
203
+
204
+ # Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do
205
+ # authentication, caching, or auditing before the intended action is performed. Or to do localization or output
206
+ # compression after the action has been performed. Filters have access to the request, response, and all the instance
207
+ # variables set by other filters in the chain or by the action (in the case of after filters).
208
+ #
209
+ # == Filter inheritance
210
+ #
211
+ # Controller inheritance hierarchies share filters downwards, but subclasses can also add or skip filters without
212
+ # affecting the superclass. For example:
213
+ #
214
+ # class BankController < ActionController::Base
215
+ # before_filter :audit
216
+ #
217
+ # private
218
+ # def audit
219
+ # # record the action and parameters in an audit log
220
+ # end
221
+ # end
222
+ #
223
+ # class VaultController < BankController
224
+ # before_filter :verify_credentials
225
+ #
226
+ # private
227
+ # def verify_credentials
228
+ # # make sure the user is allowed into the vault
229
+ # end
230
+ # end
231
+ #
232
+ # Now any actions performed on the BankController will have the audit method called before. On the VaultController,
233
+ # first the audit method is called, then the verify_credentials method. If the audit method renders or redirects, then
234
+ # verify_credentials and the intended action are never called.
235
+ #
236
+ # == Filter types
237
+ #
238
+ # A filter can take one of three forms: method reference (symbol), external class, or inline method (proc). The first
239
+ # is the most common and works by referencing a protected or private method somewhere in the inheritance hierarchy of
240
+ # the controller by use of a symbol. In the bank example above, both BankController and VaultController use this form.
241
+ #
242
+ # Using an external class makes for more easily reused generic filters, such as output compression. External filter classes
243
+ # are implemented by having a static +filter+ method on any class and then passing this class to the filter method. Example:
244
+ #
245
+ # class OutputCompressionFilter
246
+ # def self.filter(controller)
247
+ # controller.response.body = compress(controller.response.body)
248
+ # end
249
+ # end
250
+ #
251
+ # class NewspaperController < ActionController::Base
252
+ # after_filter OutputCompressionFilter
253
+ # end
254
+ #
255
+ # The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can
256
+ # manipulate them as it sees fit.
257
+ #
258
+ # The inline method (using a proc) can be used to quickly do something small that doesn't require a lot of explanation.
259
+ # Or just as a quick test. It works like this:
260
+ #
261
+ # class WeblogController < ActionController::Base
262
+ # before_filter { |controller| head(400) if controller.params["stop_action"] }
263
+ # end
264
+ #
265
+ # As you can see, the block expects to be passed the controller after it has assigned the request to the internal variables.
266
+ # This means that the block has access to both the request and response objects complete with convenience methods for params,
267
+ # session, template, and assigns. Note: The inline method doesn't strictly have to be a block; any object that responds to call
268
+ # and returns 1 or -1 on arity will do (such as a Proc or an Method object).
269
+ #
270
+ # Please note that around_filters function a little differently than the normal before and after filters with regard to filter
271
+ # types. Please see the section dedicated to around_filters below.
272
+ #
273
+ # == Filter chain ordering
274
+ #
275
+ # Using <tt>before_filter</tt> and <tt>after_filter</tt> appends the specified filters to the existing chain. That's usually
276
+ # just fine, but some times you care more about the order in which the filters are executed. When that's the case, you
277
+ # can use <tt>prepend_before_filter</tt> and <tt>prepend_after_filter</tt>. Filters added by these methods will be put at the
278
+ # beginning of their respective chain and executed before the rest. For example:
279
+ #
280
+ # class ShoppingController < ActionController::Base
281
+ # before_filter :verify_open_shop
282
+ #
283
+ # class CheckoutController < ShoppingController
284
+ # prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock
285
+ #
286
+ # The filter chain for the CheckoutController is now <tt>:ensure_items_in_cart, :ensure_items_in_stock,</tt>
287
+ # <tt>:verify_open_shop</tt>. So if either of the ensure filters renders or redirects, we'll never get around to see if the shop
288
+ # is open or not.
289
+ #
290
+ # You may pass multiple filter arguments of each type as well as a filter block.
291
+ # If a block is given, it is treated as the last argument.
292
+ #
293
+ # == Around filters
294
+ #
295
+ # Around filters wrap an action, executing code both before and after.
296
+ # They may be declared as method references, blocks, or objects responding
297
+ # to +filter+ or to both +before+ and +after+.
298
+ #
299
+ # To use a method as an +around_filter+, pass a symbol naming the Ruby method.
300
+ # Yield (or <tt>block.call</tt>) within the method to run the action.
301
+ #
302
+ # around_filter :catch_exceptions
303
+ #
304
+ # private
305
+ # def catch_exceptions
306
+ # yield
307
+ # rescue => exception
308
+ # logger.debug "Caught exception! #{exception}"
309
+ # raise
310
+ # end
311
+ #
312
+ # To use a block as an +around_filter+, pass a block taking as args both
313
+ # the controller and the action block. You can't call yield directly from
314
+ # an +around_filter+ block; explicitly call the action block instead:
315
+ #
316
+ # around_filter do |controller, action|
317
+ # logger.debug "before #{controller.action_name}"
318
+ # action.call
319
+ # logger.debug "after #{controller.action_name}"
320
+ # end
321
+ #
322
+ # To use a filter object with +around_filter+, pass an object responding
323
+ # to <tt>:filter</tt> or both <tt>:before</tt> and <tt>:after</tt>. With a
324
+ # filter method, yield to the block as above:
325
+ #
326
+ # around_filter BenchmarkingFilter
327
+ #
328
+ # class BenchmarkingFilter
329
+ # def self.filter(controller, &block)
330
+ # Benchmark.measure(&block)
331
+ # end
332
+ # end
333
+ #
334
+ # With +before+ and +after+ methods:
335
+ #
336
+ # around_filter Authorizer.new
337
+ #
338
+ # class Authorizer
339
+ # # This will run before the action. Redirecting aborts the action.
340
+ # def before(controller)
341
+ # unless user.authorized?
342
+ # redirect_to(login_url)
343
+ # end
344
+ # end
345
+ #
346
+ # # This will run after the action if and only if before did not render or redirect.
347
+ # def after(controller)
348
+ # end
349
+ # end
350
+ #
351
+ # If the filter has +before+ and +after+ methods, the +before+ method will be
352
+ # called before the action. If +before+ renders or redirects, the filter chain is
353
+ # halted and +after+ will not be run. See Filter Chain Halting below for
354
+ # an example.
355
+ #
356
+ # == Filter chain skipping
357
+ #
358
+ # Declaring a filter on a base class conveniently applies to its subclasses,
359
+ # but sometimes a subclass should skip some of its superclass' filters:
360
+ #
361
+ # class ApplicationController < ActionController::Base
362
+ # before_filter :authenticate
363
+ # around_filter :catch_exceptions
364
+ # end
365
+ #
366
+ # class WeblogController < ApplicationController
367
+ # # Will run the :authenticate and :catch_exceptions filters.
368
+ # end
369
+ #
370
+ # class SignupController < ApplicationController
371
+ # # Skip :authenticate, run :catch_exceptions.
372
+ # skip_before_filter :authenticate
373
+ # end
374
+ #
375
+ # class ProjectsController < ApplicationController
376
+ # # Skip :catch_exceptions, run :authenticate.
377
+ # skip_filter :catch_exceptions
378
+ # end
379
+ #
380
+ # class ClientsController < ApplicationController
381
+ # # Skip :catch_exceptions and :authenticate unless action is index.
382
+ # skip_filter :catch_exceptions, :authenticate, :except => :index
383
+ # end
384
+ #
385
+ # == Filter conditions
386
+ #
387
+ # Filters may be limited to specific actions by declaring the actions to
388
+ # include or exclude. Both options accept single actions
389
+ # (<tt>:only => :index</tt>) or arrays of actions
390
+ # (<tt>:except => [:foo, :bar]</tt>).
391
+ #
392
+ # class Journal < ActionController::Base
393
+ # # Require authentication for edit and delete.
394
+ # before_filter :authorize, :only => [:edit, :delete]
395
+ #
396
+ # # Passing options to a filter with a block.
397
+ # around_filter(:except => :index) do |controller, action_block|
398
+ # results = Profiler.run(&action_block)
399
+ # controller.response.sub! "</body>", "#{results}</body>"
400
+ # end
401
+ #
402
+ # private
403
+ # def authorize
404
+ # # Redirect to login unless authenticated.
405
+ # end
406
+ # end
407
+ #
408
+ # == Filter Chain Halting
409
+ #
410
+ # <tt>before_filter</tt> and <tt>around_filter</tt> may halt the request
411
+ # before a controller action is run. This is useful, for example, to deny
412
+ # access to unauthenticated users or to redirect from HTTP to HTTPS.
413
+ # Simply call render or redirect. After filters will not be executed if the filter
414
+ # chain is halted.
415
+ #
416
+ # Around filters halt the request unless the action block is called.
417
+ # Given these filters
418
+ # after_filter :after
419
+ # around_filter :around
420
+ # before_filter :before
421
+ #
422
+ # The filter chain will look like:
423
+ #
424
+ # ...
425
+ # . \
426
+ # . #around (code before yield)
427
+ # . . \
428
+ # . . #before (actual filter code is run)
429
+ # . . . \
430
+ # . . . execute controller action
431
+ # . . . /
432
+ # . . ...
433
+ # . . /
434
+ # . #around (code after yield)
435
+ # . /
436
+ # #after (actual filter code is run, unless the around filter does not yield)
437
+ #
438
+ # If +around+ returns before yielding, +after+ will still not be run. The +before+
439
+ # filter and controller action will not be run. If +before+ renders or redirects,
440
+ # the second half of +around+ and will still run but +after+ and the
441
+ # action will not. If +around+ fails to yield, +after+ will not be run.
442
+ module ClassMethods
443
+ # The passed <tt>filters</tt> will be appended to the filter_chain and
444
+ # will execute before the action on this controller is performed.
445
+ def append_before_filter(*filters, &block)
446
+ filter_chain.append_filter_to_chain(filters, :before, &block)
447
+ end
448
+
449
+ # The passed <tt>filters</tt> will be prepended to the filter_chain and
450
+ # will execute before the action on this controller is performed.
451
+ def prepend_before_filter(*filters, &block)
452
+ filter_chain.prepend_filter_to_chain(filters, :before, &block)
453
+ end
454
+
455
+ # Shorthand for append_before_filter since it's the most common.
456
+ alias :before_filter :append_before_filter
457
+
458
+ # The passed <tt>filters</tt> will be appended to the array of filters
459
+ # that run _after_ actions on this controller are performed.
460
+ def append_after_filter(*filters, &block)
461
+ filter_chain.append_filter_to_chain(filters, :after, &block)
462
+ end
463
+
464
+ # The passed <tt>filters</tt> will be prepended to the array of filters
465
+ # that run _after_ actions on this controller are performed.
466
+ def prepend_after_filter(*filters, &block)
467
+ filter_chain.prepend_filter_to_chain(filters, :after, &block)
468
+ end
469
+
470
+ # Shorthand for append_after_filter since it's the most common.
471
+ alias :after_filter :append_after_filter
472
+
473
+ # If you <tt>append_around_filter A.new, B.new</tt>, the filter chain looks like
474
+ #
475
+ # B#before
476
+ # A#before
477
+ # # run the action
478
+ # A#after
479
+ # B#after
480
+ #
481
+ # With around filters which yield to the action block, +before+ and +after+
482
+ # are the code before and after the yield.
483
+ def append_around_filter(*filters, &block)
484
+ filter_chain.append_filter_to_chain(filters, :around, &block)
485
+ end
486
+
487
+ # If you <tt>prepend_around_filter A.new, B.new</tt>, the filter chain looks like:
488
+ #
489
+ # A#before
490
+ # B#before
491
+ # # run the action
492
+ # B#after
493
+ # A#after
494
+ #
495
+ # With around filters which yield to the action block, +before+ and +after+
496
+ # are the code before and after the yield.
497
+ def prepend_around_filter(*filters, &block)
498
+ filter_chain.prepend_filter_to_chain(filters, :around, &block)
499
+ end
500
+
501
+ # Shorthand for +append_around_filter+ since it's the most common.
502
+ alias :around_filter :append_around_filter
503
+
504
+ # Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference
505
+ # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
506
+ # of many sub-controllers need a different hierarchy.
507
+ #
508
+ # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
509
+ # just like when you apply the filters.
510
+ def skip_before_filter(*filters)
511
+ filter_chain.skip_filter_in_chain(*filters, &:before?)
512
+ end
513
+
514
+ # Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference
515
+ # filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
516
+ # of many sub-controllers need a different hierarchy.
517
+ #
518
+ # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
519
+ # just like when you apply the filters.
520
+ def skip_after_filter(*filters)
521
+ filter_chain.skip_filter_in_chain(*filters, &:after?)
522
+ end
523
+
524
+ # Removes the specified filters from the filter chain. This only works for method reference (symbol)
525
+ # filters, not procs. This method is different from skip_after_filter and skip_before_filter in that
526
+ # it will match any before, after or yielding around filter.
527
+ #
528
+ # You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
529
+ # just like when you apply the filters.
530
+ def skip_filter(*filters)
531
+ filter_chain.skip_filter_in_chain(*filters)
532
+ end
533
+
534
+ # Returns an array of Filter objects for this controller.
535
+ def filter_chain
536
+ if chain = read_inheritable_attribute('filter_chain')
537
+ return chain
538
+ else
539
+ write_inheritable_attribute('filter_chain', FilterChain.new)
540
+ return filter_chain
541
+ end
542
+ end
543
+
544
+ # Returns all the before filters for this class and all its ancestors.
545
+ # This method returns the actual filter that was assigned in the controller to maintain existing functionality.
546
+ def before_filters #:nodoc:
547
+ filter_chain.select(&:before?).map(&:method)
548
+ end
549
+
550
+ # Returns all the after filters for this class and all its ancestors.
551
+ # This method returns the actual filter that was assigned in the controller to maintain existing functionality.
552
+ def after_filters #:nodoc:
553
+ filter_chain.select(&:after?).map(&:method)
554
+ end
555
+ end
556
+
557
+ module InstanceMethods # :nodoc:
558
+ def self.included(base)
559
+ base.class_eval do
560
+ alias_method_chain :perform_action, :filters
561
+ alias_method_chain :process, :filters
562
+ end
563
+ end
564
+
565
+ protected
566
+ def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc:
567
+ @before_filter_chain_aborted = false
568
+ process_without_filters(request, response, method, *arguments)
569
+ end
570
+
571
+ def perform_action_with_filters
572
+ call_filters(self.class.filter_chain, 0, 0)
573
+ end
574
+
575
+ private
576
+ def call_filters(chain, index, nesting)
577
+ index = run_before_filters(chain, index, nesting)
578
+ aborted = @before_filter_chain_aborted
579
+ perform_action_without_filters unless performed? || aborted
580
+ return index if nesting != 0 || aborted
581
+ run_after_filters(chain, index)
582
+ end
583
+
584
+ def run_before_filters(chain, index, nesting)
585
+ while chain[index]
586
+ filter, index = chain[index], index
587
+ break unless filter # end of call chain reached
588
+
589
+ case filter
590
+ when BeforeFilter
591
+ filter.call(self) # invoke before filter
592
+ index = index.next
593
+ break if @before_filter_chain_aborted
594
+ when AroundFilter
595
+ yielded = false
596
+
597
+ filter.call(self) do
598
+ yielded = true
599
+ # all remaining before and around filters will be run in this call
600
+ index = call_filters(chain, index.next, nesting.next)
601
+ end
602
+
603
+ halt_filter_chain(filter, :did_not_yield) unless yielded
604
+
605
+ break
606
+ else
607
+ break # no before or around filters left
608
+ end
609
+ end
610
+
611
+ index
612
+ end
613
+
614
+ def run_after_filters(chain, index)
615
+ seen_after_filter = false
616
+
617
+ while chain[index]
618
+ filter, index = chain[index], index
619
+ break unless filter # end of call chain reached
620
+
621
+ case filter
622
+ when AfterFilter
623
+ seen_after_filter = true
624
+ filter.call(self) # invoke after filter
625
+ else
626
+ # implementation error or someone has mucked with the filter chain
627
+ raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" if seen_after_filter
628
+ end
629
+
630
+ index = index.next
631
+ end
632
+
633
+ index.next
634
+ end
635
+
636
+ def halt_filter_chain(filter, reason)
637
+ @before_filter_chain_aborted = true
638
+ logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger
639
+ end
640
+ end
641
+ end
642
+ end