actionview 6.1.7.2 → 7.0.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +265 -261
- data/MIT-LICENSE +1 -0
- data/lib/action_view/base.rb +4 -7
- data/lib/action_view/buffers.rb +2 -2
- data/lib/action_view/cache_expiry.rb +46 -32
- 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 +6 -147
- data/lib/action_view/digestor.rb +7 -4
- data/lib/action_view/flows.rb +4 -4
- data/lib/action_view/gem_version.rb +5 -5
- data/lib/action_view/helpers/active_model_helper.rb +2 -2
- data/lib/action_view/helpers/asset_tag_helper.rb +95 -39
- data/lib/action_view/helpers/asset_url_helper.rb +16 -16
- data/lib/action_view/helpers/atom_feed_helper.rb +3 -4
- data/lib/action_view/helpers/cache_helper.rb +52 -3
- data/lib/action_view/helpers/capture_helper.rb +4 -4
- data/lib/action_view/helpers/controller_helper.rb +2 -2
- data/lib/action_view/helpers/csp_helper.rb +1 -1
- data/lib/action_view/helpers/csrf_helper.rb +2 -2
- data/lib/action_view/helpers/date_helper.rb +111 -43
- data/lib/action_view/helpers/debug_helper.rb +3 -1
- data/lib/action_view/helpers/form_helper.rb +211 -85
- data/lib/action_view/helpers/form_options_helper.rb +70 -33
- data/lib/action_view/helpers/form_tag_helper.rb +150 -53
- data/lib/action_view/helpers/javascript_helper.rb +3 -5
- data/lib/action_view/helpers/number_helper.rb +17 -16
- data/lib/action_view/helpers/output_safety_helper.rb +4 -4
- data/lib/action_view/helpers/rendering_helper.rb +5 -6
- data/lib/action_view/helpers/sanitize_helper.rb +3 -3
- data/lib/action_view/helpers/tag_helper.rb +37 -8
- data/lib/action_view/helpers/tags/base.rb +5 -25
- data/lib/action_view/helpers/tags/check_box.rb +1 -1
- data/lib/action_view/helpers/tags/collection_select.rb +1 -1
- data/lib/action_view/helpers/tags/file_field.rb +16 -0
- data/lib/action_view/helpers/tags/select.rb +1 -1
- data/lib/action_view/helpers/tags/time_field.rb +10 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
- data/lib/action_view/helpers/tags.rb +3 -2
- data/lib/action_view/helpers/text_helper.rb +25 -14
- data/lib/action_view/helpers/translation_helper.rb +12 -43
- data/lib/action_view/helpers/url_helper.rb +194 -123
- data/lib/action_view/helpers.rb +25 -25
- data/lib/action_view/layouts.rb +7 -4
- data/lib/action_view/lookup_context.rb +33 -52
- data/lib/action_view/model_naming.rb +2 -2
- data/lib/action_view/path_set.rb +16 -22
- data/lib/action_view/railtie.rb +19 -7
- data/lib/action_view/record_identifier.rb +1 -1
- data/lib/action_view/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +2 -2
- data/lib/action_view/renderer/partial_renderer.rb +1 -35
- data/lib/action_view/renderer/renderer.rb +4 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +3 -3
- data/lib/action_view/renderer/template_renderer.rb +6 -2
- data/lib/action_view/rendering.rb +3 -3
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +8 -5
- data/lib/action_view/template/error.rb +108 -13
- data/lib/action_view/template/handlers/erb.rb +6 -0
- data/lib/action_view/template/handlers.rb +3 -3
- data/lib/action_view/template/html.rb +3 -3
- data/lib/action_view/template/inline.rb +3 -3
- data/lib/action_view/template/raw_file.rb +3 -3
- data/lib/action_view/template/resolver.rb +89 -314
- data/lib/action_view/template/text.rb +3 -3
- data/lib/action_view/template/types.rb +14 -12
- data/lib/action_view/template.rb +18 -2
- 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 +7 -3
- data/lib/action_view/testing/resolvers.rb +11 -12
- data/lib/action_view/unbound_template.rb +33 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +4 -4
- data/lib/action_view.rb +2 -3
- data/lib/assets/compiled/rails-ujs.js +36 -5
- metadata +23 -16
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "cgi"
|
4
4
|
require "action_view/helpers/date_helper"
|
5
|
-
require "action_view/helpers/
|
5
|
+
require "action_view/helpers/url_helper"
|
6
6
|
require "action_view/helpers/form_tag_helper"
|
7
7
|
require "action_view/helpers/active_model_helper"
|
8
8
|
require "action_view/model_naming"
|
@@ -11,11 +11,10 @@ require "active_support/core_ext/module/attribute_accessors"
|
|
11
11
|
require "active_support/core_ext/hash/slice"
|
12
12
|
require "active_support/core_ext/string/output_safety"
|
13
13
|
require "active_support/core_ext/string/inflections"
|
14
|
-
require "active_support/core_ext/symbol/starts_ends_with"
|
15
14
|
|
16
15
|
module ActionView
|
17
16
|
# = Action View Form Helpers
|
18
|
-
module Helpers
|
17
|
+
module Helpers # :nodoc:
|
19
18
|
# Form helpers are designed to make working with resources much easier
|
20
19
|
# compared to using vanilla HTML.
|
21
20
|
#
|
@@ -283,6 +282,12 @@ module ActionView
|
|
283
282
|
# ...
|
284
283
|
# <% end %>
|
285
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
|
+
#
|
286
291
|
# You can also set the answer format, like this:
|
287
292
|
#
|
288
293
|
# <%= form_for(@post, format: :json) do |f| %>
|
@@ -427,50 +432,45 @@ module ActionView
|
|
427
432
|
# <% end %>
|
428
433
|
def form_for(record, options = {}, &block)
|
429
434
|
raise ArgumentError, "Missing block" unless block_given?
|
430
|
-
html_options = options[:html] ||= {}
|
431
435
|
|
432
436
|
case record
|
433
437
|
when String, Symbol
|
438
|
+
model = nil
|
434
439
|
object_name = record
|
435
|
-
object = nil
|
436
440
|
else
|
437
|
-
|
441
|
+
model = convert_to_model(record)
|
442
|
+
object = _object_for_form_builder(record)
|
438
443
|
raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
|
439
444
|
object_name = options[:as] || model_name_from_record_or_class(object).param_key
|
440
|
-
apply_form_for_options!(
|
445
|
+
apply_form_for_options!(object, options)
|
441
446
|
end
|
442
447
|
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
+
remote = options.delete(:remote)
|
449
|
+
|
450
|
+
if remote && !embed_authenticity_token_in_remote_forms && options[:authenticity_token].blank?
|
451
|
+
options[:authenticity_token] = false
|
452
|
+
end
|
448
453
|
|
449
|
-
|
450
|
-
|
451
|
-
|
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)
|
452
459
|
|
453
|
-
|
454
|
-
form_tag_with_body(html_options, output)
|
460
|
+
form_with(**options, &block)
|
455
461
|
end
|
456
462
|
|
457
|
-
def apply_form_for_options!(
|
463
|
+
def apply_form_for_options!(object, options) # :nodoc:
|
458
464
|
object = convert_to_model(object)
|
459
465
|
|
460
466
|
as = options[:as]
|
461
467
|
namespace = options[:namespace]
|
462
|
-
action
|
468
|
+
action = object.respond_to?(:persisted?) && object.persisted? ? :edit : :new
|
469
|
+
options[:html] ||= {}
|
463
470
|
options[:html].reverse_merge!(
|
464
471
|
class: as ? "#{action}_#{as}" : dom_class(object, action),
|
465
472
|
id: (as ? [namespace, action, as] : [namespace, dom_id(object, action)]).compact.join("_").presence,
|
466
|
-
method: method
|
467
473
|
)
|
468
|
-
|
469
|
-
options[:url] ||= if options.key?(:format)
|
470
|
-
polymorphic_path(record, format: options.delete(:format))
|
471
|
-
else
|
472
|
-
polymorphic_path(record, {})
|
473
|
-
end
|
474
474
|
end
|
475
475
|
private :apply_form_for_options!
|
476
476
|
|
@@ -478,6 +478,8 @@ module ActionView
|
|
478
478
|
|
479
479
|
mattr_accessor :form_with_generates_ids, default: false
|
480
480
|
|
481
|
+
mattr_accessor :multiple_file_field_include_hidden, default: false
|
482
|
+
|
481
483
|
# Creates a form tag based on mixing URLs, scopes, or models.
|
482
484
|
#
|
483
485
|
# # Using just a URL:
|
@@ -485,7 +487,16 @@ module ActionView
|
|
485
487
|
# <%= form.text_field :title %>
|
486
488
|
# <% end %>
|
487
489
|
# # =>
|
488
|
-
# <form action="/posts" method="post"
|
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">
|
489
500
|
# <input type="text" name="title">
|
490
501
|
# </form>
|
491
502
|
#
|
@@ -494,7 +505,7 @@ module ActionView
|
|
494
505
|
# <%= form.text_field :title %>
|
495
506
|
# <% end %>
|
496
507
|
# # =>
|
497
|
-
# <form action="/posts" method="post"
|
508
|
+
# <form action="/posts" method="post">
|
498
509
|
# <input type="text" name="post[title]">
|
499
510
|
# </form>
|
500
511
|
#
|
@@ -503,7 +514,7 @@ module ActionView
|
|
503
514
|
# <%= form.text_field :title %>
|
504
515
|
# <% end %>
|
505
516
|
# # =>
|
506
|
-
# <form action="/posts" method="post"
|
517
|
+
# <form action="/posts" method="post">
|
507
518
|
# <input type="text" name="post[title]">
|
508
519
|
# </form>
|
509
520
|
#
|
@@ -512,7 +523,7 @@ module ActionView
|
|
512
523
|
# <%= form.text_field :title %>
|
513
524
|
# <% end %>
|
514
525
|
# # =>
|
515
|
-
# <form action="/posts/1" method="post"
|
526
|
+
# <form action="/posts/1" method="post">
|
516
527
|
# <input type="hidden" name="_method" value="patch">
|
517
528
|
# <input type="text" name="post[title]" value="<the title of the post>">
|
518
529
|
# </form>
|
@@ -523,7 +534,7 @@ module ActionView
|
|
523
534
|
# <%= form.text_field :but_in_forms_they_can %>
|
524
535
|
# <% end %>
|
525
536
|
# # =>
|
526
|
-
# <form action="/cats" method="post"
|
537
|
+
# <form action="/cats" method="post">
|
527
538
|
# <input type="text" name="cat[cats_dont_have_gills]">
|
528
539
|
# <input type="text" name="cat[but_in_forms_they_can]">
|
529
540
|
# </form>
|
@@ -604,10 +615,16 @@ module ActionView
|
|
604
615
|
# This is helpful when fragment-caching the form. Remote forms
|
605
616
|
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
606
617
|
# unnecessary unless you support browsers without JavaScript.
|
607
|
-
# * <tt>:local</tt> -
|
608
|
-
#
|
609
|
-
#
|
610
|
-
#
|
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>).
|
611
628
|
# * <tt>:skip_enforcing_utf8</tt> - If set to true, a hidden input with name
|
612
629
|
# utf8 is not output.
|
613
630
|
# * <tt>:builder</tt> - Override the object used to build the form.
|
@@ -735,13 +752,14 @@ module ActionView
|
|
735
752
|
# form_with(**options.merge(builder: LabellingFormBuilder), &block)
|
736
753
|
# end
|
737
754
|
def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
|
738
|
-
options
|
739
|
-
options[:skip_default_ids] = !form_with_generates_ids
|
755
|
+
options = { allow_method_names_outside_object: true, skip_default_ids: !form_with_generates_ids }.merge!(options)
|
740
756
|
|
741
757
|
if model
|
742
|
-
url
|
758
|
+
if url != false
|
759
|
+
url ||= polymorphic_path(model, format: format)
|
760
|
+
end
|
743
761
|
|
744
|
-
model = model
|
762
|
+
model = _object_for_form_builder(model)
|
745
763
|
scope ||= model_name_from_record_or_class(model).param_key
|
746
764
|
end
|
747
765
|
|
@@ -996,12 +1014,14 @@ module ActionView
|
|
996
1014
|
# <% end %>
|
997
1015
|
#
|
998
1016
|
# Note that fields_for will automatically generate a hidden field
|
999
|
-
# to store the ID of the record
|
1000
|
-
# hidden field is not needed and you
|
1001
|
-
# to prevent fields_for from
|
1017
|
+
# to store the ID of the record if it responds to <tt>persisted?</tt>.
|
1018
|
+
# There are circumstances where this hidden field is not needed and you
|
1019
|
+
# can pass <tt>include_id: false</tt> to prevent fields_for from
|
1020
|
+
# rendering it automatically.
|
1002
1021
|
def fields_for(record_name, record_object = nil, options = {}, &block)
|
1003
|
-
|
1004
|
-
|
1022
|
+
options = { model: record_object, allow_method_names_outside_object: false, skip_default_ids: false }.merge!(options)
|
1023
|
+
|
1024
|
+
fields(record_name, **options, &block)
|
1005
1025
|
end
|
1006
1026
|
|
1007
1027
|
# Scopes input fields with either an explicit scope or model.
|
@@ -1050,10 +1070,10 @@ module ActionView
|
|
1050
1070
|
# to work with an object as a base, like
|
1051
1071
|
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
1052
1072
|
def fields(scope = nil, model: nil, **options, &block)
|
1053
|
-
options
|
1054
|
-
options[:skip_default_ids] = !form_with_generates_ids
|
1073
|
+
options = { allow_method_names_outside_object: true, skip_default_ids: !form_with_generates_ids }.merge!(options)
|
1055
1074
|
|
1056
1075
|
if model
|
1076
|
+
model = _object_for_form_builder(model)
|
1057
1077
|
scope ||= model_name_from_record_or_class(model).param_key
|
1058
1078
|
end
|
1059
1079
|
|
@@ -1063,7 +1083,7 @@ module ActionView
|
|
1063
1083
|
|
1064
1084
|
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
|
1065
1085
|
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
|
1066
|
-
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
|
1086
|
+
# is found in the current I18n locale (through <tt>helpers.label.<modelname>.<attribute></tt>) or you specify it explicitly.
|
1067
1087
|
# Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
|
1068
1088
|
# onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
|
1069
1089
|
# target labels for radio_button tags (where the value is used in the ID of the input tag).
|
@@ -1197,6 +1217,7 @@ module ActionView
|
|
1197
1217
|
# * Creates standard HTML attributes for the tag.
|
1198
1218
|
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
|
1199
1219
|
# * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
|
1220
|
+
# * <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.
|
1200
1221
|
# * <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.
|
1201
1222
|
#
|
1202
1223
|
# ==== Examples
|
@@ -1215,6 +1236,8 @@ module ActionView
|
|
1215
1236
|
# file_field(:attachment, :file, class: 'file_input')
|
1216
1237
|
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
|
1217
1238
|
def file_field(object_name, method, options = {})
|
1239
|
+
options = { include_hidden: multiple_file_field_include_hidden }.merge!(options)
|
1240
|
+
|
1218
1241
|
Tags::FileField.new(object_name, method, self, convert_direct_upload_option_to_url(options.dup)).render
|
1219
1242
|
end
|
1220
1243
|
|
@@ -1252,6 +1275,12 @@ module ActionView
|
|
1252
1275
|
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
|
1253
1276
|
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
|
1254
1277
|
#
|
1278
|
+
# ==== Options
|
1279
|
+
#
|
1280
|
+
# * Any standard HTML attributes for the tag can be passed in, for example +:class+.
|
1281
|
+
# * <tt>:checked</tt> - +true+ or +false+ forces the state of the checkbox to be checked or not.
|
1282
|
+
# * <tt>:include_hidden</tt> - If set to false, the auxiliary hidden field described below will not be generated.
|
1283
|
+
#
|
1255
1284
|
# ==== Gotcha
|
1256
1285
|
#
|
1257
1286
|
# The HTML specification says unchecked check boxes are not successful, and
|
@@ -1265,7 +1294,7 @@ module ActionView
|
|
1265
1294
|
# wouldn't update the flag.
|
1266
1295
|
#
|
1267
1296
|
# To prevent this the helper generates an auxiliary hidden field before
|
1268
|
-
#
|
1297
|
+
# every check box. The hidden field has the same name and its
|
1269
1298
|
# attributes mimic an unchecked check box.
|
1270
1299
|
#
|
1271
1300
|
# This way, the client either sends only the hidden field (representing
|
@@ -1289,6 +1318,8 @@ module ActionView
|
|
1289
1318
|
# In that case it is preferable to either use +check_box_tag+ or to use
|
1290
1319
|
# hashes instead of arrays.
|
1291
1320
|
#
|
1321
|
+
# ==== Examples
|
1322
|
+
#
|
1292
1323
|
# # Let's say that @post.validated? is 1:
|
1293
1324
|
# check_box("post", "validated")
|
1294
1325
|
# # => <input name="post[validated]" type="hidden" value="0" />
|
@@ -1403,13 +1434,16 @@ module ActionView
|
|
1403
1434
|
# Returns a text_field of type "time".
|
1404
1435
|
#
|
1405
1436
|
# The default value is generated by trying to call +strftime+ with "%T.%L"
|
1406
|
-
# on the object's value.
|
1407
|
-
# by
|
1437
|
+
# on the object's value. If you pass <tt>include_seconds: false</tt>, it will be
|
1438
|
+
# formatted by trying to call +strftime+ with "%H:%M" on the object's value.
|
1439
|
+
# It is also possible to override this by passing the "value" option.
|
1440
|
+
#
|
1441
|
+
# ==== Options
|
1442
|
+
#
|
1443
|
+
# Supports the same options as FormTagHelper#time_field_tag.
|
1408
1444
|
#
|
1409
|
-
#
|
1410
|
-
# * Accepts same options as time_field_tag
|
1445
|
+
# ==== Examples
|
1411
1446
|
#
|
1412
|
-
# === Example
|
1413
1447
|
# time_field("task", "started_at")
|
1414
1448
|
# # => <input id="task_started_at" name="task[started_at]" type="time" />
|
1415
1449
|
#
|
@@ -1425,6 +1459,12 @@ module ActionView
|
|
1425
1459
|
# time_field("task", "started_at", min: "01:00:00")
|
1426
1460
|
# # => <input id="task_started_at" name="task[started_at]" type="time" min="01:00:00.000" />
|
1427
1461
|
#
|
1462
|
+
# By default, provided times will be formatted including seconds. You can render just the hour
|
1463
|
+
# and minute by passing <tt>include_seconds: false</tt>. Some browsers will render a simpler UI
|
1464
|
+
# if you exclude seconds in the timestamp format.
|
1465
|
+
#
|
1466
|
+
# time_field("task", "started_at", value: Time.now, include_seconds: false)
|
1467
|
+
# # => <input id="task_started_at" name="task[started_at]" type="time" value="01:00" />
|
1428
1468
|
def time_field(object_name, method, options = {})
|
1429
1469
|
Tags::TimeField.new(object_name, method, self, options).render
|
1430
1470
|
end
|
@@ -1515,7 +1555,8 @@ module ActionView
|
|
1515
1555
|
# Returns an input tag of type "number".
|
1516
1556
|
#
|
1517
1557
|
# ==== Options
|
1518
|
-
#
|
1558
|
+
#
|
1559
|
+
# Supports the same options as FormTagHelper#number_field_tag.
|
1519
1560
|
def number_field(object_name, method, options = {})
|
1520
1561
|
Tags::NumberField.new(object_name, method, self, options).render
|
1521
1562
|
end
|
@@ -1523,39 +1564,30 @@ module ActionView
|
|
1523
1564
|
# Returns an input tag of type "range".
|
1524
1565
|
#
|
1525
1566
|
# ==== Options
|
1526
|
-
#
|
1567
|
+
#
|
1568
|
+
# Supports the same options as FormTagHelper#range_field_tag.
|
1527
1569
|
def range_field(object_name, method, options = {})
|
1528
1570
|
Tags::RangeField.new(object_name, method, self, options).render
|
1529
1571
|
end
|
1530
1572
|
|
1573
|
+
def _object_for_form_builder(object) # :nodoc:
|
1574
|
+
object.is_a?(Array) ? object.last : object
|
1575
|
+
end
|
1576
|
+
|
1531
1577
|
private
|
1532
1578
|
def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: !form_with_generates_remote_forms,
|
1533
1579
|
skip_enforcing_utf8: nil, **options)
|
1534
|
-
html_options = options.slice(:id, :class, :multipart, :method, :data).merge(html)
|
1580
|
+
html_options = options.slice(:id, :class, :multipart, :method, :data, :authenticity_token).merge!(html)
|
1581
|
+
html_options[:remote] = html.delete(:remote) || !local
|
1535
1582
|
html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
html_options[:action] = url_for(url_for_options || {})
|
1543
|
-
html_options[:"accept-charset"] = "UTF-8"
|
1544
|
-
html_options[:"data-remote"] = true unless local
|
1545
|
-
|
1546
|
-
html_options[:authenticity_token] = options.delete(:authenticity_token)
|
1547
|
-
|
1548
|
-
if !local && html_options[:authenticity_token].blank?
|
1549
|
-
html_options[:authenticity_token] = embed_authenticity_token_in_remote_forms
|
1550
|
-
end
|
1551
|
-
|
1552
|
-
if html_options[:authenticity_token] == true
|
1553
|
-
# Include the default authenticity_token, which is only generated when it's set to nil,
|
1554
|
-
# but we needed the true value to override the default of no authenticity_token on data-remote.
|
1555
|
-
html_options[:authenticity_token] = nil
|
1583
|
+
if skip_enforcing_utf8.nil?
|
1584
|
+
if options.key?(:enforce_utf8)
|
1585
|
+
html_options[:enforce_utf8] = options[:enforce_utf8]
|
1586
|
+
end
|
1587
|
+
else
|
1588
|
+
html_options[:enforce_utf8] = !skip_enforcing_utf8
|
1556
1589
|
end
|
1557
|
-
|
1558
|
-
html_options.stringify_keys!
|
1590
|
+
html_options_for_form(url_for_options.nil? ? {} : url_for_options, html_options)
|
1559
1591
|
end
|
1560
1592
|
|
1561
1593
|
def instantiate_builder(record_name, record_object, options)
|
@@ -1685,6 +1717,69 @@ module ActionView
|
|
1685
1717
|
@index = options[:index] || options[:child_index]
|
1686
1718
|
end
|
1687
1719
|
|
1720
|
+
# Generate an HTML <tt>id</tt> attribute value.
|
1721
|
+
#
|
1722
|
+
# return the <tt><form></tt> element's <tt>id</tt> attribute.
|
1723
|
+
#
|
1724
|
+
# <%= form_for @post do |f| %>
|
1725
|
+
# <%# ... %>
|
1726
|
+
#
|
1727
|
+
# <% content_for :sticky_footer do %>
|
1728
|
+
# <%= form.button(form: f.id) %>
|
1729
|
+
# <% end %>
|
1730
|
+
# <% end %>
|
1731
|
+
#
|
1732
|
+
# In the example above, the <tt>:sticky_footer</tt> content area will
|
1733
|
+
# exist outside of the <tt><form></tt> element. By declaring the
|
1734
|
+
# <tt>form</tt> HTML attribute, we hint to the browser that the generated
|
1735
|
+
# <tt><button></tt> element should be treated as the <tt><form></tt>
|
1736
|
+
# element's submit button, regardless of where it exists in the DOM.
|
1737
|
+
def id
|
1738
|
+
options.dig(:html, :id) || options[:id]
|
1739
|
+
end
|
1740
|
+
|
1741
|
+
# Generate an HTML <tt>id</tt> attribute value for the given field
|
1742
|
+
#
|
1743
|
+
# Return the value generated by the <tt>FormBuilder</tt> for the given
|
1744
|
+
# attribute name.
|
1745
|
+
#
|
1746
|
+
# <%= form_for @post do |f| %>
|
1747
|
+
# <%= f.label :title %>
|
1748
|
+
# <%= f.text_field :title, aria: { describedby: f.field_id(:title, :error) } %>
|
1749
|
+
# <%= tag.span("is blank", id: f.field_id(:title, :error) %>
|
1750
|
+
# <% end %>
|
1751
|
+
#
|
1752
|
+
# In the example above, the <tt><input type="text"></tt> element built by
|
1753
|
+
# the call to <tt>FormBuilder#text_field</tt> declares an
|
1754
|
+
# <tt>aria-describedby</tt> attribute referencing the <tt><span></tt>
|
1755
|
+
# element, sharing a common <tt>id</tt> root (<tt>post_title</tt>, in this
|
1756
|
+
# case).
|
1757
|
+
def field_id(method, *suffixes, namespace: @options[:namespace], index: @index)
|
1758
|
+
@template.field_id(@object_name, method, *suffixes, namespace: namespace, index: index)
|
1759
|
+
end
|
1760
|
+
|
1761
|
+
# Generate an HTML <tt>name</tt> attribute value for the given name and
|
1762
|
+
# field combination
|
1763
|
+
#
|
1764
|
+
# Return the value generated by the <tt>FormBuilder</tt> for the given
|
1765
|
+
# attribute name.
|
1766
|
+
#
|
1767
|
+
# <%= form_for @post do |f| %>
|
1768
|
+
# <%= f.text_field :title, name: f.field_name(:title, :subtitle) %>
|
1769
|
+
# <%# => <input type="text" name="post[title][subtitle]">
|
1770
|
+
# <% end %>
|
1771
|
+
#
|
1772
|
+
# <%= form_for @post do |f| %>
|
1773
|
+
# <%= f.field_tag :tag, name: f.field_name(:tag, multiple: true) %>
|
1774
|
+
# <%# => <input type="text" name="post[tag][]">
|
1775
|
+
# <% end %>
|
1776
|
+
#
|
1777
|
+
def field_name(method, *methods, multiple: false, index: @index)
|
1778
|
+
object_name = @options.fetch(:as) { @object_name }
|
1779
|
+
|
1780
|
+
@template.field_name(object_name, method, *methods, index: index, multiple: multiple)
|
1781
|
+
end
|
1782
|
+
|
1688
1783
|
##
|
1689
1784
|
# :method: text_field
|
1690
1785
|
#
|
@@ -2171,7 +2266,7 @@ module ActionView
|
|
2171
2266
|
return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
|
2172
2267
|
end
|
2173
2268
|
else
|
2174
|
-
record_object =
|
2269
|
+
record_object = @template._object_for_form_builder(record_name)
|
2175
2270
|
record_name = model_name_from_record_or_class(record_object).param_key
|
2176
2271
|
end
|
2177
2272
|
|
@@ -2195,7 +2290,7 @@ module ActionView
|
|
2195
2290
|
@template.fields_for(record_name, record_object, fields_options, &block)
|
2196
2291
|
end
|
2197
2292
|
|
2198
|
-
# See the docs for the
|
2293
|
+
# See the docs for the ActionView::Helpers::FormHelper#fields helper method.
|
2199
2294
|
def fields(scope = nil, model: nil, **options, &block)
|
2200
2295
|
options[:allow_method_names_outside_object] = true
|
2201
2296
|
options[:skip_default_ids] = !FormHelper.form_with_generates_ids
|
@@ -2207,7 +2302,7 @@ module ActionView
|
|
2207
2302
|
|
2208
2303
|
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
|
2209
2304
|
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
|
2210
|
-
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
|
2305
|
+
# is found in the current I18n locale (through <tt>helpers.label.<modelname>.<attribute></tt>) or you specify it explicitly.
|
2211
2306
|
# Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
|
2212
2307
|
# onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
|
2213
2308
|
# target labels for radio_button tags (where the value is used in the ID of the input tag).
|
@@ -2281,6 +2376,12 @@ module ActionView
|
|
2281
2376
|
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
|
2282
2377
|
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
|
2283
2378
|
#
|
2379
|
+
# ==== Options
|
2380
|
+
#
|
2381
|
+
# * Any standard HTML attributes for the tag can be passed in, for example +:class+.
|
2382
|
+
# * <tt>:checked</tt> - +true+ or +false+ forces the state of the checkbox to be checked or not.
|
2383
|
+
# * <tt>:include_hidden</tt> - If set to false, the auxiliary hidden field described below will not be generated.
|
2384
|
+
#
|
2284
2385
|
# ==== Gotcha
|
2285
2386
|
#
|
2286
2387
|
# The HTML specification says unchecked check boxes are not successful, and
|
@@ -2294,7 +2395,7 @@ module ActionView
|
|
2294
2395
|
# wouldn't update the flag.
|
2295
2396
|
#
|
2296
2397
|
# To prevent this the helper generates an auxiliary hidden field before
|
2297
|
-
#
|
2398
|
+
# every check box. The hidden field has the same name and its
|
2298
2399
|
# attributes mimic an unchecked check box.
|
2299
2400
|
#
|
2300
2401
|
# This way, the client either sends only the hidden field (representing
|
@@ -2318,6 +2419,8 @@ module ActionView
|
|
2318
2419
|
# In that case it is preferable to either use +check_box_tag+ or to use
|
2319
2420
|
# hashes instead of arrays.
|
2320
2421
|
#
|
2422
|
+
# ==== Examples
|
2423
|
+
#
|
2321
2424
|
# # Let's say that @post.validated? is 1:
|
2322
2425
|
# check_box("validated")
|
2323
2426
|
# # => <input name="post[validated]" type="hidden" value="0" />
|
@@ -2386,12 +2489,13 @@ module ActionView
|
|
2386
2489
|
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
|
2387
2490
|
# shown.
|
2388
2491
|
#
|
2389
|
-
# Using this method inside a +
|
2492
|
+
# Using this method inside a +form_with+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
|
2390
2493
|
#
|
2391
2494
|
# ==== Options
|
2392
2495
|
# * Creates standard HTML attributes for the tag.
|
2393
2496
|
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
|
2394
2497
|
# * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
|
2498
|
+
# * <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.
|
2395
2499
|
# * <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.
|
2396
2500
|
#
|
2397
2501
|
# ==== Examples
|
@@ -2483,6 +2587,9 @@ module ActionView
|
|
2483
2587
|
# button("Create post")
|
2484
2588
|
# # => <button name='button' type='submit'>Create post</button>
|
2485
2589
|
#
|
2590
|
+
# button(:draft, value: true)
|
2591
|
+
# # => <button id="post_draft" name="post[draft]" value="true" type="submit">Create post</button>
|
2592
|
+
#
|
2486
2593
|
# button do
|
2487
2594
|
# content_tag(:strong, 'Ask me!')
|
2488
2595
|
# end
|
@@ -2497,14 +2604,31 @@ module ActionView
|
|
2497
2604
|
# # <strong>Create post</strong>
|
2498
2605
|
# # </button>
|
2499
2606
|
#
|
2607
|
+
# button(:draft, value: true) do
|
2608
|
+
# content_tag(:strong, "Save as draft")
|
2609
|
+
# end
|
2610
|
+
# # => <button id="post_draft" name="post[draft]" value="true" type="submit">
|
2611
|
+
# # <strong>Save as draft</strong>
|
2612
|
+
# # </button>
|
2613
|
+
#
|
2500
2614
|
def button(value = nil, options = {}, &block)
|
2501
|
-
|
2615
|
+
case value
|
2616
|
+
when Hash
|
2617
|
+
value, options = nil, value
|
2618
|
+
when Symbol
|
2619
|
+
value, options = nil, { name: field_name(value), id: field_id(value) }.merge!(options.to_h)
|
2620
|
+
end
|
2502
2621
|
value ||= submit_default_value
|
2503
2622
|
|
2504
2623
|
if block_given?
|
2505
2624
|
value = @template.capture { yield(value) }
|
2506
2625
|
end
|
2507
2626
|
|
2627
|
+
formmethod = options[:formmethod]
|
2628
|
+
if formmethod.present? && !/post|get/i.match?(formmethod) && !options.key?(:name) && !options.key?(:value)
|
2629
|
+
options.merge! formmethod: :post, name: "_method", value: formmethod
|
2630
|
+
end
|
2631
|
+
|
2508
2632
|
@template.button_tag(value, options)
|
2509
2633
|
end
|
2510
2634
|
|
@@ -2565,7 +2689,9 @@ module ActionView
|
|
2565
2689
|
else
|
2566
2690
|
options[:child_index] = nested_child_index(name)
|
2567
2691
|
end
|
2568
|
-
|
2692
|
+
if content = fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
|
2693
|
+
output << content
|
2694
|
+
end
|
2569
2695
|
end
|
2570
2696
|
output
|
2571
2697
|
elsif association
|