actionview 5.0.7.2 → 5.1.0.beta1
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 +5 -5
- data/CHANGELOG.md +92 -384
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/action_view.rb +5 -5
- data/lib/action_view/base.rb +19 -19
- data/lib/action_view/buffers.rb +1 -1
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/dependency_tracker.rb +4 -5
- data/lib/action_view/digestor.rb +6 -7
- data/lib/action_view/flows.rb +5 -6
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers.rb +1 -1
- data/lib/action_view/helpers/active_model_helper.rb +8 -8
- data/lib/action_view/helpers/asset_tag_helper.rb +62 -36
- data/lib/action_view/helpers/asset_url_helper.rb +111 -49
- data/lib/action_view/helpers/atom_feed_helper.rb +12 -13
- data/lib/action_view/helpers/cache_helper.rb +34 -20
- data/lib/action_view/helpers/capture_helper.rb +2 -2
- data/lib/action_view/helpers/controller_helper.rb +3 -11
- data/lib/action_view/helpers/csrf_helper.rb +3 -3
- data/lib/action_view/helpers/date_helper.rb +109 -107
- data/lib/action_view/helpers/debug_helper.rb +2 -3
- data/lib/action_view/helpers/form_helper.rb +406 -31
- data/lib/action_view/helpers/form_options_helper.rb +12 -12
- data/lib/action_view/helpers/form_tag_helper.rb +19 -18
- data/lib/action_view/helpers/javascript_helper.rb +6 -6
- data/lib/action_view/helpers/number_helper.rb +48 -46
- data/lib/action_view/helpers/output_safety_helper.rb +8 -8
- data/lib/action_view/helpers/rendering_helper.rb +2 -2
- data/lib/action_view/helpers/sanitize_helper.rb +6 -8
- data/lib/action_view/helpers/tag_helper.rb +194 -77
- data/lib/action_view/helpers/tags/base.rb +121 -102
- data/lib/action_view/helpers/tags/check_box.rb +17 -17
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +8 -8
- data/lib/action_view/helpers/tags/collection_helpers.rb +60 -60
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +2 -2
- data/lib/action_view/helpers/tags/collection_select.rb +2 -2
- data/lib/action_view/helpers/tags/date_select.rb +36 -36
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +2 -2
- data/lib/action_view/helpers/tags/label.rb +4 -0
- data/lib/action_view/helpers/tags/password_field.rb +1 -1
- data/lib/action_view/helpers/tags/radio_button.rb +4 -4
- data/lib/action_view/helpers/tags/select.rb +9 -9
- data/lib/action_view/helpers/tags/text_area.rb +1 -1
- data/lib/action_view/helpers/tags/text_field.rb +5 -5
- data/lib/action_view/helpers/tags/translator.rb +14 -12
- data/lib/action_view/helpers/text_helper.rb +20 -19
- data/lib/action_view/helpers/translation_helper.rb +6 -6
- data/lib/action_view/helpers/url_helper.rb +42 -38
- data/lib/action_view/layouts.rb +51 -47
- data/lib/action_view/log_subscriber.rb +24 -9
- data/lib/action_view/lookup_context.rb +19 -25
- data/lib/action_view/path_set.rb +19 -19
- data/lib/action_view/railtie.rb +3 -3
- data/lib/action_view/record_identifier.rb +6 -6
- data/lib/action_view/renderer/abstract_renderer.rb +17 -17
- data/lib/action_view/renderer/partial_renderer.rb +188 -187
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +7 -1
- data/lib/action_view/renderer/streaming_template_renderer.rb +45 -47
- data/lib/action_view/renderer/template_renderer.rb +64 -66
- data/lib/action_view/rendering.rb +4 -5
- data/lib/action_view/routing_url_for.rb +9 -13
- data/lib/action_view/tasks/cache_digests.rake +7 -7
- data/lib/action_view/template.rb +26 -27
- data/lib/action_view/template/error.rb +5 -15
- data/lib/action_view/template/handlers.rb +4 -4
- data/lib/action_view/template/handlers/builder.rb +7 -7
- data/lib/action_view/template/handlers/erb.rb +9 -76
- data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +9 -0
- data/lib/action_view/template/handlers/erb/erubi.rb +81 -0
- data/lib/action_view/template/handlers/erb/erubis.rb +81 -0
- data/lib/action_view/template/html.rb +2 -4
- data/lib/action_view/template/resolver.rb +107 -90
- data/lib/action_view/template/text.rb +5 -8
- data/lib/action_view/template/types.rb +1 -1
- data/lib/action_view/test_case.rb +20 -21
- data/lib/action_view/testing/resolvers.rb +29 -30
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +20 -8
- data/lib/assets/compiled/rails-ujs.js +648 -0
- metadata +19 -14
@@ -4,7 +4,6 @@ module ActionView
|
|
4
4
|
# Provides a set of methods for making it easier to debug Rails objects.
|
5
5
|
module Helpers
|
6
6
|
module DebugHelper
|
7
|
-
|
8
7
|
include TagHelper
|
9
8
|
|
10
9
|
# Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
|
@@ -25,10 +24,10 @@ module ActionView
|
|
25
24
|
def debug(object)
|
26
25
|
Marshal::dump(object)
|
27
26
|
object = ERB::Util.html_escape(object.to_yaml)
|
28
|
-
content_tag(:pre, object, :
|
27
|
+
content_tag(:pre, object, class: "debug_dump")
|
29
28
|
rescue # errors from Marshal or YAML
|
30
29
|
# Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
|
31
|
-
content_tag(:code, object.inspect, :
|
30
|
+
content_tag(:code, object.inspect, class: "debug_dump")
|
32
31
|
end
|
33
32
|
end
|
34
33
|
end
|
@@ -1,14 +1,14 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
1
|
+
require "cgi"
|
2
|
+
require "action_view/helpers/date_helper"
|
3
|
+
require "action_view/helpers/tag_helper"
|
4
|
+
require "action_view/helpers/form_tag_helper"
|
5
|
+
require "action_view/helpers/active_model_helper"
|
6
|
+
require "action_view/model_naming"
|
7
|
+
require "action_view/record_identifier"
|
8
|
+
require "active_support/core_ext/module/attribute_accessors"
|
9
|
+
require "active_support/core_ext/hash/slice"
|
10
|
+
require "active_support/core_ext/string/output_safety"
|
11
|
+
require "active_support/core_ext/string/inflections"
|
12
12
|
|
13
13
|
module ActionView
|
14
14
|
# = Action View Form Helpers
|
@@ -467,13 +467,273 @@ module ActionView
|
|
467
467
|
)
|
468
468
|
|
469
469
|
options[:url] ||= if options.key?(:format)
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
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
|
|
477
|
+
# Creates a form tag based on mixing URLs, scopes, or models.
|
478
|
+
#
|
479
|
+
# # Using just a URL:
|
480
|
+
# <%= form_with url: posts_path do |form| %>
|
481
|
+
# <%= form.text_field :title %>
|
482
|
+
# <% end %>
|
483
|
+
# # =>
|
484
|
+
# <form action="/posts" method="post" data-remote="true">
|
485
|
+
# <input type="text" name="title">
|
486
|
+
# </form>
|
487
|
+
#
|
488
|
+
# # Adding a scope prefixes the input field names:
|
489
|
+
# <%= form_with scope: :post, url: posts_path do |form| %>
|
490
|
+
# <%= form.text_field :title %>
|
491
|
+
# <% end %>
|
492
|
+
# # =>
|
493
|
+
# <form action="/posts" method="post" data-remote="true">
|
494
|
+
# <input type="text" name="post[title]">
|
495
|
+
# </form>
|
496
|
+
#
|
497
|
+
# # Using a model infers both the URL and scope:
|
498
|
+
# <%= form_with model: Post.new do |form| %>
|
499
|
+
# <%= form.text_field :title %>
|
500
|
+
# <% end %>
|
501
|
+
# # =>
|
502
|
+
# <form action="/posts" method="post" data-remote="true">
|
503
|
+
# <input type="text" name="post[title]">
|
504
|
+
# </form>
|
505
|
+
#
|
506
|
+
# # An existing model makes an update form and fills out field values:
|
507
|
+
# <%= form_with model: Post.first do |form| %>
|
508
|
+
# <%= form.text_field :title %>
|
509
|
+
# <% end %>
|
510
|
+
# # =>
|
511
|
+
# <form action="/posts/1" method="post" data-remote="true">
|
512
|
+
# <input type="hidden" name="_method" value="patch">
|
513
|
+
# <input type="text" name="post[title]" value="<the title of the post>">
|
514
|
+
# </form>
|
515
|
+
#
|
516
|
+
# # Though the fields don't have to correspond to model attributes:
|
517
|
+
# <%= form_with model: Cat.new do |form| %>
|
518
|
+
# <%= form.text_field :cats_dont_have_gills %>
|
519
|
+
# <%= form.text_field :but_in_forms_they_can %>
|
520
|
+
# <% end %>
|
521
|
+
# # =>
|
522
|
+
# <form action="/cats" method="post" data-remote="true">
|
523
|
+
# <input type="text" name="cat[cats_dont_have_gills]">
|
524
|
+
# <input type="text" name="cat[but_in_forms_they_can]">
|
525
|
+
# </form>
|
526
|
+
#
|
527
|
+
# The parameters in the forms are accessible in controllers according to
|
528
|
+
# their name nesting. So inputs named +title+ and <tt>post[title]</tt> are
|
529
|
+
# accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
|
530
|
+
# respectively.
|
531
|
+
#
|
532
|
+
# By default +form_with+ attaches the <tt>data-remote</tt> attribute
|
533
|
+
# submitting the form via an XMLHTTPRequest in the background if an
|
534
|
+
# Unobtrusive JavaScript driver, like rails-ujs, is used. See the
|
535
|
+
# <tt>:local</tt> option for more.
|
536
|
+
#
|
537
|
+
# For ease of comparison the examples above left out the submit button,
|
538
|
+
# as well as the auto generated hidden fields that enable UTF-8 support
|
539
|
+
# and adds an authenticity token needed for cross site request forgery
|
540
|
+
# protection.
|
541
|
+
#
|
542
|
+
# ==== +form_with+ options
|
543
|
+
#
|
544
|
+
# * <tt>:url</tt> - The URL the form submits to. Akin to values passed to
|
545
|
+
# +url_for+ or +link_to+. For example, you may use a named route
|
546
|
+
# directly. When a <tt>:scope</tt> is passed without a <tt>:url</tt> the
|
547
|
+
# form just submits to the current URL.
|
548
|
+
# * <tt>:method</tt> - The method to use when submitting the form, usually
|
549
|
+
# either "get" or "post". If "patch", "put", "delete", or another verb
|
550
|
+
# is used, a hidden input named <tt>_method</tt> is added to
|
551
|
+
# simulate the verb over post.
|
552
|
+
# * <tt>:format</tt> - The format of the route the form submits to.
|
553
|
+
# Useful when submitting to another resource type, like <tt>:json</tt>.
|
554
|
+
# Skipped if a <tt>:url</tt> is passed.
|
555
|
+
# * <tt>:scope</tt> - The scope to prefix input field names with and
|
556
|
+
# thereby how the submitted parameters are grouped in controllers.
|
557
|
+
# * <tt>:model</tt> - A model object to infer the <tt>:url</tt> and
|
558
|
+
# <tt>:scope</tt> by, plus fill out input field values.
|
559
|
+
# So if a +title+ attribute is set to "Ahoy!" then a +title+ input
|
560
|
+
# field's value would be "Ahoy!".
|
561
|
+
# If the model is a new record a create form is generated, if an
|
562
|
+
# existing record, however, an update form is generated.
|
563
|
+
# Pass <tt>:scope</tt> or <tt>:url</tt> to override the defaults.
|
564
|
+
# E.g. turn <tt>params[:post]</tt> into <tt>params[:article]</tt>.
|
565
|
+
# * <tt>:authenticity_token</tt> - Authenticity token to use in the form.
|
566
|
+
# Override with a custom authenticity token or pass <tt>false</tt> to
|
567
|
+
# skip the authenticity token field altogether.
|
568
|
+
# Useful when submitting to an external resource like a payment gateway
|
569
|
+
# that might limit the valid fields.
|
570
|
+
# Remote forms may omit the embedded authenticity token by setting
|
571
|
+
# <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
|
572
|
+
# This is helpful when fragment-caching the form. Remote forms
|
573
|
+
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
574
|
+
# unnecessary unless you support browsers without JavaScript.
|
575
|
+
# * <tt>:local</tt> - By default form submits are remote and unobstrusive XHRs.
|
576
|
+
# Disable remote submits with <tt>local: true</tt>.
|
577
|
+
# * <tt>:skip_enforcing_utf8</tt> - By default a hidden field named +utf8+
|
578
|
+
# is output to enforce UTF-8 submits. Set to true to skip the field.
|
579
|
+
# * <tt>:builder</tt> - Override the object used to build the form.
|
580
|
+
# * <tt>:id</tt> - Optional HTML id attribute.
|
581
|
+
# * <tt>:class</tt> - Optional HTML class attribute.
|
582
|
+
# * <tt>:data</tt> - Optional HTML data attributes.
|
583
|
+
# * <tt>:html</tt> - Other optional HTML attributes for the form tag.
|
584
|
+
#
|
585
|
+
# === Examples
|
586
|
+
#
|
587
|
+
# When not passing a block, +form_with+ just generates an opening form tag.
|
588
|
+
#
|
589
|
+
# <%= form_with(model: @post, url: super_posts_path) %>
|
590
|
+
# <%= form_with(model: @post, scope: :article) %>
|
591
|
+
# <%= form_with(model: @post, format: :json) %>
|
592
|
+
# <%= form_with(model: @post, authenticity_token: false) %> # Disables the token.
|
593
|
+
#
|
594
|
+
# For namespaced routes, like +admin_post_url+:
|
595
|
+
#
|
596
|
+
# <%= form_with(model: [ :admin, @post ]) do |form| %>
|
597
|
+
# ...
|
598
|
+
# <% end %>
|
599
|
+
#
|
600
|
+
# If your resource has associations defined, for example, you want to add comments
|
601
|
+
# to the document given that the routes are set correctly:
|
602
|
+
#
|
603
|
+
# <%= form_with(model: [ @document, Comment.new ]) do |form| %>
|
604
|
+
# ...
|
605
|
+
# <% end %>
|
606
|
+
#
|
607
|
+
# Where <tt>@document = Document.find(params[:id])</tt>.
|
608
|
+
#
|
609
|
+
# When using labels +form_with+ requires setting the id on the field being
|
610
|
+
# labelled:
|
611
|
+
#
|
612
|
+
# <%= form_with(model: @post) do |form| %>
|
613
|
+
# <%= form.label :title %>
|
614
|
+
# <%= form.text_field :title, id: :post_title %>
|
615
|
+
# <% end %>
|
616
|
+
#
|
617
|
+
# See +label+ for more on how the +for+ attribute is derived.
|
618
|
+
#
|
619
|
+
# === Mixing with other form helpers
|
620
|
+
#
|
621
|
+
# While +form_with+ uses a FormBuilder object it's possible to mix and
|
622
|
+
# match the stand-alone FormHelper methods and methods
|
623
|
+
# from FormTagHelper:
|
624
|
+
#
|
625
|
+
# <%= form_with scope: :person do |form| %>
|
626
|
+
# <%= form.text_field :first_name %>
|
627
|
+
# <%= form.text_field :last_name %>
|
628
|
+
#
|
629
|
+
# <%= text_area :person, :biography %>
|
630
|
+
# <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
|
631
|
+
#
|
632
|
+
# <%= form.submit %>
|
633
|
+
# <% end %>
|
634
|
+
#
|
635
|
+
# Same goes for the methods in FormOptionHelper and DateHelper designed
|
636
|
+
# to work with an object as a base, like
|
637
|
+
# FormOptionHelper#collection_select and DateHelper#datetime_select.
|
638
|
+
#
|
639
|
+
# === Setting the method
|
640
|
+
#
|
641
|
+
# You can force the form to use the full array of HTTP verbs by setting
|
642
|
+
#
|
643
|
+
# method: (:get|:post|:patch|:put|:delete)
|
644
|
+
#
|
645
|
+
# in the options hash. If the verb is not GET or POST, which are natively
|
646
|
+
# supported by HTML forms, the form will be set to POST and a hidden input
|
647
|
+
# called _method will carry the intended verb for the server to interpret.
|
648
|
+
#
|
649
|
+
# === Setting HTML options
|
650
|
+
#
|
651
|
+
# You can set data attributes directly in a data hash, but HTML options
|
652
|
+
# besides id and class must be wrapped in an HTML key:
|
653
|
+
#
|
654
|
+
# <%= form_with(model: @post, data: { behavior: "autosave" }, html: { name: "go" }) do |form| %>
|
655
|
+
# ...
|
656
|
+
# <% end %>
|
657
|
+
#
|
658
|
+
# generates
|
659
|
+
#
|
660
|
+
# <form action="/posts/123" method="post" data-behavior="autosave" name="go">
|
661
|
+
# <input name="_method" type="hidden" value="patch" />
|
662
|
+
# ...
|
663
|
+
# </form>
|
664
|
+
#
|
665
|
+
# === Removing hidden model id's
|
666
|
+
#
|
667
|
+
# The +form_with+ method automatically includes the model id as a hidden field in the form.
|
668
|
+
# This is used to maintain the correlation between the form data and its associated model.
|
669
|
+
# Some ORM systems do not use IDs on nested models so in this case you want to be able
|
670
|
+
# to disable the hidden id.
|
671
|
+
#
|
672
|
+
# In the following example the Post model has many Comments stored within it in a NoSQL database,
|
673
|
+
# thus there is no primary key for comments.
|
674
|
+
#
|
675
|
+
# <%= form_with(model: @post) do |form| %>
|
676
|
+
# <%= form.fields(:comments, skip_id: true) do |fields| %>
|
677
|
+
# ...
|
678
|
+
# <% end %>
|
679
|
+
# <% end %>
|
680
|
+
#
|
681
|
+
# === Customized form builders
|
682
|
+
#
|
683
|
+
# You can also build forms using a customized FormBuilder class. Subclass
|
684
|
+
# FormBuilder and override or define some more helpers, then use your
|
685
|
+
# custom builder. For example, let's say you made a helper to
|
686
|
+
# automatically add labels to form inputs.
|
687
|
+
#
|
688
|
+
# <%= form_with model: @person, url: { action: "create" }, builder: LabellingFormBuilder do |form| %>
|
689
|
+
# <%= form.text_field :first_name %>
|
690
|
+
# <%= form.text_field :last_name %>
|
691
|
+
# <%= form.text_area :biography %>
|
692
|
+
# <%= form.check_box :admin %>
|
693
|
+
# <%= form.submit %>
|
694
|
+
# <% end %>
|
695
|
+
#
|
696
|
+
# In this case, if you use:
|
697
|
+
#
|
698
|
+
# <%= render form %>
|
699
|
+
#
|
700
|
+
# The rendered template is <tt>people/_labelling_form</tt> and the local
|
701
|
+
# variable referencing the form builder is called
|
702
|
+
# <tt>labelling_form</tt>.
|
703
|
+
#
|
704
|
+
# The custom FormBuilder class is automatically merged with the options
|
705
|
+
# of a nested +fields+ call, unless it's explicitly set.
|
706
|
+
#
|
707
|
+
# In many cases you will want to wrap the above in another helper, so you
|
708
|
+
# could do something like the following:
|
709
|
+
#
|
710
|
+
# def labelled_form_with(**options, &block)
|
711
|
+
# form_with(**options.merge(builder: LabellingFormBuilder), &block)
|
712
|
+
# end
|
713
|
+
def form_with(model: nil, scope: nil, url: nil, format: nil, **options)
|
714
|
+
options[:allow_method_names_outside_object] = true
|
715
|
+
options[:skip_default_ids] = true
|
716
|
+
|
717
|
+
if model
|
718
|
+
url ||= polymorphic_path(model, format: format)
|
719
|
+
|
720
|
+
model = model.last if model.is_a?(Array)
|
721
|
+
scope ||= model_name_from_record_or_class(model).param_key
|
722
|
+
end
|
723
|
+
|
724
|
+
if block_given?
|
725
|
+
builder = instantiate_builder(scope, model, options)
|
726
|
+
output = capture(builder, &Proc.new)
|
727
|
+
options[:multipart] ||= builder.multipart?
|
728
|
+
|
729
|
+
html_options = html_options_for_form_with(url, model, options)
|
730
|
+
form_tag_with_body(html_options, output)
|
731
|
+
else
|
732
|
+
html_options = html_options_for_form_with(url, model, options)
|
733
|
+
form_tag_html(html_options)
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
477
737
|
# Creates a scope around a specific model object like form_for, but
|
478
738
|
# doesn't create the form tags themselves. This makes fields_for suitable
|
479
739
|
# for specifying additional model objects in the same form.
|
@@ -720,6 +980,74 @@ module ActionView
|
|
720
980
|
capture(builder, &block)
|
721
981
|
end
|
722
982
|
|
983
|
+
# Scopes input fields with either an explicit scope or model.
|
984
|
+
# Like +form_with+ does with <tt>:scope</tt> or <tt>:model</tt>,
|
985
|
+
# except it doesn't output the form tags.
|
986
|
+
#
|
987
|
+
# # Using a scope prefixes the input field names:
|
988
|
+
# <%= fields :comment do |fields| %>
|
989
|
+
# <%= fields.text_field :body %>
|
990
|
+
# <% end %>
|
991
|
+
# # => <input type="text" name="comment[body]>
|
992
|
+
#
|
993
|
+
# # Using a model infers the scope and assigns field values:
|
994
|
+
# <%= fields model: Comment.new(body: "full bodied") do |fields| %<
|
995
|
+
# <%= fields.text_field :body %>
|
996
|
+
# <% end %>
|
997
|
+
# # =>
|
998
|
+
# <input type="text" name="comment[body] value="full bodied">
|
999
|
+
#
|
1000
|
+
# # Using +fields+ with +form_with+:
|
1001
|
+
# <%= form_with model: @post do |form| %>
|
1002
|
+
# <%= form.text_field :title %>
|
1003
|
+
#
|
1004
|
+
# <%= form.fields :comment do |fields| %>
|
1005
|
+
# <%= fields.text_field :body %>
|
1006
|
+
# <% end %>
|
1007
|
+
# <% end %>
|
1008
|
+
#
|
1009
|
+
# Much like +form_with+ a FormBuilder instance associated with the scope
|
1010
|
+
# or model is yielded, so any generated field names are prefixed with
|
1011
|
+
# either the passed scope or the scope inferred from the <tt>:model</tt>.
|
1012
|
+
#
|
1013
|
+
# When using labels +fields+ requires setting the id on the field being
|
1014
|
+
# labelled:
|
1015
|
+
#
|
1016
|
+
# <%= fields :comment do |fields| %>
|
1017
|
+
# <%= fields.label :body %>
|
1018
|
+
# <%= fields.text_field :body, id: :comment_body %>
|
1019
|
+
# <% end %>
|
1020
|
+
#
|
1021
|
+
# See +label+ for more on how the +for+ attribute is derived.
|
1022
|
+
#
|
1023
|
+
# === Mixing with other form helpers
|
1024
|
+
#
|
1025
|
+
# While +form_with+ uses a FormBuilder object it's possible to mix and
|
1026
|
+
# match the stand-alone FormHelper methods and methods
|
1027
|
+
# from FormTagHelper:
|
1028
|
+
#
|
1029
|
+
# <%= fields model: @comment do |fields| %>
|
1030
|
+
# <%= fields.text_field :body %>
|
1031
|
+
#
|
1032
|
+
# <%= text_area :commenter, :biography %>
|
1033
|
+
# <%= check_box_tag "comment[all_caps]", "1", @comment.commenter.hulk_mode? %>
|
1034
|
+
# <% end %>
|
1035
|
+
#
|
1036
|
+
# Same goes for the methods in FormOptionHelper and DateHelper designed
|
1037
|
+
# to work with an object as a base, like
|
1038
|
+
# FormOptionHelper#collection_select and DateHelper#datetime_select.
|
1039
|
+
def fields(scope = nil, model: nil, **options, &block)
|
1040
|
+
options[:allow_method_names_outside_object] = true
|
1041
|
+
options[:skip_default_ids] = true
|
1042
|
+
|
1043
|
+
if model
|
1044
|
+
scope ||= model_name_from_record_or_class(model).param_key
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
builder = instantiate_builder(scope, model, options)
|
1048
|
+
capture(builder, &block)
|
1049
|
+
end
|
1050
|
+
|
723
1051
|
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
|
724
1052
|
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
|
725
1053
|
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
|
@@ -965,6 +1293,7 @@ module ActionView
|
|
965
1293
|
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
|
966
1294
|
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
|
967
1295
|
#
|
1296
|
+
# # Let's say that @user.receive_newsletter returns "no":
|
968
1297
|
# radio_button("user", "receive_newsletter", "yes")
|
969
1298
|
# radio_button("user", "receive_newsletter", "no")
|
970
1299
|
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
|
@@ -1048,7 +1377,7 @@ module ActionView
|
|
1048
1377
|
# Returns a text_field of type "time".
|
1049
1378
|
#
|
1050
1379
|
# The default value is generated by trying to call +strftime+ with "%T.%L"
|
1051
|
-
# on the
|
1380
|
+
# on the object's value. It is still possible to override that
|
1052
1381
|
# by passing the "value" option.
|
1053
1382
|
#
|
1054
1383
|
# === Options
|
@@ -1174,6 +1503,32 @@ module ActionView
|
|
1174
1503
|
end
|
1175
1504
|
|
1176
1505
|
private
|
1506
|
+
def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: false,
|
1507
|
+
skip_enforcing_utf8: false, **options)
|
1508
|
+
html_options = options.slice(:id, :class, :multipart, :method, :data).merge(html)
|
1509
|
+
html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
|
1510
|
+
html_options[:enforce_utf8] = !skip_enforcing_utf8
|
1511
|
+
|
1512
|
+
html_options[:enctype] = "multipart/form-data" if html_options.delete(:multipart)
|
1513
|
+
|
1514
|
+
# The following URL is unescaped, this is just a hash of options, and it is the
|
1515
|
+
# responsibility of the caller to escape all the values.
|
1516
|
+
html_options[:action] = url_for(url_for_options || {})
|
1517
|
+
html_options[:"accept-charset"] = "UTF-8"
|
1518
|
+
html_options[:"data-remote"] = true unless local
|
1519
|
+
|
1520
|
+
if !local && !embed_authenticity_token_in_remote_forms &&
|
1521
|
+
html_options[:authenticity_token].blank?
|
1522
|
+
# The authenticity token is taken from the meta tag in this case
|
1523
|
+
html_options[:authenticity_token] = false
|
1524
|
+
elsif html_options[:authenticity_token] == true
|
1525
|
+
# Include the default authenticity_token, which is only generated when its set to nil,
|
1526
|
+
# but we needed the true value to override the default of no authenticity_token on data-remote.
|
1527
|
+
html_options[:authenticity_token] = nil
|
1528
|
+
end
|
1529
|
+
|
1530
|
+
html_options.stringify_keys!
|
1531
|
+
end
|
1177
1532
|
|
1178
1533
|
def instantiate_builder(record_name, record_object, options)
|
1179
1534
|
case record_name
|
@@ -1182,7 +1537,7 @@ module ActionView
|
|
1182
1537
|
object_name = record_name
|
1183
1538
|
else
|
1184
1539
|
object = record_name
|
1185
|
-
object_name = model_name_from_record_or_class(object).param_key
|
1540
|
+
object_name = model_name_from_record_or_class(object).param_key if object
|
1186
1541
|
end
|
1187
1542
|
|
1188
1543
|
builder = options[:builder] || default_form_builder_class
|
@@ -1248,7 +1603,7 @@ module ActionView
|
|
1248
1603
|
|
1249
1604
|
# The methods which wrap a form helper call.
|
1250
1605
|
class_attribute :field_helpers
|
1251
|
-
self.field_helpers = [:fields_for, :label, :text_field, :password_field,
|
1606
|
+
self.field_helpers = [:fields_for, :fields, :label, :text_field, :password_field,
|
1252
1607
|
:hidden_field, :file_field, :text_area, :check_box,
|
1253
1608
|
:radio_button, :color_field, :search_field,
|
1254
1609
|
:telephone_field, :phone_field, :date_field,
|
@@ -1270,7 +1625,7 @@ module ActionView
|
|
1270
1625
|
end
|
1271
1626
|
|
1272
1627
|
def self._to_partial_path
|
1273
|
-
@_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/,
|
1628
|
+
@_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, "")
|
1274
1629
|
end
|
1275
1630
|
|
1276
1631
|
def to_partial_path
|
@@ -1284,19 +1639,23 @@ module ActionView
|
|
1284
1639
|
def initialize(object_name, object, template, options)
|
1285
1640
|
@nested_child_index = {}
|
1286
1641
|
@object_name, @object, @template, @options = object_name, object, template, options
|
1287
|
-
@default_options = @options ? @options.slice(:index, :namespace) : {}
|
1642
|
+
@default_options = @options ? @options.slice(:index, :namespace, :skip_default_ids, :allow_method_names_outside_object) : {}
|
1643
|
+
|
1644
|
+
convert_to_legacy_options(@options)
|
1645
|
+
|
1288
1646
|
if @object_name.to_s.match(/\[\]$/)
|
1289
|
-
if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}")
|
1647
|
+
if (object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}")) && object.respond_to?(:to_param)
|
1290
1648
|
@auto_index = object.to_param
|
1291
1649
|
else
|
1292
1650
|
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
|
1293
1651
|
end
|
1294
1652
|
end
|
1653
|
+
|
1295
1654
|
@multipart = nil
|
1296
1655
|
@index = options[:index] || options[:child_index]
|
1297
1656
|
end
|
1298
1657
|
|
1299
|
-
(field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
|
1658
|
+
(field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
|
1300
1659
|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
1301
1660
|
def #{selector}(method, options = {}) # def text_field(method, options = {})
|
1302
1661
|
@template.send( # @template.send(
|
@@ -1574,18 +1933,28 @@ module ActionView
|
|
1574
1933
|
end
|
1575
1934
|
|
1576
1935
|
record_name = if index
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1936
|
+
"#{object_name}[#{index}][#{record_name}]"
|
1937
|
+
elsif record_name.to_s.end_with?("[]")
|
1938
|
+
record_name = record_name.to_s.sub(/(.*)\[\]$/, "[\\1][#{record_object.id}]")
|
1939
|
+
"#{object_name}#{record_name}"
|
1940
|
+
else
|
1941
|
+
"#{object_name}[#{record_name}]"
|
1942
|
+
end
|
1584
1943
|
fields_options[:child_index] = index
|
1585
1944
|
|
1586
1945
|
@template.fields_for(record_name, record_object, fields_options, &block)
|
1587
1946
|
end
|
1588
1947
|
|
1948
|
+
# See the docs for the <tt>ActionView::FormHelper.fields</tt> helper method.
|
1949
|
+
def fields(scope = nil, model: nil, **options, &block)
|
1950
|
+
options[:allow_method_names_outside_object] = true
|
1951
|
+
options[:skip_default_ids] = true
|
1952
|
+
|
1953
|
+
convert_to_legacy_options(options)
|
1954
|
+
|
1955
|
+
fields_for(scope || model, model, **options, &block)
|
1956
|
+
end
|
1957
|
+
|
1589
1958
|
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
|
1590
1959
|
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
|
1591
1960
|
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
|
@@ -1712,7 +2081,7 @@ module ActionView
|
|
1712
2081
|
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
|
1713
2082
|
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
|
1714
2083
|
#
|
1715
|
-
# # Let's say that @user.
|
2084
|
+
# # Let's say that @user.receive_newsletter returns "no":
|
1716
2085
|
# radio_button("receive_newsletter", "yes")
|
1717
2086
|
# radio_button("receive_newsletter", "no")
|
1718
2087
|
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
|
@@ -1809,7 +2178,7 @@ module ActionView
|
|
1809
2178
|
# post:
|
1810
2179
|
# create: "Add %{model}"
|
1811
2180
|
#
|
1812
|
-
def submit(value=nil, options={})
|
2181
|
+
def submit(value = nil, options = {})
|
1813
2182
|
value, options = nil, value if value.is_a?(Hash)
|
1814
2183
|
value ||= submit_default_value
|
1815
2184
|
@template.submit_tag(value, options)
|
@@ -1934,6 +2303,12 @@ module ActionView
|
|
1934
2303
|
@nested_child_index[name] ||= -1
|
1935
2304
|
@nested_child_index[name] += 1
|
1936
2305
|
end
|
2306
|
+
|
2307
|
+
def convert_to_legacy_options(options)
|
2308
|
+
if options.key?(:skip_id)
|
2309
|
+
options[:include_id] = !options.delete(:skip_id)
|
2310
|
+
end
|
2311
|
+
end
|
1937
2312
|
end
|
1938
2313
|
end
|
1939
2314
|
|