actionview 7.0.8.1 → 7.2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -425
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/app/assets/javascripts/rails-ujs.esm.js +686 -0
  6. data/app/assets/javascripts/rails-ujs.js +630 -0
  7. data/lib/action_view/base.rb +52 -14
  8. data/lib/action_view/buffers.rb +106 -8
  9. data/lib/action_view/cache_expiry.rb +44 -41
  10. data/lib/action_view/context.rb +1 -1
  11. data/lib/action_view/dependency_tracker/{ripper_tracker.rb → ruby_tracker.rb} +4 -3
  12. data/lib/action_view/dependency_tracker.rb +1 -1
  13. data/lib/action_view/deprecator.rb +7 -0
  14. data/lib/action_view/digestor.rb +1 -1
  15. data/lib/action_view/gem_version.rb +3 -3
  16. data/lib/action_view/helpers/active_model_helper.rb +1 -1
  17. data/lib/action_view/helpers/asset_tag_helper.rb +151 -55
  18. data/lib/action_view/helpers/asset_url_helper.rb +6 -5
  19. data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
  20. data/lib/action_view/helpers/cache_helper.rb +7 -13
  21. data/lib/action_view/helpers/capture_helper.rb +30 -10
  22. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  23. data/lib/action_view/helpers/controller_helper.rb +6 -0
  24. data/lib/action_view/helpers/csp_helper.rb +2 -2
  25. data/lib/action_view/helpers/csrf_helper.rb +3 -3
  26. data/lib/action_view/helpers/date_helper.rb +17 -19
  27. data/lib/action_view/helpers/debug_helper.rb +3 -3
  28. data/lib/action_view/helpers/form_helper.rb +248 -214
  29. data/lib/action_view/helpers/form_options_helper.rb +2 -1
  30. data/lib/action_view/helpers/form_tag_helper.rb +125 -58
  31. data/lib/action_view/helpers/javascript_helper.rb +1 -0
  32. data/lib/action_view/helpers/number_helper.rb +37 -330
  33. data/lib/action_view/helpers/output_safety_helper.rb +6 -6
  34. data/lib/action_view/helpers/rendering_helper.rb +1 -1
  35. data/lib/action_view/helpers/sanitize_helper.rb +51 -21
  36. data/lib/action_view/helpers/tag_helper.rb +210 -42
  37. data/lib/action_view/helpers/tags/base.rb +11 -52
  38. data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
  39. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
  40. data/lib/action_view/helpers/tags/collection_select.rb +3 -0
  41. data/lib/action_view/helpers/tags/date_field.rb +1 -1
  42. data/lib/action_view/helpers/tags/date_select.rb +2 -0
  43. data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
  45. data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
  46. data/lib/action_view/helpers/tags/month_field.rb +1 -1
  47. data/lib/action_view/helpers/tags/select.rb +3 -0
  48. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  49. data/lib/action_view/helpers/tags/time_field.rb +1 -1
  50. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
  51. data/lib/action_view/helpers/tags/week_field.rb +1 -1
  52. data/lib/action_view/helpers/tags/weekday_select.rb +3 -0
  53. data/lib/action_view/helpers/tags.rb +2 -0
  54. data/lib/action_view/helpers/text_helper.rb +157 -85
  55. data/lib/action_view/helpers/translation_helper.rb +3 -3
  56. data/lib/action_view/helpers/url_helper.rb +35 -80
  57. data/lib/action_view/helpers.rb +2 -0
  58. data/lib/action_view/layouts.rb +8 -8
  59. data/lib/action_view/log_subscriber.rb +57 -36
  60. data/lib/action_view/lookup_context.rb +29 -13
  61. data/lib/action_view/path_registry.rb +57 -0
  62. data/lib/action_view/path_set.rb +13 -14
  63. data/lib/action_view/railtie.rb +25 -3
  64. data/lib/action_view/record_identifier.rb +15 -8
  65. data/lib/action_view/render_parser/prism_render_parser.rb +127 -0
  66. data/lib/action_view/render_parser/ripper_render_parser.rb +341 -0
  67. data/lib/action_view/render_parser.rb +21 -169
  68. data/lib/action_view/renderer/abstract_renderer.rb +2 -2
  69. data/lib/action_view/renderer/collection_renderer.rb +10 -2
  70. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +2 -1
  71. data/lib/action_view/renderer/partial_renderer.rb +2 -1
  72. data/lib/action_view/renderer/renderer.rb +34 -38
  73. data/lib/action_view/renderer/streaming_template_renderer.rb +3 -2
  74. data/lib/action_view/renderer/template_renderer.rb +3 -2
  75. data/lib/action_view/rendering.rb +26 -8
  76. data/lib/action_view/template/error.rb +14 -1
  77. data/lib/action_view/template/handlers/builder.rb +4 -4
  78. data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
  79. data/lib/action_view/template/handlers/erb.rb +73 -1
  80. data/lib/action_view/template/handlers.rb +1 -1
  81. data/lib/action_view/template/html.rb +1 -1
  82. data/lib/action_view/template/raw_file.rb +1 -1
  83. data/lib/action_view/template/renderable.rb +8 -2
  84. data/lib/action_view/template/resolver.rb +9 -3
  85. data/lib/action_view/template/text.rb +1 -1
  86. data/lib/action_view/template/types.rb +25 -34
  87. data/lib/action_view/template.rb +278 -55
  88. data/lib/action_view/template_path.rb +2 -0
  89. data/lib/action_view/test_case.rb +181 -28
  90. data/lib/action_view/unbound_template.rb +17 -7
  91. data/lib/action_view/version.rb +1 -1
  92. data/lib/action_view/view_paths.rb +15 -24
  93. data/lib/action_view.rb +4 -1
  94. metadata +31 -31
  95. data/lib/action_view/ripper_ast_parser.rb +0 -198
  96. data/lib/assets/compiled/rails-ujs.js +0 -777
@@ -7,14 +7,16 @@ require "action_view/helpers/form_tag_helper"
7
7
  require "action_view/helpers/active_model_helper"
8
8
  require "action_view/model_naming"
9
9
  require "action_view/record_identifier"
10
+ require "active_support/code_generator"
10
11
  require "active_support/core_ext/module/attribute_accessors"
11
12
  require "active_support/core_ext/hash/slice"
12
13
  require "active_support/core_ext/string/output_safety"
13
14
  require "active_support/core_ext/string/inflections"
14
15
 
15
16
  module ActionView
16
- # = Action View Form Helpers
17
17
  module Helpers # :nodoc:
18
+ # = Action View Form \Helpers
19
+ #
18
20
  # Form helpers are designed to make working with resources much easier
19
21
  # compared to using vanilla HTML.
20
22
  #
@@ -28,7 +30,7 @@ module ActionView
28
30
  # when the form is initially displayed, input fields corresponding to attributes
29
31
  # of the resource should show the current values of those attributes.
30
32
  #
31
- # In Rails, this is usually achieved by creating the form using +form_for+ and
33
+ # In \Rails, this is usually achieved by creating the form using +form_for+ and
32
34
  # a number of related helper methods. +form_for+ generates an appropriate <tt>form</tt>
33
35
  # tag and yields a form builder object that knows the model the form is about.
34
36
  # Input fields are created by calling methods defined on the form builder, which
@@ -122,7 +124,7 @@ module ActionView
122
124
  # of a specific model object.
123
125
  #
124
126
  # The method can be used in several slightly different ways, depending on
125
- # how much you wish to rely on Rails to infer automatically from the model
127
+ # how much you wish to rely on \Rails to infer automatically from the model
126
128
  # how the form should be constructed. For a generic model object, a form
127
129
  # can be created by passing +form_for+ a string or symbol representing
128
130
  # the object we are concerned with:
@@ -211,18 +213,18 @@ module ActionView
211
213
  # In the examples above, the object to be created or edited was
212
214
  # represented by a symbol passed to +form_for+, and we noted that
213
215
  # a string can also be used equivalently. It is also possible, however,
214
- # to pass a model object itself to +form_for+. For example, if <tt>@post</tt>
216
+ # to pass a model object itself to +form_for+. For example, if <tt>@article</tt>
215
217
  # is an existing record you wish to edit, you can create the form using
216
218
  #
217
- # <%= form_for @post do |f| %>
219
+ # <%= form_for @article do |f| %>
218
220
  # ...
219
221
  # <% end %>
220
222
  #
221
223
  # This behaves in almost the same way as outlined previously, with a
222
224
  # couple of small exceptions. First, the prefix used to name the input
223
225
  # elements within the form (hence the key that denotes them in the +params+
224
- # hash) is actually derived from the object's _class_, e.g. <tt>params[:post]</tt>
225
- # if the object's class is +Post+. However, this can be overwritten using
226
+ # hash) is actually derived from the object's _class_, e.g. <tt>params[:article]</tt>
227
+ # if the object's class is +Article+. However, this can be overwritten using
226
228
  # the <tt>:as</tt> option, e.g. -
227
229
  #
228
230
  # <%= form_for(@person, as: :client) do |f| %>
@@ -234,15 +236,15 @@ module ActionView
234
236
  # Secondly, the field values shown when the form is initially displayed
235
237
  # are taken from the attributes of the object passed to +form_for+,
236
238
  # regardless of whether the object is an instance
237
- # variable. So, for example, if we had a _local_ variable +post+
239
+ # variable. So, for example, if we had a _local_ variable +article+
238
240
  # representing an existing record,
239
241
  #
240
- # <%= form_for post do |f| %>
242
+ # <%= form_for article do |f| %>
241
243
  # ...
242
244
  # <% end %>
243
245
  #
244
246
  # would produce a form with fields whose initial state reflect the current
245
- # values of the attributes of +post+.
247
+ # values of the attributes of +article+.
246
248
  #
247
249
  # === Resource-oriented style
248
250
  #
@@ -251,52 +253,52 @@ module ActionView
251
253
  # form is going to be sent. However, further simplification is possible
252
254
  # if the record passed to +form_for+ is a _resource_, i.e. it corresponds
253
255
  # to a set of RESTful routes, e.g. defined using the +resources+ method
254
- # in <tt>config/routes.rb</tt>. In this case Rails will simply infer the
256
+ # in <tt>config/routes.rb</tt>. In this case \Rails will simply infer the
255
257
  # appropriate URL from the record itself. For example,
256
258
  #
257
- # <%= form_for @post do |f| %>
259
+ # <%= form_for @article do |f| %>
258
260
  # ...
259
261
  # <% end %>
260
262
  #
261
263
  # is then equivalent to something like:
262
264
  #
263
- # <%= form_for @post, as: :post, url: post_path(@post), method: :patch, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
265
+ # <%= form_for @article, as: :article, url: article_path(@article), method: :patch, html: { class: "edit_article", id: "edit_article_45" } do |f| %>
264
266
  # ...
265
267
  # <% end %>
266
268
  #
267
269
  # And for a new record
268
270
  #
269
- # <%= form_for(Post.new) do |f| %>
271
+ # <%= form_for(Article.new) do |f| %>
270
272
  # ...
271
273
  # <% end %>
272
274
  #
273
275
  # is equivalent to something like:
274
276
  #
275
- # <%= form_for @post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
277
+ # <%= form_for @article, as: :article, url: articles_path, html: { class: "new_article", id: "new_article" } do |f| %>
276
278
  # ...
277
279
  # <% end %>
278
280
  #
279
281
  # However you can still overwrite individual conventions, such as:
280
282
  #
281
- # <%= form_for(@post, url: super_posts_path) do |f| %>
283
+ # <%= form_for(@article, url: super_articles_path) do |f| %>
282
284
  # ...
283
285
  # <% end %>
284
286
  #
285
287
  # You can omit the <tt>action</tt> attribute by passing <tt>url: false</tt>:
286
288
  #
287
- # <%= form_for(@post, url: false) do |f| %>
289
+ # <%= form_for(@article, url: false) do |f| %>
288
290
  # ...
289
291
  # <% end %>
290
292
  #
291
293
  # You can also set the answer format, like this:
292
294
  #
293
- # <%= form_for(@post, format: :json) do |f| %>
295
+ # <%= form_for(@article, format: :json) do |f| %>
294
296
  # ...
295
297
  # <% end %>
296
298
  #
297
- # For namespaced routes, like +admin_post_url+:
299
+ # For namespaced routes, like +admin_article_url+:
298
300
  #
299
- # <%= form_for([:admin, @post]) do |f| %>
301
+ # <%= form_for([:admin, @article]) do |f| %>
300
302
  # ...
301
303
  # <% end %>
302
304
  #
@@ -332,7 +334,7 @@ module ActionView
332
334
  #
333
335
  # Example:
334
336
  #
335
- # <%= form_for(@post, remote: true) do |f| %>
337
+ # <%= form_for(@article, remote: true) do |f| %>
336
338
  # ...
337
339
  # <% end %>
338
340
  #
@@ -348,7 +350,7 @@ module ActionView
348
350
  # You can set data attributes directly by passing in a data hash, but all other HTML options must be wrapped in
349
351
  # the HTML key. Example:
350
352
  #
351
- # <%= form_for(@post, data: { behavior: "autosave" }, html: { name: "go" }) do |f| %>
353
+ # <%= form_for(@article, data: { behavior: "autosave" }, html: { name: "go" }) do |f| %>
352
354
  # ...
353
355
  # <% end %>
354
356
  #
@@ -366,12 +368,12 @@ module ActionView
366
368
  # Some ORM systems do not use IDs on nested models so in this case you want to be able
367
369
  # to disable the hidden id.
368
370
  #
369
- # In the following example the Post model has many Comments stored within it in a NoSQL database,
371
+ # In the following example the Article model has many Comments stored within it in a NoSQL database,
370
372
  # thus there is no primary key for comments.
371
373
  #
372
374
  # Example:
373
375
  #
374
- # <%= form_for(@post) do |f| %>
376
+ # <%= form_for(@article) do |f| %>
375
377
  # <%= f.fields_for(:comments, include_id: false) do |cf| %>
376
378
  # ...
377
379
  # <% end %>
@@ -435,10 +437,10 @@ module ActionView
435
437
 
436
438
  case record
437
439
  when String, Symbol
438
- model = nil
440
+ model = false
439
441
  object_name = record
440
442
  else
441
- model = convert_to_model(record)
443
+ model = record
442
444
  object = _object_for_form_builder(record)
443
445
  raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
444
446
  object_name = options[:as] || model_name_from_record_or_class(object).param_key
@@ -465,13 +467,12 @@ module ActionView
465
467
 
466
468
  as = options[:as]
467
469
  namespace = options[:namespace]
468
- action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :patch] : [:new, :post]
470
+ action = object.respond_to?(:persisted?) && object.persisted? ? :edit : :new
469
471
  options[:html] ||= {}
470
472
  options[:html].reverse_merge!(
471
473
  class: as ? "#{action}_#{as}" : dom_class(object, action),
472
474
  id: (as ? [namespace, action, as] : [namespace, dom_id(object, action)]).compact.join("_").presence,
473
475
  )
474
- options[:method] ||= method
475
476
  end
476
477
  private :apply_form_for_options!
477
478
 
@@ -484,12 +485,12 @@ module ActionView
484
485
  # Creates a form tag based on mixing URLs, scopes, or models.
485
486
  #
486
487
  # # Using just a URL:
487
- # <%= form_with url: posts_path do |form| %>
488
+ # <%= form_with url: articles_path do |form| %>
488
489
  # <%= form.text_field :title %>
489
490
  # <% end %>
490
491
  # # =>
491
- # <form action="/posts" method="post">
492
- # <input type="text" name="title">
492
+ # <form action="/articles" method="post">
493
+ # <input type="text" name="title" />
493
494
  # </form>
494
495
  #
495
496
  # # With an intentionally empty URL:
@@ -497,38 +498,37 @@ module ActionView
497
498
  # <%= form.text_field :title %>
498
499
  # <% end %>
499
500
  # # =>
500
- # <form method="post" data-remote="true">
501
- # <input type="text" name="title">
501
+ # <form method="post">
502
+ # <input type="text" name="title" />
502
503
  # </form>
503
504
  #
504
505
  # # Adding a scope prefixes the input field names:
505
- # <%= form_with scope: :post, url: posts_path do |form| %>
506
+ # <%= form_with scope: :article, url: articles_path do |form| %>
506
507
  # <%= form.text_field :title %>
507
508
  # <% end %>
508
509
  # # =>
509
- # <form action="/posts" method="post">
510
- # <input type="text" name="post[title]">
510
+ # <form action="/articles" method="post">
511
+ # <input type="text" name="article[title]" />
511
512
  # </form>
512
513
  #
513
514
  # # Using a model infers both the URL and scope:
514
- # <%= form_with model: Post.new do |form| %>
515
+ # <%= form_with model: Article.new do |form| %>
515
516
  # <%= form.text_field :title %>
516
517
  # <% end %>
517
518
  # # =>
518
- # <form action="/posts" method="post">
519
- # <input type="text" name="post[title]">
519
+ # <form action="/articles" method="post">
520
+ # <input type="text" name="article[title]" />
520
521
  # </form>
521
522
  #
522
523
  # # An existing model makes an update form and fills out field values:
523
- # <%= form_with model: Post.first do |form| %>
524
+ # <%= form_with model: Article.first do |form| %>
524
525
  # <%= form.text_field :title %>
525
526
  # <% end %>
526
527
  # # =>
527
- # <form action="/posts/1" method="post">
528
- # <input type="hidden" name="_method" value="patch">
529
- # <input type="text" name="post[title]" value="<the title of the post>">
528
+ # <form action="/articles/1" method="post">
529
+ # <input type="hidden" name="_method" value="patch" />
530
+ # <input type="text" name="article[title]" value="<the title of the article>" />
530
531
  # </form>
531
- #
532
532
  # # Though the fields don't have to correspond to model attributes:
533
533
  # <%= form_with model: Cat.new do |form| %>
534
534
  # <%= form.text_field :cats_dont_have_gills %>
@@ -536,13 +536,13 @@ module ActionView
536
536
  # <% end %>
537
537
  # # =>
538
538
  # <form action="/cats" method="post">
539
- # <input type="text" name="cat[cats_dont_have_gills]">
540
- # <input type="text" name="cat[but_in_forms_they_can]">
539
+ # <input type="text" name="cat[cats_dont_have_gills]" />
540
+ # <input type="text" name="cat[but_in_forms_they_can]" />
541
541
  # </form>
542
542
  #
543
543
  # The parameters in the forms are accessible in controllers according to
544
- # their name nesting. So inputs named +title+ and <tt>post[title]</tt> are
545
- # accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
544
+ # their name nesting. So inputs named +title+ and <tt>article[title]</tt> are
545
+ # accessible as <tt>params[:title]</tt> and <tt>params[:article][:title]</tt>
546
546
  # respectively.
547
547
  #
548
548
  # For ease of comparison the examples above left out the submit button,
@@ -556,27 +556,27 @@ module ActionView
556
556
  # is a _resource_. It corresponds to a set of RESTful routes, most likely
557
557
  # defined via +resources+ in <tt>config/routes.rb</tt>.
558
558
  #
559
- # So when passing such a model record, Rails infers the URL and method.
559
+ # So when passing such a model record, \Rails infers the URL and method.
560
560
  #
561
- # <%= form_with model: @post do |form| %>
561
+ # <%= form_with model: @article do |form| %>
562
562
  # ...
563
563
  # <% end %>
564
564
  #
565
565
  # is then equivalent to something like:
566
566
  #
567
- # <%= form_with scope: :post, url: post_path(@post), method: :patch do |form| %>
567
+ # <%= form_with scope: :article, url: article_path(@article), method: :patch do |form| %>
568
568
  # ...
569
569
  # <% end %>
570
570
  #
571
571
  # And for a new record
572
572
  #
573
- # <%= form_with model: Post.new do |form| %>
573
+ # <%= form_with model: Article.new do |form| %>
574
574
  # ...
575
575
  # <% end %>
576
576
  #
577
577
  # is equivalent to something like:
578
578
  #
579
- # <%= form_with scope: :post, url: posts_path do |form| %>
579
+ # <%= form_with scope: :article, url: articles_path do |form| %>
580
580
  # ...
581
581
  # <% end %>
582
582
  #
@@ -605,7 +605,7 @@ module ActionView
605
605
  # If the model is a new record a create form is generated, if an
606
606
  # existing record, however, an update form is generated.
607
607
  # Pass <tt>:scope</tt> or <tt>:url</tt> to override the defaults.
608
- # E.g. turn <tt>params[:post]</tt> into <tt>params[:article]</tt>.
608
+ # E.g. turn <tt>params[:article]</tt> into <tt>params[:blog]</tt>.
609
609
  # * <tt>:authenticity_token</tt> - Authenticity token to use in the form.
610
610
  # Override with a custom authenticity token or pass <tt>false</tt> to
611
611
  # skip the authenticity token field altogether.
@@ -619,12 +619,12 @@ module ActionView
619
619
  # * <tt>:local</tt> - Whether to use standard HTTP form submission.
620
620
  # When set to <tt>true</tt>, the form is submitted via standard HTTP.
621
621
  # When set to <tt>false</tt>, the form is submitted as a "remote form", which
622
- # is handled by Rails UJS as an XHR. When unspecified, the behavior is derived
622
+ # is handled by \Rails UJS as an XHR. When unspecified, the behavior is derived
623
623
  # from <tt>config.action_view.form_with_generates_remote_forms</tt> where the
624
624
  # config's value is actually the inverse of what <tt>local</tt>'s value would be.
625
- # As of Rails 6.1, that configuration option defaults to <tt>false</tt>
625
+ # As of \Rails 6.1, that configuration option defaults to <tt>false</tt>
626
626
  # (which has the equivalent effect of passing <tt>local: true</tt>).
627
- # In previous versions of Rails, that configuration option defaults to
627
+ # In previous versions of \Rails, that configuration option defaults to
628
628
  # <tt>true</tt> (the equivalent of passing <tt>local: false</tt>).
629
629
  # * <tt>:skip_enforcing_utf8</tt> - If set to true, a hidden input with name
630
630
  # utf8 is not output.
@@ -638,14 +638,14 @@ module ActionView
638
638
  #
639
639
  # When not passing a block, +form_with+ just generates an opening form tag.
640
640
  #
641
- # <%= form_with(model: @post, url: super_posts_path) %>
642
- # <%= form_with(model: @post, scope: :article) %>
643
- # <%= form_with(model: @post, format: :json) %>
644
- # <%= form_with(model: @post, authenticity_token: false) %> # Disables the token.
641
+ # <%= form_with(model: @article, url: super_articles_path) %>
642
+ # <%= form_with(model: @article, scope: :blog) %>
643
+ # <%= form_with(model: @article, format: :json) %>
644
+ # <%= form_with(model: @article, authenticity_token: false) %> # Disables the token.
645
645
  #
646
- # For namespaced routes, like +admin_post_url+:
646
+ # For namespaced routes, like +admin_article_url+:
647
647
  #
648
- # <%= form_with(model: [ :admin, @post ]) do |form| %>
648
+ # <%= form_with(model: [ :admin, @article ]) do |form| %>
649
649
  # ...
650
650
  # <% end %>
651
651
  #
@@ -693,13 +693,13 @@ module ActionView
693
693
  # You can set data attributes directly in a data hash, but HTML options
694
694
  # besides id and class must be wrapped in an HTML key:
695
695
  #
696
- # <%= form_with(model: @post, data: { behavior: "autosave" }, html: { name: "go" }) do |form| %>
696
+ # <%= form_with(model: @article, data: { behavior: "autosave" }, html: { name: "go" }) do |form| %>
697
697
  # ...
698
698
  # <% end %>
699
699
  #
700
700
  # generates
701
701
  #
702
- # <form action="/posts/123" method="post" data-behavior="autosave" name="go">
702
+ # <form action="/articles/123" method="post" data-behavior="autosave" name="go">
703
703
  # <input name="_method" type="hidden" value="patch" />
704
704
  # ...
705
705
  # </form>
@@ -711,10 +711,10 @@ module ActionView
711
711
  # Some ORM systems do not use IDs on nested models so in this case you want to be able
712
712
  # to disable the hidden id.
713
713
  #
714
- # In the following example the Post model has many Comments stored within it in a NoSQL database,
714
+ # In the following example the Article model has many Comments stored within it in a NoSQL database,
715
715
  # thus there is no primary key for comments.
716
716
  #
717
- # <%= form_with(model: @post) do |form| %>
717
+ # <%= form_with(model: @article) do |form| %>
718
718
  # <%= form.fields(:comments, skip_id: true) do |fields| %>
719
719
  # ...
720
720
  # <% end %>
@@ -752,15 +752,21 @@ module ActionView
752
752
  # def labelled_form_with(**options, &block)
753
753
  # form_with(**options.merge(builder: LabellingFormBuilder), &block)
754
754
  # end
755
- def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
755
+ def form_with(model: false, scope: nil, url: nil, format: nil, **options, &block)
756
+ ActionView.deprecator.warn("Passing nil to the :model argument is deprecated and will raise in Rails 8.0") if model.nil?
757
+
756
758
  options = { allow_method_names_outside_object: true, skip_default_ids: !form_with_generates_ids }.merge!(options)
757
759
 
758
760
  if model
759
761
  if url != false
760
- url ||= polymorphic_path(model, format: format)
762
+ url ||= if format.nil?
763
+ polymorphic_path(model, {})
764
+ else
765
+ polymorphic_path(model, format: format)
766
+ end
761
767
  end
762
768
 
763
- model = _object_for_form_builder(model)
769
+ model = convert_to_model(_object_for_form_builder(model))
764
770
  scope ||= model_name_from_record_or_class(model).param_key
765
771
  end
766
772
 
@@ -777,23 +783,23 @@ module ActionView
777
783
  end
778
784
  end
779
785
 
780
- # Creates a scope around a specific model object like form_for, but
786
+ # Creates a scope around a specific model object like form_with, but
781
787
  # doesn't create the form tags themselves. This makes fields_for suitable
782
788
  # for specifying additional model objects in the same form.
783
789
  #
784
- # Although the usage and purpose of +fields_for+ is similar to +form_for+'s,
785
- # its method signature is slightly different. Like +form_for+, it yields
790
+ # Although the usage and purpose of +fields_for+ is similar to +form_with+'s,
791
+ # its method signature is slightly different. Like +form_with+, it yields
786
792
  # a FormBuilder object associated with a particular model object to a block,
787
793
  # and within the block allows methods to be called on the builder to
788
794
  # generate fields associated with the model object. Fields may reflect
789
795
  # a model object in two ways - how they are named (hence how submitted
790
796
  # values appear within the +params+ hash in the controller) and what
791
- # default values are shown when the form the fields appear in is first
792
- # displayed. In order for both of these features to be specified independently,
797
+ # default values are shown when the form fields are first displayed.
798
+ # In order for both of these features to be specified independently,
793
799
  # both an object name (represented by either a symbol or string) and the
794
800
  # object itself can be passed to the method separately -
795
801
  #
796
- # <%= form_for @person do |person_form| %>
802
+ # <%= form_with model: @person do |person_form| %>
797
803
  # First name: <%= person_form.text_field :first_name %>
798
804
  # Last name : <%= person_form.text_field :last_name %>
799
805
  #
@@ -874,7 +880,7 @@ module ActionView
874
880
  #
875
881
  # This model can now be used with a nested fields_for, like so:
876
882
  #
877
- # <%= form_for @person do |person_form| %>
883
+ # <%= form_with model: @person do |person_form| %>
878
884
  # ...
879
885
  # <%= person_form.fields_for :address do |address_fields| %>
880
886
  # Street : <%= address_fields.text_field :street %>
@@ -904,7 +910,7 @@ module ActionView
904
910
  # with a value that evaluates to +true+, you will destroy the associated
905
911
  # model (e.g. 1, '1', true, or 'true'):
906
912
  #
907
- # <%= form_for @person do |person_form| %>
913
+ # <%= form_with model: @person do |person_form| %>
908
914
  # ...
909
915
  # <%= person_form.fields_for :address do |address_fields| %>
910
916
  # ...
@@ -945,7 +951,7 @@ module ActionView
945
951
  # the nested fields_for call will be repeated for each instance in the
946
952
  # collection:
947
953
  #
948
- # <%= form_for @person do |person_form| %>
954
+ # <%= form_with model: @person do |person_form| %>
949
955
  # ...
950
956
  # <%= person_form.fields_for :projects do |project_fields| %>
951
957
  # <% if project_fields.object.active? %>
@@ -957,7 +963,7 @@ module ActionView
957
963
  #
958
964
  # It's also possible to specify the instance to be used:
959
965
  #
960
- # <%= form_for @person do |person_form| %>
966
+ # <%= form_with model: @person do |person_form| %>
961
967
  # ...
962
968
  # <% @person.projects.each do |project| %>
963
969
  # <% if project.active? %>
@@ -971,7 +977,7 @@ module ActionView
971
977
  #
972
978
  # Or a collection to be used:
973
979
  #
974
- # <%= form_for @person do |person_form| %>
980
+ # <%= form_with model: @person do |person_form| %>
975
981
  # ...
976
982
  # <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
977
983
  # Name: <%= project_fields.text_field :name %>
@@ -993,7 +999,7 @@ module ActionView
993
999
  # parameter with a value that evaluates to +true+
994
1000
  # (e.g. 1, '1', true, or 'true'):
995
1001
  #
996
- # <%= form_for @person do |person_form| %>
1002
+ # <%= form_with model: @person do |person_form| %>
997
1003
  # ...
998
1004
  # <%= person_form.fields_for :projects do |project_fields| %>
999
1005
  # Delete: <%= project_fields.check_box :_destroy %>
@@ -1002,10 +1008,10 @@ module ActionView
1002
1008
  # <% end %>
1003
1009
  #
1004
1010
  # When a collection is used you might want to know the index of each
1005
- # object into the array. For this purpose, the <tt>index</tt> method
1006
- # is available in the FormBuilder object.
1011
+ # object in the array. For this purpose, the <tt>index</tt> method is
1012
+ # available in the FormBuilder object.
1007
1013
  #
1008
- # <%= form_for @person do |person_form| %>
1014
+ # <%= form_with model: @person do |person_form| %>
1009
1015
  # ...
1010
1016
  # <%= person_form.fields_for :projects do |project_fields| %>
1011
1017
  # Project #<%= project_fields.index %>
@@ -1042,7 +1048,7 @@ module ActionView
1042
1048
  # # => <input type="text" name="comment[body]" value="full bodied">
1043
1049
  #
1044
1050
  # # Using +fields+ with +form_with+:
1045
- # <%= form_with model: @post do |form| %>
1051
+ # <%= form_with model: @article do |form| %>
1046
1052
  # <%= form.text_field :title %>
1047
1053
  #
1048
1054
  # <%= form.fields :comment do |fields| %>
@@ -1090,56 +1096,58 @@ module ActionView
1090
1096
  # target labels for radio_button tags (where the value is used in the ID of the input tag).
1091
1097
  #
1092
1098
  # ==== Examples
1093
- # label(:post, :title)
1094
- # # => <label for="post_title">Title</label>
1099
+ # label(:article, :title)
1100
+ # # => <label for="article_title">Title</label>
1095
1101
  #
1096
1102
  # You can localize your labels based on model and attribute names.
1097
1103
  # For example you can define the following in your locale (e.g. en.yml)
1098
1104
  #
1099
1105
  # helpers:
1100
1106
  # label:
1101
- # post:
1107
+ # article:
1102
1108
  # body: "Write your entire text here"
1103
1109
  #
1104
1110
  # Which then will result in
1105
1111
  #
1106
- # label(:post, :body)
1107
- # # => <label for="post_body">Write your entire text here</label>
1112
+ # label(:article, :body)
1113
+ # # => <label for="article_body">Write your entire text here</label>
1108
1114
  #
1109
1115
  # Localization can also be based purely on the translation of the attribute-name
1110
1116
  # (if you are using ActiveRecord):
1111
1117
  #
1112
1118
  # activerecord:
1113
1119
  # attributes:
1114
- # post:
1120
+ # article:
1115
1121
  # cost: "Total cost"
1116
1122
  #
1117
- # label(:post, :cost)
1118
- # # => <label for="post_cost">Total cost</label>
1123
+ # <code></code>
1119
1124
  #
1120
- # label(:post, :title, "A short title")
1121
- # # => <label for="post_title">A short title</label>
1125
+ # label(:article, :cost)
1126
+ # # => <label for="article_cost">Total cost</label>
1122
1127
  #
1123
- # label(:post, :title, "A short title", class: "title_label")
1124
- # # => <label for="post_title" class="title_label">A short title</label>
1128
+ # label(:article, :title, "A short title")
1129
+ # # => <label for="article_title">A short title</label>
1125
1130
  #
1126
- # label(:post, :privacy, "Public Post", value: "public")
1127
- # # => <label for="post_privacy_public">Public Post</label>
1131
+ # label(:article, :title, "A short title", class: "title_label")
1132
+ # # => <label for="article_title" class="title_label">A short title</label>
1128
1133
  #
1129
- # label(:post, :cost) do |translation|
1134
+ # label(:article, :privacy, "Public Article", value: "public")
1135
+ # # => <label for="article_privacy_public">Public Article</label>
1136
+ #
1137
+ # label(:article, :cost) do |translation|
1130
1138
  # content_tag(:span, translation, class: "cost_label")
1131
1139
  # end
1132
- # # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
1140
+ # # => <label for="article_cost"><span class="cost_label">Total cost</span></label>
1133
1141
  #
1134
- # label(:post, :cost) do |builder|
1142
+ # label(:article, :cost) do |builder|
1135
1143
  # content_tag(:span, builder.translation, class: "cost_label")
1136
1144
  # end
1137
- # # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
1145
+ # # => <label for="article_cost"><span class="cost_label">Total cost</span></label>
1138
1146
  #
1139
- # label(:post, :terms) do
1147
+ # label(:article, :terms) do
1140
1148
  # raw('Accept <a href="/terms">Terms</a>.')
1141
1149
  # end
1142
- # # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
1150
+ # # => <label for="article_terms">Accept <a href="/terms">Terms</a>.</label>
1143
1151
  def label(object_name, method, content_or_options = nil, options = nil, &block)
1144
1152
  Tags::Label.new(object_name, method, self, content_or_options, options).render(&block)
1145
1153
  end
@@ -1150,14 +1158,14 @@ module ActionView
1150
1158
  # shown.
1151
1159
  #
1152
1160
  # ==== Examples
1153
- # text_field(:post, :title, size: 20)
1154
- # # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
1161
+ # text_field(:article, :title, size: 20)
1162
+ # # => <input type="text" id="article_title" name="article[title]" size="20" value="#{@article.title}" />
1155
1163
  #
1156
- # text_field(:post, :title, class: "create_input")
1157
- # # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
1164
+ # text_field(:article, :title, class: "create_input")
1165
+ # # => <input type="text" id="article_title" name="article[title]" value="#{@article.title}" class="create_input" />
1158
1166
  #
1159
- # text_field(:post, :title, maxlength: 30, class: "title_input")
1160
- # # => <input type="text" id="post_title" name="post[title]" maxlength="30" size="30" value="#{@post.title}" class="title_input" />
1167
+ # text_field(:article, :title, maxlength: 30, class: "title_input")
1168
+ # # => <input type="text" id="article_title" name="article[title]" maxlength="30" size="30" value="#{@article.title}" class="title_input" />
1161
1169
  #
1162
1170
  # text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }")
1163
1171
  # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }"/>
@@ -1198,8 +1206,8 @@ module ActionView
1198
1206
  # hidden_field(:signup, :pass_confirm)
1199
1207
  # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
1200
1208
  #
1201
- # hidden_field(:post, :tag_list)
1202
- # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
1209
+ # hidden_field(:article, :tag_list)
1210
+ # # => <input type="hidden" id="article_tag_list" name="article[tag_list]" value="#{@article.tag_list}" />
1203
1211
  #
1204
1212
  # hidden_field(:user, :token)
1205
1213
  # # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
@@ -1212,7 +1220,7 @@ module ActionView
1212
1220
  # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
1213
1221
  # shown.
1214
1222
  #
1215
- # Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
1223
+ # Using this method inside a +form_with+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
1216
1224
  #
1217
1225
  # ==== Options
1218
1226
  # * Creates standard HTML attributes for the tag.
@@ -1225,14 +1233,14 @@ module ActionView
1225
1233
  # file_field(:user, :avatar)
1226
1234
  # # => <input type="file" id="user_avatar" name="user[avatar]" />
1227
1235
  #
1228
- # file_field(:post, :image, multiple: true)
1229
- # # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
1236
+ # file_field(:article, :image, multiple: true)
1237
+ # # => <input type="file" id="article_image" name="article[image][]" multiple="multiple" />
1230
1238
  #
1231
- # file_field(:post, :attached, accept: 'text/html')
1232
- # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
1239
+ # file_field(:article, :attached, accept: 'text/html')
1240
+ # # => <input accept="text/html" type="file" id="article_attached" name="article[attached]" />
1233
1241
  #
1234
- # file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
1235
- # # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
1242
+ # file_field(:article, :image, accept: 'image/png,image/gif,image/jpeg')
1243
+ # # => <input type="file" id="article_image" name="article[image]" accept="image/png,image/gif,image/jpeg" />
1236
1244
  #
1237
1245
  # file_field(:attachment, :file, class: 'file_input')
1238
1246
  # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
@@ -1247,9 +1255,9 @@ module ActionView
1247
1255
  # hash with +options+.
1248
1256
  #
1249
1257
  # ==== Examples
1250
- # text_area(:post, :body, cols: 20, rows: 40)
1251
- # # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
1252
- # # #{@post.body}
1258
+ # text_area(:article, :body, cols: 20, rows: 40)
1259
+ # # => <textarea cols="20" rows="40" id="article_body" name="article[body]">
1260
+ # # #{@article.body}
1253
1261
  # # </textarea>
1254
1262
  #
1255
1263
  # text_area(:comment, :text, size: "20x30")
@@ -1312,7 +1320,7 @@ module ActionView
1312
1320
  # ...
1313
1321
  # <% end %>
1314
1322
  #
1315
- # because parameter name repetition is precisely what Rails seeks to distinguish
1323
+ # because parameter name repetition is precisely what \Rails seeks to distinguish
1316
1324
  # the elements of the array. For each item with a checked check box you
1317
1325
  # get an extra ghost item with only that attribute, assigned to "0".
1318
1326
  #
@@ -1321,10 +1329,10 @@ module ActionView
1321
1329
  #
1322
1330
  # ==== Examples
1323
1331
  #
1324
- # # Let's say that @post.validated? is 1:
1325
- # check_box("post", "validated")
1326
- # # => <input name="post[validated]" type="hidden" value="0" />
1327
- # # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
1332
+ # # Let's say that @article.validated? is 1:
1333
+ # check_box("article", "validated")
1334
+ # # => <input name="article[validated]" type="hidden" value="0" />
1335
+ # # <input checked="checked" type="checkbox" id="article_validated" name="article[validated]" value="1" />
1328
1336
  #
1329
1337
  # # Let's say that @puppy.gooddog is "no":
1330
1338
  # check_box("puppy", "gooddog", {}, "yes", "no")
@@ -1345,11 +1353,11 @@ module ActionView
1345
1353
  # To force the radio button to be checked pass <tt>checked: true</tt> in the
1346
1354
  # +options+ hash. You may pass HTML options there as well.
1347
1355
  #
1348
- # # Let's say that @post.category returns "rails":
1349
- # radio_button("post", "category", "rails")
1350
- # radio_button("post", "category", "java")
1351
- # # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
1352
- # # <input type="radio" id="post_category_java" name="post[category]" value="java" />
1356
+ # # Let's say that @article.category returns "rails":
1357
+ # radio_button("article", "category", "rails")
1358
+ # radio_button("article", "category", "java")
1359
+ # # => <input type="radio" id="article_category_rails" name="article[category]" value="rails" checked="checked" />
1360
+ # # <input type="radio" id="article_category_java" name="article[category]" value="java" />
1353
1361
  #
1354
1362
  # # Let's say that @user.receive_newsletter returns "no":
1355
1363
  # radio_button("user", "receive_newsletter", "yes")
@@ -1495,6 +1503,12 @@ module ActionView
1495
1503
  # datetime_field("user", "born_on", min: "2014-05-20T00:00:00")
1496
1504
  # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" />
1497
1505
  #
1506
+ # By default, provided datetimes will be formatted including seconds. You can render just the date, hour,
1507
+ # and minute by passing <tt>include_seconds: false</tt>.
1508
+ #
1509
+ # @user.born_on = Time.current
1510
+ # datetime_field("user", "born_on", include_seconds: false)
1511
+ # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="2014-05-20T14:35" />
1498
1512
  def datetime_field(object_name, method, options = {})
1499
1513
  Tags::DatetimeLocalField.new(object_name, method, self, options).render
1500
1514
  end
@@ -1611,12 +1625,14 @@ module ActionView
1611
1625
  end
1612
1626
  end
1613
1627
 
1628
+ # = Action View Form Builder
1629
+ #
1614
1630
  # A +FormBuilder+ object is associated with a particular model object and
1615
1631
  # allows you to generate fields associated with the model object. The
1616
- # +FormBuilder+ object is yielded when using +form_for+ or +fields_for+.
1632
+ # +FormBuilder+ object is yielded when using +form_with+ or +fields_for+.
1617
1633
  # For example:
1618
1634
  #
1619
- # <%= form_for @person do |person_form| %>
1635
+ # <%= form_with model: @person do |person_form| %>
1620
1636
  # Name: <%= person_form.text_field :name %>
1621
1637
  # Admin: <%= person_form.check_box :admin %>
1622
1638
  # <% end %>
@@ -1652,7 +1668,7 @@ module ActionView
1652
1668
  #
1653
1669
  # The +div_radio_button+ code from above can now be used as follows:
1654
1670
  #
1655
- # <%= form_for @person, :builder => MyFormBuilder do |f| %>
1671
+ # <%= form_with model: @person, :builder => MyFormBuilder do |f| %>
1656
1672
  # I am a child: <%= f.div_radio_button(:admin, "child") %>
1657
1673
  # I am an adult: <%= f.div_radio_button(:admin, "adult") %>
1658
1674
  # <% end -%>
@@ -1722,7 +1738,7 @@ module ActionView
1722
1738
  #
1723
1739
  # return the <tt><form></tt> element's <tt>id</tt> attribute.
1724
1740
  #
1725
- # <%= form_for @post do |f| %>
1741
+ # <%= form_with model: @article do |f| %>
1726
1742
  # <%# ... %>
1727
1743
  #
1728
1744
  # <% content_for :sticky_footer do %>
@@ -1744,7 +1760,7 @@ module ActionView
1744
1760
  # Return the value generated by the <tt>FormBuilder</tt> for the given
1745
1761
  # attribute name.
1746
1762
  #
1747
- # <%= form_for @post do |f| %>
1763
+ # <%= form_with model: @article do |f| %>
1748
1764
  # <%= f.label :title %>
1749
1765
  # <%= f.text_field :title, aria: { describedby: f.field_id(:title, :error) } %>
1750
1766
  # <%= tag.span("is blank", id: f.field_id(:title, :error) %>
@@ -1753,7 +1769,7 @@ module ActionView
1753
1769
  # In the example above, the <tt><input type="text"></tt> element built by
1754
1770
  # the call to <tt>FormBuilder#text_field</tt> declares an
1755
1771
  # <tt>aria-describedby</tt> attribute referencing the <tt><span></tt>
1756
- # element, sharing a common <tt>id</tt> root (<tt>post_title</tt>, in this
1772
+ # element, sharing a common <tt>id</tt> root (<tt>article_title</tt>, in this
1757
1773
  # case).
1758
1774
  def field_id(method, *suffixes, namespace: @options[:namespace], index: @options[:index])
1759
1775
  @template.field_id(@object_name, method, *suffixes, namespace: namespace, index: index)
@@ -1765,14 +1781,14 @@ module ActionView
1765
1781
  # Return the value generated by the <tt>FormBuilder</tt> for the given
1766
1782
  # attribute name.
1767
1783
  #
1768
- # <%= form_for @post do |f| %>
1784
+ # <%= form_with model: @article do |f| %>
1769
1785
  # <%= f.text_field :title, name: f.field_name(:title, :subtitle) %>
1770
- # <%# => <input type="text" name="post[title][subtitle]">
1786
+ # <%# => <input type="text" name="article[title][subtitle]"> %>
1771
1787
  # <% end %>
1772
1788
  #
1773
- # <%= form_for @post do |f| %>
1774
- # <%= f.field_tag :tag, name: f.field_name(:tag, multiple: true) %>
1775
- # <%# => <input type="text" name="post[tag][]">
1789
+ # <%= form_with model: @article do |f| %>
1790
+ # <%= f.text_field :tag, name: f.field_name(:tag, multiple: true) %>
1791
+ # <%# => <input type="text" name="article[tag][]"> %>
1776
1792
  # <% end %>
1777
1793
  #
1778
1794
  def field_name(method, *methods, multiple: false, index: @options[:index])
@@ -2002,35 +2018,39 @@ module ActionView
2002
2018
  #
2003
2019
  # Please refer to the documentation of the base helper for details.
2004
2020
 
2005
- (field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
2006
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
2007
- def #{selector}(method, options = {}) # def text_field(method, options = {})
2008
- @template.public_send( # @template.public_send(
2009
- #{selector.inspect}, # :text_field,
2010
- @object_name, # @object_name,
2011
- method, # method,
2012
- objectify_options(options)) # objectify_options(options))
2013
- end # end
2014
- RUBY_EVAL
2021
+ ActiveSupport::CodeGenerator.batch(self, __FILE__, __LINE__) do |code_generator|
2022
+ (field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
2023
+ code_generator.define_cached_method(selector, namespace: :form_builder) do |batch|
2024
+ batch.push <<-RUBY_EVAL
2025
+ def #{selector}(method, options = {}) # def text_field(method, options = {})
2026
+ @template.public_send( # @template.public_send(
2027
+ #{selector.inspect}, # :text_field,
2028
+ @object_name, # @object_name,
2029
+ method, # method,
2030
+ objectify_options(options)) # objectify_options(options))
2031
+ end # end
2032
+ RUBY_EVAL
2033
+ end
2034
+ end
2015
2035
  end
2016
2036
 
2017
- # Creates a scope around a specific model object like form_for, but
2037
+ # Creates a scope around a specific model object like form_with, but
2018
2038
  # doesn't create the form tags themselves. This makes fields_for suitable
2019
2039
  # for specifying additional model objects in the same form.
2020
2040
  #
2021
- # Although the usage and purpose of +fields_for+ is similar to +form_for+'s,
2022
- # its method signature is slightly different. Like +form_for+, it yields
2041
+ # Although the usage and purpose of +fields_for+ is similar to +form_with+'s,
2042
+ # its method signature is slightly different. Like +form_with+, it yields
2023
2043
  # a FormBuilder object associated with a particular model object to a block,
2024
2044
  # and within the block allows methods to be called on the builder to
2025
2045
  # generate fields associated with the model object. Fields may reflect
2026
2046
  # a model object in two ways - how they are named (hence how submitted
2027
2047
  # values appear within the +params+ hash in the controller) and what
2028
- # default values are shown when the form the fields appear in is first
2029
- # displayed. In order for both of these features to be specified independently,
2048
+ # default values are shown when the form fields are first displayed.
2049
+ # In order for both of these features to be specified independently,
2030
2050
  # both an object name (represented by either a symbol or string) and the
2031
2051
  # object itself can be passed to the method separately -
2032
2052
  #
2033
- # <%= form_for @person do |person_form| %>
2053
+ # <%= form_with model: @person do |person_form| %>
2034
2054
  # First name: <%= person_form.text_field :first_name %>
2035
2055
  # Last name : <%= person_form.text_field :last_name %>
2036
2056
  #
@@ -2075,6 +2095,18 @@ module ActionView
2075
2095
  # DateHelper that are designed to work with an object as base, like
2076
2096
  # FormOptionsHelper#collection_select and DateHelper#datetime_select.
2077
2097
  #
2098
+ # +fields_for+ tries to be smart about parameters, but it can be confused if both
2099
+ # name and value parameters are provided and the provided value has the shape of an
2100
+ # option Hash. To remove the ambiguity, explicitly pass an option Hash, even if empty.
2101
+ #
2102
+ # <%= form_with model: @person do |person_form| %>
2103
+ # ...
2104
+ # <%= fields_for :permission, @person.permission, {} do |permission_fields| %>
2105
+ # Admin?: <%= check_box_tag permission_fields.field_name(:admin), @person.permission[:admin] %>
2106
+ # <% end %>
2107
+ # ...
2108
+ # <% end %>
2109
+ #
2078
2110
  # === Nested Attributes Examples
2079
2111
  #
2080
2112
  # When the object belonging to the current scope has a nested attribute
@@ -2111,7 +2143,7 @@ module ActionView
2111
2143
  #
2112
2144
  # This model can now be used with a nested fields_for, like so:
2113
2145
  #
2114
- # <%= form_for @person do |person_form| %>
2146
+ # <%= form_with model: @person do |person_form| %>
2115
2147
  # ...
2116
2148
  # <%= person_form.fields_for :address do |address_fields| %>
2117
2149
  # Street : <%= address_fields.text_field :street %>
@@ -2141,7 +2173,7 @@ module ActionView
2141
2173
  # with a value that evaluates to +true+, you will destroy the associated
2142
2174
  # model (e.g. 1, '1', true, or 'true'):
2143
2175
  #
2144
- # <%= form_for @person do |person_form| %>
2176
+ # <%= form_with model: @person do |person_form| %>
2145
2177
  # ...
2146
2178
  # <%= person_form.fields_for :address do |address_fields| %>
2147
2179
  # ...
@@ -2182,7 +2214,7 @@ module ActionView
2182
2214
  # the nested fields_for call will be repeated for each instance in the
2183
2215
  # collection:
2184
2216
  #
2185
- # <%= form_for @person do |person_form| %>
2217
+ # <%= form_with model: @person do |person_form| %>
2186
2218
  # ...
2187
2219
  # <%= person_form.fields_for :projects do |project_fields| %>
2188
2220
  # <% if project_fields.object.active? %>
@@ -2194,7 +2226,7 @@ module ActionView
2194
2226
  #
2195
2227
  # It's also possible to specify the instance to be used:
2196
2228
  #
2197
- # <%= form_for @person do |person_form| %>
2229
+ # <%= form_with model: @person do |person_form| %>
2198
2230
  # ...
2199
2231
  # <% @person.projects.each do |project| %>
2200
2232
  # <% if project.active? %>
@@ -2208,7 +2240,7 @@ module ActionView
2208
2240
  #
2209
2241
  # Or a collection to be used:
2210
2242
  #
2211
- # <%= form_for @person do |person_form| %>
2243
+ # <%= form_with model: @person do |person_form| %>
2212
2244
  # ...
2213
2245
  # <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
2214
2246
  # Name: <%= project_fields.text_field :name %>
@@ -2230,7 +2262,7 @@ module ActionView
2230
2262
  # parameter with a value that evaluates to +true+
2231
2263
  # (e.g. 1, '1', true, or 'true'):
2232
2264
  #
2233
- # <%= form_for @person do |person_form| %>
2265
+ # <%= form_with model: @person do |person_form| %>
2234
2266
  # ...
2235
2267
  # <%= person_form.fields_for :projects do |project_fields| %>
2236
2268
  # Delete: <%= project_fields.check_box :_destroy %>
@@ -2239,10 +2271,10 @@ module ActionView
2239
2271
  # <% end %>
2240
2272
  #
2241
2273
  # When a collection is used you might want to know the index of each
2242
- # object into the array. For this purpose, the <tt>index</tt> method
2274
+ # object in the array. For this purpose, the <tt>index</tt> method
2243
2275
  # is available in the FormBuilder object.
2244
2276
  #
2245
- # <%= form_for @person do |person_form| %>
2277
+ # <%= form_with model: @person do |person_form| %>
2246
2278
  # ...
2247
2279
  # <%= person_form.fields_for :projects do |project_fields| %>
2248
2280
  # Project #<%= project_fields.index %>
@@ -2255,8 +2287,9 @@ module ActionView
2255
2287
  # to store the ID of the record. There are circumstances where this
2256
2288
  # hidden field is not needed and you can pass <tt>include_id: false</tt>
2257
2289
  # to prevent fields_for from rendering it automatically.
2258
- def fields_for(record_name, record_object = nil, fields_options = {}, &block)
2259
- fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
2290
+ def fields_for(record_name, record_object = nil, fields_options = nil, &block)
2291
+ fields_options, record_object = record_object, nil if fields_options.nil? && record_object.is_a?(Hash) && record_object.extractable_options?
2292
+ fields_options ||= {}
2260
2293
  fields_options[:builder] ||= options[:builder]
2261
2294
  fields_options[:namespace] = options[:namespace]
2262
2295
  fields_options[:parent_builder] = self
@@ -2310,50 +2343,52 @@ module ActionView
2310
2343
  #
2311
2344
  # ==== Examples
2312
2345
  # label(:title)
2313
- # # => <label for="post_title">Title</label>
2346
+ # # => <label for="article_title">Title</label>
2314
2347
  #
2315
2348
  # You can localize your labels based on model and attribute names.
2316
2349
  # For example you can define the following in your locale (e.g. en.yml)
2317
2350
  #
2318
2351
  # helpers:
2319
2352
  # label:
2320
- # post:
2353
+ # article:
2321
2354
  # body: "Write your entire text here"
2322
2355
  #
2323
2356
  # Which then will result in
2324
2357
  #
2325
2358
  # label(:body)
2326
- # # => <label for="post_body">Write your entire text here</label>
2359
+ # # => <label for="article_body">Write your entire text here</label>
2327
2360
  #
2328
2361
  # Localization can also be based purely on the translation of the attribute-name
2329
2362
  # (if you are using ActiveRecord):
2330
2363
  #
2331
2364
  # activerecord:
2332
2365
  # attributes:
2333
- # post:
2366
+ # article:
2334
2367
  # cost: "Total cost"
2335
2368
  #
2369
+ # <code></code>
2370
+ #
2336
2371
  # label(:cost)
2337
- # # => <label for="post_cost">Total cost</label>
2372
+ # # => <label for="article_cost">Total cost</label>
2338
2373
  #
2339
2374
  # label(:title, "A short title")
2340
- # # => <label for="post_title">A short title</label>
2375
+ # # => <label for="article_title">A short title</label>
2341
2376
  #
2342
2377
  # label(:title, "A short title", class: "title_label")
2343
- # # => <label for="post_title" class="title_label">A short title</label>
2378
+ # # => <label for="article_title" class="title_label">A short title</label>
2344
2379
  #
2345
- # label(:privacy, "Public Post", value: "public")
2346
- # # => <label for="post_privacy_public">Public Post</label>
2380
+ # label(:privacy, "Public Article", value: "public")
2381
+ # # => <label for="article_privacy_public">Public Article</label>
2347
2382
  #
2348
2383
  # label(:cost) do |translation|
2349
2384
  # content_tag(:span, translation, class: "cost_label")
2350
2385
  # end
2351
- # # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
2386
+ # # => <label for="article_cost"><span class="cost_label">Total cost</span></label>
2352
2387
  #
2353
2388
  # label(:cost) do |builder|
2354
2389
  # content_tag(:span, builder.translation, class: "cost_label")
2355
2390
  # end
2356
- # # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
2391
+ # # => <label for="article_cost"><span class="cost_label">Total cost</span></label>
2357
2392
  #
2358
2393
  # label(:cost) do |builder|
2359
2394
  # content_tag(:span, builder.translation, class: [
@@ -2361,12 +2396,12 @@ module ActionView
2361
2396
  # ("error_label" if builder.object.errors.include?(:cost))
2362
2397
  # ])
2363
2398
  # end
2364
- # # => <label for="post_cost"><span class="cost_label error_label">Total cost</span></label>
2399
+ # # => <label for="article_cost"><span class="cost_label error_label">Total cost</span></label>
2365
2400
  #
2366
2401
  # label(:terms) do
2367
2402
  # raw('Accept <a href="/terms">Terms</a>.')
2368
2403
  # end
2369
- # # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
2404
+ # # => <label for="article_terms">Accept <a href="/terms">Terms</a>.</label>
2370
2405
  def label(method, text = nil, options = {}, &block)
2371
2406
  @template.label(@object_name, method, text, objectify_options(options), &block)
2372
2407
  end
@@ -2413,7 +2448,7 @@ module ActionView
2413
2448
  # ...
2414
2449
  # <% end %>
2415
2450
  #
2416
- # because parameter name repetition is precisely what Rails seeks to distinguish
2451
+ # because parameter name repetition is precisely what \Rails seeks to distinguish
2417
2452
  # the elements of the array. For each item with a checked check box you
2418
2453
  # get an extra ghost item with only that attribute, assigned to "0".
2419
2454
  #
@@ -2422,10 +2457,10 @@ module ActionView
2422
2457
  #
2423
2458
  # ==== Examples
2424
2459
  #
2425
- # # Let's say that @post.validated? is 1:
2460
+ # # Let's say that @article.validated? is 1:
2426
2461
  # check_box("validated")
2427
- # # => <input name="post[validated]" type="hidden" value="0" />
2428
- # # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
2462
+ # # => <input name="article[validated]" type="hidden" value="0" />
2463
+ # # <input checked="checked" type="checkbox" id="article_validated" name="article[validated]" value="1" />
2429
2464
  #
2430
2465
  # # Let's say that @puppy.gooddog is "no":
2431
2466
  # check_box("gooddog", {}, "yes", "no")
@@ -2447,11 +2482,11 @@ module ActionView
2447
2482
  # To force the radio button to be checked pass <tt>checked: true</tt> in the
2448
2483
  # +options+ hash. You may pass HTML options there as well.
2449
2484
  #
2450
- # # Let's say that @post.category returns "rails":
2485
+ # # Let's say that @article.category returns "rails":
2451
2486
  # radio_button("category", "rails")
2452
2487
  # radio_button("category", "java")
2453
- # # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
2454
- # # <input type="radio" id="post_category_java" name="post[category]" value="java" />
2488
+ # # => <input type="radio" id="article_category_rails" name="article[category]" value="rails" checked="checked" />
2489
+ # # <input type="radio" id="article_category_java" name="article[category]" value="java" />
2455
2490
  #
2456
2491
  # # Let's say that @user.receive_newsletter returns "no":
2457
2492
  # radio_button("receive_newsletter", "yes")
@@ -2472,9 +2507,9 @@ module ActionView
2472
2507
  # hidden_field(:pass_confirm)
2473
2508
  # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="true" />
2474
2509
  #
2475
- # # Let's say that @post.tag_list returns "blog, ruby":
2510
+ # # Let's say that @article.tag_list returns "blog, ruby":
2476
2511
  # hidden_field(:tag_list)
2477
- # # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="blog, ruby" />
2512
+ # # => <input type="hidden" id="article_tag_list" name="article[tag_list]" value="blog, ruby" />
2478
2513
  #
2479
2514
  # # Let's say that @user.token returns "abcde":
2480
2515
  # hidden_field(:token)
@@ -2496,7 +2531,7 @@ module ActionView
2496
2531
  # * Creates standard HTML attributes for the tag.
2497
2532
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
2498
2533
  # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
2499
- # * <tt>:include_hidden</tt> - When <tt>multiple: true</tt> and <tt>include_hidden: true</tt>, the field will be prefixed with an <tt><input type="hidden"></tt> field with an empty value to support submitting an empty collection of files.
2534
+ # * <tt>:include_hidden</tt> - When <tt>multiple: true</tt> and <tt>include_hidden: true</tt>, the field will be prefixed with an <tt><input type="hidden"></tt> field with an empty value to support submitting an empty collection of files. Since <tt>include_hidden</tt> will default to <tt>config.active_storage.multiple_file_field_include_hidden</tt> if you don't specify <tt>include_hidden</tt>, you will need to pass <tt>include_hidden: false</tt> to prevent submitting an empty collection of files when passing <tt>multiple: true</tt>.
2500
2535
  # * <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.
2501
2536
  #
2502
2537
  # ==== Examples
@@ -2504,17 +2539,17 @@ module ActionView
2504
2539
  # file_field(:avatar)
2505
2540
  # # => <input type="file" id="user_avatar" name="user[avatar]" />
2506
2541
  #
2507
- # # Let's say that @post has image:
2542
+ # # Let's say that @article has image:
2508
2543
  # file_field(:image, :multiple => true)
2509
- # # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
2544
+ # # => <input type="file" id="article_image" name="article[image][]" multiple="multiple" />
2510
2545
  #
2511
- # # Let's say that @post has attached:
2546
+ # # Let's say that @article has attached:
2512
2547
  # file_field(:attached, accept: 'text/html')
2513
- # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
2548
+ # # => <input accept="text/html" type="file" id="article_attached" name="article[attached]" />
2514
2549
  #
2515
- # # Let's say that @post has image:
2550
+ # # Let's say that @article has image:
2516
2551
  # file_field(:image, accept: 'image/png,image/gif,image/jpeg')
2517
- # # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
2552
+ # # => <input type="file" id="article_image" name="article[image]" accept="image/png,image/gif,image/jpeg" />
2518
2553
  #
2519
2554
  # # Let's say that @attachment has file:
2520
2555
  # file_field(:file, class: 'file_input')
@@ -2527,12 +2562,12 @@ module ActionView
2527
2562
  # Add the submit button for the given form. When no value is given, it checks
2528
2563
  # if the object is a new resource or not to create the proper label:
2529
2564
  #
2530
- # <%= form_for @post do |f| %>
2565
+ # <%= form_with model: @article do |f| %>
2531
2566
  # <%= f.submit %>
2532
2567
  # <% end %>
2533
2568
  #
2534
- # In the example above, if <tt>@post</tt> is a new record, it will use "Create Post" as
2535
- # submit button label; otherwise, it uses "Update Post".
2569
+ # In the example above, if <tt>@article</tt> is a new record, it will use "Create Article" as
2570
+ # submit button label; otherwise, it uses "Update Article".
2536
2571
  #
2537
2572
  # Those labels can be customized using I18n under the +helpers.submit+ key and using
2538
2573
  # <tt>%{model}</tt> for translation interpolation:
@@ -2548,7 +2583,7 @@ module ActionView
2548
2583
  # en:
2549
2584
  # helpers:
2550
2585
  # submit:
2551
- # post:
2586
+ # article:
2552
2587
  # create: "Add %{model}"
2553
2588
  #
2554
2589
  def submit(value = nil, options = {})
@@ -2560,12 +2595,11 @@ module ActionView
2560
2595
  # Add the submit button for the given form. When no value is given, it checks
2561
2596
  # if the object is a new resource or not to create the proper label:
2562
2597
  #
2563
- # <%= form_for @post do |f| %>
2598
+ # <%= form_with model: @article do |f| %>
2564
2599
  # <%= f.button %>
2565
2600
  # <% end %>
2566
- #
2567
- # In the example above, if <tt>@post</tt> is a new record, it will use "Create Post" as
2568
- # button label; otherwise, it uses "Update Post".
2601
+ # In the example above, if <tt>@article</tt> is a new record, it will use "Create Article" as
2602
+ # button label; otherwise, it uses "Update Article".
2569
2603
  #
2570
2604
  # Those labels can be customized using I18n under the +helpers.submit+ key
2571
2605
  # (the same as submit helper) and using <tt>%{model}</tt> for translation interpolation:
@@ -2581,15 +2615,15 @@ module ActionView
2581
2615
  # en:
2582
2616
  # helpers:
2583
2617
  # submit:
2584
- # post:
2618
+ # article:
2585
2619
  # create: "Add %{model}"
2586
2620
  #
2587
2621
  # ==== Examples
2588
- # button("Create post")
2589
- # # => <button name='button' type='submit'>Create post</button>
2622
+ # button("Create article")
2623
+ # # => <button name='button' type='submit'>Create article</button>
2590
2624
  #
2591
2625
  # button(:draft, value: true)
2592
- # # => <button id="post_draft" name="post[draft]" value="true" type="submit">Create post</button>
2626
+ # # => <button id="article_draft" name="article[draft]" value="true" type="submit">Create article</button>
2593
2627
  #
2594
2628
  # button do
2595
2629
  # content_tag(:strong, 'Ask me!')
@@ -2602,13 +2636,13 @@ module ActionView
2602
2636
  # content_tag(:strong, text)
2603
2637
  # end
2604
2638
  # # => <button name='button' type='submit'>
2605
- # # <strong>Create post</strong>
2639
+ # # <strong>Create article</strong>
2606
2640
  # # </button>
2607
2641
  #
2608
2642
  # button(:draft, value: true) do
2609
2643
  # content_tag(:strong, "Save as draft")
2610
2644
  # end
2611
- # # => <button id="post_draft" name="post[draft]" value="true" type="submit">
2645
+ # # => <button id="article_draft" name="article[draft]" value="true" type="submit">
2612
2646
  # # <strong>Save as draft</strong>
2613
2647
  # # </button>
2614
2648
  #