actionview 5.1.4 → 6.1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +199 -168
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -5
- data/lib/action_view.rb +10 -4
- data/lib/action_view/base.rb +87 -23
- data/lib/action_view/buffers.rb +17 -0
- data/lib/action_view/cache_expiry.rb +52 -0
- data/lib/action_view/context.rb +7 -11
- data/lib/action_view/dependency_tracker.rb +12 -4
- data/lib/action_view/digestor.rb +24 -23
- data/lib/action_view/flows.rb +2 -1
- data/lib/action_view/gem_version.rb +4 -2
- data/lib/action_view/helpers.rb +4 -2
- data/lib/action_view/helpers/active_model_helper.rb +9 -4
- data/lib/action_view/helpers/asset_tag_helper.rb +220 -57
- data/lib/action_view/helpers/asset_url_helper.rb +28 -23
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -2
- data/lib/action_view/helpers/cache_helper.rb +39 -28
- data/lib/action_view/helpers/capture_helper.rb +13 -7
- data/lib/action_view/helpers/controller_helper.rb +3 -1
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +5 -3
- data/lib/action_view/helpers/date_helper.rb +78 -33
- data/lib/action_view/helpers/debug_helper.rb +4 -2
- data/lib/action_view/helpers/form_helper.rb +357 -106
- data/lib/action_view/helpers/form_options_helper.rb +45 -39
- data/lib/action_view/helpers/form_tag_helper.rb +42 -27
- data/lib/action_view/helpers/javascript_helper.rb +28 -12
- data/lib/action_view/helpers/number_helper.rb +16 -8
- data/lib/action_view/helpers/output_safety_helper.rb +3 -1
- data/lib/action_view/helpers/rendering_helper.rb +20 -9
- data/lib/action_view/helpers/sanitize_helper.rb +15 -19
- data/lib/action_view/helpers/tag_helper.rb +100 -24
- data/lib/action_view/helpers/tags.rb +3 -1
- data/lib/action_view/helpers/tags/base.rb +30 -21
- data/lib/action_view/helpers/tags/check_box.rb +3 -2
- data/lib/action_view/helpers/tags/checkable.rb +4 -2
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +2 -1
- data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +2 -1
- data/lib/action_view/helpers/tags/collection_select.rb +3 -1
- data/lib/action_view/helpers/tags/color_field.rb +4 -3
- data/lib/action_view/helpers/tags/date_field.rb +3 -2
- data/lib/action_view/helpers/tags/date_select.rb +5 -4
- data/lib/action_view/helpers/tags/datetime_field.rb +3 -2
- data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
- data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
- data/lib/action_view/helpers/tags/email_field.rb +2 -0
- data/lib/action_view/helpers/tags/file_field.rb +2 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -1
- data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
- data/lib/action_view/helpers/tags/label.rb +6 -5
- data/lib/action_view/helpers/tags/month_field.rb +3 -2
- data/lib/action_view/helpers/tags/number_field.rb +2 -0
- data/lib/action_view/helpers/tags/password_field.rb +2 -0
- data/lib/action_view/helpers/tags/placeholderable.rb +2 -0
- data/lib/action_view/helpers/tags/radio_button.rb +3 -2
- data/lib/action_view/helpers/tags/range_field.rb +2 -0
- data/lib/action_view/helpers/tags/search_field.rb +2 -0
- data/lib/action_view/helpers/tags/select.rb +4 -3
- data/lib/action_view/helpers/tags/tel_field.rb +2 -0
- data/lib/action_view/helpers/tags/text_area.rb +3 -1
- data/lib/action_view/helpers/tags/text_field.rb +3 -2
- data/lib/action_view/helpers/tags/time_field.rb +3 -2
- data/lib/action_view/helpers/tags/time_select.rb +2 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
- data/lib/action_view/helpers/tags/translator.rb +3 -6
- data/lib/action_view/helpers/tags/url_field.rb +2 -0
- data/lib/action_view/helpers/tags/week_field.rb +3 -2
- data/lib/action_view/helpers/text_helper.rb +11 -10
- data/lib/action_view/helpers/translation_helper.rb +102 -52
- data/lib/action_view/helpers/url_helper.rb +150 -32
- data/lib/action_view/layouts.rb +15 -15
- data/lib/action_view/log_subscriber.rb +32 -15
- data/lib/action_view/lookup_context.rb +67 -39
- data/lib/action_view/model_naming.rb +2 -0
- data/lib/action_view/path_set.rb +5 -12
- data/lib/action_view/railtie.rb +46 -21
- data/lib/action_view/record_identifier.rb +4 -3
- data/lib/action_view/renderer/abstract_renderer.rb +144 -11
- data/lib/action_view/renderer/collection_renderer.rb +196 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer.rb +33 -283
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +64 -17
- data/lib/action_view/renderer/renderer.rb +61 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +14 -8
- data/lib/action_view/renderer/template_renderer.rb +36 -26
- data/lib/action_view/rendering.rb +57 -38
- data/lib/action_view/routing_url_for.rb +15 -12
- data/lib/action_view/tasks/cache_digests.rake +2 -0
- data/lib/action_view/template.rb +69 -76
- data/lib/action_view/template/error.rb +32 -18
- data/lib/action_view/template/handlers.rb +4 -2
- data/lib/action_view/template/handlers/builder.rb +5 -6
- data/lib/action_view/template/handlers/erb.rb +20 -19
- data/lib/action_view/template/handlers/erb/erubi.rb +17 -9
- data/lib/action_view/template/handlers/html.rb +3 -1
- data/lib/action_view/template/handlers/raw.rb +4 -2
- data/lib/action_view/template/html.rb +8 -7
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +24 -0
- data/lib/action_view/template/resolver.rb +194 -152
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/text.rb +5 -4
- data/lib/action_view/template/types.rb +3 -1
- data/lib/action_view/test_case.rb +38 -30
- data/lib/action_view/testing/resolvers.rb +20 -27
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/version.rb +2 -0
- data/lib/action_view/view_paths.rb +61 -40
- data/lib/assets/compiled/rails-ujs.js +84 -23
- metadata +34 -23
- data/lib/action_view/helpers/record_tag_helper.rb +0 -21
- data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +0 -9
- data/lib/action_view/template/handlers/erb/erubis.rb +0 -81
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView
|
2
4
|
# = Action View CSRF Helper
|
3
|
-
module Helpers
|
5
|
+
module Helpers #:nodoc:
|
4
6
|
module CsrfHelper
|
5
7
|
# Returns meta tags "csrf-param" and "csrf-token" with the name of the cross-site
|
6
8
|
# request forgery protection parameter and token, respectively.
|
@@ -15,10 +17,10 @@ module ActionView
|
|
15
17
|
# You don't need to use these tags for regular forms as they generate their own hidden fields.
|
16
18
|
#
|
17
19
|
# For AJAX requests other than GETs, extract the "csrf-token" from the meta-tag and send as the
|
18
|
-
# "X-CSRF-Token" HTTP header. If you are using
|
20
|
+
# "X-CSRF-Token" HTTP header. If you are using rails-ujs this happens automatically.
|
19
21
|
#
|
20
22
|
def csrf_meta_tags
|
21
|
-
if protect_against_forgery?
|
23
|
+
if defined?(protect_against_forgery?) && protect_against_forgery?
|
22
24
|
[
|
23
25
|
tag("meta", name: "csrf-param", content: request_forgery_protection_token),
|
24
26
|
tag("meta", name: "csrf-token", content: form_authenticity_token)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "date"
|
2
4
|
require "action_view/helpers/tag_helper"
|
3
5
|
require "active_support/core_ext/array/extract_options"
|
@@ -7,7 +9,7 @@ require "active_support/core_ext/object/acts_like"
|
|
7
9
|
require "active_support/core_ext/object/with_options"
|
8
10
|
|
9
11
|
module ActionView
|
10
|
-
module Helpers
|
12
|
+
module Helpers #:nodoc:
|
11
13
|
# = Action View Date Helpers
|
12
14
|
#
|
13
15
|
# The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
|
@@ -114,7 +116,7 @@ module ActionView
|
|
114
116
|
when 10..19 then locale.t :less_than_x_seconds, count: 20
|
115
117
|
when 20..39 then locale.t :half_a_minute
|
116
118
|
when 40..59 then locale.t :less_than_x_minutes, count: 1
|
117
|
-
|
119
|
+
else locale.t :x_minutes, count: 1
|
118
120
|
end
|
119
121
|
|
120
122
|
when 2...45 then locale.t :x_minutes, count: distance_in_minutes
|
@@ -129,7 +131,7 @@ module ActionView
|
|
129
131
|
when 43200...86400 then locale.t :about_x_months, count: (distance_in_minutes.to_f / 43200.0).round
|
130
132
|
# 60 days up to 365 days
|
131
133
|
when 86400...525600 then locale.t :x_months, count: (distance_in_minutes.to_f / 43200.0).round
|
132
|
-
|
134
|
+
else
|
133
135
|
from_year = from_time.year
|
134
136
|
from_year += 1 if from_time.month >= 3
|
135
137
|
to_year = to_time.year
|
@@ -195,14 +197,15 @@ module ActionView
|
|
195
197
|
# and +:name+ (string). A format string would be something like "%{name} (%<number>02d)" for example.
|
196
198
|
# See <tt>Kernel.sprintf</tt> for documentation on format sequences.
|
197
199
|
# * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
|
198
|
-
# * <tt>:time_separator</tt> - Specifies a string to separate the time fields. Default is "
|
199
|
-
# * <tt>:datetime_separator</tt>- Specifies a string to separate the date and time fields. Default is "
|
200
|
+
# * <tt>:time_separator</tt> - Specifies a string to separate the time fields. Default is " : ".
|
201
|
+
# * <tt>:datetime_separator</tt>- Specifies a string to separate the date and time fields. Default is " — ".
|
200
202
|
# * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Date.today.year - 5</tt> if
|
201
203
|
# you are creating new record. While editing existing record, <tt>:start_year</tt> defaults to
|
202
204
|
# the current selected year minus 5.
|
203
205
|
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Date.today.year + 5</tt> if
|
204
206
|
# you are creating new record. While editing existing record, <tt>:end_year</tt> defaults to
|
205
207
|
# the current selected year plus 5.
|
208
|
+
# * <tt>:year_format</tt> - Set format of years for year select. Lambda should be passed.
|
206
209
|
# * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
|
207
210
|
# as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
|
208
211
|
# first of the given month in order to not create invalid dates like 31 February.
|
@@ -273,6 +276,9 @@ module ActionView
|
|
273
276
|
# # Generates a date select with custom prompts.
|
274
277
|
# date_select("article", "written_on", prompt: { day: 'Select day', month: 'Select month', year: 'Select year' })
|
275
278
|
#
|
279
|
+
# # Generates a date select with custom year format.
|
280
|
+
# date_select("article", "written_on", year_format: ->(year) { "Heisei #{year - 1988}" })
|
281
|
+
#
|
276
282
|
# The selects are prepared for multi-parameter assignment to an Active Record object.
|
277
283
|
#
|
278
284
|
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
|
@@ -300,15 +306,15 @@ module ActionView
|
|
300
306
|
# time_select("article", "start_time", include_seconds: true)
|
301
307
|
#
|
302
308
|
# # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30, and 45.
|
303
|
-
# time_select 'game', 'game_time', {minute_step: 15}
|
309
|
+
# time_select 'game', 'game_time', { minute_step: 15 }
|
304
310
|
#
|
305
311
|
# # Creates a time select tag with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
|
306
|
-
# time_select("article", "written_on", prompt: {hour: 'Choose hour', minute: 'Choose minute', second: 'Choose seconds'})
|
307
|
-
# time_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
|
312
|
+
# time_select("article", "written_on", prompt: { hour: 'Choose hour', minute: 'Choose minute', second: 'Choose seconds' })
|
313
|
+
# time_select("article", "written_on", prompt: { hour: true }) # generic prompt for hours
|
308
314
|
# time_select("article", "written_on", prompt: true) # generic prompts for all
|
309
315
|
#
|
310
316
|
# # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
|
311
|
-
# time_select 'game', 'game_time', {ampm: true}
|
317
|
+
# time_select 'game', 'game_time', { ampm: true }
|
312
318
|
#
|
313
319
|
# The selects are prepared for multi-parameter assignment to an Active Record object.
|
314
320
|
#
|
@@ -344,8 +350,8 @@ module ActionView
|
|
344
350
|
# datetime_select("article", "written_on", discard_type: true)
|
345
351
|
#
|
346
352
|
# # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
|
347
|
-
# datetime_select("article", "written_on", prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
|
348
|
-
# datetime_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
|
353
|
+
# datetime_select("article", "written_on", prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
|
354
|
+
# datetime_select("article", "written_on", prompt: { hour: true }) # generic prompt for hours
|
349
355
|
# datetime_select("article", "written_on", prompt: true) # generic prompts for all
|
350
356
|
#
|
351
357
|
# The selects are prepared for multi-parameter assignment to an Active Record object.
|
@@ -395,8 +401,8 @@ module ActionView
|
|
395
401
|
# select_datetime(my_date_time, prefix: 'payday')
|
396
402
|
#
|
397
403
|
# # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
|
398
|
-
# select_datetime(my_date_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
|
399
|
-
# select_datetime(my_date_time, prompt: {hour: true}) # generic prompt for hours
|
404
|
+
# select_datetime(my_date_time, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
|
405
|
+
# select_datetime(my_date_time, prompt: { hour: true }) # generic prompt for hours
|
400
406
|
# select_datetime(my_date_time, prompt: true) # generic prompts for all
|
401
407
|
def select_datetime(datetime = Time.current, options = {}, html_options = {})
|
402
408
|
DateTimeSelector.new(datetime, options, html_options).select_datetime
|
@@ -434,8 +440,8 @@ module ActionView
|
|
434
440
|
# select_date(my_date, prefix: 'payday')
|
435
441
|
#
|
436
442
|
# # Generates a date select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
|
437
|
-
# select_date(my_date, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
|
438
|
-
# select_date(my_date, prompt: {hour: true}) # generic prompt for hours
|
443
|
+
# select_date(my_date, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
|
444
|
+
# select_date(my_date, prompt: { hour: true }) # generic prompt for hours
|
439
445
|
# select_date(my_date, prompt: true) # generic prompts for all
|
440
446
|
def select_date(date = Date.current, options = {}, html_options = {})
|
441
447
|
DateTimeSelector.new(date, options, html_options).select_date
|
@@ -474,8 +480,8 @@ module ActionView
|
|
474
480
|
# select_time(my_time, start_hour: 2, end_hour: 14)
|
475
481
|
#
|
476
482
|
# # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
|
477
|
-
# select_time(my_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
|
478
|
-
# select_time(my_time, prompt: {hour: true}) # generic prompt for hours
|
483
|
+
# select_time(my_time, prompt: { day: 'Choose day', month: 'Choose month', year: 'Choose year' })
|
484
|
+
# select_time(my_time, prompt: { hour: true }) # generic prompt for hours
|
479
485
|
# select_time(my_time, prompt: true) # generic prompts for all
|
480
486
|
def select_time(datetime = Time.current, options = {}, html_options = {})
|
481
487
|
DateTimeSelector.new(datetime, options, html_options).select_time
|
@@ -666,8 +672,6 @@ module ActionView
|
|
666
672
|
# <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
|
667
673
|
# time_tag Date.yesterday, 'Yesterday' # =>
|
668
674
|
# <time datetime="2010-11-03">Yesterday</time>
|
669
|
-
# time_tag Date.today, pubdate: true # =>
|
670
|
-
# <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
|
671
675
|
# time_tag Date.today, datetime: Date.today.strftime('%G-W%V') # =>
|
672
676
|
# <time datetime="2010-W44">November 04, 2010</time>
|
673
677
|
#
|
@@ -679,13 +683,11 @@ module ActionView
|
|
679
683
|
options = args.extract_options!
|
680
684
|
format = options.delete(:format) || :long
|
681
685
|
content = args.first || I18n.l(date_or_time, format: format)
|
682
|
-
datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.iso8601
|
683
686
|
|
684
|
-
content_tag("time"
|
687
|
+
content_tag("time", content, options.reverse_merge(datetime: date_or_time.iso8601), &block)
|
685
688
|
end
|
686
689
|
|
687
690
|
private
|
688
|
-
|
689
691
|
def normalize_distance_of_time_argument_to_time(value)
|
690
692
|
if value.is_a?(Numeric)
|
691
693
|
Time.at(value)
|
@@ -700,7 +702,7 @@ module ActionView
|
|
700
702
|
class DateTimeSelector #:nodoc:
|
701
703
|
include ActionView::Helpers::TagHelper
|
702
704
|
|
703
|
-
DEFAULT_PREFIX = "date"
|
705
|
+
DEFAULT_PREFIX = "date"
|
704
706
|
POSITION = {
|
705
707
|
year: 1, month: 2, day: 3, hour: 4, minute: 5, second: 6
|
706
708
|
}.freeze
|
@@ -821,14 +823,14 @@ module ActionView
|
|
821
823
|
1.upto(12) do |month_number|
|
822
824
|
options = { value: month_number }
|
823
825
|
options[:selected] = "selected" if month == month_number
|
824
|
-
month_options << content_tag("option"
|
826
|
+
month_options << content_tag("option", month_name(month_number), options) + "\n"
|
825
827
|
end
|
826
828
|
build_select(:month, month_options.join)
|
827
829
|
end
|
828
830
|
end
|
829
831
|
|
830
832
|
def select_year
|
831
|
-
if
|
833
|
+
if !year || @datetime == 0
|
832
834
|
val = "1"
|
833
835
|
middle_year = Date.today.year
|
834
836
|
else
|
@@ -849,7 +851,7 @@ module ActionView
|
|
849
851
|
raise ArgumentError, "There are too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter."
|
850
852
|
end
|
851
853
|
|
852
|
-
|
854
|
+
build_select(:year, build_year_options(val, options))
|
853
855
|
end
|
854
856
|
end
|
855
857
|
|
@@ -932,6 +934,21 @@ module ActionView
|
|
932
934
|
end
|
933
935
|
end
|
934
936
|
|
937
|
+
# Looks up year names by number.
|
938
|
+
#
|
939
|
+
# year_name(1998) # => 1998
|
940
|
+
#
|
941
|
+
# If the <tt>:year_format</tt> option is passed:
|
942
|
+
#
|
943
|
+
# year_name(1998) # => "Heisei 10"
|
944
|
+
def year_name(number)
|
945
|
+
if year_format_lambda = @options[:year_format]
|
946
|
+
year_format_lambda.call(number)
|
947
|
+
else
|
948
|
+
number
|
949
|
+
end
|
950
|
+
end
|
951
|
+
|
935
952
|
def date_order
|
936
953
|
@date_order ||= @options[:order] || translated_date_order
|
937
954
|
end
|
@@ -988,7 +1005,35 @@ module ActionView
|
|
988
1005
|
tag_options[:selected] = "selected" if selected == i
|
989
1006
|
text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
|
990
1007
|
text = options[:ampm] ? AMPM_TRANSLATION[i] : text
|
991
|
-
select_options << content_tag("option"
|
1008
|
+
select_options << content_tag("option", text, tag_options)
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
(select_options.join("\n") + "\n").html_safe
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
# Build select option HTML for year.
|
1015
|
+
# If <tt>year_format</tt> option is not passed
|
1016
|
+
# build_year_options(1998, start: 1998, end: 2000)
|
1017
|
+
# => "<option value="1998" selected="selected">1998</option>
|
1018
|
+
# <option value="1999">1999</option>
|
1019
|
+
# <option value="2000">2000</option>"
|
1020
|
+
#
|
1021
|
+
# If <tt>year_format</tt> option is passed
|
1022
|
+
# build_year_options(1998, start: 1998, end: 2000, year_format: ->year { "Heisei #{ year - 1988 }" })
|
1023
|
+
# => "<option value="1998" selected="selected">Heisei 10</option>
|
1024
|
+
# <option value="1999">Heisei 11</option>
|
1025
|
+
# <option value="2000">Heisei 12</option>"
|
1026
|
+
def build_year_options(selected, options = {})
|
1027
|
+
start = options.delete(:start)
|
1028
|
+
stop = options.delete(:end)
|
1029
|
+
step = options.delete(:step)
|
1030
|
+
|
1031
|
+
select_options = []
|
1032
|
+
start.step(stop, step) do |value|
|
1033
|
+
tag_options = { value: value }
|
1034
|
+
tag_options[:selected] = "selected" if selected == value
|
1035
|
+
text = year_name(value)
|
1036
|
+
select_options << content_tag("option", text, tag_options)
|
992
1037
|
end
|
993
1038
|
|
994
1039
|
(select_options.join("\n") + "\n").html_safe
|
@@ -1007,12 +1052,12 @@ module ActionView
|
|
1007
1052
|
select_options[:disabled] = "disabled" if @options[:disabled]
|
1008
1053
|
select_options[:class] = css_class_attribute(type, select_options[:class], @options[:with_css_classes]) if @options[:with_css_classes]
|
1009
1054
|
|
1010
|
-
select_html = "\n"
|
1011
|
-
select_html << content_tag("option"
|
1055
|
+
select_html = +"\n"
|
1056
|
+
select_html << content_tag("option", "", value: "", label: " ") + "\n" if @options[:include_blank]
|
1012
1057
|
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
|
1013
1058
|
select_html << select_options_as_html
|
1014
1059
|
|
1015
|
-
(content_tag("select"
|
1060
|
+
(content_tag("select", select_html.html_safe, select_options) + "\n").html_safe
|
1016
1061
|
end
|
1017
1062
|
|
1018
1063
|
# Builds the css class value for the select element
|
@@ -1045,7 +1090,7 @@ module ActionView
|
|
1045
1090
|
I18n.translate(:"datetime.prompts.#{type}", locale: @options[:locale])
|
1046
1091
|
end
|
1047
1092
|
|
1048
|
-
prompt ? content_tag("option"
|
1093
|
+
prompt ? content_tag("option", prompt, value: "") : ""
|
1049
1094
|
end
|
1050
1095
|
|
1051
1096
|
# Builds hidden input tag for date part and value.
|
@@ -1089,11 +1134,11 @@ module ActionView
|
|
1089
1134
|
# Given an ordering of datetime components, create the selection HTML
|
1090
1135
|
# and join them with their appropriate separators.
|
1091
1136
|
def build_selects_from_types(order)
|
1092
|
-
select = ""
|
1137
|
+
select = +""
|
1093
1138
|
first_visible = order.find { |type| !@options[:"discard_#{type}"] }
|
1094
1139
|
order.reverse_each do |type|
|
1095
1140
|
separator = separator(type) unless type == first_visible # don't add before first visible field
|
1096
|
-
select.insert(0, separator.to_s +
|
1141
|
+
select.insert(0, separator.to_s + public_send("select_#{type}").to_s)
|
1097
1142
|
end
|
1098
1143
|
select.html_safe
|
1099
1144
|
end
|
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView
|
2
4
|
# = Action View Debug Helper
|
3
5
|
#
|
4
6
|
# Provides a set of methods for making it easier to debug Rails objects.
|
5
|
-
module Helpers
|
7
|
+
module Helpers #:nodoc:
|
6
8
|
module DebugHelper
|
7
9
|
include TagHelper
|
8
10
|
|
@@ -22,7 +24,7 @@ module ActionView
|
|
22
24
|
# created_at:
|
23
25
|
# </pre>
|
24
26
|
def debug(object)
|
25
|
-
Marshal
|
27
|
+
Marshal.dump(object)
|
26
28
|
object = ERB::Util.html_escape(object.to_yaml)
|
27
29
|
content_tag(:pre, object, class: "debug_dump")
|
28
30
|
rescue # errors from Marshal or YAML
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "cgi"
|
2
4
|
require "action_view/helpers/date_helper"
|
3
5
|
require "action_view/helpers/tag_helper"
|
@@ -9,15 +11,16 @@ require "active_support/core_ext/module/attribute_accessors"
|
|
9
11
|
require "active_support/core_ext/hash/slice"
|
10
12
|
require "active_support/core_ext/string/output_safety"
|
11
13
|
require "active_support/core_ext/string/inflections"
|
14
|
+
require "active_support/core_ext/symbol/starts_ends_with"
|
12
15
|
|
13
16
|
module ActionView
|
14
17
|
# = Action View Form Helpers
|
15
|
-
module Helpers
|
18
|
+
module Helpers #:nodoc:
|
16
19
|
# Form helpers are designed to make working with resources much easier
|
17
20
|
# compared to using vanilla HTML.
|
18
21
|
#
|
19
22
|
# Typically, a form designed to create or update a resource reflects the
|
20
|
-
# identity of the resource in several ways: (i) the
|
23
|
+
# identity of the resource in several ways: (i) the URL that the form is
|
21
24
|
# sent to (the form element's +action+ attribute) should result in a request
|
22
25
|
# being routed to the appropriate controller action (with the appropriate <tt>:id</tt>
|
23
26
|
# parameter in the case of an existing resource), (ii) input fields should
|
@@ -164,7 +167,7 @@ module ActionView
|
|
164
167
|
# So for example you may use a named route directly. When the model is
|
165
168
|
# represented by a string or symbol, as in the example above, if the
|
166
169
|
# <tt>:url</tt> option is not specified, by default the form will be
|
167
|
-
# sent back to the current
|
170
|
+
# sent back to the current URL (We will describe below an alternative
|
168
171
|
# resource-oriented usage of +form_for+ in which the URL does not need
|
169
172
|
# to be specified explicitly).
|
170
173
|
# * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
|
@@ -183,8 +186,7 @@ module ActionView
|
|
183
186
|
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
184
187
|
# unnecessary unless you support browsers without JavaScript.
|
185
188
|
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive
|
186
|
-
# JavaScript drivers to control the submit behavior.
|
187
|
-
# behavior is an ajax submit.
|
189
|
+
# JavaScript drivers to control the submit behavior.
|
188
190
|
# * <tt>:enforce_utf8</tt> - If set to false, a hidden input with name
|
189
191
|
# utf8 is not output.
|
190
192
|
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
|
@@ -201,9 +203,9 @@ module ActionView
|
|
201
203
|
# <%= f.submit %>
|
202
204
|
# <% end %>
|
203
205
|
#
|
204
|
-
# This also works for the methods in
|
206
|
+
# This also works for the methods in FormOptionsHelper and DateHelper that
|
205
207
|
# are designed to work with an object as base, like
|
206
|
-
#
|
208
|
+
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
207
209
|
#
|
208
210
|
# === #form_for with a model object
|
209
211
|
#
|
@@ -320,10 +322,8 @@ module ActionView
|
|
320
322
|
# remote: true
|
321
323
|
#
|
322
324
|
# in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
|
323
|
-
# behavior. The
|
324
|
-
#
|
325
|
-
# Even though it's using JavaScript to serialize the form elements, the form submission will work just like
|
326
|
-
# a regular submission as viewed by the receiving side (all elements available in <tt>params</tt>).
|
325
|
+
# behavior. The form submission will work just like a regular submission as viewed by the receiving
|
326
|
+
# side (all elements available in <tt>params</tt>).
|
327
327
|
#
|
328
328
|
# Example:
|
329
329
|
#
|
@@ -416,13 +416,13 @@ module ActionView
|
|
416
416
|
#
|
417
417
|
# To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
|
418
418
|
#
|
419
|
-
# <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
|
419
|
+
# <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f| %>
|
420
420
|
# ...
|
421
421
|
# <% end %>
|
422
422
|
#
|
423
423
|
# If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
|
424
424
|
#
|
425
|
-
# <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
|
425
|
+
# <%= form_for @invoice, url: external_url, authenticity_token: false do |f| %>
|
426
426
|
# ...
|
427
427
|
# <% end %>
|
428
428
|
def form_for(record, options = {}, &block)
|
@@ -474,7 +474,9 @@ module ActionView
|
|
474
474
|
end
|
475
475
|
private :apply_form_for_options!
|
476
476
|
|
477
|
-
mattr_accessor
|
477
|
+
mattr_accessor :form_with_generates_remote_forms, default: true
|
478
|
+
|
479
|
+
mattr_accessor :form_with_generates_ids, default: false
|
478
480
|
|
479
481
|
# Creates a form tag based on mixing URLs, scopes, or models.
|
480
482
|
#
|
@@ -531,11 +533,6 @@ module ActionView
|
|
531
533
|
# accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
|
532
534
|
# respectively.
|
533
535
|
#
|
534
|
-
# By default +form_with+ attaches the <tt>data-remote</tt> attribute
|
535
|
-
# submitting the form via an XMLHTTPRequest in the background if an
|
536
|
-
# Unobtrusive JavaScript driver, like rails-ujs, is used. See the
|
537
|
-
# <tt>:local</tt> option for more.
|
538
|
-
#
|
539
536
|
# For ease of comparison the examples above left out the submit button,
|
540
537
|
# as well as the auto generated hidden fields that enable UTF-8 support
|
541
538
|
# and adds an authenticity token needed for cross site request forgery
|
@@ -586,6 +583,9 @@ module ActionView
|
|
586
583
|
# Skipped if a <tt>:url</tt> is passed.
|
587
584
|
# * <tt>:scope</tt> - The scope to prefix input field names with and
|
588
585
|
# thereby how the submitted parameters are grouped in controllers.
|
586
|
+
# * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
|
587
|
+
# id attributes on form elements. The namespace attribute will be prefixed
|
588
|
+
# with underscore on the generated HTML id.
|
589
589
|
# * <tt>:model</tt> - A model object to infer the <tt>:url</tt> and
|
590
590
|
# <tt>:scope</tt> by, plus fill out input field values.
|
591
591
|
# So if a +title+ attribute is set to "Ahoy!" then a +title+ input
|
@@ -604,10 +604,12 @@ module ActionView
|
|
604
604
|
# This is helpful when fragment-caching the form. Remote forms
|
605
605
|
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
606
606
|
# unnecessary unless you support browsers without JavaScript.
|
607
|
-
# * <tt>:local</tt> - By default form submits
|
608
|
-
#
|
609
|
-
#
|
610
|
-
#
|
607
|
+
# * <tt>:local</tt> - By default form submits via typical HTTP requests.
|
608
|
+
# Enable remote and unobtrusive XHRs submits with <tt>local: false</tt>.
|
609
|
+
# Remote forms may be enabled by default by setting
|
610
|
+
# <tt>config.action_view.form_with_generates_remote_forms = true</tt>.
|
611
|
+
# * <tt>:skip_enforcing_utf8</tt> - If set to true, a hidden input with name
|
612
|
+
# utf8 is not output.
|
611
613
|
# * <tt>:builder</tt> - Override the object used to build the form.
|
612
614
|
# * <tt>:id</tt> - Optional HTML id attribute.
|
613
615
|
# * <tt>:class</tt> - Optional HTML class attribute.
|
@@ -638,16 +640,6 @@ module ActionView
|
|
638
640
|
#
|
639
641
|
# Where <tt>@document = Document.find(params[:id])</tt>.
|
640
642
|
#
|
641
|
-
# When using labels +form_with+ requires setting the id on the field being
|
642
|
-
# labelled:
|
643
|
-
#
|
644
|
-
# <%= form_with(model: @post) do |form| %>
|
645
|
-
# <%= form.label :title %>
|
646
|
-
# <%= form.text_field :title, id: :post_title %>
|
647
|
-
# <% end %>
|
648
|
-
#
|
649
|
-
# See +label+ for more on how the +for+ attribute is derived.
|
650
|
-
#
|
651
643
|
# === Mixing with other form helpers
|
652
644
|
#
|
653
645
|
# While +form_with+ uses a FormBuilder object it's possible to mix and
|
@@ -664,9 +656,9 @@ module ActionView
|
|
664
656
|
# <%= form.submit %>
|
665
657
|
# <% end %>
|
666
658
|
#
|
667
|
-
# Same goes for the methods in
|
659
|
+
# Same goes for the methods in FormOptionsHelper and DateHelper designed
|
668
660
|
# to work with an object as a base, like
|
669
|
-
#
|
661
|
+
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
670
662
|
#
|
671
663
|
# === Setting the method
|
672
664
|
#
|
@@ -742,9 +734,9 @@ module ActionView
|
|
742
734
|
# def labelled_form_with(**options, &block)
|
743
735
|
# form_with(**options.merge(builder: LabellingFormBuilder), &block)
|
744
736
|
# end
|
745
|
-
def form_with(model: nil, scope: nil, url: nil, format: nil, **options)
|
737
|
+
def form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
|
746
738
|
options[:allow_method_names_outside_object] = true
|
747
|
-
options[:skip_default_ids] =
|
739
|
+
options[:skip_default_ids] = !form_with_generates_ids
|
748
740
|
|
749
741
|
if model
|
750
742
|
url ||= polymorphic_path(model, format: format)
|
@@ -755,13 +747,13 @@ module ActionView
|
|
755
747
|
|
756
748
|
if block_given?
|
757
749
|
builder = instantiate_builder(scope, model, options)
|
758
|
-
output = capture(builder, &
|
750
|
+
output = capture(builder, &block)
|
759
751
|
options[:multipart] ||= builder.multipart?
|
760
752
|
|
761
|
-
html_options = html_options_for_form_with(url, model, options)
|
753
|
+
html_options = html_options_for_form_with(url, model, **options)
|
762
754
|
form_tag_with_body(html_options, output)
|
763
755
|
else
|
764
|
-
html_options = html_options_for_form_with(url, model, options)
|
756
|
+
html_options = html_options_for_form_with(url, model, **options)
|
765
757
|
form_tag_html(html_options)
|
766
758
|
end
|
767
759
|
end
|
@@ -823,9 +815,9 @@ module ActionView
|
|
823
815
|
# _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
|
824
816
|
# of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
|
825
817
|
#
|
826
|
-
# Note: This also works for the methods in
|
818
|
+
# Note: This also works for the methods in FormOptionsHelper and
|
827
819
|
# DateHelper that are designed to work with an object as base, like
|
828
|
-
#
|
820
|
+
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
829
821
|
#
|
830
822
|
# === Nested Attributes Examples
|
831
823
|
#
|
@@ -891,7 +883,7 @@ module ActionView
|
|
891
883
|
#
|
892
884
|
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
|
893
885
|
# with a value that evaluates to +true+, you will destroy the associated
|
894
|
-
# model (
|
886
|
+
# model (e.g. 1, '1', true, or 'true'):
|
895
887
|
#
|
896
888
|
# <%= form_for @person do |person_form| %>
|
897
889
|
# ...
|
@@ -980,7 +972,7 @@ module ActionView
|
|
980
972
|
# This will allow you to specify which models to destroy in the
|
981
973
|
# attributes hash by adding a form element for the <tt>_destroy</tt>
|
982
974
|
# parameter with a value that evaluates to +true+
|
983
|
-
# (
|
975
|
+
# (e.g. 1, '1', true, or 'true'):
|
984
976
|
#
|
985
977
|
# <%= form_for @person do |person_form| %>
|
986
978
|
# ...
|
@@ -1020,14 +1012,13 @@ module ActionView
|
|
1020
1012
|
# <%= fields :comment do |fields| %>
|
1021
1013
|
# <%= fields.text_field :body %>
|
1022
1014
|
# <% end %>
|
1023
|
-
# # => <input type="text" name="comment[body]>
|
1015
|
+
# # => <input type="text" name="comment[body]">
|
1024
1016
|
#
|
1025
1017
|
# # Using a model infers the scope and assigns field values:
|
1026
|
-
# <%= fields model: Comment.new(body: "full bodied") do |fields|
|
1018
|
+
# <%= fields model: Comment.new(body: "full bodied") do |fields| %>
|
1027
1019
|
# <%= fields.text_field :body %>
|
1028
1020
|
# <% end %>
|
1029
|
-
# # =>
|
1030
|
-
# <input type="text" name="comment[body] value="full bodied">
|
1021
|
+
# # => <input type="text" name="comment[body]" value="full bodied">
|
1031
1022
|
#
|
1032
1023
|
# # Using +fields+ with +form_with+:
|
1033
1024
|
# <%= form_with model: @post do |form| %>
|
@@ -1042,16 +1033,6 @@ module ActionView
|
|
1042
1033
|
# or model is yielded, so any generated field names are prefixed with
|
1043
1034
|
# either the passed scope or the scope inferred from the <tt>:model</tt>.
|
1044
1035
|
#
|
1045
|
-
# When using labels +fields+ requires setting the id on the field being
|
1046
|
-
# labelled:
|
1047
|
-
#
|
1048
|
-
# <%= fields :comment do |fields| %>
|
1049
|
-
# <%= fields.label :body %>
|
1050
|
-
# <%= fields.text_field :body, id: :comment_body %>
|
1051
|
-
# <% end %>
|
1052
|
-
#
|
1053
|
-
# See +label+ for more on how the +for+ attribute is derived.
|
1054
|
-
#
|
1055
1036
|
# === Mixing with other form helpers
|
1056
1037
|
#
|
1057
1038
|
# While +form_with+ uses a FormBuilder object it's possible to mix and
|
@@ -1065,12 +1046,12 @@ module ActionView
|
|
1065
1046
|
# <%= check_box_tag "comment[all_caps]", "1", @comment.commenter.hulk_mode? %>
|
1066
1047
|
# <% end %>
|
1067
1048
|
#
|
1068
|
-
# Same goes for the methods in
|
1049
|
+
# Same goes for the methods in FormOptionsHelper and DateHelper designed
|
1069
1050
|
# to work with an object as a base, like
|
1070
|
-
#
|
1051
|
+
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
1071
1052
|
def fields(scope = nil, model: nil, **options, &block)
|
1072
1053
|
options[:allow_method_names_outside_object] = true
|
1073
|
-
options[:skip_default_ids] =
|
1054
|
+
options[:skip_default_ids] = !form_with_generates_ids
|
1074
1055
|
|
1075
1056
|
if model
|
1076
1057
|
scope ||= model_name_from_record_or_class(model).param_key
|
@@ -1124,6 +1105,16 @@ module ActionView
|
|
1124
1105
|
# label(:post, :privacy, "Public Post", value: "public")
|
1125
1106
|
# # => <label for="post_privacy_public">Public Post</label>
|
1126
1107
|
#
|
1108
|
+
# label(:post, :cost) do |translation|
|
1109
|
+
# content_tag(:span, translation, class: "cost_label")
|
1110
|
+
# end
|
1111
|
+
# # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
|
1112
|
+
#
|
1113
|
+
# label(:post, :cost) do |builder|
|
1114
|
+
# content_tag(:span, builder.translation, class: "cost_label")
|
1115
|
+
# end
|
1116
|
+
# # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
|
1117
|
+
#
|
1127
1118
|
# label(:post, :terms) do
|
1128
1119
|
# raw('Accept <a href="/terms">Terms</a>.')
|
1129
1120
|
# end
|
@@ -1144,6 +1135,9 @@ module ActionView
|
|
1144
1135
|
# text_field(:post, :title, class: "create_input")
|
1145
1136
|
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
|
1146
1137
|
#
|
1138
|
+
# text_field(:post, :title, maxlength: 30, class: "title_input")
|
1139
|
+
# # => <input type="text" id="post_title" name="post[title]" maxlength="30" size="30" value="#{@post.title}" class="title_input" />
|
1140
|
+
#
|
1147
1141
|
# text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }")
|
1148
1142
|
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }"/>
|
1149
1143
|
#
|
@@ -1221,7 +1215,7 @@ module ActionView
|
|
1221
1215
|
# file_field(:attachment, :file, class: 'file_input')
|
1222
1216
|
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
|
1223
1217
|
def file_field(object_name, method, options = {})
|
1224
|
-
Tags::FileField.new(object_name, method, self, options).render
|
1218
|
+
Tags::FileField.new(object_name, method, self, convert_direct_upload_option_to_url(options.dup)).render
|
1225
1219
|
end
|
1226
1220
|
|
1227
1221
|
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
|
@@ -1536,10 +1530,10 @@ module ActionView
|
|
1536
1530
|
|
1537
1531
|
private
|
1538
1532
|
def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: !form_with_generates_remote_forms,
|
1539
|
-
skip_enforcing_utf8:
|
1533
|
+
skip_enforcing_utf8: nil, **options)
|
1540
1534
|
html_options = options.slice(:id, :class, :multipart, :method, :data).merge(html)
|
1541
1535
|
html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
|
1542
|
-
html_options[:enforce_utf8] = !skip_enforcing_utf8
|
1536
|
+
html_options[:enforce_utf8] = !skip_enforcing_utf8 unless skip_enforcing_utf8.nil?
|
1543
1537
|
|
1544
1538
|
html_options[:enctype] = "multipart/form-data" if html_options.delete(:multipart)
|
1545
1539
|
|
@@ -1597,7 +1591,7 @@ module ActionView
|
|
1597
1591
|
# In the above block, a +FormBuilder+ object is yielded as the
|
1598
1592
|
# +person_form+ variable. This allows you to generate the +text_field+
|
1599
1593
|
# and +check_box+ fields by specifying their eponymous methods, which
|
1600
|
-
# modify the underlying template and associates the
|
1594
|
+
# modify the underlying template and associates the <tt>@person</tt> model object
|
1601
1595
|
# with the form.
|
1602
1596
|
#
|
1603
1597
|
# The +FormBuilder+ object can be thought of as serving as a proxy for the
|
@@ -1636,14 +1630,15 @@ module ActionView
|
|
1636
1630
|
include ModelNaming
|
1637
1631
|
|
1638
1632
|
# The methods which wrap a form helper call.
|
1639
|
-
class_attribute :field_helpers
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1633
|
+
class_attribute :field_helpers, default: [
|
1634
|
+
:fields_for, :fields, :label, :text_field, :password_field,
|
1635
|
+
:hidden_field, :file_field, :text_area, :check_box,
|
1636
|
+
:radio_button, :color_field, :search_field,
|
1637
|
+
:telephone_field, :phone_field, :date_field,
|
1638
|
+
:time_field, :datetime_field, :datetime_local_field,
|
1639
|
+
:month_field, :week_field, :url_field, :email_field,
|
1640
|
+
:number_field, :range_field
|
1641
|
+
]
|
1647
1642
|
|
1648
1643
|
attr_accessor :object_name, :object, :options
|
1649
1644
|
|
@@ -1674,11 +1669,12 @@ module ActionView
|
|
1674
1669
|
@nested_child_index = {}
|
1675
1670
|
@object_name, @object, @template, @options = object_name, object, template, options
|
1676
1671
|
@default_options = @options ? @options.slice(:index, :namespace, :skip_default_ids, :allow_method_names_outside_object) : {}
|
1672
|
+
@default_html_options = @default_options.except(:skip_default_ids, :allow_method_names_outside_object)
|
1677
1673
|
|
1678
1674
|
convert_to_legacy_options(@options)
|
1679
1675
|
|
1680
|
-
if @object_name
|
1681
|
-
if (object ||= @template.instance_variable_get("@#{
|
1676
|
+
if @object_name&.end_with?("[]")
|
1677
|
+
if (object ||= @template.instance_variable_get("@#{@object_name[0..-3]}")) && object.respond_to?(:to_param)
|
1682
1678
|
@auto_index = object.to_param
|
1683
1679
|
else
|
1684
1680
|
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
|
@@ -1689,11 +1685,232 @@ module ActionView
|
|
1689
1685
|
@index = options[:index] || options[:child_index]
|
1690
1686
|
end
|
1691
1687
|
|
1688
|
+
##
|
1689
|
+
# :method: text_field
|
1690
|
+
#
|
1691
|
+
# :call-seq: text_field(method, options = {})
|
1692
|
+
#
|
1693
|
+
# Wraps ActionView::Helpers::FormHelper#text_field for form builders:
|
1694
|
+
#
|
1695
|
+
# <%= form_with model: @user do |f| %>
|
1696
|
+
# <%= f.text_field :name %>
|
1697
|
+
# <% end %>
|
1698
|
+
#
|
1699
|
+
# Please refer to the documentation of the base helper for details.
|
1700
|
+
|
1701
|
+
##
|
1702
|
+
# :method: password_field
|
1703
|
+
#
|
1704
|
+
# :call-seq: password_field(method, options = {})
|
1705
|
+
#
|
1706
|
+
# Wraps ActionView::Helpers::FormHelper#password_field for form builders:
|
1707
|
+
#
|
1708
|
+
# <%= form_with model: @user do |f| %>
|
1709
|
+
# <%= f.password_field :password %>
|
1710
|
+
# <% end %>
|
1711
|
+
#
|
1712
|
+
# Please refer to the documentation of the base helper for details.
|
1713
|
+
|
1714
|
+
##
|
1715
|
+
# :method: text_area
|
1716
|
+
#
|
1717
|
+
# :call-seq: text_area(method, options = {})
|
1718
|
+
#
|
1719
|
+
# Wraps ActionView::Helpers::FormHelper#text_area for form builders:
|
1720
|
+
#
|
1721
|
+
# <%= form_with model: @user do |f| %>
|
1722
|
+
# <%= f.text_area :detail %>
|
1723
|
+
# <% end %>
|
1724
|
+
#
|
1725
|
+
# Please refer to the documentation of the base helper for details.
|
1726
|
+
|
1727
|
+
##
|
1728
|
+
# :method: color_field
|
1729
|
+
#
|
1730
|
+
# :call-seq: color_field(method, options = {})
|
1731
|
+
#
|
1732
|
+
# Wraps ActionView::Helpers::FormHelper#color_field for form builders:
|
1733
|
+
#
|
1734
|
+
# <%= form_with model: @user do |f| %>
|
1735
|
+
# <%= f.color_field :favorite_color %>
|
1736
|
+
# <% end %>
|
1737
|
+
#
|
1738
|
+
# Please refer to the documentation of the base helper for details.
|
1739
|
+
|
1740
|
+
##
|
1741
|
+
# :method: search_field
|
1742
|
+
#
|
1743
|
+
# :call-seq: search_field(method, options = {})
|
1744
|
+
#
|
1745
|
+
# Wraps ActionView::Helpers::FormHelper#search_field for form builders:
|
1746
|
+
#
|
1747
|
+
# <%= form_with model: @user do |f| %>
|
1748
|
+
# <%= f.search_field :name %>
|
1749
|
+
# <% end %>
|
1750
|
+
#
|
1751
|
+
# Please refer to the documentation of the base helper for details.
|
1752
|
+
|
1753
|
+
##
|
1754
|
+
# :method: telephone_field
|
1755
|
+
#
|
1756
|
+
# :call-seq: telephone_field(method, options = {})
|
1757
|
+
#
|
1758
|
+
# Wraps ActionView::Helpers::FormHelper#telephone_field for form builders:
|
1759
|
+
#
|
1760
|
+
# <%= form_with model: @user do |f| %>
|
1761
|
+
# <%= f.telephone_field :phone %>
|
1762
|
+
# <% end %>
|
1763
|
+
#
|
1764
|
+
# Please refer to the documentation of the base helper for details.
|
1765
|
+
|
1766
|
+
##
|
1767
|
+
# :method: phone_field
|
1768
|
+
#
|
1769
|
+
# :call-seq: phone_field(method, options = {})
|
1770
|
+
#
|
1771
|
+
# Wraps ActionView::Helpers::FormHelper#phone_field for form builders:
|
1772
|
+
#
|
1773
|
+
# <%= form_with model: @user do |f| %>
|
1774
|
+
# <%= f.phone_field :phone %>
|
1775
|
+
# <% end %>
|
1776
|
+
#
|
1777
|
+
# Please refer to the documentation of the base helper for details.
|
1778
|
+
|
1779
|
+
##
|
1780
|
+
# :method: date_field
|
1781
|
+
#
|
1782
|
+
# :call-seq: date_field(method, options = {})
|
1783
|
+
#
|
1784
|
+
# Wraps ActionView::Helpers::FormHelper#date_field for form builders:
|
1785
|
+
#
|
1786
|
+
# <%= form_with model: @user do |f| %>
|
1787
|
+
# <%= f.date_field :born_on %>
|
1788
|
+
# <% end %>
|
1789
|
+
#
|
1790
|
+
# Please refer to the documentation of the base helper for details.
|
1791
|
+
|
1792
|
+
##
|
1793
|
+
# :method: time_field
|
1794
|
+
#
|
1795
|
+
# :call-seq: time_field(method, options = {})
|
1796
|
+
#
|
1797
|
+
# Wraps ActionView::Helpers::FormHelper#time_field for form builders:
|
1798
|
+
#
|
1799
|
+
# <%= form_with model: @user do |f| %>
|
1800
|
+
# <%= f.time_field :born_at %>
|
1801
|
+
# <% end %>
|
1802
|
+
#
|
1803
|
+
# Please refer to the documentation of the base helper for details.
|
1804
|
+
|
1805
|
+
##
|
1806
|
+
# :method: datetime_field
|
1807
|
+
#
|
1808
|
+
# :call-seq: datetime_field(method, options = {})
|
1809
|
+
#
|
1810
|
+
# Wraps ActionView::Helpers::FormHelper#datetime_field for form builders:
|
1811
|
+
#
|
1812
|
+
# <%= form_with model: @user do |f| %>
|
1813
|
+
# <%= f.datetime_field :graduation_day %>
|
1814
|
+
# <% end %>
|
1815
|
+
#
|
1816
|
+
# Please refer to the documentation of the base helper for details.
|
1817
|
+
|
1818
|
+
##
|
1819
|
+
# :method: datetime_local_field
|
1820
|
+
#
|
1821
|
+
# :call-seq: datetime_local_field(method, options = {})
|
1822
|
+
#
|
1823
|
+
# Wraps ActionView::Helpers::FormHelper#datetime_local_field for form builders:
|
1824
|
+
#
|
1825
|
+
# <%= form_with model: @user do |f| %>
|
1826
|
+
# <%= f.datetime_local_field :graduation_day %>
|
1827
|
+
# <% end %>
|
1828
|
+
#
|
1829
|
+
# Please refer to the documentation of the base helper for details.
|
1830
|
+
|
1831
|
+
##
|
1832
|
+
# :method: month_field
|
1833
|
+
#
|
1834
|
+
# :call-seq: month_field(method, options = {})
|
1835
|
+
#
|
1836
|
+
# Wraps ActionView::Helpers::FormHelper#month_field for form builders:
|
1837
|
+
#
|
1838
|
+
# <%= form_with model: @user do |f| %>
|
1839
|
+
# <%= f.month_field :birthday_month %>
|
1840
|
+
# <% end %>
|
1841
|
+
#
|
1842
|
+
# Please refer to the documentation of the base helper for details.
|
1843
|
+
|
1844
|
+
##
|
1845
|
+
# :method: week_field
|
1846
|
+
#
|
1847
|
+
# :call-seq: week_field(method, options = {})
|
1848
|
+
#
|
1849
|
+
# Wraps ActionView::Helpers::FormHelper#week_field for form builders:
|
1850
|
+
#
|
1851
|
+
# <%= form_with model: @user do |f| %>
|
1852
|
+
# <%= f.week_field :birthday_week %>
|
1853
|
+
# <% end %>
|
1854
|
+
#
|
1855
|
+
# Please refer to the documentation of the base helper for details.
|
1856
|
+
|
1857
|
+
##
|
1858
|
+
# :method: url_field
|
1859
|
+
#
|
1860
|
+
# :call-seq: url_field(method, options = {})
|
1861
|
+
#
|
1862
|
+
# Wraps ActionView::Helpers::FormHelper#url_field for form builders:
|
1863
|
+
#
|
1864
|
+
# <%= form_with model: @user do |f| %>
|
1865
|
+
# <%= f.url_field :homepage %>
|
1866
|
+
# <% end %>
|
1867
|
+
#
|
1868
|
+
# Please refer to the documentation of the base helper for details.
|
1869
|
+
|
1870
|
+
##
|
1871
|
+
# :method: email_field
|
1872
|
+
#
|
1873
|
+
# :call-seq: email_field(method, options = {})
|
1874
|
+
#
|
1875
|
+
# Wraps ActionView::Helpers::FormHelper#email_field for form builders:
|
1876
|
+
#
|
1877
|
+
# <%= form_with model: @user do |f| %>
|
1878
|
+
# <%= f.email_field :address %>
|
1879
|
+
# <% end %>
|
1880
|
+
#
|
1881
|
+
# Please refer to the documentation of the base helper for details.
|
1882
|
+
|
1883
|
+
##
|
1884
|
+
# :method: number_field
|
1885
|
+
#
|
1886
|
+
# :call-seq: number_field(method, options = {})
|
1887
|
+
#
|
1888
|
+
# Wraps ActionView::Helpers::FormHelper#number_field for form builders:
|
1889
|
+
#
|
1890
|
+
# <%= form_with model: @user do |f| %>
|
1891
|
+
# <%= f.number_field :age %>
|
1892
|
+
# <% end %>
|
1893
|
+
#
|
1894
|
+
# Please refer to the documentation of the base helper for details.
|
1895
|
+
|
1896
|
+
##
|
1897
|
+
# :method: range_field
|
1898
|
+
#
|
1899
|
+
# :call-seq: range_field(method, options = {})
|
1900
|
+
#
|
1901
|
+
# Wraps ActionView::Helpers::FormHelper#range_field for form builders:
|
1902
|
+
#
|
1903
|
+
# <%= form_with model: @user do |f| %>
|
1904
|
+
# <%= f.range_field :age %>
|
1905
|
+
# <% end %>
|
1906
|
+
#
|
1907
|
+
# Please refer to the documentation of the base helper for details.
|
1908
|
+
|
1692
1909
|
(field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
|
1693
1910
|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
1694
1911
|
def #{selector}(method, options = {}) # def text_field(method, options = {})
|
1695
|
-
@template.
|
1696
|
-
#{selector.inspect}, #
|
1912
|
+
@template.public_send( # @template.public_send(
|
1913
|
+
#{selector.inspect}, # :text_field,
|
1697
1914
|
@object_name, # @object_name,
|
1698
1915
|
method, # method,
|
1699
1916
|
objectify_options(options)) # objectify_options(options))
|
@@ -1758,9 +1975,9 @@ module ActionView
|
|
1758
1975
|
# _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
|
1759
1976
|
# of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
|
1760
1977
|
#
|
1761
|
-
# Note: This also works for the methods in
|
1978
|
+
# Note: This also works for the methods in FormOptionsHelper and
|
1762
1979
|
# DateHelper that are designed to work with an object as base, like
|
1763
|
-
#
|
1980
|
+
# FormOptionsHelper#collection_select and DateHelper#datetime_select.
|
1764
1981
|
#
|
1765
1982
|
# === Nested Attributes Examples
|
1766
1983
|
#
|
@@ -1826,7 +2043,7 @@ module ActionView
|
|
1826
2043
|
#
|
1827
2044
|
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
|
1828
2045
|
# with a value that evaluates to +true+, you will destroy the associated
|
1829
|
-
# model (
|
2046
|
+
# model (e.g. 1, '1', true, or 'true'):
|
1830
2047
|
#
|
1831
2048
|
# <%= form_for @person do |person_form| %>
|
1832
2049
|
# ...
|
@@ -1915,7 +2132,7 @@ module ActionView
|
|
1915
2132
|
# This will allow you to specify which models to destroy in the
|
1916
2133
|
# attributes hash by adding a form element for the <tt>_destroy</tt>
|
1917
2134
|
# parameter with a value that evaluates to +true+
|
1918
|
-
# (
|
2135
|
+
# (e.g. 1, '1', true, or 'true'):
|
1919
2136
|
#
|
1920
2137
|
# <%= form_for @person do |person_form| %>
|
1921
2138
|
# ...
|
@@ -1962,15 +2179,14 @@ module ActionView
|
|
1962
2179
|
index = if options.has_key?(:index)
|
1963
2180
|
options[:index]
|
1964
2181
|
elsif defined?(@auto_index)
|
1965
|
-
object_name = object_name.to_s.
|
2182
|
+
object_name = object_name.to_s.delete_suffix("[]")
|
1966
2183
|
@auto_index
|
1967
2184
|
end
|
1968
2185
|
|
1969
2186
|
record_name = if index
|
1970
2187
|
"#{object_name}[#{index}][#{record_name}]"
|
1971
|
-
elsif record_name.
|
1972
|
-
record_name
|
1973
|
-
"#{object_name}#{record_name}"
|
2188
|
+
elsif record_name.end_with?("[]")
|
2189
|
+
"#{object_name}[#{record_name[0..-3]}][#{record_object.id}]"
|
1974
2190
|
else
|
1975
2191
|
"#{object_name}[#{record_name}]"
|
1976
2192
|
end
|
@@ -1982,11 +2198,11 @@ module ActionView
|
|
1982
2198
|
# See the docs for the <tt>ActionView::FormHelper.fields</tt> helper method.
|
1983
2199
|
def fields(scope = nil, model: nil, **options, &block)
|
1984
2200
|
options[:allow_method_names_outside_object] = true
|
1985
|
-
options[:skip_default_ids] =
|
2201
|
+
options[:skip_default_ids] = !FormHelper.form_with_generates_ids
|
1986
2202
|
|
1987
2203
|
convert_to_legacy_options(options)
|
1988
2204
|
|
1989
|
-
fields_for(scope || model, model,
|
2205
|
+
fields_for(scope || model, model, options, &block)
|
1990
2206
|
end
|
1991
2207
|
|
1992
2208
|
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
|
@@ -2033,6 +2249,24 @@ module ActionView
|
|
2033
2249
|
# label(:privacy, "Public Post", value: "public")
|
2034
2250
|
# # => <label for="post_privacy_public">Public Post</label>
|
2035
2251
|
#
|
2252
|
+
# label(:cost) do |translation|
|
2253
|
+
# content_tag(:span, translation, class: "cost_label")
|
2254
|
+
# end
|
2255
|
+
# # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
|
2256
|
+
#
|
2257
|
+
# label(:cost) do |builder|
|
2258
|
+
# content_tag(:span, builder.translation, class: "cost_label")
|
2259
|
+
# end
|
2260
|
+
# # => <label for="post_cost"><span class="cost_label">Total cost</span></label>
|
2261
|
+
#
|
2262
|
+
# label(:cost) do |builder|
|
2263
|
+
# content_tag(:span, builder.translation, class: [
|
2264
|
+
# "cost_label",
|
2265
|
+
# ("error_label" if builder.object.errors.include?(:cost))
|
2266
|
+
# ])
|
2267
|
+
# end
|
2268
|
+
# # => <label for="post_cost"><span class="cost_label error_label">Total cost</span></label>
|
2269
|
+
#
|
2036
2270
|
# label(:terms) do
|
2037
2271
|
# raw('Accept <a href="/terms">Terms</a>.')
|
2038
2272
|
# end
|
@@ -2192,11 +2426,11 @@ module ActionView
|
|
2192
2426
|
# <%= f.submit %>
|
2193
2427
|
# <% end %>
|
2194
2428
|
#
|
2195
|
-
# In the example above, if
|
2196
|
-
# submit button label
|
2429
|
+
# In the example above, if <tt>@post</tt> is a new record, it will use "Create Post" as
|
2430
|
+
# submit button label; otherwise, it uses "Update Post".
|
2197
2431
|
#
|
2198
|
-
# Those labels can be customized using I18n
|
2199
|
-
#
|
2432
|
+
# Those labels can be customized using I18n under the +helpers.submit+ key and using
|
2433
|
+
# <tt>%{model}</tt> for translation interpolation:
|
2200
2434
|
#
|
2201
2435
|
# en:
|
2202
2436
|
# helpers:
|
@@ -2204,7 +2438,7 @@ module ActionView
|
|
2204
2438
|
# create: "Create a %{model}"
|
2205
2439
|
# update: "Confirm changes to %{model}"
|
2206
2440
|
#
|
2207
|
-
# It also searches for a key specific
|
2441
|
+
# It also searches for a key specific to the given object:
|
2208
2442
|
#
|
2209
2443
|
# en:
|
2210
2444
|
# helpers:
|
@@ -2225,11 +2459,11 @@ module ActionView
|
|
2225
2459
|
# <%= f.button %>
|
2226
2460
|
# <% end %>
|
2227
2461
|
#
|
2228
|
-
# In the example above, if
|
2229
|
-
# button label
|
2462
|
+
# In the example above, if <tt>@post</tt> is a new record, it will use "Create Post" as
|
2463
|
+
# button label; otherwise, it uses "Update Post".
|
2230
2464
|
#
|
2231
|
-
# Those labels can be customized using I18n
|
2232
|
-
# (the same as submit helper) and
|
2465
|
+
# Those labels can be customized using I18n under the +helpers.submit+ key
|
2466
|
+
# (the same as submit helper) and using <tt>%{model}</tt> for translation interpolation:
|
2233
2467
|
#
|
2234
2468
|
# en:
|
2235
2469
|
# helpers:
|
@@ -2237,7 +2471,7 @@ module ActionView
|
|
2237
2471
|
# create: "Create a %{model}"
|
2238
2472
|
# update: "Confirm changes to %{model}"
|
2239
2473
|
#
|
2240
|
-
# It also searches for a key specific
|
2474
|
+
# It also searches for a key specific to the given object:
|
2241
2475
|
#
|
2242
2476
|
# en:
|
2243
2477
|
# helpers:
|
@@ -2256,19 +2490,33 @@ module ActionView
|
|
2256
2490
|
# # <strong>Ask me!</strong>
|
2257
2491
|
# # </button>
|
2258
2492
|
#
|
2493
|
+
# button do |text|
|
2494
|
+
# content_tag(:strong, text)
|
2495
|
+
# end
|
2496
|
+
# # => <button name='button' type='submit'>
|
2497
|
+
# # <strong>Create post</strong>
|
2498
|
+
# # </button>
|
2499
|
+
#
|
2259
2500
|
def button(value = nil, options = {}, &block)
|
2260
2501
|
value, options = nil, value if value.is_a?(Hash)
|
2261
2502
|
value ||= submit_default_value
|
2262
|
-
|
2503
|
+
|
2504
|
+
if block_given?
|
2505
|
+
value = @template.capture { yield(value) }
|
2506
|
+
end
|
2507
|
+
|
2508
|
+
@template.button_tag(value, options)
|
2263
2509
|
end
|
2264
2510
|
|
2265
|
-
def emitted_hidden_id?
|
2511
|
+
def emitted_hidden_id? # :nodoc:
|
2266
2512
|
@emitted_hidden_id ||= nil
|
2267
2513
|
end
|
2268
2514
|
|
2269
2515
|
private
|
2270
2516
|
def objectify_options(options)
|
2271
|
-
@default_options.merge(options
|
2517
|
+
result = @default_options.merge(options)
|
2518
|
+
result[:object] = @object
|
2519
|
+
result
|
2272
2520
|
end
|
2273
2521
|
|
2274
2522
|
def submit_default_value
|
@@ -2282,7 +2530,12 @@ module ActionView
|
|
2282
2530
|
end
|
2283
2531
|
|
2284
2532
|
defaults = []
|
2285
|
-
|
2533
|
+
# Object is a model and it is not overwritten by as and scope option.
|
2534
|
+
if object.respond_to?(:model_name) && object_name.to_s == model.downcase
|
2535
|
+
defaults << :"helpers.submit.#{object.model_name.i18n_key}.#{key}"
|
2536
|
+
else
|
2537
|
+
defaults << :"helpers.submit.#{object_name}.#{key}"
|
2538
|
+
end
|
2286
2539
|
defaults << :"helpers.submit.#{key}"
|
2287
2540
|
defaults << "#{key.to_s.humanize} #{model}"
|
2288
2541
|
|
@@ -2298,9 +2551,9 @@ module ActionView
|
|
2298
2551
|
association = convert_to_model(association)
|
2299
2552
|
|
2300
2553
|
if association.respond_to?(:persisted?)
|
2301
|
-
association = [association] if @object.
|
2554
|
+
association = [association] if @object.public_send(association_name).respond_to?(:to_ary)
|
2302
2555
|
elsif !association.respond_to?(:to_ary)
|
2303
|
-
association = @object.
|
2556
|
+
association = @object.public_send(association_name)
|
2304
2557
|
end
|
2305
2558
|
|
2306
2559
|
if association.respond_to?(:to_ary)
|
@@ -2347,8 +2600,6 @@ module ActionView
|
|
2347
2600
|
end
|
2348
2601
|
|
2349
2602
|
ActiveSupport.on_load(:action_view) do
|
2350
|
-
cattr_accessor
|
2351
|
-
::ActionView::Helpers::FormBuilder
|
2352
|
-
end
|
2603
|
+
cattr_accessor :default_form_builder, instance_writer: false, instance_reader: false, default: ::ActionView::Helpers::FormBuilder
|
2353
2604
|
end
|
2354
2605
|
end
|