actionview 4.2.11.1 → 7.0.2.4
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.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +229 -215
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -8
- data/lib/action_view/base.rb +116 -43
- data/lib/action_view/buffers.rb +20 -3
- data/lib/action_view/cache_expiry.rb +66 -0
- data/lib/action_view/context.rb +8 -12
- data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
- data/lib/action_view/dependency_tracker.rb +21 -122
- data/lib/action_view/digestor.rb +92 -85
- data/lib/action_view/flows.rb +15 -16
- data/lib/action_view/gem_version.rb +6 -4
- data/lib/action_view/helpers/active_model_helper.rb +17 -12
- data/lib/action_view/helpers/asset_tag_helper.rb +356 -101
- data/lib/action_view/helpers/asset_url_helper.rb +180 -74
- data/lib/action_view/helpers/atom_feed_helper.rb +21 -19
- data/lib/action_view/helpers/cache_helper.rb +156 -43
- data/lib/action_view/helpers/capture_helper.rb +21 -14
- data/lib/action_view/helpers/controller_helper.rb +16 -5
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +8 -6
- data/lib/action_view/helpers/date_helper.rb +288 -132
- data/lib/action_view/helpers/debug_helper.rb +9 -6
- data/lib/action_view/helpers/form_helper.rb +956 -173
- data/lib/action_view/helpers/form_options_helper.rb +178 -97
- data/lib/action_view/helpers/form_tag_helper.rb +220 -101
- data/lib/action_view/helpers/javascript_helper.rb +33 -19
- data/lib/action_view/helpers/number_helper.rb +88 -63
- data/lib/action_view/helpers/output_safety_helper.rb +38 -6
- data/lib/action_view/helpers/rendering_helper.rb +21 -10
- data/lib/action_view/helpers/sanitize_helper.rb +31 -32
- data/lib/action_view/helpers/tag_helper.rb +332 -71
- data/lib/action_view/helpers/tags/base.rb +123 -99
- data/lib/action_view/helpers/tags/check_box.rb +21 -20
- data/lib/action_view/helpers/tags/checkable.rb +4 -2
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
- data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
- data/lib/action_view/helpers/tags/collection_select.rb +5 -3
- data/lib/action_view/helpers/tags/color_field.rb +4 -3
- data/lib/action_view/helpers/tags/date_field.rb +3 -2
- data/lib/action_view/helpers/tags/date_select.rb +38 -37
- data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
- data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
- data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
- data/lib/action_view/helpers/tags/email_field.rb +2 -0
- data/lib/action_view/helpers/tags/file_field.rb +18 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
- data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
- data/lib/action_view/helpers/tags/label.rb +7 -2
- data/lib/action_view/helpers/tags/month_field.rb +3 -2
- data/lib/action_view/helpers/tags/number_field.rb +2 -0
- data/lib/action_view/helpers/tags/password_field.rb +3 -1
- data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
- data/lib/action_view/helpers/tags/radio_button.rb +7 -6
- data/lib/action_view/helpers/tags/range_field.rb +2 -0
- data/lib/action_view/helpers/tags/search_field.rb +14 -9
- data/lib/action_view/helpers/tags/select.rb +11 -10
- data/lib/action_view/helpers/tags/tel_field.rb +2 -0
- data/lib/action_view/helpers/tags/text_area.rb +4 -2
- data/lib/action_view/helpers/tags/text_field.rb +8 -8
- data/lib/action_view/helpers/tags/time_field.rb +12 -2
- data/lib/action_view/helpers/tags/time_select.rb +2 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
- data/lib/action_view/helpers/tags/translator.rb +15 -16
- data/lib/action_view/helpers/tags/url_field.rb +2 -0
- data/lib/action_view/helpers/tags/week_field.rb +3 -2
- data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
- data/lib/action_view/helpers/tags.rb +5 -2
- data/lib/action_view/helpers/text_helper.rb +80 -51
- data/lib/action_view/helpers/translation_helper.rb +120 -69
- data/lib/action_view/helpers/url_helper.rb +398 -171
- data/lib/action_view/helpers.rb +29 -27
- data/lib/action_view/layouts.rb +68 -63
- data/lib/action_view/log_subscriber.rb +77 -10
- data/lib/action_view/lookup_context.rb +137 -113
- data/lib/action_view/model_naming.rb +4 -2
- data/lib/action_view/path_set.rb +28 -32
- data/lib/action_view/railtie.rb +74 -13
- data/lib/action_view/record_identifier.rb +53 -26
- data/lib/action_view/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +152 -15
- data/lib/action_view/renderer/collection_renderer.rb +196 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
- data/lib/action_view/renderer/partial_renderer.rb +51 -333
- data/lib/action_view/renderer/renderer.rb +68 -11
- data/lib/action_view/renderer/streaming_template_renderer.rb +60 -56
- data/lib/action_view/renderer/template_renderer.rb +87 -74
- data/lib/action_view/rendering.rb +73 -47
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +35 -24
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +151 -41
- data/lib/action_view/template/handlers/builder.rb +12 -13
- data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
- data/lib/action_view/template/handlers/erb.rb +29 -89
- data/lib/action_view/template/handlers/html.rb +11 -0
- data/lib/action_view/template/handlers/raw.rb +4 -4
- data/lib/action_view/template/handlers.rb +14 -10
- data/lib/action_view/template/html.rb +12 -13
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +24 -0
- data/lib/action_view/template/resolver.rb +139 -300
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +10 -12
- data/lib/action_view/template/types.rb +28 -26
- data/lib/action_view/template.rb +123 -91
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +64 -0
- data/lib/action_view/test_case.rb +70 -53
- data/lib/action_view/testing/resolvers.rb +25 -35
- data/lib/action_view/unbound_template.rb +57 -0
- data/lib/action_view/version.rb +3 -1
- data/lib/action_view/view_paths.rb +73 -58
- data/lib/action_view.rb +16 -11
- data/lib/assets/compiled/rails-ujs.js +746 -0
- metadata +52 -32
- data/lib/action_view/helpers/record_tag_helper.rb +0 -108
- data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,22 +1,25 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cgi"
|
4
|
+
require "action_view/helpers/date_helper"
|
5
|
+
require "action_view/helpers/url_helper"
|
6
|
+
require "action_view/helpers/form_tag_helper"
|
7
|
+
require "action_view/helpers/active_model_helper"
|
8
|
+
require "action_view/model_naming"
|
9
|
+
require "action_view/record_identifier"
|
10
|
+
require "active_support/core_ext/module/attribute_accessors"
|
11
|
+
require "active_support/core_ext/hash/slice"
|
12
|
+
require "active_support/core_ext/string/output_safety"
|
13
|
+
require "active_support/core_ext/string/inflections"
|
11
14
|
|
12
15
|
module ActionView
|
13
16
|
# = Action View Form Helpers
|
14
|
-
module Helpers
|
17
|
+
module Helpers # :nodoc:
|
15
18
|
# Form helpers are designed to make working with resources much easier
|
16
19
|
# compared to using vanilla HTML.
|
17
20
|
#
|
18
21
|
# Typically, a form designed to create or update a resource reflects the
|
19
|
-
# identity of the resource in several ways: (i) the
|
22
|
+
# identity of the resource in several ways: (i) the URL that the form is
|
20
23
|
# sent to (the form element's +action+ attribute) should result in a request
|
21
24
|
# being routed to the appropriate controller action (with the appropriate <tt>:id</tt>
|
22
25
|
# parameter in the case of an existing resource), (ii) input fields should
|
@@ -66,9 +69,10 @@ module ActionView
|
|
66
69
|
#
|
67
70
|
# In particular, thanks to the conventions followed in the generated field names, the
|
68
71
|
# controller gets a nested hash <tt>params[:person]</tt> with the person attributes
|
69
|
-
# set in the form. That hash is ready to be passed to <tt>Person.
|
72
|
+
# set in the form. That hash is ready to be passed to <tt>Person.new</tt>:
|
70
73
|
#
|
71
|
-
#
|
74
|
+
# @person = Person.new(params[:person])
|
75
|
+
# if @person.save
|
72
76
|
# # success
|
73
77
|
# else
|
74
78
|
# # error handling
|
@@ -110,6 +114,9 @@ module ActionView
|
|
110
114
|
include FormTagHelper
|
111
115
|
include UrlHelper
|
112
116
|
include ModelNaming
|
117
|
+
include RecordIdentifier
|
118
|
+
|
119
|
+
attr_internal :default_form_builder
|
113
120
|
|
114
121
|
# Creates a form that allows the user to create or update the attributes
|
115
122
|
# of a specific model object.
|
@@ -138,6 +145,7 @@ module ActionView
|
|
138
145
|
# will get expanded to
|
139
146
|
#
|
140
147
|
# <%= text_field :person, :first_name %>
|
148
|
+
#
|
141
149
|
# which results in an HTML <tt><input></tt> tag whose +name+ attribute is
|
142
150
|
# <tt>person[first_name]</tt>. This means that when the form is submitted,
|
143
151
|
# the value entered by the user will be available in the controller as
|
@@ -158,7 +166,7 @@ module ActionView
|
|
158
166
|
# So for example you may use a named route directly. When the model is
|
159
167
|
# represented by a string or symbol, as in the example above, if the
|
160
168
|
# <tt>:url</tt> option is not specified, by default the form will be
|
161
|
-
# sent back to the current
|
169
|
+
# sent back to the current URL (We will describe below an alternative
|
162
170
|
# resource-oriented usage of +form_for+ in which the URL does not need
|
163
171
|
# to be specified explicitly).
|
164
172
|
# * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
|
@@ -177,8 +185,7 @@ module ActionView
|
|
177
185
|
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
178
186
|
# unnecessary unless you support browsers without JavaScript.
|
179
187
|
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive
|
180
|
-
# JavaScript drivers to control the submit behavior.
|
181
|
-
# behavior is an ajax submit.
|
188
|
+
# JavaScript drivers to control the submit behavior.
|
182
189
|
# * <tt>:enforce_utf8</tt> - If set to false, a hidden input with name
|
183
190
|
# utf8 is not output.
|
184
191
|
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
|
@@ -195,9 +202,9 @@ module ActionView
|
|
195
202
|
# <%= f.submit %>
|
196
203
|
# <% end %>
|
197
204
|
#
|
198
|
-
# This also works for the methods in
|
205
|
+
# This also works for the methods in FormOptionsHelper and DateHelper that
|
199
206
|
# are designed to work with an object as base, like
|
200
|
-
#
|
207
|
+
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
201
208
|
#
|
202
209
|
# === #form_for with a model object
|
203
210
|
#
|
@@ -275,6 +282,12 @@ module ActionView
|
|
275
282
|
# ...
|
276
283
|
# <% end %>
|
277
284
|
#
|
285
|
+
# You can omit the <tt>action</tt> attribute by passing <tt>url: false</tt>:
|
286
|
+
#
|
287
|
+
# <%= form_for(@post, url: false) do |f| %>
|
288
|
+
# ...
|
289
|
+
# <% end %>
|
290
|
+
#
|
278
291
|
# You can also set the answer format, like this:
|
279
292
|
#
|
280
293
|
# <%= form_for(@post, format: :json) do |f| %>
|
@@ -314,10 +327,8 @@ module ActionView
|
|
314
327
|
# remote: true
|
315
328
|
#
|
316
329
|
# in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
|
317
|
-
# behavior. The
|
318
|
-
#
|
319
|
-
# Even though it's using JavaScript to serialize the form elements, the form submission will work just like
|
320
|
-
# a regular submission as viewed by the receiving side (all elements available in <tt>params</tt>).
|
330
|
+
# behavior. The form submission will work just like a regular submission as viewed by the receiving
|
331
|
+
# side (all elements available in <tt>params</tt>).
|
321
332
|
#
|
322
333
|
# Example:
|
323
334
|
#
|
@@ -410,64 +421,361 @@ module ActionView
|
|
410
421
|
#
|
411
422
|
# To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
|
412
423
|
#
|
413
|
-
# <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
|
424
|
+
# <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f| %>
|
414
425
|
# ...
|
415
426
|
# <% end %>
|
416
427
|
#
|
417
428
|
# If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
|
418
429
|
#
|
419
|
-
# <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
|
430
|
+
# <%= form_for @invoice, url: external_url, authenticity_token: false do |f| %>
|
420
431
|
# ...
|
421
432
|
# <% end %>
|
422
433
|
def form_for(record, options = {}, &block)
|
423
434
|
raise ArgumentError, "Missing block" unless block_given?
|
424
|
-
html_options = options[:html] ||= {}
|
425
435
|
|
426
436
|
case record
|
427
437
|
when String, Symbol
|
438
|
+
model = nil
|
428
439
|
object_name = record
|
429
|
-
object = nil
|
430
440
|
else
|
431
|
-
|
441
|
+
model = record
|
442
|
+
object = _object_for_form_builder(record)
|
432
443
|
raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
|
433
444
|
object_name = options[:as] || model_name_from_record_or_class(object).param_key
|
434
|
-
apply_form_for_options!(
|
445
|
+
apply_form_for_options!(object, options)
|
435
446
|
end
|
436
447
|
|
437
|
-
|
438
|
-
html_options[:remote] = options.delete(:remote) if options.has_key?(:remote)
|
439
|
-
html_options[:method] = options.delete(:method) if options.has_key?(:method)
|
440
|
-
html_options[:enforce_utf8] = options.delete(:enforce_utf8) if options.has_key?(:enforce_utf8)
|
441
|
-
html_options[:authenticity_token] = options.delete(:authenticity_token)
|
448
|
+
remote = options.delete(:remote)
|
442
449
|
|
443
|
-
|
444
|
-
|
445
|
-
|
450
|
+
if remote && !embed_authenticity_token_in_remote_forms && options[:authenticity_token].blank?
|
451
|
+
options[:authenticity_token] = false
|
452
|
+
end
|
446
453
|
|
447
|
-
|
448
|
-
|
454
|
+
options[:model] = model
|
455
|
+
options[:scope] = object_name
|
456
|
+
options[:local] = !remote
|
457
|
+
options[:skip_default_ids] = false
|
458
|
+
options[:allow_method_names_outside_object] = options.fetch(:allow_method_names_outside_object, false)
|
459
|
+
|
460
|
+
form_with(**options, &block)
|
449
461
|
end
|
450
462
|
|
451
|
-
def apply_form_for_options!(
|
463
|
+
def apply_form_for_options!(object, options) # :nodoc:
|
452
464
|
object = convert_to_model(object)
|
453
465
|
|
454
466
|
as = options[:as]
|
455
467
|
namespace = options[:namespace]
|
456
|
-
action
|
468
|
+
action = object.respond_to?(:persisted?) && object.persisted? ? :edit : :new
|
469
|
+
options[:html] ||= {}
|
457
470
|
options[:html].reverse_merge!(
|
458
471
|
class: as ? "#{action}_#{as}" : dom_class(object, action),
|
459
472
|
id: (as ? [namespace, action, as] : [namespace, dom_id(object, action)]).compact.join("_").presence,
|
460
|
-
method: method
|
461
473
|
)
|
462
|
-
|
463
|
-
options[:url] ||= if options.key?(:format)
|
464
|
-
polymorphic_path(record, format: options.delete(:format))
|
465
|
-
else
|
466
|
-
polymorphic_path(record, {})
|
467
|
-
end
|
468
474
|
end
|
469
475
|
private :apply_form_for_options!
|
470
476
|
|
477
|
+
mattr_accessor :form_with_generates_remote_forms, default: true
|
478
|
+
|
479
|
+
mattr_accessor :form_with_generates_ids, default: false
|
480
|
+
|
481
|
+
mattr_accessor :multiple_file_field_include_hidden, default: false
|
482
|
+
|
483
|
+
# Creates a form tag based on mixing URLs, scopes, or models.
|
484
|
+
#
|
485
|
+
# # Using just a URL:
|
486
|
+
# <%= form_with url: posts_path do |form| %>
|
487
|
+
# <%= form.text_field :title %>
|
488
|
+
# <% end %>
|
489
|
+
# # =>
|
490
|
+
# <form action="/posts" method="post">
|
491
|
+
# <input type="text" name="title">
|
492
|
+
# </form>
|
493
|
+
#
|
494
|
+
# # With an intentionally empty URL:
|
495
|
+
# <%= form_with url: false do |form| %>
|
496
|
+
# <%= form.text_field :title %>
|
497
|
+
# <% end %>
|
498
|
+
# # =>
|
499
|
+
# <form method="post" data-remote="true">
|
500
|
+
# <input type="text" name="title">
|
501
|
+
# </form>
|
502
|
+
#
|
503
|
+
# # Adding a scope prefixes the input field names:
|
504
|
+
# <%= form_with scope: :post, url: posts_path do |form| %>
|
505
|
+
# <%= form.text_field :title %>
|
506
|
+
# <% end %>
|
507
|
+
# # =>
|
508
|
+
# <form action="/posts" method="post">
|
509
|
+
# <input type="text" name="post[title]">
|
510
|
+
# </form>
|
511
|
+
#
|
512
|
+
# # Using a model infers both the URL and scope:
|
513
|
+
# <%= form_with model: Post.new do |form| %>
|
514
|
+
# <%= form.text_field :title %>
|
515
|
+
# <% end %>
|
516
|
+
# # =>
|
517
|
+
# <form action="/posts" method="post">
|
518
|
+
# <input type="text" name="post[title]">
|
519
|
+
# </form>
|
520
|
+
#
|
521
|
+
# # An existing model makes an update form and fills out field values:
|
522
|
+
# <%= form_with model: Post.first do |form| %>
|
523
|
+
# <%= form.text_field :title %>
|
524
|
+
# <% end %>
|
525
|
+
# # =>
|
526
|
+
# <form action="/posts/1" method="post">
|
527
|
+
# <input type="hidden" name="_method" value="patch">
|
528
|
+
# <input type="text" name="post[title]" value="<the title of the post>">
|
529
|
+
# </form>
|
530
|
+
#
|
531
|
+
# # Though the fields don't have to correspond to model attributes:
|
532
|
+
# <%= form_with model: Cat.new do |form| %>
|
533
|
+
# <%= form.text_field :cats_dont_have_gills %>
|
534
|
+
# <%= form.text_field :but_in_forms_they_can %>
|
535
|
+
# <% end %>
|
536
|
+
# # =>
|
537
|
+
# <form action="/cats" method="post">
|
538
|
+
# <input type="text" name="cat[cats_dont_have_gills]">
|
539
|
+
# <input type="text" name="cat[but_in_forms_they_can]">
|
540
|
+
# </form>
|
541
|
+
#
|
542
|
+
# The parameters in the forms are accessible in controllers according to
|
543
|
+
# their name nesting. So inputs named +title+ and <tt>post[title]</tt> are
|
544
|
+
# accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
|
545
|
+
# respectively.
|
546
|
+
#
|
547
|
+
# For ease of comparison the examples above left out the submit button,
|
548
|
+
# as well as the auto generated hidden fields that enable UTF-8 support
|
549
|
+
# and adds an authenticity token needed for cross site request forgery
|
550
|
+
# protection.
|
551
|
+
#
|
552
|
+
# === Resource-oriented style
|
553
|
+
#
|
554
|
+
# In many of the examples just shown, the +:model+ passed to +form_with+
|
555
|
+
# is a _resource_. It corresponds to a set of RESTful routes, most likely
|
556
|
+
# defined via +resources+ in <tt>config/routes.rb</tt>.
|
557
|
+
#
|
558
|
+
# So when passing such a model record, Rails infers the URL and method.
|
559
|
+
#
|
560
|
+
# <%= form_with model: @post do |form| %>
|
561
|
+
# ...
|
562
|
+
# <% end %>
|
563
|
+
#
|
564
|
+
# is then equivalent to something like:
|
565
|
+
#
|
566
|
+
# <%= form_with scope: :post, url: post_path(@post), method: :patch do |form| %>
|
567
|
+
# ...
|
568
|
+
# <% end %>
|
569
|
+
#
|
570
|
+
# And for a new record
|
571
|
+
#
|
572
|
+
# <%= form_with model: Post.new do |form| %>
|
573
|
+
# ...
|
574
|
+
# <% end %>
|
575
|
+
#
|
576
|
+
# is equivalent to something like:
|
577
|
+
#
|
578
|
+
# <%= form_with scope: :post, url: posts_path do |form| %>
|
579
|
+
# ...
|
580
|
+
# <% end %>
|
581
|
+
#
|
582
|
+
# ==== +form_with+ options
|
583
|
+
#
|
584
|
+
# * <tt>:url</tt> - The URL the form submits to. Akin to values passed to
|
585
|
+
# +url_for+ or +link_to+. For example, you may use a named route
|
586
|
+
# directly. When a <tt>:scope</tt> is passed without a <tt>:url</tt> the
|
587
|
+
# form just submits to the current URL.
|
588
|
+
# * <tt>:method</tt> - The method to use when submitting the form, usually
|
589
|
+
# either "get" or "post". If "patch", "put", "delete", or another verb
|
590
|
+
# is used, a hidden input named <tt>_method</tt> is added to
|
591
|
+
# simulate the verb over post.
|
592
|
+
# * <tt>:format</tt> - The format of the route the form submits to.
|
593
|
+
# Useful when submitting to another resource type, like <tt>:json</tt>.
|
594
|
+
# Skipped if a <tt>:url</tt> is passed.
|
595
|
+
# * <tt>:scope</tt> - The scope to prefix input field names with and
|
596
|
+
# thereby how the submitted parameters are grouped in controllers.
|
597
|
+
# * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
|
598
|
+
# id attributes on form elements. The namespace attribute will be prefixed
|
599
|
+
# with underscore on the generated HTML id.
|
600
|
+
# * <tt>:model</tt> - A model object to infer the <tt>:url</tt> and
|
601
|
+
# <tt>:scope</tt> by, plus fill out input field values.
|
602
|
+
# So if a +title+ attribute is set to "Ahoy!" then a +title+ input
|
603
|
+
# field's value would be "Ahoy!".
|
604
|
+
# If the model is a new record a create form is generated, if an
|
605
|
+
# existing record, however, an update form is generated.
|
606
|
+
# Pass <tt>:scope</tt> or <tt>:url</tt> to override the defaults.
|
607
|
+
# E.g. turn <tt>params[:post]</tt> into <tt>params[:article]</tt>.
|
608
|
+
# * <tt>:authenticity_token</tt> - Authenticity token to use in the form.
|
609
|
+
# Override with a custom authenticity token or pass <tt>false</tt> to
|
610
|
+
# skip the authenticity token field altogether.
|
611
|
+
# Useful when submitting to an external resource like a payment gateway
|
612
|
+
# that might limit the valid fields.
|
613
|
+
# Remote forms may omit the embedded authenticity token by setting
|
614
|
+
# <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
|
615
|
+
# This is helpful when fragment-caching the form. Remote forms
|
616
|
+
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
617
|
+
# unnecessary unless you support browsers without JavaScript.
|
618
|
+
# * <tt>:local</tt> - Whether to use standard HTTP form submission.
|
619
|
+
# When set to <tt>true</tt>, the form is submitted via standard HTTP.
|
620
|
+
# When set to <tt>false</tt>, the form is submitted as a "remote form", which
|
621
|
+
# is handled by Rails UJS as an XHR. When unspecified, the behavior is derived
|
622
|
+
# from <tt>config.action_view.form_with_generates_remote_forms</tt> where the
|
623
|
+
# config's value is actually the inverse of what <tt>local</tt>'s value would be.
|
624
|
+
# As of Rails 6.1, that configuration option defaults to <tt>false</tt>
|
625
|
+
# (which has the equivalent effect of passing <tt>local: true</tt>).
|
626
|
+
# In previous versions of Rails, that configuration option defaults to
|
627
|
+
# <tt>true</tt> (the equivalent of passing <tt>local: false</tt>).
|
628
|
+
# * <tt>:skip_enforcing_utf8</tt> - If set to true, a hidden input with name
|
629
|
+
# utf8 is not output.
|
630
|
+
# * <tt>:builder</tt> - Override the object used to build the form.
|
631
|
+
# * <tt>:id</tt> - Optional HTML id attribute.
|
632
|
+
# * <tt>:class</tt> - Optional HTML class attribute.
|
633
|
+
# * <tt>:data</tt> - Optional HTML data attributes.
|
634
|
+
# * <tt>:html</tt> - Other optional HTML attributes for the form tag.
|
635
|
+
#
|
636
|
+
# === Examples
|
637
|
+
#
|
638
|
+
# When not passing a block, +form_with+ just generates an opening form tag.
|
639
|
+
#
|
640
|
+
# <%= form_with(model: @post, url: super_posts_path) %>
|
641
|
+
# <%= form_with(model: @post, scope: :article) %>
|
642
|
+
# <%= form_with(model: @post, format: :json) %>
|
643
|
+
# <%= form_with(model: @post, authenticity_token: false) %> # Disables the token.
|
644
|
+
#
|
645
|
+
# For namespaced routes, like +admin_post_url+:
|
646
|
+
#
|
647
|
+
# <%= form_with(model: [ :admin, @post ]) do |form| %>
|
648
|
+
# ...
|
649
|
+
# <% end %>
|
650
|
+
#
|
651
|
+
# If your resource has associations defined, for example, you want to add comments
|
652
|
+
# to the document given that the routes are set correctly:
|
653
|
+
#
|
654
|
+
# <%= form_with(model: [ @document, Comment.new ]) do |form| %>
|
655
|
+
# ...
|
656
|
+
# <% end %>
|
657
|
+
#
|
658
|
+
# Where <tt>@document = Document.find(params[:id])</tt>.
|
659
|
+
#
|
660
|
+
# === Mixing with other form helpers
|
661
|
+
#
|
662
|
+
# While +form_with+ uses a FormBuilder object it's possible to mix and
|
663
|
+
# match the stand-alone FormHelper methods and methods
|
664
|
+
# from FormTagHelper:
|
665
|
+
#
|
666
|
+
# <%= form_with scope: :person do |form| %>
|
667
|
+
# <%= form.text_field :first_name %>
|
668
|
+
# <%= form.text_field :last_name %>
|
669
|
+
#
|
670
|
+
# <%= text_area :person, :biography %>
|
671
|
+
# <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
|
672
|
+
#
|
673
|
+
# <%= form.submit %>
|
674
|
+
# <% end %>
|
675
|
+
#
|
676
|
+
# Same goes for the methods in FormOptionsHelper and DateHelper designed
|
677
|
+
# to work with an object as a base, like
|
678
|
+
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
679
|
+
#
|
680
|
+
# === Setting the method
|
681
|
+
#
|
682
|
+
# You can force the form to use the full array of HTTP verbs by setting
|
683
|
+
#
|
684
|
+
# method: (:get|:post|:patch|:put|:delete)
|
685
|
+
#
|
686
|
+
# in the options hash. If the verb is not GET or POST, which are natively
|
687
|
+
# supported by HTML forms, the form will be set to POST and a hidden input
|
688
|
+
# called _method will carry the intended verb for the server to interpret.
|
689
|
+
#
|
690
|
+
# === Setting HTML options
|
691
|
+
#
|
692
|
+
# You can set data attributes directly in a data hash, but HTML options
|
693
|
+
# besides id and class must be wrapped in an HTML key:
|
694
|
+
#
|
695
|
+
# <%= form_with(model: @post, data: { behavior: "autosave" }, html: { name: "go" }) do |form| %>
|
696
|
+
# ...
|
697
|
+
# <% end %>
|
698
|
+
#
|
699
|
+
# generates
|
700
|
+
#
|
701
|
+
# <form action="/posts/123" method="post" data-behavior="autosave" name="go">
|
702
|
+
# <input name="_method" type="hidden" value="patch" />
|
703
|
+
# ...
|
704
|
+
# </form>
|
705
|
+
#
|
706
|
+
# === Removing hidden model id's
|
707
|
+
#
|
708
|
+
# The +form_with+ method automatically includes the model id as a hidden field in the form.
|
709
|
+
# This is used to maintain the correlation between the form data and its associated model.
|
710
|
+
# Some ORM systems do not use IDs on nested models so in this case you want to be able
|
711
|
+
# to disable the hidden id.
|
712
|
+
#
|
713
|
+
# In the following example the Post model has many Comments stored within it in a NoSQL database,
|
714
|
+
# thus there is no primary key for comments.
|
715
|
+
#
|
716
|
+
# <%= form_with(model: @post) do |form| %>
|
717
|
+
# <%= form.fields(:comments, skip_id: true) do |fields| %>
|
718
|
+
# ...
|
719
|
+
# <% end %>
|
720
|
+
# <% end %>
|
721
|
+
#
|
722
|
+
# === Customized form builders
|
723
|
+
#
|
724
|
+
# You can also build forms using a customized FormBuilder class. Subclass
|
725
|
+
# FormBuilder and override or define some more helpers, then use your
|
726
|
+
# custom builder. For example, let's say you made a helper to
|
727
|
+
# automatically add labels to form inputs.
|
728
|
+
#
|
729
|
+
# <%= form_with model: @person, url: { action: "create" }, builder: LabellingFormBuilder do |form| %>
|
730
|
+
# <%= form.text_field :first_name %>
|
731
|
+
# <%= form.text_field :last_name %>
|
732
|
+
# <%= form.text_area :biography %>
|
733
|
+
# <%= form.check_box :admin %>
|
734
|
+
# <%= form.submit %>
|
735
|
+
# <% end %>
|
736
|
+
#
|
737
|
+
# In this case, if you use:
|
738
|
+
#
|
739
|
+
# <%= render form %>
|
740
|
+
#
|
741
|
+
# The rendered template is <tt>people/_labelling_form</tt> and the local
|
742
|
+
# variable referencing the form builder is called
|
743
|
+
# <tt>labelling_form</tt>.
|
744
|
+
#
|
745
|
+
# The custom FormBuilder class is automatically merged with the options
|
746
|
+
# of a nested +fields+ call, unless it's explicitly set.
|
747
|
+
#
|
748
|
+
# In many cases you will want to wrap the above in another helper, so you
|
749
|
+
# could do something like the following:
|
750
|
+
#
|
751
|
+
# def labelled_form_with(**options, &block)
|
752
|
+
# form_with(**options.merge(builder: LabellingFormBuilder), &block)
|
753
|
+
# end
|
754
|
+
def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
|
755
|
+
options = { allow_method_names_outside_object: true, skip_default_ids: !form_with_generates_ids }.merge!(options)
|
756
|
+
|
757
|
+
if model
|
758
|
+
if url != false
|
759
|
+
url ||= polymorphic_path(model, format: format)
|
760
|
+
end
|
761
|
+
|
762
|
+
model = _object_for_form_builder(model)
|
763
|
+
scope ||= model_name_from_record_or_class(model).param_key
|
764
|
+
end
|
765
|
+
|
766
|
+
if block_given?
|
767
|
+
builder = instantiate_builder(scope, model, options)
|
768
|
+
output = capture(builder, &block)
|
769
|
+
options[:multipart] ||= builder.multipart?
|
770
|
+
|
771
|
+
html_options = html_options_for_form_with(url, model, **options)
|
772
|
+
form_tag_with_body(html_options, output)
|
773
|
+
else
|
774
|
+
html_options = html_options_for_form_with(url, model, **options)
|
775
|
+
form_tag_html(html_options)
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
471
779
|
# Creates a scope around a specific model object like form_for, but
|
472
780
|
# doesn't create the form tags themselves. This makes fields_for suitable
|
473
781
|
# for specifying additional model objects in the same form.
|
@@ -525,9 +833,9 @@ module ActionView
|
|
525
833
|
# _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
|
526
834
|
# of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
|
527
835
|
#
|
528
|
-
# Note: This also works for the methods in
|
836
|
+
# Note: This also works for the methods in FormOptionsHelper and
|
529
837
|
# DateHelper that are designed to work with an object as base, like
|
530
|
-
#
|
838
|
+
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
531
839
|
#
|
532
840
|
# === Nested Attributes Examples
|
533
841
|
#
|
@@ -593,7 +901,7 @@ module ActionView
|
|
593
901
|
#
|
594
902
|
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
|
595
903
|
# with a value that evaluates to +true+, you will destroy the associated
|
596
|
-
# model (
|
904
|
+
# model (e.g. 1, '1', true, or 'true'):
|
597
905
|
#
|
598
906
|
# <%= form_for @person do |person_form| %>
|
599
907
|
# ...
|
@@ -682,7 +990,7 @@ module ActionView
|
|
682
990
|
# This will allow you to specify which models to destroy in the
|
683
991
|
# attributes hash by adding a form element for the <tt>_destroy</tt>
|
684
992
|
# parameter with a value that evaluates to +true+
|
685
|
-
# (
|
993
|
+
# (e.g. 1, '1', true, or 'true'):
|
686
994
|
#
|
687
995
|
# <%= form_for @person do |person_form| %>
|
688
996
|
# ...
|
@@ -710,7 +1018,65 @@ module ActionView
|
|
710
1018
|
# hidden field is not needed and you can pass <tt>include_id: false</tt>
|
711
1019
|
# to prevent fields_for from rendering it automatically.
|
712
1020
|
def fields_for(record_name, record_object = nil, options = {}, &block)
|
713
|
-
|
1021
|
+
options = { model: record_object, allow_method_names_outside_object: false, skip_default_ids: false }.merge!(options)
|
1022
|
+
|
1023
|
+
fields(record_name, **options, &block)
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
# Scopes input fields with either an explicit scope or model.
|
1027
|
+
# Like +form_with+ does with <tt>:scope</tt> or <tt>:model</tt>,
|
1028
|
+
# except it doesn't output the form tags.
|
1029
|
+
#
|
1030
|
+
# # Using a scope prefixes the input field names:
|
1031
|
+
# <%= fields :comment do |fields| %>
|
1032
|
+
# <%= fields.text_field :body %>
|
1033
|
+
# <% end %>
|
1034
|
+
# # => <input type="text" name="comment[body]">
|
1035
|
+
#
|
1036
|
+
# # Using a model infers the scope and assigns field values:
|
1037
|
+
# <%= fields model: Comment.new(body: "full bodied") do |fields| %>
|
1038
|
+
# <%= fields.text_field :body %>
|
1039
|
+
# <% end %>
|
1040
|
+
# # => <input type="text" name="comment[body]" value="full bodied">
|
1041
|
+
#
|
1042
|
+
# # Using +fields+ with +form_with+:
|
1043
|
+
# <%= form_with model: @post do |form| %>
|
1044
|
+
# <%= form.text_field :title %>
|
1045
|
+
#
|
1046
|
+
# <%= form.fields :comment do |fields| %>
|
1047
|
+
# <%= fields.text_field :body %>
|
1048
|
+
# <% end %>
|
1049
|
+
# <% end %>
|
1050
|
+
#
|
1051
|
+
# Much like +form_with+ a FormBuilder instance associated with the scope
|
1052
|
+
# or model is yielded, so any generated field names are prefixed with
|
1053
|
+
# either the passed scope or the scope inferred from the <tt>:model</tt>.
|
1054
|
+
#
|
1055
|
+
# === Mixing with other form helpers
|
1056
|
+
#
|
1057
|
+
# While +form_with+ uses a FormBuilder object it's possible to mix and
|
1058
|
+
# match the stand-alone FormHelper methods and methods
|
1059
|
+
# from FormTagHelper:
|
1060
|
+
#
|
1061
|
+
# <%= fields model: @comment do |fields| %>
|
1062
|
+
# <%= fields.text_field :body %>
|
1063
|
+
#
|
1064
|
+
# <%= text_area :commenter, :biography %>
|
1065
|
+
# <%= check_box_tag "comment[all_caps]", "1", @comment.commenter.hulk_mode? %>
|
1066
|
+
# <% end %>
|
1067
|
+
#
|
1068
|
+
# Same goes for the methods in FormOptionsHelper and DateHelper designed
|
1069
|
+
# to work with an object as a base, like
|
1070
|
+
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
1071
|
+
def fields(scope = nil, model: nil, **options, &block)
|
1072
|
+
options = { allow_method_names_outside_object: true, skip_default_ids: !form_with_generates_ids }.merge!(options)
|
1073
|
+
|
1074
|
+
if model
|
1075
|
+
model = _object_for_form_builder(model)
|
1076
|
+
scope ||= model_name_from_record_or_class(model).param_key
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
builder = instantiate_builder(scope, model, options)
|
714
1080
|
capture(builder, &block)
|
715
1081
|
end
|
716
1082
|
|
@@ -758,8 +1124,18 @@ module ActionView
|
|
758
1124
|
# label(:post, :privacy, "Public Post", value: "public")
|
759
1125
|
# # => <label for="post_privacy_public">Public Post</label>
|
760
1126
|
#
|
1127
|
+
# label(:post, :cost) do |translation|
|
1128
|
+
# content_tag(:span, translation, class: "cost_label")
|
1129
|
+
# end
|
1130
|
+
# # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
|
1131
|
+
#
|
1132
|
+
# label(:post, :cost) do |builder|
|
1133
|
+
# content_tag(:span, builder.translation, class: "cost_label")
|
1134
|
+
# end
|
1135
|
+
# # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
|
1136
|
+
#
|
761
1137
|
# label(:post, :terms) do
|
762
|
-
# 'Accept <a href="/terms">Terms</a>.'
|
1138
|
+
# raw('Accept <a href="/terms">Terms</a>.')
|
763
1139
|
# end
|
764
1140
|
# # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
|
765
1141
|
def label(object_name, method, content_or_options = nil, options = nil, &block)
|
@@ -778,6 +1154,9 @@ module ActionView
|
|
778
1154
|
# text_field(:post, :title, class: "create_input")
|
779
1155
|
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
|
780
1156
|
#
|
1157
|
+
# text_field(:post, :title, maxlength: 30, class: "title_input")
|
1158
|
+
# # => <input type="text" id="post_title" name="post[title]" maxlength="30" size="30" value="#{@post.title}" class="title_input" />
|
1159
|
+
#
|
781
1160
|
# text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }")
|
782
1161
|
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }"/>
|
783
1162
|
#
|
@@ -837,14 +1216,15 @@ module ActionView
|
|
837
1216
|
# * Creates standard HTML attributes for the tag.
|
838
1217
|
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
|
839
1218
|
# * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
|
1219
|
+
# * <tt>:include_hidden</tt> - When <tt>multiple: true</tt> and <tt>include_hidden: true</tt>, the field will be prefixed with an <tt><input type="hidden"></tt> field with an empty value to support submitting an empty collection of files.
|
840
1220
|
# * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
|
841
1221
|
#
|
842
1222
|
# ==== Examples
|
843
1223
|
# file_field(:user, :avatar)
|
844
1224
|
# # => <input type="file" id="user_avatar" name="user[avatar]" />
|
845
1225
|
#
|
846
|
-
# file_field(:post, :image, :
|
847
|
-
# # => <input type="file" id="post_image" name="post[image]" multiple="
|
1226
|
+
# file_field(:post, :image, multiple: true)
|
1227
|
+
# # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
|
848
1228
|
#
|
849
1229
|
# file_field(:post, :attached, accept: 'text/html')
|
850
1230
|
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
|
@@ -855,7 +1235,9 @@ module ActionView
|
|
855
1235
|
# file_field(:attachment, :file, class: 'file_input')
|
856
1236
|
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
|
857
1237
|
def file_field(object_name, method, options = {})
|
858
|
-
|
1238
|
+
options = { include_hidden: multiple_file_field_include_hidden }.merge!(options)
|
1239
|
+
|
1240
|
+
Tags::FileField.new(object_name, method, self, convert_direct_upload_option_to_url(options.dup)).render
|
859
1241
|
end
|
860
1242
|
|
861
1243
|
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
|
@@ -892,6 +1274,12 @@ module ActionView
|
|
892
1274
|
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
|
893
1275
|
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
|
894
1276
|
#
|
1277
|
+
# ==== Options
|
1278
|
+
#
|
1279
|
+
# * Any standard HTML attributes for the tag can be passed in, for example +:class+.
|
1280
|
+
# * <tt>:checked</tt> - +true+ or +false+ forces the state of the checkbox to be checked or not.
|
1281
|
+
# * <tt>:include_hidden</tt> - If set to false, the auxiliary hidden field described below will not be generated.
|
1282
|
+
#
|
895
1283
|
# ==== Gotcha
|
896
1284
|
#
|
897
1285
|
# The HTML specification says unchecked check boxes are not successful, and
|
@@ -905,7 +1293,7 @@ module ActionView
|
|
905
1293
|
# wouldn't update the flag.
|
906
1294
|
#
|
907
1295
|
# To prevent this the helper generates an auxiliary hidden field before
|
908
|
-
#
|
1296
|
+
# every check box. The hidden field has the same name and its
|
909
1297
|
# attributes mimic an unchecked check box.
|
910
1298
|
#
|
911
1299
|
# This way, the client either sends only the hidden field (representing
|
@@ -929,6 +1317,8 @@ module ActionView
|
|
929
1317
|
# In that case it is preferable to either use +check_box_tag+ or to use
|
930
1318
|
# hashes instead of arrays.
|
931
1319
|
#
|
1320
|
+
# ==== Examples
|
1321
|
+
#
|
932
1322
|
# # Let's say that @post.validated? is 1:
|
933
1323
|
# check_box("post", "validated")
|
934
1324
|
# # => <input name="post[validated]" type="hidden" value="0" />
|
@@ -959,6 +1349,7 @@ module ActionView
|
|
959
1349
|
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
|
960
1350
|
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
|
961
1351
|
#
|
1352
|
+
# # Let's say that @user.receive_newsletter returns "no":
|
962
1353
|
# radio_button("user", "receive_newsletter", "yes")
|
963
1354
|
# radio_button("user", "receive_newsletter", "no")
|
964
1355
|
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
|
@@ -1014,7 +1405,7 @@ module ActionView
|
|
1014
1405
|
# date_field("user", "born_on")
|
1015
1406
|
# # => <input id="user_born_on" name="user[born_on]" type="date" />
|
1016
1407
|
#
|
1017
|
-
# The default value is generated by trying to call "
|
1408
|
+
# The default value is generated by trying to call +strftime+ with "%Y-%m-%d"
|
1018
1409
|
# on the object's value, which makes it behave as expected for instances
|
1019
1410
|
# of DateTime and ActiveSupport::TimeWithZone. You can still override that
|
1020
1411
|
# by passing the "value" option explicitly, e.g.
|
@@ -1042,8 +1433,9 @@ module ActionView
|
|
1042
1433
|
# Returns a text_field of type "time".
|
1043
1434
|
#
|
1044
1435
|
# The default value is generated by trying to call +strftime+ with "%T.%L"
|
1045
|
-
# on the
|
1046
|
-
# by
|
1436
|
+
# on the object's value. If you pass <tt>include_seconds: false</tt>, it will be
|
1437
|
+
# formatted by trying to call +strftime+ with "%H:%M" on the object's value.
|
1438
|
+
# It is also possible to override this by passing the "value" option.
|
1047
1439
|
#
|
1048
1440
|
# === Options
|
1049
1441
|
# * Accepts same options as time_field_tag
|
@@ -1064,42 +1456,19 @@ module ActionView
|
|
1064
1456
|
# time_field("task", "started_at", min: "01:00:00")
|
1065
1457
|
# # => <input id="task_started_at" name="task[started_at]" type="time" min="01:00:00.000" />
|
1066
1458
|
#
|
1459
|
+
# By default, provided times will be formatted including seconds. You can render just the hour
|
1460
|
+
# and minute by passing <tt>include_seconds: false</tt>. Some browsers will render a simpler UI
|
1461
|
+
# if you exclude seconds in the timestamp format.
|
1462
|
+
#
|
1463
|
+
# time_field("task", "started_at", value: Time.now, include_seconds: false)
|
1464
|
+
# # => <input id="task_started_at" name="task[started_at]" type="time" value="01:00" />
|
1067
1465
|
def time_field(object_name, method, options = {})
|
1068
1466
|
Tags::TimeField.new(object_name, method, self, options).render
|
1069
1467
|
end
|
1070
1468
|
|
1071
|
-
# Returns a text_field of type "datetime".
|
1072
|
-
#
|
1073
|
-
# datetime_field("user", "born_on")
|
1074
|
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" />
|
1075
|
-
#
|
1076
|
-
# The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T.%L%z"
|
1077
|
-
# on the object's value, which makes it behave as expected for instances
|
1078
|
-
# of DateTime and ActiveSupport::TimeWithZone.
|
1079
|
-
#
|
1080
|
-
# @user.born_on = Date.new(1984, 1, 12)
|
1081
|
-
# datetime_field("user", "born_on")
|
1082
|
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" />
|
1083
|
-
#
|
1084
|
-
# You can create values for the "min" and "max" attributes by passing
|
1085
|
-
# instances of Date or Time to the options hash.
|
1086
|
-
#
|
1087
|
-
# datetime_field("user", "born_on", min: Date.today)
|
1088
|
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" />
|
1089
|
-
#
|
1090
|
-
# Alternatively, you can pass a String formatted as an ISO8601 datetime
|
1091
|
-
# with UTC offset as the values for "min" and "max."
|
1092
|
-
#
|
1093
|
-
# datetime_field("user", "born_on", min: "2014-05-20T00:00:00+0000")
|
1094
|
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" />
|
1095
|
-
#
|
1096
|
-
def datetime_field(object_name, method, options = {})
|
1097
|
-
Tags::DatetimeField.new(object_name, method, self, options).render
|
1098
|
-
end
|
1099
|
-
|
1100
1469
|
# Returns a text_field of type "datetime-local".
|
1101
1470
|
#
|
1102
|
-
#
|
1471
|
+
# datetime_field("user", "born_on")
|
1103
1472
|
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" />
|
1104
1473
|
#
|
1105
1474
|
# The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T"
|
@@ -1107,25 +1476,27 @@ module ActionView
|
|
1107
1476
|
# of DateTime and ActiveSupport::TimeWithZone.
|
1108
1477
|
#
|
1109
1478
|
# @user.born_on = Date.new(1984, 1, 12)
|
1110
|
-
#
|
1479
|
+
# datetime_field("user", "born_on")
|
1111
1480
|
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" />
|
1112
1481
|
#
|
1113
1482
|
# You can create values for the "min" and "max" attributes by passing
|
1114
1483
|
# instances of Date or Time to the options hash.
|
1115
1484
|
#
|
1116
|
-
#
|
1485
|
+
# datetime_field("user", "born_on", min: Date.today)
|
1117
1486
|
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" />
|
1118
1487
|
#
|
1119
1488
|
# Alternatively, you can pass a String formatted as an ISO8601 datetime as
|
1120
1489
|
# the values for "min" and "max."
|
1121
1490
|
#
|
1122
|
-
#
|
1491
|
+
# datetime_field("user", "born_on", min: "2014-05-20T00:00:00")
|
1123
1492
|
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" />
|
1124
1493
|
#
|
1125
|
-
def
|
1494
|
+
def datetime_field(object_name, method, options = {})
|
1126
1495
|
Tags::DatetimeLocalField.new(object_name, method, self, options).render
|
1127
1496
|
end
|
1128
1497
|
|
1498
|
+
alias datetime_local_field datetime_field
|
1499
|
+
|
1129
1500
|
# Returns a text_field of type "month".
|
1130
1501
|
#
|
1131
1502
|
# month_field("user", "born_on")
|
@@ -1194,7 +1565,25 @@ module ActionView
|
|
1194
1565
|
Tags::RangeField.new(object_name, method, self, options).render
|
1195
1566
|
end
|
1196
1567
|
|
1568
|
+
def _object_for_form_builder(object) # :nodoc:
|
1569
|
+
object.is_a?(Array) ? object.last : object
|
1570
|
+
end
|
1571
|
+
|
1197
1572
|
private
|
1573
|
+
def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: !form_with_generates_remote_forms,
|
1574
|
+
skip_enforcing_utf8: nil, **options)
|
1575
|
+
html_options = options.slice(:id, :class, :multipart, :method, :data, :authenticity_token).merge!(html)
|
1576
|
+
html_options[:remote] = html.delete(:remote) || !local
|
1577
|
+
html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
|
1578
|
+
if skip_enforcing_utf8.nil?
|
1579
|
+
if options.key?(:enforce_utf8)
|
1580
|
+
html_options[:enforce_utf8] = options[:enforce_utf8]
|
1581
|
+
end
|
1582
|
+
else
|
1583
|
+
html_options[:enforce_utf8] = !skip_enforcing_utf8
|
1584
|
+
end
|
1585
|
+
html_options_for_form(url_for_options.nil? ? {} : url_for_options, html_options)
|
1586
|
+
end
|
1198
1587
|
|
1199
1588
|
def instantiate_builder(record_name, record_object, options)
|
1200
1589
|
case record_name
|
@@ -1203,7 +1592,7 @@ module ActionView
|
|
1203
1592
|
object_name = record_name
|
1204
1593
|
else
|
1205
1594
|
object = record_name
|
1206
|
-
object_name = model_name_from_record_or_class(object).param_key
|
1595
|
+
object_name = model_name_from_record_or_class(object).param_key if object
|
1207
1596
|
end
|
1208
1597
|
|
1209
1598
|
builder = options[:builder] || default_form_builder_class
|
@@ -1211,7 +1600,7 @@ module ActionView
|
|
1211
1600
|
end
|
1212
1601
|
|
1213
1602
|
def default_form_builder_class
|
1214
|
-
builder = ActionView::Base.default_form_builder
|
1603
|
+
builder = default_form_builder || ActionView::Base.default_form_builder
|
1215
1604
|
builder.respond_to?(:constantize) ? builder.constantize : builder
|
1216
1605
|
end
|
1217
1606
|
end
|
@@ -1229,7 +1618,7 @@ module ActionView
|
|
1229
1618
|
# In the above block, a +FormBuilder+ object is yielded as the
|
1230
1619
|
# +person_form+ variable. This allows you to generate the +text_field+
|
1231
1620
|
# and +check_box+ fields by specifying their eponymous methods, which
|
1232
|
-
# modify the underlying template and associates the
|
1621
|
+
# modify the underlying template and associates the <tt>@person</tt> model object
|
1233
1622
|
# with the form.
|
1234
1623
|
#
|
1235
1624
|
# The +FormBuilder+ object can be thought of as serving as a proxy for the
|
@@ -1268,14 +1657,15 @@ module ActionView
|
|
1268
1657
|
include ModelNaming
|
1269
1658
|
|
1270
1659
|
# The methods which wrap a form helper call.
|
1271
|
-
class_attribute :field_helpers
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1660
|
+
class_attribute :field_helpers, default: [
|
1661
|
+
:fields_for, :fields, :label, :text_field, :password_field,
|
1662
|
+
:hidden_field, :file_field, :text_area, :check_box,
|
1663
|
+
:radio_button, :color_field, :search_field,
|
1664
|
+
:telephone_field, :phone_field, :date_field,
|
1665
|
+
:time_field, :datetime_field, :datetime_local_field,
|
1666
|
+
:month_field, :week_field, :url_field, :email_field,
|
1667
|
+
:number_field, :range_field
|
1668
|
+
]
|
1279
1669
|
|
1280
1670
|
attr_accessor :object_name, :object, :options
|
1281
1671
|
|
@@ -1291,7 +1681,7 @@ module ActionView
|
|
1291
1681
|
end
|
1292
1682
|
|
1293
1683
|
def self._to_partial_path
|
1294
|
-
@_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/,
|
1684
|
+
@_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, "")
|
1295
1685
|
end
|
1296
1686
|
|
1297
1687
|
def to_partial_path
|
@@ -1305,23 +1695,312 @@ module ActionView
|
|
1305
1695
|
def initialize(object_name, object, template, options)
|
1306
1696
|
@nested_child_index = {}
|
1307
1697
|
@object_name, @object, @template, @options = object_name, object, template, options
|
1308
|
-
@default_options = @options ? @options.slice(:index, :namespace) : {}
|
1309
|
-
|
1310
|
-
|
1698
|
+
@default_options = @options ? @options.slice(:index, :namespace, :skip_default_ids, :allow_method_names_outside_object) : {}
|
1699
|
+
@default_html_options = @default_options.except(:skip_default_ids, :allow_method_names_outside_object)
|
1700
|
+
|
1701
|
+
convert_to_legacy_options(@options)
|
1702
|
+
|
1703
|
+
if @object_name&.end_with?("[]")
|
1704
|
+
if (object ||= @template.instance_variable_get("@#{@object_name[0..-3]}")) && object.respond_to?(:to_param)
|
1311
1705
|
@auto_index = object.to_param
|
1312
1706
|
else
|
1313
1707
|
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
|
1314
1708
|
end
|
1315
1709
|
end
|
1710
|
+
|
1316
1711
|
@multipart = nil
|
1317
1712
|
@index = options[:index] || options[:child_index]
|
1318
1713
|
end
|
1319
1714
|
|
1320
|
-
|
1715
|
+
# Generate an HTML <tt>id</tt> attribute value.
|
1716
|
+
#
|
1717
|
+
# return the <tt><form></tt> element's <tt>id</tt> attribute.
|
1718
|
+
#
|
1719
|
+
# <%= form_for @post do |f| %>
|
1720
|
+
# <%# ... %>
|
1721
|
+
#
|
1722
|
+
# <% content_for :sticky_footer do %>
|
1723
|
+
# <%= form.button(form: f.id) %>
|
1724
|
+
# <% end %>
|
1725
|
+
# <% end %>
|
1726
|
+
#
|
1727
|
+
# In the example above, the <tt>:sticky_footer</tt> content area will
|
1728
|
+
# exist outside of the <tt><form></tt> element. By declaring the
|
1729
|
+
# <tt>form</tt> HTML attribute, we hint to the browser that the generated
|
1730
|
+
# <tt><button></tt> element should be treated as the <tt><form></tt>
|
1731
|
+
# element's submit button, regardless of where it exists in the DOM.
|
1732
|
+
def id
|
1733
|
+
options.dig(:html, :id)
|
1734
|
+
end
|
1735
|
+
|
1736
|
+
# Generate an HTML <tt>id</tt> attribute value for the given field
|
1737
|
+
#
|
1738
|
+
# Return the value generated by the <tt>FormBuilder</tt> for the given
|
1739
|
+
# attribute name.
|
1740
|
+
#
|
1741
|
+
# <%= form_for @post do |f| %>
|
1742
|
+
# <%= f.label :title %>
|
1743
|
+
# <%= f.text_field :title, aria: { describedby: f.field_id(:title, :error) } %>
|
1744
|
+
# <%= tag.span("is blank", id: f.field_id(:title, :error) %>
|
1745
|
+
# <% end %>
|
1746
|
+
#
|
1747
|
+
# In the example above, the <tt><input type="text"></tt> element built by
|
1748
|
+
# the call to <tt>FormBuilder#text_field</tt> declares an
|
1749
|
+
# <tt>aria-describedby</tt> attribute referencing the <tt><span></tt>
|
1750
|
+
# element, sharing a common <tt>id</tt> root (<tt>post_title</tt>, in this
|
1751
|
+
# case).
|
1752
|
+
def field_id(method, *suffixes, namespace: @options[:namespace], index: @index)
|
1753
|
+
@template.field_id(@object_name, method, *suffixes, namespace: namespace, index: index)
|
1754
|
+
end
|
1755
|
+
|
1756
|
+
# Generate an HTML <tt>name</tt> attribute value for the given name and
|
1757
|
+
# field combination
|
1758
|
+
#
|
1759
|
+
# Return the value generated by the <tt>FormBuilder</tt> for the given
|
1760
|
+
# attribute name.
|
1761
|
+
#
|
1762
|
+
# <%= form_for @post do |f| %>
|
1763
|
+
# <%= f.text_field :title, name: f.field_name(:title, :subtitle) %>
|
1764
|
+
# <%# => <input type="text" name="post[title][subtitle]">
|
1765
|
+
# <% end %>
|
1766
|
+
#
|
1767
|
+
# <%= form_for @post do |f| %>
|
1768
|
+
# <%= f.field_tag :tag, name: f.field_name(:tag, multiple: true) %>
|
1769
|
+
# <%# => <input type="text" name="post[tag][]">
|
1770
|
+
# <% end %>
|
1771
|
+
#
|
1772
|
+
def field_name(method, *methods, multiple: false, index: @index)
|
1773
|
+
object_name = @options.fetch(:as) { @object_name }
|
1774
|
+
|
1775
|
+
@template.field_name(object_name, method, *methods, index: index, multiple: multiple)
|
1776
|
+
end
|
1777
|
+
|
1778
|
+
##
|
1779
|
+
# :method: text_field
|
1780
|
+
#
|
1781
|
+
# :call-seq: text_field(method, options = {})
|
1782
|
+
#
|
1783
|
+
# Wraps ActionView::Helpers::FormHelper#text_field for form builders:
|
1784
|
+
#
|
1785
|
+
# <%= form_with model: @user do |f| %>
|
1786
|
+
# <%= f.text_field :name %>
|
1787
|
+
# <% end %>
|
1788
|
+
#
|
1789
|
+
# Please refer to the documentation of the base helper for details.
|
1790
|
+
|
1791
|
+
##
|
1792
|
+
# :method: password_field
|
1793
|
+
#
|
1794
|
+
# :call-seq: password_field(method, options = {})
|
1795
|
+
#
|
1796
|
+
# Wraps ActionView::Helpers::FormHelper#password_field for form builders:
|
1797
|
+
#
|
1798
|
+
# <%= form_with model: @user do |f| %>
|
1799
|
+
# <%= f.password_field :password %>
|
1800
|
+
# <% end %>
|
1801
|
+
#
|
1802
|
+
# Please refer to the documentation of the base helper for details.
|
1803
|
+
|
1804
|
+
##
|
1805
|
+
# :method: text_area
|
1806
|
+
#
|
1807
|
+
# :call-seq: text_area(method, options = {})
|
1808
|
+
#
|
1809
|
+
# Wraps ActionView::Helpers::FormHelper#text_area for form builders:
|
1810
|
+
#
|
1811
|
+
# <%= form_with model: @user do |f| %>
|
1812
|
+
# <%= f.text_area :detail %>
|
1813
|
+
# <% end %>
|
1814
|
+
#
|
1815
|
+
# Please refer to the documentation of the base helper for details.
|
1816
|
+
|
1817
|
+
##
|
1818
|
+
# :method: color_field
|
1819
|
+
#
|
1820
|
+
# :call-seq: color_field(method, options = {})
|
1821
|
+
#
|
1822
|
+
# Wraps ActionView::Helpers::FormHelper#color_field for form builders:
|
1823
|
+
#
|
1824
|
+
# <%= form_with model: @user do |f| %>
|
1825
|
+
# <%= f.color_field :favorite_color %>
|
1826
|
+
# <% end %>
|
1827
|
+
#
|
1828
|
+
# Please refer to the documentation of the base helper for details.
|
1829
|
+
|
1830
|
+
##
|
1831
|
+
# :method: search_field
|
1832
|
+
#
|
1833
|
+
# :call-seq: search_field(method, options = {})
|
1834
|
+
#
|
1835
|
+
# Wraps ActionView::Helpers::FormHelper#search_field for form builders:
|
1836
|
+
#
|
1837
|
+
# <%= form_with model: @user do |f| %>
|
1838
|
+
# <%= f.search_field :name %>
|
1839
|
+
# <% end %>
|
1840
|
+
#
|
1841
|
+
# Please refer to the documentation of the base helper for details.
|
1842
|
+
|
1843
|
+
##
|
1844
|
+
# :method: telephone_field
|
1845
|
+
#
|
1846
|
+
# :call-seq: telephone_field(method, options = {})
|
1847
|
+
#
|
1848
|
+
# Wraps ActionView::Helpers::FormHelper#telephone_field for form builders:
|
1849
|
+
#
|
1850
|
+
# <%= form_with model: @user do |f| %>
|
1851
|
+
# <%= f.telephone_field :phone %>
|
1852
|
+
# <% end %>
|
1853
|
+
#
|
1854
|
+
# Please refer to the documentation of the base helper for details.
|
1855
|
+
|
1856
|
+
##
|
1857
|
+
# :method: phone_field
|
1858
|
+
#
|
1859
|
+
# :call-seq: phone_field(method, options = {})
|
1860
|
+
#
|
1861
|
+
# Wraps ActionView::Helpers::FormHelper#phone_field for form builders:
|
1862
|
+
#
|
1863
|
+
# <%= form_with model: @user do |f| %>
|
1864
|
+
# <%= f.phone_field :phone %>
|
1865
|
+
# <% end %>
|
1866
|
+
#
|
1867
|
+
# Please refer to the documentation of the base helper for details.
|
1868
|
+
|
1869
|
+
##
|
1870
|
+
# :method: date_field
|
1871
|
+
#
|
1872
|
+
# :call-seq: date_field(method, options = {})
|
1873
|
+
#
|
1874
|
+
# Wraps ActionView::Helpers::FormHelper#date_field for form builders:
|
1875
|
+
#
|
1876
|
+
# <%= form_with model: @user do |f| %>
|
1877
|
+
# <%= f.date_field :born_on %>
|
1878
|
+
# <% end %>
|
1879
|
+
#
|
1880
|
+
# Please refer to the documentation of the base helper for details.
|
1881
|
+
|
1882
|
+
##
|
1883
|
+
# :method: time_field
|
1884
|
+
#
|
1885
|
+
# :call-seq: time_field(method, options = {})
|
1886
|
+
#
|
1887
|
+
# Wraps ActionView::Helpers::FormHelper#time_field for form builders:
|
1888
|
+
#
|
1889
|
+
# <%= form_with model: @user do |f| %>
|
1890
|
+
# <%= f.time_field :born_at %>
|
1891
|
+
# <% end %>
|
1892
|
+
#
|
1893
|
+
# Please refer to the documentation of the base helper for details.
|
1894
|
+
|
1895
|
+
##
|
1896
|
+
# :method: datetime_field
|
1897
|
+
#
|
1898
|
+
# :call-seq: datetime_field(method, options = {})
|
1899
|
+
#
|
1900
|
+
# Wraps ActionView::Helpers::FormHelper#datetime_field for form builders:
|
1901
|
+
#
|
1902
|
+
# <%= form_with model: @user do |f| %>
|
1903
|
+
# <%= f.datetime_field :graduation_day %>
|
1904
|
+
# <% end %>
|
1905
|
+
#
|
1906
|
+
# Please refer to the documentation of the base helper for details.
|
1907
|
+
|
1908
|
+
##
|
1909
|
+
# :method: datetime_local_field
|
1910
|
+
#
|
1911
|
+
# :call-seq: datetime_local_field(method, options = {})
|
1912
|
+
#
|
1913
|
+
# Wraps ActionView::Helpers::FormHelper#datetime_local_field for form builders:
|
1914
|
+
#
|
1915
|
+
# <%= form_with model: @user do |f| %>
|
1916
|
+
# <%= f.datetime_local_field :graduation_day %>
|
1917
|
+
# <% end %>
|
1918
|
+
#
|
1919
|
+
# Please refer to the documentation of the base helper for details.
|
1920
|
+
|
1921
|
+
##
|
1922
|
+
# :method: month_field
|
1923
|
+
#
|
1924
|
+
# :call-seq: month_field(method, options = {})
|
1925
|
+
#
|
1926
|
+
# Wraps ActionView::Helpers::FormHelper#month_field for form builders:
|
1927
|
+
#
|
1928
|
+
# <%= form_with model: @user do |f| %>
|
1929
|
+
# <%= f.month_field :birthday_month %>
|
1930
|
+
# <% end %>
|
1931
|
+
#
|
1932
|
+
# Please refer to the documentation of the base helper for details.
|
1933
|
+
|
1934
|
+
##
|
1935
|
+
# :method: week_field
|
1936
|
+
#
|
1937
|
+
# :call-seq: week_field(method, options = {})
|
1938
|
+
#
|
1939
|
+
# Wraps ActionView::Helpers::FormHelper#week_field for form builders:
|
1940
|
+
#
|
1941
|
+
# <%= form_with model: @user do |f| %>
|
1942
|
+
# <%= f.week_field :birthday_week %>
|
1943
|
+
# <% end %>
|
1944
|
+
#
|
1945
|
+
# Please refer to the documentation of the base helper for details.
|
1946
|
+
|
1947
|
+
##
|
1948
|
+
# :method: url_field
|
1949
|
+
#
|
1950
|
+
# :call-seq: url_field(method, options = {})
|
1951
|
+
#
|
1952
|
+
# Wraps ActionView::Helpers::FormHelper#url_field for form builders:
|
1953
|
+
#
|
1954
|
+
# <%= form_with model: @user do |f| %>
|
1955
|
+
# <%= f.url_field :homepage %>
|
1956
|
+
# <% end %>
|
1957
|
+
#
|
1958
|
+
# Please refer to the documentation of the base helper for details.
|
1959
|
+
|
1960
|
+
##
|
1961
|
+
# :method: email_field
|
1962
|
+
#
|
1963
|
+
# :call-seq: email_field(method, options = {})
|
1964
|
+
#
|
1965
|
+
# Wraps ActionView::Helpers::FormHelper#email_field for form builders:
|
1966
|
+
#
|
1967
|
+
# <%= form_with model: @user do |f| %>
|
1968
|
+
# <%= f.email_field :address %>
|
1969
|
+
# <% end %>
|
1970
|
+
#
|
1971
|
+
# Please refer to the documentation of the base helper for details.
|
1972
|
+
|
1973
|
+
##
|
1974
|
+
# :method: number_field
|
1975
|
+
#
|
1976
|
+
# :call-seq: number_field(method, options = {})
|
1977
|
+
#
|
1978
|
+
# Wraps ActionView::Helpers::FormHelper#number_field for form builders:
|
1979
|
+
#
|
1980
|
+
# <%= form_with model: @user do |f| %>
|
1981
|
+
# <%= f.number_field :age %>
|
1982
|
+
# <% end %>
|
1983
|
+
#
|
1984
|
+
# Please refer to the documentation of the base helper for details.
|
1985
|
+
|
1986
|
+
##
|
1987
|
+
# :method: range_field
|
1988
|
+
#
|
1989
|
+
# :call-seq: range_field(method, options = {})
|
1990
|
+
#
|
1991
|
+
# Wraps ActionView::Helpers::FormHelper#range_field for form builders:
|
1992
|
+
#
|
1993
|
+
# <%= form_with model: @user do |f| %>
|
1994
|
+
# <%= f.range_field :age %>
|
1995
|
+
# <% end %>
|
1996
|
+
#
|
1997
|
+
# Please refer to the documentation of the base helper for details.
|
1998
|
+
|
1999
|
+
(field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
|
1321
2000
|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
1322
2001
|
def #{selector}(method, options = {}) # def text_field(method, options = {})
|
1323
|
-
@template.
|
1324
|
-
#{selector.inspect}, #
|
2002
|
+
@template.public_send( # @template.public_send(
|
2003
|
+
#{selector.inspect}, # :text_field,
|
1325
2004
|
@object_name, # @object_name,
|
1326
2005
|
method, # method,
|
1327
2006
|
objectify_options(options)) # objectify_options(options))
|
@@ -1386,9 +2065,9 @@ module ActionView
|
|
1386
2065
|
# _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
|
1387
2066
|
# of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
|
1388
2067
|
#
|
1389
|
-
# Note: This also works for the methods in
|
2068
|
+
# Note: This also works for the methods in FormOptionsHelper and
|
1390
2069
|
# DateHelper that are designed to work with an object as base, like
|
1391
|
-
#
|
2070
|
+
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
1392
2071
|
#
|
1393
2072
|
# === Nested Attributes Examples
|
1394
2073
|
#
|
@@ -1454,7 +2133,7 @@ module ActionView
|
|
1454
2133
|
#
|
1455
2134
|
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
|
1456
2135
|
# with a value that evaluates to +true+, you will destroy the associated
|
1457
|
-
# model (
|
2136
|
+
# model (e.g. 1, '1', true, or 'true'):
|
1458
2137
|
#
|
1459
2138
|
# <%= form_for @person do |person_form| %>
|
1460
2139
|
# ...
|
@@ -1543,7 +2222,7 @@ module ActionView
|
|
1543
2222
|
# This will allow you to specify which models to destroy in the
|
1544
2223
|
# attributes hash by adding a form element for the <tt>_destroy</tt>
|
1545
2224
|
# parameter with a value that evaluates to +true+
|
1546
|
-
# (
|
2225
|
+
# (e.g. 1, '1', true, or 'true'):
|
1547
2226
|
#
|
1548
2227
|
# <%= form_for @person do |person_form| %>
|
1549
2228
|
# ...
|
@@ -1582,23 +2261,40 @@ module ActionView
|
|
1582
2261
|
return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
|
1583
2262
|
end
|
1584
2263
|
else
|
1585
|
-
record_object =
|
2264
|
+
record_object = @template._object_for_form_builder(record_name)
|
1586
2265
|
record_name = model_name_from_record_or_class(record_object).param_key
|
1587
2266
|
end
|
1588
2267
|
|
2268
|
+
object_name = @object_name
|
1589
2269
|
index = if options.has_key?(:index)
|
1590
2270
|
options[:index]
|
1591
2271
|
elsif defined?(@auto_index)
|
1592
|
-
|
2272
|
+
object_name = object_name.to_s.delete_suffix("[]")
|
1593
2273
|
@auto_index
|
1594
2274
|
end
|
1595
2275
|
|
1596
|
-
record_name =
|
2276
|
+
record_name = if index
|
2277
|
+
"#{object_name}[#{index}][#{record_name}]"
|
2278
|
+
elsif record_name.end_with?("[]")
|
2279
|
+
"#{object_name}[#{record_name[0..-3]}][#{record_object.id}]"
|
2280
|
+
else
|
2281
|
+
"#{object_name}[#{record_name}]"
|
2282
|
+
end
|
1597
2283
|
fields_options[:child_index] = index
|
1598
2284
|
|
1599
2285
|
@template.fields_for(record_name, record_object, fields_options, &block)
|
1600
2286
|
end
|
1601
2287
|
|
2288
|
+
# See the docs for the <tt>ActionView::FormHelper.fields</tt> helper method.
|
2289
|
+
def fields(scope = nil, model: nil, **options, &block)
|
2290
|
+
options[:allow_method_names_outside_object] = true
|
2291
|
+
options[:skip_default_ids] = !FormHelper.form_with_generates_ids
|
2292
|
+
|
2293
|
+
convert_to_legacy_options(options)
|
2294
|
+
|
2295
|
+
fields_for(scope || model, model, options, &block)
|
2296
|
+
end
|
2297
|
+
|
1602
2298
|
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
|
1603
2299
|
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
|
1604
2300
|
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
|
@@ -1607,7 +2303,7 @@ module ActionView
|
|
1607
2303
|
# target labels for radio_button tags (where the value is used in the ID of the input tag).
|
1608
2304
|
#
|
1609
2305
|
# ==== Examples
|
1610
|
-
# label(:
|
2306
|
+
# label(:title)
|
1611
2307
|
# # => <label for="post_title">Title</label>
|
1612
2308
|
#
|
1613
2309
|
# You can localize your labels based on model and attribute names.
|
@@ -1620,7 +2316,7 @@ module ActionView
|
|
1620
2316
|
#
|
1621
2317
|
# Which then will result in
|
1622
2318
|
#
|
1623
|
-
# label(:
|
2319
|
+
# label(:body)
|
1624
2320
|
# # => <label for="post_body">Write your entire text here</label>
|
1625
2321
|
#
|
1626
2322
|
# Localization can also be based purely on the translation of the attribute-name
|
@@ -1631,21 +2327,40 @@ module ActionView
|
|
1631
2327
|
# post:
|
1632
2328
|
# cost: "Total cost"
|
1633
2329
|
#
|
1634
|
-
# label(:
|
2330
|
+
# label(:cost)
|
1635
2331
|
# # => <label for="post_cost">Total cost</label>
|
1636
2332
|
#
|
1637
|
-
# label(:
|
2333
|
+
# label(:title, "A short title")
|
1638
2334
|
# # => <label for="post_title">A short title</label>
|
1639
2335
|
#
|
1640
|
-
# label(:
|
2336
|
+
# label(:title, "A short title", class: "title_label")
|
1641
2337
|
# # => <label for="post_title" class="title_label">A short title</label>
|
1642
2338
|
#
|
1643
|
-
# label(:
|
2339
|
+
# label(:privacy, "Public Post", value: "public")
|
1644
2340
|
# # => <label for="post_privacy_public">Public Post</label>
|
1645
2341
|
#
|
1646
|
-
# label(:
|
1647
|
-
#
|
2342
|
+
# label(:cost) do |translation|
|
2343
|
+
# content_tag(:span, translation, class: "cost_label")
|
2344
|
+
# end
|
2345
|
+
# # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
|
2346
|
+
#
|
2347
|
+
# label(:cost) do |builder|
|
2348
|
+
# content_tag(:span, builder.translation, class: "cost_label")
|
2349
|
+
# end
|
2350
|
+
# # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
|
2351
|
+
#
|
2352
|
+
# label(:cost) do |builder|
|
2353
|
+
# content_tag(:span, builder.translation, class: [
|
2354
|
+
# "cost_label",
|
2355
|
+
# ("error_label" if builder.object.errors.include?(:cost))
|
2356
|
+
# ])
|
2357
|
+
# end
|
2358
|
+
# # => <label for="post_cost"><span class="cost_label error_label">Total cost</span></label>
|
2359
|
+
#
|
2360
|
+
# label(:terms) do
|
2361
|
+
# raw('Accept <a href="/terms">Terms</a>.')
|
1648
2362
|
# end
|
2363
|
+
# # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
|
1649
2364
|
def label(method, text = nil, options = {}, &block)
|
1650
2365
|
@template.label(@object_name, method, text, objectify_options(options), &block)
|
1651
2366
|
end
|
@@ -1656,6 +2371,12 @@ module ActionView
|
|
1656
2371
|
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
|
1657
2372
|
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
|
1658
2373
|
#
|
2374
|
+
# ==== Options
|
2375
|
+
#
|
2376
|
+
# * Any standard HTML attributes for the tag can be passed in, for example +:class+.
|
2377
|
+
# * <tt>:checked</tt> - +true+ or +false+ forces the state of the checkbox to be checked or not.
|
2378
|
+
# * <tt>:include_hidden</tt> - If set to false, the auxiliary hidden field described below will not be generated.
|
2379
|
+
#
|
1659
2380
|
# ==== Gotcha
|
1660
2381
|
#
|
1661
2382
|
# The HTML specification says unchecked check boxes are not successful, and
|
@@ -1669,7 +2390,7 @@ module ActionView
|
|
1669
2390
|
# wouldn't update the flag.
|
1670
2391
|
#
|
1671
2392
|
# To prevent this the helper generates an auxiliary hidden field before
|
1672
|
-
#
|
2393
|
+
# every check box. The hidden field has the same name and its
|
1673
2394
|
# attributes mimic an unchecked check box.
|
1674
2395
|
#
|
1675
2396
|
# This way, the client either sends only the hidden field (representing
|
@@ -1693,17 +2414,20 @@ module ActionView
|
|
1693
2414
|
# In that case it is preferable to either use +check_box_tag+ or to use
|
1694
2415
|
# hashes instead of arrays.
|
1695
2416
|
#
|
2417
|
+
# ==== Examples
|
2418
|
+
#
|
1696
2419
|
# # Let's say that @post.validated? is 1:
|
1697
|
-
# check_box("
|
2420
|
+
# check_box("validated")
|
1698
2421
|
# # => <input name="post[validated]" type="hidden" value="0" />
|
1699
2422
|
# # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
|
1700
2423
|
#
|
1701
2424
|
# # Let's say that @puppy.gooddog is "no":
|
1702
|
-
# check_box("
|
2425
|
+
# check_box("gooddog", {}, "yes", "no")
|
1703
2426
|
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
|
1704
2427
|
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
|
1705
2428
|
#
|
1706
|
-
#
|
2429
|
+
# # Let's say that @eula.accepted is "no":
|
2430
|
+
# check_box("accepted", { class: 'eula_check' }, "yes", "no")
|
1707
2431
|
# # => <input name="eula[accepted]" type="hidden" value="no" />
|
1708
2432
|
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
|
1709
2433
|
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
|
@@ -1718,13 +2442,14 @@ module ActionView
|
|
1718
2442
|
# +options+ hash. You may pass HTML options there as well.
|
1719
2443
|
#
|
1720
2444
|
# # Let's say that @post.category returns "rails":
|
1721
|
-
# radio_button("
|
1722
|
-
# radio_button("
|
2445
|
+
# radio_button("category", "rails")
|
2446
|
+
# radio_button("category", "java")
|
1723
2447
|
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
|
1724
2448
|
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
|
1725
2449
|
#
|
1726
|
-
#
|
1727
|
-
# radio_button("
|
2450
|
+
# # Let's say that @user.receive_newsletter returns "no":
|
2451
|
+
# radio_button("receive_newsletter", "yes")
|
2452
|
+
# radio_button("receive_newsletter", "no")
|
1728
2453
|
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
|
1729
2454
|
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
|
1730
2455
|
def radio_button(method, tag_value, options = {})
|
@@ -1737,14 +2462,17 @@ module ActionView
|
|
1737
2462
|
# shown.
|
1738
2463
|
#
|
1739
2464
|
# ==== Examples
|
1740
|
-
#
|
1741
|
-
#
|
2465
|
+
# # Let's say that @signup.pass_confirm returns true:
|
2466
|
+
# hidden_field(:pass_confirm)
|
2467
|
+
# # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="true" />
|
1742
2468
|
#
|
1743
|
-
#
|
1744
|
-
#
|
2469
|
+
# # Let's say that @post.tag_list returns "blog, ruby":
|
2470
|
+
# hidden_field(:tag_list)
|
2471
|
+
# # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="blog, ruby" />
|
1745
2472
|
#
|
1746
|
-
#
|
1747
|
-
#
|
2473
|
+
# # Let's say that @user.token returns "abcde":
|
2474
|
+
# hidden_field(:token)
|
2475
|
+
# # => <input type="hidden" id="user_token" name="user[token]" value="abcde" />
|
1748
2476
|
#
|
1749
2477
|
def hidden_field(method, options = {})
|
1750
2478
|
@emitted_hidden_id = true if method == :id
|
@@ -1756,28 +2484,34 @@ module ActionView
|
|
1756
2484
|
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
|
1757
2485
|
# shown.
|
1758
2486
|
#
|
1759
|
-
# Using this method inside a +
|
2487
|
+
# Using this method inside a +form_with+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
|
1760
2488
|
#
|
1761
2489
|
# ==== Options
|
1762
2490
|
# * Creates standard HTML attributes for the tag.
|
1763
2491
|
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
|
1764
2492
|
# * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
|
2493
|
+
# * <tt>:include_hidden</tt> - When <tt>multiple: true</tt> and <tt>include_hidden: true</tt>, the field will be prefixed with an <tt><input type="hidden"></tt> field with an empty value to support submitting an empty collection of files.
|
1765
2494
|
# * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
|
1766
2495
|
#
|
1767
2496
|
# ==== Examples
|
1768
|
-
#
|
2497
|
+
# # Let's say that @user has avatar:
|
2498
|
+
# file_field(:avatar)
|
1769
2499
|
# # => <input type="file" id="user_avatar" name="user[avatar]" />
|
1770
2500
|
#
|
1771
|
-
#
|
1772
|
-
#
|
2501
|
+
# # Let's say that @post has image:
|
2502
|
+
# file_field(:image, :multiple => true)
|
2503
|
+
# # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
|
1773
2504
|
#
|
1774
|
-
#
|
2505
|
+
# # Let's say that @post has attached:
|
2506
|
+
# file_field(:attached, accept: 'text/html')
|
1775
2507
|
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
|
1776
2508
|
#
|
1777
|
-
#
|
2509
|
+
# # Let's say that @post has image:
|
2510
|
+
# file_field(:image, accept: 'image/png,image/gif,image/jpeg')
|
1778
2511
|
# # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
|
1779
2512
|
#
|
1780
|
-
#
|
2513
|
+
# # Let's say that @attachment has file:
|
2514
|
+
# file_field(:file, class: 'file_input')
|
1781
2515
|
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
|
1782
2516
|
def file_field(method, options = {})
|
1783
2517
|
self.multipart = true
|
@@ -1791,11 +2525,11 @@ module ActionView
|
|
1791
2525
|
# <%= f.submit %>
|
1792
2526
|
# <% end %>
|
1793
2527
|
#
|
1794
|
-
# In the example above, if
|
1795
|
-
# submit button label
|
2528
|
+
# In the example above, if <tt>@post</tt> is a new record, it will use "Create Post" as
|
2529
|
+
# submit button label; otherwise, it uses "Update Post".
|
1796
2530
|
#
|
1797
|
-
# Those labels can be customized using I18n
|
1798
|
-
#
|
2531
|
+
# Those labels can be customized using I18n under the +helpers.submit+ key and using
|
2532
|
+
# <tt>%{model}</tt> for translation interpolation:
|
1799
2533
|
#
|
1800
2534
|
# en:
|
1801
2535
|
# helpers:
|
@@ -1803,7 +2537,7 @@ module ActionView
|
|
1803
2537
|
# create: "Create a %{model}"
|
1804
2538
|
# update: "Confirm changes to %{model}"
|
1805
2539
|
#
|
1806
|
-
# It also searches for a key specific
|
2540
|
+
# It also searches for a key specific to the given object:
|
1807
2541
|
#
|
1808
2542
|
# en:
|
1809
2543
|
# helpers:
|
@@ -1811,7 +2545,7 @@ module ActionView
|
|
1811
2545
|
# post:
|
1812
2546
|
# create: "Add %{model}"
|
1813
2547
|
#
|
1814
|
-
def submit(value=nil, options={})
|
2548
|
+
def submit(value = nil, options = {})
|
1815
2549
|
value, options = nil, value if value.is_a?(Hash)
|
1816
2550
|
value ||= submit_default_value
|
1817
2551
|
@template.submit_tag(value, options)
|
@@ -1824,11 +2558,11 @@ module ActionView
|
|
1824
2558
|
# <%= f.button %>
|
1825
2559
|
# <% end %>
|
1826
2560
|
#
|
1827
|
-
# In the example above, if
|
1828
|
-
# button label
|
2561
|
+
# In the example above, if <tt>@post</tt> is a new record, it will use "Create Post" as
|
2562
|
+
# button label; otherwise, it uses "Update Post".
|
1829
2563
|
#
|
1830
|
-
# Those labels can be customized using I18n
|
1831
|
-
# (the same as submit helper) and
|
2564
|
+
# Those labels can be customized using I18n under the +helpers.submit+ key
|
2565
|
+
# (the same as submit helper) and using <tt>%{model}</tt> for translation interpolation:
|
1832
2566
|
#
|
1833
2567
|
# en:
|
1834
2568
|
# helpers:
|
@@ -1836,7 +2570,7 @@ module ActionView
|
|
1836
2570
|
# create: "Create a %{model}"
|
1837
2571
|
# update: "Confirm changes to %{model}"
|
1838
2572
|
#
|
1839
|
-
# It also searches for a key specific
|
2573
|
+
# It also searches for a key specific to the given object:
|
1840
2574
|
#
|
1841
2575
|
# en:
|
1842
2576
|
# helpers:
|
@@ -1845,9 +2579,12 @@ module ActionView
|
|
1845
2579
|
# create: "Add %{model}"
|
1846
2580
|
#
|
1847
2581
|
# ==== Examples
|
1848
|
-
# button("Create
|
2582
|
+
# button("Create post")
|
1849
2583
|
# # => <button name='button' type='submit'>Create post</button>
|
1850
2584
|
#
|
2585
|
+
# button(:draft, value: true)
|
2586
|
+
# # => <button id="post_draft" name="post[draft]" value="true" type="submit">Create post</button>
|
2587
|
+
#
|
1851
2588
|
# button do
|
1852
2589
|
# content_tag(:strong, 'Ask me!')
|
1853
2590
|
# end
|
@@ -1855,19 +2592,50 @@ module ActionView
|
|
1855
2592
|
# # <strong>Ask me!</strong>
|
1856
2593
|
# # </button>
|
1857
2594
|
#
|
2595
|
+
# button do |text|
|
2596
|
+
# content_tag(:strong, text)
|
2597
|
+
# end
|
2598
|
+
# # => <button name='button' type='submit'>
|
2599
|
+
# # <strong>Create post</strong>
|
2600
|
+
# # </button>
|
2601
|
+
#
|
2602
|
+
# button(:draft, value: true) do
|
2603
|
+
# content_tag(:strong, "Save as draft")
|
2604
|
+
# end
|
2605
|
+
# # => <button id="post_draft" name="post[draft]" value="true" type="submit">
|
2606
|
+
# # <strong>Save as draft</strong>
|
2607
|
+
# # </button>
|
2608
|
+
#
|
1858
2609
|
def button(value = nil, options = {}, &block)
|
1859
|
-
|
2610
|
+
case value
|
2611
|
+
when Hash
|
2612
|
+
value, options = nil, value
|
2613
|
+
when Symbol
|
2614
|
+
value, options = nil, { name: field_name(value), id: field_id(value) }.merge!(options.to_h)
|
2615
|
+
end
|
1860
2616
|
value ||= submit_default_value
|
1861
|
-
|
2617
|
+
|
2618
|
+
if block_given?
|
2619
|
+
value = @template.capture { yield(value) }
|
2620
|
+
end
|
2621
|
+
|
2622
|
+
formmethod = options[:formmethod]
|
2623
|
+
if formmethod.present? && !/post|get/i.match?(formmethod) && !options.key?(:name) && !options.key?(:value)
|
2624
|
+
options.merge! formmethod: :post, name: "_method", value: formmethod
|
2625
|
+
end
|
2626
|
+
|
2627
|
+
@template.button_tag(value, options)
|
1862
2628
|
end
|
1863
2629
|
|
1864
|
-
def emitted_hidden_id?
|
2630
|
+
def emitted_hidden_id? # :nodoc:
|
1865
2631
|
@emitted_hidden_id ||= nil
|
1866
2632
|
end
|
1867
2633
|
|
1868
2634
|
private
|
1869
2635
|
def objectify_options(options)
|
1870
|
-
@default_options.merge(options
|
2636
|
+
result = @default_options.merge(options)
|
2637
|
+
result[:object] = @object
|
2638
|
+
result
|
1871
2639
|
end
|
1872
2640
|
|
1873
2641
|
def submit_default_value
|
@@ -1881,7 +2649,12 @@ module ActionView
|
|
1881
2649
|
end
|
1882
2650
|
|
1883
2651
|
defaults = []
|
1884
|
-
|
2652
|
+
# Object is a model and it is not overwritten by as and scope option.
|
2653
|
+
if object.respond_to?(:model_name) && object_name.to_s == model.downcase
|
2654
|
+
defaults << :"helpers.submit.#{object.model_name.i18n_key}.#{key}"
|
2655
|
+
else
|
2656
|
+
defaults << :"helpers.submit.#{object_name}.#{key}"
|
2657
|
+
end
|
1885
2658
|
defaults << :"helpers.submit.#{key}"
|
1886
2659
|
defaults << "#{key.to_s.humanize} #{model}"
|
1887
2660
|
|
@@ -1897,17 +2670,23 @@ module ActionView
|
|
1897
2670
|
association = convert_to_model(association)
|
1898
2671
|
|
1899
2672
|
if association.respond_to?(:persisted?)
|
1900
|
-
association = [association] if @object.
|
2673
|
+
association = [association] if @object.public_send(association_name).respond_to?(:to_ary)
|
1901
2674
|
elsif !association.respond_to?(:to_ary)
|
1902
|
-
association = @object.
|
2675
|
+
association = @object.public_send(association_name)
|
1903
2676
|
end
|
1904
2677
|
|
1905
2678
|
if association.respond_to?(:to_ary)
|
1906
2679
|
explicit_child_index = options[:child_index]
|
1907
2680
|
output = ActiveSupport::SafeBuffer.new
|
1908
2681
|
association.each do |child|
|
1909
|
-
|
1910
|
-
|
2682
|
+
if explicit_child_index
|
2683
|
+
options[:child_index] = explicit_child_index.call if explicit_child_index.respond_to?(:call)
|
2684
|
+
else
|
2685
|
+
options[:child_index] = nested_child_index(name)
|
2686
|
+
end
|
2687
|
+
if content = fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
|
2688
|
+
output << content
|
2689
|
+
end
|
1911
2690
|
end
|
1912
2691
|
output
|
1913
2692
|
elsif association
|
@@ -1932,12 +2711,16 @@ module ActionView
|
|
1932
2711
|
@nested_child_index[name] ||= -1
|
1933
2712
|
@nested_child_index[name] += 1
|
1934
2713
|
end
|
2714
|
+
|
2715
|
+
def convert_to_legacy_options(options)
|
2716
|
+
if options.key?(:skip_id)
|
2717
|
+
options[:include_id] = !options.delete(:skip_id)
|
2718
|
+
end
|
2719
|
+
end
|
1935
2720
|
end
|
1936
2721
|
end
|
1937
2722
|
|
1938
2723
|
ActiveSupport.on_load(:action_view) do
|
1939
|
-
cattr_accessor
|
1940
|
-
::ActionView::Helpers::FormBuilder
|
1941
|
-
end
|
2724
|
+
cattr_accessor :default_form_builder, instance_writer: false, instance_reader: false, default: ::ActionView::Helpers::FormBuilder
|
1942
2725
|
end
|
1943
2726
|
end
|