actionview 4.2.11.1 → 5.2.7

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.

Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +108 -240
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -6
  5. data/lib/action_view/base.rb +38 -28
  6. data/lib/action_view/buffers.rb +3 -1
  7. data/lib/action_view/context.rb +3 -3
  8. data/lib/action_view/dependency_tracker.rb +54 -20
  9. data/lib/action_view/digestor.rb +94 -83
  10. data/lib/action_view/flows.rb +11 -11
  11. data/lib/action_view/gem_version.rb +5 -3
  12. data/lib/action_view/helpers/active_model_helper.rb +17 -11
  13. data/lib/action_view/helpers/asset_tag_helper.rb +244 -62
  14. data/lib/action_view/helpers/asset_url_helper.rb +170 -67
  15. data/lib/action_view/helpers/atom_feed_helper.rb +19 -17
  16. data/lib/action_view/helpers/cache_helper.rb +105 -42
  17. data/lib/action_view/helpers/capture_helper.rb +16 -13
  18. data/lib/action_view/helpers/controller_helper.rb +15 -4
  19. data/lib/action_view/helpers/csp_helper.rb +24 -0
  20. data/lib/action_view/helpers/csrf_helper.rb +7 -5
  21. data/lib/action_view/helpers/date_helper.rb +170 -112
  22. data/lib/action_view/helpers/debug_helper.rb +7 -6
  23. data/lib/action_view/helpers/form_helper.rb +521 -127
  24. data/lib/action_view/helpers/form_options_helper.rb +109 -63
  25. data/lib/action_view/helpers/form_tag_helper.rb +110 -67
  26. data/lib/action_view/helpers/javascript_helper.rb +27 -12
  27. data/lib/action_view/helpers/number_helper.rb +77 -58
  28. data/lib/action_view/helpers/output_safety_helper.rb +36 -4
  29. data/lib/action_view/helpers/record_tag_helper.rb +14 -99
  30. data/lib/action_view/helpers/rendering_helper.rb +6 -5
  31. data/lib/action_view/helpers/sanitize_helper.rb +20 -15
  32. data/lib/action_view/helpers/tag_helper.rb +198 -73
  33. data/lib/action_view/helpers/tags/base.rb +134 -97
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -18
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -33
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +70 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -11
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +3 -1
  41. data/lib/action_view/helpers/tags/date_field.rb +2 -0
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -36
  43. data/lib/action_view/helpers/tags/datetime_field.rb +4 -2
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +2 -0
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
  50. data/lib/action_view/helpers/tags/label.rb +3 -1
  51. data/lib/action_view/helpers/tags/month_field.rb +2 -0
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -5
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  58. data/lib/action_view/helpers/tags/select.rb +11 -9
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  61. data/lib/action_view/helpers/tags/text_field.rb +8 -7
  62. data/lib/action_view/helpers/tags/time_field.rb +2 -0
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +17 -13
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +2 -0
  68. data/lib/action_view/helpers/tags.rb +3 -1
  69. data/lib/action_view/helpers/text_helper.rb +55 -36
  70. data/lib/action_view/helpers/translation_helper.rb +74 -32
  71. data/lib/action_view/helpers/url_helper.rb +159 -104
  72. data/lib/action_view/helpers.rb +5 -1
  73. data/lib/action_view/layouts.rb +65 -58
  74. data/lib/action_view/log_subscriber.rb +60 -8
  75. data/lib/action_view/lookup_context.rb +80 -65
  76. data/lib/action_view/model_naming.rb +3 -1
  77. data/lib/action_view/path_set.rb +30 -19
  78. data/lib/action_view/railtie.rb +39 -6
  79. data/lib/action_view/record_identifier.rb +53 -25
  80. data/lib/action_view/renderer/abstract_renderer.rb +21 -15
  81. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +57 -0
  82. data/lib/action_view/renderer/partial_renderer.rb +218 -214
  83. data/lib/action_view/renderer/renderer.rb +8 -6
  84. data/lib/action_view/renderer/streaming_template_renderer.rb +50 -48
  85. data/lib/action_view/renderer/template_renderer.rb +67 -66
  86. data/lib/action_view/rendering.rb +19 -14
  87. data/lib/action_view/routing_url_for.rb +27 -17
  88. data/lib/action_view/tasks/cache_digests.rake +25 -0
  89. data/lib/action_view/template/error.rb +16 -16
  90. data/lib/action_view/template/handlers/builder.rb +10 -11
  91. data/lib/action_view/template/handlers/erb/erubi.rb +83 -0
  92. data/lib/action_view/template/handlers/erb.rb +9 -80
  93. data/lib/action_view/template/handlers/html.rb +11 -0
  94. data/lib/action_view/template/handlers/raw.rb +3 -3
  95. data/lib/action_view/template/handlers.rb +11 -7
  96. data/lib/action_view/template/html.rb +5 -5
  97. data/lib/action_view/template/resolver.rb +140 -115
  98. data/lib/action_view/template/text.rb +8 -9
  99. data/lib/action_view/template/types.rb +18 -18
  100. data/lib/action_view/template.rb +56 -31
  101. data/lib/action_view/test_case.rb +50 -29
  102. data/lib/action_view/testing/resolvers.rb +31 -31
  103. data/lib/action_view/version.rb +3 -1
  104. data/lib/action_view/view_paths.rb +28 -34
  105. data/lib/action_view.rb +8 -7
  106. data/lib/assets/compiled/rails-ujs.js +720 -0
  107. metadata +25 -24
  108. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,22 +1,25 @@
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 'active_support/core_ext/module/attribute_accessors'
8
- require 'active_support/core_ext/hash/slice'
9
- require 'active_support/core_ext/string/output_safety'
10
- require 'active_support/core_ext/string/inflections'
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+ require "action_view/helpers/date_helper"
5
+ require "action_view/helpers/tag_helper"
6
+ require "action_view/helpers/form_tag_helper"
7
+ require "action_view/helpers/active_model_helper"
8
+ require "action_view/model_naming"
9
+ require "action_view/record_identifier"
10
+ require "active_support/core_ext/module/attribute_accessors"
11
+ require "active_support/core_ext/hash/slice"
12
+ require "active_support/core_ext/string/output_safety"
13
+ require "active_support/core_ext/string/inflections"
11
14
 
12
15
  module ActionView
13
16
  # = Action View Form Helpers
14
- module Helpers
17
+ module Helpers #:nodoc:
15
18
  # Form helpers are designed to make working with resources much easier
16
19
  # compared to using vanilla HTML.
17
20
  #
18
21
  # Typically, a form designed to create or update a resource reflects the
19
- # identity of the resource in several ways: (i) the url that the form is
22
+ # identity of the resource in several ways: (i) the URL that the form is
20
23
  # sent to (the form element's +action+ attribute) should result in a request
21
24
  # being routed to the appropriate controller action (with the appropriate <tt>:id</tt>
22
25
  # parameter in the case of an existing resource), (ii) input fields should
@@ -66,9 +69,10 @@ module ActionView
66
69
  #
67
70
  # In particular, thanks to the conventions followed in the generated field names, the
68
71
  # controller gets a nested hash <tt>params[:person]</tt> with the person attributes
69
- # set in the form. That hash is ready to be passed to <tt>Person.create</tt>:
72
+ # set in the form. That hash is ready to be passed to <tt>Person.new</tt>:
70
73
  #
71
- # if @person = Person.create(params[:person])
74
+ # @person = Person.new(params[:person])
75
+ # if @person.save
72
76
  # # success
73
77
  # else
74
78
  # # error handling
@@ -110,6 +114,9 @@ module ActionView
110
114
  include FormTagHelper
111
115
  include UrlHelper
112
116
  include ModelNaming
117
+ include RecordIdentifier
118
+
119
+ attr_internal :default_form_builder
113
120
 
114
121
  # Creates a form that allows the user to create or update the attributes
115
122
  # of a specific model object.
@@ -138,6 +145,7 @@ module ActionView
138
145
  # will get expanded to
139
146
  #
140
147
  # <%= text_field :person, :first_name %>
148
+ #
141
149
  # which results in an HTML <tt><input></tt> tag whose +name+ attribute is
142
150
  # <tt>person[first_name]</tt>. This means that when the form is submitted,
143
151
  # the value entered by the user will be available in the controller as
@@ -158,7 +166,7 @@ module ActionView
158
166
  # So for example you may use a named route directly. When the model is
159
167
  # represented by a string or symbol, as in the example above, if the
160
168
  # <tt>:url</tt> option is not specified, by default the form will be
161
- # sent back to the current url (We will describe below an alternative
169
+ # sent back to the current URL (We will describe below an alternative
162
170
  # resource-oriented usage of +form_for+ in which the URL does not need
163
171
  # to be specified explicitly).
164
172
  # * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
@@ -195,9 +203,9 @@ module ActionView
195
203
  # <%= f.submit %>
196
204
  # <% end %>
197
205
  #
198
- # This also works for the methods in FormOptionHelper and DateHelper that
206
+ # This also works for the methods in FormOptionsHelper and DateHelper that
199
207
  # are designed to work with an object as base, like
200
- # FormOptionHelper#collection_select and DateHelper#datetime_select.
208
+ # FormOptionsHelper#collection_select and DateHelper#datetime_select.
201
209
  #
202
210
  # === #form_for with a model object
203
211
  #
@@ -410,13 +418,13 @@ module ActionView
410
418
  #
411
419
  # To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
412
420
  #
413
- # <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
421
+ # <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f| %>
414
422
  # ...
415
423
  # <% end %>
416
424
  #
417
425
  # If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
418
426
  #
419
- # <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
427
+ # <%= form_for @invoice, url: external_url, authenticity_token: false do |f| %>
420
428
  # ...
421
429
  # <% end %>
422
430
  def form_for(record, options = {}, &block)
@@ -461,13 +469,297 @@ module ActionView
461
469
  )
462
470
 
463
471
  options[:url] ||= if options.key?(:format)
464
- polymorphic_path(record, format: options.delete(:format))
465
- else
466
- polymorphic_path(record, {})
467
- end
472
+ polymorphic_path(record, format: options.delete(:format))
473
+ else
474
+ polymorphic_path(record, {})
475
+ end
468
476
  end
469
477
  private :apply_form_for_options!
470
478
 
479
+ mattr_accessor :form_with_generates_remote_forms, default: true
480
+
481
+ mattr_accessor :form_with_generates_ids, default: false
482
+
483
+ # Creates a form tag based on mixing URLs, scopes, or models.
484
+ #
485
+ # # Using just a URL:
486
+ # <%= form_with url: posts_path do |form| %>
487
+ # <%= form.text_field :title %>
488
+ # <% end %>
489
+ # # =>
490
+ # <form action="/posts" method="post" data-remote="true">
491
+ # <input type="text" name="title">
492
+ # </form>
493
+ #
494
+ # # Adding a scope prefixes the input field names:
495
+ # <%= form_with scope: :post, url: posts_path do |form| %>
496
+ # <%= form.text_field :title %>
497
+ # <% end %>
498
+ # # =>
499
+ # <form action="/posts" method="post" data-remote="true">
500
+ # <input type="text" name="post[title]">
501
+ # </form>
502
+ #
503
+ # # Using a model infers both the URL and scope:
504
+ # <%= form_with model: Post.new do |form| %>
505
+ # <%= form.text_field :title %>
506
+ # <% end %>
507
+ # # =>
508
+ # <form action="/posts" method="post" data-remote="true">
509
+ # <input type="text" name="post[title]">
510
+ # </form>
511
+ #
512
+ # # An existing model makes an update form and fills out field values:
513
+ # <%= form_with model: Post.first do |form| %>
514
+ # <%= form.text_field :title %>
515
+ # <% end %>
516
+ # # =>
517
+ # <form action="/posts/1" method="post" data-remote="true">
518
+ # <input type="hidden" name="_method" value="patch">
519
+ # <input type="text" name="post[title]" value="<the title of the post>">
520
+ # </form>
521
+ #
522
+ # # Though the fields don't have to correspond to model attributes:
523
+ # <%= form_with model: Cat.new do |form| %>
524
+ # <%= form.text_field :cats_dont_have_gills %>
525
+ # <%= form.text_field :but_in_forms_they_can %>
526
+ # <% end %>
527
+ # # =>
528
+ # <form action="/cats" method="post" data-remote="true">
529
+ # <input type="text" name="cat[cats_dont_have_gills]">
530
+ # <input type="text" name="cat[but_in_forms_they_can]">
531
+ # </form>
532
+ #
533
+ # The parameters in the forms are accessible in controllers according to
534
+ # their name nesting. So inputs named +title+ and <tt>post[title]</tt> are
535
+ # accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
536
+ # respectively.
537
+ #
538
+ # By default +form_with+ attaches the <tt>data-remote</tt> attribute
539
+ # submitting the form via an XMLHTTPRequest in the background if an
540
+ # Unobtrusive JavaScript driver, like rails-ujs, is used. See the
541
+ # <tt>:local</tt> option for more.
542
+ #
543
+ # For ease of comparison the examples above left out the submit button,
544
+ # as well as the auto generated hidden fields that enable UTF-8 support
545
+ # and adds an authenticity token needed for cross site request forgery
546
+ # protection.
547
+ #
548
+ # === Resource-oriented style
549
+ #
550
+ # In many of the examples just shown, the +:model+ passed to +form_with+
551
+ # is a _resource_. It corresponds to a set of RESTful routes, most likely
552
+ # defined via +resources+ in <tt>config/routes.rb</tt>.
553
+ #
554
+ # So when passing such a model record, Rails infers the URL and method.
555
+ #
556
+ # <%= form_with model: @post do |form| %>
557
+ # ...
558
+ # <% end %>
559
+ #
560
+ # is then equivalent to something like:
561
+ #
562
+ # <%= form_with scope: :post, url: post_path(@post), method: :patch do |form| %>
563
+ # ...
564
+ # <% end %>
565
+ #
566
+ # And for a new record
567
+ #
568
+ # <%= form_with model: Post.new do |form| %>
569
+ # ...
570
+ # <% end %>
571
+ #
572
+ # is equivalent to something like:
573
+ #
574
+ # <%= form_with scope: :post, url: posts_path do |form| %>
575
+ # ...
576
+ # <% end %>
577
+ #
578
+ # ==== +form_with+ options
579
+ #
580
+ # * <tt>:url</tt> - The URL the form submits to. Akin to values passed to
581
+ # +url_for+ or +link_to+. For example, you may use a named route
582
+ # directly. When a <tt>:scope</tt> is passed without a <tt>:url</tt> the
583
+ # form just submits to the current URL.
584
+ # * <tt>:method</tt> - The method to use when submitting the form, usually
585
+ # either "get" or "post". If "patch", "put", "delete", or another verb
586
+ # is used, a hidden input named <tt>_method</tt> is added to
587
+ # simulate the verb over post.
588
+ # * <tt>:format</tt> - The format of the route the form submits to.
589
+ # Useful when submitting to another resource type, like <tt>:json</tt>.
590
+ # Skipped if a <tt>:url</tt> is passed.
591
+ # * <tt>:scope</tt> - The scope to prefix input field names with and
592
+ # thereby how the submitted parameters are grouped in controllers.
593
+ # * <tt>:model</tt> - A model object to infer the <tt>:url</tt> and
594
+ # <tt>:scope</tt> by, plus fill out input field values.
595
+ # So if a +title+ attribute is set to "Ahoy!" then a +title+ input
596
+ # field's value would be "Ahoy!".
597
+ # If the model is a new record a create form is generated, if an
598
+ # existing record, however, an update form is generated.
599
+ # Pass <tt>:scope</tt> or <tt>:url</tt> to override the defaults.
600
+ # E.g. turn <tt>params[:post]</tt> into <tt>params[:article]</tt>.
601
+ # * <tt>:authenticity_token</tt> - Authenticity token to use in the form.
602
+ # Override with a custom authenticity token or pass <tt>false</tt> to
603
+ # skip the authenticity token field altogether.
604
+ # Useful when submitting to an external resource like a payment gateway
605
+ # that might limit the valid fields.
606
+ # Remote forms may omit the embedded authenticity token by setting
607
+ # <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
608
+ # This is helpful when fragment-caching the form. Remote forms
609
+ # get the authenticity token from the <tt>meta</tt> tag, so embedding is
610
+ # unnecessary unless you support browsers without JavaScript.
611
+ # * <tt>:local</tt> - By default form submits are remote and unobtrusive XHRs.
612
+ # Disable remote submits with <tt>local: true</tt>.
613
+ # * <tt>:skip_enforcing_utf8</tt> - By default a hidden field named +utf8+
614
+ # is output to enforce UTF-8 submits. Set to true to skip the field.
615
+ # * <tt>:builder</tt> - Override the object used to build the form.
616
+ # * <tt>:id</tt> - Optional HTML id attribute.
617
+ # * <tt>:class</tt> - Optional HTML class attribute.
618
+ # * <tt>:data</tt> - Optional HTML data attributes.
619
+ # * <tt>:html</tt> - Other optional HTML attributes for the form tag.
620
+ #
621
+ # === Examples
622
+ #
623
+ # When not passing a block, +form_with+ just generates an opening form tag.
624
+ #
625
+ # <%= form_with(model: @post, url: super_posts_path) %>
626
+ # <%= form_with(model: @post, scope: :article) %>
627
+ # <%= form_with(model: @post, format: :json) %>
628
+ # <%= form_with(model: @post, authenticity_token: false) %> # Disables the token.
629
+ #
630
+ # For namespaced routes, like +admin_post_url+:
631
+ #
632
+ # <%= form_with(model: [ :admin, @post ]) do |form| %>
633
+ # ...
634
+ # <% end %>
635
+ #
636
+ # If your resource has associations defined, for example, you want to add comments
637
+ # to the document given that the routes are set correctly:
638
+ #
639
+ # <%= form_with(model: [ @document, Comment.new ]) do |form| %>
640
+ # ...
641
+ # <% end %>
642
+ #
643
+ # Where <tt>@document = Document.find(params[:id])</tt>.
644
+ #
645
+ # === Mixing with other form helpers
646
+ #
647
+ # While +form_with+ uses a FormBuilder object it's possible to mix and
648
+ # match the stand-alone FormHelper methods and methods
649
+ # from FormTagHelper:
650
+ #
651
+ # <%= form_with scope: :person do |form| %>
652
+ # <%= form.text_field :first_name %>
653
+ # <%= form.text_field :last_name %>
654
+ #
655
+ # <%= text_area :person, :biography %>
656
+ # <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
657
+ #
658
+ # <%= form.submit %>
659
+ # <% end %>
660
+ #
661
+ # Same goes for the methods in FormOptionsHelper and DateHelper designed
662
+ # to work with an object as a base, like
663
+ # FormOptionsHelper#collection_select and DateHelper#datetime_select.
664
+ #
665
+ # === Setting the method
666
+ #
667
+ # You can force the form to use the full array of HTTP verbs by setting
668
+ #
669
+ # method: (:get|:post|:patch|:put|:delete)
670
+ #
671
+ # in the options hash. If the verb is not GET or POST, which are natively
672
+ # supported by HTML forms, the form will be set to POST and a hidden input
673
+ # called _method will carry the intended verb for the server to interpret.
674
+ #
675
+ # === Setting HTML options
676
+ #
677
+ # You can set data attributes directly in a data hash, but HTML options
678
+ # besides id and class must be wrapped in an HTML key:
679
+ #
680
+ # <%= form_with(model: @post, data: { behavior: "autosave" }, html: { name: "go" }) do |form| %>
681
+ # ...
682
+ # <% end %>
683
+ #
684
+ # generates
685
+ #
686
+ # <form action="/posts/123" method="post" data-behavior="autosave" name="go">
687
+ # <input name="_method" type="hidden" value="patch" />
688
+ # ...
689
+ # </form>
690
+ #
691
+ # === Removing hidden model id's
692
+ #
693
+ # The +form_with+ method automatically includes the model id as a hidden field in the form.
694
+ # This is used to maintain the correlation between the form data and its associated model.
695
+ # Some ORM systems do not use IDs on nested models so in this case you want to be able
696
+ # to disable the hidden id.
697
+ #
698
+ # In the following example the Post model has many Comments stored within it in a NoSQL database,
699
+ # thus there is no primary key for comments.
700
+ #
701
+ # <%= form_with(model: @post) do |form| %>
702
+ # <%= form.fields(:comments, skip_id: true) do |fields| %>
703
+ # ...
704
+ # <% end %>
705
+ # <% end %>
706
+ #
707
+ # === Customized form builders
708
+ #
709
+ # You can also build forms using a customized FormBuilder class. Subclass
710
+ # FormBuilder and override or define some more helpers, then use your
711
+ # custom builder. For example, let's say you made a helper to
712
+ # automatically add labels to form inputs.
713
+ #
714
+ # <%= form_with model: @person, url: { action: "create" }, builder: LabellingFormBuilder do |form| %>
715
+ # <%= form.text_field :first_name %>
716
+ # <%= form.text_field :last_name %>
717
+ # <%= form.text_area :biography %>
718
+ # <%= form.check_box :admin %>
719
+ # <%= form.submit %>
720
+ # <% end %>
721
+ #
722
+ # In this case, if you use:
723
+ #
724
+ # <%= render form %>
725
+ #
726
+ # The rendered template is <tt>people/_labelling_form</tt> and the local
727
+ # variable referencing the form builder is called
728
+ # <tt>labelling_form</tt>.
729
+ #
730
+ # The custom FormBuilder class is automatically merged with the options
731
+ # of a nested +fields+ call, unless it's explicitly set.
732
+ #
733
+ # In many cases you will want to wrap the above in another helper, so you
734
+ # could do something like the following:
735
+ #
736
+ # def labelled_form_with(**options, &block)
737
+ # form_with(**options.merge(builder: LabellingFormBuilder), &block)
738
+ # end
739
+ def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
740
+ options[:allow_method_names_outside_object] = true
741
+ options[:skip_default_ids] = !form_with_generates_ids
742
+
743
+ if model
744
+ url ||= polymorphic_path(model, format: format)
745
+
746
+ model = model.last if model.is_a?(Array)
747
+ scope ||= model_name_from_record_or_class(model).param_key
748
+ end
749
+
750
+ if block_given?
751
+ builder = instantiate_builder(scope, model, options)
752
+ output = capture(builder, &block)
753
+ options[:multipart] ||= builder.multipart?
754
+
755
+ html_options = html_options_for_form_with(url, model, options)
756
+ form_tag_with_body(html_options, output)
757
+ else
758
+ html_options = html_options_for_form_with(url, model, options)
759
+ form_tag_html(html_options)
760
+ end
761
+ end
762
+
471
763
  # Creates a scope around a specific model object like form_for, but
472
764
  # doesn't create the form tags themselves. This makes fields_for suitable
473
765
  # for specifying additional model objects in the same form.
@@ -525,9 +817,9 @@ module ActionView
525
817
  # _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
526
818
  # of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
527
819
  #
528
- # Note: This also works for the methods in FormOptionHelper and
820
+ # Note: This also works for the methods in FormOptionsHelper and
529
821
  # DateHelper that are designed to work with an object as base, like
530
- # FormOptionHelper#collection_select and DateHelper#datetime_select.
822
+ # FormOptionsHelper#collection_select and DateHelper#datetime_select.
531
823
  #
532
824
  # === Nested Attributes Examples
533
825
  #
@@ -714,6 +1006,63 @@ module ActionView
714
1006
  capture(builder, &block)
715
1007
  end
716
1008
 
1009
+ # Scopes input fields with either an explicit scope or model.
1010
+ # Like +form_with+ does with <tt>:scope</tt> or <tt>:model</tt>,
1011
+ # except it doesn't output the form tags.
1012
+ #
1013
+ # # Using a scope prefixes the input field names:
1014
+ # <%= fields :comment do |fields| %>
1015
+ # <%= fields.text_field :body %>
1016
+ # <% end %>
1017
+ # # => <input type="text" name="comment[body]">
1018
+ #
1019
+ # # Using a model infers the scope and assigns field values:
1020
+ # <%= fields model: Comment.new(body: "full bodied") do |fields| %>
1021
+ # <%= fields.text_field :body %>
1022
+ # <% end %>
1023
+ # # => <input type="text" name="comment[body]" value="full bodied">
1024
+ #
1025
+ # # Using +fields+ with +form_with+:
1026
+ # <%= form_with model: @post do |form| %>
1027
+ # <%= form.text_field :title %>
1028
+ #
1029
+ # <%= form.fields :comment do |fields| %>
1030
+ # <%= fields.text_field :body %>
1031
+ # <% end %>
1032
+ # <% end %>
1033
+ #
1034
+ # Much like +form_with+ a FormBuilder instance associated with the scope
1035
+ # or model is yielded, so any generated field names are prefixed with
1036
+ # either the passed scope or the scope inferred from the <tt>:model</tt>.
1037
+ #
1038
+ # === Mixing with other form helpers
1039
+ #
1040
+ # While +form_with+ uses a FormBuilder object it's possible to mix and
1041
+ # match the stand-alone FormHelper methods and methods
1042
+ # from FormTagHelper:
1043
+ #
1044
+ # <%= fields model: @comment do |fields| %>
1045
+ # <%= fields.text_field :body %>
1046
+ #
1047
+ # <%= text_area :commenter, :biography %>
1048
+ # <%= check_box_tag "comment[all_caps]", "1", @comment.commenter.hulk_mode? %>
1049
+ # <% end %>
1050
+ #
1051
+ # Same goes for the methods in FormOptionsHelper and DateHelper designed
1052
+ # to work with an object as a base, like
1053
+ # FormOptionsHelper#collection_select and DateHelper#datetime_select.
1054
+ def fields(scope = nil, model: nil, **options, &block)
1055
+ options[:allow_method_names_outside_object] = true
1056
+ options[:skip_default_ids] = !form_with_generates_ids
1057
+
1058
+ if model
1059
+ scope ||= model_name_from_record_or_class(model).param_key
1060
+ end
1061
+
1062
+ builder = instantiate_builder(scope, model, options)
1063
+ capture(builder, &block)
1064
+ end
1065
+
717
1066
  # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
718
1067
  # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
719
1068
  # is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
@@ -759,7 +1108,7 @@ module ActionView
759
1108
  # # => <label for="post_privacy_public">Public Post</label>
760
1109
  #
761
1110
  # label(:post, :terms) do
762
- # 'Accept <a href="/terms">Terms</a>.'.html_safe
1111
+ # raw('Accept <a href="/terms">Terms</a>.')
763
1112
  # end
764
1113
  # # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
765
1114
  def label(object_name, method, content_or_options = nil, options = nil, &block)
@@ -843,8 +1192,8 @@ module ActionView
843
1192
  # file_field(:user, :avatar)
844
1193
  # # => <input type="file" id="user_avatar" name="user[avatar]" />
845
1194
  #
846
- # file_field(:post, :image, :multiple => true)
847
- # # => <input type="file" id="post_image" name="post[image]" multiple="true" />
1195
+ # file_field(:post, :image, multiple: true)
1196
+ # # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
848
1197
  #
849
1198
  # file_field(:post, :attached, accept: 'text/html')
850
1199
  # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
@@ -855,7 +1204,7 @@ module ActionView
855
1204
  # file_field(:attachment, :file, class: 'file_input')
856
1205
  # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
857
1206
  def file_field(object_name, method, options = {})
858
- Tags::FileField.new(object_name, method, self, options).render
1207
+ Tags::FileField.new(object_name, method, self, convert_direct_upload_option_to_url(options.dup)).render
859
1208
  end
860
1209
 
861
1210
  # Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
@@ -959,6 +1308,7 @@ module ActionView
959
1308
  # # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
960
1309
  # # <input type="radio" id="post_category_java" name="post[category]" value="java" />
961
1310
  #
1311
+ # # Let's say that @user.receive_newsletter returns "no":
962
1312
  # radio_button("user", "receive_newsletter", "yes")
963
1313
  # radio_button("user", "receive_newsletter", "no")
964
1314
  # # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
@@ -1014,7 +1364,7 @@ module ActionView
1014
1364
  # date_field("user", "born_on")
1015
1365
  # # => <input id="user_born_on" name="user[born_on]" type="date" />
1016
1366
  #
1017
- # The default value is generated by trying to call "to_date"
1367
+ # The default value is generated by trying to call +strftime+ with "%Y-%m-%d"
1018
1368
  # on the object's value, which makes it behave as expected for instances
1019
1369
  # of DateTime and ActiveSupport::TimeWithZone. You can still override that
1020
1370
  # by passing the "value" option explicitly, e.g.
@@ -1042,7 +1392,7 @@ module ActionView
1042
1392
  # Returns a text_field of type "time".
1043
1393
  #
1044
1394
  # The default value is generated by trying to call +strftime+ with "%T.%L"
1045
- # on the objects's value. It is still possible to override that
1395
+ # on the object's value. It is still possible to override that
1046
1396
  # by passing the "value" option.
1047
1397
  #
1048
1398
  # === Options
@@ -1068,38 +1418,9 @@ module ActionView
1068
1418
  Tags::TimeField.new(object_name, method, self, options).render
1069
1419
  end
1070
1420
 
1071
- # Returns a text_field of type "datetime".
1072
- #
1073
- # datetime_field("user", "born_on")
1074
- # # => <input id="user_born_on" name="user[born_on]" type="datetime" />
1075
- #
1076
- # The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T.%L%z"
1077
- # on the object's value, which makes it behave as expected for instances
1078
- # of DateTime and ActiveSupport::TimeWithZone.
1079
- #
1080
- # @user.born_on = Date.new(1984, 1, 12)
1081
- # datetime_field("user", "born_on")
1082
- # # => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" />
1083
- #
1084
- # You can create values for the "min" and "max" attributes by passing
1085
- # instances of Date or Time to the options hash.
1086
- #
1087
- # datetime_field("user", "born_on", min: Date.today)
1088
- # # => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" />
1089
- #
1090
- # Alternatively, you can pass a String formatted as an ISO8601 datetime
1091
- # with UTC offset as the values for "min" and "max."
1092
- #
1093
- # datetime_field("user", "born_on", min: "2014-05-20T00:00:00+0000")
1094
- # # => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" />
1095
- #
1096
- def datetime_field(object_name, method, options = {})
1097
- Tags::DatetimeField.new(object_name, method, self, options).render
1098
- end
1099
-
1100
1421
  # Returns a text_field of type "datetime-local".
1101
1422
  #
1102
- # datetime_local_field("user", "born_on")
1423
+ # datetime_field("user", "born_on")
1103
1424
  # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" />
1104
1425
  #
1105
1426
  # The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T"
@@ -1107,25 +1428,27 @@ module ActionView
1107
1428
  # of DateTime and ActiveSupport::TimeWithZone.
1108
1429
  #
1109
1430
  # @user.born_on = Date.new(1984, 1, 12)
1110
- # datetime_local_field("user", "born_on")
1431
+ # datetime_field("user", "born_on")
1111
1432
  # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" />
1112
1433
  #
1113
1434
  # You can create values for the "min" and "max" attributes by passing
1114
1435
  # instances of Date or Time to the options hash.
1115
1436
  #
1116
- # datetime_local_field("user", "born_on", min: Date.today)
1437
+ # datetime_field("user", "born_on", min: Date.today)
1117
1438
  # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" />
1118
1439
  #
1119
1440
  # Alternatively, you can pass a String formatted as an ISO8601 datetime as
1120
1441
  # the values for "min" and "max."
1121
1442
  #
1122
- # datetime_local_field("user", "born_on", min: "2014-05-20T00:00:00")
1443
+ # datetime_field("user", "born_on", min: "2014-05-20T00:00:00")
1123
1444
  # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" />
1124
1445
  #
1125
- def datetime_local_field(object_name, method, options = {})
1446
+ def datetime_field(object_name, method, options = {})
1126
1447
  Tags::DatetimeLocalField.new(object_name, method, self, options).render
1127
1448
  end
1128
1449
 
1450
+ alias datetime_local_field datetime_field
1451
+
1129
1452
  # Returns a text_field of type "month".
1130
1453
  #
1131
1454
  # month_field("user", "born_on")
@@ -1195,6 +1518,34 @@ module ActionView
1195
1518
  end
1196
1519
 
1197
1520
  private
1521
+ def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: !form_with_generates_remote_forms,
1522
+ skip_enforcing_utf8: false, **options)
1523
+ html_options = options.slice(:id, :class, :multipart, :method, :data).merge(html)
1524
+ html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
1525
+ html_options[:enforce_utf8] = !skip_enforcing_utf8
1526
+
1527
+ html_options[:enctype] = "multipart/form-data" if html_options.delete(:multipart)
1528
+
1529
+ # The following URL is unescaped, this is just a hash of options, and it is the
1530
+ # responsibility of the caller to escape all the values.
1531
+ html_options[:action] = url_for(url_for_options || {})
1532
+ html_options[:"accept-charset"] = "UTF-8"
1533
+ html_options[:"data-remote"] = true unless local
1534
+
1535
+ html_options[:authenticity_token] = options.delete(:authenticity_token)
1536
+
1537
+ if !local && html_options[:authenticity_token].blank?
1538
+ html_options[:authenticity_token] = embed_authenticity_token_in_remote_forms
1539
+ end
1540
+
1541
+ if html_options[:authenticity_token] == true
1542
+ # Include the default authenticity_token, which is only generated when it's set to nil,
1543
+ # but we needed the true value to override the default of no authenticity_token on data-remote.
1544
+ html_options[:authenticity_token] = nil
1545
+ end
1546
+
1547
+ html_options.stringify_keys!
1548
+ end
1198
1549
 
1199
1550
  def instantiate_builder(record_name, record_object, options)
1200
1551
  case record_name
@@ -1203,7 +1554,7 @@ module ActionView
1203
1554
  object_name = record_name
1204
1555
  else
1205
1556
  object = record_name
1206
- object_name = model_name_from_record_or_class(object).param_key
1557
+ object_name = model_name_from_record_or_class(object).param_key if object
1207
1558
  end
1208
1559
 
1209
1560
  builder = options[:builder] || default_form_builder_class
@@ -1211,7 +1562,7 @@ module ActionView
1211
1562
  end
1212
1563
 
1213
1564
  def default_form_builder_class
1214
- builder = ActionView::Base.default_form_builder
1565
+ builder = default_form_builder || ActionView::Base.default_form_builder
1215
1566
  builder.respond_to?(:constantize) ? builder.constantize : builder
1216
1567
  end
1217
1568
  end
@@ -1229,7 +1580,7 @@ module ActionView
1229
1580
  # In the above block, a +FormBuilder+ object is yielded as the
1230
1581
  # +person_form+ variable. This allows you to generate the +text_field+
1231
1582
  # and +check_box+ fields by specifying their eponymous methods, which
1232
- # modify the underlying template and associates the +@person+ model object
1583
+ # modify the underlying template and associates the <tt>@person</tt> model object
1233
1584
  # with the form.
1234
1585
  #
1235
1586
  # The +FormBuilder+ object can be thought of as serving as a proxy for the
@@ -1268,14 +1619,15 @@ module ActionView
1268
1619
  include ModelNaming
1269
1620
 
1270
1621
  # The methods which wrap a form helper call.
1271
- class_attribute :field_helpers
1272
- self.field_helpers = [:fields_for, :label, :text_field, :password_field,
1273
- :hidden_field, :file_field, :text_area, :check_box,
1274
- :radio_button, :color_field, :search_field,
1275
- :telephone_field, :phone_field, :date_field,
1276
- :time_field, :datetime_field, :datetime_local_field,
1277
- :month_field, :week_field, :url_field, :email_field,
1278
- :number_field, :range_field]
1622
+ class_attribute :field_helpers, default: [
1623
+ :fields_for, :fields, :label, :text_field, :password_field,
1624
+ :hidden_field, :file_field, :text_area, :check_box,
1625
+ :radio_button, :color_field, :search_field,
1626
+ :telephone_field, :phone_field, :date_field,
1627
+ :time_field, :datetime_field, :datetime_local_field,
1628
+ :month_field, :week_field, :url_field, :email_field,
1629
+ :number_field, :range_field
1630
+ ]
1279
1631
 
1280
1632
  attr_accessor :object_name, :object, :options
1281
1633
 
@@ -1291,7 +1643,7 @@ module ActionView
1291
1643
  end
1292
1644
 
1293
1645
  def self._to_partial_path
1294
- @_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, '')
1646
+ @_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, "")
1295
1647
  end
1296
1648
 
1297
1649
  def to_partial_path
@@ -1305,19 +1657,24 @@ module ActionView
1305
1657
  def initialize(object_name, object, template, options)
1306
1658
  @nested_child_index = {}
1307
1659
  @object_name, @object, @template, @options = object_name, object, template, options
1308
- @default_options = @options ? @options.slice(:index, :namespace) : {}
1660
+ @default_options = @options ? @options.slice(:index, :namespace, :skip_default_ids, :allow_method_names_outside_object) : {}
1661
+ @default_html_options = @default_options.except(:skip_default_ids, :allow_method_names_outside_object)
1662
+
1663
+ convert_to_legacy_options(@options)
1664
+
1309
1665
  if @object_name.to_s.match(/\[\]$/)
1310
- if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
1666
+ if (object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}")) && object.respond_to?(:to_param)
1311
1667
  @auto_index = object.to_param
1312
1668
  else
1313
1669
  raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
1314
1670
  end
1315
1671
  end
1672
+
1316
1673
  @multipart = nil
1317
1674
  @index = options[:index] || options[:child_index]
1318
1675
  end
1319
1676
 
1320
- (field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
1677
+ (field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
1321
1678
  class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
1322
1679
  def #{selector}(method, options = {}) # def text_field(method, options = {})
1323
1680
  @template.send( # @template.send(
@@ -1386,9 +1743,9 @@ module ActionView
1386
1743
  # _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
1387
1744
  # of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
1388
1745
  #
1389
- # Note: This also works for the methods in FormOptionHelper and
1746
+ # Note: This also works for the methods in FormOptionsHelper and
1390
1747
  # DateHelper that are designed to work with an object as base, like
1391
- # FormOptionHelper#collection_select and DateHelper#datetime_select.
1748
+ # FormOptionsHelper#collection_select and DateHelper#datetime_select.
1392
1749
  #
1393
1750
  # === Nested Attributes Examples
1394
1751
  #
@@ -1586,19 +1943,37 @@ module ActionView
1586
1943
  record_name = model_name_from_record_or_class(record_object).param_key
1587
1944
  end
1588
1945
 
1946
+ object_name = @object_name
1589
1947
  index = if options.has_key?(:index)
1590
1948
  options[:index]
1591
1949
  elsif defined?(@auto_index)
1592
- self.object_name = @object_name.to_s.sub(/\[\]$/,"")
1950
+ object_name = object_name.to_s.sub(/\[\]$/, "")
1593
1951
  @auto_index
1594
1952
  end
1595
1953
 
1596
- record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]"
1954
+ record_name = if index
1955
+ "#{object_name}[#{index}][#{record_name}]"
1956
+ elsif record_name.to_s.end_with?("[]")
1957
+ record_name = record_name.to_s.sub(/(.*)\[\]$/, "[\\1][#{record_object.id}]")
1958
+ "#{object_name}#{record_name}"
1959
+ else
1960
+ "#{object_name}[#{record_name}]"
1961
+ end
1597
1962
  fields_options[:child_index] = index
1598
1963
 
1599
1964
  @template.fields_for(record_name, record_object, fields_options, &block)
1600
1965
  end
1601
1966
 
1967
+ # See the docs for the <tt>ActionView::FormHelper.fields</tt> helper method.
1968
+ def fields(scope = nil, model: nil, **options, &block)
1969
+ options[:allow_method_names_outside_object] = true
1970
+ options[:skip_default_ids] = !FormHelper.form_with_generates_ids
1971
+
1972
+ convert_to_legacy_options(options)
1973
+
1974
+ fields_for(scope || model, model, options, &block)
1975
+ end
1976
+
1602
1977
  # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
1603
1978
  # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
1604
1979
  # is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
@@ -1607,7 +1982,7 @@ module ActionView
1607
1982
  # target labels for radio_button tags (where the value is used in the ID of the input tag).
1608
1983
  #
1609
1984
  # ==== Examples
1610
- # label(:post, :title)
1985
+ # label(:title)
1611
1986
  # # => <label for="post_title">Title</label>
1612
1987
  #
1613
1988
  # You can localize your labels based on model and attribute names.
@@ -1620,7 +1995,7 @@ module ActionView
1620
1995
  #
1621
1996
  # Which then will result in
1622
1997
  #
1623
- # label(:post, :body)
1998
+ # label(:body)
1624
1999
  # # => <label for="post_body">Write your entire text here</label>
1625
2000
  #
1626
2001
  # Localization can also be based purely on the translation of the attribute-name
@@ -1631,21 +2006,22 @@ module ActionView
1631
2006
  # post:
1632
2007
  # cost: "Total cost"
1633
2008
  #
1634
- # label(:post, :cost)
2009
+ # label(:cost)
1635
2010
  # # => <label for="post_cost">Total cost</label>
1636
2011
  #
1637
- # label(:post, :title, "A short title")
2012
+ # label(:title, "A short title")
1638
2013
  # # => <label for="post_title">A short title</label>
1639
2014
  #
1640
- # label(:post, :title, "A short title", class: "title_label")
2015
+ # label(:title, "A short title", class: "title_label")
1641
2016
  # # => <label for="post_title" class="title_label">A short title</label>
1642
2017
  #
1643
- # label(:post, :privacy, "Public Post", value: "public")
2018
+ # label(:privacy, "Public Post", value: "public")
1644
2019
  # # => <label for="post_privacy_public">Public Post</label>
1645
2020
  #
1646
- # label(:post, :terms) do
1647
- # 'Accept <a href="/terms">Terms</a>.'.html_safe
2021
+ # label(:terms) do
2022
+ # raw('Accept <a href="/terms">Terms</a>.')
1648
2023
  # end
2024
+ # # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
1649
2025
  def label(method, text = nil, options = {}, &block)
1650
2026
  @template.label(@object_name, method, text, objectify_options(options), &block)
1651
2027
  end
@@ -1694,16 +2070,17 @@ module ActionView
1694
2070
  # hashes instead of arrays.
1695
2071
  #
1696
2072
  # # Let's say that @post.validated? is 1:
1697
- # check_box("post", "validated")
2073
+ # check_box("validated")
1698
2074
  # # => <input name="post[validated]" type="hidden" value="0" />
1699
2075
  # # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
1700
2076
  #
1701
2077
  # # Let's say that @puppy.gooddog is "no":
1702
- # check_box("puppy", "gooddog", {}, "yes", "no")
2078
+ # check_box("gooddog", {}, "yes", "no")
1703
2079
  # # => <input name="puppy[gooddog]" type="hidden" value="no" />
1704
2080
  # # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
1705
2081
  #
1706
- # check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
2082
+ # # Let's say that @eula.accepted is "no":
2083
+ # check_box("accepted", { class: 'eula_check' }, "yes", "no")
1707
2084
  # # => <input name="eula[accepted]" type="hidden" value="no" />
1708
2085
  # # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
1709
2086
  def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
@@ -1718,13 +2095,14 @@ module ActionView
1718
2095
  # +options+ hash. You may pass HTML options there as well.
1719
2096
  #
1720
2097
  # # Let's say that @post.category returns "rails":
1721
- # radio_button("post", "category", "rails")
1722
- # radio_button("post", "category", "java")
2098
+ # radio_button("category", "rails")
2099
+ # radio_button("category", "java")
1723
2100
  # # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
1724
2101
  # # <input type="radio" id="post_category_java" name="post[category]" value="java" />
1725
2102
  #
1726
- # radio_button("user", "receive_newsletter", "yes")
1727
- # radio_button("user", "receive_newsletter", "no")
2103
+ # # Let's say that @user.receive_newsletter returns "no":
2104
+ # radio_button("receive_newsletter", "yes")
2105
+ # radio_button("receive_newsletter", "no")
1728
2106
  # # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
1729
2107
  # # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
1730
2108
  def radio_button(method, tag_value, options = {})
@@ -1737,14 +2115,17 @@ module ActionView
1737
2115
  # shown.
1738
2116
  #
1739
2117
  # ==== Examples
1740
- # hidden_field(:signup, :pass_confirm)
1741
- # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
2118
+ # # Let's say that @signup.pass_confirm returns true:
2119
+ # hidden_field(:pass_confirm)
2120
+ # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="true" />
1742
2121
  #
1743
- # hidden_field(:post, :tag_list)
1744
- # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
2122
+ # # Let's say that @post.tag_list returns "blog, ruby":
2123
+ # hidden_field(:tag_list)
2124
+ # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="blog, ruby" />
1745
2125
  #
1746
- # hidden_field(:user, :token)
1747
- # # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
2126
+ # # Let's say that @user.token returns "abcde":
2127
+ # hidden_field(:token)
2128
+ # # => <input type="hidden" id="user_token" name="user[token]" value="abcde" />
1748
2129
  #
1749
2130
  def hidden_field(method, options = {})
1750
2131
  @emitted_hidden_id = true if method == :id
@@ -1765,19 +2146,24 @@ module ActionView
1765
2146
  # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
1766
2147
  #
1767
2148
  # ==== Examples
1768
- # file_field(:user, :avatar)
2149
+ # # Let's say that @user has avatar:
2150
+ # file_field(:avatar)
1769
2151
  # # => <input type="file" id="user_avatar" name="user[avatar]" />
1770
2152
  #
1771
- # file_field(:post, :image, :multiple => true)
1772
- # # => <input type="file" id="post_image" name="post[image]" multiple="true" />
2153
+ # # Let's say that @post has image:
2154
+ # file_field(:image, :multiple => true)
2155
+ # # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
1773
2156
  #
1774
- # file_field(:post, :attached, accept: 'text/html')
2157
+ # # Let's say that @post has attached:
2158
+ # file_field(:attached, accept: 'text/html')
1775
2159
  # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
1776
2160
  #
1777
- # file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
2161
+ # # Let's say that @post has image:
2162
+ # file_field(:image, accept: 'image/png,image/gif,image/jpeg')
1778
2163
  # # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
1779
2164
  #
1780
- # file_field(:attachment, :file, class: 'file_input')
2165
+ # # Let's say that @attachment has file:
2166
+ # file_field(:file, class: 'file_input')
1781
2167
  # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
1782
2168
  def file_field(method, options = {})
1783
2169
  self.multipart = true
@@ -1791,11 +2177,11 @@ module ActionView
1791
2177
  # <%= f.submit %>
1792
2178
  # <% end %>
1793
2179
  #
1794
- # In the example above, if @post is a new record, it will use "Create Post" as
1795
- # submit button label, otherwise, it uses "Update Post".
2180
+ # In the example above, if <tt>@post</tt> is a new record, it will use "Create Post" as
2181
+ # submit button label; otherwise, it uses "Update Post".
1796
2182
  #
1797
- # Those labels can be customized using I18n, under the helpers.submit key and accept
1798
- # the %{model} as translation interpolation:
2183
+ # Those labels can be customized using I18n under the +helpers.submit+ key and using
2184
+ # <tt>%{model}</tt> for translation interpolation:
1799
2185
  #
1800
2186
  # en:
1801
2187
  # helpers:
@@ -1803,7 +2189,7 @@ module ActionView
1803
2189
  # create: "Create a %{model}"
1804
2190
  # update: "Confirm changes to %{model}"
1805
2191
  #
1806
- # It also searches for a key specific for the given object:
2192
+ # It also searches for a key specific to the given object:
1807
2193
  #
1808
2194
  # en:
1809
2195
  # helpers:
@@ -1811,7 +2197,7 @@ module ActionView
1811
2197
  # post:
1812
2198
  # create: "Add %{model}"
1813
2199
  #
1814
- def submit(value=nil, options={})
2200
+ def submit(value = nil, options = {})
1815
2201
  value, options = nil, value if value.is_a?(Hash)
1816
2202
  value ||= submit_default_value
1817
2203
  @template.submit_tag(value, options)
@@ -1824,11 +2210,11 @@ module ActionView
1824
2210
  # <%= f.button %>
1825
2211
  # <% end %>
1826
2212
  #
1827
- # In the example above, if @post is a new record, it will use "Create Post" as
1828
- # button label, otherwise, it uses "Update Post".
2213
+ # In the example above, if <tt>@post</tt> is a new record, it will use "Create Post" as
2214
+ # button label; otherwise, it uses "Update Post".
1829
2215
  #
1830
- # Those labels can be customized using I18n, under the helpers.submit key
1831
- # (the same as submit helper) and accept the %{model} as translation interpolation:
2216
+ # Those labels can be customized using I18n under the +helpers.submit+ key
2217
+ # (the same as submit helper) and using <tt>%{model}</tt> for translation interpolation:
1832
2218
  #
1833
2219
  # en:
1834
2220
  # helpers:
@@ -1836,7 +2222,7 @@ module ActionView
1836
2222
  # create: "Create a %{model}"
1837
2223
  # update: "Confirm changes to %{model}"
1838
2224
  #
1839
- # It also searches for a key specific for the given object:
2225
+ # It also searches for a key specific to the given object:
1840
2226
  #
1841
2227
  # en:
1842
2228
  # helpers:
@@ -1845,7 +2231,7 @@ module ActionView
1845
2231
  # create: "Add %{model}"
1846
2232
  #
1847
2233
  # ==== Examples
1848
- # button("Create a post")
2234
+ # button("Create post")
1849
2235
  # # => <button name='button' type='submit'>Create post</button>
1850
2236
  #
1851
2237
  # button do
@@ -1906,7 +2292,11 @@ module ActionView
1906
2292
  explicit_child_index = options[:child_index]
1907
2293
  output = ActiveSupport::SafeBuffer.new
1908
2294
  association.each do |child|
1909
- options[:child_index] = nested_child_index(name) unless explicit_child_index
2295
+ if explicit_child_index
2296
+ options[:child_index] = explicit_child_index.call if explicit_child_index.respond_to?(:call)
2297
+ else
2298
+ options[:child_index] = nested_child_index(name)
2299
+ end
1910
2300
  output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
1911
2301
  end
1912
2302
  output
@@ -1932,12 +2322,16 @@ module ActionView
1932
2322
  @nested_child_index[name] ||= -1
1933
2323
  @nested_child_index[name] += 1
1934
2324
  end
2325
+
2326
+ def convert_to_legacy_options(options)
2327
+ if options.key?(:skip_id)
2328
+ options[:include_id] = !options.delete(:skip_id)
2329
+ end
2330
+ end
1935
2331
  end
1936
2332
  end
1937
2333
 
1938
2334
  ActiveSupport.on_load(:action_view) do
1939
- cattr_accessor(:default_form_builder, instance_writer: false, instance_reader: false) do
1940
- ::ActionView::Helpers::FormBuilder
1941
- end
2335
+ cattr_accessor :default_form_builder, instance_writer: false, instance_reader: false, default: ::ActionView::Helpers::FormBuilder
1942
2336
  end
1943
2337
  end