actionpack-2.3.17-rack-upgrade 2.3.17
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +5250 -0
- data/MIT-LICENSE +21 -0
- data/README +409 -0
- data/RUNNING_UNIT_TESTS +24 -0
- data/Rakefile +158 -0
- data/install.rb +30 -0
- data/lib/action_controller.rb +113 -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 +169 -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 +1425 -0
- data/lib/action_controller/benchmarking.rb +107 -0
- data/lib/action_controller/caching.rb +71 -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/cgi_ext.rb +15 -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_process.rb +77 -0
- data/lib/action_controller/cookies.rb +197 -0
- data/lib/action_controller/dispatcher.rb +133 -0
- data/lib/action_controller/failsafe.rb +87 -0
- data/lib/action_controller/filters.rb +680 -0
- data/lib/action_controller/flash.rb +213 -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 +708 -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 +518 -0
- data/lib/action_controller/request_forgery_protection.rb +116 -0
- data/lib/action_controller/rescue.rb +183 -0
- data/lib/action_controller/resources.rb +682 -0
- data/lib/action_controller/response.rb +237 -0
- data/lib/action_controller/routing.rb +388 -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 +503 -0
- data/lib/action_controller/routing/routing_ext.rb +49 -0
- data/lib/action_controller/routing/segments.rb +343 -0
- data/lib/action_controller/session/abstract_store.rb +276 -0
- data/lib/action_controller/session/cookie_store.rb +240 -0
- data/lib/action_controller/session/mem_cache_store.rb +60 -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 +229 -0
- data/lib/action_controller/vendor/html-scanner.rb +16 -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 +58 -0
- data/lib/action_view/base.rb +362 -0
- data/lib/action_view/helpers.rb +61 -0
- data/lib/action_view/helpers/active_record_helper.rb +305 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +695 -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/csrf_helper.rb +14 -0
- data/lib/action_view/helpers/date_helper.rb +989 -0
- data/lib/action_view/helpers/debug_helper.rb +38 -0
- data/lib/action_view/helpers/form_helper.rb +1118 -0
- data/lib/action_view/helpers/form_options_helper.rb +599 -0
- data/lib/action_view/helpers/form_tag_helper.rb +490 -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 +251 -0
- data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
- data/lib/action_view/helpers/tag_helper.rb +151 -0
- data/lib/action_view/helpers/text_helper.rb +597 -0
- data/lib/action_view/helpers/translation_helper.rb +67 -0
- data/lib/action_view/helpers/url_helper.rb +637 -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 +241 -0
- data/lib/action_view/paths.rb +77 -0
- data/lib/action_view/reloadable_template.rb +117 -0
- data/lib/action_view/renderable.rb +109 -0
- data/lib/action_view/renderable_partial.rb +53 -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.rb +48 -0
- data/lib/action_view/template_handlers/builder.rb +17 -0
- data/lib/action_view/template_handlers/erb.rb +25 -0
- data/lib/action_view/template_handlers/rjs.rb +13 -0
- data/lib/action_view/test_case.rb +162 -0
- data/lib/actionpack.rb +2 -0
- data/test/abstract_unit.rb +78 -0
- data/test/active_record_unit.rb +104 -0
- data/test/activerecord/active_record_store_test.rb +221 -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 +545 -0
- data/test/controller/addresses_render_test.rb +37 -0
- data/test/controller/assert_select_test.rb +735 -0
- data/test/controller/base_test.rb +217 -0
- data/test/controller/benchmark_test.rb +32 -0
- data/test/controller/caching_test.rb +743 -0
- data/test/controller/capture_test.rb +66 -0
- data/test/controller/content_type_test.rb +178 -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 +208 -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 +174 -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 +281 -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 +526 -0
- data/test/controller/layout_test.rb +215 -0
- data/test/controller/localized_templates_test.rb +24 -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/output_escaping_test.rb +19 -0
- data/test/controller/polymorphic_routes_test.rb +297 -0
- data/test/controller/rack_test.rb +308 -0
- data/test/controller/record_identifier_test.rb +139 -0
- data/test/controller/redirect_test.rb +285 -0
- data/test/controller/reloader_test.rb +125 -0
- data/test/controller/render_test.rb +1783 -0
- data/test/controller/request/json_params_parsing_test.rb +65 -0
- data/test/controller/request/multipart_params_parsing_test.rb +177 -0
- data/test/controller/request/query_string_parsing_test.rb +129 -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 +233 -0
- data/test/controller/request_test.rb +398 -0
- data/test/controller/rescue_test.rb +541 -0
- data/test/controller/resources_test.rb +1393 -0
- data/test/controller/routing_test.rb +2592 -0
- data/test/controller/selector_test.rb +628 -0
- data/test/controller/send_file_test.rb +171 -0
- data/test/controller/session/abstract_store_test.rb +64 -0
- data/test/controller/session/cookie_store_test.rb +354 -0
- data/test/controller/session/mem_cache_store_test.rb +187 -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 +395 -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.yml +21 -0
- data/test/fixtures/developers/_developer.erb +1 -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/localized/hello_world.de.html +1 -0
- data/test/fixtures/localized/hello_world.en.html +1 -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/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.yml +7 -0
- data/test/fixtures/projects/_project.erb +1 -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.yml +15 -0
- data/test/fixtures/replies/_reply.erb +1 -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/session_autoload_test/session_autoload_test/foo.rb +10 -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_counter_with_as.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/_utf8_partial.html.erb +1 -0
- data/test/fixtures/test/_utf8_partial_magic.html.erb +2 -0
- data/test/fixtures/test/action_talk_to_layout.erb +2 -0
- data/test/fixtures/test/array_translation.erb +1 -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/scoped_array_translation.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/translation.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 +4 -0
- data/test/fixtures/test/utf8_magic.html.erb +5 -0
- data/test/fixtures/test/utf8_magic_with_bare_partial.html.erb +5 -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_i18n_test.rb +51 -0
- data/test/template/active_record_helper_test.rb +302 -0
- data/test/template/asset_tag_helper_test.rb +770 -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 +2603 -0
- data/test/template/erb_util_test.rb +36 -0
- data/test/template/form_helper_test.rb +1447 -0
- data/test/template/form_options_helper_i18n_test.rb +27 -0
- data/test/template/form_options_helper_test.rb +811 -0
- data/test/template/form_tag_helper_test.rb +356 -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 +329 -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 +601 -0
- data/test/template/translation_helper_test.rb +95 -0
- data/test/template/url_helper_test.rb +641 -0
- data/test/testing_sandbox.rb +15 -0
- data/test/view/test_case_test.rb +176 -0
- metadata +519 -0
@@ -0,0 +1,116 @@
|
|
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
|
+
helper_method :form_authenticity_token
|
9
|
+
helper_method :protect_against_forgery?
|
10
|
+
end
|
11
|
+
base.extend(ClassMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
|
15
|
+
# forged link from another site, is done by embedding a token based on a random string stored in the session (which an attacker wouldn't know) in all
|
16
|
+
# forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
|
17
|
+
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
|
18
|
+
# scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway.
|
19
|
+
#
|
20
|
+
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
|
21
|
+
# ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
|
22
|
+
# production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0
|
23
|
+
# applications.
|
24
|
+
#
|
25
|
+
# The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form manually (without the
|
26
|
+
# 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
|
27
|
+
# set its value to what is returned by <tt>form_authenticity_token</tt>. Same applies to manually constructed Ajax requests. To
|
28
|
+
# make the token available through a global variable to scripts on a certain page, you could add something like this to a view:
|
29
|
+
#
|
30
|
+
# <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
|
31
|
+
#
|
32
|
+
# Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to
|
33
|
+
# config/environments/test.rb:
|
34
|
+
#
|
35
|
+
# # Disable request forgery protection in test environment
|
36
|
+
# config.action_controller.allow_forgery_protection = false
|
37
|
+
#
|
38
|
+
# == Learn more about CSRF (Cross-Site Request Forgery) attacks
|
39
|
+
#
|
40
|
+
# Here are some resources:
|
41
|
+
# * http://isc.sans.org/diary.html?storyid=1750
|
42
|
+
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
43
|
+
#
|
44
|
+
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
|
45
|
+
# There are a few guidelines you should follow:
|
46
|
+
#
|
47
|
+
# * Keep your GET requests safe and idempotent. More reading material:
|
48
|
+
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
|
49
|
+
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
|
50
|
+
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
|
51
|
+
#
|
52
|
+
module ClassMethods
|
53
|
+
# Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
|
54
|
+
#
|
55
|
+
# Example:
|
56
|
+
#
|
57
|
+
# class FooController < ApplicationController
|
58
|
+
# protect_from_forgery :except => :index
|
59
|
+
#
|
60
|
+
# # you can disable csrf protection on controller-by-controller basis:
|
61
|
+
# skip_before_filter :verify_authenticity_token
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# Valid Options:
|
65
|
+
#
|
66
|
+
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
|
67
|
+
def protect_from_forgery(options = {})
|
68
|
+
self.request_forgery_protection_token ||= :authenticity_token
|
69
|
+
before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except)
|
70
|
+
if options[:secret] || options[:digest]
|
71
|
+
ActiveSupport::Deprecation.warn("protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect", caller)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
|
78
|
+
def verify_authenticity_token
|
79
|
+
verified_request? || handle_unverified_request
|
80
|
+
end
|
81
|
+
|
82
|
+
def handle_unverified_request
|
83
|
+
reset_session
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns true or false if a request is verified. Checks:
|
87
|
+
#
|
88
|
+
# * is the format restricted? By default, only HTML requests are checked.
|
89
|
+
# * is it a GET request? Gets should be safe and idempotent
|
90
|
+
# * Does the form_authenticity_token match the given token value from the params?
|
91
|
+
def verified_request?
|
92
|
+
!protect_against_forgery? ||
|
93
|
+
request.get? ||
|
94
|
+
form_authenticity_token == form_authenticity_param ||
|
95
|
+
form_authenticity_token == request.headers['X-CSRF-Token']
|
96
|
+
end
|
97
|
+
|
98
|
+
def form_authenticity_param
|
99
|
+
params[request_forgery_protection_token]
|
100
|
+
end
|
101
|
+
|
102
|
+
def verifiable_request_format?
|
103
|
+
!request.content_type.nil? && request.content_type.verify_request?
|
104
|
+
end
|
105
|
+
|
106
|
+
# Sets the token value for the current session. Pass a <tt>:secret</tt> option
|
107
|
+
# in +protect_from_forgery+ to add a custom salt to the hash.
|
108
|
+
def form_authenticity_token
|
109
|
+
session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32)
|
110
|
+
end
|
111
|
+
|
112
|
+
def protect_against_forgery?
|
113
|
+
allow_forgery_protection && request_forgery_protection_token
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
module ActionController #:nodoc:
|
2
|
+
# Actions that fail to perform as expected throw exceptions. These
|
3
|
+
# exceptions can either be rescued for the public view (with a nice
|
4
|
+
# user-friendly explanation) or for the developers view (with tons of
|
5
|
+
# debugging information). The developers view is already implemented by
|
6
|
+
# the Action Controller, but the public view should be tailored to your
|
7
|
+
# specific application.
|
8
|
+
#
|
9
|
+
# The default behavior for public exceptions is to render a static html
|
10
|
+
# file with the name of the error code thrown. If no such file exists, an
|
11
|
+
# empty response is sent with the correct status code.
|
12
|
+
#
|
13
|
+
# You can override what constitutes a local request by overriding the
|
14
|
+
# <tt>local_request?</tt> method in your own controller. Custom rescue
|
15
|
+
# behavior is achieved by overriding the <tt>rescue_action_in_public</tt>
|
16
|
+
# and <tt>rescue_action_locally</tt> methods.
|
17
|
+
module Rescue
|
18
|
+
LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
|
19
|
+
|
20
|
+
DEFAULT_RESCUE_RESPONSE = :internal_server_error
|
21
|
+
DEFAULT_RESCUE_RESPONSES = {
|
22
|
+
'ActionController::RoutingError' => :not_found,
|
23
|
+
'ActionController::UnknownAction' => :not_found,
|
24
|
+
'ActiveRecord::RecordNotFound' => :not_found,
|
25
|
+
'ActiveRecord::StaleObjectError' => :conflict,
|
26
|
+
'ActiveRecord::RecordInvalid' => :unprocessable_entity,
|
27
|
+
'ActiveRecord::RecordNotSaved' => :unprocessable_entity,
|
28
|
+
'ActionController::MethodNotAllowed' => :method_not_allowed,
|
29
|
+
'ActionController::NotImplemented' => :not_implemented,
|
30
|
+
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
|
31
|
+
}
|
32
|
+
|
33
|
+
DEFAULT_RESCUE_TEMPLATE = 'diagnostics'
|
34
|
+
DEFAULT_RESCUE_TEMPLATES = {
|
35
|
+
'ActionView::MissingTemplate' => 'missing_template',
|
36
|
+
'ActionController::RoutingError' => 'routing_error',
|
37
|
+
'ActionController::UnknownAction' => 'unknown_action',
|
38
|
+
'ActionView::TemplateError' => 'template_error'
|
39
|
+
}
|
40
|
+
|
41
|
+
RESCUES_TEMPLATE_PATH = ActionView::Template::EagerPath.new_and_loaded(
|
42
|
+
File.join(File.dirname(__FILE__), "templates"))
|
43
|
+
|
44
|
+
def self.included(base) #:nodoc:
|
45
|
+
base.cattr_accessor :rescue_responses
|
46
|
+
base.rescue_responses = Hash.new(DEFAULT_RESCUE_RESPONSE)
|
47
|
+
base.rescue_responses.update DEFAULT_RESCUE_RESPONSES
|
48
|
+
|
49
|
+
base.cattr_accessor :rescue_templates
|
50
|
+
base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE)
|
51
|
+
base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES
|
52
|
+
|
53
|
+
base.extend(ClassMethods)
|
54
|
+
base.send :include, ActiveSupport::Rescuable
|
55
|
+
|
56
|
+
base.class_eval do
|
57
|
+
alias_method_chain :perform_action, :rescue
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module ClassMethods
|
62
|
+
def call_with_exception(env, exception) #:nodoc:
|
63
|
+
request = env["action_controller.rescue.request"] ||= Request.new(env)
|
64
|
+
response = env["action_controller.rescue.response"] ||= Response.new
|
65
|
+
new.process(request, response, :rescue_action, exception)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
# Exception handler called when the performance of an action raises
|
71
|
+
# an exception.
|
72
|
+
def rescue_action(exception)
|
73
|
+
rescue_with_handler(exception) ||
|
74
|
+
rescue_action_without_handler(exception)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Overwrite to implement custom logging of errors. By default
|
78
|
+
# logs as fatal.
|
79
|
+
def log_error(exception) #:doc:
|
80
|
+
ActiveSupport::Deprecation.silence do
|
81
|
+
if ActionView::TemplateError === exception
|
82
|
+
logger.fatal(exception.to_s)
|
83
|
+
else
|
84
|
+
logger.fatal(
|
85
|
+
"\n#{exception.class} (#{exception.message}):\n " +
|
86
|
+
clean_backtrace(exception).join("\n ") + "\n\n"
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Overwrite to implement public exception handling (for requests
|
93
|
+
# answering false to <tt>local_request?</tt>). By default will call
|
94
|
+
# render_optional_error_file. Override this method to provide more
|
95
|
+
# user friendly error messages.
|
96
|
+
def rescue_action_in_public(exception) #:doc:
|
97
|
+
render_optional_error_file response_code_for_rescue(exception)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Attempts to render a static error page based on the
|
101
|
+
# <tt>status_code</tt> thrown, or just return headers if no such file
|
102
|
+
# exists. At first, it will try to render a localized static page.
|
103
|
+
# For example, if a 500 error is being handled Rails and locale is :da,
|
104
|
+
# it will first attempt to render the file at <tt>public/500.da.html</tt>
|
105
|
+
# then attempt to render <tt>public/500.html</tt>. If none of them exist,
|
106
|
+
# the body of the response will be left empty.
|
107
|
+
def render_optional_error_file(status_code)
|
108
|
+
status = interpret_status(status_code)
|
109
|
+
locale_path = "#{Rails.public_path}/#{status[0,3]}.#{I18n.locale}.html" if I18n.locale
|
110
|
+
path = "#{Rails.public_path}/#{status[0,3]}.html"
|
111
|
+
|
112
|
+
if locale_path && File.exist?(locale_path)
|
113
|
+
render :file => locale_path, :status => status, :content_type => Mime::HTML
|
114
|
+
elsif File.exist?(path)
|
115
|
+
render :file => path, :status => status, :content_type => Mime::HTML
|
116
|
+
else
|
117
|
+
head status
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# True if the request came from localhost, 127.0.0.1. Override this
|
122
|
+
# method if you wish to redefine the meaning of a local request to
|
123
|
+
# include remote IP addresses or other criteria.
|
124
|
+
def local_request? #:doc:
|
125
|
+
LOCALHOST.any?{ |local_ip| request.remote_addr =~ local_ip && request.remote_ip =~ local_ip }
|
126
|
+
end
|
127
|
+
|
128
|
+
# Render detailed diagnostics for unhandled exceptions rescued from
|
129
|
+
# a controller action.
|
130
|
+
def rescue_action_locally(exception)
|
131
|
+
@template.instance_variable_set("@exception", exception)
|
132
|
+
@template.instance_variable_set("@rescues_path", RESCUES_TEMPLATE_PATH)
|
133
|
+
@template.instance_variable_set("@contents",
|
134
|
+
@template.render(:file => template_path_for_local_rescue(exception)))
|
135
|
+
|
136
|
+
response.content_type = Mime::HTML
|
137
|
+
render_for_file(rescues_path("layout"),
|
138
|
+
response_code_for_rescue(exception))
|
139
|
+
end
|
140
|
+
|
141
|
+
def rescue_action_without_handler(exception)
|
142
|
+
log_error(exception) if logger
|
143
|
+
erase_results if performed?
|
144
|
+
|
145
|
+
# Let the exception alter the response if it wants.
|
146
|
+
# For example, MethodNotAllowed sets the Allow header.
|
147
|
+
if exception.respond_to?(:handle_response!)
|
148
|
+
exception.handle_response!(response)
|
149
|
+
end
|
150
|
+
|
151
|
+
if consider_all_requests_local || local_request?
|
152
|
+
rescue_action_locally(exception)
|
153
|
+
else
|
154
|
+
rescue_action_in_public(exception)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
def perform_action_with_rescue #:nodoc:
|
160
|
+
perform_action_without_rescue
|
161
|
+
rescue Exception => exception
|
162
|
+
rescue_action(exception)
|
163
|
+
end
|
164
|
+
|
165
|
+
def rescues_path(template_name)
|
166
|
+
RESCUES_TEMPLATE_PATH["rescues/#{template_name}.erb"]
|
167
|
+
end
|
168
|
+
|
169
|
+
def template_path_for_local_rescue(exception)
|
170
|
+
rescues_path(rescue_templates[exception.class.name])
|
171
|
+
end
|
172
|
+
|
173
|
+
def response_code_for_rescue(exception)
|
174
|
+
rescue_responses[exception.class.name]
|
175
|
+
end
|
176
|
+
|
177
|
+
def clean_backtrace(exception)
|
178
|
+
defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
|
179
|
+
Rails.backtrace_cleaner.clean(exception.backtrace) :
|
180
|
+
exception.backtrace
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,682 @@
|
|
1
|
+
module ActionController
|
2
|
+
# == Overview
|
3
|
+
#
|
4
|
+
# ActionController::Resources are a way of defining RESTful \resources. A RESTful \resource, in basic terms,
|
5
|
+
# is something that can be pointed at and it will respond with a representation of the data requested.
|
6
|
+
# In real terms this could mean a user with a browser requests an HTML page, or that a desktop application
|
7
|
+
# requests XML data.
|
8
|
+
#
|
9
|
+
# RESTful design is based on the assumption that there are four generic verbs that a user of an
|
10
|
+
# application can request from a \resource (the noun).
|
11
|
+
#
|
12
|
+
# \Resources can be requested using four basic HTTP verbs (GET, POST, PUT, DELETE), the method used
|
13
|
+
# denotes the type of action that should take place.
|
14
|
+
#
|
15
|
+
# === The Different Methods and their Usage
|
16
|
+
#
|
17
|
+
# * GET - Requests for a \resource, no saving or editing of a \resource should occur in a GET request.
|
18
|
+
# * POST - Creation of \resources.
|
19
|
+
# * PUT - Editing of attributes on a \resource.
|
20
|
+
# * DELETE - Deletion of a \resource.
|
21
|
+
#
|
22
|
+
# === Examples
|
23
|
+
#
|
24
|
+
# # A GET request on the Posts resource is asking for all Posts
|
25
|
+
# GET /posts
|
26
|
+
#
|
27
|
+
# # A GET request on a single Post resource is asking for that particular Post
|
28
|
+
# GET /posts/1
|
29
|
+
#
|
30
|
+
# # A POST request on the Posts resource is asking for a Post to be created with the supplied details
|
31
|
+
# POST /posts # with => { :post => { :title => "My Whizzy New Post", :body => "I've got a brand new combine harvester" } }
|
32
|
+
#
|
33
|
+
# # A PUT request on a single Post resource is asking for a Post to be updated
|
34
|
+
# PUT /posts # with => { :id => 1, :post => { :title => "Changed Whizzy Title" } }
|
35
|
+
#
|
36
|
+
# # A DELETE request on a single Post resource is asking for it to be deleted
|
37
|
+
# DELETE /posts # with => { :id => 1 }
|
38
|
+
#
|
39
|
+
# By using the REST convention, users of our application can assume certain things about how the data
|
40
|
+
# is requested and how it is returned. Rails simplifies the routing part of RESTful design by
|
41
|
+
# supplying you with methods to create them in your routes.rb file.
|
42
|
+
#
|
43
|
+
# Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer
|
44
|
+
module Resources
|
45
|
+
INHERITABLE_OPTIONS = :namespace, :shallow
|
46
|
+
|
47
|
+
class Resource #:nodoc:
|
48
|
+
DEFAULT_ACTIONS = :index, :create, :new, :edit, :show, :update, :destroy
|
49
|
+
|
50
|
+
attr_reader :collection_methods, :member_methods, :new_methods
|
51
|
+
attr_reader :path_prefix, :name_prefix, :path_segment
|
52
|
+
attr_reader :plural, :singular
|
53
|
+
attr_reader :options
|
54
|
+
|
55
|
+
def initialize(entities, options)
|
56
|
+
@plural ||= entities
|
57
|
+
@singular ||= options[:singular] || plural.to_s.singularize
|
58
|
+
@path_segment = options.delete(:as) || @plural
|
59
|
+
|
60
|
+
@options = options
|
61
|
+
|
62
|
+
arrange_actions
|
63
|
+
add_default_actions
|
64
|
+
set_allowed_actions
|
65
|
+
set_prefixes
|
66
|
+
end
|
67
|
+
|
68
|
+
def controller
|
69
|
+
@controller ||= "#{options[:namespace]}#{(options[:controller] || plural).to_s}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def requirements(with_id = false)
|
73
|
+
@requirements ||= @options[:requirements] || {}
|
74
|
+
@id_requirement ||= { :id => @requirements.delete(:id) || /[^#{Routing::SEPARATORS.join}]+/ }
|
75
|
+
|
76
|
+
with_id ? @requirements.merge(@id_requirement) : @requirements
|
77
|
+
end
|
78
|
+
|
79
|
+
def conditions
|
80
|
+
@conditions ||= @options[:conditions] || {}
|
81
|
+
end
|
82
|
+
|
83
|
+
def path
|
84
|
+
@path ||= "#{path_prefix}/#{path_segment}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def new_path
|
88
|
+
new_action = self.options[:path_names][:new] if self.options[:path_names]
|
89
|
+
new_action ||= Base.resources_path_names[:new]
|
90
|
+
@new_path ||= "#{path}/#{new_action}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def shallow_path_prefix
|
94
|
+
@shallow_path_prefix ||= @options[:shallow] ? @options[:namespace].try(:sub, /\/$/, '') : path_prefix
|
95
|
+
end
|
96
|
+
|
97
|
+
def member_path
|
98
|
+
@member_path ||= "#{shallow_path_prefix}/#{path_segment}/:id"
|
99
|
+
end
|
100
|
+
|
101
|
+
def nesting_path_prefix
|
102
|
+
@nesting_path_prefix ||= "#{shallow_path_prefix}/#{path_segment}/:#{singular}_id"
|
103
|
+
end
|
104
|
+
|
105
|
+
def shallow_name_prefix
|
106
|
+
@shallow_name_prefix ||= @options[:shallow] ? @options[:namespace].try(:gsub, /\//, '_') : name_prefix
|
107
|
+
end
|
108
|
+
|
109
|
+
def nesting_name_prefix
|
110
|
+
"#{shallow_name_prefix}#{singular}_"
|
111
|
+
end
|
112
|
+
|
113
|
+
def action_separator
|
114
|
+
@action_separator ||= Base.resource_action_separator
|
115
|
+
end
|
116
|
+
|
117
|
+
def uncountable?
|
118
|
+
@singular.to_s == @plural.to_s
|
119
|
+
end
|
120
|
+
|
121
|
+
def has_action?(action)
|
122
|
+
!DEFAULT_ACTIONS.include?(action) || action_allowed?(action)
|
123
|
+
end
|
124
|
+
|
125
|
+
protected
|
126
|
+
def arrange_actions
|
127
|
+
@collection_methods = arrange_actions_by_methods(options.delete(:collection))
|
128
|
+
@member_methods = arrange_actions_by_methods(options.delete(:member))
|
129
|
+
@new_methods = arrange_actions_by_methods(options.delete(:new))
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_default_actions
|
133
|
+
add_default_action(member_methods, :get, :edit)
|
134
|
+
add_default_action(new_methods, :get, :new)
|
135
|
+
end
|
136
|
+
|
137
|
+
def set_allowed_actions
|
138
|
+
only, except = @options.values_at(:only, :except)
|
139
|
+
@allowed_actions ||= {}
|
140
|
+
|
141
|
+
if only == :all || except == :none
|
142
|
+
only = nil
|
143
|
+
except = []
|
144
|
+
elsif only == :none || except == :all
|
145
|
+
only = []
|
146
|
+
except = nil
|
147
|
+
end
|
148
|
+
|
149
|
+
if only
|
150
|
+
@allowed_actions[:only] = Array(only).map(&:to_sym)
|
151
|
+
elsif except
|
152
|
+
@allowed_actions[:except] = Array(except).map(&:to_sym)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def action_allowed?(action)
|
157
|
+
only, except = @allowed_actions.values_at(:only, :except)
|
158
|
+
(!only || only.include?(action)) && (!except || !except.include?(action))
|
159
|
+
end
|
160
|
+
|
161
|
+
def set_prefixes
|
162
|
+
@path_prefix = options.delete(:path_prefix)
|
163
|
+
@name_prefix = options.delete(:name_prefix)
|
164
|
+
end
|
165
|
+
|
166
|
+
def arrange_actions_by_methods(actions)
|
167
|
+
(actions || {}).inject({}) do |flipped_hash, (key, value)|
|
168
|
+
(flipped_hash[value] ||= []) << key
|
169
|
+
flipped_hash
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def add_default_action(collection, method, action)
|
174
|
+
(collection[method] ||= []).unshift(action)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class SingletonResource < Resource #:nodoc:
|
179
|
+
def initialize(entity, options)
|
180
|
+
@singular = @plural = entity
|
181
|
+
options[:controller] ||= @singular.to_s.pluralize
|
182
|
+
super
|
183
|
+
end
|
184
|
+
|
185
|
+
alias_method :shallow_path_prefix, :path_prefix
|
186
|
+
alias_method :shallow_name_prefix, :name_prefix
|
187
|
+
alias_method :member_path, :path
|
188
|
+
alias_method :nesting_path_prefix, :path
|
189
|
+
end
|
190
|
+
|
191
|
+
# Creates named routes for implementing verb-oriented controllers
|
192
|
+
# for a collection \resource.
|
193
|
+
#
|
194
|
+
# For example:
|
195
|
+
#
|
196
|
+
# map.resources :messages
|
197
|
+
#
|
198
|
+
# will map the following actions in the corresponding controller:
|
199
|
+
#
|
200
|
+
# class MessagesController < ActionController::Base
|
201
|
+
# # GET messages_url
|
202
|
+
# def index
|
203
|
+
# # return all messages
|
204
|
+
# end
|
205
|
+
#
|
206
|
+
# # GET new_message_url
|
207
|
+
# def new
|
208
|
+
# # return an HTML form for describing a new message
|
209
|
+
# end
|
210
|
+
#
|
211
|
+
# # POST messages_url
|
212
|
+
# def create
|
213
|
+
# # create a new message
|
214
|
+
# end
|
215
|
+
#
|
216
|
+
# # GET message_url(:id => 1)
|
217
|
+
# def show
|
218
|
+
# # find and return a specific message
|
219
|
+
# end
|
220
|
+
#
|
221
|
+
# # GET edit_message_url(:id => 1)
|
222
|
+
# def edit
|
223
|
+
# # return an HTML form for editing a specific message
|
224
|
+
# end
|
225
|
+
#
|
226
|
+
# # PUT message_url(:id => 1)
|
227
|
+
# def update
|
228
|
+
# # find and update a specific message
|
229
|
+
# end
|
230
|
+
#
|
231
|
+
# # DELETE message_url(:id => 1)
|
232
|
+
# def destroy
|
233
|
+
# # delete a specific message
|
234
|
+
# end
|
235
|
+
# end
|
236
|
+
#
|
237
|
+
# Along with the routes themselves, +resources+ generates named routes for use in
|
238
|
+
# controllers and views. <tt>map.resources :messages</tt> produces the following named routes and helpers:
|
239
|
+
#
|
240
|
+
# Named Route Helpers
|
241
|
+
# ============ =====================================================
|
242
|
+
# messages messages_url, hash_for_messages_url,
|
243
|
+
# messages_path, hash_for_messages_path
|
244
|
+
#
|
245
|
+
# message message_url(id), hash_for_message_url(id),
|
246
|
+
# message_path(id), hash_for_message_path(id)
|
247
|
+
#
|
248
|
+
# new_message new_message_url, hash_for_new_message_url,
|
249
|
+
# new_message_path, hash_for_new_message_path
|
250
|
+
#
|
251
|
+
# edit_message edit_message_url(id), hash_for_edit_message_url(id),
|
252
|
+
# edit_message_path(id), hash_for_edit_message_path(id)
|
253
|
+
#
|
254
|
+
# You can use these helpers instead of +url_for+ or methods that take +url_for+ parameters. For example:
|
255
|
+
#
|
256
|
+
# redirect_to :controller => 'messages', :action => 'index'
|
257
|
+
# # and
|
258
|
+
# <%= link_to "edit this message", :controller => 'messages', :action => 'edit', :id => @message.id %>
|
259
|
+
#
|
260
|
+
# now become:
|
261
|
+
#
|
262
|
+
# redirect_to messages_url
|
263
|
+
# # and
|
264
|
+
# <%= link_to "edit this message", edit_message_url(@message) # calls @message.id automatically
|
265
|
+
#
|
266
|
+
# Since web browsers don't support the PUT and DELETE verbs, you will need to add a parameter '_method' to your
|
267
|
+
# form tags. The form helpers make this a little easier. For an update form with a <tt>@message</tt> object:
|
268
|
+
#
|
269
|
+
# <%= form_tag message_path(@message), :method => :put %>
|
270
|
+
#
|
271
|
+
# or
|
272
|
+
#
|
273
|
+
# <% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
|
274
|
+
#
|
275
|
+
# or
|
276
|
+
#
|
277
|
+
# <% form_for @message do |f| %>
|
278
|
+
#
|
279
|
+
# which takes into account whether <tt>@message</tt> is a new record or not and generates the
|
280
|
+
# path and method accordingly.
|
281
|
+
#
|
282
|
+
# The +resources+ method accepts the following options to customize the resulting routes:
|
283
|
+
# * <tt>:collection</tt> - Add named routes for other actions that operate on the collection.
|
284
|
+
# Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>,
|
285
|
+
# an array of any of the previous, or <tt>:any</tt> if the method does not matter.
|
286
|
+
# These routes map to a URL like /messages/rss, with a route of +rss_messages_url+.
|
287
|
+
# * <tt>:member</tt> - Same as <tt>:collection</tt>, but for actions that operate on a specific member.
|
288
|
+
# * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new \resource action.
|
289
|
+
# * <tt>:controller</tt> - Specify the controller name for the routes.
|
290
|
+
# * <tt>:singular</tt> - Specify the singular name used in the member routes.
|
291
|
+
# * <tt>:requirements</tt> - Set custom routing parameter requirements; this is a hash of either
|
292
|
+
# regular expressions (which must match for the route to match) or extra parameters. For example:
|
293
|
+
#
|
294
|
+
# map.resource :profile, :path_prefix => ':name', :requirements => { :name => /[a-zA-Z]+/, :extra => 'value' }
|
295
|
+
#
|
296
|
+
# will only match if the first part is alphabetic, and will pass the parameter :extra to the controller.
|
297
|
+
# * <tt>:conditions</tt> - Specify custom routing recognition conditions. \Resources sets the <tt>:method</tt> value for the method-specific routes.
|
298
|
+
# * <tt>:as</tt> - Specify a different \resource name to use in the URL path. For example:
|
299
|
+
# # products_path == '/productos'
|
300
|
+
# map.resources :products, :as => 'productos' do |product|
|
301
|
+
# # product_reviews_path(product) == '/productos/1234/comentarios'
|
302
|
+
# product.resources :product_reviews, :as => 'comentarios'
|
303
|
+
# end
|
304
|
+
#
|
305
|
+
# * <tt>:has_one</tt> - Specify nested \resources, this is a shorthand for mapping singleton \resources beneath the current.
|
306
|
+
# * <tt>:has_many</tt> - Same has <tt>:has_one</tt>, but for plural \resources.
|
307
|
+
#
|
308
|
+
# You may directly specify the routing association with +has_one+ and +has_many+ like:
|
309
|
+
#
|
310
|
+
# map.resources :notes, :has_one => :author, :has_many => [:comments, :attachments]
|
311
|
+
#
|
312
|
+
# This is the same as:
|
313
|
+
#
|
314
|
+
# map.resources :notes do |notes|
|
315
|
+
# notes.resource :author
|
316
|
+
# notes.resources :comments
|
317
|
+
# notes.resources :attachments
|
318
|
+
# end
|
319
|
+
#
|
320
|
+
# * <tt>:path_names</tt> - Specify different path names for the actions. For example:
|
321
|
+
# # new_products_path == '/productos/nuevo'
|
322
|
+
# # bids_product_path(1) == '/productos/1/licitacoes'
|
323
|
+
# map.resources :products, :as => 'productos', :member => { :bids => :get }, :path_names => { :new => 'nuevo', :bids => 'licitacoes' }
|
324
|
+
#
|
325
|
+
# You can also set default action names from an environment, like this:
|
326
|
+
# config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' }
|
327
|
+
#
|
328
|
+
# * <tt>:path_prefix</tt> - Set a prefix to the routes with required route variables.
|
329
|
+
#
|
330
|
+
# Weblog comments usually belong to a post, so you might use +resources+ like:
|
331
|
+
#
|
332
|
+
# map.resources :articles
|
333
|
+
# map.resources :comments, :path_prefix => '/articles/:article_id'
|
334
|
+
#
|
335
|
+
# You can nest +resources+ calls to set this automatically:
|
336
|
+
#
|
337
|
+
# map.resources :articles do |article|
|
338
|
+
# article.resources :comments
|
339
|
+
# end
|
340
|
+
#
|
341
|
+
# The comment \resources work the same, but must now include a value for <tt>:article_id</tt>.
|
342
|
+
#
|
343
|
+
# article_comments_url(@article)
|
344
|
+
# article_comment_url(@article, @comment)
|
345
|
+
#
|
346
|
+
# article_comments_url(:article_id => @article)
|
347
|
+
# article_comment_url(:article_id => @article, :id => @comment)
|
348
|
+
#
|
349
|
+
# If you don't want to load all objects from the database you might want to use the <tt>article_id</tt> directly:
|
350
|
+
#
|
351
|
+
# articles_comments_url(@comment.article_id, @comment)
|
352
|
+
#
|
353
|
+
# * <tt>:name_prefix</tt> - Define a prefix for all generated routes, usually ending in an underscore.
|
354
|
+
# Use this if you have named routes that may clash.
|
355
|
+
#
|
356
|
+
# map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_'
|
357
|
+
# map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_'
|
358
|
+
#
|
359
|
+
# You may also use <tt>:name_prefix</tt> to override the generic named routes in a nested \resource:
|
360
|
+
#
|
361
|
+
# map.resources :articles do |article|
|
362
|
+
# article.resources :comments, :name_prefix => nil
|
363
|
+
# end
|
364
|
+
#
|
365
|
+
# This will yield named \resources like so:
|
366
|
+
#
|
367
|
+
# comments_url(@article)
|
368
|
+
# comment_url(@article, @comment)
|
369
|
+
#
|
370
|
+
# * <tt>:shallow</tt> - If true, paths for nested resources which reference a specific member
|
371
|
+
# (ie. those with an :id parameter) will not use the parent path prefix or name prefix.
|
372
|
+
#
|
373
|
+
# The <tt>:shallow</tt> option is inherited by any nested resource(s).
|
374
|
+
#
|
375
|
+
# For example, 'users', 'posts' and 'comments' all use shallow paths with the following nested resources:
|
376
|
+
#
|
377
|
+
# map.resources :users, :shallow => true do |user|
|
378
|
+
# user.resources :posts do |post|
|
379
|
+
# post.resources :comments
|
380
|
+
# end
|
381
|
+
# end
|
382
|
+
# # --> GET /users/1/posts (maps to the PostsController#index action as usual)
|
383
|
+
# # also adds the usual named route called "user_posts"
|
384
|
+
# # --> GET /posts/2 (maps to the PostsController#show action as if it were not nested)
|
385
|
+
# # also adds the named route called "post"
|
386
|
+
# # --> GET /posts/2/comments (maps to the CommentsController#index action)
|
387
|
+
# # also adds the named route called "post_comments"
|
388
|
+
# # --> GET /comments/2 (maps to the CommentsController#show action as if it were not nested)
|
389
|
+
# # also adds the named route called "comment"
|
390
|
+
#
|
391
|
+
# You may also use <tt>:shallow</tt> in combination with the +has_one+ and +has_many+ shorthand notations like:
|
392
|
+
#
|
393
|
+
# map.resources :users, :has_many => { :posts => :comments }, :shallow => true
|
394
|
+
#
|
395
|
+
# * <tt>:only</tt> and <tt>:except</tt> - Specify which of the seven default actions should be routed to.
|
396
|
+
#
|
397
|
+
# <tt>:only</tt> and <tt>:except</tt> may be set to <tt>:all</tt>, <tt>:none</tt>, an action name or a
|
398
|
+
# list of action names. By default, routes are generated for all seven actions.
|
399
|
+
#
|
400
|
+
# For example:
|
401
|
+
#
|
402
|
+
# map.resources :posts, :only => [:index, :show] do |post|
|
403
|
+
# post.resources :comments, :except => [:update, :destroy]
|
404
|
+
# end
|
405
|
+
# # --> GET /posts (maps to the PostsController#index action)
|
406
|
+
# # --> POST /posts (fails)
|
407
|
+
# # --> GET /posts/1 (maps to the PostsController#show action)
|
408
|
+
# # --> DELETE /posts/1 (fails)
|
409
|
+
# # --> POST /posts/1/comments (maps to the CommentsController#create action)
|
410
|
+
# # --> PUT /posts/1/comments/1 (fails)
|
411
|
+
#
|
412
|
+
# If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied.
|
413
|
+
#
|
414
|
+
# Examples:
|
415
|
+
#
|
416
|
+
# map.resources :messages, :path_prefix => "/thread/:thread_id"
|
417
|
+
# # --> GET /thread/7/messages/1
|
418
|
+
#
|
419
|
+
# map.resources :messages, :collection => { :rss => :get }
|
420
|
+
# # --> GET /messages/rss (maps to the #rss action)
|
421
|
+
# # also adds a named route called "rss_messages"
|
422
|
+
#
|
423
|
+
# map.resources :messages, :member => { :mark => :post }
|
424
|
+
# # --> POST /messages/1/mark (maps to the #mark action)
|
425
|
+
# # also adds a named route called "mark_message"
|
426
|
+
#
|
427
|
+
# map.resources :messages, :new => { :preview => :post }
|
428
|
+
# # --> POST /messages/new/preview (maps to the #preview action)
|
429
|
+
# # also adds a named route called "preview_new_message"
|
430
|
+
#
|
431
|
+
# map.resources :messages, :new => { :new => :any, :preview => :post }
|
432
|
+
# # --> POST /messages/new/preview (maps to the #preview action)
|
433
|
+
# # also adds a named route called "preview_new_message"
|
434
|
+
# # --> /messages/new can be invoked via any request method
|
435
|
+
#
|
436
|
+
# map.resources :messages, :controller => "categories",
|
437
|
+
# :path_prefix => "/category/:category_id",
|
438
|
+
# :name_prefix => "category_"
|
439
|
+
# # --> GET /categories/7/messages/1
|
440
|
+
# # has named route "category_message"
|
441
|
+
#
|
442
|
+
# The +resources+ method sets HTTP method restrictions on the routes it generates. For example, making an
|
443
|
+
# HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in
|
444
|
+
# <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for \resource routes.
|
445
|
+
def resources(*entities, &block)
|
446
|
+
options = entities.extract_options!
|
447
|
+
entities.each { |entity| map_resource(entity, options.dup, &block) }
|
448
|
+
end
|
449
|
+
|
450
|
+
# Creates named routes for implementing verb-oriented controllers for a singleton \resource.
|
451
|
+
# A singleton \resource is global to its current context. For unnested singleton \resources,
|
452
|
+
# the \resource is global to the current user visiting the application, such as a user's
|
453
|
+
# <tt>/account</tt> profile. For nested singleton \resources, the \resource is global to its parent
|
454
|
+
# \resource, such as a <tt>projects</tt> \resource that <tt>has_one :project_manager</tt>.
|
455
|
+
# The <tt>project_manager</tt> should be mapped as a singleton \resource under <tt>projects</tt>:
|
456
|
+
#
|
457
|
+
# map.resources :projects do |project|
|
458
|
+
# project.resource :project_manager
|
459
|
+
# end
|
460
|
+
#
|
461
|
+
# See +resources+ for general conventions. These are the main differences:
|
462
|
+
# * A singular name is given to <tt>map.resource</tt>. The default controller name is still taken from the plural name.
|
463
|
+
# * To specify a custom plural name, use the <tt>:plural</tt> option. There is no <tt>:singular</tt> option.
|
464
|
+
# * No default index route is created for the singleton \resource controller.
|
465
|
+
# * When nesting singleton \resources, only the singular name is used as the path prefix (example: 'account/messages/1')
|
466
|
+
#
|
467
|
+
# For example:
|
468
|
+
#
|
469
|
+
# map.resource :account
|
470
|
+
#
|
471
|
+
# maps these actions in the Accounts controller:
|
472
|
+
#
|
473
|
+
# class AccountsController < ActionController::Base
|
474
|
+
# # GET new_account_url
|
475
|
+
# def new
|
476
|
+
# # return an HTML form for describing the new account
|
477
|
+
# end
|
478
|
+
#
|
479
|
+
# # POST account_url
|
480
|
+
# def create
|
481
|
+
# # create an account
|
482
|
+
# end
|
483
|
+
#
|
484
|
+
# # GET account_url
|
485
|
+
# def show
|
486
|
+
# # find and return the account
|
487
|
+
# end
|
488
|
+
#
|
489
|
+
# # GET edit_account_url
|
490
|
+
# def edit
|
491
|
+
# # return an HTML form for editing the account
|
492
|
+
# end
|
493
|
+
#
|
494
|
+
# # PUT account_url
|
495
|
+
# def update
|
496
|
+
# # find and update the account
|
497
|
+
# end
|
498
|
+
#
|
499
|
+
# # DELETE account_url
|
500
|
+
# def destroy
|
501
|
+
# # delete the account
|
502
|
+
# end
|
503
|
+
# end
|
504
|
+
#
|
505
|
+
# Along with the routes themselves, +resource+ generates named routes for
|
506
|
+
# use in controllers and views. <tt>map.resource :account</tt> produces
|
507
|
+
# these named routes and helpers:
|
508
|
+
#
|
509
|
+
# Named Route Helpers
|
510
|
+
# ============ =============================================
|
511
|
+
# account account_url, hash_for_account_url,
|
512
|
+
# account_path, hash_for_account_path
|
513
|
+
#
|
514
|
+
# new_account new_account_url, hash_for_new_account_url,
|
515
|
+
# new_account_path, hash_for_new_account_path
|
516
|
+
#
|
517
|
+
# edit_account edit_account_url, hash_for_edit_account_url,
|
518
|
+
# edit_account_path, hash_for_edit_account_path
|
519
|
+
def resource(*entities, &block)
|
520
|
+
options = entities.extract_options!
|
521
|
+
entities.each { |entity| map_singleton_resource(entity, options.dup, &block) }
|
522
|
+
end
|
523
|
+
|
524
|
+
private
|
525
|
+
def map_resource(entities, options = {}, &block)
|
526
|
+
resource = Resource.new(entities, options)
|
527
|
+
|
528
|
+
with_options :controller => resource.controller do |map|
|
529
|
+
map_associations(resource, options)
|
530
|
+
|
531
|
+
if block_given?
|
532
|
+
with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
|
533
|
+
end
|
534
|
+
|
535
|
+
map_collection_actions(map, resource)
|
536
|
+
map_default_collection_actions(map, resource)
|
537
|
+
map_new_actions(map, resource)
|
538
|
+
map_member_actions(map, resource)
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
def map_singleton_resource(entities, options = {}, &block)
|
543
|
+
resource = SingletonResource.new(entities, options)
|
544
|
+
|
545
|
+
with_options :controller => resource.controller do |map|
|
546
|
+
map_associations(resource, options)
|
547
|
+
|
548
|
+
if block_given?
|
549
|
+
with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
|
550
|
+
end
|
551
|
+
|
552
|
+
map_collection_actions(map, resource)
|
553
|
+
map_new_actions(map, resource)
|
554
|
+
map_member_actions(map, resource)
|
555
|
+
map_default_singleton_actions(map, resource)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
def map_associations(resource, options)
|
560
|
+
map_has_many_associations(resource, options.delete(:has_many), options) if options[:has_many]
|
561
|
+
|
562
|
+
path_prefix = "#{options.delete(:path_prefix)}#{resource.nesting_path_prefix}"
|
563
|
+
name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}"
|
564
|
+
|
565
|
+
Array(options[:has_one]).each do |association|
|
566
|
+
resource(association, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => path_prefix, :name_prefix => name_prefix))
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
def map_has_many_associations(resource, associations, options)
|
571
|
+
case associations
|
572
|
+
when Hash
|
573
|
+
associations.each do |association,has_many|
|
574
|
+
map_has_many_associations(resource, association, options.merge(:has_many => has_many))
|
575
|
+
end
|
576
|
+
when Array
|
577
|
+
associations.each do |association|
|
578
|
+
map_has_many_associations(resource, association, options)
|
579
|
+
end
|
580
|
+
when Symbol, String
|
581
|
+
resources(associations, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :has_many => options[:has_many]))
|
582
|
+
else
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
def map_collection_actions(map, resource)
|
587
|
+
resource.collection_methods.each do |method, actions|
|
588
|
+
actions.each do |action|
|
589
|
+
[method].flatten.each do |m|
|
590
|
+
action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
|
591
|
+
action_path ||= action
|
592
|
+
|
593
|
+
map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
|
594
|
+
end
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
def map_default_collection_actions(map, resource)
|
600
|
+
index_route_name = "#{resource.name_prefix}#{resource.plural}"
|
601
|
+
|
602
|
+
if resource.uncountable?
|
603
|
+
index_route_name << "_index"
|
604
|
+
end
|
605
|
+
|
606
|
+
map_resource_routes(map, resource, :index, resource.path, index_route_name)
|
607
|
+
map_resource_routes(map, resource, :create, resource.path, index_route_name)
|
608
|
+
end
|
609
|
+
|
610
|
+
def map_default_singleton_actions(map, resource)
|
611
|
+
map_resource_routes(map, resource, :create, resource.path, "#{resource.shallow_name_prefix}#{resource.singular}")
|
612
|
+
end
|
613
|
+
|
614
|
+
def map_new_actions(map, resource)
|
615
|
+
resource.new_methods.each do |method, actions|
|
616
|
+
actions.each do |action|
|
617
|
+
route_path = resource.new_path
|
618
|
+
route_name = "new_#{resource.name_prefix}#{resource.singular}"
|
619
|
+
|
620
|
+
unless action == :new
|
621
|
+
route_path = "#{route_path}#{resource.action_separator}#{action}"
|
622
|
+
route_name = "#{action}_#{route_name}"
|
623
|
+
end
|
624
|
+
|
625
|
+
map_resource_routes(map, resource, action, route_path, route_name, method)
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
def map_member_actions(map, resource)
|
631
|
+
resource.member_methods.each do |method, actions|
|
632
|
+
actions.each do |action|
|
633
|
+
[method].flatten.each do |m|
|
634
|
+
action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
|
635
|
+
action_path ||= Base.resources_path_names[action] || action
|
636
|
+
|
637
|
+
map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m, { :force_id => true })
|
638
|
+
end
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
route_path = "#{resource.shallow_name_prefix}#{resource.singular}"
|
643
|
+
map_resource_routes(map, resource, :show, resource.member_path, route_path)
|
644
|
+
map_resource_routes(map, resource, :update, resource.member_path, route_path)
|
645
|
+
map_resource_routes(map, resource, :destroy, resource.member_path, route_path)
|
646
|
+
end
|
647
|
+
|
648
|
+
def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil, resource_options = {} )
|
649
|
+
if resource.has_action?(action)
|
650
|
+
action_options = action_options_for(action, resource, method, resource_options)
|
651
|
+
formatted_route_path = "#{route_path}.:format"
|
652
|
+
|
653
|
+
if route_name && @set.named_routes[route_name.to_sym].nil?
|
654
|
+
map.named_route(route_name, formatted_route_path, action_options)
|
655
|
+
else
|
656
|
+
map.connect(formatted_route_path, action_options)
|
657
|
+
end
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
def add_conditions_for(conditions, method)
|
662
|
+
({:conditions => conditions.dup}).tap do |options|
|
663
|
+
options[:conditions][:method] = method unless method == :any
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
def action_options_for(action, resource, method = nil, resource_options = {})
|
668
|
+
default_options = { :action => action.to_s }
|
669
|
+
require_id = !resource.kind_of?(SingletonResource)
|
670
|
+
force_id = resource_options[:force_id] && !resource.kind_of?(SingletonResource)
|
671
|
+
|
672
|
+
case default_options[:action]
|
673
|
+
when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
|
674
|
+
when "create"; default_options.merge(add_conditions_for(resource.conditions, method || :post)).merge(resource.requirements)
|
675
|
+
when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id))
|
676
|
+
when "update"; default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id))
|
677
|
+
when "destroy"; default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id))
|
678
|
+
else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(force_id))
|
679
|
+
end
|
680
|
+
end
|
681
|
+
end
|
682
|
+
end
|