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.
- data/CHANGELOG +7 -0
- data/MIT-LICENSE +21 -0
- data/README +469 -0
- data/RUNNING_UNIT_TESTS +24 -0
- data/Rakefile +146 -0
- data/install.rb +30 -0
- data/lib/action_controller.rb +79 -0
- data/lib/action_controller/assertions.rb +69 -0
- data/lib/action_controller/assertions/dom_assertions.rb +39 -0
- data/lib/action_controller/assertions/model_assertions.rb +20 -0
- data/lib/action_controller/assertions/response_assertions.rb +172 -0
- data/lib/action_controller/assertions/routing_assertions.rb +146 -0
- data/lib/action_controller/assertions/selector_assertions.rb +491 -0
- data/lib/action_controller/assertions/tag_assertions.rb +130 -0
- data/lib/action_controller/base.rb +1288 -0
- data/lib/action_controller/benchmarking.rb +94 -0
- data/lib/action_controller/caching.rb +72 -0
- data/lib/action_controller/caching/actions.rb +144 -0
- data/lib/action_controller/caching/fragments.rb +138 -0
- data/lib/action_controller/caching/pages.rb +154 -0
- data/lib/action_controller/caching/sql_cache.rb +18 -0
- data/lib/action_controller/caching/sweeping.rb +97 -0
- data/lib/action_controller/cgi_ext.rb +16 -0
- data/lib/action_controller/cgi_ext/cookie.rb +110 -0
- data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
- data/lib/action_controller/cgi_ext/session.rb +73 -0
- data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
- data/lib/action_controller/cgi_process.rb +223 -0
- data/lib/action_controller/components.rb +166 -0
- data/lib/action_controller/cookies.rb +96 -0
- data/lib/action_controller/dispatcher.rb +162 -0
- data/lib/action_controller/filters.rb +642 -0
- data/lib/action_controller/flash.rb +172 -0
- data/lib/action_controller/headers.rb +31 -0
- data/lib/action_controller/helpers.rb +221 -0
- data/lib/action_controller/http_authentication.rb +124 -0
- data/lib/action_controller/integration.rb +634 -0
- data/lib/action_controller/layout.rb +309 -0
- data/lib/action_controller/mime_responds.rb +173 -0
- data/lib/action_controller/mime_type.rb +186 -0
- data/lib/action_controller/mime_types.rb +20 -0
- data/lib/action_controller/polymorphic_routes.rb +191 -0
- data/lib/action_controller/record_identifier.rb +102 -0
- data/lib/action_controller/request.rb +764 -0
- data/lib/action_controller/request_forgery_protection.rb +140 -0
- data/lib/action_controller/request_profiler.rb +169 -0
- data/lib/action_controller/rescue.rb +258 -0
- data/lib/action_controller/resources.rb +572 -0
- data/lib/action_controller/response.rb +76 -0
- data/lib/action_controller/routing.rb +387 -0
- data/lib/action_controller/routing/builder.rb +203 -0
- data/lib/action_controller/routing/optimisations.rb +120 -0
- data/lib/action_controller/routing/recognition_optimisation.rb +162 -0
- data/lib/action_controller/routing/route.rb +240 -0
- data/lib/action_controller/routing/route_set.rb +436 -0
- data/lib/action_controller/routing/routing_ext.rb +46 -0
- data/lib/action_controller/routing/segments.rb +283 -0
- data/lib/action_controller/session/active_record_store.rb +340 -0
- data/lib/action_controller/session/cookie_store.rb +166 -0
- data/lib/action_controller/session/drb_server.rb +32 -0
- data/lib/action_controller/session/drb_store.rb +35 -0
- data/lib/action_controller/session/mem_cache_store.rb +98 -0
- data/lib/action_controller/session_management.rb +158 -0
- data/lib/action_controller/status_codes.rb +88 -0
- data/lib/action_controller/streaming.rb +155 -0
- data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
- data/lib/action_controller/templates/rescues/_trace.erb +26 -0
- data/lib/action_controller/templates/rescues/diagnostics.erb +11 -0
- data/lib/action_controller/templates/rescues/layout.erb +29 -0
- data/lib/action_controller/templates/rescues/missing_template.erb +2 -0
- data/lib/action_controller/templates/rescues/routing_error.erb +10 -0
- data/lib/action_controller/templates/rescues/template_error.erb +21 -0
- data/lib/action_controller/templates/rescues/unknown_action.erb +2 -0
- data/lib/action_controller/test_case.rb +83 -0
- data/lib/action_controller/test_process.rb +526 -0
- data/lib/action_controller/url_rewriter.rb +142 -0
- data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
- data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
- data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
- data/lib/action_controller/verification.rb +130 -0
- data/lib/action_pack.rb +24 -0
- data/lib/action_pack/version.rb +9 -0
- data/lib/action_view.rb +44 -0
- data/lib/action_view/base.rb +335 -0
- data/lib/action_view/helpers/active_record_helper.rb +276 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +599 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +143 -0
- data/lib/action_view/helpers/benchmark_helper.rb +33 -0
- data/lib/action_view/helpers/cache_helper.rb +40 -0
- data/lib/action_view/helpers/capture_helper.rb +161 -0
- data/lib/action_view/helpers/date_helper.rb +711 -0
- data/lib/action_view/helpers/debug_helper.rb +31 -0
- data/lib/action_view/helpers/form_helper.rb +767 -0
- data/lib/action_view/helpers/form_options_helper.rb +458 -0
- data/lib/action_view/helpers/form_tag_helper.rb +458 -0
- data/lib/action_view/helpers/javascript_helper.rb +148 -0
- data/lib/action_view/helpers/number_helper.rb +186 -0
- data/lib/action_view/helpers/record_identification_helper.rb +20 -0
- data/lib/action_view/helpers/record_tag_helper.rb +59 -0
- data/lib/action_view/helpers/sanitize_helper.rb +229 -0
- data/lib/action_view/helpers/tag_helper.rb +134 -0
- data/lib/action_view/helpers/text_helper.rb +507 -0
- data/lib/action_view/helpers/url_helper.rb +573 -0
- data/lib/action_view/inline_template.rb +20 -0
- data/lib/action_view/partial_template.rb +70 -0
- data/lib/action_view/partials.rb +158 -0
- data/lib/action_view/template.rb +125 -0
- data/lib/action_view/template_error.rb +110 -0
- data/lib/action_view/template_finder.rb +176 -0
- data/lib/action_view/template_handler.rb +34 -0
- data/lib/action_view/template_handlers/builder.rb +27 -0
- data/lib/action_view/template_handlers/compilable.rb +128 -0
- data/lib/action_view/template_handlers/erb.rb +56 -0
- data/lib/action_view/test_case.rb +58 -0
- data/lib/actionpack.rb +1 -0
- data/test/abstract_unit.rb +36 -0
- data/test/active_record_unit.rb +105 -0
- data/test/activerecord/active_record_store_test.rb +141 -0
- data/test/activerecord/render_partial_with_record_identification_test.rb +191 -0
- data/test/adv_attr_test.rb +20 -0
- data/test/controller/action_pack_assertions_test.rb +543 -0
- data/test/controller/addresses_render_test.rb +43 -0
- data/test/controller/assert_select_test.rb +331 -0
- data/test/controller/base_test.rb +219 -0
- data/test/controller/benchmark_test.rb +32 -0
- data/test/controller/caching_test.rb +581 -0
- data/test/controller/capture_test.rb +89 -0
- data/test/controller/cgi_test.rb +116 -0
- data/test/controller/components_test.rb +140 -0
- data/test/controller/content_type_test.rb +139 -0
- data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
- data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
- data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
- data/test/controller/cookie_test.rb +146 -0
- data/test/controller/custom_handler_test.rb +45 -0
- data/test/controller/deprecation/deprecated_base_methods_test.rb +37 -0
- data/test/controller/dispatcher_test.rb +105 -0
- data/test/controller/fake_controllers.rb +33 -0
- data/test/controller/fake_models.rb +11 -0
- data/test/controller/filter_params_test.rb +49 -0
- data/test/controller/filters_test.rb +881 -0
- data/test/controller/flash_test.rb +146 -0
- data/test/controller/header_test.rb +14 -0
- data/test/controller/helper_test.rb +210 -0
- data/test/controller/html-scanner/cdata_node_test.rb +15 -0
- data/test/controller/html-scanner/document_test.rb +148 -0
- data/test/controller/html-scanner/node_test.rb +89 -0
- data/test/controller/html-scanner/sanitizer_test.rb +269 -0
- data/test/controller/html-scanner/tag_node_test.rb +238 -0
- data/test/controller/html-scanner/text_node_test.rb +50 -0
- data/test/controller/html-scanner/tokenizer_test.rb +131 -0
- data/test/controller/http_authentication_test.rb +54 -0
- data/test/controller/integration_test.rb +252 -0
- data/test/controller/integration_upload_test.rb +43 -0
- data/test/controller/layout_test.rb +255 -0
- data/test/controller/mime_responds_test.rb +514 -0
- data/test/controller/mime_type_test.rb +84 -0
- data/test/controller/new_render_test.rb +843 -0
- data/test/controller/polymorphic_routes_test.rb +174 -0
- data/test/controller/record_identifier_test.rb +139 -0
- data/test/controller/redirect_test.rb +289 -0
- data/test/controller/render_test.rb +484 -0
- data/test/controller/request_forgery_protection_test.rb +305 -0
- data/test/controller/request_test.rb +928 -0
- data/test/controller/rescue_test.rb +517 -0
- data/test/controller/resources_test.rb +873 -0
- data/test/controller/routing_test.rb +2464 -0
- data/test/controller/selector_test.rb +628 -0
- data/test/controller/send_file_test.rb +138 -0
- data/test/controller/session/cookie_store_test.rb +258 -0
- data/test/controller/session/mem_cache_store_test.rb +181 -0
- data/test/controller/session_fixation_test.rb +89 -0
- data/test/controller/session_management_test.rb +178 -0
- data/test/controller/test_test.rb +695 -0
- data/test/controller/url_rewriter_test.rb +310 -0
- data/test/controller/verification_test.rb +270 -0
- data/test/controller/view_paths_test.rb +140 -0
- data/test/controller/webservice_test.rb +229 -0
- data/test/fixtures/addresses/list.erb +1 -0
- data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
- data/test/fixtures/companies.yml +24 -0
- data/test/fixtures/company.rb +10 -0
- data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
- data/test/fixtures/content_type/render_default_for_js.js.erb +1 -0
- data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
- data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
- data/test/fixtures/customers/_customer.html.erb +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +49 -0
- data/test/fixtures/developer.rb +9 -0
- data/test/fixtures/developers.yml +21 -0
- data/test/fixtures/developers_projects.yml +13 -0
- data/test/fixtures/fun/games/hello_world.erb +1 -0
- data/test/fixtures/functional_caching/_partial.erb +3 -0
- data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
- data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
- data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
- data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
- data/test/fixtures/helpers/abc_helper.rb +5 -0
- data/test/fixtures/helpers/fun/games_helper.rb +3 -0
- data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
- data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
- data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
- data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
- data/test/fixtures/layouts/block_with_layout.erb +3 -0
- data/test/fixtures/layouts/builder.builder +3 -0
- data/test/fixtures/layouts/partial_with_layout.erb +3 -0
- data/test/fixtures/layouts/standard.erb +1 -0
- data/test/fixtures/layouts/talk_from_action.erb +2 -0
- data/test/fixtures/layouts/yield.erb +2 -0
- data/test/fixtures/mascot.rb +3 -0
- data/test/fixtures/mascots.yml +4 -0
- data/test/fixtures/mascots/_mascot.html.erb +1 -0
- data/test/fixtures/multipart/binary_file +0 -0
- data/test/fixtures/multipart/boundary_problem_file +10 -0
- data/test/fixtures/multipart/bracketed_param +5 -0
- data/test/fixtures/multipart/large_text_file +10 -0
- data/test/fixtures/multipart/mixed_files +0 -0
- data/test/fixtures/multipart/mona_lisa.jpg +0 -0
- data/test/fixtures/multipart/single_parameter +5 -0
- data/test/fixtures/multipart/text_file +10 -0
- data/test/fixtures/override/test/hello_world.erb +1 -0
- data/test/fixtures/override2/layouts/test/sub.erb +1 -0
- data/test/fixtures/post_test/layouts/post.html.erb +1 -0
- data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
- data/test/fixtures/post_test/post/index.html.erb +1 -0
- data/test/fixtures/post_test/post/index.iphone.erb +1 -0
- data/test/fixtures/post_test/super_post/index.html.erb +1 -0
- data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
- data/test/fixtures/project.rb +3 -0
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/public/404.html +1 -0
- data/test/fixtures/public/500.html +1 -0
- data/test/fixtures/public/images/rails.png +0 -0
- data/test/fixtures/public/javascripts/application.js +1 -0
- data/test/fixtures/public/javascripts/bank.js +1 -0
- data/test/fixtures/public/javascripts/robber.js +1 -0
- data/test/fixtures/public/javascripts/version.1.0.js +1 -0
- data/test/fixtures/public/stylesheets/bank.css +1 -0
- data/test/fixtures/public/stylesheets/robber.css +1 -0
- data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
- data/test/fixtures/replies.yml +15 -0
- data/test/fixtures/reply.rb +7 -0
- data/test/fixtures/respond_to/all_types_with_layout.html.erb +1 -0
- data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
- data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
- data/test/fixtures/respond_to/using_defaults.html.erb +1 -0
- data/test/fixtures/respond_to/using_defaults.js.rjs +1 -0
- data/test/fixtures/respond_to/using_defaults.xml.builder +1 -0
- data/test/fixtures/respond_to/using_defaults_with_type_list.html.erb +1 -0
- data/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs +1 -0
- data/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder +1 -0
- data/test/fixtures/scope/test/modgreet.erb +1 -0
- data/test/fixtures/shared.html.erb +1 -0
- data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
- data/test/fixtures/test/_customer.erb +1 -0
- data/test/fixtures/test/_customer_counter.erb +1 -0
- data/test/fixtures/test/_customer_greeting.erb +1 -0
- data/test/fixtures/test/_form.erb +1 -0
- data/test/fixtures/test/_hash_greeting.erb +1 -0
- data/test/fixtures/test/_hash_object.erb +2 -0
- data/test/fixtures/test/_hello.builder +1 -0
- data/test/fixtures/test/_labelling_form.erb +1 -0
- data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
- data/test/fixtures/test/_partial.erb +1 -0
- data/test/fixtures/test/_partial.html.erb +1 -0
- data/test/fixtures/test/_partial.js.erb +1 -0
- data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
- data/test/fixtures/test/_partial_only.erb +1 -0
- data/test/fixtures/test/_person.erb +2 -0
- data/test/fixtures/test/_raise.html.erb +1 -0
- data/test/fixtures/test/action_talk_to_layout.erb +2 -0
- data/test/fixtures/test/block_content_for.erb +2 -0
- data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
- data/test/fixtures/test/capturing.erb +4 -0
- data/test/fixtures/test/content_for.erb +2 -0
- data/test/fixtures/test/content_for_concatenated.erb +3 -0
- data/test/fixtures/test/content_for_with_parameter.erb +2 -0
- data/test/fixtures/test/delete_with_js.rjs +2 -0
- data/test/fixtures/test/dot.directory/render_file_with_ivar.erb +1 -0
- data/test/fixtures/test/enum_rjs_test.rjs +6 -0
- data/test/fixtures/test/erb_content_for.erb +2 -0
- data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.builder +1 -0
- data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
- data/test/fixtures/test/greeting.erb +1 -0
- data/test/fixtures/test/greeting.js.rjs +1 -0
- data/test/fixtures/test/hello.builder +4 -0
- data/test/fixtures/test/hello_world.erb +1 -0
- data/test/fixtures/test/hello_world_container.builder +3 -0
- data/test/fixtures/test/hello_world_from_rxml.builder +4 -0
- data/test/fixtures/test/hello_world_with_layout_false.erb +1 -0
- data/test/fixtures/test/hello_xml_world.builder +11 -0
- data/test/fixtures/test/list.erb +1 -0
- data/test/fixtures/test/non_erb_block_content_for.builder +4 -0
- data/test/fixtures/test/potential_conflicts.erb +4 -0
- data/test/fixtures/test/render_file_from_template.html.erb +1 -0
- data/test/fixtures/test/render_file_with_ivar.erb +1 -0
- data/test/fixtures/test/render_file_with_locals.erb +1 -0
- data/test/fixtures/test/render_to_string_test.erb +1 -0
- data/test/fixtures/test/update_element_with_capture.erb +9 -0
- data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
- data/test/fixtures/topic.rb +3 -0
- data/test/fixtures/topics.yml +22 -0
- data/test/fixtures/topics/_topic.html.erb +1 -0
- data/test/template/active_record_helper_test.rb +268 -0
- data/test/template/asset_tag_helper_test.rb +514 -0
- data/test/template/atom_feed_helper_test.rb +179 -0
- data/test/template/benchmark_helper_test.rb +60 -0
- data/test/template/date_helper_test.rb +1791 -0
- data/test/template/deprecated_erb_variable_test.rb +9 -0
- data/test/template/erb_util_test.rb +24 -0
- data/test/template/form_helper_test.rb +885 -0
- data/test/template/form_options_helper_test.rb +1333 -0
- data/test/template/form_tag_helper_test.rb +272 -0
- data/test/template/javascript_helper_test.rb +73 -0
- data/test/template/number_helper_test.rb +97 -0
- data/test/template/record_tag_helper_test.rb +54 -0
- data/test/template/sanitize_helper_test.rb +48 -0
- data/test/template/tag_helper_test.rb +77 -0
- data/test/template/template_finder_test.rb +73 -0
- data/test/template/template_object_test.rb +95 -0
- data/test/template/test_test.rb +56 -0
- data/test/template/text_helper_test.rb +367 -0
- data/test/template/url_helper_test.rb +544 -0
- data/test/testing_sandbox.rb +15 -0
- metadata +469 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
module ActionController #:nodoc:
|
|
2
|
+
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
module RequestForgeryProtection
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.class_eval do
|
|
8
|
+
class_inheritable_accessor :request_forgery_protection_options
|
|
9
|
+
self.request_forgery_protection_options = {}
|
|
10
|
+
helper_method :form_authenticity_token
|
|
11
|
+
helper_method :protect_against_forgery?
|
|
12
|
+
end
|
|
13
|
+
base.extend(ClassMethods)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
|
|
17
|
+
# forged link from another site, is done by embedding a token based on the session (which an attacker wouldn't know) in all
|
|
18
|
+
# forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
|
|
19
|
+
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
|
|
20
|
+
# scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
|
|
21
|
+
#
|
|
22
|
+
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
|
|
23
|
+
# ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
|
|
24
|
+
# production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0
|
|
25
|
+
# applications.
|
|
26
|
+
#
|
|
27
|
+
# The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form manually (without the
|
|
28
|
+
# use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to include a hidden field named like that and
|
|
29
|
+
# set its value to what is returned by <tt>form_authenticity_token</tt>. Same applies to manually constructed Ajax requests. To
|
|
30
|
+
# make the token available through a global variable to scripts on a certain page, you could add something like this to a view:
|
|
31
|
+
#
|
|
32
|
+
# <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
|
|
33
|
+
#
|
|
34
|
+
# Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to
|
|
35
|
+
# config/environments/test.rb:
|
|
36
|
+
#
|
|
37
|
+
# # Disable request forgery protection in test environment
|
|
38
|
+
# config.action_controller.allow_forgery_protection = false
|
|
39
|
+
#
|
|
40
|
+
# == Learn more about CSRF (Cross-Site Request Forgery) attacks
|
|
41
|
+
#
|
|
42
|
+
# Here are some resources:
|
|
43
|
+
# * http://isc.sans.org/diary.html?storyid=1750
|
|
44
|
+
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
|
45
|
+
#
|
|
46
|
+
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
|
|
47
|
+
# There are a few guidelines you should follow:
|
|
48
|
+
#
|
|
49
|
+
# * Keep your GET requests safe and idempotent. More reading material:
|
|
50
|
+
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
|
|
51
|
+
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
|
|
52
|
+
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
|
|
53
|
+
#
|
|
54
|
+
module ClassMethods
|
|
55
|
+
# Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
|
|
56
|
+
#
|
|
57
|
+
# Example:
|
|
58
|
+
#
|
|
59
|
+
# class FooController < ApplicationController
|
|
60
|
+
# # uses the cookie session store (then you don't need a separate :secret)
|
|
61
|
+
# protect_from_forgery :except => :index
|
|
62
|
+
#
|
|
63
|
+
# # uses one of the other session stores that uses a session_id value.
|
|
64
|
+
# protect_from_forgery :secret => 'my-little-pony', :except => :index
|
|
65
|
+
#
|
|
66
|
+
# # you can disable csrf protection on controller-by-controller basis:
|
|
67
|
+
# skip_before_filter :verify_authenticity_token
|
|
68
|
+
# end
|
|
69
|
+
#
|
|
70
|
+
# Valid Options:
|
|
71
|
+
#
|
|
72
|
+
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
|
|
73
|
+
# * <tt>:secret</tt> - Custom salt used to generate the <tt>form_authenticity_token</tt>.
|
|
74
|
+
# Leave this off if you are using the cookie session store.
|
|
75
|
+
# * <tt>:digest</tt> - Message digest used for hashing. Defaults to 'SHA1'.
|
|
76
|
+
def protect_from_forgery(options = {})
|
|
77
|
+
self.request_forgery_protection_token ||= :authenticity_token
|
|
78
|
+
before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except)
|
|
79
|
+
request_forgery_protection_options.update(options)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
protected
|
|
84
|
+
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
|
|
85
|
+
def verify_authenticity_token
|
|
86
|
+
verified_request? || raise(ActionController::InvalidAuthenticityToken)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Returns true or false if a request is verified. Checks:
|
|
90
|
+
#
|
|
91
|
+
# * is the format restricted? By default, only HTML and AJAX requests are checked.
|
|
92
|
+
# * is it a GET request? Gets should be safe and idempotent
|
|
93
|
+
# * Does the form_authenticity_token match the given _token value from the params?
|
|
94
|
+
def verified_request?
|
|
95
|
+
!protect_against_forgery? ||
|
|
96
|
+
request.method == :get ||
|
|
97
|
+
!verifiable_request_format? ||
|
|
98
|
+
form_authenticity_token == params[request_forgery_protection_token]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def verifiable_request_format?
|
|
102
|
+
request.content_type.nil? || request.content_type.verify_request?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Sets the token value for the current session. Pass a <tt>:secret</tt> option
|
|
106
|
+
# in +protect_from_forgery+ to add a custom salt to the hash.
|
|
107
|
+
def form_authenticity_token
|
|
108
|
+
@form_authenticity_token ||= if !session.respond_to?(:session_id)
|
|
109
|
+
raise InvalidAuthenticityToken, "Request Forgery Protection requires a valid session. Use #allow_forgery_protection to disable it, or use a valid session."
|
|
110
|
+
elsif request_forgery_protection_options[:secret]
|
|
111
|
+
authenticity_token_from_session_id
|
|
112
|
+
elsif session.respond_to?(:dbman) && session.dbman.respond_to?(:generate_digest)
|
|
113
|
+
authenticity_token_from_cookie_session
|
|
114
|
+
else
|
|
115
|
+
raise InvalidAuthenticityToken, "No :secret given to the #protect_from_forgery call. Set that or use a session store capable of generating its own keys (Cookie Session Store)."
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Generates a unique digest using the session_id and the CSRF secret.
|
|
120
|
+
def authenticity_token_from_session_id
|
|
121
|
+
key = if request_forgery_protection_options[:secret].respond_to?(:call)
|
|
122
|
+
request_forgery_protection_options[:secret].call(@session)
|
|
123
|
+
else
|
|
124
|
+
request_forgery_protection_options[:secret]
|
|
125
|
+
end
|
|
126
|
+
digest = request_forgery_protection_options[:digest] ||= 'SHA1'
|
|
127
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(digest), key.to_s, session.session_id.to_s)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# No secret was given, so assume this is a cookie session store.
|
|
131
|
+
def authenticity_token_from_cookie_session
|
|
132
|
+
session[:csrf_id] ||= CGI::Session.generate_unique_id
|
|
133
|
+
session.dbman.generate_digest(session[:csrf_id])
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def protect_against_forgery?
|
|
137
|
+
allow_forgery_protection && request_forgery_protection_token
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
require 'action_controller/integration'
|
|
3
|
+
|
|
4
|
+
module ActionController
|
|
5
|
+
class RequestProfiler
|
|
6
|
+
# Wrap up the integration session runner.
|
|
7
|
+
class Sandbox
|
|
8
|
+
include Integration::Runner
|
|
9
|
+
|
|
10
|
+
def self.benchmark(n, script)
|
|
11
|
+
new(script).benchmark(n)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(script_path)
|
|
15
|
+
@quiet = false
|
|
16
|
+
define_run_method(script_path)
|
|
17
|
+
reset!
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def benchmark(n, profiling = false)
|
|
21
|
+
@quiet = true
|
|
22
|
+
print ' '
|
|
23
|
+
|
|
24
|
+
result = Benchmark.realtime do
|
|
25
|
+
n.times do |i|
|
|
26
|
+
run(profiling)
|
|
27
|
+
print_progress(i)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
puts
|
|
32
|
+
result
|
|
33
|
+
ensure
|
|
34
|
+
@quiet = false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def say(message)
|
|
38
|
+
puts " #{message}" unless @quiet
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
def define_run_method(script_path)
|
|
43
|
+
script = File.read(script_path)
|
|
44
|
+
|
|
45
|
+
source = <<-end_source
|
|
46
|
+
def run(profiling = false)
|
|
47
|
+
if profiling
|
|
48
|
+
RubyProf.resume do
|
|
49
|
+
#{script}
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
#{script}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
old_request_count = request_count
|
|
56
|
+
reset!
|
|
57
|
+
self.request_count = old_request_count
|
|
58
|
+
end
|
|
59
|
+
end_source
|
|
60
|
+
|
|
61
|
+
instance_eval source, script_path, 1
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def print_progress(i)
|
|
65
|
+
print "\n " if i % 60 == 0
|
|
66
|
+
print ' ' if i % 10 == 0
|
|
67
|
+
print '.'
|
|
68
|
+
$stdout.flush
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
attr_reader :options
|
|
74
|
+
|
|
75
|
+
def initialize(options = {})
|
|
76
|
+
@options = default_options.merge(options)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def self.run(args = nil, options = {})
|
|
81
|
+
profiler = new(options)
|
|
82
|
+
profiler.parse_options(args) if args
|
|
83
|
+
profiler.run
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def run
|
|
87
|
+
sandbox = Sandbox.new(options[:script])
|
|
88
|
+
|
|
89
|
+
puts 'Warming up once'
|
|
90
|
+
|
|
91
|
+
elapsed = warmup(sandbox)
|
|
92
|
+
puts '%.2f sec, %d requests, %d req/sec' % [elapsed, sandbox.request_count, sandbox.request_count / elapsed]
|
|
93
|
+
puts "\n#{options[:benchmark] ? 'Benchmarking' : 'Profiling'} #{options[:n]}x"
|
|
94
|
+
|
|
95
|
+
options[:benchmark] ? benchmark(sandbox) : profile(sandbox)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def profile(sandbox)
|
|
99
|
+
load_ruby_prof
|
|
100
|
+
|
|
101
|
+
benchmark(sandbox, true)
|
|
102
|
+
results = RubyProf.stop
|
|
103
|
+
|
|
104
|
+
show_profile_results results
|
|
105
|
+
results
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def benchmark(sandbox, profiling = false)
|
|
109
|
+
sandbox.request_count = 0
|
|
110
|
+
elapsed = sandbox.benchmark(options[:n], profiling).to_f
|
|
111
|
+
count = sandbox.request_count.to_i
|
|
112
|
+
puts '%.2f sec, %d requests, %d req/sec' % [elapsed, count, count / elapsed]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def warmup(sandbox)
|
|
116
|
+
Benchmark.realtime { sandbox.run(false) }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def default_options
|
|
120
|
+
{ :n => 100, :open => 'open %s &' }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Parse command-line options
|
|
124
|
+
def parse_options(args)
|
|
125
|
+
OptionParser.new do |opt|
|
|
126
|
+
opt.banner = "USAGE: #{$0} [options] [session script path]"
|
|
127
|
+
|
|
128
|
+
opt.on('-n', '--times [100]', 'How many requests to process. Defaults to 100.') { |v| options[:n] = v.to_i if v }
|
|
129
|
+
opt.on('-b', '--benchmark', 'Benchmark instead of profiling') { |v| options[:benchmark] = v }
|
|
130
|
+
opt.on('-m', '--measure [mode]', 'Which ruby-prof measure mode to use: process_time, wall_time, cpu_time, allocations, or memory. Defaults to process_time.') { |v| options[:measure] = v }
|
|
131
|
+
opt.on('--open [CMD]', 'Command to open profile results. Defaults to "open %s &"') { |v| options[:open] = v }
|
|
132
|
+
opt.on('-h', '--help', 'Show this help') { puts opt; exit }
|
|
133
|
+
|
|
134
|
+
opt.parse args
|
|
135
|
+
|
|
136
|
+
if args.empty?
|
|
137
|
+
puts opt
|
|
138
|
+
exit
|
|
139
|
+
end
|
|
140
|
+
options[:script] = args.pop
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
protected
|
|
145
|
+
def load_ruby_prof
|
|
146
|
+
begin
|
|
147
|
+
gem 'ruby-prof', '>= 0.6.1'
|
|
148
|
+
require 'ruby-prof'
|
|
149
|
+
if mode = options[:measure]
|
|
150
|
+
RubyProf.measure_mode = RubyProf.const_get(mode.upcase)
|
|
151
|
+
end
|
|
152
|
+
rescue LoadError
|
|
153
|
+
abort '`gem install ruby-prof` to use the profiler'
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def show_profile_results(results)
|
|
158
|
+
File.open "#{RAILS_ROOT}/tmp/profile-graph.html", 'w' do |file|
|
|
159
|
+
RubyProf::GraphHtmlPrinter.new(results).print(file)
|
|
160
|
+
`#{options[:open] % file.path}` if options[:open]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
File.open "#{RAILS_ROOT}/tmp/profile-flat.txt", 'w' do |file|
|
|
164
|
+
RubyProf::FlatPrinter.new(results).print(file)
|
|
165
|
+
`#{options[:open] % file.path}` if options[:open]
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
module ActionController #:nodoc:
|
|
2
|
+
# Actions that fail to perform as expected throw exceptions. These exceptions can either be rescued for the public view
|
|
3
|
+
# (with a nice user-friendly explanation) or for the developers view (with tons of debugging information). The developers view
|
|
4
|
+
# is already implemented by the Action Controller, but the public view should be tailored to your specific application.
|
|
5
|
+
#
|
|
6
|
+
# The default behavior for public exceptions is to render a static html file with the name of the error code thrown. If no such
|
|
7
|
+
# file exists, an empty response is sent with the correct status code.
|
|
8
|
+
#
|
|
9
|
+
# You can override what constitutes a local request by overriding the <tt>local_request?</tt> method in your own controller.
|
|
10
|
+
# Custom rescue behavior is achieved by overriding the <tt>rescue_action_in_public</tt> and <tt>rescue_action_locally</tt> methods.
|
|
11
|
+
module Rescue
|
|
12
|
+
LOCALHOST = '127.0.0.1'.freeze
|
|
13
|
+
|
|
14
|
+
DEFAULT_RESCUE_RESPONSE = :internal_server_error
|
|
15
|
+
DEFAULT_RESCUE_RESPONSES = {
|
|
16
|
+
'ActionController::RoutingError' => :not_found,
|
|
17
|
+
'ActionController::UnknownAction' => :not_found,
|
|
18
|
+
'ActiveRecord::RecordNotFound' => :not_found,
|
|
19
|
+
'ActiveRecord::StaleObjectError' => :conflict,
|
|
20
|
+
'ActiveRecord::RecordInvalid' => :unprocessable_entity,
|
|
21
|
+
'ActiveRecord::RecordNotSaved' => :unprocessable_entity,
|
|
22
|
+
'ActionController::MethodNotAllowed' => :method_not_allowed,
|
|
23
|
+
'ActionController::NotImplemented' => :not_implemented,
|
|
24
|
+
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
DEFAULT_RESCUE_TEMPLATE = 'diagnostics'
|
|
28
|
+
DEFAULT_RESCUE_TEMPLATES = {
|
|
29
|
+
'ActionView::MissingTemplate' => 'missing_template',
|
|
30
|
+
'ActionController::RoutingError' => 'routing_error',
|
|
31
|
+
'ActionController::UnknownAction' => 'unknown_action',
|
|
32
|
+
'ActionView::TemplateError' => 'template_error'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def self.included(base) #:nodoc:
|
|
36
|
+
base.cattr_accessor :rescue_responses
|
|
37
|
+
base.rescue_responses = Hash.new(DEFAULT_RESCUE_RESPONSE)
|
|
38
|
+
base.rescue_responses.update DEFAULT_RESCUE_RESPONSES
|
|
39
|
+
|
|
40
|
+
base.cattr_accessor :rescue_templates
|
|
41
|
+
base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE)
|
|
42
|
+
base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES
|
|
43
|
+
|
|
44
|
+
base.class_inheritable_array :rescue_handlers
|
|
45
|
+
base.rescue_handlers = []
|
|
46
|
+
|
|
47
|
+
base.extend(ClassMethods)
|
|
48
|
+
base.class_eval do
|
|
49
|
+
alias_method_chain :perform_action, :rescue
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
module ClassMethods
|
|
54
|
+
def process_with_exception(request, response, exception) #:nodoc:
|
|
55
|
+
new.process(request, response, :rescue_action, exception)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Rescue exceptions raised in controller actions.
|
|
59
|
+
#
|
|
60
|
+
# <tt>rescue_from</tt> receives a series of exception classes or class
|
|
61
|
+
# names, and a trailing <tt>:with</tt> option with the name of a method
|
|
62
|
+
# or a Proc object to be called to handle them. Alternatively a block can
|
|
63
|
+
# be given.
|
|
64
|
+
#
|
|
65
|
+
# Handlers that take one argument will be called with the exception, so
|
|
66
|
+
# that the exception can be inspected when dealing with it.
|
|
67
|
+
#
|
|
68
|
+
# Handlers are inherited. They are searched from right to left, from
|
|
69
|
+
# bottom to top, and up the hierarchy. The handler of the first class for
|
|
70
|
+
# which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
|
|
71
|
+
# any.
|
|
72
|
+
#
|
|
73
|
+
# class ApplicationController < ActionController::Base
|
|
74
|
+
# rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
|
|
75
|
+
# rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
|
|
76
|
+
#
|
|
77
|
+
# rescue_from 'MyAppError::Base' do |exception|
|
|
78
|
+
# render :xml => exception, :status => 500
|
|
79
|
+
# end
|
|
80
|
+
#
|
|
81
|
+
# protected
|
|
82
|
+
# def deny_access
|
|
83
|
+
# ...
|
|
84
|
+
# end
|
|
85
|
+
#
|
|
86
|
+
# def show_errors(exception)
|
|
87
|
+
# exception.record.new_record? ? ...
|
|
88
|
+
# end
|
|
89
|
+
# end
|
|
90
|
+
def rescue_from(*klasses, &block)
|
|
91
|
+
options = klasses.extract_options!
|
|
92
|
+
unless options.has_key?(:with)
|
|
93
|
+
block_given? ? options[:with] = block : raise(ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument.")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
klasses.each do |klass|
|
|
97
|
+
key = if klass.is_a?(Class) && klass <= Exception
|
|
98
|
+
klass.name
|
|
99
|
+
elsif klass.is_a?(String)
|
|
100
|
+
klass
|
|
101
|
+
else
|
|
102
|
+
raise(ArgumentError, "#{klass} is neither an Exception nor a String")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Order is important, we put the pair at the end. When dealing with an
|
|
106
|
+
# exception we will follow the documented order going from right to left.
|
|
107
|
+
rescue_handlers << [key, options[:with]]
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
protected
|
|
113
|
+
# Exception handler called when the performance of an action raises an exception.
|
|
114
|
+
def rescue_action(exception)
|
|
115
|
+
log_error(exception) if logger
|
|
116
|
+
erase_results if performed?
|
|
117
|
+
|
|
118
|
+
# Let the exception alter the response if it wants.
|
|
119
|
+
# For example, MethodNotAllowed sets the Allow header.
|
|
120
|
+
if exception.respond_to?(:handle_response!)
|
|
121
|
+
exception.handle_response!(response)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
if consider_all_requests_local || local_request?
|
|
125
|
+
rescue_action_locally(exception)
|
|
126
|
+
else
|
|
127
|
+
rescue_action_in_public(exception)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Overwrite to implement custom logging of errors. By default logs as fatal.
|
|
132
|
+
def log_error(exception) #:doc:
|
|
133
|
+
ActiveSupport::Deprecation.silence do
|
|
134
|
+
if ActionView::TemplateError === exception
|
|
135
|
+
logger.fatal(exception.to_s)
|
|
136
|
+
else
|
|
137
|
+
logger.fatal(
|
|
138
|
+
"\n\n#{exception.class} (#{exception.message}):\n " +
|
|
139
|
+
clean_backtrace(exception).join("\n ") +
|
|
140
|
+
"\n\n"
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>). By
|
|
147
|
+
# default will call render_optional_error_file. Override this method to provide more user friendly error messages.s
|
|
148
|
+
def rescue_action_in_public(exception) #:doc:
|
|
149
|
+
render_optional_error_file response_code_for_rescue(exception)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Attempts to render a static error page based on the <tt>status_code</tt> thrown,
|
|
153
|
+
# or just return headers if no such file exists. For example, if a 500 error is
|
|
154
|
+
# being handled Rails will first attempt to render the file at <tt>public/500.html</tt>.
|
|
155
|
+
# If the file doesn't exist, the body of the response will be left empty.
|
|
156
|
+
def render_optional_error_file(status_code)
|
|
157
|
+
status = interpret_status(status_code)
|
|
158
|
+
path = "#{Rails.public_path}/#{status[0,3]}.html"
|
|
159
|
+
if File.exist?(path)
|
|
160
|
+
render :file => path, :status => status
|
|
161
|
+
else
|
|
162
|
+
head status
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# True if the request came from localhost, 127.0.0.1. Override this
|
|
167
|
+
# method if you wish to redefine the meaning of a local request to
|
|
168
|
+
# include remote IP addresses or other criteria.
|
|
169
|
+
def local_request? #:doc:
|
|
170
|
+
request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Render detailed diagnostics for unhandled exceptions rescued from
|
|
174
|
+
# a controller action.
|
|
175
|
+
def rescue_action_locally(exception)
|
|
176
|
+
add_variables_to_assigns
|
|
177
|
+
@template.instance_variable_set("@exception", exception)
|
|
178
|
+
@template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub")))
|
|
179
|
+
@template.send!(:assign_variables_from_controller)
|
|
180
|
+
|
|
181
|
+
@template.instance_variable_set("@contents", @template.render_file(template_path_for_local_rescue(exception), false))
|
|
182
|
+
|
|
183
|
+
response.content_type = Mime::HTML
|
|
184
|
+
render_for_file(rescues_path("layout"), response_code_for_rescue(exception))
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Tries to rescue the exception by looking up and calling a registered handler.
|
|
188
|
+
def rescue_action_with_handler(exception)
|
|
189
|
+
if handler = handler_for_rescue(exception)
|
|
190
|
+
if handler.arity != 0
|
|
191
|
+
handler.call(exception)
|
|
192
|
+
else
|
|
193
|
+
handler.call
|
|
194
|
+
end
|
|
195
|
+
true # don't rely on the return value of the handler
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
private
|
|
200
|
+
def perform_action_with_rescue #:nodoc:
|
|
201
|
+
perform_action_without_rescue
|
|
202
|
+
rescue Exception => exception
|
|
203
|
+
rescue_action_with_handler(exception) || rescue_action(exception)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def rescues_path(template_name)
|
|
207
|
+
"#{File.dirname(__FILE__)}/templates/rescues/#{template_name}.erb"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def template_path_for_local_rescue(exception)
|
|
211
|
+
rescues_path(rescue_templates[exception.class.name])
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def response_code_for_rescue(exception)
|
|
215
|
+
rescue_responses[exception.class.name]
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def handler_for_rescue(exception)
|
|
219
|
+
# We go from right to left because pairs are pushed onto rescue_handlers
|
|
220
|
+
# as rescue_from declarations are found.
|
|
221
|
+
_, handler = *rescue_handlers.reverse.detect do |klass_name, handler|
|
|
222
|
+
# The purpose of allowing strings in rescue_from is to support the
|
|
223
|
+
# declaration of handler associations for exception classes whose
|
|
224
|
+
# definition is yet unknown.
|
|
225
|
+
#
|
|
226
|
+
# Since this loop needs the constants it would be inconsistent to
|
|
227
|
+
# assume they should exist at this point. An early raised exception
|
|
228
|
+
# could trigger some other handler and the array could include
|
|
229
|
+
# precisely a string whose corresponding constant has not yet been
|
|
230
|
+
# seen. This is why we are tolerant to unknown constants.
|
|
231
|
+
#
|
|
232
|
+
# Note that this tolerance only matters if the exception was given as
|
|
233
|
+
# a string, otherwise a NameError will be raised by the interpreter
|
|
234
|
+
# itself when rescue_from CONSTANT is executed.
|
|
235
|
+
klass = self.class.const_get(klass_name) rescue nil
|
|
236
|
+
klass ||= klass_name.constantize rescue nil
|
|
237
|
+
exception.is_a?(klass) if klass
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
case handler
|
|
241
|
+
when Symbol
|
|
242
|
+
method(handler)
|
|
243
|
+
when Proc
|
|
244
|
+
handler.bind(self)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def clean_backtrace(exception)
|
|
249
|
+
if backtrace = exception.backtrace
|
|
250
|
+
if defined?(RAILS_ROOT)
|
|
251
|
+
backtrace.map { |line| line.sub RAILS_ROOT, '' }
|
|
252
|
+
else
|
|
253
|
+
backtrace
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|