actionpack_csi 2.3.5.p6
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 +5184 -0
- data/MIT-LICENSE +21 -0
- data/README +409 -0
- data/RUNNING_UNIT_TESTS +24 -0
- data/Rakefile +160 -0
- data/install.rb +30 -0
- data/lib/action_controller/assertions/dom_assertions.rb +55 -0
- data/lib/action_controller/assertions/model_assertions.rb +21 -0
- data/lib/action_controller/assertions/response_assertions.rb +160 -0
- data/lib/action_controller/assertions/routing_assertions.rb +146 -0
- data/lib/action_controller/assertions/selector_assertions.rb +638 -0
- data/lib/action_controller/assertions/tag_assertions.rb +127 -0
- data/lib/action_controller/base.rb +1423 -0
- data/lib/action_controller/benchmarking.rb +107 -0
- data/lib/action_controller/caching/actions.rb +177 -0
- data/lib/action_controller/caching/fragments.rb +120 -0
- data/lib/action_controller/caching/pages.rb +152 -0
- data/lib/action_controller/caching/sweeper.rb +45 -0
- data/lib/action_controller/caching/sweeping.rb +55 -0
- data/lib/action_controller/caching.rb +71 -0
- data/lib/action_controller/cgi_ext/cookie.rb +112 -0
- data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
- data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
- data/lib/action_controller/cgi_ext.rb +15 -0
- data/lib/action_controller/cgi_process.rb +77 -0
- data/lib/action_controller/cookies.rb +95 -0
- data/lib/action_controller/dispatcher.rb +133 -0
- data/lib/action_controller/failsafe.rb +86 -0
- data/lib/action_controller/filters.rb +680 -0
- data/lib/action_controller/flash.rb +171 -0
- data/lib/action_controller/headers.rb +33 -0
- data/lib/action_controller/helpers.rb +225 -0
- data/lib/action_controller/http_authentication.rb +309 -0
- data/lib/action_controller/integration.rb +692 -0
- data/lib/action_controller/layout.rb +286 -0
- data/lib/action_controller/middleware_stack.rb +119 -0
- data/lib/action_controller/middlewares.rb +14 -0
- data/lib/action_controller/mime_responds.rb +193 -0
- data/lib/action_controller/mime_type.rb +212 -0
- data/lib/action_controller/mime_types.rb +21 -0
- data/lib/action_controller/params_parser.rb +77 -0
- data/lib/action_controller/performance_test.rb +15 -0
- data/lib/action_controller/polymorphic_routes.rb +189 -0
- data/lib/action_controller/rack_lint_patch.rb +36 -0
- data/lib/action_controller/record_identifier.rb +104 -0
- data/lib/action_controller/reloader.rb +54 -0
- data/lib/action_controller/request.rb +493 -0
- data/lib/action_controller/request_forgery_protection.rb +113 -0
- data/lib/action_controller/rescue.rb +183 -0
- data/lib/action_controller/resources.rb +682 -0
- data/lib/action_controller/response.rb +239 -0
- data/lib/action_controller/routing/builder.rb +197 -0
- data/lib/action_controller/routing/optimisations.rb +130 -0
- data/lib/action_controller/routing/recognition_optimisation.rb +167 -0
- data/lib/action_controller/routing/route.rb +265 -0
- data/lib/action_controller/routing/route_set.rb +502 -0
- data/lib/action_controller/routing/routing_ext.rb +49 -0
- data/lib/action_controller/routing/segments.rb +343 -0
- data/lib/action_controller/routing.rb +388 -0
- data/lib/action_controller/session/abstract_store.rb +181 -0
- data/lib/action_controller/session/cookie_store.rb +221 -0
- data/lib/action_controller/session/mem_cache_store.rb +51 -0
- data/lib/action_controller/session_management.rb +54 -0
- data/lib/action_controller/status_codes.rb +88 -0
- data/lib/action_controller/streaming.rb +181 -0
- data/lib/action_controller/string_coercion.rb +29 -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 +209 -0
- data/lib/action_controller/test_process.rb +580 -0
- data/lib/action_controller/translation.rb +13 -0
- data/lib/action_controller/uploaded_file.rb +44 -0
- data/lib/action_controller/url_rewriter.rb +216 -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/vendor/html-scanner.rb +16 -0
- data/lib/action_controller/verification.rb +130 -0
- data/lib/action_controller.rb +113 -0
- data/lib/action_pack/version.rb +9 -0
- data/lib/action_pack.rb +24 -0
- data/lib/action_view/base.rb +362 -0
- data/lib/action_view/erb/util.rb +44 -0
- data/lib/action_view/helpers/active_record_helper.rb +305 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +694 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
- data/lib/action_view/helpers/benchmark_helper.rb +54 -0
- data/lib/action_view/helpers/cache_helper.rb +39 -0
- data/lib/action_view/helpers/capture_helper.rb +136 -0
- data/lib/action_view/helpers/date_helper.rb +988 -0
- data/lib/action_view/helpers/debug_helper.rb +38 -0
- data/lib/action_view/helpers/form_helper.rb +1074 -0
- data/lib/action_view/helpers/form_options_helper.rb +600 -0
- data/lib/action_view/helpers/form_tag_helper.rb +487 -0
- data/lib/action_view/helpers/javascript_helper.rb +208 -0
- data/lib/action_view/helpers/number_helper.rb +308 -0
- data/lib/action_view/helpers/prototype_helper.rb +1305 -0
- data/lib/action_view/helpers/raw_output_helper.rb +9 -0
- data/lib/action_view/helpers/record_identification_helper.rb +20 -0
- data/lib/action_view/helpers/record_tag_helper.rb +58 -0
- data/lib/action_view/helpers/sanitize_helper.rb +259 -0
- data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
- data/lib/action_view/helpers/tag_helper.rb +150 -0
- data/lib/action_view/helpers/text_helper.rb +587 -0
- data/lib/action_view/helpers/translation_helper.rb +39 -0
- data/lib/action_view/helpers/url_helper.rb +639 -0
- data/lib/action_view/helpers.rb +59 -0
- data/lib/action_view/inline_template.rb +19 -0
- data/lib/action_view/locale/en.yml +117 -0
- data/lib/action_view/partials.rb +240 -0
- data/lib/action_view/paths.rb +69 -0
- data/lib/action_view/reloadable_template.rb +117 -0
- data/lib/action_view/renderable.rb +95 -0
- data/lib/action_view/renderable_partial.rb +47 -0
- data/lib/action_view/safe_buffer.rb +28 -0
- data/lib/action_view/template.rb +252 -0
- data/lib/action_view/template_error.rb +99 -0
- data/lib/action_view/template_handler.rb +34 -0
- data/lib/action_view/template_handlers/builder.rb +17 -0
- data/lib/action_view/template_handlers/erb.rb +22 -0
- data/lib/action_view/template_handlers/rjs.rb +13 -0
- data/lib/action_view/template_handlers.rb +48 -0
- data/lib/action_view/test_case.rb +162 -0
- data/lib/action_view.rb +58 -0
- data/lib/actionpack.rb +2 -0
- data/test/abstract_unit.rb +61 -0
- data/test/active_record_unit.rb +104 -0
- data/test/activerecord/active_record_store_test.rb +174 -0
- data/test/activerecord/render_partial_with_record_identification_test.rb +188 -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 +37 -0
- data/test/controller/assert_select_test.rb +734 -0
- data/test/controller/base_test.rb +217 -0
- data/test/controller/benchmark_test.rb +32 -0
- data/test/controller/caching_test.rb +729 -0
- data/test/controller/capture_test.rb +66 -0
- data/test/controller/content_type_test.rb +168 -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 +134 -0
- data/test/controller/deprecation/deprecated_base_methods_test.rb +32 -0
- data/test/controller/dispatcher_test.rb +144 -0
- data/test/controller/dom_assertions_test.rb +53 -0
- data/test/controller/failsafe_test.rb +60 -0
- data/test/controller/fake_controllers.rb +33 -0
- data/test/controller/fake_models.rb +19 -0
- data/test/controller/filter_params_test.rb +52 -0
- data/test/controller/filters_test.rb +885 -0
- data/test/controller/flash_test.rb +147 -0
- data/test/controller/header_test.rb +14 -0
- data/test/controller/helper_test.rb +224 -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 +274 -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_basic_authentication_test.rb +113 -0
- data/test/controller/http_digest_authentication_test.rb +254 -0
- data/test/controller/integration_test.rb +483 -0
- data/test/controller/layout_test.rb +215 -0
- data/test/controller/logging_test.rb +46 -0
- data/test/controller/middleware_stack_test.rb +90 -0
- data/test/controller/mime_responds_test.rb +536 -0
- data/test/controller/mime_type_test.rb +93 -0
- data/test/controller/polymorphic_routes_test.rb +297 -0
- data/test/controller/rack_test.rb +311 -0
- data/test/controller/record_identifier_test.rb +139 -0
- data/test/controller/redirect_test.rb +285 -0
- data/test/controller/reloader_test.rb +124 -0
- data/test/controller/render_test.rb +1762 -0
- data/test/controller/request/json_params_parsing_test.rb +65 -0
- data/test/controller/request/multipart_params_parsing_test.rb +162 -0
- data/test/controller/request/query_string_parsing_test.rb +120 -0
- data/test/controller/request/test_request_test.rb +35 -0
- data/test/controller/request/url_encoded_params_parsing_test.rb +146 -0
- data/test/controller/request/xml_params_parsing_test.rb +103 -0
- data/test/controller/request_forgery_protection_test.rb +265 -0
- data/test/controller/request_test.rb +395 -0
- data/test/controller/rescue_test.rb +536 -0
- data/test/controller/resources_test.rb +1393 -0
- data/test/controller/routing_test.rb +2591 -0
- data/test/controller/selector_test.rb +628 -0
- data/test/controller/send_file_test.rb +171 -0
- data/test/controller/session/cookie_store_test.rb +216 -0
- data/test/controller/session/mem_cache_store_test.rb +127 -0
- data/test/controller/session/test_session_test.rb +58 -0
- data/test/controller/test_test.rb +700 -0
- data/test/controller/translation_test.rb +26 -0
- data/test/controller/url_rewriter_test.rb +385 -0
- data/test/controller/verification_test.rb +270 -0
- data/test/controller/view_paths_test.rb +141 -0
- data/test/controller/webservice_test.rb +273 -0
- data/test/fixtures/_top_level_partial.html.erb +1 -0
- data/test/fixtures/_top_level_partial_only.erb +1 -0
- data/test/fixtures/addresses/list.erb +1 -0
- data/test/fixtures/alternate_helpers/foo_helper.rb +3 -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_rhtml.rhtml +1 -0
- data/test/fixtures/content_type/render_default_for_rjs.rjs +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/_developer.erb +1 -0
- data/test/fixtures/developers.yml +21 -0
- data/test/fixtures/developers_projects.yml +13 -0
- data/test/fixtures/failsafe/500.html +1 -0
- data/test/fixtures/fun/games/_game.erb +1 -0
- data/test/fixtures/fun/games/hello_world.erb +1 -0
- data/test/fixtures/fun/serious/games/_game.erb +1 -0
- data/test/fixtures/functional_caching/_partial.erb +3 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.html.erb +3 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs +6 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder +5 -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/inline_fragment_cached.html.erb +2 -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/abs_path_layout.rhtml +1 -0
- data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
- data/test/fixtures/layout_tests/alt/layouts/alt.rhtml +0 -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/_column.html.erb +2 -0
- data/test/fixtures/layouts/block_with_layout.erb +3 -0
- data/test/fixtures/layouts/builder.builder +3 -0
- data/test/fixtures/layouts/default_html.html.erb +1 -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/xhr.html.erb +2 -0
- data/test/fixtures/layouts/yield.erb +2 -0
- data/test/fixtures/mascot.rb +3 -0
- data/test/fixtures/mascots/_mascot.html.erb +1 -0
- data/test/fixtures/mascots.yml +4 -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/empty +10 -0
- data/test/fixtures/multipart/hello.txt +1 -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/none +9 -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/_project.erb +1 -0
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/public/404.html +1 -0
- data/test/fixtures/public/500.da.html +1 -0
- data/test/fixtures/public/500.html +1 -0
- data/test/fixtures/public/absolute/test.css +23 -0
- data/test/fixtures/public/absolute/test.js +63 -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/controls.js +1 -0
- data/test/fixtures/public/javascripts/dragdrop.js +1 -0
- data/test/fixtures/public/javascripts/effects.js +1 -0
- data/test/fixtures/public/javascripts/prototype.js +1 -0
- data/test/fixtures/public/javascripts/robber.js +1 -0
- data/test/fixtures/public/javascripts/subdir/subdir.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/subdir/subdir.css +1 -0
- data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
- data/test/fixtures/quiz/questions/_question.html.erb +1 -0
- data/test/fixtures/replies/_reply.erb +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/all_types_with_layout.js.rjs +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/_counter.html.erb +1 -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/_customer_with_var.erb +1 -0
- data/test/fixtures/test/_form.erb +1 -0
- data/test/fixtures/test/_from_helper.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_block_with_args.html.erb +3 -0
- data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
- data/test/fixtures/test/_local_inspector.html.erb +1 -0
- data/test/fixtures/test/_one.html.erb +1 -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/_partial_with_only_html_version.html.erb +1 -0
- data/test/fixtures/test/_person.erb +2 -0
- data/test/fixtures/test/_raise.html.erb +1 -0
- data/test/fixtures/test/_two.html.erb +1 -0
- data/test/fixtures/test/action_talk_to_layout.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/dont_pick_me +1 -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/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.da.html.erb +1 -0
- data/test/fixtures/test/hello_world.erb +1 -0
- data/test/fixtures/test/hello_world.erb~ +1 -0
- data/test/fixtures/test/hello_world.pt-BR.html.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/hyphen-ated.erb +1 -0
- data/test/fixtures/test/implicit_content_type.atom.builder +2 -0
- data/test/fixtures/test/list.erb +1 -0
- data/test/fixtures/test/malformed/malformed.en.html.erb~ +1 -0
- data/test/fixtures/test/malformed/malformed.erb~ +1 -0
- data/test/fixtures/test/malformed/malformed.html.erb~ +1 -0
- data/test/fixtures/test/nested_layout.erb +3 -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_explicit_html_template.js.rjs +1 -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_implicit_html_template.js.rjs +1 -0
- data/test/fixtures/test/render_implicit_html_template_from_xhr_request.da.html.erb +1 -0
- data/test/fixtures/test/render_implicit_html_template_from_xhr_request.html.erb +1 -0
- data/test/fixtures/test/render_implicit_js_template_without_layout.js.erb +1 -0
- data/test/fixtures/test/render_to_string_test.erb +1 -0
- data/test/fixtures/test/sub_template_raise.html.erb +1 -0
- data/test/fixtures/test/template.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/test/using_layout_around_block_with_args.html.erb +1 -0
- data/test/fixtures/test/utf8.html.erb +2 -0
- data/test/fixtures/topic.rb +3 -0
- data/test/fixtures/topics/_topic.html.erb +1 -0
- data/test/fixtures/topics.yml +22 -0
- data/test/template/active_record_helper_i18n_test.rb +44 -0
- data/test/template/active_record_helper_test.rb +302 -0
- data/test/template/asset_tag_helper_test.rb +771 -0
- data/test/template/atom_feed_helper_test.rb +315 -0
- data/test/template/benchmark_helper_test.rb +86 -0
- data/test/template/compiled_templates_test.rb +204 -0
- data/test/template/date_helper_i18n_test.rb +121 -0
- data/test/template/date_helper_test.rb +2485 -0
- data/test/template/erb_util_test.rb +24 -0
- data/test/template/form_helper_test.rb +1393 -0
- data/test/template/form_options_helper_i18n_test.rb +27 -0
- data/test/template/form_options_helper_test.rb +807 -0
- data/test/template/form_tag_helper_test.rb +344 -0
- data/test/template/javascript_helper_test.rb +106 -0
- data/test/template/number_helper_i18n_test.rb +69 -0
- data/test/template/number_helper_test.rb +132 -0
- data/test/template/prototype_helper_test.rb +639 -0
- data/test/template/raw_output_helper_test.rb +21 -0
- data/test/template/record_tag_helper_test.rb +58 -0
- data/test/template/render_test.rb +290 -0
- data/test/template/sanitize_helper_test.rb +57 -0
- data/test/template/scriptaculous_helper_test.rb +90 -0
- data/test/template/tag_helper_test.rb +98 -0
- data/test/template/template_test.rb +32 -0
- data/test/template/test_test.rb +54 -0
- data/test/template/text_helper_test.rb +543 -0
- data/test/template/translation_helper_test.rb +32 -0
- data/test/template/url_helper_test.rb +622 -0
- data/test/testing_sandbox.rb +15 -0
- data/test/view/safe_buffer_test.rb +36 -0
- data/test/view/test_case_test.rb +176 -0
- metadata +531 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
require 'digest/md5'
|
|
2
|
+
|
|
3
|
+
module ActionController # :nodoc:
|
|
4
|
+
# Represents an HTTP response generated by a controller action. One can use
|
|
5
|
+
# an ActionController::Response object to retrieve the current state
|
|
6
|
+
# of the response, or customize the response. An Response object can
|
|
7
|
+
# either represent a "real" HTTP response (i.e. one that is meant to be sent
|
|
8
|
+
# back to the web browser) or a test response (i.e. one that is generated
|
|
9
|
+
# from integration tests). See CgiResponse and TestResponse, respectively.
|
|
10
|
+
#
|
|
11
|
+
# Response is mostly a Ruby on Rails framework implement detail, and
|
|
12
|
+
# should never be used directly in controllers. Controllers should use the
|
|
13
|
+
# methods defined in ActionController::Base instead. For example, if you want
|
|
14
|
+
# to set the HTTP response's content MIME type, then use
|
|
15
|
+
# ActionControllerBase#headers instead of Response#headers.
|
|
16
|
+
#
|
|
17
|
+
# Nevertheless, integration tests may want to inspect controller responses in
|
|
18
|
+
# more detail, and that's when Response can be useful for application
|
|
19
|
+
# developers. Integration test methods such as
|
|
20
|
+
# ActionController::Integration::Session#get and
|
|
21
|
+
# ActionController::Integration::Session#post return objects of type
|
|
22
|
+
# TestResponse (which are of course also of type Response).
|
|
23
|
+
#
|
|
24
|
+
# For example, the following demo integration "test" prints the body of the
|
|
25
|
+
# controller response to the console:
|
|
26
|
+
#
|
|
27
|
+
# class DemoControllerTest < ActionController::IntegrationTest
|
|
28
|
+
# def test_print_root_path_to_console
|
|
29
|
+
# get('/')
|
|
30
|
+
# puts @response.body
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
class Response < Rack::Response
|
|
34
|
+
DEFAULT_HEADERS = { "Cache-Control" => "no-cache" }
|
|
35
|
+
attr_accessor :request
|
|
36
|
+
|
|
37
|
+
attr_accessor :session, :assigns, :template, :layout
|
|
38
|
+
attr_accessor :redirected_to, :redirected_to_method_params
|
|
39
|
+
|
|
40
|
+
delegate :default_charset, :to => 'ActionController::Base'
|
|
41
|
+
|
|
42
|
+
def initialize
|
|
43
|
+
@status = 200
|
|
44
|
+
@header = Rack::Utils::HeaderHash.new(DEFAULT_HEADERS)
|
|
45
|
+
|
|
46
|
+
@writer = lambda { |x| @body << x }
|
|
47
|
+
@block = nil
|
|
48
|
+
|
|
49
|
+
@body = "",
|
|
50
|
+
@session = []
|
|
51
|
+
@assigns = []
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def location; headers['Location'] end
|
|
55
|
+
def location=(url) headers['Location'] = url end
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Sets the HTTP response's content MIME type. For example, in the controller
|
|
59
|
+
# you could write this:
|
|
60
|
+
#
|
|
61
|
+
# response.content_type = "text/plain"
|
|
62
|
+
#
|
|
63
|
+
# If a character set has been defined for this response (see charset=) then
|
|
64
|
+
# the character set information will also be included in the content type
|
|
65
|
+
# information.
|
|
66
|
+
def content_type=(mime_type)
|
|
67
|
+
self.headers["Content-Type"] =
|
|
68
|
+
if mime_type =~ /charset/ || (c = charset).nil?
|
|
69
|
+
mime_type.to_s
|
|
70
|
+
else
|
|
71
|
+
"#{mime_type}; charset=#{c}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns the response's content MIME type, or nil if content type has been set.
|
|
76
|
+
def content_type
|
|
77
|
+
content_type = String(headers["Content-Type"] || headers["type"]).split(";")[0]
|
|
78
|
+
content_type.blank? ? nil : content_type
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Set the charset of the Content-Type header. Set to nil to remove it.
|
|
82
|
+
# If no content type is set, it defaults to HTML.
|
|
83
|
+
def charset=(charset)
|
|
84
|
+
headers["Content-Type"] =
|
|
85
|
+
if charset
|
|
86
|
+
"#{content_type || Mime::HTML}; charset=#{charset}"
|
|
87
|
+
else
|
|
88
|
+
content_type || Mime::HTML.to_s
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def charset
|
|
93
|
+
charset = String(headers["Content-Type"] || headers["type"]).split(";")[1]
|
|
94
|
+
charset.blank? ? nil : charset.strip.split("=")[1]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def last_modified
|
|
98
|
+
if last = headers['Last-Modified']
|
|
99
|
+
Time.httpdate(last)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def last_modified?
|
|
104
|
+
headers.include?('Last-Modified')
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def last_modified=(utc_time)
|
|
108
|
+
headers['Last-Modified'] = utc_time.httpdate
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def etag
|
|
112
|
+
headers['ETag']
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def etag?
|
|
116
|
+
headers.include?('ETag')
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def etag=(etag)
|
|
120
|
+
if etag.blank?
|
|
121
|
+
headers.delete('ETag')
|
|
122
|
+
else
|
|
123
|
+
headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}")
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def redirect(url, status)
|
|
128
|
+
self.status = status
|
|
129
|
+
self.location = url.gsub(/[\r\n]/, '')
|
|
130
|
+
self.body = "<html><body>You are being <a href=\"#{CGI.escapeHTML(url)}\">redirected</a>.</body></html>"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def sending_file?
|
|
134
|
+
headers["Content-Transfer-Encoding"] == "binary"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def assign_default_content_type_and_charset!
|
|
138
|
+
self.content_type ||= Mime::HTML
|
|
139
|
+
self.charset ||= default_charset unless sending_file?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def prepare!
|
|
143
|
+
assign_default_content_type_and_charset!
|
|
144
|
+
handle_conditional_get!
|
|
145
|
+
set_content_length!
|
|
146
|
+
convert_content_type!
|
|
147
|
+
convert_language!
|
|
148
|
+
convert_cookies!
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def each(&callback)
|
|
152
|
+
if @body.respond_to?(:call)
|
|
153
|
+
@writer = lambda { |x| callback.call(x) }
|
|
154
|
+
@body.call(self, self)
|
|
155
|
+
elsif @body.respond_to?(:to_str)
|
|
156
|
+
yield @body
|
|
157
|
+
else
|
|
158
|
+
@body.each(&callback)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
@writer = callback
|
|
162
|
+
@block.call(self) if @block
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def write(str)
|
|
166
|
+
@writer.call str.to_s
|
|
167
|
+
str
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def flush #:nodoc:
|
|
171
|
+
ActiveSupport::Deprecation.warn(
|
|
172
|
+
'Calling output.flush is no longer needed for streaming output ' +
|
|
173
|
+
'because ActionController::Response automatically handles it', caller)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def set_cookie(key, value)
|
|
177
|
+
if value.has_key?(:http_only)
|
|
178
|
+
ActiveSupport::Deprecation.warn(
|
|
179
|
+
"The :http_only option in ActionController::Response#set_cookie " +
|
|
180
|
+
"has been renamed. Please use :httponly instead.", caller)
|
|
181
|
+
value[:httponly] ||= value.delete(:http_only)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
super(key, value)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
private
|
|
188
|
+
def handle_conditional_get!
|
|
189
|
+
if etag? || last_modified?
|
|
190
|
+
set_conditional_cache_control!
|
|
191
|
+
elsif nonempty_ok_response?
|
|
192
|
+
self.etag = body
|
|
193
|
+
|
|
194
|
+
if request && request.etag_matches?(etag)
|
|
195
|
+
self.status = '304 Not Modified'
|
|
196
|
+
self.body = ''
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
set_conditional_cache_control!
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def nonempty_ok_response?
|
|
204
|
+
ok = !status || status.to_s[0..2] == '200'
|
|
205
|
+
ok && body.is_a?(String) && !body.empty?
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def set_conditional_cache_control!
|
|
209
|
+
if headers['Cache-Control'] == DEFAULT_HEADERS['Cache-Control']
|
|
210
|
+
headers['Cache-Control'] = 'private, max-age=0, must-revalidate'
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def convert_content_type!
|
|
215
|
+
headers['Content-Type'] ||= "text/html"
|
|
216
|
+
headers['Content-Type'] += "; charset=" + headers.delete('charset') if headers['charset']
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Don't set the Content-Length for block-based bodies as that would mean
|
|
220
|
+
# reading it all into memory. Not nice for, say, a 2GB streaming file.
|
|
221
|
+
def set_content_length!
|
|
222
|
+
if status && status.to_s[0..2] == '204'
|
|
223
|
+
headers.delete('Content-Length')
|
|
224
|
+
elsif length = headers['Content-Length']
|
|
225
|
+
headers['Content-Length'] = length.to_s
|
|
226
|
+
elsif !body.respond_to?(:call) && (!status || status.to_s[0..2] != '304')
|
|
227
|
+
headers["Content-Length"] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def convert_language!
|
|
232
|
+
headers["Content-Language"] = headers.delete("language") if headers["language"]
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def convert_cookies!
|
|
236
|
+
headers['Set-Cookie'] = Array(headers['Set-Cookie']).compact
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
module ActionController
|
|
2
|
+
module Routing
|
|
3
|
+
class RouteBuilder #:nodoc:
|
|
4
|
+
attr_reader :separators, :optional_separators
|
|
5
|
+
attr_reader :separator_regexp, :nonseparator_regexp, :interval_regexp
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@separators = Routing::SEPARATORS
|
|
9
|
+
@optional_separators = %w( / )
|
|
10
|
+
|
|
11
|
+
@separator_regexp = /[#{Regexp.escape(separators.join)}]/
|
|
12
|
+
@nonseparator_regexp = /\A([^#{Regexp.escape(separators.join)}]+)/
|
|
13
|
+
@interval_regexp = /(.*?)(#{separator_regexp}|$)/
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Accepts a "route path" (a string defining a route), and returns the array
|
|
17
|
+
# of segments that corresponds to it. Note that the segment array is only
|
|
18
|
+
# partially initialized--the defaults and requirements, for instance, need
|
|
19
|
+
# to be set separately, via the +assign_route_options+ method, and the
|
|
20
|
+
# <tt>optional?</tt> method for each segment will not be reliable until after
|
|
21
|
+
# +assign_route_options+ is called, as well.
|
|
22
|
+
def segments_for_route_path(path)
|
|
23
|
+
rest, segments = path, []
|
|
24
|
+
|
|
25
|
+
until rest.empty?
|
|
26
|
+
segment, rest = segment_for(rest)
|
|
27
|
+
segments << segment
|
|
28
|
+
end
|
|
29
|
+
segments
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# A factory method that returns a new segment instance appropriate for the
|
|
33
|
+
# format of the given string.
|
|
34
|
+
def segment_for(string)
|
|
35
|
+
segment =
|
|
36
|
+
case string
|
|
37
|
+
when /\A\.(:format)?\//
|
|
38
|
+
OptionalFormatSegment.new
|
|
39
|
+
when /\A:(\w+)/
|
|
40
|
+
key = $1.to_sym
|
|
41
|
+
key == :controller ? ControllerSegment.new(key) : DynamicSegment.new(key)
|
|
42
|
+
when /\A\*(\w+)/
|
|
43
|
+
PathSegment.new($1.to_sym, :optional => true)
|
|
44
|
+
when /\A\?(.*?)\?/
|
|
45
|
+
StaticSegment.new($1, :optional => true)
|
|
46
|
+
when nonseparator_regexp
|
|
47
|
+
StaticSegment.new($1)
|
|
48
|
+
when separator_regexp
|
|
49
|
+
DividerSegment.new($&, :optional => optional_separators.include?($&))
|
|
50
|
+
end
|
|
51
|
+
[segment, $~.post_match]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Split the given hash of options into requirement and default hashes. The
|
|
55
|
+
# segments are passed alongside in order to distinguish between default values
|
|
56
|
+
# and requirements.
|
|
57
|
+
def divide_route_options(segments, options)
|
|
58
|
+
options = options.except(:path_prefix, :name_prefix)
|
|
59
|
+
|
|
60
|
+
if options[:namespace]
|
|
61
|
+
options[:controller] = "#{options.delete(:namespace).sub(/\/$/, '')}/#{options[:controller]}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
requirements = (options.delete(:requirements) || {}).dup
|
|
65
|
+
defaults = (options.delete(:defaults) || {}).dup
|
|
66
|
+
conditions = (options.delete(:conditions) || {}).dup
|
|
67
|
+
|
|
68
|
+
validate_route_conditions(conditions)
|
|
69
|
+
|
|
70
|
+
path_keys = segments.collect { |segment| segment.key if segment.respond_to?(:key) }.compact
|
|
71
|
+
options.each do |key, value|
|
|
72
|
+
hash = (path_keys.include?(key) && ! value.is_a?(Regexp)) ? defaults : requirements
|
|
73
|
+
hash[key] = value
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
[defaults, requirements, conditions]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Takes a hash of defaults and a hash of requirements, and assigns them to
|
|
80
|
+
# the segments. Any unused requirements (which do not correspond to a segment)
|
|
81
|
+
# are returned as a hash.
|
|
82
|
+
def assign_route_options(segments, defaults, requirements)
|
|
83
|
+
route_requirements = {} # Requirements that do not belong to a segment
|
|
84
|
+
|
|
85
|
+
segment_named = Proc.new do |key|
|
|
86
|
+
segments.detect { |segment| segment.key == key if segment.respond_to?(:key) }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
requirements.each do |key, requirement|
|
|
90
|
+
segment = segment_named[key]
|
|
91
|
+
if segment
|
|
92
|
+
raise TypeError, "#{key}: requirements on a path segment must be regular expressions" unless requirement.is_a?(Regexp)
|
|
93
|
+
if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
|
|
94
|
+
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
|
|
95
|
+
end
|
|
96
|
+
if requirement.multiline?
|
|
97
|
+
raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
|
|
98
|
+
end
|
|
99
|
+
segment.regexp = requirement
|
|
100
|
+
else
|
|
101
|
+
route_requirements[key] = requirement
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
defaults.each do |key, default|
|
|
106
|
+
segment = segment_named[key]
|
|
107
|
+
raise ArgumentError, "#{key}: No matching segment exists; cannot assign default" unless segment
|
|
108
|
+
segment.is_optional = true
|
|
109
|
+
segment.default = default.to_param if default
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
assign_default_route_options(segments)
|
|
113
|
+
ensure_required_segments(segments)
|
|
114
|
+
route_requirements
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Assign default options, such as 'index' as a default for <tt>:action</tt>. This
|
|
118
|
+
# method must be run *after* user supplied requirements and defaults have
|
|
119
|
+
# been applied to the segments.
|
|
120
|
+
def assign_default_route_options(segments)
|
|
121
|
+
segments.each do |segment|
|
|
122
|
+
next unless segment.is_a? DynamicSegment
|
|
123
|
+
case segment.key
|
|
124
|
+
when :action
|
|
125
|
+
if segment.regexp.nil? || segment.regexp.match('index').to_s == 'index'
|
|
126
|
+
segment.default ||= 'index'
|
|
127
|
+
segment.is_optional = true
|
|
128
|
+
end
|
|
129
|
+
when :id
|
|
130
|
+
if segment.default.nil? && segment.regexp.nil? || segment.regexp =~ ''
|
|
131
|
+
segment.is_optional = true
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Makes sure that there are no optional segments that precede a required
|
|
138
|
+
# segment. If any are found that precede a required segment, they are
|
|
139
|
+
# made required.
|
|
140
|
+
def ensure_required_segments(segments)
|
|
141
|
+
allow_optional = true
|
|
142
|
+
segments.reverse_each do |segment|
|
|
143
|
+
allow_optional &&= segment.optional?
|
|
144
|
+
if !allow_optional && segment.optional?
|
|
145
|
+
unless segment.optionality_implied?
|
|
146
|
+
warn "Route segment \"#{segment.to_s}\" cannot be optional because it precedes a required segment. This segment will be required."
|
|
147
|
+
end
|
|
148
|
+
segment.is_optional = false
|
|
149
|
+
elsif allow_optional && segment.respond_to?(:default) && segment.default
|
|
150
|
+
# if a segment has a default, then it is optional
|
|
151
|
+
segment.is_optional = true
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Construct and return a route with the given path and options.
|
|
157
|
+
def build(path, options)
|
|
158
|
+
# Wrap the path with slashes
|
|
159
|
+
path = "/#{path}" unless path[0] == ?/
|
|
160
|
+
path = "#{path}/" unless path[-1] == ?/
|
|
161
|
+
|
|
162
|
+
prefix = options[:path_prefix].to_s.gsub(/^\//,'')
|
|
163
|
+
path = "/#{prefix}#{path}" unless prefix.blank?
|
|
164
|
+
|
|
165
|
+
segments = segments_for_route_path(path)
|
|
166
|
+
defaults, requirements, conditions = divide_route_options(segments, options)
|
|
167
|
+
requirements = assign_route_options(segments, defaults, requirements)
|
|
168
|
+
|
|
169
|
+
# TODO: Segments should be frozen on initialize
|
|
170
|
+
segments.each { |segment| segment.freeze }
|
|
171
|
+
|
|
172
|
+
route = Route.new(segments, requirements, conditions)
|
|
173
|
+
|
|
174
|
+
if !route.significant_keys.include?(:controller)
|
|
175
|
+
raise ArgumentError, "Illegal route: the :controller must be specified!"
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
route.freeze
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
private
|
|
182
|
+
def validate_route_conditions(conditions)
|
|
183
|
+
if method = conditions[:method]
|
|
184
|
+
[method].flatten.each do |m|
|
|
185
|
+
if m == :head
|
|
186
|
+
raise ArgumentError, "HTTP method HEAD is invalid in route conditions. Rails processes HEAD requests the same as GETs, returning just the response headers"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
unless HTTP_METHODS.include?(m.to_sym)
|
|
190
|
+
raise ArgumentError, "Invalid HTTP method specified in route conditions: #{conditions.inspect}"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
module ActionController
|
|
2
|
+
module Routing
|
|
3
|
+
# Much of the slow performance from routes comes from the
|
|
4
|
+
# complexity of expiry, <tt>:requirements</tt> matching, defaults providing
|
|
5
|
+
# and figuring out which url pattern to use. With named routes
|
|
6
|
+
# we can avoid the expense of finding the right route. So if
|
|
7
|
+
# they've provided the right number of arguments, and have no
|
|
8
|
+
# <tt>:requirements</tt>, we can just build up a string and return it.
|
|
9
|
+
#
|
|
10
|
+
# To support building optimisations for other common cases, the
|
|
11
|
+
# generation code is separated into several classes
|
|
12
|
+
module Optimisation
|
|
13
|
+
def generate_optimisation_block(route, kind)
|
|
14
|
+
return "" unless route.optimise?
|
|
15
|
+
OPTIMISERS.inject("") do |memo, klazz|
|
|
16
|
+
memo << klazz.new(route, kind).source_code
|
|
17
|
+
memo
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Optimiser
|
|
22
|
+
attr_reader :route, :kind
|
|
23
|
+
GLOBAL_GUARD_CONDITIONS = [
|
|
24
|
+
"(!defined?(default_url_options) || default_url_options.blank?)",
|
|
25
|
+
"(!defined?(controller.default_url_options) || controller.default_url_options.blank?)",
|
|
26
|
+
"defined?(request)",
|
|
27
|
+
"request"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
def initialize(route, kind)
|
|
31
|
+
@route = route
|
|
32
|
+
@kind = kind
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def guard_conditions
|
|
36
|
+
["false"]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def generation_code
|
|
40
|
+
'nil'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def source_code
|
|
44
|
+
if applicable?
|
|
45
|
+
guard_condition = (GLOBAL_GUARD_CONDITIONS + guard_conditions).join(" && ")
|
|
46
|
+
"return #{generation_code} if #{guard_condition}\n"
|
|
47
|
+
else
|
|
48
|
+
"\n"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Temporarily disabled <tt>:url</tt> optimisation pending proper solution to
|
|
53
|
+
# Issues around request.host etc.
|
|
54
|
+
def applicable?
|
|
55
|
+
true
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Given a route
|
|
60
|
+
#
|
|
61
|
+
# map.person '/people/:id'
|
|
62
|
+
#
|
|
63
|
+
# If the user calls <tt>person_url(@person)</tt>, we can simply
|
|
64
|
+
# return a string like "/people/#{@person.to_param}"
|
|
65
|
+
# rather than triggering the expensive logic in +url_for+.
|
|
66
|
+
class PositionalArguments < Optimiser
|
|
67
|
+
def guard_conditions
|
|
68
|
+
number_of_arguments = route.required_segment_keys.size
|
|
69
|
+
# if they're using foo_url(:id=>2) it's one
|
|
70
|
+
# argument, but we don't want to generate /foos/id2
|
|
71
|
+
if number_of_arguments == 1
|
|
72
|
+
["args.size == 1", "!args.first.is_a?(Hash)"]
|
|
73
|
+
else
|
|
74
|
+
["args.size == #{number_of_arguments}"]
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def generation_code
|
|
79
|
+
elements = []
|
|
80
|
+
idx = 0
|
|
81
|
+
|
|
82
|
+
if kind == :url
|
|
83
|
+
elements << '#{request.protocol}'
|
|
84
|
+
elements << '#{request.host_with_port}'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
elements << '#{ActionController::Base.relative_url_root if ActionController::Base.relative_url_root}'
|
|
88
|
+
|
|
89
|
+
# The last entry in <tt>route.segments</tt> appears to *always* be a
|
|
90
|
+
# 'divider segment' for '/' but we have assertions to ensure that
|
|
91
|
+
# we don't include the trailing slashes, so skip them.
|
|
92
|
+
(route.segments.size == 1 ? route.segments : route.segments[0..-2]).each do |segment|
|
|
93
|
+
if segment.is_a?(DynamicSegment)
|
|
94
|
+
elements << segment.interpolation_chunk("args[#{idx}].to_param")
|
|
95
|
+
idx += 1
|
|
96
|
+
else
|
|
97
|
+
elements << segment.interpolation_chunk
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
%("#{elements * ''}")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# This case is mostly the same as the positional arguments case
|
|
105
|
+
# above, but it supports additional query parameters as the last
|
|
106
|
+
# argument
|
|
107
|
+
class PositionalArgumentsWithAdditionalParams < PositionalArguments
|
|
108
|
+
def guard_conditions
|
|
109
|
+
["args.size == #{route.segment_keys.size + 1}"] +
|
|
110
|
+
UrlRewriter::RESERVED_OPTIONS.collect{ |key| "!args.last.has_key?(:#{key})" }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# This case uses almost the same code as positional arguments,
|
|
114
|
+
# but add a question mark and args.last.to_query on the end,
|
|
115
|
+
# unless the last arg is empty
|
|
116
|
+
def generation_code
|
|
117
|
+
super.insert(-2, '#{\'?\' + args.last.to_query unless args.last.empty?}')
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# To avoid generating "http://localhost/?host=foo.example.com" we
|
|
121
|
+
# can't use this optimisation on routes without any segments
|
|
122
|
+
def applicable?
|
|
123
|
+
super && route.segment_keys.size > 0
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
OPTIMISERS = [PositionalArguments, PositionalArgumentsWithAdditionalParams]
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|