actionview 7.0.0.alpha2 → 7.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +123 -0
- data/lib/action_view/base.rb +1 -4
- data/lib/action_view/cache_expiry.rb +1 -1
- data/lib/action_view/gem_version.rb +1 -1
- data/lib/action_view/helpers/active_model_helper.rb +1 -1
- data/lib/action_view/helpers/asset_url_helper.rb +2 -2
- data/lib/action_view/helpers/cache_helper.rb +9 -8
- data/lib/action_view/helpers/date_helper.rb +57 -2
- data/lib/action_view/helpers/form_helper.rb +119 -64
- data/lib/action_view/helpers/form_options_helper.rb +6 -3
- data/lib/action_view/helpers/form_tag_helper.rb +53 -6
- data/lib/action_view/helpers/tag_helper.rb +17 -2
- data/lib/action_view/helpers/tags/base.rb +2 -10
- data/lib/action_view/helpers/tags/check_box.rb +1 -1
- data/lib/action_view/helpers/tags/hidden_field.rb +4 -0
- data/lib/action_view/helpers/tags/weekday_select.rb +2 -1
- data/lib/action_view/helpers/translation_helper.rb +9 -39
- data/lib/action_view/helpers/url_helper.rb +56 -10
- data/lib/action_view/model_naming.rb +1 -1
- data/lib/action_view/railtie.rb +5 -6
- data/lib/action_view/template.rb +8 -1
- metadata +13 -12
@@ -282,6 +282,12 @@ module ActionView
|
|
282
282
|
# ...
|
283
283
|
# <% end %>
|
284
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
|
+
#
|
285
291
|
# You can also set the answer format, like this:
|
286
292
|
#
|
287
293
|
# <%= form_for(@post, format: :json) do |f| %>
|
@@ -426,50 +432,45 @@ module ActionView
|
|
426
432
|
# <% end %>
|
427
433
|
def form_for(record, options = {}, &block)
|
428
434
|
raise ArgumentError, "Missing block" unless block_given?
|
429
|
-
html_options = options[:html] ||= {}
|
430
435
|
|
431
436
|
case record
|
432
437
|
when String, Symbol
|
438
|
+
model = nil
|
433
439
|
object_name = record
|
434
|
-
object = nil
|
435
440
|
else
|
436
|
-
|
441
|
+
model = record
|
442
|
+
object = _object_for_form_builder(record)
|
437
443
|
raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
|
438
444
|
object_name = options[:as] || model_name_from_record_or_class(object).param_key
|
439
|
-
apply_form_for_options!(
|
445
|
+
apply_form_for_options!(object, options)
|
440
446
|
end
|
441
447
|
|
442
|
-
|
443
|
-
html_options[:remote] = options.delete(:remote) if options.has_key?(:remote)
|
444
|
-
html_options[:method] = options.delete(:method) if options.has_key?(:method)
|
445
|
-
html_options[:enforce_utf8] = options.delete(:enforce_utf8) if options.has_key?(:enforce_utf8)
|
446
|
-
html_options[:authenticity_token] = options.delete(:authenticity_token)
|
448
|
+
remote = options.delete(:remote)
|
447
449
|
|
448
|
-
|
449
|
-
|
450
|
-
|
450
|
+
if remote && !embed_authenticity_token_in_remote_forms && options[:authenticity_token].blank?
|
451
|
+
options[:authenticity_token] = false
|
452
|
+
end
|
453
|
+
|
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)
|
451
459
|
|
452
|
-
|
453
|
-
form_tag_with_body(html_options, output)
|
460
|
+
form_with(**options, &block)
|
454
461
|
end
|
455
462
|
|
456
|
-
def apply_form_for_options!(
|
463
|
+
def apply_form_for_options!(object, options) # :nodoc:
|
457
464
|
object = convert_to_model(object)
|
458
465
|
|
459
466
|
as = options[:as]
|
460
467
|
namespace = options[:namespace]
|
461
|
-
action
|
468
|
+
action = object.respond_to?(:persisted?) && object.persisted? ? :edit : :new
|
469
|
+
options[:html] ||= {}
|
462
470
|
options[:html].reverse_merge!(
|
463
471
|
class: as ? "#{action}_#{as}" : dom_class(object, action),
|
464
472
|
id: (as ? [namespace, action, as] : [namespace, dom_id(object, action)]).compact.join("_").presence,
|
465
|
-
method: method
|
466
473
|
)
|
467
|
-
|
468
|
-
options[:url] ||= if options.key?(:format)
|
469
|
-
polymorphic_path(record, format: options.delete(:format))
|
470
|
-
else
|
471
|
-
polymorphic_path(record, {})
|
472
|
-
end
|
473
474
|
end
|
474
475
|
private :apply_form_for_options!
|
475
476
|
|
@@ -484,7 +485,16 @@ module ActionView
|
|
484
485
|
# <%= form.text_field :title %>
|
485
486
|
# <% end %>
|
486
487
|
# # =>
|
487
|
-
# <form action="/posts" method="post"
|
488
|
+
# <form action="/posts" method="post">
|
489
|
+
# <input type="text" name="title">
|
490
|
+
# </form>
|
491
|
+
#
|
492
|
+
# # With an intentionally empty URL:
|
493
|
+
# <%= form_with url: false do |form| %>
|
494
|
+
# <%= form.text_field :title %>
|
495
|
+
# <% end %>
|
496
|
+
# # =>
|
497
|
+
# <form method="post" data-remote="true">
|
488
498
|
# <input type="text" name="title">
|
489
499
|
# </form>
|
490
500
|
#
|
@@ -493,7 +503,7 @@ module ActionView
|
|
493
503
|
# <%= form.text_field :title %>
|
494
504
|
# <% end %>
|
495
505
|
# # =>
|
496
|
-
# <form action="/posts" method="post"
|
506
|
+
# <form action="/posts" method="post">
|
497
507
|
# <input type="text" name="post[title]">
|
498
508
|
# </form>
|
499
509
|
#
|
@@ -502,7 +512,7 @@ module ActionView
|
|
502
512
|
# <%= form.text_field :title %>
|
503
513
|
# <% end %>
|
504
514
|
# # =>
|
505
|
-
# <form action="/posts" method="post"
|
515
|
+
# <form action="/posts" method="post">
|
506
516
|
# <input type="text" name="post[title]">
|
507
517
|
# </form>
|
508
518
|
#
|
@@ -511,7 +521,7 @@ module ActionView
|
|
511
521
|
# <%= form.text_field :title %>
|
512
522
|
# <% end %>
|
513
523
|
# # =>
|
514
|
-
# <form action="/posts/1" method="post"
|
524
|
+
# <form action="/posts/1" method="post">
|
515
525
|
# <input type="hidden" name="_method" value="patch">
|
516
526
|
# <input type="text" name="post[title]" value="<the title of the post>">
|
517
527
|
# </form>
|
@@ -522,7 +532,7 @@ module ActionView
|
|
522
532
|
# <%= form.text_field :but_in_forms_they_can %>
|
523
533
|
# <% end %>
|
524
534
|
# # =>
|
525
|
-
# <form action="/cats" method="post"
|
535
|
+
# <form action="/cats" method="post">
|
526
536
|
# <input type="text" name="cat[cats_dont_have_gills]">
|
527
537
|
# <input type="text" name="cat[but_in_forms_they_can]">
|
528
538
|
# </form>
|
@@ -740,13 +750,14 @@ module ActionView
|
|
740
750
|
# form_with(**options.merge(builder: LabellingFormBuilder), &block)
|
741
751
|
# end
|
742
752
|
def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
|
743
|
-
options
|
744
|
-
options[:skip_default_ids] = !form_with_generates_ids
|
753
|
+
options = { allow_method_names_outside_object: true, skip_default_ids: !form_with_generates_ids }.merge!(options)
|
745
754
|
|
746
755
|
if model
|
747
|
-
url
|
756
|
+
if url != false
|
757
|
+
url ||= polymorphic_path(model, format: format)
|
758
|
+
end
|
748
759
|
|
749
|
-
model = model
|
760
|
+
model = _object_for_form_builder(model)
|
750
761
|
scope ||= model_name_from_record_or_class(model).param_key
|
751
762
|
end
|
752
763
|
|
@@ -1005,8 +1016,9 @@ module ActionView
|
|
1005
1016
|
# hidden field is not needed and you can pass <tt>include_id: false</tt>
|
1006
1017
|
# to prevent fields_for from rendering it automatically.
|
1007
1018
|
def fields_for(record_name, record_object = nil, options = {}, &block)
|
1008
|
-
|
1009
|
-
|
1019
|
+
options = { model: record_object, allow_method_names_outside_object: false, skip_default_ids: false }.merge!(options)
|
1020
|
+
|
1021
|
+
fields(record_name, **options, &block)
|
1010
1022
|
end
|
1011
1023
|
|
1012
1024
|
# Scopes input fields with either an explicit scope or model.
|
@@ -1055,10 +1067,10 @@ module ActionView
|
|
1055
1067
|
# to work with an object as a base, like
|
1056
1068
|
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
1057
1069
|
def fields(scope = nil, model: nil, **options, &block)
|
1058
|
-
options
|
1059
|
-
options[:skip_default_ids] = !form_with_generates_ids
|
1070
|
+
options = { allow_method_names_outside_object: true, skip_default_ids: !form_with_generates_ids }.merge!(options)
|
1060
1071
|
|
1061
1072
|
if model
|
1073
|
+
model = _object_for_form_builder(model)
|
1062
1074
|
scope ||= model_name_from_record_or_class(model).param_key
|
1063
1075
|
end
|
1064
1076
|
|
@@ -1220,7 +1232,7 @@ module ActionView
|
|
1220
1232
|
# file_field(:attachment, :file, class: 'file_input')
|
1221
1233
|
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
|
1222
1234
|
def file_field(object_name, method, options = {})
|
1223
|
-
Tags::FileField.new(object_name, method, self, convert_direct_upload_option_to_url(options.dup)).render
|
1235
|
+
Tags::FileField.new(object_name, method, self, convert_direct_upload_option_to_url(method, options.dup)).render
|
1224
1236
|
end
|
1225
1237
|
|
1226
1238
|
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
|
@@ -1257,6 +1269,12 @@ module ActionView
|
|
1257
1269
|
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
|
1258
1270
|
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
|
1259
1271
|
#
|
1272
|
+
# ==== Options
|
1273
|
+
#
|
1274
|
+
# * Any standard HTML attributes for the tag can be passed in, for example +:class+.
|
1275
|
+
# * <tt>:checked</tt> - +true+ or +false+ forces the state of the checkbox to be checked or not.
|
1276
|
+
# * <tt>:include_hidden</tt> - If set to false, the auxiliary hidden field described below will not be generated.
|
1277
|
+
#
|
1260
1278
|
# ==== Gotcha
|
1261
1279
|
#
|
1262
1280
|
# The HTML specification says unchecked check boxes are not successful, and
|
@@ -1270,7 +1288,7 @@ module ActionView
|
|
1270
1288
|
# wouldn't update the flag.
|
1271
1289
|
#
|
1272
1290
|
# To prevent this the helper generates an auxiliary hidden field before
|
1273
|
-
#
|
1291
|
+
# every check box. The hidden field has the same name and its
|
1274
1292
|
# attributes mimic an unchecked check box.
|
1275
1293
|
#
|
1276
1294
|
# This way, the client either sends only the hidden field (representing
|
@@ -1294,6 +1312,8 @@ module ActionView
|
|
1294
1312
|
# In that case it is preferable to either use +check_box_tag+ or to use
|
1295
1313
|
# hashes instead of arrays.
|
1296
1314
|
#
|
1315
|
+
# ==== Examples
|
1316
|
+
#
|
1297
1317
|
# # Let's say that @post.validated? is 1:
|
1298
1318
|
# check_box("post", "validated")
|
1299
1319
|
# # => <input name="post[validated]" type="hidden" value="0" />
|
@@ -1540,34 +1560,24 @@ module ActionView
|
|
1540
1560
|
Tags::RangeField.new(object_name, method, self, options).render
|
1541
1561
|
end
|
1542
1562
|
|
1563
|
+
def _object_for_form_builder(object) # :nodoc:
|
1564
|
+
object.is_a?(Array) ? object.last : object
|
1565
|
+
end
|
1566
|
+
|
1543
1567
|
private
|
1544
1568
|
def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: !form_with_generates_remote_forms,
|
1545
1569
|
skip_enforcing_utf8: nil, **options)
|
1546
|
-
html_options = options.slice(:id, :class, :multipart, :method, :data).merge(html)
|
1570
|
+
html_options = options.slice(:id, :class, :multipart, :method, :data, :authenticity_token).merge!(html)
|
1571
|
+
html_options[:remote] = html.delete(:remote) || !local
|
1547
1572
|
html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
html_options[:action] = url_for(url_for_options || {})
|
1555
|
-
html_options[:"accept-charset"] = "UTF-8"
|
1556
|
-
html_options[:"data-remote"] = true unless local
|
1557
|
-
|
1558
|
-
html_options[:authenticity_token] = options.delete(:authenticity_token)
|
1559
|
-
|
1560
|
-
if !local && html_options[:authenticity_token].blank?
|
1561
|
-
html_options[:authenticity_token] = embed_authenticity_token_in_remote_forms
|
1562
|
-
end
|
1563
|
-
|
1564
|
-
if html_options[:authenticity_token] == true
|
1565
|
-
# Include the default authenticity_token, which is only generated when it's set to nil,
|
1566
|
-
# but we needed the true value to override the default of no authenticity_token on data-remote.
|
1567
|
-
html_options[:authenticity_token] = nil
|
1573
|
+
if skip_enforcing_utf8.nil?
|
1574
|
+
if options.key?(:enforce_utf8)
|
1575
|
+
html_options[:enforce_utf8] = options[:enforce_utf8]
|
1576
|
+
end
|
1577
|
+
else
|
1578
|
+
html_options[:enforce_utf8] = !skip_enforcing_utf8
|
1568
1579
|
end
|
1569
|
-
|
1570
|
-
html_options.stringify_keys!
|
1580
|
+
html_options_for_form(url_for_options.nil? ? {} : url_for_options, html_options)
|
1571
1581
|
end
|
1572
1582
|
|
1573
1583
|
def instantiate_builder(record_name, record_object, options)
|
@@ -1738,6 +1748,28 @@ module ActionView
|
|
1738
1748
|
@template.field_id(@object_name, method, *suffixes, index: index)
|
1739
1749
|
end
|
1740
1750
|
|
1751
|
+
# Generate an HTML <tt>name</tt> attribute value for the given name and
|
1752
|
+
# field combination
|
1753
|
+
#
|
1754
|
+
# Return the value generated by the <tt>FormBuilder</tt> for the given
|
1755
|
+
# attribute name.
|
1756
|
+
#
|
1757
|
+
# <%= form_for @post do |f| %>
|
1758
|
+
# <%= f.text_field :title, name: f.field_name(:title, :subtitle) %>
|
1759
|
+
# <%# => <input type="text" name="post[title][subtitle]">
|
1760
|
+
# <% end %>
|
1761
|
+
#
|
1762
|
+
# <%= form_for @post do |f| %>
|
1763
|
+
# <%= f.field_tag :tag, name: f.field_name(:tag, multiple: true) %>
|
1764
|
+
# <%# => <input type="text" name="post[tag][]">
|
1765
|
+
# <% end %>
|
1766
|
+
#
|
1767
|
+
def field_name(method, *methods, multiple: false, index: @index)
|
1768
|
+
object_name = @options.fetch(:as) { @object_name }
|
1769
|
+
|
1770
|
+
@template.field_name(object_name, method, *methods, index: index, multiple: multiple)
|
1771
|
+
end
|
1772
|
+
|
1741
1773
|
##
|
1742
1774
|
# :method: text_field
|
1743
1775
|
#
|
@@ -2224,7 +2256,7 @@ module ActionView
|
|
2224
2256
|
return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
|
2225
2257
|
end
|
2226
2258
|
else
|
2227
|
-
record_object =
|
2259
|
+
record_object = @template._object_for_form_builder(record_name)
|
2228
2260
|
record_name = model_name_from_record_or_class(record_object).param_key
|
2229
2261
|
end
|
2230
2262
|
|
@@ -2334,6 +2366,12 @@ module ActionView
|
|
2334
2366
|
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
|
2335
2367
|
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
|
2336
2368
|
#
|
2369
|
+
# ==== Options
|
2370
|
+
#
|
2371
|
+
# * Any standard HTML attributes for the tag can be passed in, for example +:class+.
|
2372
|
+
# * <tt>:checked</tt> - +true+ or +false+ forces the state of the checkbox to be checked or not.
|
2373
|
+
# * <tt>:include_hidden</tt> - If set to false, the auxiliary hidden field described below will not be generated.
|
2374
|
+
#
|
2337
2375
|
# ==== Gotcha
|
2338
2376
|
#
|
2339
2377
|
# The HTML specification says unchecked check boxes are not successful, and
|
@@ -2347,7 +2385,7 @@ module ActionView
|
|
2347
2385
|
# wouldn't update the flag.
|
2348
2386
|
#
|
2349
2387
|
# To prevent this the helper generates an auxiliary hidden field before
|
2350
|
-
#
|
2388
|
+
# every check box. The hidden field has the same name and its
|
2351
2389
|
# attributes mimic an unchecked check box.
|
2352
2390
|
#
|
2353
2391
|
# This way, the client either sends only the hidden field (representing
|
@@ -2371,6 +2409,8 @@ module ActionView
|
|
2371
2409
|
# In that case it is preferable to either use +check_box_tag+ or to use
|
2372
2410
|
# hashes instead of arrays.
|
2373
2411
|
#
|
2412
|
+
# ==== Examples
|
2413
|
+
#
|
2374
2414
|
# # Let's say that @post.validated? is 1:
|
2375
2415
|
# check_box("validated")
|
2376
2416
|
# # => <input name="post[validated]" type="hidden" value="0" />
|
@@ -2536,6 +2576,9 @@ module ActionView
|
|
2536
2576
|
# button("Create post")
|
2537
2577
|
# # => <button name='button' type='submit'>Create post</button>
|
2538
2578
|
#
|
2579
|
+
# button(:draft, value: true)
|
2580
|
+
# # => <button name="post[draft]" value="true" type="submit">Create post</button>
|
2581
|
+
#
|
2539
2582
|
# button do
|
2540
2583
|
# content_tag(:strong, 'Ask me!')
|
2541
2584
|
# end
|
@@ -2550,8 +2593,20 @@ module ActionView
|
|
2550
2593
|
# # <strong>Create post</strong>
|
2551
2594
|
# # </button>
|
2552
2595
|
#
|
2596
|
+
# button(:draft, value: true) do
|
2597
|
+
# content_tag(:strong, "Save as draft")
|
2598
|
+
# end
|
2599
|
+
# # => <button name="post[draft]" value="true" type="submit">
|
2600
|
+
# # <strong>Save as draft</strong>
|
2601
|
+
# # </button>
|
2602
|
+
#
|
2553
2603
|
def button(value = nil, options = {}, &block)
|
2554
|
-
|
2604
|
+
case value
|
2605
|
+
when Hash
|
2606
|
+
value, options = nil, value
|
2607
|
+
when Symbol
|
2608
|
+
value, options[:name] = nil, field_name(value)
|
2609
|
+
end
|
2555
2610
|
value ||= submit_default_value
|
2556
2611
|
|
2557
2612
|
if block_given?
|
@@ -597,15 +597,18 @@ module ActionView
|
|
597
597
|
# Returns a string of option tags for the days of the week.
|
598
598
|
#
|
599
599
|
# Options:
|
600
|
-
# * <tt>:index_as_value</tt> - Defaults to false, set to true to use the
|
600
|
+
# * <tt>:index_as_value</tt> - Defaults to false, set to true to use the indexes from
|
601
|
+
# `I18n.translate("date.day_names")` as the values. By default, Sunday is always 0.
|
601
602
|
# * <tt>:day_format</tt> - The I18n key of the array to use for the weekday options.
|
602
603
|
# Defaults to :day_names, set to :abbr_day_names for abbreviations.
|
604
|
+
# * <tt>:beginning_of_week</tt> - Defaults to Date.beginning_of_week.
|
603
605
|
#
|
604
606
|
# NOTE: Only the option tags are returned, you have to wrap this call in
|
605
607
|
# a regular HTML select tag.
|
606
|
-
def weekday_options_for_select(selected = nil, index_as_value: false, day_format: :day_names)
|
608
|
+
def weekday_options_for_select(selected = nil, index_as_value: false, day_format: :day_names, beginning_of_week: Date.beginning_of_week)
|
607
609
|
day_names = I18n.translate("date.#{day_format}")
|
608
|
-
day_names = day_names.map.with_index.
|
610
|
+
day_names = day_names.map.with_index.to_a if index_as_value
|
611
|
+
day_names = day_names.rotate(Date::DAYS_INTO_WEEK.fetch(beginning_of_week))
|
609
612
|
|
610
613
|
options_for_select(day_names, selected)
|
611
614
|
end
|
@@ -62,6 +62,9 @@ module ActionView
|
|
62
62
|
#
|
63
63
|
# <%= form_tag('/posts', remote: true) %>
|
64
64
|
# # => <form action="/posts" method="post" data-remote="true">
|
65
|
+
|
66
|
+
# form_tag(false, method: :get)
|
67
|
+
# # => <form method="get">
|
65
68
|
#
|
66
69
|
# form_tag('http://far.away.com/form', authenticity_token: false)
|
67
70
|
# # form without authenticity token
|
@@ -114,6 +117,32 @@ module ActionView
|
|
114
117
|
end
|
115
118
|
end
|
116
119
|
|
120
|
+
# Generate an HTML <tt>name</tt> attribute value for the given name and
|
121
|
+
# field combination
|
122
|
+
#
|
123
|
+
# Return the value generated by the <tt>FormBuilder</tt> for the given
|
124
|
+
# attribute name.
|
125
|
+
#
|
126
|
+
# <%= text_field_tag :post, :title, name: field_name(:post, :title, :subtitle) %>
|
127
|
+
# <%# => <input type="text" name="post[title][subtitle]">
|
128
|
+
#
|
129
|
+
# <%= text_field_tag :post, :tag, name: field_name(:post, :tag, multiple: true) %>
|
130
|
+
# <%# => <input type="text" name="post[tag][]">
|
131
|
+
#
|
132
|
+
def field_name(object_name, method_name, *method_names, multiple: false, index: nil)
|
133
|
+
names = method_names.map! { |name| "[#{name}]" }.join
|
134
|
+
|
135
|
+
# a little duplication to construct fewer strings
|
136
|
+
case
|
137
|
+
when object_name.empty?
|
138
|
+
"#{method_name}#{names}#{multiple ? "[]" : ""}"
|
139
|
+
when index
|
140
|
+
"#{object_name}[#{index}][#{method_name}]#{names}#{multiple ? "[]" : ""}"
|
141
|
+
else
|
142
|
+
"#{object_name}[#{method_name}]#{names}#{multiple ? "[]" : ""}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
117
146
|
# Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
|
118
147
|
# choice selection box.
|
119
148
|
#
|
@@ -277,7 +306,7 @@ module ActionView
|
|
277
306
|
# # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
|
278
307
|
# # type="hidden" value="" />
|
279
308
|
def hidden_field_tag(name, value = nil, options = {})
|
280
|
-
text_field_tag(name, value, options.merge(type: :hidden))
|
309
|
+
text_field_tag(name, value, options.merge(type: :hidden, autocomplete: "off"))
|
281
310
|
end
|
282
311
|
|
283
312
|
# Creates a file upload field. If you are using file uploads then you will also need
|
@@ -316,7 +345,7 @@ module ActionView
|
|
316
345
|
# file_field_tag 'file', accept: 'text/html', class: 'upload', value: 'index.html'
|
317
346
|
# # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
|
318
347
|
def file_field_tag(name, options = {})
|
319
|
-
text_field_tag(name, nil, convert_direct_upload_option_to_url(options.merge(type: :file)))
|
348
|
+
text_field_tag(name, nil, convert_direct_upload_option_to_url(name, options.merge(type: :file)))
|
320
349
|
end
|
321
350
|
|
322
351
|
# Creates a password field, a masked text field that will hide the users input behind a mask character.
|
@@ -866,7 +895,7 @@ module ActionView
|
|
866
895
|
# Use raw HTML to ensure the value is written as an HTML entity; it
|
867
896
|
# needs to be the right character regardless of which encoding the
|
868
897
|
# browser infers.
|
869
|
-
'<input name="utf8" type="hidden" value="✓" />'.html_safe
|
898
|
+
'<input name="utf8" type="hidden" value="✓" autocomplete="off" />'.html_safe
|
870
899
|
end
|
871
900
|
|
872
901
|
private
|
@@ -875,13 +904,17 @@ module ActionView
|
|
875
904
|
html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
|
876
905
|
# The following URL is unescaped, this is just a hash of options, and it is the
|
877
906
|
# responsibility of the caller to escape all the values.
|
878
|
-
html_options["action"]
|
907
|
+
if url_for_options == false || html_options["action"] == false
|
908
|
+
html_options.delete("action")
|
909
|
+
else
|
910
|
+
html_options["action"] = url_for(url_for_options)
|
911
|
+
end
|
879
912
|
html_options["accept-charset"] = "UTF-8"
|
880
913
|
|
881
914
|
html_options["data-remote"] = true if html_options.delete("remote")
|
882
915
|
|
883
916
|
if html_options["data-remote"] &&
|
884
|
-
|
917
|
+
embed_authenticity_token_in_remote_forms == false &&
|
885
918
|
html_options["authenticity_token"].blank?
|
886
919
|
# The authenticity token is taken from the meta tag in this case
|
887
920
|
html_options["authenticity_token"] = false
|
@@ -954,9 +987,23 @@ module ActionView
|
|
954
987
|
tag_options.delete("data-disable-with")
|
955
988
|
end
|
956
989
|
|
957
|
-
def convert_direct_upload_option_to_url(options)
|
990
|
+
def convert_direct_upload_option_to_url(name, options)
|
958
991
|
if options.delete(:direct_upload) && respond_to?(:rails_direct_uploads_url)
|
959
992
|
options["data-direct-upload-url"] = rails_direct_uploads_url
|
993
|
+
|
994
|
+
if options[:object] && options[:object].class.respond_to?(:reflect_on_attachment)
|
995
|
+
attachment_reflection = options[:object].class.reflect_on_attachment(name)
|
996
|
+
|
997
|
+
class_with_attachment = "#{options[:object].class.name.underscore}##{name}"
|
998
|
+
options["data-direct-upload-attachment-name"] = class_with_attachment
|
999
|
+
|
1000
|
+
service_name = attachment_reflection.options[:service_name] || ActiveStorage::Blob.service.name
|
1001
|
+
options["data-direct-upload-token"] = ActiveStorage::DirectUploadToken.generate_direct_upload_token(
|
1002
|
+
class_with_attachment,
|
1003
|
+
service_name,
|
1004
|
+
session
|
1005
|
+
)
|
1006
|
+
end
|
960
1007
|
end
|
961
1008
|
options
|
962
1009
|
end
|
@@ -45,7 +45,8 @@ module ActionView
|
|
45
45
|
include CaptureHelper
|
46
46
|
include OutputSafetyHelper
|
47
47
|
|
48
|
-
|
48
|
+
HTML_VOID_ELEMENTS = %i(area base br col circle embed hr img input keygen link meta param source track wbr).to_set
|
49
|
+
SVG_VOID_ELEMENTS = %i(animate animateMotion animateTransform circle ellipse line path polygon polyline rect set stop use view).to_set
|
49
50
|
|
50
51
|
def initialize(view_context)
|
51
52
|
@view_context = view_context
|
@@ -66,7 +67,7 @@ module ActionView
|
|
66
67
|
|
67
68
|
def tag_string(name, content = nil, escape_attributes: true, **options, &block)
|
68
69
|
content = @view_context.capture(self, &block) if block_given?
|
69
|
-
if
|
70
|
+
if (HTML_VOID_ELEMENTS.include?(name) || SVG_VOID_ELEMENTS.include?(name)) && content.nil?
|
70
71
|
"<#{name.to_s.dasherize}#{tag_options(options, escape_attributes)}>".html_safe
|
71
72
|
else
|
72
73
|
content_tag_string(name.to_s.dasherize, content || "", options, escape_attributes)
|
@@ -234,6 +235,20 @@ module ActionView
|
|
234
235
|
# # A void element:
|
235
236
|
# tag.br # => <br>
|
236
237
|
#
|
238
|
+
# === Building HTML attributes
|
239
|
+
#
|
240
|
+
# Transforms a Hash into HTML attributes, ready to be interpolated into
|
241
|
+
# ERB. Includes or omits boolean attributes based on their truthiness.
|
242
|
+
# Transforms keys nested within
|
243
|
+
# <tt>aria:</tt> or <tt>data:</tt> objects into `aria-` and `data-`
|
244
|
+
# prefixed attributes:
|
245
|
+
#
|
246
|
+
# <input <%= tag.attributes(type: :text, aria: { label: "Search" }) %>>
|
247
|
+
# # => <input type="text" aria-label="Search">
|
248
|
+
#
|
249
|
+
# <button <%= tag.attributes id: "call-to-action", disabled: false, aria: { expanded: false } %> class="primary">Get Started!</button>
|
250
|
+
# # => <button id="call-to-action" aria-expanded="false" class="primary">Get Started!</button>
|
251
|
+
#
|
237
252
|
# === Legacy syntax
|
238
253
|
#
|
239
254
|
# The following format is for legacy syntax support. It will be deprecated in future versions of Rails.
|
@@ -105,15 +105,7 @@ module ActionView
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def tag_name(multiple = false, index = nil)
|
108
|
-
|
109
|
-
case
|
110
|
-
when @object_name.empty?
|
111
|
-
"#{sanitized_method_name}#{multiple ? "[]" : ""}"
|
112
|
-
when index
|
113
|
-
"#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
|
114
|
-
else
|
115
|
-
"#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
|
116
|
-
end
|
108
|
+
@template_object.field_name(@object_name, sanitized_method_name, multiple: multiple, index: index)
|
117
109
|
end
|
118
110
|
|
119
111
|
def tag_id(index = nil)
|
@@ -141,7 +133,7 @@ module ActionView
|
|
141
133
|
select = content_tag("select", add_options(option_tags, options, value), html_options)
|
142
134
|
|
143
135
|
if html_options["multiple"] && options.fetch(:include_hidden, true)
|
144
|
-
tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "") + select
|
136
|
+
tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "", autocomplete: "off") + select
|
145
137
|
else
|
146
138
|
select
|
147
139
|
end
|
@@ -57,7 +57,7 @@ module ActionView
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def hidden_field_for_checkbox(options)
|
60
|
-
@unchecked_value ? tag("input", options.slice("name", "disabled", "form").merge!("type" => "hidden", "value" => @unchecked_value)) : "".html_safe
|
60
|
+
@unchecked_value ? tag("input", options.slice("name", "disabled", "form").merge!("type" => "hidden", "value" => @unchecked_value, "autocomplete" => "off")) : "".html_safe
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
@@ -15,7 +15,8 @@ module ActionView
|
|
15
15
|
weekday_options_for_select(
|
16
16
|
value || @options[:selected],
|
17
17
|
index_as_value: @options.fetch(:index_as_value, false),
|
18
|
-
day_format: @options.fetch(:day_format, :day_names)
|
18
|
+
day_format: @options.fetch(:day_format, :day_names),
|
19
|
+
beginning_of_week: @options.fetch(:beginning_of_week, Date.beginning_of_week)
|
19
20
|
),
|
20
21
|
@options,
|
21
22
|
@html_options
|