actionpack 1.13.6 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +1400 -20
- data/MIT-LICENSE +1 -1
- data/README +5 -5
- data/RUNNING_UNIT_TESTS +4 -5
- data/Rakefile +5 -6
- data/install.rb +2 -2
- data/lib/action_controller.rb +11 -15
- data/lib/action_controller/assertions.rb +12 -25
- data/lib/action_controller/assertions/dom_assertions.rb +18 -4
- data/lib/action_controller/assertions/model_assertions.rb +8 -1
- data/lib/action_controller/assertions/response_assertions.rb +35 -12
- data/lib/action_controller/assertions/routing_assertions.rb +56 -12
- data/lib/action_controller/assertions/selector_assertions.rb +105 -38
- data/lib/action_controller/assertions/tag_assertions.rb +28 -15
- data/lib/action_controller/base.rb +318 -250
- data/lib/action_controller/benchmarking.rb +33 -29
- data/lib/action_controller/caching.rb +130 -64
- data/lib/action_controller/cgi_ext.rb +16 -0
- data/lib/action_controller/cgi_ext/{cookie_performance_fix.rb → cookie.rb} +25 -40
- 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 +23 -0
- data/lib/action_controller/cgi_process.rb +34 -57
- data/lib/action_controller/components.rb +19 -36
- data/lib/action_controller/cookies.rb +10 -9
- data/lib/action_controller/dispatcher.rb +195 -0
- data/lib/action_controller/filters.rb +35 -34
- data/lib/action_controller/flash.rb +30 -35
- data/lib/action_controller/helpers.rb +121 -47
- data/lib/action_controller/http_authentication.rb +126 -0
- data/lib/action_controller/integration.rb +105 -101
- data/lib/action_controller/layout.rb +59 -47
- data/lib/action_controller/mime_responds.rb +57 -68
- data/lib/action_controller/mime_type.rb +43 -80
- data/lib/action_controller/mime_types.rb +20 -0
- data/lib/action_controller/polymorphic_routes.rb +88 -0
- data/lib/action_controller/record_identifier.rb +91 -0
- data/lib/action_controller/request.rb +553 -88
- data/lib/action_controller/request_forgery_protection.rb +126 -0
- data/lib/action_controller/request_profiler.rb +138 -0
- data/lib/action_controller/rescue.rb +185 -69
- data/lib/action_controller/resources.rb +211 -172
- data/lib/action_controller/response.rb +49 -8
- data/lib/action_controller/routing.rb +359 -236
- data/lib/action_controller/routing_optimisation.rb +119 -0
- data/lib/action_controller/session/active_record_store.rb +3 -2
- data/lib/action_controller/session/cookie_store.rb +161 -0
- data/lib/action_controller/session/mem_cache_store.rb +9 -16
- data/lib/action_controller/session_management.rb +17 -8
- data/lib/action_controller/streaming.rb +6 -3
- data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
- data/lib/action_controller/templates/rescues/{_trace.rhtml → _trace.erb} +0 -0
- data/lib/action_controller/templates/rescues/{diagnostics.rhtml → diagnostics.erb} +2 -2
- data/lib/action_controller/templates/rescues/{layout.rhtml → layout.erb} +0 -0
- data/lib/action_controller/templates/rescues/{missing_template.rhtml → missing_template.erb} +0 -0
- data/lib/action_controller/templates/rescues/{routing_error.rhtml → routing_error.erb} +0 -0
- data/lib/action_controller/templates/rescues/{template_error.rhtml → template_error.erb} +2 -2
- data/lib/action_controller/templates/rescues/{unknown_action.rhtml → unknown_action.erb} +0 -0
- data/lib/action_controller/test_case.rb +53 -0
- data/lib/action_controller/test_process.rb +59 -46
- data/lib/action_controller/url_rewriter.rb +48 -24
- data/lib/action_controller/vendor/html-scanner/html/document.rb +7 -4
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +11 -6
- data/lib/action_controller/verification.rb +27 -21
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +4 -4
- data/lib/action_view.rb +2 -3
- data/lib/action_view/base.rb +218 -63
- data/lib/action_view/compiled_templates.rb +1 -2
- data/lib/action_view/helpers/active_record_helper.rb +35 -17
- data/lib/action_view/helpers/asset_tag_helper.rb +395 -87
- data/lib/action_view/helpers/atom_feed_helper.rb +111 -0
- data/lib/action_view/helpers/benchmark_helper.rb +12 -5
- data/lib/action_view/helpers/cache_helper.rb +29 -0
- data/lib/action_view/helpers/capture_helper.rb +97 -63
- data/lib/action_view/helpers/date_helper.rb +295 -35
- data/lib/action_view/helpers/debug_helper.rb +6 -2
- data/lib/action_view/helpers/form_helper.rb +354 -111
- data/lib/action_view/helpers/form_options_helper.rb +171 -109
- data/lib/action_view/helpers/form_tag_helper.rb +332 -76
- data/lib/action_view/helpers/javascript_helper.rb +35 -11
- data/lib/action_view/helpers/javascripts/controls.js +484 -354
- data/lib/action_view/helpers/javascripts/dragdrop.js +88 -58
- data/lib/action_view/helpers/javascripts/effects.js +396 -364
- data/lib/action_view/helpers/javascripts/prototype.js +2817 -1107
- data/lib/action_view/helpers/number_helper.rb +84 -60
- data/lib/action_view/helpers/prototype_helper.rb +419 -43
- 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 +223 -0
- data/lib/action_view/helpers/scriptaculous_helper.rb +63 -4
- data/lib/action_view/helpers/tag_helper.rb +69 -39
- data/lib/action_view/helpers/text_helper.rb +221 -148
- data/lib/action_view/helpers/url_helper.rb +283 -165
- data/lib/action_view/partials.rb +134 -62
- data/lib/action_view/template_error.rb +4 -12
- data/lib/actionpack.rb +1 -0
- data/test/abstract_unit.rb +21 -1
- data/test/action_view_test.rb +26 -0
- data/test/active_record_unit.rb +12 -20
- data/test/activerecord/active_record_store_test.rb +2 -2
- data/test/activerecord/render_partial_with_record_identification_test.rb +74 -0
- data/test/controller/action_pack_assertions_test.rb +21 -152
- data/test/controller/addresses_render_test.rb +2 -7
- data/test/controller/assert_select_test.rb +120 -14
- data/test/controller/base_test.rb +11 -13
- data/test/controller/caching_test.rb +125 -5
- data/test/controller/capture_test.rb +23 -16
- data/test/controller/cgi_test.rb +66 -391
- data/test/controller/components_test.rb +31 -42
- data/test/controller/content_type_test.rb +1 -1
- data/test/controller/cookie_test.rb +42 -14
- data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -42
- data/test/controller/dispatcher_test.rb +123 -0
- data/test/controller/fake_models.rb +5 -0
- data/test/controller/filters_test.rb +44 -7
- data/test/controller/flash_test.rb +46 -2
- data/test/controller/fragment_store_setting_test.rb +10 -8
- data/test/controller/helper_test.rb +19 -2
- data/test/controller/html-scanner/document_test.rb +124 -0
- data/test/controller/html-scanner/node_test.rb +69 -0
- data/test/controller/html-scanner/sanitizer_test.rb +250 -0
- data/test/controller/html-scanner/tag_node_test.rb +239 -0
- data/test/controller/html-scanner/text_node_test.rb +51 -0
- data/test/controller/html-scanner/tokenizer_test.rb +125 -0
- data/test/controller/http_authentication_test.rb +54 -0
- data/test/controller/integration_test.rb +12 -26
- data/test/controller/layout_test.rb +64 -12
- data/test/controller/mime_responds_test.rb +193 -38
- data/test/controller/mime_type_test.rb +30 -8
- data/test/controller/new_render_test.rb +104 -22
- data/test/controller/polymorphic_routes_test.rb +98 -0
- data/test/controller/record_identifier_test.rb +103 -0
- data/test/controller/redirect_test.rb +120 -18
- data/test/controller/render_test.rb +195 -45
- data/test/controller/request_forgery_protection_test.rb +217 -0
- data/test/controller/request_test.rb +545 -27
- data/test/controller/rescue_test.rb +501 -0
- data/test/controller/resources_test.rb +258 -132
- data/test/controller/routing_test.rb +502 -106
- data/test/controller/selector_test.rb +5 -5
- data/test/controller/send_file_test.rb +17 -7
- data/test/controller/session/cookie_store_test.rb +246 -0
- data/test/controller/session/mem_cache_store_test.rb +182 -0
- data/test/controller/session_fixation_test.rb +8 -11
- data/test/controller/session_management_test.rb +7 -7
- data/test/controller/test_test.rb +150 -38
- data/test/controller/url_rewriter_test.rb +87 -12
- data/test/controller/verification_test.rb +11 -0
- data/test/controller/view_paths_test.rb +137 -0
- data/test/controller/webservice_test.rb +11 -75
- data/test/fixtures/addresses/{list.rhtml → list.erb} +0 -0
- data/test/fixtures/db_definitions/sqlite.sql +2 -1
- data/test/fixtures/developer.rb +2 -0
- data/test/fixtures/fun/games/{hello_world.rhtml → hello_world.erb} +0 -0
- data/test/fixtures/helpers/fun/pdf_helper.rb +1 -1
- data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
- data/test/fixtures/layouts/{builder.rxml → builder.builder} +0 -0
- data/test/fixtures/layouts/{standard.rhtml → standard.erb} +0 -0
- data/test/fixtures/layouts/{talk_from_action.rhtml → talk_from_action.erb} +0 -0
- data/test/fixtures/layouts/{yield.rhtml → yield.erb} +0 -0
- data/test/fixtures/multipart/binary_file +0 -0
- data/test/fixtures/multipart/bracketed_param +5 -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/public/404.html +1 -0
- data/test/fixtures/public/500.html +1 -0
- data/test/fixtures/public/javascripts/application.js +0 -1
- data/test/fixtures/public/javascripts/bank.js +1 -0
- data/test/fixtures/public/javascripts/robber.js +1 -0
- data/test/fixtures/public/stylesheets/bank.css +1 -0
- data/test/fixtures/public/stylesheets/robber.css +1 -0
- data/test/fixtures/replies.yml +2 -0
- data/test/fixtures/reply.rb +2 -1
- data/test/fixtures/respond_to/{all_types_with_layout.rhtml → all_types_with_layout.html.erb} +0 -0
- data/test/fixtures/respond_to/{all_types_with_layout.rjs → all_types_with_layout.js.rjs} +0 -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.rhtml → using_defaults.html.erb} +0 -0
- data/test/fixtures/respond_to/{using_defaults.rjs → using_defaults.js.rjs} +0 -0
- data/test/fixtures/respond_to/{using_defaults.rxml → using_defaults.xml.builder} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rhtml → using_defaults_with_type_list.html.erb} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rjs → using_defaults_with_type_list.js.rjs} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rxml → using_defaults_with_type_list.xml.builder} +0 -0
- data/test/fixtures/scope/test/{modgreet.rhtml → modgreet.erb} +0 -0
- data/test/fixtures/test/{_customer.rhtml → _customer.erb} +0 -0
- data/test/fixtures/test/{_customer_greeting.rhtml → _customer_greeting.erb} +0 -0
- data/test/fixtures/test/_hash_greeting.erb +1 -0
- data/test/fixtures/test/_hash_object.erb +2 -0
- data/test/fixtures/test/{_hello.rxml → _hello.builder} +0 -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.rhtml → _partial_only.erb} +0 -0
- data/test/fixtures/test/{_person.rhtml → _person.erb} +0 -0
- data/test/fixtures/test/{action_talk_to_layout.rhtml → action_talk_to_layout.erb} +0 -0
- data/test/fixtures/test/{block_content_for.rhtml → block_content_for.erb} +0 -0
- data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
- data/test/fixtures/test/{capturing.rhtml → capturing.erb} +0 -0
- data/test/fixtures/test/{content_for.rhtml → content_for.erb} +0 -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/dot.directory/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
- data/test/fixtures/test/{erb_content_for.rhtml → erb_content_for.erb} +0 -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.rhtml → greeting.erb} +0 -0
- data/test/fixtures/test/{hello.rxml → hello.builder} +0 -0
- data/test/fixtures/test/{hello_world.rxml → hello_world.builder} +0 -0
- data/test/fixtures/test/{hello_world.rhtml → hello_world.erb} +0 -0
- data/test/fixtures/test/{hello_world_container.rxml → hello_world_container.builder} +0 -0
- data/test/fixtures/test/{hello_world_with_layout_false.rhtml → hello_world_with_layout_false.erb} +0 -0
- data/test/fixtures/test/{hello_xml_world.rxml → hello_xml_world.builder} +0 -0
- data/test/fixtures/test/list.erb +1 -0
- data/test/fixtures/test/{non_erb_block_content_for.rxml → non_erb_block_content_for.builder} +0 -0
- data/test/fixtures/test/{potential_conflicts.rhtml → potential_conflicts.erb} +0 -0
- data/test/fixtures/test/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
- data/test/fixtures/test/{render_file_with_locals.rhtml → render_file_with_locals.erb} +0 -0
- data/test/fixtures/test/{render_to_string_test.rhtml → render_to_string_test.erb} +0 -0
- data/test/fixtures/test/{update_element_with_capture.rhtml → update_element_with_capture.erb} +0 -0
- data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
- data/test/fixtures/topic.rb +1 -1
- data/test/template/active_record_helper_test.rb +67 -20
- data/test/template/asset_tag_helper_test.rb +222 -54
- data/test/template/atom_feed_helper_test.rb +101 -0
- data/test/template/benchmark_helper_test.rb +2 -2
- data/test/template/compiled_templates_test.rb +76 -32
- data/test/template/date_helper_test.rb +125 -9
- data/test/template/form_helper_test.rb +326 -33
- data/test/template/form_options_helper_test.rb +822 -15
- data/test/template/form_tag_helper_test.rb +96 -30
- data/test/template/javascript_helper_test.rb +61 -13
- data/test/template/number_helper_test.rb +12 -11
- data/test/template/prototype_helper_test.rb +185 -24
- data/test/template/sanitize_helper_test.rb +49 -0
- data/test/template/scriptaculous_helper_test.rb +9 -3
- data/test/template/tag_helper_test.rb +13 -2
- data/test/template/text_helper_test.rb +38 -52
- data/test/template/url_helper_test.rb +216 -46
- metadata +144 -116
- data/examples/.htaccess +0 -24
- data/examples/address_book/index.rhtml +0 -33
- data/examples/address_book/layout.rhtml +0 -8
- data/examples/address_book_controller.cgi +0 -9
- data/examples/address_book_controller.fcgi +0 -6
- data/examples/address_book_controller.rb +0 -52
- data/examples/address_book_controller.rbx +0 -4
- data/examples/benchmark.rb +0 -52
- data/examples/benchmark_with_ar.fcgi +0 -89
- data/examples/blog_controller.cgi +0 -53
- data/examples/debate/index.rhtml +0 -14
- data/examples/debate/new_topic.rhtml +0 -22
- data/examples/debate/topic.rhtml +0 -32
- data/examples/debate_controller.cgi +0 -57
- data/lib/action_controller/assertions/deprecated_assertions.rb +0 -228
- data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -36
- data/lib/action_controller/cgi_ext/cgi_methods.rb +0 -211
- data/lib/action_controller/cgi_ext/pstore_performance_fix.rb +0 -30
- data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +0 -95
- data/lib/action_controller/cgi_ext/session_performance_fix.rb +0 -30
- data/lib/action_controller/deprecated_dependencies.rb +0 -65
- data/lib/action_controller/deprecated_redirects.rb +0 -17
- data/lib/action_controller/deprecated_request_methods.rb +0 -34
- data/lib/action_controller/macros/auto_complete.rb +0 -53
- data/lib/action_controller/macros/in_place_editing.rb +0 -33
- data/lib/action_controller/pagination.rb +0 -408
- data/lib/action_controller/scaffolding.rb +0 -189
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -44
- data/lib/action_controller/templates/scaffolds/edit.rhtml +0 -7
- data/lib/action_controller/templates/scaffolds/layout.rhtml +0 -69
- data/lib/action_controller/templates/scaffolds/list.rhtml +0 -27
- data/lib/action_controller/templates/scaffolds/new.rhtml +0 -6
- data/lib/action_controller/templates/scaffolds/show.rhtml +0 -9
- data/lib/action_controller/vendor/xml_node.rb +0 -97
- data/lib/action_view/helpers/deprecated_helper.rb +0 -37
- data/lib/action_view/helpers/java_script_macros_helper.rb +0 -233
- data/lib/action_view/helpers/pagination_helper.rb +0 -86
- data/test/activerecord/active_record_assertions_test.rb +0 -92
- data/test/activerecord/pagination_test.rb +0 -165
- data/test/controller/deprecated_instance_variables_test.rb +0 -48
- data/test/controller/raw_post_test.rb +0 -68
- data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +0 -1
- data/test/fixtures/respond_to/layouts/standard.rhtml +0 -1
- data/test/fixtures/test/_hash_object.rhtml +0 -1
- data/test/fixtures/test/list.rhtml +0 -1
- data/test/template/deprecated_helper_test.rb +0 -36
- data/test/template/deprecated_instance_variables_test.rb +0 -43
- data/test/template/java_script_macros_helper_test.rb +0 -109
@@ -0,0 +1,126 @@
|
|
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
|
+
module ClassMethods
|
17
|
+
# Protect a controller's actions from CSRF attacks by ensuring that all forms are coming from the current web application, not
|
18
|
+
# a forged link from another site. This is done by embedding a token based on the session (which an attacker wouldn't know) in
|
19
|
+
# all forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
|
20
|
+
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
|
21
|
+
# scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
|
22
|
+
#
|
23
|
+
# You turn this on with the #protect_from_forgery method, which will perform the check and raise
|
24
|
+
# an ActionController::InvalidAuthenticityToken if the token doesn't match what was expected. And it will add
|
25
|
+
# a _authenticity_token parameter to all forms that are automatically generated by Rails. You can customize the error message
|
26
|
+
# given through public/422.html.
|
27
|
+
#
|
28
|
+
# Learn more about CSRF (Cross-Site Request Forgery) attacks:
|
29
|
+
#
|
30
|
+
# * http://isc.sans.org/diary.html?storyid=1750
|
31
|
+
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
32
|
+
#
|
33
|
+
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
|
34
|
+
# There are a few guidelines you should follow:
|
35
|
+
#
|
36
|
+
# * Keep your GET requests safe and idempotent. More reading material:
|
37
|
+
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
|
38
|
+
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
|
39
|
+
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
|
40
|
+
#
|
41
|
+
# If you need to construct a request yourself, but still want to take advantage of forgery protection, you can grab the
|
42
|
+
# authenticity_token using the form_authenticity_token helper method and make it part of the parameters yourself.
|
43
|
+
#
|
44
|
+
# Example:
|
45
|
+
#
|
46
|
+
# class FooController < ApplicationController
|
47
|
+
# # uses the cookie session store (then you don't need a separate :secret)
|
48
|
+
# protect_from_forgery :except => :index
|
49
|
+
#
|
50
|
+
# # uses one of the other session stores that uses a session_id value.
|
51
|
+
# protect_from_forgery :secret => 'my-little-pony', :except => :index
|
52
|
+
#
|
53
|
+
# # you can disable csrf protection on controller-by-controller basis:
|
54
|
+
# skip_before_filter :verify_authenticity_token
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# Valid Options:
|
58
|
+
#
|
59
|
+
# * <tt>:only/:except</tt> - passed to the before_filter call. Set which actions are verified.
|
60
|
+
# * <tt>:secret</tt> - Custom salt used to generate the form_authenticity_token.
|
61
|
+
# Leave this off if you are using the cookie session store.
|
62
|
+
# * <tt>:digest</tt> - Message digest used for hashing. Defaults to 'SHA1'
|
63
|
+
def protect_from_forgery(options = {})
|
64
|
+
self.request_forgery_protection_token ||= :authenticity_token
|
65
|
+
before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except)
|
66
|
+
request_forgery_protection_options.update(options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
|
72
|
+
def verify_authenticity_token
|
73
|
+
verified_request? || raise(ActionController::InvalidAuthenticityToken)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns true or false if a request is verified. Checks:
|
77
|
+
#
|
78
|
+
# * is the format restricted? By default, only HTML and AJAX requests are checked.
|
79
|
+
# * is it a GET request? Gets should be safe and idempotent
|
80
|
+
# * Does the form_authenticity_token match the given _token value from the params?
|
81
|
+
def verified_request?
|
82
|
+
!protect_against_forgery? ||
|
83
|
+
request.method == :get ||
|
84
|
+
!verifiable_request_format? ||
|
85
|
+
form_authenticity_token == params[request_forgery_protection_token]
|
86
|
+
end
|
87
|
+
|
88
|
+
def verifiable_request_format?
|
89
|
+
request.format.html? || request.format.js?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Sets the token value for the current session. Pass a :secret option in #protect_from_forgery to add a custom salt to the hash.
|
93
|
+
def form_authenticity_token
|
94
|
+
@form_authenticity_token ||= if request_forgery_protection_options[:secret]
|
95
|
+
authenticity_token_from_session_id
|
96
|
+
elsif session.respond_to?(:dbman) && session.dbman.respond_to?(:generate_digest)
|
97
|
+
authenticity_token_from_cookie_session
|
98
|
+
elsif session.nil?
|
99
|
+
raise InvalidAuthenticityToken, "Request Forgery Protection requires a valid session. Use #allow_forgery_protection to disable it, or use a valid session."
|
100
|
+
else
|
101
|
+
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)."
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Generates a unique digest using the session_id and the CSRF secret.
|
106
|
+
def authenticity_token_from_session_id
|
107
|
+
key = if request_forgery_protection_options[:secret].respond_to?(:call)
|
108
|
+
request_forgery_protection_options[:secret].call(@session)
|
109
|
+
else
|
110
|
+
request_forgery_protection_options[:secret]
|
111
|
+
end
|
112
|
+
digest = request_forgery_protection_options[:digest] ||= 'SHA1'
|
113
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(digest), key.to_s, session.session_id.to_s)
|
114
|
+
end
|
115
|
+
|
116
|
+
# No secret was given, so assume this is a cookie session store.
|
117
|
+
def authenticity_token_from_cookie_session
|
118
|
+
session[:csrf_id] ||= CGI::Session.generate_unique_id
|
119
|
+
session.dbman.generate_digest(session[:csrf_id])
|
120
|
+
end
|
121
|
+
|
122
|
+
def protect_against_forgery?
|
123
|
+
allow_forgery_protection && request_forgery_protection_token
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,138 @@
|
|
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(File.read(script_path))
|
17
|
+
reset!
|
18
|
+
end
|
19
|
+
|
20
|
+
def benchmark(n)
|
21
|
+
@quiet = true
|
22
|
+
print ' '
|
23
|
+
result = Benchmark.realtime do
|
24
|
+
n.times do |i|
|
25
|
+
run
|
26
|
+
print i % 10 == 0 ? 'x' : '.'
|
27
|
+
$stdout.flush
|
28
|
+
end
|
29
|
+
end
|
30
|
+
puts
|
31
|
+
result
|
32
|
+
ensure
|
33
|
+
@quiet = false
|
34
|
+
end
|
35
|
+
|
36
|
+
def say(message)
|
37
|
+
puts " #{message}" unless @quiet
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def define_run_method(script)
|
42
|
+
instance_eval "def run; #{script}; end", __FILE__, __LINE__
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
attr_reader :options
|
48
|
+
|
49
|
+
def initialize(options = {})
|
50
|
+
@options = default_options.merge(options)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def self.run(args = nil, options = {})
|
55
|
+
profiler = new(options)
|
56
|
+
profiler.parse_options(args) if args
|
57
|
+
profiler.run
|
58
|
+
end
|
59
|
+
|
60
|
+
def run
|
61
|
+
sandbox = Sandbox.new(options[:script])
|
62
|
+
|
63
|
+
puts 'Warming up once'
|
64
|
+
|
65
|
+
elapsed = warmup(sandbox)
|
66
|
+
puts '%.2f sec, %d requests, %d req/sec' % [elapsed, sandbox.request_count, sandbox.request_count / elapsed]
|
67
|
+
puts "\n#{options[:benchmark] ? 'Benchmarking' : 'Profiling'} #{options[:n]}x"
|
68
|
+
|
69
|
+
options[:benchmark] ? benchmark(sandbox) : profile(sandbox)
|
70
|
+
end
|
71
|
+
|
72
|
+
def profile(sandbox)
|
73
|
+
load_ruby_prof
|
74
|
+
|
75
|
+
results = RubyProf.profile { benchmark(sandbox) }
|
76
|
+
|
77
|
+
show_profile_results results
|
78
|
+
results
|
79
|
+
end
|
80
|
+
|
81
|
+
def benchmark(sandbox)
|
82
|
+
sandbox.request_count = 0
|
83
|
+
elapsed = sandbox.benchmark(options[:n]).to_f
|
84
|
+
count = sandbox.request_count.to_i
|
85
|
+
puts '%.2f sec, %d requests, %d req/sec' % [elapsed, count, count / elapsed]
|
86
|
+
end
|
87
|
+
|
88
|
+
def warmup(sandbox)
|
89
|
+
Benchmark.realtime { sandbox.run }
|
90
|
+
end
|
91
|
+
|
92
|
+
def default_options
|
93
|
+
{ :n => 100, :open => 'open %s &' }
|
94
|
+
end
|
95
|
+
|
96
|
+
# Parse command-line options
|
97
|
+
def parse_options(args)
|
98
|
+
OptionParser.new do |opt|
|
99
|
+
opt.banner = "USAGE: #{$0} [options] [session script path]"
|
100
|
+
|
101
|
+
opt.on('-n', '--times [0000]', 'How many requests to process. Defaults to 100.') { |v| options[:n] = v.to_i }
|
102
|
+
opt.on('-b', '--benchmark', 'Benchmark instead of profiling') { |v| options[:benchmark] = v }
|
103
|
+
opt.on('--open [CMD]', 'Command to open profile results. Defaults to "open %s &"') { |v| options[:open] = v }
|
104
|
+
opt.on('-h', '--help', 'Show this help') { puts opt; exit }
|
105
|
+
|
106
|
+
opt.parse args
|
107
|
+
|
108
|
+
if args.empty?
|
109
|
+
puts opt
|
110
|
+
exit
|
111
|
+
end
|
112
|
+
options[:script] = args.pop
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
protected
|
117
|
+
def load_ruby_prof
|
118
|
+
begin
|
119
|
+
require 'ruby-prof'
|
120
|
+
#RubyProf.measure_mode = RubyProf::ALLOCATED_OBJECTS
|
121
|
+
rescue LoadError
|
122
|
+
abort '`gem install ruby-prof` to use the profiler'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def show_profile_results(results)
|
127
|
+
File.open "#{RAILS_ROOT}/tmp/profile-graph.html", 'w' do |file|
|
128
|
+
RubyProf::GraphHtmlPrinter.new(results).print(file)
|
129
|
+
`#{options[:open] % file.path}` if options[:open]
|
130
|
+
end
|
131
|
+
|
132
|
+
File.open "#{RAILS_ROOT}/tmp/profile-flat.txt", 'w' do |file|
|
133
|
+
RubyProf::FlatPrinter.new(results).print(file)
|
134
|
+
`#{options[:open] % file.path}` if options[:open]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -1,22 +1,110 @@
|
|
1
1
|
module ActionController #:nodoc:
|
2
|
-
# Actions that fail to perform as expected throw exceptions. These exceptions can either be rescued for the public view
|
2
|
+
# Actions that fail to perform as expected throw exceptions. These exceptions can either be rescued for the public view
|
3
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
|
-
#
|
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.
|
6
8
|
#
|
7
|
-
# You can
|
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.
|
8
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
|
+
'ActionController::MissingTemplate' => 'missing_template',
|
30
|
+
'ActionController::RoutingError' => 'routing_error',
|
31
|
+
'ActionController::UnknownAction' => 'unknown_action',
|
32
|
+
'ActionView::TemplateError' => 'template_error'
|
33
|
+
}
|
34
|
+
|
9
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
|
+
|
10
47
|
base.extend(ClassMethods)
|
11
48
|
base.class_eval do
|
12
49
|
alias_method_chain :perform_action, :rescue
|
13
50
|
end
|
14
51
|
end
|
15
52
|
|
16
|
-
module ClassMethods
|
17
|
-
def process_with_exception(request, response, exception)
|
53
|
+
module ClassMethods
|
54
|
+
def process_with_exception(request, response, exception) #:nodoc:
|
18
55
|
new.process(request, response, :rescue_action, exception)
|
19
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 :with option with the name of a method or a Proc
|
62
|
+
# object to be called to handle them. Alternatively a block can be given.
|
63
|
+
#
|
64
|
+
# Handlers that take one argument will be called with the exception, so
|
65
|
+
# that the exception can be inspected when dealing with it.
|
66
|
+
#
|
67
|
+
# Handlers are inherited. They are searched from right to left, from
|
68
|
+
# bottom to top, and up the hierarchy. The handler of the first class for
|
69
|
+
# which exception.is_a?(klass) holds true is the one invoked, if any.
|
70
|
+
#
|
71
|
+
# class ApplicationController < ActionController::Base
|
72
|
+
# rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
|
73
|
+
# rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
|
74
|
+
#
|
75
|
+
# rescue_from 'MyAppError::Base' do |exception|
|
76
|
+
# render :xml => exception, :status => 500
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# protected
|
80
|
+
# def deny_access
|
81
|
+
# ...
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# def show_errors(exception)
|
85
|
+
# exception.record.new_record? ? ...
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
def rescue_from(*klasses, &block)
|
89
|
+
options = klasses.extract_options!
|
90
|
+
unless options.has_key?(:with)
|
91
|
+
block_given? ? options[:with] = block : raise(ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument.")
|
92
|
+
end
|
93
|
+
|
94
|
+
klasses.each do |klass|
|
95
|
+
key = if klass.is_a?(Class) && klass <= Exception
|
96
|
+
klass.name
|
97
|
+
elsif klass.is_a?(String)
|
98
|
+
klass
|
99
|
+
else
|
100
|
+
raise(ArgumentError, "#{klass} is neither an Exception nor a String")
|
101
|
+
end
|
102
|
+
|
103
|
+
# Order is important, we put the pair at the end. When dealing with an
|
104
|
+
# exception we will follow the documented order going from right to left.
|
105
|
+
rescue_handlers << [key, options[:with]]
|
106
|
+
end
|
107
|
+
end
|
20
108
|
end
|
21
109
|
|
22
110
|
protected
|
@@ -25,6 +113,12 @@ module ActionController #:nodoc:
|
|
25
113
|
log_error(exception) if logger
|
26
114
|
erase_results if performed?
|
27
115
|
|
116
|
+
# Let the exception alter the response if it wants.
|
117
|
+
# For example, MethodNotAllowed sets the Allow header.
|
118
|
+
if exception.respond_to?(:handle_response!)
|
119
|
+
exception.handle_response!(response)
|
120
|
+
end
|
121
|
+
|
28
122
|
if consider_all_requests_local || local_request?
|
29
123
|
rescue_action_locally(exception)
|
30
124
|
else
|
@@ -47,96 +141,118 @@ module ActionController #:nodoc:
|
|
47
141
|
end
|
48
142
|
end
|
49
143
|
|
50
|
-
# Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>).
|
144
|
+
# Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>). By
|
145
|
+
# default will call render_optional_error_file. Override this method to provide more user friendly error messages.s
|
51
146
|
def rescue_action_in_public(exception) #:doc:
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
147
|
+
render_optional_error_file response_code_for_rescue(exception)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Attempts to render a static error page based on the <tt>status_code</tt> thrown,
|
151
|
+
# or just return headers if no such file exists. For example, if a 500 error is
|
152
|
+
# being handled Rails will first attempt to render the file at <tt>public/500.html</tt>.
|
153
|
+
# If the file doesn't exist, the body of the response will be left empty.
|
154
|
+
def render_optional_error_file(status_code)
|
155
|
+
status = interpret_status(status_code)
|
156
|
+
path = "#{RAILS_ROOT}/public/#{status[0,3]}.html"
|
157
|
+
if File.exists?(path)
|
158
|
+
render :file => path, :status => status
|
159
|
+
else
|
160
|
+
head status
|
57
161
|
end
|
58
162
|
end
|
59
163
|
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
164
|
+
# True if the request came from localhost, 127.0.0.1. Override this
|
165
|
+
# method if you wish to redefine the meaning of a local request to
|
166
|
+
# include remote IP addresses or other criteria.
|
63
167
|
def local_request? #:doc:
|
64
|
-
|
168
|
+
request.remote_addr == LOCALHOST and request.remote_ip == LOCALHOST
|
65
169
|
end
|
66
170
|
|
67
|
-
#
|
171
|
+
# Render detailed diagnostics for unhandled exceptions rescued from
|
172
|
+
# a controller action.
|
68
173
|
def rescue_action_locally(exception)
|
69
174
|
add_variables_to_assigns
|
70
175
|
@template.instance_variable_set("@exception", exception)
|
71
|
-
@template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub")))
|
72
|
-
@template.send(:assign_variables_from_controller)
|
176
|
+
@template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub")))
|
177
|
+
@template.send!(:assign_variables_from_controller)
|
73
178
|
|
74
179
|
@template.instance_variable_set("@contents", @template.render_file(template_path_for_local_rescue(exception), false))
|
75
|
-
|
180
|
+
|
76
181
|
response.content_type = Mime::HTML
|
77
|
-
|
182
|
+
render_for_file(rescues_path("layout"), response_code_for_rescue(exception))
|
78
183
|
end
|
79
|
-
|
80
|
-
private
|
81
|
-
def perform_action_with_rescue #:nodoc:
|
82
|
-
begin
|
83
|
-
perform_action_without_rescue
|
84
|
-
rescue Exception => exception # errors from action performed
|
85
|
-
if defined?(Breakpoint) && params["BP-RETRY"]
|
86
|
-
msg = exception.backtrace.first
|
87
|
-
if md = /^(.+?):(\d+)(?::in `(.+)')?$/.match(msg) then
|
88
|
-
origin_file, origin_line = md[1], md[2].to_i
|
89
|
-
|
90
|
-
set_trace_func(lambda do |type, file, line, method, context, klass|
|
91
|
-
if file == origin_file and line == origin_line then
|
92
|
-
set_trace_func(nil)
|
93
|
-
params["BP-RETRY"] = false
|
94
|
-
|
95
|
-
callstack = caller
|
96
|
-
callstack.slice!(0) if callstack.first["rescue.rb"]
|
97
|
-
file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
|
98
|
-
|
99
|
-
message = "Exception at #{file}:#{line}#{" in `#{method}'" if method}." # `� ( for ruby-mode)
|
100
|
-
|
101
|
-
Breakpoint.handle_breakpoint(context, message, file, line)
|
102
|
-
end
|
103
|
-
end)
|
104
|
-
|
105
|
-
retry
|
106
|
-
end
|
107
|
-
end
|
108
184
|
|
109
|
-
|
185
|
+
# Tries to rescue the exception by looking up and calling a registered handler.
|
186
|
+
def rescue_action_with_handler(exception)
|
187
|
+
if handler = handler_for_rescue(exception)
|
188
|
+
if handler.arity != 0
|
189
|
+
handler.call(exception)
|
190
|
+
else
|
191
|
+
handler.call
|
192
|
+
end
|
193
|
+
true # don't rely on the return value of the handler
|
110
194
|
end
|
111
195
|
end
|
112
196
|
|
197
|
+
private
|
198
|
+
def perform_action_with_rescue #:nodoc:
|
199
|
+
perform_action_without_rescue
|
200
|
+
rescue Exception => exception # errors from action performed
|
201
|
+
return if rescue_action_with_handler(exception)
|
202
|
+
|
203
|
+
rescue_action(exception)
|
204
|
+
end
|
205
|
+
|
113
206
|
def rescues_path(template_name)
|
114
|
-
File.dirname(__FILE__)
|
207
|
+
"#{File.dirname(__FILE__)}/templates/rescues/#{template_name}.erb"
|
115
208
|
end
|
116
209
|
|
117
210
|
def template_path_for_local_rescue(exception)
|
118
|
-
rescues_path(
|
119
|
-
case exception
|
120
|
-
when MissingTemplate then "missing_template"
|
121
|
-
when RoutingError then "routing_error"
|
122
|
-
when UnknownAction then "unknown_action"
|
123
|
-
when ActionView::TemplateError then "template_error"
|
124
|
-
else "diagnostics"
|
125
|
-
end
|
126
|
-
)
|
211
|
+
rescues_path(rescue_templates[exception.class.name])
|
127
212
|
end
|
128
|
-
|
213
|
+
|
129
214
|
def response_code_for_rescue(exception)
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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)
|
135
245
|
end
|
136
246
|
end
|
137
|
-
|
247
|
+
|
138
248
|
def clean_backtrace(exception)
|
139
|
-
|
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
|
140
256
|
end
|
141
257
|
end
|
142
258
|
end
|