actionview 5.2.4.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 +4 -4
- data/CHANGELOG.md +221 -93
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -3
- data/lib/action_view.rb +7 -2
- data/lib/action_view/base.rb +81 -15
- data/lib/action_view/buffers.rb +15 -0
- data/lib/action_view/cache_expiry.rb +52 -0
- data/lib/action_view/context.rb +5 -9
- data/lib/action_view/dependency_tracker.rb +10 -4
- data/lib/action_view/digestor.rb +15 -22
- data/lib/action_view/flows.rb +0 -1
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers.rb +0 -2
- data/lib/action_view/helpers/active_model_helper.rb +0 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +63 -46
- data/lib/action_view/helpers/asset_url_helper.rb +9 -6
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -1
- data/lib/action_view/helpers/cache_helper.rb +23 -22
- data/lib/action_view/helpers/capture_helper.rb +4 -0
- data/lib/action_view/helpers/csp_helper.rb +4 -2
- data/lib/action_view/helpers/csrf_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +73 -30
- data/lib/action_view/helpers/form_helper.rb +305 -37
- data/lib/action_view/helpers/form_options_helper.rb +23 -23
- data/lib/action_view/helpers/form_tag_helper.rb +19 -16
- data/lib/action_view/helpers/javascript_helper.rb +12 -11
- data/lib/action_view/helpers/number_helper.rb +14 -8
- data/lib/action_view/helpers/output_safety_helper.rb +1 -1
- data/lib/action_view/helpers/rendering_helper.rb +17 -7
- data/lib/action_view/helpers/sanitize_helper.rb +12 -18
- data/lib/action_view/helpers/tag_helper.rb +98 -22
- data/lib/action_view/helpers/tags/base.rb +18 -11
- data/lib/action_view/helpers/tags/check_box.rb +0 -1
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
- data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
- data/lib/action_view/helpers/tags/color_field.rb +1 -2
- data/lib/action_view/helpers/tags/date_field.rb +1 -2
- data/lib/action_view/helpers/tags/date_select.rb +2 -3
- data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
- data/lib/action_view/helpers/tags/datetime_local_field.rb +1 -2
- data/lib/action_view/helpers/tags/label.rb +4 -1
- data/lib/action_view/helpers/tags/month_field.rb +1 -2
- data/lib/action_view/helpers/tags/radio_button.rb +0 -1
- data/lib/action_view/helpers/tags/select.rb +1 -2
- data/lib/action_view/helpers/tags/text_field.rb +0 -1
- data/lib/action_view/helpers/tags/time_field.rb +1 -2
- data/lib/action_view/helpers/tags/translator.rb +1 -6
- data/lib/action_view/helpers/tags/week_field.rb +1 -2
- data/lib/action_view/helpers/text_helper.rb +3 -4
- data/lib/action_view/helpers/translation_helper.rb +93 -55
- data/lib/action_view/helpers/url_helper.rb +121 -27
- data/lib/action_view/layouts.rb +8 -10
- data/lib/action_view/log_subscriber.rb +30 -15
- data/lib/action_view/lookup_context.rb +63 -35
- data/lib/action_view/path_set.rb +3 -12
- data/lib/action_view/railtie.rb +42 -26
- data/lib/action_view/record_identifier.rb +2 -3
- data/lib/action_view/renderer/abstract_renderer.rb +142 -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 +21 -273
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +61 -16
- data/lib/action_view/renderer/renderer.rb +59 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +10 -8
- data/lib/action_view/renderer/template_renderer.rb +35 -27
- data/lib/action_view/rendering.rb +54 -33
- data/lib/action_view/routing_url_for.rb +13 -12
- data/lib/action_view/template.rb +66 -75
- data/lib/action_view/template/error.rb +30 -15
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/handlers/builder.rb +2 -2
- data/lib/action_view/template/handlers/erb.rb +16 -11
- data/lib/action_view/template/handlers/erb/erubi.rb +15 -9
- data/lib/action_view/template/handlers/html.rb +1 -1
- data/lib/action_view/template/handlers/raw.rb +2 -2
- data/lib/action_view/template/html.rb +5 -6
- 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 +191 -150
- 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 +2 -3
- data/lib/action_view/test_case.rb +21 -29
- data/lib/action_view/testing/resolvers.rb +18 -27
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/view_paths.rb +59 -38
- data/lib/assets/compiled/rails-ujs.js +29 -3
- metadata +32 -21
- data/lib/action_view/helpers/record_tag_helper.rb +0 -23
@@ -52,7 +52,7 @@ module ActionView
|
|
52
52
|
# solution being slower. You should be sure to measure your actual
|
53
53
|
# performance across targeted browsers both before and after this change.
|
54
54
|
#
|
55
|
-
# To implement the corresponding hosts you can either
|
55
|
+
# To implement the corresponding hosts you can either set up four actual
|
56
56
|
# hosts or use wildcard DNS to CNAME the wildcard to a single asset host.
|
57
57
|
# You can read more about setting up your DNS CNAME records from your ISP.
|
58
58
|
#
|
@@ -80,7 +80,7 @@ module ActionView
|
|
80
80
|
# absolute path of the asset, for example "/assets/rails.png".
|
81
81
|
#
|
82
82
|
# ActionController::Base.asset_host = Proc.new { |source|
|
83
|
-
# if source.
|
83
|
+
# if source.end_with?('.css')
|
84
84
|
# "http://stylesheets.example.com"
|
85
85
|
# else
|
86
86
|
# "http://assets.example.com"
|
@@ -98,8 +98,9 @@ module ActionView
|
|
98
98
|
# have SSL certificates for each of the asset hosts this technique allows you
|
99
99
|
# to avoid warnings in the client about mixed media.
|
100
100
|
# Note that the +request+ parameter might not be supplied, e.g. when the assets
|
101
|
-
# are precompiled
|
102
|
-
# since a +Proc+ allows missing parameters and sets them
|
101
|
+
# are precompiled with the command `bin/rails assets:precompile`. Make sure to use a
|
102
|
+
# +Proc+ instead of a lambda, since a +Proc+ allows missing parameters and sets them
|
103
|
+
# to +nil+.
|
103
104
|
#
|
104
105
|
# config.action_controller.asset_host = Proc.new { |source, request|
|
105
106
|
# if request && request.ssl?
|
@@ -132,6 +133,8 @@ module ActionView
|
|
132
133
|
# which is implemented by sprockets-rails.
|
133
134
|
#
|
134
135
|
# asset_path("application.js") # => "/assets/application-60aa4fdc5cea14baf5400fba1abf4f2a46a5166bad4772b1effe341570f07de9.js"
|
136
|
+
# asset_path('application.js', host: 'example.com') # => "//example.com/assets/application.js"
|
137
|
+
# asset_path("application.js", host: 'example.com', protocol: 'https') # => "https://example.com/assets/application.js"
|
135
138
|
#
|
136
139
|
# === Without the asset pipeline (<tt>skip_pipeline: true</tt>)
|
137
140
|
#
|
@@ -187,7 +190,7 @@ module ActionView
|
|
187
190
|
return "" if source.blank?
|
188
191
|
return source if URI_REGEXP.match?(source)
|
189
192
|
|
190
|
-
tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, ""
|
193
|
+
tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, "")
|
191
194
|
|
192
195
|
if extname = compute_asset_extname(source, options)
|
193
196
|
source = "#{source}#{extname}"
|
@@ -203,7 +206,7 @@ module ActionView
|
|
203
206
|
|
204
207
|
relative_url_root = defined?(config.relative_url_root) && config.relative_url_root
|
205
208
|
if relative_url_root
|
206
|
-
source = File.join(relative_url_root, source) unless source.
|
209
|
+
source = File.join(relative_url_root, source) unless source.start_with?("#{relative_url_root}/")
|
207
210
|
end
|
208
211
|
|
209
212
|
if host = compute_asset_host(source, options)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "set"
|
4
|
+
require "active_support/core_ext/symbol/starts_ends_with"
|
4
5
|
|
5
6
|
module ActionView
|
6
7
|
# = Action View Atom Feed Helpers
|
@@ -115,7 +116,7 @@ module ActionView
|
|
115
116
|
end
|
116
117
|
|
117
118
|
feed_opts = { "xml:lang" => options[:language] || "en-US", "xmlns" => "http://www.w3.org/2005/Atom" }
|
118
|
-
feed_opts.merge!(options).
|
119
|
+
feed_opts.merge!(options).select! { |k, _| k.start_with?("xml") }
|
119
120
|
|
120
121
|
xml.feed(feed_opts) do
|
121
122
|
xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
|
@@ -24,8 +24,8 @@ module ActionView
|
|
24
24
|
# This approach will assume that when a new topic is added, you'll touch
|
25
25
|
# the project. The cache key generated from this call will be something like:
|
26
26
|
#
|
27
|
-
# views/template/action
|
28
|
-
# ^template path
|
27
|
+
# views/template/action:7a1156131a6928cb0026877f8b749ac9/projects/123
|
28
|
+
# ^template path ^template tree digest ^class ^id
|
29
29
|
#
|
30
30
|
# This cache key is stable, but it's combined with a cache version derived from the project
|
31
31
|
# record. When the project updated_at is touched, the #cache_version changes, even
|
@@ -165,8 +165,8 @@ module ActionView
|
|
165
165
|
# expire the cache.
|
166
166
|
def cache(name = {}, options = {}, &block)
|
167
167
|
if controller.respond_to?(:perform_caching) && controller.perform_caching
|
168
|
-
name_options = options.slice(:skip_digest
|
169
|
-
safe_concat(fragment_for(cache_fragment_name(name, name_options), options, &block))
|
168
|
+
name_options = options.slice(:skip_digest)
|
169
|
+
safe_concat(fragment_for(cache_fragment_name(name, **name_options), options, &block))
|
170
170
|
else
|
171
171
|
yield
|
172
172
|
end
|
@@ -201,34 +201,35 @@ module ActionView
|
|
201
201
|
end
|
202
202
|
|
203
203
|
# This helper returns the name of a cache key for a given fragment cache
|
204
|
-
# call. By supplying
|
204
|
+
# call. By supplying <tt>skip_digest: true</tt> to cache, the digestion of cache
|
205
205
|
# fragments can be manually bypassed. This is useful when cache fragments
|
206
206
|
# cannot be manually expired unless you know the exact key which is the
|
207
207
|
# case when using memcached.
|
208
|
-
|
209
|
-
# The digest will be generated using +virtual_path:+ if it is provided.
|
210
|
-
#
|
211
|
-
def cache_fragment_name(name = {}, skip_digest: nil, virtual_path: nil)
|
208
|
+
def cache_fragment_name(name = {}, skip_digest: nil, digest_path: nil)
|
212
209
|
if skip_digest
|
213
210
|
name
|
214
211
|
else
|
215
|
-
fragment_name_with_digest(name,
|
212
|
+
fragment_name_with_digest(name, digest_path)
|
216
213
|
end
|
217
214
|
end
|
218
215
|
|
219
|
-
|
216
|
+
def digest_path_from_template(template) # :nodoc:
|
217
|
+
digest = Digestor.digest(name: template.virtual_path, format: template.format, finder: lookup_context, dependencies: view_cache_dependencies)
|
220
218
|
|
221
|
-
|
222
|
-
|
219
|
+
if digest.present?
|
220
|
+
"#{template.virtual_path}:#{digest}"
|
221
|
+
else
|
222
|
+
template.virtual_path
|
223
|
+
end
|
224
|
+
end
|
223
225
|
|
224
|
-
|
225
|
-
|
226
|
+
private
|
227
|
+
def fragment_name_with_digest(name, digest_path)
|
228
|
+
name = controller.url_for(name).split("://").last if name.is_a?(Hash)
|
226
229
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
[ virtual_path, name ]
|
231
|
-
end
|
230
|
+
if @current_template&.virtual_path || digest_path
|
231
|
+
digest_path ||= digest_path_from_template(@current_template)
|
232
|
+
[ digest_path, name ]
|
232
233
|
else
|
233
234
|
name
|
234
235
|
end
|
@@ -236,10 +237,10 @@ module ActionView
|
|
236
237
|
|
237
238
|
def fragment_for(name = {}, options = nil, &block)
|
238
239
|
if content = read_fragment_for(name, options)
|
239
|
-
@view_renderer.cache_hits[@virtual_path] = :hit if defined?(@view_renderer)
|
240
|
+
@view_renderer.cache_hits[@current_template&.virtual_path] = :hit if defined?(@view_renderer)
|
240
241
|
content
|
241
242
|
else
|
242
|
-
@view_renderer.cache_hits[@virtual_path] = :miss if defined?(@view_renderer)
|
243
|
+
@view_renderer.cache_hits[@current_template&.virtual_path] = :miss if defined?(@view_renderer)
|
243
244
|
write_fragment_for(name, options, &block)
|
244
245
|
end
|
245
246
|
end
|
@@ -36,6 +36,10 @@ module ActionView
|
|
36
36
|
# </body>
|
37
37
|
# </html>
|
38
38
|
#
|
39
|
+
# The return of capture is the string generated by the block. For Example:
|
40
|
+
#
|
41
|
+
# @greeting # => "Welcome to my shiny new web page! The date and time is 2018-09-06 11:09:16 -0500"
|
42
|
+
#
|
39
43
|
def capture(*args)
|
40
44
|
value = nil
|
41
45
|
buffer = with_output_buffer { value = yield(*args) }
|
@@ -14,9 +14,11 @@ module ActionView
|
|
14
14
|
# This is used by the Rails UJS helper to create dynamically
|
15
15
|
# loaded inline <script> elements.
|
16
16
|
#
|
17
|
-
def csp_meta_tag
|
17
|
+
def csp_meta_tag(**options)
|
18
18
|
if content_security_policy?
|
19
|
-
|
19
|
+
options[:name] = "csp-nonce"
|
20
|
+
options[:content] = content_security_policy_nonce
|
21
|
+
tag("meta", options)
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|
@@ -20,7 +20,7 @@ module ActionView
|
|
20
20
|
# "X-CSRF-Token" HTTP header. If you are using rails-ujs this happens automatically.
|
21
21
|
#
|
22
22
|
def csrf_meta_tags
|
23
|
-
if protect_against_forgery?
|
23
|
+
if defined?(protect_against_forgery?) && protect_against_forgery?
|
24
24
|
[
|
25
25
|
tag("meta", name: "csrf-param", content: request_forgery_protection_token),
|
26
26
|
tag("meta", name: "csrf-token", content: form_authenticity_token)
|
@@ -197,14 +197,15 @@ module ActionView
|
|
197
197
|
# and +:name+ (string). A format string would be something like "%{name} (%<number>02d)" for example.
|
198
198
|
# See <tt>Kernel.sprintf</tt> for documentation on format sequences.
|
199
199
|
# * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
|
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
|
+
# * <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 " — ".
|
202
202
|
# * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Date.today.year - 5</tt> if
|
203
203
|
# you are creating new record. While editing existing record, <tt>:start_year</tt> defaults to
|
204
204
|
# the current selected year minus 5.
|
205
205
|
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Date.today.year + 5</tt> if
|
206
206
|
# you are creating new record. While editing existing record, <tt>:end_year</tt> defaults to
|
207
207
|
# the current selected year plus 5.
|
208
|
+
# * <tt>:year_format</tt> - Set format of years for year select. Lambda should be passed.
|
208
209
|
# * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
|
209
210
|
# as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
|
210
211
|
# first of the given month in order to not create invalid dates like 31 February.
|
@@ -275,6 +276,9 @@ module ActionView
|
|
275
276
|
# # Generates a date select with custom prompts.
|
276
277
|
# date_select("article", "written_on", prompt: { day: 'Select day', month: 'Select month', year: 'Select year' })
|
277
278
|
#
|
279
|
+
# # Generates a date select with custom year format.
|
280
|
+
# date_select("article", "written_on", year_format: ->(year) { "Heisei #{year - 1988}" })
|
281
|
+
#
|
278
282
|
# The selects are prepared for multi-parameter assignment to an Active Record object.
|
279
283
|
#
|
280
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
|
@@ -302,15 +306,15 @@ module ActionView
|
|
302
306
|
# time_select("article", "start_time", include_seconds: true)
|
303
307
|
#
|
304
308
|
# # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30, and 45.
|
305
|
-
# time_select 'game', 'game_time', {minute_step: 15}
|
309
|
+
# time_select 'game', 'game_time', { minute_step: 15 }
|
306
310
|
#
|
307
311
|
# # Creates a time select tag with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
|
308
|
-
# time_select("article", "written_on", prompt: {hour: 'Choose hour', minute: 'Choose minute', second: 'Choose seconds'})
|
309
|
-
# 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
|
310
314
|
# time_select("article", "written_on", prompt: true) # generic prompts for all
|
311
315
|
#
|
312
316
|
# # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
|
313
|
-
# time_select 'game', 'game_time', {ampm: true}
|
317
|
+
# time_select 'game', 'game_time', { ampm: true }
|
314
318
|
#
|
315
319
|
# The selects are prepared for multi-parameter assignment to an Active Record object.
|
316
320
|
#
|
@@ -346,8 +350,8 @@ module ActionView
|
|
346
350
|
# datetime_select("article", "written_on", discard_type: true)
|
347
351
|
#
|
348
352
|
# # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
|
349
|
-
# datetime_select("article", "written_on", prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
|
350
|
-
# 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
|
351
355
|
# datetime_select("article", "written_on", prompt: true) # generic prompts for all
|
352
356
|
#
|
353
357
|
# The selects are prepared for multi-parameter assignment to an Active Record object.
|
@@ -397,8 +401,8 @@ module ActionView
|
|
397
401
|
# select_datetime(my_date_time, prefix: 'payday')
|
398
402
|
#
|
399
403
|
# # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
|
400
|
-
# select_datetime(my_date_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
|
401
|
-
# 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
|
402
406
|
# select_datetime(my_date_time, prompt: true) # generic prompts for all
|
403
407
|
def select_datetime(datetime = Time.current, options = {}, html_options = {})
|
404
408
|
DateTimeSelector.new(datetime, options, html_options).select_datetime
|
@@ -436,8 +440,8 @@ module ActionView
|
|
436
440
|
# select_date(my_date, prefix: 'payday')
|
437
441
|
#
|
438
442
|
# # Generates a date select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
|
439
|
-
# select_date(my_date, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
|
440
|
-
# 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
|
441
445
|
# select_date(my_date, prompt: true) # generic prompts for all
|
442
446
|
def select_date(date = Date.current, options = {}, html_options = {})
|
443
447
|
DateTimeSelector.new(date, options, html_options).select_date
|
@@ -476,8 +480,8 @@ module ActionView
|
|
476
480
|
# select_time(my_time, start_hour: 2, end_hour: 14)
|
477
481
|
#
|
478
482
|
# # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
|
479
|
-
# select_time(my_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
|
480
|
-
# 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
|
481
485
|
# select_time(my_time, prompt: true) # generic prompts for all
|
482
486
|
def select_time(datetime = Time.current, options = {}, html_options = {})
|
483
487
|
DateTimeSelector.new(datetime, options, html_options).select_time
|
@@ -668,8 +672,6 @@ module ActionView
|
|
668
672
|
# <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
|
669
673
|
# time_tag Date.yesterday, 'Yesterday' # =>
|
670
674
|
# <time datetime="2010-11-03">Yesterday</time>
|
671
|
-
# time_tag Date.today, pubdate: true # =>
|
672
|
-
# <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
|
673
675
|
# time_tag Date.today, datetime: Date.today.strftime('%G-W%V') # =>
|
674
676
|
# <time datetime="2010-W44">November 04, 2010</time>
|
675
677
|
#
|
@@ -681,13 +683,11 @@ module ActionView
|
|
681
683
|
options = args.extract_options!
|
682
684
|
format = options.delete(:format) || :long
|
683
685
|
content = args.first || I18n.l(date_or_time, format: format)
|
684
|
-
datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.iso8601
|
685
686
|
|
686
|
-
content_tag("time"
|
687
|
+
content_tag("time", content, options.reverse_merge(datetime: date_or_time.iso8601), &block)
|
687
688
|
end
|
688
689
|
|
689
690
|
private
|
690
|
-
|
691
691
|
def normalize_distance_of_time_argument_to_time(value)
|
692
692
|
if value.is_a?(Numeric)
|
693
693
|
Time.at(value)
|
@@ -702,7 +702,7 @@ module ActionView
|
|
702
702
|
class DateTimeSelector #:nodoc:
|
703
703
|
include ActionView::Helpers::TagHelper
|
704
704
|
|
705
|
-
DEFAULT_PREFIX = "date"
|
705
|
+
DEFAULT_PREFIX = "date"
|
706
706
|
POSITION = {
|
707
707
|
year: 1, month: 2, day: 3, hour: 4, minute: 5, second: 6
|
708
708
|
}.freeze
|
@@ -823,14 +823,14 @@ module ActionView
|
|
823
823
|
1.upto(12) do |month_number|
|
824
824
|
options = { value: month_number }
|
825
825
|
options[:selected] = "selected" if month == month_number
|
826
|
-
month_options << content_tag("option"
|
826
|
+
month_options << content_tag("option", month_name(month_number), options) + "\n"
|
827
827
|
end
|
828
828
|
build_select(:month, month_options.join)
|
829
829
|
end
|
830
830
|
end
|
831
831
|
|
832
832
|
def select_year
|
833
|
-
if
|
833
|
+
if !year || @datetime == 0
|
834
834
|
val = "1"
|
835
835
|
middle_year = Date.today.year
|
836
836
|
else
|
@@ -851,7 +851,7 @@ module ActionView
|
|
851
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."
|
852
852
|
end
|
853
853
|
|
854
|
-
|
854
|
+
build_select(:year, build_year_options(val, options))
|
855
855
|
end
|
856
856
|
end
|
857
857
|
|
@@ -934,6 +934,21 @@ module ActionView
|
|
934
934
|
end
|
935
935
|
end
|
936
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
|
+
|
937
952
|
def date_order
|
938
953
|
@date_order ||= @options[:order] || translated_date_order
|
939
954
|
end
|
@@ -990,7 +1005,35 @@ module ActionView
|
|
990
1005
|
tag_options[:selected] = "selected" if selected == i
|
991
1006
|
text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
|
992
1007
|
text = options[:ampm] ? AMPM_TRANSLATION[i] : text
|
993
|
-
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)
|
994
1037
|
end
|
995
1038
|
|
996
1039
|
(select_options.join("\n") + "\n").html_safe
|
@@ -1009,12 +1052,12 @@ module ActionView
|
|
1009
1052
|
select_options[:disabled] = "disabled" if @options[:disabled]
|
1010
1053
|
select_options[:class] = css_class_attribute(type, select_options[:class], @options[:with_css_classes]) if @options[:with_css_classes]
|
1011
1054
|
|
1012
|
-
select_html = "\n"
|
1013
|
-
select_html << content_tag("option"
|
1055
|
+
select_html = +"\n"
|
1056
|
+
select_html << content_tag("option", "", value: "", label: " ") + "\n" if @options[:include_blank]
|
1014
1057
|
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
|
1015
1058
|
select_html << select_options_as_html
|
1016
1059
|
|
1017
|
-
(content_tag("select"
|
1060
|
+
(content_tag("select", select_html.html_safe, select_options) + "\n").html_safe
|
1018
1061
|
end
|
1019
1062
|
|
1020
1063
|
# Builds the css class value for the select element
|
@@ -1047,7 +1090,7 @@ module ActionView
|
|
1047
1090
|
I18n.translate(:"datetime.prompts.#{type}", locale: @options[:locale])
|
1048
1091
|
end
|
1049
1092
|
|
1050
|
-
prompt ? content_tag("option"
|
1093
|
+
prompt ? content_tag("option", prompt, value: "") : ""
|
1051
1094
|
end
|
1052
1095
|
|
1053
1096
|
# Builds hidden input tag for date part and value.
|
@@ -1091,11 +1134,11 @@ module ActionView
|
|
1091
1134
|
# Given an ordering of datetime components, create the selection HTML
|
1092
1135
|
# and join them with their appropriate separators.
|
1093
1136
|
def build_selects_from_types(order)
|
1094
|
-
select = ""
|
1137
|
+
select = +""
|
1095
1138
|
first_visible = order.find { |type| !@options[:"discard_#{type}"] }
|
1096
1139
|
order.reverse_each do |type|
|
1097
1140
|
separator = separator(type) unless type == first_visible # don't add before first visible field
|
1098
|
-
select.insert(0, separator.to_s +
|
1141
|
+
select.insert(0, separator.to_s + public_send("select_#{type}").to_s)
|
1099
1142
|
end
|
1100
1143
|
select.html_safe
|
1101
1144
|
end
|
@@ -11,6 +11,7 @@ require "active_support/core_ext/module/attribute_accessors"
|
|
11
11
|
require "active_support/core_ext/hash/slice"
|
12
12
|
require "active_support/core_ext/string/output_safety"
|
13
13
|
require "active_support/core_ext/string/inflections"
|
14
|
+
require "active_support/core_ext/symbol/starts_ends_with"
|
14
15
|
|
15
16
|
module ActionView
|
16
17
|
# = Action View Form Helpers
|
@@ -185,8 +186,7 @@ module ActionView
|
|
185
186
|
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
186
187
|
# unnecessary unless you support browsers without JavaScript.
|
187
188
|
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive
|
188
|
-
# JavaScript drivers to control the submit behavior.
|
189
|
-
# behavior is an ajax submit.
|
189
|
+
# JavaScript drivers to control the submit behavior.
|
190
190
|
# * <tt>:enforce_utf8</tt> - If set to false, a hidden input with name
|
191
191
|
# utf8 is not output.
|
192
192
|
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
|
@@ -322,10 +322,8 @@ module ActionView
|
|
322
322
|
# remote: true
|
323
323
|
#
|
324
324
|
# in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
|
325
|
-
# behavior. The
|
326
|
-
#
|
327
|
-
# Even though it's using JavaScript to serialize the form elements, the form submission will work just like
|
328
|
-
# 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>).
|
329
327
|
#
|
330
328
|
# Example:
|
331
329
|
#
|
@@ -535,11 +533,6 @@ module ActionView
|
|
535
533
|
# accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
|
536
534
|
# respectively.
|
537
535
|
#
|
538
|
-
# By default +form_with+ attaches the <tt>data-remote</tt> attribute
|
539
|
-
# submitting the form via an XMLHTTPRequest in the background if an
|
540
|
-
# Unobtrusive JavaScript driver, like rails-ujs, is used. See the
|
541
|
-
# <tt>:local</tt> option for more.
|
542
|
-
#
|
543
536
|
# For ease of comparison the examples above left out the submit button,
|
544
537
|
# as well as the auto generated hidden fields that enable UTF-8 support
|
545
538
|
# and adds an authenticity token needed for cross site request forgery
|
@@ -590,6 +583,9 @@ module ActionView
|
|
590
583
|
# Skipped if a <tt>:url</tt> is passed.
|
591
584
|
# * <tt>:scope</tt> - The scope to prefix input field names with and
|
592
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.
|
593
589
|
# * <tt>:model</tt> - A model object to infer the <tt>:url</tt> and
|
594
590
|
# <tt>:scope</tt> by, plus fill out input field values.
|
595
591
|
# So if a +title+ attribute is set to "Ahoy!" then a +title+ input
|
@@ -608,10 +604,12 @@ module ActionView
|
|
608
604
|
# This is helpful when fragment-caching the form. Remote forms
|
609
605
|
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
610
606
|
# unnecessary unless you support browsers without JavaScript.
|
611
|
-
# * <tt>:local</tt> - By default form submits
|
612
|
-
#
|
613
|
-
#
|
614
|
-
#
|
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.
|
615
613
|
# * <tt>:builder</tt> - Override the object used to build the form.
|
616
614
|
# * <tt>:id</tt> - Optional HTML id attribute.
|
617
615
|
# * <tt>:class</tt> - Optional HTML class attribute.
|
@@ -752,10 +750,10 @@ module ActionView
|
|
752
750
|
output = capture(builder, &block)
|
753
751
|
options[:multipart] ||= builder.multipart?
|
754
752
|
|
755
|
-
html_options = html_options_for_form_with(url, model, options)
|
753
|
+
html_options = html_options_for_form_with(url, model, **options)
|
756
754
|
form_tag_with_body(html_options, output)
|
757
755
|
else
|
758
|
-
html_options = html_options_for_form_with(url, model, options)
|
756
|
+
html_options = html_options_for_form_with(url, model, **options)
|
759
757
|
form_tag_html(html_options)
|
760
758
|
end
|
761
759
|
end
|
@@ -885,7 +883,7 @@ module ActionView
|
|
885
883
|
#
|
886
884
|
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
|
887
885
|
# with a value that evaluates to +true+, you will destroy the associated
|
888
|
-
# model (
|
886
|
+
# model (e.g. 1, '1', true, or 'true'):
|
889
887
|
#
|
890
888
|
# <%= form_for @person do |person_form| %>
|
891
889
|
# ...
|
@@ -974,7 +972,7 @@ module ActionView
|
|
974
972
|
# This will allow you to specify which models to destroy in the
|
975
973
|
# attributes hash by adding a form element for the <tt>_destroy</tt>
|
976
974
|
# parameter with a value that evaluates to +true+
|
977
|
-
# (
|
975
|
+
# (e.g. 1, '1', true, or 'true'):
|
978
976
|
#
|
979
977
|
# <%= form_for @person do |person_form| %>
|
980
978
|
# ...
|
@@ -1107,6 +1105,16 @@ module ActionView
|
|
1107
1105
|
# label(:post, :privacy, "Public Post", value: "public")
|
1108
1106
|
# # => <label for="post_privacy_public">Public Post</label>
|
1109
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
|
+
#
|
1110
1118
|
# label(:post, :terms) do
|
1111
1119
|
# raw('Accept <a href="/terms">Terms</a>.')
|
1112
1120
|
# end
|
@@ -1127,6 +1135,9 @@ module ActionView
|
|
1127
1135
|
# text_field(:post, :title, class: "create_input")
|
1128
1136
|
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
|
1129
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
|
+
#
|
1130
1141
|
# text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }")
|
1131
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!'); }"/>
|
1132
1143
|
#
|
@@ -1519,10 +1530,10 @@ module ActionView
|
|
1519
1530
|
|
1520
1531
|
private
|
1521
1532
|
def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: !form_with_generates_remote_forms,
|
1522
|
-
skip_enforcing_utf8:
|
1533
|
+
skip_enforcing_utf8: nil, **options)
|
1523
1534
|
html_options = options.slice(:id, :class, :multipart, :method, :data).merge(html)
|
1524
1535
|
html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
|
1525
|
-
html_options[:enforce_utf8] = !skip_enforcing_utf8
|
1536
|
+
html_options[:enforce_utf8] = !skip_enforcing_utf8 unless skip_enforcing_utf8.nil?
|
1526
1537
|
|
1527
1538
|
html_options[:enctype] = "multipart/form-data" if html_options.delete(:multipart)
|
1528
1539
|
|
@@ -1662,8 +1673,8 @@ module ActionView
|
|
1662
1673
|
|
1663
1674
|
convert_to_legacy_options(@options)
|
1664
1675
|
|
1665
|
-
if @object_name
|
1666
|
-
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)
|
1667
1678
|
@auto_index = object.to_param
|
1668
1679
|
else
|
1669
1680
|
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
|
@@ -1674,11 +1685,232 @@ module ActionView
|
|
1674
1685
|
@index = options[:index] || options[:child_index]
|
1675
1686
|
end
|
1676
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
|
+
|
1677
1909
|
(field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
|
1678
1910
|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
1679
1911
|
def #{selector}(method, options = {}) # def text_field(method, options = {})
|
1680
|
-
@template.
|
1681
|
-
#{selector.inspect}, #
|
1912
|
+
@template.public_send( # @template.public_send(
|
1913
|
+
#{selector.inspect}, # :text_field,
|
1682
1914
|
@object_name, # @object_name,
|
1683
1915
|
method, # method,
|
1684
1916
|
objectify_options(options)) # objectify_options(options))
|
@@ -1811,7 +2043,7 @@ module ActionView
|
|
1811
2043
|
#
|
1812
2044
|
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
|
1813
2045
|
# with a value that evaluates to +true+, you will destroy the associated
|
1814
|
-
# model (
|
2046
|
+
# model (e.g. 1, '1', true, or 'true'):
|
1815
2047
|
#
|
1816
2048
|
# <%= form_for @person do |person_form| %>
|
1817
2049
|
# ...
|
@@ -1900,7 +2132,7 @@ module ActionView
|
|
1900
2132
|
# This will allow you to specify which models to destroy in the
|
1901
2133
|
# attributes hash by adding a form element for the <tt>_destroy</tt>
|
1902
2134
|
# parameter with a value that evaluates to +true+
|
1903
|
-
# (
|
2135
|
+
# (e.g. 1, '1', true, or 'true'):
|
1904
2136
|
#
|
1905
2137
|
# <%= form_for @person do |person_form| %>
|
1906
2138
|
# ...
|
@@ -1947,15 +2179,14 @@ module ActionView
|
|
1947
2179
|
index = if options.has_key?(:index)
|
1948
2180
|
options[:index]
|
1949
2181
|
elsif defined?(@auto_index)
|
1950
|
-
object_name = object_name.to_s.
|
2182
|
+
object_name = object_name.to_s.delete_suffix("[]")
|
1951
2183
|
@auto_index
|
1952
2184
|
end
|
1953
2185
|
|
1954
2186
|
record_name = if index
|
1955
2187
|
"#{object_name}[#{index}][#{record_name}]"
|
1956
|
-
elsif record_name.
|
1957
|
-
record_name
|
1958
|
-
"#{object_name}#{record_name}"
|
2188
|
+
elsif record_name.end_with?("[]")
|
2189
|
+
"#{object_name}[#{record_name[0..-3]}][#{record_object.id}]"
|
1959
2190
|
else
|
1960
2191
|
"#{object_name}[#{record_name}]"
|
1961
2192
|
end
|
@@ -2018,6 +2249,24 @@ module ActionView
|
|
2018
2249
|
# label(:privacy, "Public Post", value: "public")
|
2019
2250
|
# # => <label for="post_privacy_public">Public Post</label>
|
2020
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
|
+
#
|
2021
2270
|
# label(:terms) do
|
2022
2271
|
# raw('Accept <a href="/terms">Terms</a>.')
|
2023
2272
|
# end
|
@@ -2241,19 +2490,33 @@ module ActionView
|
|
2241
2490
|
# # <strong>Ask me!</strong>
|
2242
2491
|
# # </button>
|
2243
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
|
+
#
|
2244
2500
|
def button(value = nil, options = {}, &block)
|
2245
2501
|
value, options = nil, value if value.is_a?(Hash)
|
2246
2502
|
value ||= submit_default_value
|
2247
|
-
|
2503
|
+
|
2504
|
+
if block_given?
|
2505
|
+
value = @template.capture { yield(value) }
|
2506
|
+
end
|
2507
|
+
|
2508
|
+
@template.button_tag(value, options)
|
2248
2509
|
end
|
2249
2510
|
|
2250
|
-
def emitted_hidden_id?
|
2511
|
+
def emitted_hidden_id? # :nodoc:
|
2251
2512
|
@emitted_hidden_id ||= nil
|
2252
2513
|
end
|
2253
2514
|
|
2254
2515
|
private
|
2255
2516
|
def objectify_options(options)
|
2256
|
-
@default_options.merge(options
|
2517
|
+
result = @default_options.merge(options)
|
2518
|
+
result[:object] = @object
|
2519
|
+
result
|
2257
2520
|
end
|
2258
2521
|
|
2259
2522
|
def submit_default_value
|
@@ -2267,7 +2530,12 @@ module ActionView
|
|
2267
2530
|
end
|
2268
2531
|
|
2269
2532
|
defaults = []
|
2270
|
-
|
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
|
2271
2539
|
defaults << :"helpers.submit.#{key}"
|
2272
2540
|
defaults << "#{key.to_s.humanize} #{model}"
|
2273
2541
|
|
@@ -2283,9 +2551,9 @@ module ActionView
|
|
2283
2551
|
association = convert_to_model(association)
|
2284
2552
|
|
2285
2553
|
if association.respond_to?(:persisted?)
|
2286
|
-
association = [association] if @object.
|
2554
|
+
association = [association] if @object.public_send(association_name).respond_to?(:to_ary)
|
2287
2555
|
elsif !association.respond_to?(:to_ary)
|
2288
|
-
association = @object.
|
2556
|
+
association = @object.public_send(association_name)
|
2289
2557
|
end
|
2290
2558
|
|
2291
2559
|
if association.respond_to?(:to_ary)
|