actionview 6.1.4.1 → 7.0.0.rc2
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 +189 -248
- data/MIT-LICENSE +1 -1
- 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 +4 -4
- data/lib/action_view/helpers/active_model_helper.rb +2 -2
- data/lib/action_view/helpers/asset_tag_helper.rb +84 -29
- data/lib/action_view/helpers/asset_url_helper.rb +9 -9
- 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 +2 -2
- 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 +1 -1
- data/lib/action_view/helpers/date_helper.rb +62 -7
- data/lib/action_view/helpers/debug_helper.rb +3 -1
- data/lib/action_view/helpers/form_helper.rb +190 -75
- data/lib/action_view/helpers/form_options_helper.rb +68 -33
- data/lib/action_view/helpers/form_tag_helper.rb +126 -36
- data/lib/action_view/helpers/javascript_helper.rb +3 -5
- data/lib/action_view/helpers/number_helper.rb +3 -4
- data/lib/action_view/helpers/output_safety_helper.rb +2 -2
- data/lib/action_view/helpers/rendering_helper.rb +1 -1
- data/lib/action_view/helpers/sanitize_helper.rb +2 -2
- data/lib/action_view/helpers/tag_helper.rb +34 -6
- data/lib/action_view/helpers/tags/base.rb +4 -24
- data/lib/action_view/helpers/tags/check_box.rb +2 -2
- data/lib/action_view/helpers/tags/collection_select.rb +1 -1
- data/lib/action_view/helpers/tags/hidden_field.rb +4 -0
- 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 +24 -13
- data/lib/action_view/helpers/translation_helper.rb +10 -41
- data/lib/action_view/helpers/url_helper.rb +166 -91
- data/lib/action_view/helpers.rb +25 -25
- 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/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +2 -2
- data/lib/action_view/renderer/partial_renderer.rb +0 -34
- 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 +2 -2
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +1 -1
- 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 +84 -311
- 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 +6 -2
- data/lib/action_view/testing/resolvers.rb +11 -12
- data/lib/action_view/unbound_template.rb +33 -7
- data/lib/action_view.rb +3 -4
- metadata +22 -14
@@ -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 = 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
|
|
@@ -485,7 +485,16 @@ module ActionView
|
|
485
485
|
# <%= form.text_field :title %>
|
486
486
|
# <% end %>
|
487
487
|
# # =>
|
488
|
-
# <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">
|
489
498
|
# <input type="text" name="title">
|
490
499
|
# </form>
|
491
500
|
#
|
@@ -494,7 +503,7 @@ module ActionView
|
|
494
503
|
# <%= form.text_field :title %>
|
495
504
|
# <% end %>
|
496
505
|
# # =>
|
497
|
-
# <form action="/posts" method="post"
|
506
|
+
# <form action="/posts" method="post">
|
498
507
|
# <input type="text" name="post[title]">
|
499
508
|
# </form>
|
500
509
|
#
|
@@ -503,7 +512,7 @@ module ActionView
|
|
503
512
|
# <%= form.text_field :title %>
|
504
513
|
# <% end %>
|
505
514
|
# # =>
|
506
|
-
# <form action="/posts" method="post"
|
515
|
+
# <form action="/posts" method="post">
|
507
516
|
# <input type="text" name="post[title]">
|
508
517
|
# </form>
|
509
518
|
#
|
@@ -512,7 +521,7 @@ module ActionView
|
|
512
521
|
# <%= form.text_field :title %>
|
513
522
|
# <% end %>
|
514
523
|
# # =>
|
515
|
-
# <form action="/posts/1" method="post"
|
524
|
+
# <form action="/posts/1" method="post">
|
516
525
|
# <input type="hidden" name="_method" value="patch">
|
517
526
|
# <input type="text" name="post[title]" value="<the title of the post>">
|
518
527
|
# </form>
|
@@ -523,7 +532,7 @@ module ActionView
|
|
523
532
|
# <%= form.text_field :but_in_forms_they_can %>
|
524
533
|
# <% end %>
|
525
534
|
# # =>
|
526
|
-
# <form action="/cats" method="post"
|
535
|
+
# <form action="/cats" method="post">
|
527
536
|
# <input type="text" name="cat[cats_dont_have_gills]">
|
528
537
|
# <input type="text" name="cat[but_in_forms_they_can]">
|
529
538
|
# </form>
|
@@ -604,10 +613,16 @@ module ActionView
|
|
604
613
|
# This is helpful when fragment-caching the form. Remote forms
|
605
614
|
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
606
615
|
# unnecessary unless you support browsers without JavaScript.
|
607
|
-
# * <tt>:local</tt> -
|
608
|
-
#
|
609
|
-
#
|
610
|
-
#
|
616
|
+
# * <tt>:local</tt> - Whether to use standard HTTP form submission.
|
617
|
+
# When set to <tt>true</tt>, the form is submitted via standard HTTP.
|
618
|
+
# When set to <tt>false</tt>, the form is submitted as a "remote form", which
|
619
|
+
# is handled by Rails UJS as an XHR. When unspecified, the behavior is derived
|
620
|
+
# from <tt>config.action_view.form_with_generates_remote_forms</tt> where the
|
621
|
+
# config's value is actually the inverse of what <tt>local</tt>'s value would be.
|
622
|
+
# As of Rails 6.1, that configuration option defaults to <tt>false</tt>
|
623
|
+
# (which has the equivalent effect of passing <tt>local: true</tt>).
|
624
|
+
# In previous versions of Rails, that configuration option defaults to
|
625
|
+
# <tt>true</tt> (the equivalent of passing <tt>local: false</tt>).
|
611
626
|
# * <tt>:skip_enforcing_utf8</tt> - If set to true, a hidden input with name
|
612
627
|
# utf8 is not output.
|
613
628
|
# * <tt>:builder</tt> - Override the object used to build the form.
|
@@ -735,13 +750,14 @@ module ActionView
|
|
735
750
|
# form_with(**options.merge(builder: LabellingFormBuilder), &block)
|
736
751
|
# end
|
737
752
|
def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
|
738
|
-
options
|
739
|
-
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)
|
740
754
|
|
741
755
|
if model
|
742
|
-
url
|
756
|
+
if url != false
|
757
|
+
url ||= polymorphic_path(model, format: format)
|
758
|
+
end
|
743
759
|
|
744
|
-
model = model
|
760
|
+
model = _object_for_form_builder(model)
|
745
761
|
scope ||= model_name_from_record_or_class(model).param_key
|
746
762
|
end
|
747
763
|
|
@@ -1000,8 +1016,9 @@ module ActionView
|
|
1000
1016
|
# hidden field is not needed and you can pass <tt>include_id: false</tt>
|
1001
1017
|
# to prevent fields_for from rendering it automatically.
|
1002
1018
|
def fields_for(record_name, record_object = nil, options = {}, &block)
|
1003
|
-
|
1004
|
-
|
1019
|
+
options = { model: record_object, allow_method_names_outside_object: false, skip_default_ids: false }.merge!(options)
|
1020
|
+
|
1021
|
+
fields(record_name, **options, &block)
|
1005
1022
|
end
|
1006
1023
|
|
1007
1024
|
# Scopes input fields with either an explicit scope or model.
|
@@ -1050,10 +1067,10 @@ module ActionView
|
|
1050
1067
|
# to work with an object as a base, like
|
1051
1068
|
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
1052
1069
|
def fields(scope = nil, model: nil, **options, &block)
|
1053
|
-
options
|
1054
|
-
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)
|
1055
1071
|
|
1056
1072
|
if model
|
1073
|
+
model = _object_for_form_builder(model)
|
1057
1074
|
scope ||= model_name_from_record_or_class(model).param_key
|
1058
1075
|
end
|
1059
1076
|
|
@@ -1215,7 +1232,7 @@ module ActionView
|
|
1215
1232
|
# file_field(:attachment, :file, class: 'file_input')
|
1216
1233
|
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
|
1217
1234
|
def file_field(object_name, method, options = {})
|
1218
|
-
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
|
1219
1236
|
end
|
1220
1237
|
|
1221
1238
|
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
|
@@ -1252,6 +1269,12 @@ module ActionView
|
|
1252
1269
|
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
|
1253
1270
|
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
|
1254
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
|
+
#
|
1255
1278
|
# ==== Gotcha
|
1256
1279
|
#
|
1257
1280
|
# The HTML specification says unchecked check boxes are not successful, and
|
@@ -1265,7 +1288,7 @@ module ActionView
|
|
1265
1288
|
# wouldn't update the flag.
|
1266
1289
|
#
|
1267
1290
|
# To prevent this the helper generates an auxiliary hidden field before
|
1268
|
-
#
|
1291
|
+
# every check box. The hidden field has the same name and its
|
1269
1292
|
# attributes mimic an unchecked check box.
|
1270
1293
|
#
|
1271
1294
|
# This way, the client either sends only the hidden field (representing
|
@@ -1289,6 +1312,8 @@ module ActionView
|
|
1289
1312
|
# In that case it is preferable to either use +check_box_tag+ or to use
|
1290
1313
|
# hashes instead of arrays.
|
1291
1314
|
#
|
1315
|
+
# ==== Examples
|
1316
|
+
#
|
1292
1317
|
# # Let's say that @post.validated? is 1:
|
1293
1318
|
# check_box("post", "validated")
|
1294
1319
|
# # => <input name="post[validated]" type="hidden" value="0" />
|
@@ -1403,8 +1428,9 @@ module ActionView
|
|
1403
1428
|
# Returns a text_field of type "time".
|
1404
1429
|
#
|
1405
1430
|
# The default value is generated by trying to call +strftime+ with "%T.%L"
|
1406
|
-
# on the object's value.
|
1407
|
-
# by
|
1431
|
+
# on the object's value. If you pass <tt>include_seconds: false</tt>, it will be
|
1432
|
+
# formatted by trying to call +strftime+ with "%H:%M" on the object's value.
|
1433
|
+
# It is also possible to override this by passing the "value" option.
|
1408
1434
|
#
|
1409
1435
|
# === Options
|
1410
1436
|
# * Accepts same options as time_field_tag
|
@@ -1425,6 +1451,12 @@ module ActionView
|
|
1425
1451
|
# time_field("task", "started_at", min: "01:00:00")
|
1426
1452
|
# # => <input id="task_started_at" name="task[started_at]" type="time" min="01:00:00.000" />
|
1427
1453
|
#
|
1454
|
+
# By default, provided times will be formatted including seconds. You can render just the hour
|
1455
|
+
# and minute by passing <tt>include_seconds: false</tt>. Some browsers will render a simpler UI
|
1456
|
+
# if you exclude seconds in the timestamp format.
|
1457
|
+
#
|
1458
|
+
# time_field("task", "started_at", value: Time.now, include_seconds: false)
|
1459
|
+
# # => <input id="task_started_at" name="task[started_at]" type="time" value="01:00" />
|
1428
1460
|
def time_field(object_name, method, options = {})
|
1429
1461
|
Tags::TimeField.new(object_name, method, self, options).render
|
1430
1462
|
end
|
@@ -1528,34 +1560,24 @@ module ActionView
|
|
1528
1560
|
Tags::RangeField.new(object_name, method, self, options).render
|
1529
1561
|
end
|
1530
1562
|
|
1563
|
+
def _object_for_form_builder(object) # :nodoc:
|
1564
|
+
object.is_a?(Array) ? object.last : object
|
1565
|
+
end
|
1566
|
+
|
1531
1567
|
private
|
1532
1568
|
def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: !form_with_generates_remote_forms,
|
1533
1569
|
skip_enforcing_utf8: nil, **options)
|
1534
|
-
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
|
1535
1572
|
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
|
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
|
1556
1579
|
end
|
1557
|
-
|
1558
|
-
html_options.stringify_keys!
|
1580
|
+
html_options_for_form(url_for_options.nil? ? {} : url_for_options, html_options)
|
1559
1581
|
end
|
1560
1582
|
|
1561
1583
|
def instantiate_builder(record_name, record_object, options)
|
@@ -1685,6 +1707,69 @@ module ActionView
|
|
1685
1707
|
@index = options[:index] || options[:child_index]
|
1686
1708
|
end
|
1687
1709
|
|
1710
|
+
# Generate an HTML <tt>id</tt> attribute value.
|
1711
|
+
#
|
1712
|
+
# return the <tt><form></tt> element's <tt>id</tt> attribute.
|
1713
|
+
#
|
1714
|
+
# <%= form_for @post do |f| %>
|
1715
|
+
# <%# ... %>
|
1716
|
+
#
|
1717
|
+
# <% content_for :sticky_footer do %>
|
1718
|
+
# <%= form.button(form: f.id) %>
|
1719
|
+
# <% end %>
|
1720
|
+
# <% end %>
|
1721
|
+
#
|
1722
|
+
# In the example above, the <tt>:sticky_footer</tt> content area will
|
1723
|
+
# exist outside of the <tt><form></tt> element. By declaring the
|
1724
|
+
# <tt>form</tt> HTML attribute, we hint to the browser that the generated
|
1725
|
+
# <tt><button></tt> element should be treated as the <tt><form></tt>
|
1726
|
+
# element's submit button, regardless of where it exists in the DOM.
|
1727
|
+
def id
|
1728
|
+
options.dig(:html, :id)
|
1729
|
+
end
|
1730
|
+
|
1731
|
+
# Generate an HTML <tt>id</tt> attribute value for the given field
|
1732
|
+
#
|
1733
|
+
# Return the value generated by the <tt>FormBuilder</tt> for the given
|
1734
|
+
# attribute name.
|
1735
|
+
#
|
1736
|
+
# <%= form_for @post do |f| %>
|
1737
|
+
# <%= f.label :title %>
|
1738
|
+
# <%= f.text_field :title, aria: { describedby: f.field_id(:title, :error) } %>
|
1739
|
+
# <%= tag.span("is blank", id: f.field_id(:title, :error) %>
|
1740
|
+
# <% end %>
|
1741
|
+
#
|
1742
|
+
# In the example above, the <tt><input type="text"></tt> element built by
|
1743
|
+
# the call to <tt>FormBuilder#text_field</tt> declares an
|
1744
|
+
# <tt>aria-describedby</tt> attribute referencing the <tt><span></tt>
|
1745
|
+
# element, sharing a common <tt>id</tt> root (<tt>post_title</tt>, in this
|
1746
|
+
# case).
|
1747
|
+
def field_id(method, *suffixes, index: @index)
|
1748
|
+
@template.field_id(@object_name, method, *suffixes, index: index)
|
1749
|
+
end
|
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
|
+
|
1688
1773
|
##
|
1689
1774
|
# :method: text_field
|
1690
1775
|
#
|
@@ -2171,7 +2256,7 @@ module ActionView
|
|
2171
2256
|
return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
|
2172
2257
|
end
|
2173
2258
|
else
|
2174
|
-
record_object =
|
2259
|
+
record_object = @template._object_for_form_builder(record_name)
|
2175
2260
|
record_name = model_name_from_record_or_class(record_object).param_key
|
2176
2261
|
end
|
2177
2262
|
|
@@ -2281,6 +2366,12 @@ module ActionView
|
|
2281
2366
|
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
|
2282
2367
|
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
|
2283
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
|
+
#
|
2284
2375
|
# ==== Gotcha
|
2285
2376
|
#
|
2286
2377
|
# The HTML specification says unchecked check boxes are not successful, and
|
@@ -2294,7 +2385,7 @@ module ActionView
|
|
2294
2385
|
# wouldn't update the flag.
|
2295
2386
|
#
|
2296
2387
|
# To prevent this the helper generates an auxiliary hidden field before
|
2297
|
-
#
|
2388
|
+
# every check box. The hidden field has the same name and its
|
2298
2389
|
# attributes mimic an unchecked check box.
|
2299
2390
|
#
|
2300
2391
|
# This way, the client either sends only the hidden field (representing
|
@@ -2318,6 +2409,8 @@ module ActionView
|
|
2318
2409
|
# In that case it is preferable to either use +check_box_tag+ or to use
|
2319
2410
|
# hashes instead of arrays.
|
2320
2411
|
#
|
2412
|
+
# ==== Examples
|
2413
|
+
#
|
2321
2414
|
# # Let's say that @post.validated? is 1:
|
2322
2415
|
# check_box("validated")
|
2323
2416
|
# # => <input name="post[validated]" type="hidden" value="0" />
|
@@ -2386,7 +2479,7 @@ module ActionView
|
|
2386
2479
|
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
|
2387
2480
|
# shown.
|
2388
2481
|
#
|
2389
|
-
# Using this method inside a +
|
2482
|
+
# Using this method inside a +form_with+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
|
2390
2483
|
#
|
2391
2484
|
# ==== Options
|
2392
2485
|
# * Creates standard HTML attributes for the tag.
|
@@ -2483,6 +2576,9 @@ module ActionView
|
|
2483
2576
|
# button("Create post")
|
2484
2577
|
# # => <button name='button' type='submit'>Create post</button>
|
2485
2578
|
#
|
2579
|
+
# button(:draft, value: true)
|
2580
|
+
# # => <button name="post[draft]" value="true" type="submit">Create post</button>
|
2581
|
+
#
|
2486
2582
|
# button do
|
2487
2583
|
# content_tag(:strong, 'Ask me!')
|
2488
2584
|
# end
|
@@ -2497,14 +2593,31 @@ module ActionView
|
|
2497
2593
|
# # <strong>Create post</strong>
|
2498
2594
|
# # </button>
|
2499
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
|
+
#
|
2500
2603
|
def button(value = nil, options = {}, &block)
|
2501
|
-
|
2604
|
+
case value
|
2605
|
+
when Hash
|
2606
|
+
value, options = nil, value
|
2607
|
+
when Symbol
|
2608
|
+
value, options[:name] = nil, field_name(value)
|
2609
|
+
end
|
2502
2610
|
value ||= submit_default_value
|
2503
2611
|
|
2504
2612
|
if block_given?
|
2505
2613
|
value = @template.capture { yield(value) }
|
2506
2614
|
end
|
2507
2615
|
|
2616
|
+
formmethod = options[:formmethod]
|
2617
|
+
if formmethod.present? && !/post|get/i.match?(formmethod) && !options.key?(:name) && !options.key?(:value)
|
2618
|
+
options.merge! formmethod: :post, name: "_method", value: formmethod
|
2619
|
+
end
|
2620
|
+
|
2508
2621
|
@template.button_tag(value, options)
|
2509
2622
|
end
|
2510
2623
|
|
@@ -2565,7 +2678,9 @@ module ActionView
|
|
2565
2678
|
else
|
2566
2679
|
options[:child_index] = nested_child_index(name)
|
2567
2680
|
end
|
2568
|
-
|
2681
|
+
if content = fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
|
2682
|
+
output << content
|
2683
|
+
end
|
2569
2684
|
end
|
2570
2685
|
output
|
2571
2686
|
elsif association
|