actionpack 2.0.5 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +149 -7
- data/MIT-LICENSE +1 -1
- data/README +1 -1
- data/Rakefile +5 -6
- data/lib/action_controller.rb +2 -2
- data/lib/action_controller/assertions/model_assertions.rb +2 -1
- data/lib/action_controller/assertions/response_assertions.rb +4 -2
- data/lib/action_controller/assertions/routing_assertions.rb +3 -3
- data/lib/action_controller/assertions/selector_assertions.rb +30 -27
- data/lib/action_controller/assertions/tag_assertions.rb +3 -3
- data/lib/action_controller/base.rb +103 -129
- data/lib/action_controller/benchmarking.rb +3 -3
- data/lib/action_controller/caching.rb +41 -652
- data/lib/action_controller/caching/actions.rb +144 -0
- data/lib/action_controller/caching/fragments.rb +138 -0
- data/lib/action_controller/caching/pages.rb +154 -0
- data/lib/action_controller/caching/sql_cache.rb +18 -0
- data/lib/action_controller/caching/sweeping.rb +97 -0
- data/lib/action_controller/cgi_ext/cookie.rb +27 -23
- data/lib/action_controller/cgi_ext/stdinput.rb +1 -0
- data/lib/action_controller/cgi_process.rb +6 -4
- data/lib/action_controller/components.rb +7 -6
- data/lib/action_controller/cookies.rb +31 -19
- data/lib/action_controller/dispatcher.rb +51 -84
- data/lib/action_controller/filters.rb +295 -421
- data/lib/action_controller/flash.rb +1 -6
- data/lib/action_controller/headers.rb +31 -0
- data/lib/action_controller/helpers.rb +26 -9
- data/lib/action_controller/http_authentication.rb +1 -1
- data/lib/action_controller/integration.rb +65 -13
- data/lib/action_controller/layout.rb +24 -39
- data/lib/action_controller/mime_responds.rb +7 -3
- data/lib/action_controller/mime_type.rb +25 -9
- data/lib/action_controller/mime_types.rb +1 -1
- data/lib/action_controller/polymorphic_routes.rb +32 -17
- data/lib/action_controller/record_identifier.rb +10 -4
- data/lib/action_controller/request.rb +46 -30
- data/lib/action_controller/request_forgery_protection.rb +10 -9
- data/lib/action_controller/request_profiler.rb +29 -8
- data/lib/action_controller/rescue.rb +24 -24
- data/lib/action_controller/resources.rb +66 -23
- data/lib/action_controller/response.rb +2 -2
- data/lib/action_controller/routing.rb +113 -1229
- data/lib/action_controller/routing/builder.rb +204 -0
- data/lib/action_controller/{routing_optimisation.rb → routing/optimisations.rb} +13 -12
- data/lib/action_controller/routing/recognition_optimisation.rb +158 -0
- data/lib/action_controller/routing/route.rb +240 -0
- data/lib/action_controller/routing/route_set.rb +435 -0
- data/lib/action_controller/routing/routing_ext.rb +46 -0
- data/lib/action_controller/routing/segments.rb +283 -0
- data/lib/action_controller/session/active_record_store.rb +13 -8
- data/lib/action_controller/session/cookie_store.rb +20 -17
- data/lib/action_controller/session_management.rb +10 -3
- data/lib/action_controller/streaming.rb +45 -31
- data/lib/action_controller/test_case.rb +33 -23
- data/lib/action_controller/test_process.rb +39 -35
- data/lib/action_controller/url_rewriter.rb +18 -12
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +1 -1
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/version.rb +2 -2
- data/lib/action_view.rb +11 -3
- data/lib/action_view/base.rb +73 -390
- data/lib/action_view/helpers/active_record_helper.rb +83 -62
- data/lib/action_view/helpers/asset_tag_helper.rb +101 -44
- data/lib/action_view/helpers/atom_feed_helper.rb +35 -7
- data/lib/action_view/helpers/benchmark_helper.rb +5 -3
- data/lib/action_view/helpers/cache_helper.rb +3 -2
- data/lib/action_view/helpers/capture_helper.rb +1 -2
- data/lib/action_view/helpers/date_helper.rb +104 -82
- data/lib/action_view/helpers/form_helper.rb +148 -75
- data/lib/action_view/helpers/form_options_helper.rb +44 -23
- data/lib/action_view/helpers/form_tag_helper.rb +22 -13
- data/lib/action_view/helpers/javascripts/controls.js +1 -1
- data/lib/action_view/helpers/javascripts/dragdrop.js +1 -1
- data/lib/action_view/helpers/javascripts/effects.js +1 -1
- data/lib/action_view/helpers/number_helper.rb +10 -3
- data/lib/action_view/helpers/prototype_helper.rb +61 -29
- data/lib/action_view/helpers/record_tag_helper.rb +3 -3
- data/lib/action_view/helpers/sanitize_helper.rb +23 -17
- data/lib/action_view/helpers/scriptaculous_helper.rb +86 -60
- data/lib/action_view/helpers/text_helper.rb +153 -125
- data/lib/action_view/helpers/url_helper.rb +83 -28
- data/lib/action_view/inline_template.rb +20 -0
- data/lib/action_view/partial_template.rb +70 -0
- data/lib/action_view/partials.rb +31 -73
- data/lib/action_view/template.rb +127 -0
- data/lib/action_view/template_error.rb +8 -7
- data/lib/action_view/template_finder.rb +177 -0
- data/lib/action_view/template_handler.rb +18 -1
- data/lib/action_view/template_handlers/builder.rb +10 -2
- data/lib/action_view/template_handlers/compilable.rb +128 -0
- data/lib/action_view/template_handlers/erb.rb +37 -2
- data/lib/action_view/template_handlers/rjs.rb +14 -1
- data/lib/action_view/test_case.rb +58 -0
- data/test/abstract_unit.rb +1 -1
- data/test/active_record_unit.rb +3 -6
- data/test/activerecord/active_record_store_test.rb +1 -2
- data/test/activerecord/render_partial_with_record_identification_test.rb +158 -41
- data/test/adv_attr_test.rb +20 -0
- data/test/controller/action_pack_assertions_test.rb +16 -19
- data/test/controller/addresses_render_test.rb +1 -1
- data/test/controller/assert_select_test.rb +13 -6
- data/test/controller/base_test.rb +48 -2
- data/test/controller/benchmark_test.rb +1 -2
- data/test/controller/caching_test.rb +282 -21
- data/test/controller/capture_test.rb +1 -1
- data/test/controller/cgi_test.rb +1 -1
- data/test/controller/components_test.rb +1 -1
- data/test/controller/content_type_test.rb +2 -2
- data/test/controller/cookie_test.rb +13 -2
- data/test/controller/custom_handler_test.rb +14 -10
- data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -1
- data/test/controller/dispatcher_test.rb +31 -49
- data/test/controller/fake_controllers.rb +17 -0
- data/test/controller/fake_models.rb +6 -0
- data/test/controller/filter_params_test.rb +14 -8
- data/test/controller/filters_test.rb +44 -16
- data/test/controller/flash_test.rb +2 -2
- data/test/controller/header_test.rb +14 -0
- data/test/controller/helper_test.rb +19 -15
- data/test/controller/html-scanner/document_test.rb +1 -2
- data/test/controller/html-scanner/node_test.rb +1 -2
- data/test/controller/html-scanner/sanitizer_test.rb +8 -5
- data/test/controller/html-scanner/tag_node_test.rb +1 -2
- data/test/controller/html-scanner/text_node_test.rb +2 -3
- data/test/controller/html-scanner/tokenizer_test.rb +8 -2
- data/test/controller/http_authentication_test.rb +1 -1
- data/test/controller/integration_test.rb +14 -16
- data/test/controller/integration_upload_test.rb +43 -0
- data/test/controller/layout_test.rb +26 -6
- data/test/controller/mime_responds_test.rb +39 -7
- data/test/controller/mime_type_test.rb +29 -5
- data/test/controller/new_render_test.rb +105 -34
- data/test/controller/polymorphic_routes_test.rb +32 -20
- data/test/controller/record_identifier_test.rb +38 -2
- data/test/controller/redirect_test.rb +21 -1
- data/test/controller/render_test.rb +59 -15
- data/test/controller/request_forgery_protection_test.rb +92 -5
- data/test/controller/request_test.rb +64 -6
- data/test/controller/rescue_test.rb +22 -6
- data/test/controller/resources_test.rb +102 -14
- data/test/controller/routing_test.rb +231 -19
- data/test/controller/selector_test.rb +2 -2
- data/test/controller/send_file_test.rb +14 -3
- data/test/controller/session/cookie_store_test.rb +16 -4
- data/test/controller/session/mem_cache_store_test.rb +3 -4
- data/test/controller/session_fixation_test.rb +1 -1
- data/test/controller/session_management_test.rb +23 -1
- data/test/controller/test_test.rb +39 -18
- data/test/controller/url_rewriter_test.rb +35 -1
- data/test/controller/verification_test.rb +1 -1
- data/test/controller/view_paths_test.rb +15 -12
- data/test/controller/webservice_test.rb +48 -3
- data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
- data/test/fixtures/company.rb +1 -0
- data/test/fixtures/customers/_customer.html.erb +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +6 -0
- data/test/fixtures/functional_caching/_partial.erb +3 -0
- data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
- data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
- data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
- data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
- data/test/fixtures/mascot.rb +3 -0
- data/test/fixtures/mascots.yml +4 -0
- data/test/fixtures/mascots/_mascot.html.erb +1 -0
- data/test/fixtures/multipart/boundary_problem_file +10 -0
- data/test/fixtures/public/javascripts/application.js +1 -0
- data/test/fixtures/public/javascripts/controls.js +1 -0
- data/test/fixtures/public/javascripts/dragdrop.js +1 -0
- data/test/fixtures/public/javascripts/effects.js +1 -0
- data/test/fixtures/public/javascripts/prototype.js +1 -0
- data/test/fixtures/public/javascripts/version.1.0.js +1 -0
- data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
- data/test/fixtures/reply.rb +1 -0
- data/test/fixtures/shared.html.erb +1 -0
- data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
- data/test/fixtures/test/_customer_counter.erb +1 -0
- data/test/fixtures/test/_form.erb +1 -0
- data/test/fixtures/test/_labelling_form.erb +1 -0
- data/test/fixtures/test/_raise.html.erb +1 -0
- data/test/fixtures/test/greeting.js.rjs +1 -0
- data/test/fixtures/topics/_topic.html.erb +1 -0
- data/test/template/active_record_helper_test.rb +25 -8
- data/test/template/asset_tag_helper_test.rb +100 -17
- data/test/template/atom_feed_helper_test.rb +29 -1
- data/test/template/benchmark_helper_test.rb +10 -22
- data/test/template/date_helper_test.rb +455 -153
- data/test/template/erb_util_test.rb +10 -42
- data/test/template/form_helper_test.rb +192 -66
- data/test/template/form_options_helper_test.rb +19 -8
- data/test/template/form_tag_helper_test.rb +11 -8
- data/test/template/javascript_helper_test.rb +3 -9
- data/test/template/number_helper_test.rb +6 -3
- data/test/template/prototype_helper_test.rb +27 -40
- data/test/template/record_tag_helper_test.rb +54 -0
- data/test/template/sanitize_helper_test.rb +5 -6
- data/test/template/scriptaculous_helper_test.rb +7 -13
- data/test/template/tag_helper_test.rb +3 -6
- data/test/template/template_finder_test.rb +73 -0
- data/test/template/template_object_test.rb +95 -0
- data/test/template/test_test.rb +56 -0
- data/test/template/text_helper_test.rb +46 -33
- data/test/template/url_helper_test.rb +8 -10
- metadata +65 -12
- data/lib/action_view/compiled_templates.rb +0 -69
- data/test/action_view_test.rb +0 -44
- data/test/activerecord/fixtures_test.rb +0 -24
- data/test/controller/fragment_store_setting_test.rb +0 -47
- data/test/template/compiled_templates_test.rb +0 -197
- data/test/template/deprecate_ivars_test.rb +0 -51
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'cgi'
|
|
2
2
|
require 'action_view/helpers/date_helper'
|
|
3
3
|
require 'action_view/helpers/tag_helper'
|
|
4
|
+
require 'action_view/helpers/form_tag_helper'
|
|
4
5
|
|
|
5
6
|
module ActionView
|
|
6
7
|
module Helpers
|
|
@@ -32,6 +33,15 @@ module ActionView
|
|
|
32
33
|
# <input name="commit" type="submit" value="Create" />
|
|
33
34
|
# </form>
|
|
34
35
|
#
|
|
36
|
+
# If you are using a partial for your form fields, you can use this shortcut:
|
|
37
|
+
#
|
|
38
|
+
# <% form_for :person, @person, :url => { :action => "create" } do |f| %>
|
|
39
|
+
# <%= render :partial => f %>
|
|
40
|
+
# <%= submit_tag 'Create' %>
|
|
41
|
+
# <% end %>
|
|
42
|
+
#
|
|
43
|
+
# This example will render the <tt>people/_form</tt> partial, setting a local variable called <tt>form</tt> which references the yielded FormBuilder.
|
|
44
|
+
#
|
|
35
45
|
# The <tt>params</tt> object created when this form is submitted would look like:
|
|
36
46
|
#
|
|
37
47
|
# {"action"=>"create", "controller"=>"persons", "person"=>{"first_name"=>"William", "last_name"=>"Smith"}}
|
|
@@ -42,7 +52,7 @@ module ActionView
|
|
|
42
52
|
#
|
|
43
53
|
# If the object name contains square brackets the id for the object will be inserted. For example:
|
|
44
54
|
#
|
|
45
|
-
# <%= text_field "person[]", "name" %>
|
|
55
|
+
# <%= text_field "person[]", "name" %>
|
|
46
56
|
#
|
|
47
57
|
# ...will generate the following ERb.
|
|
48
58
|
#
|
|
@@ -57,31 +67,87 @@ module ActionView
|
|
|
57
67
|
#
|
|
58
68
|
# <input type="text" id="person_1_name" name="person[1][name]" value="<%= @person.name %>" />
|
|
59
69
|
#
|
|
70
|
+
# An <tt>index</tt> option may also be passed to <tt>form_for</tt> and <tt>fields_for</tt>. This automatically applies
|
|
71
|
+
# the <tt>index</tt> to all the nested fields.
|
|
72
|
+
#
|
|
60
73
|
# There are also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html,
|
|
61
74
|
# link:classes/ActionView/Helpers/DateHelper.html, and link:classes/ActionView/Helpers/ActiveRecordHelper.html
|
|
62
75
|
module FormHelper
|
|
63
|
-
# Creates a form and a scope around a specific model object that is used as
|
|
64
|
-
# values for the fields.
|
|
76
|
+
# Creates a form and a scope around a specific model object that is used as
|
|
77
|
+
# a base for questioning about values for the fields.
|
|
65
78
|
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
79
|
+
# Rails provides succint resource-oriented form generation with +form_for+
|
|
80
|
+
# like this:
|
|
81
|
+
#
|
|
82
|
+
# <% form_for @offer do |f| %>
|
|
83
|
+
# <%= f.label :version, 'Version' %>:
|
|
84
|
+
# <%= f.text_field :version %><br />
|
|
85
|
+
# <%= f.label :author, 'Author' %>:
|
|
86
|
+
# <%= f.text_field :author %><br />
|
|
87
|
+
# <% end %>
|
|
88
|
+
#
|
|
89
|
+
# There, +form_for+ is able to generate the rest of RESTful form parameters
|
|
90
|
+
# based on introspection on the record, but to understand what it does we
|
|
91
|
+
# need to dig first into the alternative generic usage it is based upon.
|
|
92
|
+
#
|
|
93
|
+
# === Generic form_for
|
|
94
|
+
#
|
|
95
|
+
# The generic way to call +form_for+ yields a form builder around a model:
|
|
96
|
+
#
|
|
97
|
+
# <% form_for :person, :url => { :action => "update" } do |f| %>
|
|
98
|
+
# <%= f.error_messages %>
|
|
99
|
+
# First name: <%= f.text_field :first_name %><br />
|
|
100
|
+
# Last name : <%= f.text_field :last_name %><br />
|
|
101
|
+
# Biography : <%= f.text_area :biography %><br />
|
|
102
|
+
# Admin? : <%= f.check_box :admin %><br />
|
|
71
103
|
# <% end %>
|
|
72
104
|
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
105
|
+
# There, the first argument is a symbol or string with the name of the
|
|
106
|
+
# object the form is about, and also the name of the instance variable the
|
|
107
|
+
# object is stored in.
|
|
108
|
+
#
|
|
109
|
+
# The form builder acts as a regular form helper that somehow carries the
|
|
110
|
+
# model. Thus, the idea is that
|
|
111
|
+
#
|
|
112
|
+
# <%= f.text_field :first_name %>
|
|
113
|
+
#
|
|
114
|
+
# gets expanded to
|
|
115
|
+
#
|
|
116
|
+
# <%= text_field :person, :first_name %>
|
|
117
|
+
#
|
|
118
|
+
# If the instance variable is not <tt>@person</tt> you can pass the actual
|
|
119
|
+
# record as the second argument:
|
|
120
|
+
#
|
|
121
|
+
# <% form_for :person, person, :url => { :action => "update" } do |f| %>
|
|
122
|
+
# ...
|
|
123
|
+
# <% end %>
|
|
124
|
+
#
|
|
125
|
+
# In that case you can think
|
|
126
|
+
#
|
|
127
|
+
# <%= f.text_field :first_name %>
|
|
77
128
|
#
|
|
78
|
-
#
|
|
79
|
-
# approach would require <tt>text_field :person, :name, :object => person</tt>
|
|
80
|
-
# to work with local variables instead of instance ones, the form_for calls remain the same. You simply declare once with
|
|
81
|
-
# <tt>:person, person</tt> and all subsequent field calls save <tt>:person</tt> and <tt>:object => person</tt>.
|
|
129
|
+
# gets expanded to
|
|
82
130
|
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
131
|
+
# <%= text_field :person, :first_name, :object => person %>
|
|
132
|
+
#
|
|
133
|
+
# You can even display error messages of the wrapped model this way:
|
|
134
|
+
#
|
|
135
|
+
# <%= f.error_messages %>
|
|
136
|
+
#
|
|
137
|
+
# In any of its variants, the rightmost argument to +form_for+ is an
|
|
138
|
+
# optional hash of options:
|
|
139
|
+
#
|
|
140
|
+
# * <tt>:url</tt> - The URL the form is submitted to. It takes the same fields
|
|
141
|
+
# you pass to +url_for+ or +link_to+. In particular you may pass here a
|
|
142
|
+
# named route directly as well. Defaults to the current action.
|
|
143
|
+
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
|
|
144
|
+
#
|
|
145
|
+
# Worth noting is that the +form_for+ tag is called in a ERb evaluation block,
|
|
146
|
+
# not an ERb output block. So that's <tt><% %></tt>, not <tt><%= %></tt>.
|
|
147
|
+
#
|
|
148
|
+
# Also note that +form_for+ doesn't create an exclusive scope. It's still
|
|
149
|
+
# possible to use both the stand-alone FormHelper methods and methods from
|
|
150
|
+
# FormTagHelper. For example:
|
|
85
151
|
#
|
|
86
152
|
# <% form_for :person, @person, :url => { :action => "update" } do |f| %>
|
|
87
153
|
# First name: <%= f.text_field :first_name %>
|
|
@@ -90,42 +156,38 @@ module ActionView
|
|
|
90
156
|
# Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %>
|
|
91
157
|
# <% end %>
|
|
92
158
|
#
|
|
93
|
-
#
|
|
94
|
-
# like FormOptionHelper#collection_select
|
|
95
|
-
#
|
|
96
|
-
# HTML attributes for the form tag can be given as :html => {...}. For example:
|
|
97
|
-
#
|
|
98
|
-
# <% form_for :person, @person, :html => {:id => 'person_form'} do |f| %>
|
|
99
|
-
# ...
|
|
100
|
-
# <% end %>
|
|
159
|
+
# This also works for the methods in FormOptionHelper and DateHelper that are
|
|
160
|
+
# designed to work with an object as base, like FormOptionHelper#collection_select
|
|
161
|
+
# and DateHelper#datetime_select.
|
|
101
162
|
#
|
|
102
|
-
#
|
|
103
|
-
# style with CSS or manipulate with JavaScript.
|
|
163
|
+
# === Resource-oriented style
|
|
104
164
|
#
|
|
105
|
-
#
|
|
165
|
+
# As we said above, in addition to manually configuring the +form_for+ call,
|
|
166
|
+
# you can rely on automated resource identification, which will use the conventions
|
|
167
|
+
# and named routes of that approach. This is the preferred way to use +form_for+
|
|
168
|
+
# nowadays.
|
|
106
169
|
#
|
|
107
|
-
#
|
|
108
|
-
# the conventions and named routes of that approach. Examples:
|
|
170
|
+
# For example, if <tt>@post</tt> is an existing record you want to edit
|
|
109
171
|
#
|
|
110
|
-
# <% form_for
|
|
172
|
+
# <% form_for @post do |f| %>
|
|
111
173
|
# ...
|
|
112
174
|
# <% end %>
|
|
113
175
|
#
|
|
114
|
-
#
|
|
176
|
+
# is equivalent to something like:
|
|
115
177
|
#
|
|
116
178
|
# <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
|
|
117
179
|
# ...
|
|
118
180
|
# <% end %>
|
|
119
181
|
#
|
|
120
|
-
# And for new records
|
|
182
|
+
# And for new records
|
|
121
183
|
#
|
|
122
184
|
# <% form_for(Post.new) do |f| %>
|
|
123
185
|
# ...
|
|
124
186
|
# <% end %>
|
|
125
187
|
#
|
|
126
|
-
#
|
|
188
|
+
# expands to
|
|
127
189
|
#
|
|
128
|
-
# <% form_for :post,
|
|
190
|
+
# <% form_for :post, Post.new, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
|
|
129
191
|
# ...
|
|
130
192
|
# <% end %>
|
|
131
193
|
#
|
|
@@ -135,7 +197,7 @@ module ActionView
|
|
|
135
197
|
# ...
|
|
136
198
|
# <% end %>
|
|
137
199
|
#
|
|
138
|
-
# And for namespaced routes, like admin_post_url
|
|
200
|
+
# And for namespaced routes, like +admin_post_url+:
|
|
139
201
|
#
|
|
140
202
|
# <% form_for([:admin, @post]) do |f| %>
|
|
141
203
|
# ...
|
|
@@ -153,6 +215,12 @@ module ActionView
|
|
|
153
215
|
# <%= check_box_tag "person[admin]", @person.company.admin? %>
|
|
154
216
|
# <% end %>
|
|
155
217
|
#
|
|
218
|
+
# In this case, if you use this:
|
|
219
|
+
#
|
|
220
|
+
# <%= render :partial => f %>
|
|
221
|
+
#
|
|
222
|
+
# The rendered template is <tt>people/_labelling_form</tt> and the local variable referencing the form builder is called <tt>labelling_form</tt>.
|
|
223
|
+
#
|
|
156
224
|
# In many cases you will want to wrap the above in another helper, so you could do something like the following:
|
|
157
225
|
#
|
|
158
226
|
# def labelled_form_for(record_or_name_or_array, *args, &proc)
|
|
@@ -256,13 +324,13 @@ module ActionView
|
|
|
256
324
|
#
|
|
257
325
|
# ==== Examples
|
|
258
326
|
# label(:post, :title)
|
|
259
|
-
#
|
|
327
|
+
# # => <label for="post_title">Title</label>
|
|
260
328
|
#
|
|
261
329
|
# label(:post, :title, "A short title")
|
|
262
|
-
#
|
|
330
|
+
# # => <label for="post_title">A short title</label>
|
|
263
331
|
#
|
|
264
332
|
# label(:post, :title, "A short title", :class => "title_label")
|
|
265
|
-
#
|
|
333
|
+
# # => <label for="post_title" class="title_label">A short title</label>
|
|
266
334
|
#
|
|
267
335
|
def label(object_name, method, text = nil, options = {})
|
|
268
336
|
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_label_tag(text, options)
|
|
@@ -317,7 +385,7 @@ module ActionView
|
|
|
317
385
|
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
|
|
318
386
|
# shown.
|
|
319
387
|
#
|
|
320
|
-
# ==== Examples
|
|
388
|
+
# ==== Examples
|
|
321
389
|
# hidden_field(:signup, :pass_confirm)
|
|
322
390
|
# # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
|
|
323
391
|
#
|
|
@@ -384,10 +452,10 @@ module ActionView
|
|
|
384
452
|
# is set to 0 which is convenient for boolean values. Since HTTP standards say that unchecked checkboxes don't post anything,
|
|
385
453
|
# we add a hidden value with the same name as the checkbox as a work around.
|
|
386
454
|
#
|
|
387
|
-
# ==== Examples
|
|
455
|
+
# ==== Examples
|
|
388
456
|
# # Let's say that @post.validated? is 1:
|
|
389
457
|
# check_box("post", "validated")
|
|
390
|
-
# # => <input type="checkbox" id="
|
|
458
|
+
# # => <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
|
|
391
459
|
# # <input name="post[validated]" type="hidden" value="0" />
|
|
392
460
|
#
|
|
393
461
|
# # Let's say that @puppy.gooddog is "no":
|
|
@@ -395,8 +463,8 @@ module ActionView
|
|
|
395
463
|
# # => <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
|
|
396
464
|
# # <input name="puppy[gooddog]" type="hidden" value="no" />
|
|
397
465
|
#
|
|
398
|
-
# check_box("eula", "accepted", {}, "yes", "no"
|
|
399
|
-
# # => <input type="checkbox" id="eula_accepted" name="eula[accepted]" value="
|
|
466
|
+
# check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
|
|
467
|
+
# # => <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
|
|
400
468
|
# # <input name="eula[accepted]" type="hidden" value="no" />
|
|
401
469
|
#
|
|
402
470
|
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
|
|
@@ -412,27 +480,26 @@ module ActionView
|
|
|
412
480
|
# # Let's say that @post.category returns "rails":
|
|
413
481
|
# radio_button("post", "category", "rails")
|
|
414
482
|
# radio_button("post", "category", "java")
|
|
415
|
-
# # => <input type="radio" id="
|
|
416
|
-
# # <input type="radio" id="
|
|
483
|
+
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
|
|
484
|
+
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
|
|
417
485
|
#
|
|
418
486
|
# radio_button("user", "receive_newsletter", "yes")
|
|
419
487
|
# radio_button("user", "receive_newsletter", "no")
|
|
420
|
-
# # => <input type="radio" id="
|
|
421
|
-
# # <input type="radio" id="
|
|
488
|
+
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
|
|
489
|
+
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
|
|
422
490
|
def radio_button(object_name, method, tag_value, options = {})
|
|
423
491
|
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_radio_button_tag(tag_value, options)
|
|
424
492
|
end
|
|
425
493
|
end
|
|
426
494
|
|
|
427
495
|
class InstanceTag #:nodoc:
|
|
428
|
-
include Helpers::TagHelper
|
|
496
|
+
include Helpers::TagHelper, Helpers::FormTagHelper
|
|
429
497
|
|
|
430
498
|
attr_reader :method_name, :object_name
|
|
431
499
|
|
|
432
500
|
DEFAULT_FIELD_OPTIONS = { "size" => 30 }.freeze unless const_defined?(:DEFAULT_FIELD_OPTIONS)
|
|
433
501
|
DEFAULT_RADIO_OPTIONS = { }.freeze unless const_defined?(:DEFAULT_RADIO_OPTIONS)
|
|
434
502
|
DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }.freeze unless const_defined?(:DEFAULT_TEXT_AREA_OPTIONS)
|
|
435
|
-
DEFAULT_DATE_OPTIONS = { :discard_type => true }.freeze unless const_defined?(:DEFAULT_DATE_OPTIONS)
|
|
436
503
|
|
|
437
504
|
def initialize(object_name, method_name, template_object, local_binding = nil, object = nil)
|
|
438
505
|
@object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
|
|
@@ -448,11 +515,13 @@ module ActionView
|
|
|
448
515
|
end
|
|
449
516
|
|
|
450
517
|
def to_label_tag(text = nil, options = {})
|
|
518
|
+
options = options.stringify_keys
|
|
451
519
|
name_and_id = options.dup
|
|
452
520
|
add_default_name_and_id(name_and_id)
|
|
453
|
-
options
|
|
521
|
+
options.delete("index")
|
|
522
|
+
options["for"] ||= name_and_id["id"]
|
|
454
523
|
content = (text.blank? ? nil : text.to_s) || method_name.humanize
|
|
455
|
-
|
|
524
|
+
label_tag(name_and_id["id"], content, options)
|
|
456
525
|
end
|
|
457
526
|
|
|
458
527
|
def to_input_field_tag(field_type, options = {})
|
|
@@ -464,6 +533,7 @@ module ActionView
|
|
|
464
533
|
end
|
|
465
534
|
options["type"] = field_type
|
|
466
535
|
options["value"] ||= value_before_type_cast(object) unless field_type == "file"
|
|
536
|
+
options["value"] &&= html_escape(options["value"])
|
|
467
537
|
add_default_name_and_id(options)
|
|
468
538
|
tag("input", options)
|
|
469
539
|
end
|
|
@@ -481,8 +551,8 @@ module ActionView
|
|
|
481
551
|
options["checked"] = "checked" if checked
|
|
482
552
|
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
|
|
483
553
|
options["id"] ||= defined?(@auto_index) ?
|
|
484
|
-
"#{@
|
|
485
|
-
"#{
|
|
554
|
+
"#{tag_id_with_index(@auto_index)}_#{pretty_tag_value}" :
|
|
555
|
+
"#{tag_id}_#{pretty_tag_value}"
|
|
486
556
|
add_default_name_and_id(options)
|
|
487
557
|
tag("input", options)
|
|
488
558
|
end
|
|
@@ -513,15 +583,6 @@ module ActionView
|
|
|
513
583
|
tag("input", options) << tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value)
|
|
514
584
|
end
|
|
515
585
|
|
|
516
|
-
def to_date_tag()
|
|
517
|
-
defaults = DEFAULT_DATE_OPTIONS.dup
|
|
518
|
-
date = value(object) || Date.today
|
|
519
|
-
options = Proc.new { |position| defaults.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") }
|
|
520
|
-
html_day_select(date, options.call(3)) +
|
|
521
|
-
html_month_select(date, options.call(2)) +
|
|
522
|
-
html_year_select(date, options.call(1))
|
|
523
|
-
end
|
|
524
|
-
|
|
525
586
|
def to_boolean_select_tag(options = {})
|
|
526
587
|
options = options.stringify_keys
|
|
527
588
|
add_default_name_and_id(options)
|
|
@@ -574,6 +635,8 @@ module ActionView
|
|
|
574
635
|
value != 0
|
|
575
636
|
when String
|
|
576
637
|
value == checked_value
|
|
638
|
+
when Array
|
|
639
|
+
value.include?(checked_value)
|
|
577
640
|
else
|
|
578
641
|
value.to_i != 0
|
|
579
642
|
end
|
|
@@ -600,23 +663,27 @@ module ActionView
|
|
|
600
663
|
end
|
|
601
664
|
|
|
602
665
|
def tag_name
|
|
603
|
-
"#{@object_name}[#{
|
|
666
|
+
"#{@object_name}[#{sanitized_method_name}]"
|
|
604
667
|
end
|
|
605
668
|
|
|
606
669
|
def tag_name_with_index(index)
|
|
607
|
-
"#{@object_name}[#{index}][#{
|
|
670
|
+
"#{@object_name}[#{index}][#{sanitized_method_name}]"
|
|
608
671
|
end
|
|
609
672
|
|
|
610
673
|
def tag_id
|
|
611
|
-
"#{sanitized_object_name}_#{
|
|
674
|
+
"#{sanitized_object_name}_#{sanitized_method_name}"
|
|
612
675
|
end
|
|
613
676
|
|
|
614
677
|
def tag_id_with_index(index)
|
|
615
|
-
"#{sanitized_object_name}_#{index}_#{
|
|
678
|
+
"#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
|
|
616
679
|
end
|
|
617
680
|
|
|
618
681
|
def sanitized_object_name
|
|
619
|
-
@object_name.gsub(/[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
|
682
|
+
@sanitized_object_name ||= @object_name.gsub(/[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
def sanitized_method_name
|
|
686
|
+
@sanitized_method_name ||= @method_name.sub(/\?$/,"")
|
|
620
687
|
end
|
|
621
688
|
end
|
|
622
689
|
|
|
@@ -629,12 +696,13 @@ module ActionView
|
|
|
629
696
|
|
|
630
697
|
def initialize(object_name, object, template, options, proc)
|
|
631
698
|
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
|
|
699
|
+
@default_options = @options ? @options.slice(:index) : {}
|
|
632
700
|
end
|
|
633
701
|
|
|
634
702
|
(field_helpers - %w(label check_box radio_button fields_for)).each do |selector|
|
|
635
703
|
src = <<-end_src
|
|
636
704
|
def #{selector}(method, options = {})
|
|
637
|
-
@template.send(#{selector.inspect}, @object_name, method, options
|
|
705
|
+
@template.send(#{selector.inspect}, @object_name, method, objectify_options(options))
|
|
638
706
|
end
|
|
639
707
|
end_src
|
|
640
708
|
class_eval src, __FILE__, __LINE__
|
|
@@ -653,20 +721,20 @@ module ActionView
|
|
|
653
721
|
name = "#{object_name}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
|
|
654
722
|
args.unshift(object)
|
|
655
723
|
end
|
|
656
|
-
|
|
724
|
+
|
|
657
725
|
@template.fields_for(name, *args, &block)
|
|
658
726
|
end
|
|
659
727
|
|
|
660
728
|
def label(method, text = nil, options = {})
|
|
661
|
-
@template.label(@object_name, method, text, options
|
|
729
|
+
@template.label(@object_name, method, text, objectify_options(options))
|
|
662
730
|
end
|
|
663
731
|
|
|
664
732
|
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
|
|
665
|
-
@template.check_box(@object_name, method, options
|
|
733
|
+
@template.check_box(@object_name, method, objectify_options(options), checked_value, unchecked_value)
|
|
666
734
|
end
|
|
667
735
|
|
|
668
736
|
def radio_button(method, tag_value, options = {})
|
|
669
|
-
@template.radio_button(@object_name, method, tag_value, options
|
|
737
|
+
@template.radio_button(@object_name, method, tag_value, objectify_options(options))
|
|
670
738
|
end
|
|
671
739
|
|
|
672
740
|
def error_message_on(method, prepend_text = "", append_text = "", css_class = "formError")
|
|
@@ -674,12 +742,17 @@ module ActionView
|
|
|
674
742
|
end
|
|
675
743
|
|
|
676
744
|
def error_messages(options = {})
|
|
677
|
-
@template.error_messages_for(@object_name, options
|
|
745
|
+
@template.error_messages_for(@object_name, objectify_options(options))
|
|
678
746
|
end
|
|
679
747
|
|
|
680
748
|
def submit(value = "Save changes", options = {})
|
|
681
749
|
@template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
|
|
682
750
|
end
|
|
751
|
+
|
|
752
|
+
private
|
|
753
|
+
def objectify_options(options)
|
|
754
|
+
@default_options.merge(options.merge(:object => @object))
|
|
755
|
+
end
|
|
683
756
|
end
|
|
684
757
|
end
|
|
685
758
|
|
|
@@ -53,6 +53,21 @@ module ActionView
|
|
|
53
53
|
# <option value="2">Sam</option>
|
|
54
54
|
# <option value="3">Tobias</option>
|
|
55
55
|
# </select>
|
|
56
|
+
#
|
|
57
|
+
# Like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
|
|
58
|
+
# option to be in the +html_options+ parameter.
|
|
59
|
+
#
|
|
60
|
+
# Example:
|
|
61
|
+
#
|
|
62
|
+
# select("album[]", "genre", %w[rap rock country], {}, { :index => nil })
|
|
63
|
+
#
|
|
64
|
+
# becomes:
|
|
65
|
+
#
|
|
66
|
+
# <select name="album[][genre]" id="album__genre">
|
|
67
|
+
# <option value="rap">rap</option>
|
|
68
|
+
# <option value="rock">rock</option>
|
|
69
|
+
# <option value="country">country</option>
|
|
70
|
+
# </select>
|
|
56
71
|
module FormOptionsHelper
|
|
57
72
|
include ERB::Util
|
|
58
73
|
|
|
@@ -78,8 +93,8 @@ module ActionView
|
|
|
78
93
|
# This allows the user to submit a form page more than once with the expected results of creating multiple records.
|
|
79
94
|
# In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
|
|
80
95
|
#
|
|
81
|
-
# By default, post.person_id is the selected option. Specify
|
|
82
|
-
# or
|
|
96
|
+
# By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection
|
|
97
|
+
# or <tt>:selected => nil</tt> to leave all options unselected.
|
|
83
98
|
def select(object, method, choices, options = {}, html_options = {})
|
|
84
99
|
InstanceTag.new(object, method, self, nil, options.delete(:object)).to_select_tag(choices, options, html_options)
|
|
85
100
|
end
|
|
@@ -104,7 +119,7 @@ module ActionView
|
|
|
104
119
|
# end
|
|
105
120
|
# end
|
|
106
121
|
#
|
|
107
|
-
# Sample usage (selecting the associated
|
|
122
|
+
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
|
|
108
123
|
# collection_select(:post, :author_id, Author.find(:all), :id, :name_with_initial, {:prompt => true})
|
|
109
124
|
#
|
|
110
125
|
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
|
|
@@ -129,10 +144,16 @@ module ActionView
|
|
|
129
144
|
# In addition to the <tt>:include_blank</tt> option documented above,
|
|
130
145
|
# this method also supports a <tt>:model</tt> option, which defaults
|
|
131
146
|
# to TimeZone. This may be used by users to specify a different time
|
|
132
|
-
# zone model object. (See
|
|
147
|
+
# zone model object. (See +time_zone_options_for_select+ for more
|
|
133
148
|
# information.)
|
|
149
|
+
#
|
|
150
|
+
# You can also supply an array of TimeZone objects
|
|
151
|
+
# as +priority_zones+, so that they will be listed above the rest of the
|
|
152
|
+
# (long) list. (You can use TimeZone.us_zones as a convenience for
|
|
153
|
+
# obtaining a list of the US time zones.)
|
|
154
|
+
#
|
|
134
155
|
# Finally, this method supports a <tt>:default</tt> option, which selects
|
|
135
|
-
# a default TimeZone if the object's time zone is nil
|
|
156
|
+
# a default TimeZone if the object's time zone is +nil+.
|
|
136
157
|
#
|
|
137
158
|
# Examples:
|
|
138
159
|
# time_zone_select( "user", "time_zone", nil, :include_blank => true)
|
|
@@ -141,6 +162,8 @@ module ActionView
|
|
|
141
162
|
#
|
|
142
163
|
# time_zone_select( "user", 'time_zone', TimeZone.us_zones, :default => "Pacific Time (US & Canada)")
|
|
143
164
|
#
|
|
165
|
+
# time_zone_select( "user", 'time_zone', [ TimeZone['Alaska'], TimeZone['Hawaii'] ])
|
|
166
|
+
#
|
|
144
167
|
# time_zone_select( "user", "time_zone", TZInfo::Timezone.all.sort, :model => TZInfo::Timezone)
|
|
145
168
|
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
|
|
146
169
|
InstanceTag.new(object, method, self, nil, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
|
|
@@ -149,7 +172,7 @@ module ActionView
|
|
|
149
172
|
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
|
|
150
173
|
# where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
|
|
151
174
|
# the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
|
|
152
|
-
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +
|
|
175
|
+
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+
|
|
153
176
|
# may also be an array of values to be selected when using a multiple select.
|
|
154
177
|
#
|
|
155
178
|
# Examples (call, result):
|
|
@@ -194,24 +217,22 @@ module ActionView
|
|
|
194
217
|
options_for_select(options, selected)
|
|
195
218
|
end
|
|
196
219
|
|
|
197
|
-
# Returns a string of <tt><option></tt> tags, like <tt
|
|
220
|
+
# Returns a string of <tt><option></tt> tags, like <tt>options_from_collection_for_select</tt>, but
|
|
198
221
|
# groups them by <tt><optgroup></tt> tags based on the object relationships of the arguments.
|
|
199
222
|
#
|
|
200
223
|
# Parameters:
|
|
201
|
-
# +collection
|
|
202
|
-
# +group_method
|
|
203
|
-
#
|
|
204
|
-
# +
|
|
205
|
-
#
|
|
206
|
-
# +option_key_method
|
|
207
|
-
#
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
#
|
|
212
|
-
# +
|
|
213
|
-
# which will have the +selected+ attribute set. Corresponds to the return value
|
|
214
|
-
# of one of the calls to +option_key_method+. If +nil+, no selection is made.
|
|
224
|
+
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
|
|
225
|
+
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
|
|
226
|
+
# array of child objects representing the <tt><option></tt> tags.
|
|
227
|
+
# * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
|
|
228
|
+
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
|
|
229
|
+
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
|
|
230
|
+
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
|
|
231
|
+
# * +option_value_method+ - The name of a method which, when called on a child object of a member of
|
|
232
|
+
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
|
|
233
|
+
# * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
|
|
234
|
+
# which will have the +selected+ attribute set. Corresponds to the return value of one of the calls
|
|
235
|
+
# to +option_key_method+. If +nil+, no selection is made.
|
|
215
236
|
#
|
|
216
237
|
# Example object structure for use with this method:
|
|
217
238
|
# class Continent < ActiveRecord::Base
|
|
@@ -277,8 +298,8 @@ module ActionView
|
|
|
277
298
|
# a TimeZone.
|
|
278
299
|
#
|
|
279
300
|
# By default, +model+ is the TimeZone constant (which can be obtained
|
|
280
|
-
# in
|
|
281
|
-
# +model+ parameter be an object that responds to
|
|
301
|
+
# in Active Record as a value object). The only requirement is that the
|
|
302
|
+
# +model+ parameter be an object that responds to +all+, and returns
|
|
282
303
|
# an array of objects that represent time zones.
|
|
283
304
|
#
|
|
284
305
|
# NOTE: Only the option tags are returned, you have to wrap this call in
|