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
@@ -1,10 +1,14 @@
|
|
1
1
|
module ActionView
|
2
2
|
module Helpers
|
3
|
-
# Provides a set of methods for making it easier to
|
3
|
+
# Provides a set of methods for making it easier to debug Rails objects.
|
4
4
|
module DebugHelper
|
5
|
-
# Returns a <pre>-tag
|
5
|
+
# Returns a <pre>-tag that has +object+ dumped by YAML. This creates a very
|
6
|
+
# readable way to inspect an object.
|
7
|
+
#
|
8
|
+
# ==== Example
|
6
9
|
# my_hash = {'first' => 1, 'second' => 'two', 'third' => [1,2,3]}
|
7
10
|
# debug(my_hash)
|
11
|
+
#
|
8
12
|
# => <pre class='debug_dump'>---
|
9
13
|
# first: 1
|
10
14
|
# second: two
|
@@ -1,72 +1,67 @@
|
|
1
1
|
require 'cgi'
|
2
|
-
require
|
3
|
-
require
|
2
|
+
require 'action_view/helpers/date_helper'
|
3
|
+
require 'action_view/helpers/tag_helper'
|
4
4
|
|
5
5
|
module ActionView
|
6
6
|
module Helpers
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# Name:
|
12
|
-
# <%= text_field "person", "name", "size" => 20 %>
|
7
|
+
# Form helpers are designed to make working with models much easier compared to using just standard HTML
|
8
|
+
# elements by providing a set of methods for creating forms based on your models. This helper generates the HTML
|
9
|
+
# for forms, providing a method for each sort of input (e.g., text, password, select, and so on). When the form
|
10
|
+
# is submitted (i.e., when the user hits the submit button or <tt>form.submit</tt> is called via JavaScript), the form inputs will be bundled into the <tt>params</tt> object and passed back to the controller.
|
13
11
|
#
|
14
|
-
#
|
15
|
-
#
|
12
|
+
# There are two types of form helpers: those that specifically work with model attributes and those that don't.
|
13
|
+
# This helper deals with those that work with model attributes; to see an example of form helpers that don't work
|
14
|
+
# with model attributes, check the ActionView::Helpers::FormTagHelper documentation.
|
16
15
|
#
|
17
|
-
#
|
18
|
-
#
|
16
|
+
# The core method of this helper, form_for, gives you the ability to create a form for a model instance;
|
17
|
+
# for example, let's say that you have a model <tt>Person</tt> and want to create a new instance of it:
|
19
18
|
#
|
20
|
-
#
|
21
|
-
#
|
19
|
+
# # Note: a @person variable will have been created in the controller.
|
20
|
+
# # For example: @person = Person.new
|
21
|
+
# <% form_for :person, @person, :url => { :action => "create" } do |f| %>
|
22
|
+
# <%= f.text_field :first_name %>
|
23
|
+
# <%= f.text_field :last_name %>
|
24
|
+
# <%= submit_tag 'Create' %>
|
25
|
+
# <% end %>
|
22
26
|
#
|
23
|
-
#
|
24
|
-
# </form>
|
27
|
+
# The HTML generated for this would be:
|
25
28
|
#
|
26
|
-
#
|
29
|
+
# <form action="/persons/create" method="post">
|
30
|
+
# <input id="person_first_name" name="person[first_name]" size="30" type="text" />
|
31
|
+
# <input id="person_last_name" name="person[last_name]" size="30" type="text" />
|
32
|
+
# <input name="commit" type="submit" value="Create" />
|
33
|
+
# </form>
|
27
34
|
#
|
28
|
-
#
|
29
|
-
# Name:
|
30
|
-
# <input type="text" id="person_name" name="person[name]"
|
31
|
-
# size="20" value="<%= @person.name %>" />
|
35
|
+
# The <tt>params</tt> object created when this form is submitted would look like:
|
32
36
|
#
|
33
|
-
#
|
34
|
-
# <input type="password" id="person_password" name="person[password]"
|
35
|
-
# size="20" maxsize="20" value="<%= @person.password %>" />
|
37
|
+
# {"action"=>"create", "controller"=>"persons", "person"=>{"first_name"=>"William", "last_name"=>"Smith"}}
|
36
38
|
#
|
37
|
-
#
|
38
|
-
#
|
39
|
+
# The params hash has a nested <tt>person</tt> value, which can therefore be accessed with <tt>params[:person]</tt> in the controller.
|
40
|
+
# If were editing/updating an instance (e.g., <tt>Person.find(1)</tt> rather than <tt>Person.new</tt> in the controller), the objects
|
41
|
+
# attribute values are filled into the form (e.g., the <tt>person_first_name</tt> field would have that person's first name in it).
|
39
42
|
#
|
40
|
-
#
|
41
|
-
# <textarea cols="20" rows="40" id="person_description" name="person[description]">
|
42
|
-
# <%= @person.description %>
|
43
|
-
# </textarea>
|
44
|
-
#
|
45
|
-
# <input type="submit" value="Save">
|
46
|
-
# </form>
|
47
|
-
#
|
48
|
-
# If the object name contains square brackets the id for the object will be inserted. Example:
|
43
|
+
# If the object name contains square brackets the id for the object will be inserted. For example:
|
49
44
|
#
|
50
45
|
# <%= text_field "person[]", "name" %>
|
51
|
-
#
|
52
|
-
# ...
|
46
|
+
#
|
47
|
+
# ...will generate the following ERb.
|
53
48
|
#
|
54
49
|
# <input type="text" id="person_<%= @person.id %>_name" name="person[<%= @person.id %>][name]" value="<%= @person.name %>" />
|
55
50
|
#
|
56
51
|
# If the helper is being used to generate a repetitive sequence of similar form elements, for example in a partial
|
57
|
-
# used by render_collection_of_partials
|
52
|
+
# used by <tt>render_collection_of_partials</tt>, the <tt>index</tt> option may come in handy. Example:
|
58
53
|
#
|
59
54
|
# <%= text_field "person", "name", "index" => 1 %>
|
60
55
|
#
|
61
|
-
# becomes
|
56
|
+
# ...becomes...
|
62
57
|
#
|
63
58
|
# <input type="text" id="person_1_name" name="person[1][name]" value="<%= @person.name %>" />
|
64
59
|
#
|
65
|
-
# There
|
60
|
+
# There are also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html,
|
66
61
|
# link:classes/ActionView/Helpers/DateHelper.html, and link:classes/ActionView/Helpers/ActiveRecordHelper.html
|
67
62
|
module FormHelper
|
68
|
-
# Creates a form and a scope around a specific model object
|
69
|
-
# values for the fields.
|
63
|
+
# Creates a form and a scope around a specific model object that is used as a base for questioning about
|
64
|
+
# values for the fields.
|
70
65
|
#
|
71
66
|
# <% form_for :person, @person, :url => { :action => "update" } do |f| %>
|
72
67
|
# First name: <%= f.text_field :first_name %>
|
@@ -75,18 +70,18 @@ module ActionView
|
|
75
70
|
# Admin? : <%= f.check_box :admin %>
|
76
71
|
# <% end %>
|
77
72
|
#
|
78
|
-
# Worth noting is that the form_for tag is called in a ERb evaluation block, not
|
79
|
-
# not <tt><%= %></tt>. Also worth noting is that
|
73
|
+
# Worth noting is that the form_for tag is called in a ERb evaluation block, not an ERb output block. So that's <tt><% %></tt>,
|
74
|
+
# not <tt><%= %></tt>. Also worth noting is that form_for yields a <tt>form_builder</tt> object, in this example as <tt>f</tt>, which emulates
|
80
75
|
# the API for the stand-alone FormHelper methods, but without the object name. So instead of <tt>text_field :person, :name</tt>,
|
81
|
-
# you get away with <tt>f.text_field :name</tt>.
|
76
|
+
# you get away with <tt>f.text_field :name</tt>.
|
82
77
|
#
|
83
|
-
#
|
84
|
-
#
|
78
|
+
# Even further, the form_for method allows you to more easily escape the instance variable convention. So while the stand-alone
|
79
|
+
# approach would require <tt>text_field :person, :name, :object => person</tt>
|
85
80
|
# to work with local variables instead of instance ones, the form_for calls remain the same. You simply declare once with
|
86
81
|
# <tt>:person, person</tt> and all subsequent field calls save <tt>:person</tt> and <tt>:object => person</tt>.
|
87
82
|
#
|
88
83
|
# Also note that form_for doesn't create an exclusive scope. It's still possible to use both the stand-alone FormHelper methods
|
89
|
-
# and methods from FormTagHelper.
|
84
|
+
# and methods from FormTagHelper. For example:
|
90
85
|
#
|
91
86
|
# <% form_for :person, @person, :url => { :action => "update" } do |f| %>
|
92
87
|
# First name: <%= f.text_field :first_name %>
|
@@ -95,84 +90,260 @@ module ActionView
|
|
95
90
|
# Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %>
|
96
91
|
# <% end %>
|
97
92
|
#
|
98
|
-
# Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base
|
99
|
-
#
|
93
|
+
# Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base,
|
94
|
+
# like FormOptionHelper#collection_select and DateHelper#datetime_select.
|
95
|
+
#
|
96
|
+
# HTML attributes for the form tag can be given as :html => {...}. For example:
|
100
97
|
#
|
101
|
-
# Html attributes for the form tag can be given as :html => {...}. Example:
|
102
|
-
#
|
103
98
|
# <% form_for :person, @person, :html => {:id => 'person_form'} do |f| %>
|
104
99
|
# ...
|
105
100
|
# <% end %>
|
106
101
|
#
|
102
|
+
# The above form will then have the <tt>id</tt> attribute with the value </tt>person_form</tt>, which you can then
|
103
|
+
# style with CSS or manipulate with JavaScript.
|
104
|
+
#
|
105
|
+
# === Relying on record identification
|
106
|
+
#
|
107
|
+
# In addition to manually configuring the form_for call, you can also rely on record identification, which will use
|
108
|
+
# the conventions and named routes of that approach. Examples:
|
109
|
+
#
|
110
|
+
# <% form_for(@post) do |f| %>
|
111
|
+
# ...
|
112
|
+
# <% end %>
|
113
|
+
#
|
114
|
+
# This will expand to be the same as:
|
115
|
+
#
|
116
|
+
# <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
|
117
|
+
# ...
|
118
|
+
# <% end %>
|
119
|
+
#
|
120
|
+
# And for new records:
|
121
|
+
#
|
122
|
+
# <% form_for(Post.new) do |f| %>
|
123
|
+
# ...
|
124
|
+
# <% end %>
|
125
|
+
#
|
126
|
+
# This will expand to be the same as:
|
127
|
+
#
|
128
|
+
# <% form_for :post, @post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
|
129
|
+
# ...
|
130
|
+
# <% end %>
|
131
|
+
#
|
132
|
+
# You can also overwrite the individual conventions, like this:
|
133
|
+
#
|
134
|
+
# <% form_for(@post, :url => super_post_path(@post)) do |f| %>
|
135
|
+
# ...
|
136
|
+
# <% end %>
|
137
|
+
#
|
138
|
+
# And for namespaced routes, like admin_post_url:
|
139
|
+
#
|
140
|
+
# <% form_for([:admin, @post]) do |f| %>
|
141
|
+
# ...
|
142
|
+
# <% end %>
|
143
|
+
#
|
144
|
+
# === Customized form builders
|
145
|
+
#
|
107
146
|
# You can also build forms using a customized FormBuilder class. Subclass FormBuilder and override or define some more helpers,
|
108
|
-
# then use your custom builder
|
109
|
-
#
|
147
|
+
# then use your custom builder. For example, let's say you made a helper to automatically add labels to form inputs.
|
148
|
+
#
|
110
149
|
# <% form_for :person, @person, :url => { :action => "update" }, :builder => LabellingFormBuilder do |f| %>
|
111
150
|
# <%= f.text_field :first_name %>
|
112
151
|
# <%= f.text_field :last_name %>
|
113
152
|
# <%= text_area :person, :biography %>
|
114
153
|
# <%= check_box_tag "person[admin]", @person.company.admin? %>
|
115
154
|
# <% end %>
|
116
|
-
#
|
117
|
-
# In many cases you will want to wrap the above in another helper,
|
155
|
+
#
|
156
|
+
# In many cases you will want to wrap the above in another helper, so you could do something like the following:
|
118
157
|
#
|
119
158
|
# def labelled_form_for(name, object, options, &proc)
|
120
159
|
# form_for(name, object, options.merge(:builder => LabellingFormBuiler), &proc)
|
121
160
|
# end
|
122
161
|
#
|
123
|
-
|
162
|
+
# If you don't need to attach a form to a model instance, then check out FormTagHelper#form_tag.
|
163
|
+
def form_for(record_or_name_or_array, *args, &proc)
|
124
164
|
raise ArgumentError, "Missing block" unless block_given?
|
125
|
-
|
165
|
+
|
166
|
+
options = args.extract_options!
|
167
|
+
|
168
|
+
case record_or_name_or_array
|
169
|
+
when String, Symbol
|
170
|
+
object_name = record_or_name_or_array
|
171
|
+
when Array
|
172
|
+
object = record_or_name_or_array.last
|
173
|
+
object_name = ActionController::RecordIdentifier.singular_class_name(object)
|
174
|
+
apply_form_for_options!(record_or_name_or_array, options)
|
175
|
+
args.unshift object
|
176
|
+
else
|
177
|
+
object = record_or_name_or_array
|
178
|
+
object_name = ActionController::RecordIdentifier.singular_class_name(object)
|
179
|
+
apply_form_for_options!([object], options)
|
180
|
+
args.unshift object
|
181
|
+
end
|
182
|
+
|
126
183
|
concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}), proc.binding)
|
127
184
|
fields_for(object_name, *(args << options), &proc)
|
128
185
|
concat('</form>', proc.binding)
|
129
186
|
end
|
130
187
|
|
188
|
+
def apply_form_for_options!(object_or_array, options) #:nodoc:
|
189
|
+
object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array
|
190
|
+
|
191
|
+
html_options =
|
192
|
+
if object.respond_to?(:new_record?) && object.new_record?
|
193
|
+
{ :class => dom_class(object, :new), :id => dom_id(object), :method => :post }
|
194
|
+
else
|
195
|
+
{ :class => dom_class(object, :edit), :id => dom_id(object, :edit), :method => :put }
|
196
|
+
end
|
197
|
+
|
198
|
+
options[:html] ||= {}
|
199
|
+
options[:html].reverse_merge!(html_options)
|
200
|
+
options[:url] ||= polymorphic_path(object_or_array)
|
201
|
+
end
|
202
|
+
|
131
203
|
# Creates a scope around a specific model object like form_for, but doesn't create the form tags themselves. This makes
|
132
|
-
# fields_for suitable for specifying additional model objects in the same form
|
204
|
+
# fields_for suitable for specifying additional model objects in the same form:
|
133
205
|
#
|
134
|
-
#
|
206
|
+
# ==== Examples
|
207
|
+
# <% form_for @person, :url => { :action => "update" } do |person_form| %>
|
135
208
|
# First name: <%= person_form.text_field :first_name %>
|
136
209
|
# Last name : <%= person_form.text_field :last_name %>
|
137
|
-
#
|
138
|
-
# <% fields_for
|
210
|
+
#
|
211
|
+
# <% fields_for @person.permission do |permission_fields| %>
|
139
212
|
# Admin? : <%= permission_fields.check_box :admin %>
|
140
213
|
# <% end %>
|
141
214
|
# <% end %>
|
142
215
|
#
|
143
|
-
#
|
144
|
-
#
|
145
|
-
|
216
|
+
# ...or if you have an object that needs to be represented as a different parameter, like a Client that acts as a Person:
|
217
|
+
#
|
218
|
+
# <% fields_for :person, @client do |permission_fields| %>
|
219
|
+
# Admin?: <%= permission_fields.check_box :admin %>
|
220
|
+
# <% end %>
|
221
|
+
#
|
222
|
+
# ...or if you don't have an object, just a name of the parameter
|
223
|
+
#
|
224
|
+
# <% fields_for :person do |permission_fields| %>
|
225
|
+
# Admin?: <%= permission_fields.check_box :admin %>
|
226
|
+
# <% end %>
|
227
|
+
#
|
228
|
+
# Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base,
|
229
|
+
# like FormOptionHelper#collection_select and DateHelper#datetime_select.
|
230
|
+
def fields_for(record_or_name_or_array, *args, &block)
|
146
231
|
raise ArgumentError, "Missing block" unless block_given?
|
147
|
-
options = args.
|
148
|
-
|
232
|
+
options = args.extract_options!
|
233
|
+
|
234
|
+
case record_or_name_or_array
|
235
|
+
when String, Symbol
|
236
|
+
object_name = record_or_name_or_array
|
237
|
+
object = args.first
|
238
|
+
when Array
|
239
|
+
object = record_or_name_or_array.last
|
240
|
+
object_name = ActionController::RecordIdentifier.singular_class_name(object)
|
241
|
+
apply_form_for_options!(record_or_name_or_array, options)
|
242
|
+
else
|
243
|
+
object = record_or_name_or_array
|
244
|
+
object_name = ActionController::RecordIdentifier.singular_class_name(object)
|
245
|
+
end
|
149
246
|
|
150
247
|
builder = options[:builder] || ActionView::Base.default_form_builder
|
151
248
|
yield builder.new(object_name, object, self, options, block)
|
152
249
|
end
|
153
250
|
|
251
|
+
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
|
252
|
+
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify
|
253
|
+
# it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
|
254
|
+
# onto the HTML as an HTML element attribute as in the example shown.
|
255
|
+
#
|
256
|
+
# ==== Examples
|
257
|
+
# label(:post, :title)
|
258
|
+
# #=> <label for="post_title">Title</label>
|
259
|
+
#
|
260
|
+
# label(:post, :title, "A short title")
|
261
|
+
# #=> <label for="post_title">A short title</label>
|
262
|
+
#
|
263
|
+
# label(:post, :title, "A short title", :class => "title_label")
|
264
|
+
# #=> <label for="post_title" class="title_label">A short title</label>
|
265
|
+
#
|
266
|
+
def label(object_name, method, text = nil, options = {})
|
267
|
+
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_label_tag(text, options)
|
268
|
+
end
|
269
|
+
|
154
270
|
# Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
|
155
271
|
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
|
156
|
-
# hash with +options+.
|
272
|
+
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
|
273
|
+
# shown.
|
274
|
+
#
|
275
|
+
# ==== Examples
|
276
|
+
# text_field(:post, :title, :size => 20)
|
277
|
+
# # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
|
278
|
+
#
|
279
|
+
# text_field(:post, :title, :class => "create_input")
|
280
|
+
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
|
281
|
+
#
|
282
|
+
# text_field(:session, :user, :onchange => "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }")
|
283
|
+
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }"/>
|
284
|
+
#
|
285
|
+
# text_field(:snippet, :code, :size => 20, :class => 'code_input')
|
286
|
+
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
|
157
287
|
#
|
158
|
-
# Examples (call, result):
|
159
|
-
# text_field("post", "title", "size" => 20)
|
160
|
-
# <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
|
161
288
|
def text_field(object_name, method, options = {})
|
162
289
|
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("text", options)
|
163
290
|
end
|
164
291
|
|
165
|
-
#
|
292
|
+
# Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
|
293
|
+
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
|
294
|
+
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
|
295
|
+
# shown.
|
296
|
+
#
|
297
|
+
# ==== Examples
|
298
|
+
# password_field(:login, :pass, :size => 20)
|
299
|
+
# # => <input type="text" id="login_pass" name="login[pass]" size="20" value="#{@login.pass}" />
|
300
|
+
#
|
301
|
+
# password_field(:account, :secret, :class => "form_input")
|
302
|
+
# # => <input type="text" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
|
303
|
+
#
|
304
|
+
# password_field(:user, :password, :onchange => "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
|
305
|
+
# # => <input type="text" id="user_password" name="user[password]" value="#{@user.password}" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/>
|
306
|
+
#
|
307
|
+
# password_field(:account, :pin, :size => 20, :class => 'form_input')
|
308
|
+
# # => <input type="text" id="account_pin" name="account[pin]" size="20" value="#{@account.pin}" class="form_input" />
|
309
|
+
#
|
166
310
|
def password_field(object_name, method, options = {})
|
167
311
|
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("password", options)
|
168
312
|
end
|
169
313
|
|
170
|
-
#
|
314
|
+
# Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
|
315
|
+
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
|
316
|
+
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
|
317
|
+
# shown.
|
318
|
+
#
|
319
|
+
# ==== Examples
|
320
|
+
# hidden_field(:signup, :pass_confirm)
|
321
|
+
# # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
|
322
|
+
#
|
323
|
+
# hidden_field(:post, :tag_list)
|
324
|
+
# # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
|
325
|
+
#
|
326
|
+
# hidden_field(:user, :token)
|
327
|
+
# # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
|
171
328
|
def hidden_field(object_name, method, options = {})
|
172
329
|
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("hidden", options)
|
173
330
|
end
|
174
331
|
|
175
|
-
#
|
332
|
+
# Returns an file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
|
333
|
+
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
|
334
|
+
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
|
335
|
+
# shown.
|
336
|
+
#
|
337
|
+
# ==== Examples
|
338
|
+
# file_field(:user, :avatar)
|
339
|
+
# # => <input type="file" id="user_avatar" name="user[avatar]" />
|
340
|
+
#
|
341
|
+
# file_field(:post, :attached, :accept => 'text/html')
|
342
|
+
# # => <input type="file" id="post_attached" name="post[attached]" />
|
343
|
+
#
|
344
|
+
# file_field(:attachment, :file, :class => 'file_input')
|
345
|
+
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
|
346
|
+
#
|
176
347
|
def file_field(object_name, method, options = {})
|
177
348
|
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("file", options)
|
178
349
|
end
|
@@ -181,11 +352,26 @@ module ActionView
|
|
181
352
|
# on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
|
182
353
|
# hash with +options+.
|
183
354
|
#
|
184
|
-
#
|
185
|
-
# text_area(
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
355
|
+
# ==== Examples
|
356
|
+
# text_area(:post, :body, :cols => 20, :rows => 40)
|
357
|
+
# # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
|
358
|
+
# # #{@post.body}
|
359
|
+
# # </textarea>
|
360
|
+
#
|
361
|
+
# text_area(:comment, :text, :size => "20x30")
|
362
|
+
# # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
|
363
|
+
# # #{@comment.text}
|
364
|
+
# # </textarea>
|
365
|
+
#
|
366
|
+
# text_area(:application, :notes, :cols => 40, :rows => 15, :class => 'app_input')
|
367
|
+
# # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
|
368
|
+
# # #{@application.notes}
|
369
|
+
# # </textarea>
|
370
|
+
#
|
371
|
+
# text_area(:entry, :body, :size => "20x20", :disabled => 'disabled')
|
372
|
+
# # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
|
373
|
+
# # #{@entry.body}
|
374
|
+
# # </textarea>
|
189
375
|
def text_area(object_name, method, options = {})
|
190
376
|
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_text_area_tag(options)
|
191
377
|
end
|
@@ -194,18 +380,24 @@ module ActionView
|
|
194
380
|
# assigned to the template (identified by +object+). It's intended that +method+ returns an integer and if that
|
195
381
|
# integer is above zero, then the checkbox is checked. Additional options on the input tag can be passed as a
|
196
382
|
# hash with +options+. The +checked_value+ defaults to 1 while the default +unchecked_value+
|
197
|
-
# is set to 0 which is convenient for boolean values.
|
198
|
-
#
|
383
|
+
# is set to 0 which is convenient for boolean values. Since HTTP standards say that unchecked checkboxes don't post anything,
|
384
|
+
# we add a hidden value with the same name as the checkbox as a work around.
|
199
385
|
#
|
200
|
-
#
|
386
|
+
# ==== Examples
|
387
|
+
# # Let's say that @post.validated? is 1:
|
201
388
|
# check_box("post", "validated")
|
202
|
-
#
|
203
|
-
#
|
389
|
+
# # => <input type="checkbox" id="post_validate" name="post[validated]" value="1" checked="checked" />
|
390
|
+
# # <input name="post[validated]" type="hidden" value="0" />
|
204
391
|
#
|
205
|
-
#
|
392
|
+
# # Let's say that @puppy.gooddog is "no":
|
206
393
|
# check_box("puppy", "gooddog", {}, "yes", "no")
|
207
|
-
#
|
208
|
-
#
|
394
|
+
# # => <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
|
395
|
+
# # <input name="puppy[gooddog]" type="hidden" value="no" />
|
396
|
+
#
|
397
|
+
# check_box("eula", "accepted", {}, "yes", "no", :class => 'eula_check')
|
398
|
+
# # => <input type="checkbox" id="eula_accepted" name="eula[accepted]" value="no" />
|
399
|
+
# # <input name="eula[accepted]" type="hidden" value="no" />
|
400
|
+
#
|
209
401
|
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
|
210
402
|
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
|
211
403
|
end
|
@@ -214,12 +406,18 @@ module ActionView
|
|
214
406
|
# assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
|
215
407
|
# radio button will be checked. Additional options on the input tag can be passed as a
|
216
408
|
# hash with +options+.
|
217
|
-
#
|
409
|
+
#
|
410
|
+
# ==== Examples
|
411
|
+
# # Let's say that @post.category returns "rails":
|
218
412
|
# radio_button("post", "category", "rails")
|
219
413
|
# radio_button("post", "category", "java")
|
220
|
-
#
|
221
|
-
#
|
414
|
+
# # => <input type="radio" id="post_category" name="post[category]" value="rails" checked="checked" />
|
415
|
+
# # <input type="radio" id="post_category" name="post[category]" value="java" />
|
222
416
|
#
|
417
|
+
# radio_button("user", "receive_newsletter", "yes")
|
418
|
+
# radio_button("user", "receive_newsletter", "no")
|
419
|
+
# # => <input type="radio" id="user_receive_newsletter" name="user[receive_newsletter]" value="yes" />
|
420
|
+
# # <input type="radio" id="user_receive_newsletter" name="user[receive_newsletter]" value="no" checked="checked" />
|
223
421
|
def radio_button(object_name, method, tag_value, options = {})
|
224
422
|
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_radio_button_tag(tag_value, options)
|
225
423
|
end
|
@@ -240,17 +438,25 @@ module ActionView
|
|
240
438
|
@template_object, @local_binding = template_object, local_binding
|
241
439
|
@object = object
|
242
440
|
if @object_name.sub!(/\[\]$/,"")
|
243
|
-
if object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:
|
244
|
-
@auto_index = object.
|
441
|
+
if object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
|
442
|
+
@auto_index = object.to_param
|
245
443
|
else
|
246
|
-
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to
|
444
|
+
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
|
247
445
|
end
|
248
446
|
end
|
249
447
|
end
|
250
448
|
|
449
|
+
def to_label_tag(text = nil, options = {})
|
450
|
+
name_and_id = options.dup
|
451
|
+
add_default_name_and_id(name_and_id)
|
452
|
+
options["for"] = name_and_id["id"]
|
453
|
+
content = (text.blank? ? nil : text.to_s) || method_name.humanize
|
454
|
+
content_tag("label", content, options)
|
455
|
+
end
|
456
|
+
|
251
457
|
def to_input_field_tag(field_type, options = {})
|
252
458
|
options = options.stringify_keys
|
253
|
-
options["size"]
|
459
|
+
options["size"] = options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"] unless options.key?("size")
|
254
460
|
options = DEFAULT_FIELD_OPTIONS.merge(options)
|
255
461
|
if field_type == "hidden"
|
256
462
|
options.delete("size")
|
@@ -273,7 +479,7 @@ module ActionView
|
|
273
479
|
end
|
274
480
|
options["checked"] = "checked" if checked
|
275
481
|
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
|
276
|
-
options["id"] ||= defined?(@auto_index) ?
|
482
|
+
options["id"] ||= defined?(@auto_index) ?
|
277
483
|
"#{@object_name}_#{@auto_index}_#{@method_name}_#{pretty_tag_value}" :
|
278
484
|
"#{@object_name}_#{@method_name}_#{pretty_tag_value}"
|
279
485
|
add_default_name_and_id(options)
|
@@ -285,7 +491,7 @@ module ActionView
|
|
285
491
|
add_default_name_and_id(options)
|
286
492
|
|
287
493
|
if size = options.delete("size")
|
288
|
-
options["cols"], options["rows"] = size.split("x")
|
494
|
+
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
|
289
495
|
end
|
290
496
|
|
291
497
|
content_tag("textarea", html_escape(options.delete('value') || value_before_type_cast(object)), options)
|
@@ -303,7 +509,7 @@ module ActionView
|
|
303
509
|
end
|
304
510
|
options["checked"] = "checked" if checked
|
305
511
|
add_default_name_and_id(options)
|
306
|
-
tag("input", options) << tag("input", "name" => options["name"], "type" => "hidden", "value" => unchecked_value)
|
512
|
+
tag("input", options) << tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value)
|
307
513
|
end
|
308
514
|
|
309
515
|
def to_date_tag()
|
@@ -327,13 +533,13 @@ module ActionView
|
|
327
533
|
tag_text << " selected" if value
|
328
534
|
tag_text << ">True</option></select>"
|
329
535
|
end
|
330
|
-
|
536
|
+
|
331
537
|
def to_content_tag(tag_name, options = {})
|
332
538
|
content_tag(tag_name, value(object), options)
|
333
539
|
end
|
334
|
-
|
540
|
+
|
335
541
|
def object
|
336
|
-
@object || @template_object.instance_variable_get("@#{@object_name}")
|
542
|
+
@object || (@template_object.instance_variable_get("@#{@object_name}") rescue nil)
|
337
543
|
end
|
338
544
|
|
339
545
|
def value(object)
|
@@ -343,12 +549,12 @@ module ActionView
|
|
343
549
|
def value_before_type_cast(object)
|
344
550
|
self.class.value_before_type_cast(object, @method_name)
|
345
551
|
end
|
346
|
-
|
552
|
+
|
347
553
|
class << self
|
348
554
|
def value(object, method_name)
|
349
555
|
object.send method_name unless object.nil?
|
350
556
|
end
|
351
|
-
|
557
|
+
|
352
558
|
def value_before_type_cast(object, method_name)
|
353
559
|
unless object.nil?
|
354
560
|
object.respond_to?(method_name + "_before_type_cast") ?
|
@@ -356,7 +562,7 @@ module ActionView
|
|
356
562
|
object.send(method_name)
|
357
563
|
end
|
358
564
|
end
|
359
|
-
|
565
|
+
|
360
566
|
def check_box_checked?(value, checked_value)
|
361
567
|
case value
|
362
568
|
when TrueClass, FalseClass
|
@@ -371,7 +577,7 @@ module ActionView
|
|
371
577
|
value.to_i != 0
|
372
578
|
end
|
373
579
|
end
|
374
|
-
|
580
|
+
|
375
581
|
def radio_button_checked?(value, checked_value)
|
376
582
|
value.to_s == checked_value.to_s
|
377
583
|
end
|
@@ -401,11 +607,15 @@ module ActionView
|
|
401
607
|
end
|
402
608
|
|
403
609
|
def tag_id
|
404
|
-
"#{
|
610
|
+
"#{sanitized_object_name}_#{@method_name}"
|
405
611
|
end
|
406
612
|
|
407
613
|
def tag_id_with_index(index)
|
408
|
-
"#{
|
614
|
+
"#{sanitized_object_name}_#{index}_#{@method_name}"
|
615
|
+
end
|
616
|
+
|
617
|
+
def sanitized_object_name
|
618
|
+
@object_name.gsub(/[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
409
619
|
end
|
410
620
|
end
|
411
621
|
|
@@ -417,10 +627,10 @@ module ActionView
|
|
417
627
|
attr_accessor :object_name, :object, :options
|
418
628
|
|
419
629
|
def initialize(object_name, object, template, options, proc)
|
420
|
-
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
|
630
|
+
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
|
421
631
|
end
|
422
|
-
|
423
|
-
(field_helpers - %w(check_box radio_button)).each do |selector|
|
632
|
+
|
633
|
+
(field_helpers - %w(label check_box radio_button fields_for)).each do |selector|
|
424
634
|
src = <<-end_src
|
425
635
|
def #{selector}(method, options = {})
|
426
636
|
@template.send(#{selector.inspect}, @object_name, method, options.merge(:object => @object))
|
@@ -428,14 +638,47 @@ module ActionView
|
|
428
638
|
end_src
|
429
639
|
class_eval src, __FILE__, __LINE__
|
430
640
|
end
|
431
|
-
|
641
|
+
|
642
|
+
def fields_for(record_or_name_or_array, *args, &block)
|
643
|
+
case record_or_name_or_array
|
644
|
+
when String, Symbol
|
645
|
+
name = "#{object_name}[#{record_or_name_or_array}]"
|
646
|
+
when Array
|
647
|
+
object = record_or_name_or_array.last
|
648
|
+
name = "#{object_name}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
|
649
|
+
args.unshift(object)
|
650
|
+
else
|
651
|
+
object = record_or_name_or_array
|
652
|
+
name = "#{object_name}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
|
653
|
+
args.unshift(object)
|
654
|
+
end
|
655
|
+
|
656
|
+
@template.fields_for(name, *args, &block)
|
657
|
+
end
|
658
|
+
|
659
|
+
def label(method, text = nil, options = {})
|
660
|
+
@template.label(@object_name, method, text, options.merge(:object => @object))
|
661
|
+
end
|
662
|
+
|
432
663
|
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
|
433
664
|
@template.check_box(@object_name, method, options.merge(:object => @object), checked_value, unchecked_value)
|
434
665
|
end
|
435
|
-
|
666
|
+
|
436
667
|
def radio_button(method, tag_value, options = {})
|
437
668
|
@template.radio_button(@object_name, method, tag_value, options.merge(:object => @object))
|
438
669
|
end
|
670
|
+
|
671
|
+
def error_message_on(method, prepend_text = "", append_text = "", css_class = "formError")
|
672
|
+
@template.error_message_on(@object, method, prepend_text, append_text, css_class)
|
673
|
+
end
|
674
|
+
|
675
|
+
def error_messages(options = {})
|
676
|
+
@template.error_messages_for(@object_name, options.merge(:object => @object))
|
677
|
+
end
|
678
|
+
|
679
|
+
def submit(value = "Save changes", options = {})
|
680
|
+
@template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
|
681
|
+
end
|
439
682
|
end
|
440
683
|
end
|
441
684
|
|