actionpack 1.13.6 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +1400 -20
- data/MIT-LICENSE +1 -1
- data/README +5 -5
- data/RUNNING_UNIT_TESTS +4 -5
- data/Rakefile +5 -6
- data/install.rb +2 -2
- data/lib/action_controller.rb +11 -15
- data/lib/action_controller/assertions.rb +12 -25
- data/lib/action_controller/assertions/dom_assertions.rb +18 -4
- data/lib/action_controller/assertions/model_assertions.rb +8 -1
- data/lib/action_controller/assertions/response_assertions.rb +35 -12
- data/lib/action_controller/assertions/routing_assertions.rb +56 -12
- data/lib/action_controller/assertions/selector_assertions.rb +105 -38
- data/lib/action_controller/assertions/tag_assertions.rb +28 -15
- data/lib/action_controller/base.rb +318 -250
- data/lib/action_controller/benchmarking.rb +33 -29
- data/lib/action_controller/caching.rb +130 -64
- data/lib/action_controller/cgi_ext.rb +16 -0
- data/lib/action_controller/cgi_ext/{cookie_performance_fix.rb → cookie.rb} +25 -40
- data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
- data/lib/action_controller/cgi_ext/session.rb +73 -0
- data/lib/action_controller/cgi_ext/stdinput.rb +23 -0
- data/lib/action_controller/cgi_process.rb +34 -57
- data/lib/action_controller/components.rb +19 -36
- data/lib/action_controller/cookies.rb +10 -9
- data/lib/action_controller/dispatcher.rb +195 -0
- data/lib/action_controller/filters.rb +35 -34
- data/lib/action_controller/flash.rb +30 -35
- data/lib/action_controller/helpers.rb +121 -47
- data/lib/action_controller/http_authentication.rb +126 -0
- data/lib/action_controller/integration.rb +105 -101
- data/lib/action_controller/layout.rb +59 -47
- data/lib/action_controller/mime_responds.rb +57 -68
- data/lib/action_controller/mime_type.rb +43 -80
- data/lib/action_controller/mime_types.rb +20 -0
- data/lib/action_controller/polymorphic_routes.rb +88 -0
- data/lib/action_controller/record_identifier.rb +91 -0
- data/lib/action_controller/request.rb +553 -88
- data/lib/action_controller/request_forgery_protection.rb +126 -0
- data/lib/action_controller/request_profiler.rb +138 -0
- data/lib/action_controller/rescue.rb +185 -69
- data/lib/action_controller/resources.rb +211 -172
- data/lib/action_controller/response.rb +49 -8
- data/lib/action_controller/routing.rb +359 -236
- data/lib/action_controller/routing_optimisation.rb +119 -0
- data/lib/action_controller/session/active_record_store.rb +3 -2
- data/lib/action_controller/session/cookie_store.rb +161 -0
- data/lib/action_controller/session/mem_cache_store.rb +9 -16
- data/lib/action_controller/session_management.rb +17 -8
- data/lib/action_controller/streaming.rb +6 -3
- data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
- data/lib/action_controller/templates/rescues/{_trace.rhtml → _trace.erb} +0 -0
- data/lib/action_controller/templates/rescues/{diagnostics.rhtml → diagnostics.erb} +2 -2
- data/lib/action_controller/templates/rescues/{layout.rhtml → layout.erb} +0 -0
- data/lib/action_controller/templates/rescues/{missing_template.rhtml → missing_template.erb} +0 -0
- data/lib/action_controller/templates/rescues/{routing_error.rhtml → routing_error.erb} +0 -0
- data/lib/action_controller/templates/rescues/{template_error.rhtml → template_error.erb} +2 -2
- data/lib/action_controller/templates/rescues/{unknown_action.rhtml → unknown_action.erb} +0 -0
- data/lib/action_controller/test_case.rb +53 -0
- data/lib/action_controller/test_process.rb +59 -46
- data/lib/action_controller/url_rewriter.rb +48 -24
- data/lib/action_controller/vendor/html-scanner/html/document.rb +7 -4
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +11 -6
- data/lib/action_controller/verification.rb +27 -21
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +4 -4
- data/lib/action_view.rb +2 -3
- data/lib/action_view/base.rb +218 -63
- data/lib/action_view/compiled_templates.rb +1 -2
- data/lib/action_view/helpers/active_record_helper.rb +35 -17
- data/lib/action_view/helpers/asset_tag_helper.rb +395 -87
- data/lib/action_view/helpers/atom_feed_helper.rb +111 -0
- data/lib/action_view/helpers/benchmark_helper.rb +12 -5
- data/lib/action_view/helpers/cache_helper.rb +29 -0
- data/lib/action_view/helpers/capture_helper.rb +97 -63
- data/lib/action_view/helpers/date_helper.rb +295 -35
- data/lib/action_view/helpers/debug_helper.rb +6 -2
- data/lib/action_view/helpers/form_helper.rb +354 -111
- data/lib/action_view/helpers/form_options_helper.rb +171 -109
- data/lib/action_view/helpers/form_tag_helper.rb +332 -76
- data/lib/action_view/helpers/javascript_helper.rb +35 -11
- data/lib/action_view/helpers/javascripts/controls.js +484 -354
- data/lib/action_view/helpers/javascripts/dragdrop.js +88 -58
- data/lib/action_view/helpers/javascripts/effects.js +396 -364
- data/lib/action_view/helpers/javascripts/prototype.js +2817 -1107
- data/lib/action_view/helpers/number_helper.rb +84 -60
- data/lib/action_view/helpers/prototype_helper.rb +419 -43
- data/lib/action_view/helpers/record_identification_helper.rb +20 -0
- data/lib/action_view/helpers/record_tag_helper.rb +59 -0
- data/lib/action_view/helpers/sanitize_helper.rb +223 -0
- data/lib/action_view/helpers/scriptaculous_helper.rb +63 -4
- data/lib/action_view/helpers/tag_helper.rb +69 -39
- data/lib/action_view/helpers/text_helper.rb +221 -148
- data/lib/action_view/helpers/url_helper.rb +283 -165
- data/lib/action_view/partials.rb +134 -62
- data/lib/action_view/template_error.rb +4 -12
- data/lib/actionpack.rb +1 -0
- data/test/abstract_unit.rb +21 -1
- data/test/action_view_test.rb +26 -0
- data/test/active_record_unit.rb +12 -20
- data/test/activerecord/active_record_store_test.rb +2 -2
- data/test/activerecord/render_partial_with_record_identification_test.rb +74 -0
- data/test/controller/action_pack_assertions_test.rb +21 -152
- data/test/controller/addresses_render_test.rb +2 -7
- data/test/controller/assert_select_test.rb +120 -14
- data/test/controller/base_test.rb +11 -13
- data/test/controller/caching_test.rb +125 -5
- data/test/controller/capture_test.rb +23 -16
- data/test/controller/cgi_test.rb +66 -391
- data/test/controller/components_test.rb +31 -42
- data/test/controller/content_type_test.rb +1 -1
- data/test/controller/cookie_test.rb +42 -14
- data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -42
- data/test/controller/dispatcher_test.rb +123 -0
- data/test/controller/fake_models.rb +5 -0
- data/test/controller/filters_test.rb +44 -7
- data/test/controller/flash_test.rb +46 -2
- data/test/controller/fragment_store_setting_test.rb +10 -8
- data/test/controller/helper_test.rb +19 -2
- data/test/controller/html-scanner/document_test.rb +124 -0
- data/test/controller/html-scanner/node_test.rb +69 -0
- data/test/controller/html-scanner/sanitizer_test.rb +250 -0
- data/test/controller/html-scanner/tag_node_test.rb +239 -0
- data/test/controller/html-scanner/text_node_test.rb +51 -0
- data/test/controller/html-scanner/tokenizer_test.rb +125 -0
- data/test/controller/http_authentication_test.rb +54 -0
- data/test/controller/integration_test.rb +12 -26
- data/test/controller/layout_test.rb +64 -12
- data/test/controller/mime_responds_test.rb +193 -38
- data/test/controller/mime_type_test.rb +30 -8
- data/test/controller/new_render_test.rb +104 -22
- data/test/controller/polymorphic_routes_test.rb +98 -0
- data/test/controller/record_identifier_test.rb +103 -0
- data/test/controller/redirect_test.rb +120 -18
- data/test/controller/render_test.rb +195 -45
- data/test/controller/request_forgery_protection_test.rb +217 -0
- data/test/controller/request_test.rb +545 -27
- data/test/controller/rescue_test.rb +501 -0
- data/test/controller/resources_test.rb +258 -132
- data/test/controller/routing_test.rb +502 -106
- data/test/controller/selector_test.rb +5 -5
- data/test/controller/send_file_test.rb +17 -7
- data/test/controller/session/cookie_store_test.rb +246 -0
- data/test/controller/session/mem_cache_store_test.rb +182 -0
- data/test/controller/session_fixation_test.rb +8 -11
- data/test/controller/session_management_test.rb +7 -7
- data/test/controller/test_test.rb +150 -38
- data/test/controller/url_rewriter_test.rb +87 -12
- data/test/controller/verification_test.rb +11 -0
- data/test/controller/view_paths_test.rb +137 -0
- data/test/controller/webservice_test.rb +11 -75
- data/test/fixtures/addresses/{list.rhtml → list.erb} +0 -0
- data/test/fixtures/db_definitions/sqlite.sql +2 -1
- data/test/fixtures/developer.rb +2 -0
- data/test/fixtures/fun/games/{hello_world.rhtml → hello_world.erb} +0 -0
- data/test/fixtures/helpers/fun/pdf_helper.rb +1 -1
- data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
- data/test/fixtures/layouts/{builder.rxml → builder.builder} +0 -0
- data/test/fixtures/layouts/{standard.rhtml → standard.erb} +0 -0
- data/test/fixtures/layouts/{talk_from_action.rhtml → talk_from_action.erb} +0 -0
- data/test/fixtures/layouts/{yield.rhtml → yield.erb} +0 -0
- data/test/fixtures/multipart/binary_file +0 -0
- data/test/fixtures/multipart/bracketed_param +5 -0
- data/test/fixtures/override/test/hello_world.erb +1 -0
- data/test/fixtures/override2/layouts/test/sub.erb +1 -0
- data/test/fixtures/post_test/layouts/post.html.erb +1 -0
- data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
- data/test/fixtures/post_test/post/index.html.erb +1 -0
- data/test/fixtures/post_test/post/index.iphone.erb +1 -0
- data/test/fixtures/post_test/super_post/index.html.erb +1 -0
- data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
- data/test/fixtures/public/404.html +1 -0
- data/test/fixtures/public/500.html +1 -0
- data/test/fixtures/public/javascripts/application.js +0 -1
- data/test/fixtures/public/javascripts/bank.js +1 -0
- data/test/fixtures/public/javascripts/robber.js +1 -0
- data/test/fixtures/public/stylesheets/bank.css +1 -0
- data/test/fixtures/public/stylesheets/robber.css +1 -0
- data/test/fixtures/replies.yml +2 -0
- data/test/fixtures/reply.rb +2 -1
- data/test/fixtures/respond_to/{all_types_with_layout.rhtml → all_types_with_layout.html.erb} +0 -0
- data/test/fixtures/respond_to/{all_types_with_layout.rjs → all_types_with_layout.js.rjs} +0 -0
- data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
- data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
- data/test/fixtures/respond_to/{using_defaults.rhtml → using_defaults.html.erb} +0 -0
- data/test/fixtures/respond_to/{using_defaults.rjs → using_defaults.js.rjs} +0 -0
- data/test/fixtures/respond_to/{using_defaults.rxml → using_defaults.xml.builder} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rhtml → using_defaults_with_type_list.html.erb} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rjs → using_defaults_with_type_list.js.rjs} +0 -0
- data/test/fixtures/respond_to/{using_defaults_with_type_list.rxml → using_defaults_with_type_list.xml.builder} +0 -0
- data/test/fixtures/scope/test/{modgreet.rhtml → modgreet.erb} +0 -0
- data/test/fixtures/test/{_customer.rhtml → _customer.erb} +0 -0
- data/test/fixtures/test/{_customer_greeting.rhtml → _customer_greeting.erb} +0 -0
- data/test/fixtures/test/_hash_greeting.erb +1 -0
- data/test/fixtures/test/_hash_object.erb +2 -0
- data/test/fixtures/test/{_hello.rxml → _hello.builder} +0 -0
- data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
- data/test/fixtures/test/_partial.erb +1 -0
- data/test/fixtures/test/_partial.html.erb +1 -0
- data/test/fixtures/test/_partial.js.erb +1 -0
- data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
- data/test/fixtures/test/{_partial_only.rhtml → _partial_only.erb} +0 -0
- data/test/fixtures/test/{_person.rhtml → _person.erb} +0 -0
- data/test/fixtures/test/{action_talk_to_layout.rhtml → action_talk_to_layout.erb} +0 -0
- data/test/fixtures/test/{block_content_for.rhtml → block_content_for.erb} +0 -0
- data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
- data/test/fixtures/test/{capturing.rhtml → capturing.erb} +0 -0
- data/test/fixtures/test/{content_for.rhtml → content_for.erb} +0 -0
- data/test/fixtures/test/content_for_concatenated.erb +3 -0
- data/test/fixtures/test/content_for_with_parameter.erb +2 -0
- data/test/fixtures/test/dot.directory/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
- data/test/fixtures/test/{erb_content_for.rhtml → erb_content_for.erb} +0 -0
- data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.builder +1 -0
- data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
- data/test/fixtures/test/{greeting.rhtml → greeting.erb} +0 -0
- data/test/fixtures/test/{hello.rxml → hello.builder} +0 -0
- data/test/fixtures/test/{hello_world.rxml → hello_world.builder} +0 -0
- data/test/fixtures/test/{hello_world.rhtml → hello_world.erb} +0 -0
- data/test/fixtures/test/{hello_world_container.rxml → hello_world_container.builder} +0 -0
- data/test/fixtures/test/{hello_world_with_layout_false.rhtml → hello_world_with_layout_false.erb} +0 -0
- data/test/fixtures/test/{hello_xml_world.rxml → hello_xml_world.builder} +0 -0
- data/test/fixtures/test/list.erb +1 -0
- data/test/fixtures/test/{non_erb_block_content_for.rxml → non_erb_block_content_for.builder} +0 -0
- data/test/fixtures/test/{potential_conflicts.rhtml → potential_conflicts.erb} +0 -0
- data/test/fixtures/test/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
- data/test/fixtures/test/{render_file_with_locals.rhtml → render_file_with_locals.erb} +0 -0
- data/test/fixtures/test/{render_to_string_test.rhtml → render_to_string_test.erb} +0 -0
- data/test/fixtures/test/{update_element_with_capture.rhtml → update_element_with_capture.erb} +0 -0
- data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
- data/test/fixtures/topic.rb +1 -1
- data/test/template/active_record_helper_test.rb +67 -20
- data/test/template/asset_tag_helper_test.rb +222 -54
- data/test/template/atom_feed_helper_test.rb +101 -0
- data/test/template/benchmark_helper_test.rb +2 -2
- data/test/template/compiled_templates_test.rb +76 -32
- data/test/template/date_helper_test.rb +125 -9
- data/test/template/form_helper_test.rb +326 -33
- data/test/template/form_options_helper_test.rb +822 -15
- data/test/template/form_tag_helper_test.rb +96 -30
- data/test/template/javascript_helper_test.rb +61 -13
- data/test/template/number_helper_test.rb +12 -11
- data/test/template/prototype_helper_test.rb +185 -24
- data/test/template/sanitize_helper_test.rb +49 -0
- data/test/template/scriptaculous_helper_test.rb +9 -3
- data/test/template/tag_helper_test.rb +13 -2
- data/test/template/text_helper_test.rb +38 -52
- data/test/template/url_helper_test.rb +216 -46
- metadata +144 -116
- data/examples/.htaccess +0 -24
- data/examples/address_book/index.rhtml +0 -33
- data/examples/address_book/layout.rhtml +0 -8
- data/examples/address_book_controller.cgi +0 -9
- data/examples/address_book_controller.fcgi +0 -6
- data/examples/address_book_controller.rb +0 -52
- data/examples/address_book_controller.rbx +0 -4
- data/examples/benchmark.rb +0 -52
- data/examples/benchmark_with_ar.fcgi +0 -89
- data/examples/blog_controller.cgi +0 -53
- data/examples/debate/index.rhtml +0 -14
- data/examples/debate/new_topic.rhtml +0 -22
- data/examples/debate/topic.rhtml +0 -32
- data/examples/debate_controller.cgi +0 -57
- data/lib/action_controller/assertions/deprecated_assertions.rb +0 -228
- data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -36
- data/lib/action_controller/cgi_ext/cgi_methods.rb +0 -211
- data/lib/action_controller/cgi_ext/pstore_performance_fix.rb +0 -30
- data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +0 -95
- data/lib/action_controller/cgi_ext/session_performance_fix.rb +0 -30
- data/lib/action_controller/deprecated_dependencies.rb +0 -65
- data/lib/action_controller/deprecated_redirects.rb +0 -17
- data/lib/action_controller/deprecated_request_methods.rb +0 -34
- data/lib/action_controller/macros/auto_complete.rb +0 -53
- data/lib/action_controller/macros/in_place_editing.rb +0 -33
- data/lib/action_controller/pagination.rb +0 -408
- data/lib/action_controller/scaffolding.rb +0 -189
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -44
- data/lib/action_controller/templates/scaffolds/edit.rhtml +0 -7
- data/lib/action_controller/templates/scaffolds/layout.rhtml +0 -69
- data/lib/action_controller/templates/scaffolds/list.rhtml +0 -27
- data/lib/action_controller/templates/scaffolds/new.rhtml +0 -6
- data/lib/action_controller/templates/scaffolds/show.rhtml +0 -9
- data/lib/action_controller/vendor/xml_node.rb +0 -97
- data/lib/action_view/helpers/deprecated_helper.rb +0 -37
- data/lib/action_view/helpers/java_script_macros_helper.rb +0 -233
- data/lib/action_view/helpers/pagination_helper.rb +0 -86
- data/test/activerecord/active_record_assertions_test.rb +0 -92
- data/test/activerecord/pagination_test.rb +0 -165
- data/test/controller/deprecated_instance_variables_test.rb +0 -48
- data/test/controller/raw_post_test.rb +0 -68
- data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +0 -1
- data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +0 -1
- data/test/fixtures/respond_to/layouts/standard.rhtml +0 -1
- data/test/fixtures/test/_hash_object.rhtml +0 -1
- data/test/fixtures/test/list.rhtml +0 -1
- data/test/template/deprecated_helper_test.rb +0 -36
- data/test/template/deprecated_instance_variables_test.rb +0 -43
- data/test/template/java_script_macros_helper_test.rb +0 -109
@@ -4,7 +4,7 @@
|
|
4
4
|
#++
|
5
5
|
|
6
6
|
require 'rexml/document'
|
7
|
-
require
|
7
|
+
require 'html/document'
|
8
8
|
|
9
9
|
module ActionController
|
10
10
|
module Assertions
|
@@ -13,15 +13,13 @@ module ActionController
|
|
13
13
|
end
|
14
14
|
|
15
15
|
# Adds the #assert_select method for use in Rails functional
|
16
|
-
# test cases
|
17
|
-
#
|
18
|
-
# Use #assert_select to make assertions on the response HTML of a controller
|
16
|
+
# test cases, which can be used to make assertions on the response HTML of a controller
|
19
17
|
# action. You can also call #assert_select within another #assert_select to
|
20
18
|
# make assertions on elements selected by the enclosing assertion.
|
21
19
|
#
|
22
20
|
# Use #css_select to select elements without making an assertions, either
|
23
21
|
# from the response HTML or elements selected by the enclosing assertion.
|
24
|
-
#
|
22
|
+
#
|
25
23
|
# In addition to HTML responses, you can make the following assertions:
|
26
24
|
# * #assert_select_rjs -- Assertions on HTML content of RJS update and
|
27
25
|
# insertion operations.
|
@@ -29,7 +27,7 @@ module ActionController
|
|
29
27
|
# for example for dealing with feed item descriptions.
|
30
28
|
# * #assert_select_email -- Assertions on the HTML body of an e-mail.
|
31
29
|
#
|
32
|
-
# Also see HTML::Selector
|
30
|
+
# Also see HTML::Selector to learn how to use selectors.
|
33
31
|
module SelectorAssertions
|
34
32
|
# :call-seq:
|
35
33
|
# css_select(selector) => array
|
@@ -49,12 +47,26 @@ module ActionController
|
|
49
47
|
# The selector may be a CSS selector expression (+String+), an expression
|
50
48
|
# with substitution values (+Array+) or an HTML::Selector object.
|
51
49
|
#
|
52
|
-
#
|
50
|
+
# ==== Examples
|
51
|
+
# # Selects all div tags
|
52
|
+
# divs = css_select("div")
|
53
|
+
#
|
54
|
+
# # Selects all paragraph tags and does something interesting
|
55
|
+
# pars = css_select("p")
|
56
|
+
# pars.each do |par|
|
57
|
+
# # Do something fun with paragraphs here...
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# # Selects all list items in unordered lists
|
61
|
+
# items = css_select("ul>li")
|
62
|
+
#
|
63
|
+
# # Selects all form tags and then all inputs inside the form
|
53
64
|
# forms = css_select("form")
|
54
65
|
# forms.each do |form|
|
55
66
|
# inputs = css_select(form, "input")
|
56
67
|
# ...
|
57
68
|
# end
|
69
|
+
#
|
58
70
|
def css_select(*args)
|
59
71
|
# See assert_select to understand what's going on here.
|
60
72
|
arg = args.shift
|
@@ -66,6 +78,7 @@ module ActionController
|
|
66
78
|
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
|
67
79
|
elsif @selected
|
68
80
|
matches = []
|
81
|
+
|
69
82
|
@selected.each do |selected|
|
70
83
|
subset = css_select(selected, HTML::Selector.new(arg.dup, args.dup))
|
71
84
|
subset.each do |match|
|
@@ -105,12 +118,13 @@ module ActionController
|
|
105
118
|
# response HTML. Calling #assert_select inside an #assert_select block will
|
106
119
|
# run the assertion for each element selected by the enclosing assertion.
|
107
120
|
#
|
108
|
-
#
|
121
|
+
# ==== Example
|
109
122
|
# assert_select "ol>li" do |elements|
|
110
123
|
# elements.each do |element|
|
111
124
|
# assert_select element, "li"
|
112
125
|
# end
|
113
126
|
# end
|
127
|
+
#
|
114
128
|
# Or for short:
|
115
129
|
# assert_select "ol>li" do
|
116
130
|
# assert_select "li"
|
@@ -148,7 +162,7 @@ module ActionController
|
|
148
162
|
# If the method is called with a block, once all equality tests are
|
149
163
|
# evaluated the block is called with an array of all matched elements.
|
150
164
|
#
|
151
|
-
#
|
165
|
+
# ==== Examples
|
152
166
|
#
|
153
167
|
# # At least one form element
|
154
168
|
# assert_select "form"
|
@@ -196,7 +210,7 @@ module ActionController
|
|
196
210
|
# Otherwise just operate on the response document.
|
197
211
|
root = response_from_page_or_rjs
|
198
212
|
end
|
199
|
-
|
213
|
+
|
200
214
|
# First or second argument is the selector: string and we pass
|
201
215
|
# all remaining arguments. Array and we pass the argument. Also
|
202
216
|
# accepts selector itself.
|
@@ -209,7 +223,7 @@ module ActionController
|
|
209
223
|
selector = arg
|
210
224
|
else raise ArgumentError, "Expecting a selector as the first argument"
|
211
225
|
end
|
212
|
-
|
226
|
+
|
213
227
|
# Next argument is used for equality tests.
|
214
228
|
equals = {}
|
215
229
|
case arg = args.shift
|
@@ -277,14 +291,10 @@ module ActionController
|
|
277
291
|
# found one but expecting two.
|
278
292
|
message ||= content_mismatch if matches.empty?
|
279
293
|
# Test minimum/maximum occurrence.
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
if equals[:maximum]
|
285
|
-
assert matches.size <= equals[:maximum], message ||
|
286
|
-
"Expected at most #{equals[:maximum]} elements, found #{matches.size}."
|
287
|
-
end
|
294
|
+
min, max = equals[:minimum], equals[:maximum]
|
295
|
+
message = message || %(Expected #{count_description(min, max)} matching "#{selector.to_s}", found #{matches.size}.)
|
296
|
+
assert matches.size >= min, message if min
|
297
|
+
assert matches.size <= max, message if max
|
288
298
|
|
289
299
|
# If a block is given call that block. Set @selected to allow
|
290
300
|
# nested assert_select, which can be nested several levels deep.
|
@@ -300,7 +310,19 @@ module ActionController
|
|
300
310
|
# Returns all matches elements.
|
301
311
|
matches
|
302
312
|
end
|
303
|
-
|
313
|
+
|
314
|
+
def count_description(min, max) #:nodoc:
|
315
|
+
pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
|
316
|
+
|
317
|
+
if min && max && (max != min)
|
318
|
+
"between #{min} and #{max} elements"
|
319
|
+
elsif min && !(min == 1 && max == 1)
|
320
|
+
"at least #{min} #{pluralize['element', min]}"
|
321
|
+
elsif max
|
322
|
+
"at most #{max} #{pluralize['element', max]}"
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
304
326
|
# :call-seq:
|
305
327
|
# assert_select_rjs(id?) { |elements| ... }
|
306
328
|
# assert_select_rjs(statement, id?) { |elements| ... }
|
@@ -317,12 +339,17 @@ module ActionController
|
|
317
339
|
# that update or insert an element with that identifier.
|
318
340
|
#
|
319
341
|
# Use the first argument to narrow down assertions to only statements
|
320
|
-
# of that type. Possible values are
|
321
|
-
#
|
342
|
+
# of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>,
|
343
|
+
# <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tt> and
|
344
|
+
# <tt>:insert_html</tt>.
|
322
345
|
#
|
323
|
-
# Use the argument
|
346
|
+
# Use the argument <tt>:insert</tt> followed by an insertion position to narrow
|
324
347
|
# down the assertion to only statements that insert elements in that
|
325
|
-
# position. Possible values are
|
348
|
+
# position. Possible values are <tt>:top</tt>, <tt>:bottom</tt>, <tt>:before</tt>
|
349
|
+
# and <tt>:after</tt>.
|
350
|
+
#
|
351
|
+
# Using the <tt>:remove</tt> statement, you will be able to pass a block, but it will
|
352
|
+
# be ignored as there is no HTML passed for this statement.
|
326
353
|
#
|
327
354
|
# === Using blocks
|
328
355
|
#
|
@@ -339,7 +366,7 @@ module ActionController
|
|
339
366
|
# but without distinguishing whether the content is returned in an HTML
|
340
367
|
# or JavaScript.
|
341
368
|
#
|
342
|
-
#
|
369
|
+
# ==== Examples
|
343
370
|
#
|
344
371
|
# # Replacing the element foo.
|
345
372
|
# # page.replace 'foo', ...
|
@@ -352,6 +379,9 @@ module ActionController
|
|
352
379
|
# # Inserting into the element bar, top position.
|
353
380
|
# assert_select_rjs :insert, :top, "bar"
|
354
381
|
#
|
382
|
+
# # Remove the element bar
|
383
|
+
# assert_select_rjs :remove, "bar"
|
384
|
+
#
|
355
385
|
# # Changing the element foo, with an image.
|
356
386
|
# assert_select_rjs "foo" do
|
357
387
|
# assert_select "img[src=/images/logo.gif""
|
@@ -373,6 +403,7 @@ module ActionController
|
|
373
403
|
# any RJS statement.
|
374
404
|
if arg.is_a?(Symbol)
|
375
405
|
rjs_type = arg
|
406
|
+
|
376
407
|
if rjs_type == :insert
|
377
408
|
arg = args.shift
|
378
409
|
insertion = "insert_#{arg}".to_sym
|
@@ -400,20 +431,29 @@ module ActionController
|
|
400
431
|
case rjs_type
|
401
432
|
when :chained_replace, :chained_replace_html
|
402
433
|
Regexp.new("\\$\\(\"#{id}\"\\)#{statement}\\(#{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
|
434
|
+
when :remove, :show, :hide, :toggle
|
435
|
+
Regexp.new("#{statement}\\(\"#{id}\"\\)")
|
403
436
|
else
|
404
437
|
Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
|
405
438
|
end
|
406
439
|
|
407
440
|
# Duplicate the body since the next step involves destroying it.
|
408
441
|
matches = nil
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
442
|
+
case rjs_type
|
443
|
+
when :remove, :show, :hide, :toggle
|
444
|
+
matches = @response.body.match(pattern)
|
445
|
+
else
|
446
|
+
@response.body.gsub(pattern) do |match|
|
447
|
+
html = unescape_rjs($2)
|
448
|
+
matches ||= []
|
449
|
+
matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
|
450
|
+
""
|
451
|
+
end
|
414
452
|
end
|
453
|
+
|
415
454
|
if matches
|
416
|
-
|
455
|
+
assert_block("") { true } # to count the assertion
|
456
|
+
if block_given? && !([:remove, :show, :hide, :toggle].include? rjs_type)
|
417
457
|
begin
|
418
458
|
in_scope, @selected = @selected, matches
|
419
459
|
yield matches
|
@@ -441,8 +481,20 @@ module ActionController
|
|
441
481
|
# The content of each element is un-encoded, and wrapped in the root
|
442
482
|
# element +encoded+. It then calls the block with all un-encoded elements.
|
443
483
|
#
|
444
|
-
#
|
484
|
+
# ==== Examples
|
485
|
+
# # Selects all bold tags from within the title of an ATOM feed's entries (perhaps to nab a section name prefix)
|
486
|
+
# assert_select_feed :atom, 1.0 do
|
487
|
+
# # Select each entry item and then the title item
|
488
|
+
# assert_select "entry>title" do
|
489
|
+
# # Run assertions on the encoded title elements
|
490
|
+
# assert_select_encoded do
|
491
|
+
# assert_select "b"
|
492
|
+
# end
|
493
|
+
# end
|
494
|
+
# end
|
495
|
+
#
|
445
496
|
#
|
497
|
+
# # Selects all paragraph tags from within the description of an RSS feed
|
446
498
|
# assert_select_feed :rss, 2.0 do
|
447
499
|
# # Select description element of each feed item.
|
448
500
|
# assert_select "channel>item>description" do
|
@@ -493,11 +545,19 @@ module ActionController
|
|
493
545
|
# You must enable deliveries for this assertion to work, use:
|
494
546
|
# ActionMailer::Base.perform_deliveries = true
|
495
547
|
#
|
496
|
-
#
|
548
|
+
# ==== Examples
|
549
|
+
#
|
550
|
+
# assert_select_email do
|
551
|
+
# assert_select "h1", "Email alert"
|
552
|
+
# end
|
553
|
+
#
|
554
|
+
# assert_select_email do
|
555
|
+
# items = assert_select "ol>li"
|
556
|
+
# items.each do
|
557
|
+
# # Work with items here...
|
558
|
+
# end
|
559
|
+
# end
|
497
560
|
#
|
498
|
-
# assert_select_email do
|
499
|
-
# assert_select "h1", "Email alert"
|
500
|
-
# end
|
501
561
|
def assert_select_email(&block)
|
502
562
|
deliveries = ActionMailer::Base.deliveries
|
503
563
|
assert !deliveries.empty?, "No e-mail in delivery list"
|
@@ -519,6 +579,10 @@ module ActionController
|
|
519
579
|
:replace_html => /Element\.update/,
|
520
580
|
:chained_replace => /\.replace/,
|
521
581
|
:chained_replace_html => /\.update/,
|
582
|
+
:remove => /Element\.remove/,
|
583
|
+
:show => /Element\.show/,
|
584
|
+
:hide => /Element\.hide/,
|
585
|
+
:toggle => /Element\.toggle/
|
522
586
|
}
|
523
587
|
RJS_INSERTIONS = [:top, :bottom, :before, :after]
|
524
588
|
RJS_INSERTIONS.each do |insertion|
|
@@ -537,10 +601,12 @@ module ActionController
|
|
537
601
|
# #assert_select and #css_select call this to obtain the content in the HTML
|
538
602
|
# page, or from all the RJS statements, depending on the type of response.
|
539
603
|
def response_from_page_or_rjs()
|
540
|
-
content_type = @response.
|
604
|
+
content_type = @response.content_type
|
605
|
+
|
541
606
|
if content_type && content_type =~ /text\/javascript/
|
542
607
|
body = @response.body.dup
|
543
608
|
root = HTML::Node.new(nil)
|
609
|
+
|
544
610
|
while true
|
545
611
|
next if body.sub!(RJS_PATTERN_EVERYTHING) do |match|
|
546
612
|
html = unescape_rjs($3)
|
@@ -550,6 +616,7 @@ module ActionController
|
|
550
616
|
end
|
551
617
|
break
|
552
618
|
end
|
619
|
+
|
553
620
|
root
|
554
621
|
else
|
555
622
|
html_document.root
|
@@ -560,6 +627,7 @@ module ActionController
|
|
560
627
|
def unescape_rjs(rjs_string)
|
561
628
|
# RJS encodes double quotes and line breaks.
|
562
629
|
unescaped= rjs_string.gsub('\"', '"')
|
630
|
+
unescaped.gsub!(/\\\//, '/')
|
563
631
|
unescaped.gsub!('\n', "\n")
|
564
632
|
unescaped.gsub!('\076', '>')
|
565
633
|
unescaped.gsub!('\074', '<')
|
@@ -567,7 +635,6 @@ module ActionController
|
|
567
635
|
unescaped.gsub!(RJS_PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
|
568
636
|
unescaped
|
569
637
|
end
|
570
|
-
|
571
638
|
end
|
572
639
|
end
|
573
640
|
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'rexml/document'
|
2
|
-
require
|
2
|
+
require 'html/document'
|
3
3
|
|
4
4
|
module ActionController
|
5
5
|
module Assertions
|
6
|
+
# Pair of assertions to testing elements in the HTML output of the response.
|
6
7
|
module TagAssertions
|
7
8
|
# Asserts that there is a tag/node/element in the body of the response
|
8
9
|
# that meets all of the given conditions. The +conditions+ parameter must
|
@@ -37,8 +38,8 @@ module ActionController
|
|
37
38
|
# to match on the children, and only matching children will be
|
38
39
|
# counted.
|
39
40
|
# * <tt>:content</tt>: the textual content of the node must match the
|
40
|
-
#
|
41
|
-
#
|
41
|
+
# given value. This will not match HTML tags in the body of a
|
42
|
+
# tag--only text.
|
42
43
|
#
|
43
44
|
# Conditions are matched using the following algorithm:
|
44
45
|
#
|
@@ -48,39 +49,39 @@ module ActionController
|
|
48
49
|
# * if the condition is +true+, the value must not be +nil+.
|
49
50
|
# * if the condition is +false+ or +nil+, the value must be +nil+.
|
50
51
|
#
|
51
|
-
#
|
52
|
+
# === Examples
|
52
53
|
#
|
53
|
-
# #
|
54
|
+
# # Assert that there is a "span" tag
|
54
55
|
# assert_tag :tag => "span"
|
55
56
|
#
|
56
|
-
# #
|
57
|
+
# # Assert that there is a "span" tag with id="x"
|
57
58
|
# assert_tag :tag => "span", :attributes => { :id => "x" }
|
58
59
|
#
|
59
|
-
# #
|
60
|
+
# # Assert that there is a "span" tag using the short-hand
|
60
61
|
# assert_tag :span
|
61
62
|
#
|
62
|
-
# #
|
63
|
+
# # Assert that there is a "span" tag with id="x" using the short-hand
|
63
64
|
# assert_tag :span, :attributes => { :id => "x" }
|
64
65
|
#
|
65
|
-
# #
|
66
|
+
# # Assert that there is a "span" inside of a "div"
|
66
67
|
# assert_tag :tag => "span", :parent => { :tag => "div" }
|
67
68
|
#
|
68
|
-
# #
|
69
|
+
# # Assert that there is a "span" somewhere inside a table
|
69
70
|
# assert_tag :tag => "span", :ancestor => { :tag => "table" }
|
70
71
|
#
|
71
|
-
# #
|
72
|
+
# # Assert that there is a "span" with at least one "em" child
|
72
73
|
# assert_tag :tag => "span", :child => { :tag => "em" }
|
73
74
|
#
|
74
|
-
# #
|
75
|
+
# # Assert that there is a "span" containing a (possibly nested)
|
75
76
|
# # "strong" tag.
|
76
77
|
# assert_tag :tag => "span", :descendant => { :tag => "strong" }
|
77
78
|
#
|
78
|
-
# #
|
79
|
+
# # Assert that there is a "span" containing between 2 and 4 "em" tags
|
79
80
|
# # as immediate children
|
80
81
|
# assert_tag :tag => "span",
|
81
82
|
# :children => { :count => 2..4, :only => { :tag => "em" } }
|
82
83
|
#
|
83
|
-
# #
|
84
|
+
# # Get funky: assert that there is a "div", with an "ul" ancestor
|
84
85
|
# # and an "li" parent (with "class" = "enum"), and containing a
|
85
86
|
# # "span" descendant that contains text matching /hello world/
|
86
87
|
# assert_tag :tag => "div",
|
@@ -90,7 +91,7 @@ module ActionController
|
|
90
91
|
# :descendant => { :tag => "span",
|
91
92
|
# :child => /hello world/ }
|
92
93
|
#
|
93
|
-
# <
|
94
|
+
# <b>Please note</b>: #assert_tag and #assert_no_tag only work
|
94
95
|
# with well-formed XHTML. They recognize a few tags as implicitly self-closing
|
95
96
|
# (like br and hr and such) but will not work correctly with tags
|
96
97
|
# that allow optional closing tags (p, li, td). <em>You must explicitly
|
@@ -105,6 +106,18 @@ module ActionController
|
|
105
106
|
|
106
107
|
# Identical to #assert_tag, but asserts that a matching tag does _not_
|
107
108
|
# exist. (See #assert_tag for a full discussion of the syntax.)
|
109
|
+
#
|
110
|
+
# === Examples
|
111
|
+
# # Assert that there is not a "div" containing a "p"
|
112
|
+
# assert_no_tag :tag => "div", :descendant => { :tag => "p" }
|
113
|
+
#
|
114
|
+
# # Assert that an unordered list is empty
|
115
|
+
# assert_no_tag :tag => "ul", :descendant => { :tag => "li" }
|
116
|
+
#
|
117
|
+
# # Assert that there is not a "p" tag with between 1 to 3 "img" tags
|
118
|
+
# # as immediate children
|
119
|
+
# assert_no_tag :tag => "p",
|
120
|
+
# :children => { :count => 1..3, :only => { :tag => "img" } }
|
108
121
|
def assert_no_tag(*opts)
|
109
122
|
clean_backtrace do
|
110
123
|
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
|
@@ -11,10 +11,16 @@ require 'set'
|
|
11
11
|
module ActionController #:nodoc:
|
12
12
|
class ActionControllerError < StandardError #:nodoc:
|
13
13
|
end
|
14
|
+
|
14
15
|
class SessionRestoreError < ActionControllerError #:nodoc:
|
15
16
|
end
|
17
|
+
|
16
18
|
class MissingTemplate < ActionControllerError #:nodoc:
|
17
19
|
end
|
20
|
+
|
21
|
+
class RenderError < ActionControllerError #:nodoc:
|
22
|
+
end
|
23
|
+
|
18
24
|
class RoutingError < ActionControllerError #:nodoc:
|
19
25
|
attr_reader :failures
|
20
26
|
def initialize(message, failures=[])
|
@@ -22,14 +28,39 @@ module ActionController #:nodoc:
|
|
22
28
|
@failures = failures
|
23
29
|
end
|
24
30
|
end
|
31
|
+
|
32
|
+
class MethodNotAllowed < ActionControllerError #:nodoc:
|
33
|
+
attr_reader :allowed_methods
|
34
|
+
|
35
|
+
def initialize(*allowed_methods)
|
36
|
+
super("Only #{allowed_methods.to_sentence} requests are allowed.")
|
37
|
+
@allowed_methods = allowed_methods
|
38
|
+
end
|
39
|
+
|
40
|
+
def allowed_methods_header
|
41
|
+
allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', '
|
42
|
+
end
|
43
|
+
|
44
|
+
def handle_response!(response)
|
45
|
+
response.headers['Allow'] ||= allowed_methods_header
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class NotImplemented < MethodNotAllowed #:nodoc:
|
50
|
+
end
|
51
|
+
|
25
52
|
class UnknownController < ActionControllerError #:nodoc:
|
26
53
|
end
|
54
|
+
|
27
55
|
class UnknownAction < ActionControllerError #:nodoc:
|
28
56
|
end
|
57
|
+
|
29
58
|
class MissingFile < ActionControllerError #:nodoc:
|
30
59
|
end
|
60
|
+
|
31
61
|
class RenderError < ActionControllerError #:nodoc:
|
32
62
|
end
|
63
|
+
|
33
64
|
class SessionOverflowError < ActionControllerError #:nodoc:
|
34
65
|
DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
|
35
66
|
|
@@ -37,13 +68,15 @@ module ActionController #:nodoc:
|
|
37
68
|
super(message || DEFAULT_MESSAGE)
|
38
69
|
end
|
39
70
|
end
|
71
|
+
|
40
72
|
class DoubleRenderError < ActionControllerError #:nodoc:
|
41
|
-
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and
|
73
|
+
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\". Finally, note that to cause a before filter to halt execution of the rest of the filter chain, the filter must return false, explicitly, so \"render(...) and return false\"."
|
42
74
|
|
43
75
|
def initialize(message = nil)
|
44
76
|
super(message || DEFAULT_MESSAGE)
|
45
77
|
end
|
46
78
|
end
|
79
|
+
|
47
80
|
class RedirectBackError < ActionControllerError #:nodoc:
|
48
81
|
DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
|
49
82
|
|
@@ -52,6 +85,9 @@ module ActionController #:nodoc:
|
|
52
85
|
end
|
53
86
|
end
|
54
87
|
|
88
|
+
class UnknownHttpMethod < ActionControllerError #:nodoc:
|
89
|
+
end
|
90
|
+
|
55
91
|
# Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed
|
56
92
|
# on request and then either render a template or redirect to another action. An action is defined as a public method
|
57
93
|
# on the controller, which will automatically be made accessible to the web-server through Rails Routes.
|
@@ -71,7 +107,7 @@ module ActionController #:nodoc:
|
|
71
107
|
#
|
72
108
|
# Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
|
73
109
|
# after executing code in the action. For example, the +index+ action of the +GuestBookController+ would render the
|
74
|
-
# template <tt>app/views/guestbook/index.
|
110
|
+
# template <tt>app/views/guestbook/index.erb</tt> by default after populating the <tt>@entries</tt> instance variable.
|
75
111
|
#
|
76
112
|
# Unlike index, the sign action will not render a template. After performing its main purpose (creating a
|
77
113
|
# new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external
|
@@ -128,17 +164,26 @@ module ActionController #:nodoc:
|
|
128
164
|
# For removing objects from the session, you can either assign a single key to nil, like <tt>session[:person] = nil</tt>, or you can
|
129
165
|
# remove the entire session with reset_session.
|
130
166
|
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
167
|
+
# Sessions are stored in a browser cookie that's cryptographically signed, but unencrypted, by default. This prevents
|
168
|
+
# the user from tampering with the session but also allows him to see its contents.
|
169
|
+
#
|
170
|
+
# Do not put secret information in session!
|
171
|
+
#
|
172
|
+
# Other options for session storage are:
|
134
173
|
#
|
135
|
-
#
|
136
|
-
#
|
174
|
+
# ActiveRecordStore: sessions are stored in your database, which works better than PStore with multiple app servers and,
|
175
|
+
# unlike CookieStore, hides your session contents from the user. To use ActiveRecordStore, set
|
137
176
|
#
|
138
177
|
# config.action_controller.session_store = :active_record_store
|
139
178
|
#
|
140
179
|
# in your <tt>environment.rb</tt> and run <tt>rake db:sessions:create</tt>.
|
141
180
|
#
|
181
|
+
# MemCacheStore: sessions are stored as entries in your memcached cache. Set the session store type in <tt>environment.rb</tt>:
|
182
|
+
#
|
183
|
+
# config.action_controller.session_store = :mem_cache_store
|
184
|
+
#
|
185
|
+
# This assumes that memcached has been installed and configured properly. See the MemCacheStore docs for more information.
|
186
|
+
#
|
142
187
|
# == Responses
|
143
188
|
#
|
144
189
|
# Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
|
@@ -192,7 +237,7 @@ module ActionController #:nodoc:
|
|
192
237
|
#
|
193
238
|
# == Calling multiple redirects or renders
|
194
239
|
#
|
195
|
-
# An action
|
240
|
+
# An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
|
196
241
|
#
|
197
242
|
# def do_something
|
198
243
|
# redirect_to :action => "elsewhere"
|
@@ -209,7 +254,6 @@ module ActionController #:nodoc:
|
|
209
254
|
class Base
|
210
255
|
DEFAULT_RENDER_STATUS_CODE = "200 OK"
|
211
256
|
|
212
|
-
include Reloadable::Deprecated
|
213
257
|
include StatusCodes
|
214
258
|
|
215
259
|
# Determines whether the view has access to controller internals @request, @response, @session, and @template.
|
@@ -249,7 +293,7 @@ module ActionController #:nodoc:
|
|
249
293
|
# The param_parsers hash lets you register handlers which will process the http body and add parameters to the
|
250
294
|
# <tt>params</tt> hash. These handlers are invoked for post and put requests.
|
251
295
|
#
|
252
|
-
# By default application/xml is enabled. A XmlSimple class with the same param name as the root will be
|
296
|
+
# By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instantiated
|
253
297
|
# in the <tt>params</tt>. This allows XML requests to mask themselves as regular form submissions, so you can have one
|
254
298
|
# action serve both regular forms and web service requests.
|
255
299
|
#
|
@@ -271,17 +315,15 @@ module ActionController #:nodoc:
|
|
271
315
|
# A YAML parser is also available and can be turned on with:
|
272
316
|
#
|
273
317
|
# ActionController::Base.param_parsers[Mime::YAML] = :yaml
|
274
|
-
@@param_parsers = { Mime::
|
318
|
+
@@param_parsers = { Mime::MULTIPART_FORM => :multipart_form,
|
319
|
+
Mime::URL_ENCODED_FORM => :url_encoded_form,
|
320
|
+
Mime::XML => :xml_simple }
|
275
321
|
cattr_accessor :param_parsers
|
276
322
|
|
277
323
|
# Controls the default charset for all renders.
|
278
324
|
@@default_charset = "utf-8"
|
279
325
|
cattr_accessor :default_charset
|
280
|
-
|
281
|
-
# Template root determines the base from which template references will be made. So a call to render("test/template")
|
282
|
-
# will be converted to "#{template_root}/test/template.rhtml".
|
283
|
-
class_inheritable_accessor :template_root
|
284
|
-
|
326
|
+
|
285
327
|
# The logger is used for generating information on the action run-time (including benchmarking) if available.
|
286
328
|
# Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
|
287
329
|
cattr_accessor :logger
|
@@ -295,6 +337,18 @@ module ActionController #:nodoc:
|
|
295
337
|
# Controls the resource action separator
|
296
338
|
@@resource_action_separator = "/"
|
297
339
|
cattr_accessor :resource_action_separator
|
340
|
+
|
341
|
+
# Sets the token parameter name for RequestForgery. Calling #protect_from_forgery sets it to :authenticity_token by default
|
342
|
+
cattr_accessor :request_forgery_protection_token
|
343
|
+
|
344
|
+
# Indicates whether or not optimise the generated named
|
345
|
+
# route helper methods
|
346
|
+
cattr_accessor :optimise_named_routes
|
347
|
+
self.optimise_named_routes = true
|
348
|
+
|
349
|
+
# Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
|
350
|
+
class_inheritable_accessor :allow_forgery_protection
|
351
|
+
self.allow_forgery_protection = true
|
298
352
|
|
299
353
|
# Holds the request object that's primarily used to get environment variables through access like
|
300
354
|
# <tt>request.env["REQUEST_URI"]</tt>.
|
@@ -353,19 +407,57 @@ module ActionController #:nodoc:
|
|
353
407
|
# By default, all methods defined in ActionController::Base and included modules are hidden.
|
354
408
|
# More methods can be hidden using <tt>hide_actions</tt>.
|
355
409
|
def hidden_actions
|
356
|
-
|
410
|
+
unless read_inheritable_attribute(:hidden_actions)
|
411
|
+
write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods.map(&:to_s))
|
412
|
+
end
|
413
|
+
|
357
414
|
read_inheritable_attribute(:hidden_actions)
|
358
415
|
end
|
359
416
|
|
360
417
|
# Hide each of the given methods from being callable as actions.
|
361
418
|
def hide_action(*names)
|
362
|
-
write_inheritable_attribute(:hidden_actions, hidden_actions | names.
|
419
|
+
write_inheritable_attribute(:hidden_actions, hidden_actions | names.map(&:to_s))
|
420
|
+
end
|
421
|
+
|
422
|
+
## View load paths determine the bases from which template references can be made. So a call to
|
423
|
+
## render("test/template") will be looked up in the view load paths array and the closest match will be
|
424
|
+
## returned.
|
425
|
+
def view_paths
|
426
|
+
@view_paths || superclass.view_paths
|
427
|
+
end
|
428
|
+
|
429
|
+
def view_paths=(value)
|
430
|
+
@view_paths = value
|
363
431
|
end
|
364
432
|
|
365
|
-
#
|
366
|
-
#
|
433
|
+
# Adds a view_path to the front of the view_paths array.
|
434
|
+
# If the current class has no view paths, copy them from
|
435
|
+
# the superclass. This change will be visible for all future requests.
|
436
|
+
#
|
437
|
+
# ArticleController.prepend_view_path("views/default")
|
438
|
+
# ArticleController.prepend_view_path(["views/default", "views/custom"])
|
439
|
+
#
|
440
|
+
def prepend_view_path(path)
|
441
|
+
@view_paths = superclass.view_paths.dup if @view_paths.nil?
|
442
|
+
view_paths.unshift(*path)
|
443
|
+
end
|
444
|
+
|
445
|
+
# Adds a view_path to the end of the view_paths array.
|
446
|
+
# If the current class has no view paths, copy them from
|
447
|
+
# the superclass. This change will be visible for all future requests.
|
448
|
+
#
|
449
|
+
# ArticleController.append_view_path("views/default")
|
450
|
+
# ArticleController.append_view_path(["views/default", "views/custom"])
|
451
|
+
#
|
452
|
+
def append_view_path(path)
|
453
|
+
@view_paths = superclass.view_paths.dup if @view_paths.nil?
|
454
|
+
view_paths.push(*path)
|
455
|
+
end
|
456
|
+
|
457
|
+
# Replace sensitive parameter data from the request log.
|
458
|
+
# Filters parameters that have any of the arguments as a substring.
|
367
459
|
# Looks in all subhashes of the param hash for keys to filter.
|
368
|
-
# If a block is given, each key and value of the
|
460
|
+
# If a block is given, each key and value of the parameter hash and all
|
369
461
|
# subhashes is passed to it, the value or key
|
370
462
|
# can be replaced using String#replace or similar method.
|
371
463
|
#
|
@@ -412,13 +504,10 @@ module ActionController #:nodoc:
|
|
412
504
|
|
413
505
|
# Don't render layouts for templates with the given extensions.
|
414
506
|
def exempt_from_layout(*extensions)
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
/\.#{Regexp.escape(extension.to_s)}$/
|
420
|
-
end
|
421
|
-
}
|
507
|
+
regexps = extensions.collect do |extension|
|
508
|
+
extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
|
509
|
+
end
|
510
|
+
@@exempt_from_layout.merge regexps
|
422
511
|
end
|
423
512
|
end
|
424
513
|
|
@@ -435,6 +524,9 @@ module ActionController #:nodoc:
|
|
435
524
|
send(method, *arguments)
|
436
525
|
|
437
526
|
assign_default_content_type_and_charset
|
527
|
+
|
528
|
+
response.request = request
|
529
|
+
response.prepare! unless component_request?
|
438
530
|
response
|
439
531
|
ensure
|
440
532
|
process_cleanup
|
@@ -452,18 +544,25 @@ module ActionController #:nodoc:
|
|
452
544
|
# * <tt>:only_path</tt> -- if true, returns the relative URL (omitting the protocol, host name, and port) (<tt>false</tt> by default)
|
453
545
|
# * <tt>:trailing_slash</tt> -- if true, adds a trailing slash, as in "/archive/2005/". Note that this
|
454
546
|
# is currently not recommended since it breaks caching.
|
455
|
-
# * <tt>:host</tt> -- overrides the default (current) host if provided
|
456
|
-
# * <tt>:protocol</tt> -- overrides the default (current) protocol if provided
|
547
|
+
# * <tt>:host</tt> -- overrides the default (current) host if provided.
|
548
|
+
# * <tt>:protocol</tt> -- overrides the default (current) protocol if provided.
|
549
|
+
# * <tt>:port</tt> -- optionally specify the port to connect to.
|
550
|
+
# * <tt>:user</tt> -- Inline HTTP authentication (only plucked out if :password is also present).
|
551
|
+
# * <tt>:password</tt> -- Inline HTTP authentication (only plucked out if :user is also present).
|
552
|
+
# * <tt>:skip_relative_url_root</tt> -- if true, the url is not constructed using the relative_url_root of the request so the path
|
553
|
+
# will include the web server relative installation directory.
|
457
554
|
#
|
458
555
|
# The URL is generated from the remaining keys in the hash. A URL contains two key parts: the <base> and a query string.
|
459
556
|
# Routes composes a query string as the key/value pairs not included in the <base>.
|
460
557
|
#
|
461
558
|
# The default Routes setup supports a typical Rails path of "controller/action/id" where action and id are optional, with
|
462
559
|
# action defaulting to 'index' when not given. Here are some typical url_for statements and their corresponding URLs:
|
463
|
-
#
|
464
|
-
# url_for :controller => 'posts', :action => 'recent'
|
465
|
-
# url_for :controller => 'posts', :action => 'index'
|
466
|
-
# url_for :controller => 'posts', :action => '
|
560
|
+
#
|
561
|
+
# url_for :controller => 'posts', :action => 'recent' # => 'proto://host.com/posts/recent'
|
562
|
+
# url_for :controller => 'posts', :action => 'index' # => 'proto://host.com/posts'
|
563
|
+
# url_for :controller => 'posts', :action => 'index', :port=>'8033' # => 'proto://host.com:8033/posts'
|
564
|
+
# url_for :controller => 'posts', :action => 'show', :id => 10 # => 'proto://host.com/posts/show/10'
|
565
|
+
# url_for :controller => 'posts', :user => 'd', :password => '123' # => 'proto://d:123@host.com/posts'
|
467
566
|
#
|
468
567
|
# When generating a new URL, missing values may be filled in from the current request's parameters. For example,
|
469
568
|
# <tt>url_for :action => 'some_action'</tt> will retain the current controller, as expected. This behavior extends to
|
@@ -491,9 +590,9 @@ module ActionController #:nodoc:
|
|
491
590
|
# However, you might ask why the action from the current request, 'contacts', isn't carried over into the new URL. The
|
492
591
|
# answer has to do with the order in which the parameters appear in the generated path. In a nutshell, since the
|
493
592
|
# value that appears in the slot for <tt>:first</tt> is not equal to default value for <tt>:first</tt> we stop using
|
494
|
-
# defaults. On
|
593
|
+
# defaults. On its own, this rule can account for much of the typical Rails URL behavior.
|
495
594
|
#
|
496
|
-
# Although a
|
595
|
+
# Although a convenience, defaults can occasionally get in your way. In some cases a default persists longer than desired.
|
497
596
|
# The default may be cleared by adding <tt>:name => nil</tt> to <tt>url_for</tt>'s options.
|
498
597
|
# This is often required when writing form helpers, since the defaults in play may vary greatly depending upon where the
|
499
598
|
# helper is used from. The following line will redirect to PostController's default action, regardless of the page it is
|
@@ -509,22 +608,14 @@ module ActionController #:nodoc:
|
|
509
608
|
#
|
510
609
|
# This takes the current URL as is and only exchanges the action. In contrast, <tt>url_for :action => 'print'</tt>
|
511
610
|
# would have slashed-off the path components after the changed action.
|
512
|
-
def url_for(options =
|
513
|
-
case options
|
611
|
+
def url_for(options = nil) #:doc:
|
612
|
+
case options || {}
|
514
613
|
when String
|
515
614
|
options
|
516
|
-
|
517
|
-
when Symbol
|
518
|
-
ActiveSupport::Deprecation.warn(
|
519
|
-
"You called url_for(:#{options}), which is a deprecated API call. Instead you should use the named " +
|
520
|
-
"route directly, like #{options}(). Using symbols and parameters with url_for will be removed from Rails 2.0.",
|
521
|
-
caller
|
522
|
-
)
|
523
|
-
|
524
|
-
send(options, *parameters_for_method_reference)
|
525
|
-
|
526
615
|
when Hash
|
527
616
|
@url.rewrite(rewrite_options(options))
|
617
|
+
else
|
618
|
+
polymorphic_url(options)
|
528
619
|
end
|
529
620
|
end
|
530
621
|
|
@@ -543,11 +634,41 @@ module ActionController #:nodoc:
|
|
543
634
|
self.class.controller_path
|
544
635
|
end
|
545
636
|
|
546
|
-
# Test whether the session is enabled for this request.
|
547
637
|
def session_enabled?
|
548
638
|
request.session_options && request.session_options[:disabled] != false
|
549
639
|
end
|
550
640
|
|
641
|
+
self.view_paths = []
|
642
|
+
|
643
|
+
# View load paths for controller.
|
644
|
+
def view_paths
|
645
|
+
(@template || self.class).view_paths
|
646
|
+
end
|
647
|
+
|
648
|
+
def view_paths=(value)
|
649
|
+
(@template || self.class).view_paths = value
|
650
|
+
end
|
651
|
+
|
652
|
+
# Adds a view_path to the front of the view_paths array.
|
653
|
+
# This change affects the current request only.
|
654
|
+
#
|
655
|
+
# self.prepend_view_path("views/default")
|
656
|
+
# self.prepend_view_path(["views/default", "views/custom"])
|
657
|
+
#
|
658
|
+
def prepend_view_path(path)
|
659
|
+
(@template || self.class).prepend_view_path(path)
|
660
|
+
end
|
661
|
+
|
662
|
+
# Adds a view_path to the end of the view_paths array.
|
663
|
+
# This change affects the current request only.
|
664
|
+
#
|
665
|
+
# self.append_view_path("views/default")
|
666
|
+
# self.append_view_path(["views/default", "views/custom"])
|
667
|
+
#
|
668
|
+
def append_view_path(path)
|
669
|
+
(@template || self.class).append_view_path(path)
|
670
|
+
end
|
671
|
+
|
551
672
|
protected
|
552
673
|
# Renders the content that will be returned to the browser as the response body.
|
553
674
|
#
|
@@ -567,10 +688,6 @@ module ActionController #:nodoc:
|
|
567
688
|
# # but with a custom layout
|
568
689
|
# render :action => "long_goal", :layout => "spectacular"
|
569
690
|
#
|
570
|
-
# _Deprecation_ _notice_: This used to have the signatures <tt>render_action("action", status = 200)</tt>,
|
571
|
-
# <tt>render_without_layout("controller/action", status = 200)</tt>, and
|
572
|
-
# <tt>render_with_layout("controller/action", status = 200, layout)</tt>.
|
573
|
-
#
|
574
691
|
# === Rendering partials
|
575
692
|
#
|
576
693
|
# Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page
|
@@ -581,6 +698,10 @@ module ActionController #:nodoc:
|
|
581
698
|
# # Renders the same partial with a local variable.
|
582
699
|
# render :partial => "person", :locals => { :name => "david" }
|
583
700
|
#
|
701
|
+
# # Renders the partial, making @new_person available through
|
702
|
+
# # the local variable 'person'
|
703
|
+
# render :partial => "person", :object => @new_person
|
704
|
+
#
|
584
705
|
# # Renders a collection of the same partial by making each element
|
585
706
|
# # of @winners available through the local variable "person" as it
|
586
707
|
# # builds the complete response.
|
@@ -602,16 +723,19 @@ module ActionController #:nodoc:
|
|
602
723
|
# Note that the partial filename must also be a valid Ruby variable name,
|
603
724
|
# so e.g. 2005 and register-user are invalid.
|
604
725
|
#
|
605
|
-
#
|
606
|
-
#
|
607
|
-
#
|
726
|
+
#
|
727
|
+
# == Automatic etagging
|
728
|
+
#
|
729
|
+
# Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the
|
730
|
+
# response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified
|
731
|
+
# and the response body will be set to an empty string. No etag header will be inserted if it's already set.
|
608
732
|
#
|
609
733
|
# === Rendering a template
|
610
734
|
#
|
611
735
|
# Template rendering works just like action rendering except that it takes a path relative to the template root.
|
612
736
|
# The current layout is automatically applied.
|
613
737
|
#
|
614
|
-
# # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.
|
738
|
+
# # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
|
615
739
|
# render :template => "weblog/show"
|
616
740
|
#
|
617
741
|
# === Rendering a file
|
@@ -620,18 +744,16 @@ module ActionController #:nodoc:
|
|
620
744
|
# is assumed to be absolute, and the current layout is not applied.
|
621
745
|
#
|
622
746
|
# # Renders the template located at the absolute filesystem path
|
623
|
-
# render :file => "/path/to/some/template.
|
624
|
-
# render :file => "c:/path/to/some/template.
|
747
|
+
# render :file => "/path/to/some/template.erb"
|
748
|
+
# render :file => "c:/path/to/some/template.erb"
|
625
749
|
#
|
626
750
|
# # Renders a template within the current layout, and with a 404 status code
|
627
|
-
# render :file => "/path/to/some/template.
|
628
|
-
# render :file => "c:/path/to/some/template.
|
751
|
+
# render :file => "/path/to/some/template.erb", :layout => true, :status => 404
|
752
|
+
# render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404
|
629
753
|
#
|
630
754
|
# # Renders a template relative to the template root and chooses the proper file extension
|
631
755
|
# render :file => "some/template", :use_full_path => true
|
632
756
|
#
|
633
|
-
# _Deprecation_ _notice_: This used to have the signature <tt>render_file(path, status = 200)</tt>
|
634
|
-
#
|
635
757
|
# === Rendering text
|
636
758
|
#
|
637
759
|
# Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
|
@@ -644,11 +766,11 @@ module ActionController #:nodoc:
|
|
644
766
|
# render :text => "Explosion!", :status => 500
|
645
767
|
#
|
646
768
|
# # Renders the clear text "Hi there!" within the current active layout (if one exists)
|
647
|
-
# render :text => "
|
769
|
+
# render :text => "Hi there!", :layout => true
|
648
770
|
#
|
649
771
|
# # Renders the clear text "Hi there!" within the layout
|
650
772
|
# # placed in "app/views/layouts/special.r(html|xml)"
|
651
|
-
# render :text => "
|
773
|
+
# render :text => "Hi there!", :layout => "special"
|
652
774
|
#
|
653
775
|
# The :text option can also accept a Proc object, which can be used to manually control the page generation. This should
|
654
776
|
# generally be avoided, as it violates the separation between code and content, and because almost everything that can be
|
@@ -657,20 +779,24 @@ module ActionController #:nodoc:
|
|
657
779
|
# # Renders "Hello from code!"
|
658
780
|
# render :text => proc { |response, output| output.write("Hello from code!") }
|
659
781
|
#
|
660
|
-
# _Deprecation_ _notice_: This used to have the signature <tt>render_text("text", status = 200)</tt>
|
661
|
-
#
|
662
782
|
# === Rendering JSON
|
663
783
|
#
|
664
|
-
# Rendering JSON sets the content type to
|
665
|
-
# that the response will be eval'd for use as a data structure.
|
784
|
+
# Rendering JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected
|
785
|
+
# that the response will be parsed (or eval'd) for use as a data structure.
|
666
786
|
#
|
667
|
-
# # Renders '{name: "David"}'
|
787
|
+
# # Renders '{"name": "David"}'
|
668
788
|
# render :json => {:name => "David"}.to_json
|
669
789
|
#
|
790
|
+
# It's not necessary to call <tt>to_json</tt> on the object you want to render, since <tt>render</tt> will
|
791
|
+
# automatically do that for you:
|
792
|
+
#
|
793
|
+
# # Also renders '{"name": "David"}'
|
794
|
+
# render :json => {:name => "David"}
|
795
|
+
#
|
670
796
|
# Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag),
|
671
|
-
# so the callback option is provided for these cases.
|
797
|
+
# so the <tt>:callback</tt> option is provided for these cases.
|
672
798
|
#
|
673
|
-
# # Renders 'show({name: "David"})'
|
799
|
+
# # Renders 'show({"name": "David"})'
|
674
800
|
# render :json => {:name => "David"}.to_json, :callback => 'show'
|
675
801
|
#
|
676
802
|
# === Rendering an inline template
|
@@ -683,13 +809,11 @@ module ActionController #:nodoc:
|
|
683
809
|
# render :inline => "<%= 'hello, ' * 3 + 'again' %>"
|
684
810
|
#
|
685
811
|
# # Renders "<p>Good seeing you!</p>" using Builder
|
686
|
-
# render :inline => "xml.p { 'Good seeing you!' }", :type => :
|
812
|
+
# render :inline => "xml.p { 'Good seeing you!' }", :type => :builder
|
687
813
|
#
|
688
814
|
# # Renders "hello david"
|
689
815
|
# render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" }
|
690
816
|
#
|
691
|
-
# _Deprecation_ _notice_: This used to have the signature <tt>render_template(template, status = 200, type = :rhtml)</tt>
|
692
|
-
#
|
693
817
|
# === Rendering inline JavaScriptGenerator page updates
|
694
818
|
#
|
695
819
|
# In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details),
|
@@ -700,35 +824,21 @@ module ActionController #:nodoc:
|
|
700
824
|
# page.visual_effect :highlight, 'user_list'
|
701
825
|
# end
|
702
826
|
#
|
703
|
-
# === Rendering
|
827
|
+
# === Rendering with status and location headers
|
704
828
|
#
|
705
|
-
#
|
706
|
-
# when you just want to communicate a status code. Due to a bug in Safari, nothing actually means a single space.
|
829
|
+
# All renders take the :status and :location options and turn them into headers. They can even be used together:
|
707
830
|
#
|
708
|
-
#
|
709
|
-
|
710
|
-
#
|
711
|
-
# # Renders an empty response with status code 401 (access denied)
|
712
|
-
# render :nothing => true, :status => 401
|
713
|
-
def render(options = nil, deprecated_status = nil, &block) #:doc:
|
831
|
+
# render :xml => post.to_xml, :status => :created, :location => post_url(post)
|
832
|
+
def render(options = nil, &block) #:doc:
|
714
833
|
raise DoubleRenderError, "Can only render or redirect once per action" if performed?
|
715
834
|
|
716
835
|
if options.nil?
|
717
|
-
return
|
836
|
+
return render_for_file(default_template_name, nil, true)
|
718
837
|
else
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
else
|
724
|
-
ActiveSupport::Deprecation.warn(
|
725
|
-
"You called render('#{options}'), which is a deprecated API call. Instead you use " +
|
726
|
-
"render :file => #{options}. Calling render with just a string will be removed from Rails 2.0.",
|
727
|
-
caller
|
728
|
-
)
|
729
|
-
|
730
|
-
return render_file(options, deprecated_status, true)
|
731
|
-
end
|
838
|
+
if options == :update
|
839
|
+
options = { :update => true }
|
840
|
+
elsif !options.is_a?(Hash)
|
841
|
+
raise RenderError, "You called render with invalid options : #{options}"
|
732
842
|
end
|
733
843
|
end
|
734
844
|
|
@@ -736,52 +846,72 @@ module ActionController #:nodoc:
|
|
736
846
|
response.content_type = content_type.to_s
|
737
847
|
end
|
738
848
|
|
849
|
+
if location = options[:location]
|
850
|
+
response.headers["Location"] = url_for(location)
|
851
|
+
end
|
852
|
+
|
739
853
|
if text = options[:text]
|
740
|
-
|
854
|
+
render_for_text(text, options[:status])
|
741
855
|
|
742
856
|
else
|
743
857
|
if file = options[:file]
|
744
|
-
|
858
|
+
render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})
|
745
859
|
|
746
860
|
elsif template = options[:template]
|
747
|
-
|
861
|
+
render_for_file(template, options[:status], true)
|
748
862
|
|
749
863
|
elsif inline = options[:inline]
|
750
|
-
|
864
|
+
add_variables_to_assigns
|
865
|
+
render_for_text(@template.render_template(options[:type] || :erb, inline, nil, options[:locals] || {}), options[:status])
|
751
866
|
|
752
867
|
elsif action_name = options[:action]
|
753
|
-
|
754
|
-
|
755
|
-
|
868
|
+
template = default_template_name(action_name.to_s)
|
869
|
+
if options[:layout] && !template_exempt_from_layout?(template)
|
870
|
+
render_with_a_layout(:file => template, :status => options[:status], :use_full_path => true, :layout => true)
|
871
|
+
else
|
872
|
+
render_with_no_layout(:file => template, :status => options[:status], :use_full_path => true)
|
873
|
+
end
|
756
874
|
|
757
875
|
elsif xml = options[:xml]
|
758
|
-
|
876
|
+
response.content_type = Mime::XML
|
877
|
+
render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
|
759
878
|
|
760
879
|
elsif json = options[:json]
|
761
|
-
|
880
|
+
json = json.to_json unless json.is_a?(String)
|
881
|
+
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
|
882
|
+
response.content_type = Mime::JSON
|
883
|
+
render_for_text(json, options[:status])
|
762
884
|
|
763
885
|
elsif partial = options[:partial]
|
764
886
|
partial = default_template_name if partial == true
|
887
|
+
add_variables_to_assigns
|
888
|
+
|
765
889
|
if collection = options[:collection]
|
766
|
-
|
890
|
+
render_for_text(
|
891
|
+
@template.send!(:render_partial_collection, partial, collection,
|
892
|
+
options[:spacer_template], options[:locals]), options[:status]
|
893
|
+
)
|
767
894
|
else
|
768
|
-
|
895
|
+
render_for_text(
|
896
|
+
@template.send!(:render_partial, partial,
|
897
|
+
ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals]), options[:status]
|
898
|
+
)
|
769
899
|
end
|
770
900
|
|
771
901
|
elsif options[:update]
|
772
902
|
add_variables_to_assigns
|
773
|
-
@template.send :evaluate_assigns
|
903
|
+
@template.send! :evaluate_assigns
|
774
904
|
|
775
905
|
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
|
776
|
-
|
906
|
+
response.content_type = Mime::JS
|
907
|
+
render_for_text(generator.to_s)
|
777
908
|
|
778
909
|
elsif options[:nothing]
|
779
910
|
# Safari doesn't pass the headers of the return if the response is zero length
|
780
|
-
|
911
|
+
render_for_text(" ", options[:status])
|
781
912
|
|
782
913
|
else
|
783
|
-
|
784
|
-
|
914
|
+
render_for_file(default_template_name, options[:status], true)
|
785
915
|
end
|
786
916
|
end
|
787
917
|
end
|
@@ -789,87 +919,13 @@ module ActionController #:nodoc:
|
|
789
919
|
# Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead
|
790
920
|
# of sending it as the response body to the browser.
|
791
921
|
def render_to_string(options = nil, &block) #:doc:
|
792
|
-
|
922
|
+
render(options, &block)
|
793
923
|
ensure
|
794
924
|
erase_render_results
|
795
925
|
forget_variables_added_to_assigns
|
796
926
|
reset_variables_added_to_assigns
|
797
927
|
end
|
798
928
|
|
799
|
-
def render_action(action_name, status = nil, with_layout = true) #:nodoc:
|
800
|
-
template = default_template_name(action_name.to_s)
|
801
|
-
if with_layout && !template_exempt_from_layout?(template)
|
802
|
-
render_with_layout(:file => template, :status => status, :use_full_path => true, :layout => true)
|
803
|
-
else
|
804
|
-
render_without_layout(:file => template, :status => status, :use_full_path => true)
|
805
|
-
end
|
806
|
-
end
|
807
|
-
|
808
|
-
def render_file(template_path, status = nil, use_full_path = false, locals = {}) #:nodoc:
|
809
|
-
add_variables_to_assigns
|
810
|
-
assert_existence_of_template_file(template_path) if use_full_path
|
811
|
-
logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
|
812
|
-
render_text(@template.render_file(template_path, use_full_path, locals), status)
|
813
|
-
end
|
814
|
-
|
815
|
-
def render_template(template, status = nil, type = :rhtml, local_assigns = {}) #:nodoc:
|
816
|
-
add_variables_to_assigns
|
817
|
-
render_text(@template.render_template(type, template, nil, local_assigns), status)
|
818
|
-
end
|
819
|
-
|
820
|
-
def render_text(text = nil, status = nil, append_response = false) #:nodoc:
|
821
|
-
@performed_render = true
|
822
|
-
|
823
|
-
response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
|
824
|
-
|
825
|
-
if append_response
|
826
|
-
response.body ||= ''
|
827
|
-
response.body << text
|
828
|
-
else
|
829
|
-
response.body = text
|
830
|
-
end
|
831
|
-
end
|
832
|
-
|
833
|
-
def render_javascript(javascript, status = nil, append_response = true) #:nodoc:
|
834
|
-
response.content_type = Mime::JS
|
835
|
-
render_text(javascript, status, append_response)
|
836
|
-
end
|
837
|
-
|
838
|
-
def render_xml(xml, status = nil) #:nodoc:
|
839
|
-
response.content_type = Mime::XML
|
840
|
-
render_text(xml, status)
|
841
|
-
end
|
842
|
-
|
843
|
-
def render_json(json, callback = nil, status = nil) #:nodoc:
|
844
|
-
json = "#{callback}(#{json})" unless callback.blank?
|
845
|
-
|
846
|
-
response.content_type = Mime::JSON
|
847
|
-
render_text(json, status)
|
848
|
-
end
|
849
|
-
|
850
|
-
def render_nothing(status = nil) #:nodoc:
|
851
|
-
render_text(' ', status)
|
852
|
-
end
|
853
|
-
|
854
|
-
def render_partial(partial_path = default_template_name, object = nil, local_assigns = nil, status = nil) #:nodoc:
|
855
|
-
add_variables_to_assigns
|
856
|
-
render_text(@template.render_partial(partial_path, object, local_assigns), status)
|
857
|
-
end
|
858
|
-
|
859
|
-
def render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = nil, status = nil) #:nodoc:
|
860
|
-
add_variables_to_assigns
|
861
|
-
render_text(@template.render_partial_collection(partial_name, collection, partial_spacer_template, local_assigns), status)
|
862
|
-
end
|
863
|
-
|
864
|
-
def render_with_layout(template_name = default_template_name, status = nil, layout = nil) #:nodoc:
|
865
|
-
render_with_a_layout(template_name, status, layout)
|
866
|
-
end
|
867
|
-
|
868
|
-
def render_without_layout(template_name = default_template_name, status = nil) #:nodoc:
|
869
|
-
render_with_no_layout(template_name, status)
|
870
|
-
end
|
871
|
-
|
872
|
-
|
873
929
|
# Return a response that has no content (merely headers). The options
|
874
930
|
# argument is interpreted to be a hash of header names and values.
|
875
931
|
# This allows you to easily return a response that consists only of
|
@@ -956,47 +1012,62 @@ module ActionController #:nodoc:
|
|
956
1012
|
|
957
1013
|
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
|
958
1014
|
#
|
959
|
-
# * <tt>Hash</tt
|
960
|
-
# * <tt>
|
961
|
-
# * <tt>String
|
962
|
-
# * <tt
|
1015
|
+
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
|
1016
|
+
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
|
1017
|
+
# * <tt>String starting with protocol:// (like http://)</tt> - Is passed straight through as the target for redirection.
|
1018
|
+
# * <tt>String not containing a protocol</tt> - The current protocol and host is prepended to the string.
|
1019
|
+
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
|
963
1020
|
# Short-hand for redirect_to(request.env["HTTP_REFERER"])
|
964
1021
|
#
|
965
1022
|
# Examples:
|
966
1023
|
# redirect_to :action => "show", :id => 5
|
1024
|
+
# redirect_to post
|
967
1025
|
# redirect_to "http://www.rubyonrails.org"
|
968
1026
|
# redirect_to "/images/screenshot.jpg"
|
1027
|
+
# redirect_to articles_url
|
969
1028
|
# redirect_to :back
|
970
1029
|
#
|
971
|
-
# The redirection happens as a "302 Moved" header.
|
1030
|
+
# The redirection happens as a "302 Moved" header unless otherwise specified.
|
1031
|
+
#
|
1032
|
+
# Examples:
|
1033
|
+
# redirect_to post_url(@post), :status=>:found
|
1034
|
+
# redirect_to :action=>'atom', :status=>:moved_permanently
|
1035
|
+
# redirect_to post_url(@post), :status=>301
|
1036
|
+
# redirect_to :action=>'atom', :status=>302
|
972
1037
|
#
|
973
1038
|
# When using <tt>redirect_to :back</tt>, if there is no referrer,
|
974
1039
|
# RedirectBackError will be raised. You may specify some fallback
|
975
|
-
# behavior for this case by
|
976
|
-
def redirect_to(options = {},
|
1040
|
+
# behavior for this case by rescuing RedirectBackError.
|
1041
|
+
def redirect_to(options = {}, response_status = {}) #:doc:
|
1042
|
+
|
1043
|
+
if options.is_a?(Hash) && options[:status]
|
1044
|
+
status = options.delete(:status)
|
1045
|
+
elsif response_status[:status]
|
1046
|
+
status = response_status[:status]
|
1047
|
+
else
|
1048
|
+
status = 302
|
1049
|
+
end
|
1050
|
+
|
977
1051
|
case options
|
978
1052
|
when %r{^\w+://.*}
|
979
1053
|
raise DoubleRenderError if performed?
|
980
|
-
logger.info("Redirected to #{options}") if logger
|
981
|
-
response.redirect(options)
|
1054
|
+
logger.info("Redirected to #{options}") if logger && logger.info?
|
1055
|
+
response.redirect(options, interpret_status(status))
|
982
1056
|
response.redirected_to = options
|
983
1057
|
@performed_redirect = true
|
984
1058
|
|
985
1059
|
when String
|
986
|
-
redirect_to(request.protocol + request.host_with_port + options)
|
1060
|
+
redirect_to(request.protocol + request.host_with_port + options, :status=>status)
|
987
1061
|
|
988
1062
|
when :back
|
989
|
-
request.env["HTTP_REFERER"] ? redirect_to(request.env["HTTP_REFERER"]) : raise(RedirectBackError)
|
1063
|
+
request.env["HTTP_REFERER"] ? redirect_to(request.env["HTTP_REFERER"], :status=>status) : raise(RedirectBackError)
|
1064
|
+
|
1065
|
+
when Hash
|
1066
|
+
redirect_to(url_for(options), :status=>status)
|
1067
|
+
response.redirected_to = options
|
990
1068
|
|
991
1069
|
else
|
992
|
-
|
993
|
-
redirect_to(url_for(options))
|
994
|
-
response.redirected_to = options
|
995
|
-
else
|
996
|
-
# TOOD: Deprecate me!
|
997
|
-
redirect_to(url_for(options, *parameters_for_method_reference))
|
998
|
-
response.redirected_to, response.redirected_to_method_params = options, parameters_for_method_reference
|
999
|
-
end
|
1070
|
+
redirect_to(url_for(options), :status=>status)
|
1000
1071
|
end
|
1001
1072
|
end
|
1002
1073
|
|
@@ -1030,23 +1101,35 @@ module ActionController #:nodoc:
|
|
1030
1101
|
response.session = @_session
|
1031
1102
|
end
|
1032
1103
|
|
1104
|
+
|
1033
1105
|
private
|
1034
|
-
def
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
end
|
1106
|
+
def render_for_file(template_path, status = nil, use_full_path = false, locals = {}) #:nodoc:
|
1107
|
+
add_variables_to_assigns
|
1108
|
+
assert_existence_of_template_file(template_path) if use_full_path
|
1109
|
+
logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
|
1110
|
+
render_for_text(@template.render_file(template_path, use_full_path, locals), status)
|
1040
1111
|
end
|
1041
1112
|
|
1042
|
-
def
|
1043
|
-
@
|
1044
|
-
end
|
1113
|
+
def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
|
1114
|
+
@performed_render = true
|
1045
1115
|
|
1116
|
+
response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
|
1117
|
+
|
1118
|
+
if append_response
|
1119
|
+
response.body ||= ''
|
1120
|
+
response.body << text.to_s
|
1121
|
+
else
|
1122
|
+
response.body = text.is_a?(Proc) ? text : text.to_s
|
1123
|
+
end
|
1124
|
+
end
|
1125
|
+
|
1046
1126
|
def initialize_template_class(response)
|
1047
|
-
|
1127
|
+
unless @@template_class
|
1128
|
+
raise "You must assign a template class through ActionController.template_class= before processing a request"
|
1129
|
+
end
|
1048
1130
|
|
1049
|
-
response.template =
|
1131
|
+
response.template = ActionView::Base.new(view_paths, {}, self)
|
1132
|
+
response.template.extend self.class.master_helper_module
|
1050
1133
|
response.redirected_to = nil
|
1051
1134
|
@performed_render = @performed_redirect = false
|
1052
1135
|
end
|
@@ -1062,26 +1145,6 @@ module ActionController #:nodoc:
|
|
1062
1145
|
@assigns = @_response.template.assigns
|
1063
1146
|
|
1064
1147
|
@_headers = @_response.headers
|
1065
|
-
|
1066
|
-
assign_deprecated_shortcuts(request, response)
|
1067
|
-
end
|
1068
|
-
|
1069
|
-
|
1070
|
-
# TODO: assigns cookies headers params request response template
|
1071
|
-
DEPRECATED_INSTANCE_VARIABLES = %w(cookies flash headers params request response session)
|
1072
|
-
|
1073
|
-
# Gone after 1.2.
|
1074
|
-
def assign_deprecated_shortcuts(request, response)
|
1075
|
-
DEPRECATED_INSTANCE_VARIABLES.each do |method|
|
1076
|
-
var = "@#{method}"
|
1077
|
-
if instance_variables.include?(var)
|
1078
|
-
value = instance_variable_get(var)
|
1079
|
-
unless ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy === value
|
1080
|
-
raise "Deprecating #{var}, but it's already set to #{value.inspect}! Use the #{method}= writer method instead of setting #{var} directly."
|
1081
|
-
end
|
1082
|
-
end
|
1083
|
-
instance_variable_set var, ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, method)
|
1084
|
-
end
|
1085
1148
|
end
|
1086
1149
|
|
1087
1150
|
def initialize_current_url
|
@@ -1089,22 +1152,26 @@ module ActionController #:nodoc:
|
|
1089
1152
|
end
|
1090
1153
|
|
1091
1154
|
def log_processing
|
1092
|
-
if logger
|
1155
|
+
if logger && logger.info?
|
1093
1156
|
logger.info "\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
|
1094
1157
|
logger.info " Session ID: #{@_session.session_id}" if @_session and @_session.respond_to?(:session_id)
|
1095
1158
|
logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}"
|
1096
1159
|
end
|
1097
1160
|
end
|
1098
1161
|
|
1162
|
+
def default_render #:nodoc:
|
1163
|
+
render
|
1164
|
+
end
|
1165
|
+
|
1099
1166
|
def perform_action
|
1100
1167
|
if self.class.action_methods.include?(action_name)
|
1101
1168
|
send(action_name)
|
1102
|
-
|
1169
|
+
default_render unless performed?
|
1103
1170
|
elsif respond_to? :method_missing
|
1104
|
-
|
1105
|
-
|
1171
|
+
method_missing action_name
|
1172
|
+
default_render unless performed?
|
1106
1173
|
elsif template_exists? && template_public?
|
1107
|
-
|
1174
|
+
default_render
|
1108
1175
|
else
|
1109
1176
|
raise UnknownAction, "No action responded to #{action_name}", caller
|
1110
1177
|
end
|
@@ -1132,7 +1199,7 @@ module ActionController #:nodoc:
|
|
1132
1199
|
end
|
1133
1200
|
|
1134
1201
|
def self.action_methods
|
1135
|
-
@action_methods ||= Set.new(public_instance_methods - hidden_actions
|
1202
|
+
@action_methods ||= Set.new(public_instance_methods.map(&:to_s)) - hidden_actions
|
1136
1203
|
end
|
1137
1204
|
|
1138
1205
|
def add_variables_to_assigns
|
@@ -1160,7 +1227,7 @@ module ActionController #:nodoc:
|
|
1160
1227
|
end
|
1161
1228
|
|
1162
1229
|
def add_class_variables_to_assigns
|
1163
|
-
%w(
|
1230
|
+
%w(view_paths logger template_class ignore_missing_templates).each do |cvar|
|
1164
1231
|
@assigns[cvar] = self.send(cvar)
|
1165
1232
|
end
|
1166
1233
|
end
|
@@ -1199,16 +1266,17 @@ module ActionController #:nodoc:
|
|
1199
1266
|
end
|
1200
1267
|
|
1201
1268
|
def template_exempt_from_layout?(template_name = default_template_name)
|
1202
|
-
extension = @template.pick_template_extension(template_name)
|
1269
|
+
extension = @template && @template.pick_template_extension(template_name)
|
1203
1270
|
name_with_extension = !template_name.include?('.') && extension ? "#{template_name}.#{extension}" : template_name
|
1204
|
-
|
1271
|
+
@@exempt_from_layout.any? { |ext| name_with_extension =~ ext }
|
1205
1272
|
end
|
1206
1273
|
|
1207
1274
|
def assert_existence_of_template_file(template_name)
|
1208
1275
|
unless template_exists?(template_name) || ignore_missing_templates
|
1209
|
-
full_template_path =
|
1276
|
+
full_template_path = template_name.include?('.') ? template_name : "#{template_name}.#{@template.template_format}.erb"
|
1277
|
+
display_paths = view_paths.join(':')
|
1210
1278
|
template_type = (template_name =~ /layouts/i) ? 'layout' : 'template'
|
1211
|
-
raise(MissingTemplate, "Missing #{template_type} #{full_template_path}")
|
1279
|
+
raise(MissingTemplate, "Missing #{template_type} #{full_template_path} in view path #{display_paths}")
|
1212
1280
|
end
|
1213
1281
|
end
|
1214
1282
|
|