actionview 6.1.7.10 → 7.0.0.alpha1
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 +89 -409
- data/MIT-LICENSE +2 -1
- data/lib/action_view/base.rb +3 -3
- data/lib/action_view/buffers.rb +2 -2
- data/lib/action_view/cache_expiry.rb +46 -32
- data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
- data/lib/action_view/dependency_tracker.rb +6 -147
- data/lib/action_view/digestor.rb +7 -4
- data/lib/action_view/flows.rb +4 -4
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers/active_model_helper.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +84 -29
- data/lib/action_view/helpers/asset_url_helper.rb +7 -7
- data/lib/action_view/helpers/atom_feed_helper.rb +3 -4
- data/lib/action_view/helpers/cache_helper.rb +51 -3
- data/lib/action_view/helpers/capture_helper.rb +2 -2
- data/lib/action_view/helpers/controller_helper.rb +2 -2
- data/lib/action_view/helpers/csp_helper.rb +1 -1
- data/lib/action_view/helpers/csrf_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +6 -7
- data/lib/action_view/helpers/debug_helper.rb +3 -1
- data/lib/action_view/helpers/form_helper.rb +72 -12
- data/lib/action_view/helpers/form_options_helper.rb +65 -33
- data/lib/action_view/helpers/form_tag_helper.rb +75 -32
- data/lib/action_view/helpers/javascript_helper.rb +3 -5
- data/lib/action_view/helpers/number_helper.rb +3 -4
- data/lib/action_view/helpers/output_safety_helper.rb +2 -2
- data/lib/action_view/helpers/rendering_helper.rb +1 -1
- data/lib/action_view/helpers/sanitize_helper.rb +2 -2
- data/lib/action_view/helpers/tag_helper.rb +25 -44
- data/lib/action_view/helpers/tags/base.rb +3 -15
- data/lib/action_view/helpers/tags/check_box.rb +2 -2
- data/lib/action_view/helpers/tags/collection_select.rb +1 -1
- data/lib/action_view/helpers/tags/hidden_field.rb +0 -4
- data/lib/action_view/helpers/tags/time_field.rb +10 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +27 -0
- data/lib/action_view/helpers/tags.rb +3 -2
- data/lib/action_view/helpers/text_helper.rb +24 -13
- data/lib/action_view/helpers/translation_helper.rb +1 -2
- data/lib/action_view/helpers/url_helper.rb +102 -77
- data/lib/action_view/helpers.rb +25 -25
- data/lib/action_view/lookup_context.rb +33 -52
- data/lib/action_view/model_naming.rb +1 -1
- data/lib/action_view/path_set.rb +16 -22
- data/lib/action_view/railtie.rb +14 -1
- data/lib/action_view/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +2 -2
- data/lib/action_view/renderer/partial_renderer.rb +0 -34
- data/lib/action_view/renderer/renderer.rb +4 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +3 -3
- data/lib/action_view/renderer/template_renderer.rb +6 -2
- data/lib/action_view/rendering.rb +2 -2
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +1 -1
- data/lib/action_view/template/error.rb +108 -13
- data/lib/action_view/template/handlers/erb.rb +6 -0
- data/lib/action_view/template/handlers.rb +3 -3
- data/lib/action_view/template/html.rb +3 -3
- data/lib/action_view/template/inline.rb +3 -3
- data/lib/action_view/template/raw_file.rb +3 -3
- data/lib/action_view/template/resolver.rb +84 -311
- data/lib/action_view/template/text.rb +3 -3
- data/lib/action_view/template/types.rb +14 -12
- data/lib/action_view/template.rb +10 -1
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +64 -0
- data/lib/action_view/test_case.rb +6 -2
- data/lib/action_view/testing/resolvers.rb +11 -12
- data/lib/action_view/unbound_template.rb +33 -7
- data/lib/action_view.rb +3 -4
- data/lib/assets/compiled/rails-ujs.js +5 -36
- metadata +22 -16
@@ -8,7 +8,7 @@ require "action_view/helpers/tag_helper"
|
|
8
8
|
|
9
9
|
module ActionView
|
10
10
|
# = Action View Asset Tag Helpers
|
11
|
-
module Helpers
|
11
|
+
module Helpers # :nodoc:
|
12
12
|
# This module provides methods for generating HTML that links views to assets such
|
13
13
|
# as images, JavaScripts, stylesheets, and feeds. These methods do not verify
|
14
14
|
# the assets exist before linking to them:
|
@@ -16,14 +16,15 @@ module ActionView
|
|
16
16
|
# image_tag("rails.png")
|
17
17
|
# # => <img src="/assets/rails.png" />
|
18
18
|
# stylesheet_link_tag("application")
|
19
|
-
# # => <link href="/assets/application.css?body=1"
|
19
|
+
# # => <link href="/assets/application.css?body=1" rel="stylesheet" />
|
20
20
|
module AssetTagHelper
|
21
|
-
extend ActiveSupport::Concern
|
22
|
-
|
23
21
|
include AssetUrlHelper
|
24
22
|
include TagHelper
|
25
23
|
|
24
|
+
mattr_accessor :image_loading
|
25
|
+
mattr_accessor :image_decoding
|
26
26
|
mattr_accessor :preload_links_header
|
27
|
+
mattr_accessor :apply_stylesheet_media_default
|
27
28
|
|
28
29
|
# Returns an HTML script tag for each of the +sources+ provided.
|
29
30
|
#
|
@@ -93,11 +94,12 @@ module ActionView
|
|
93
94
|
crossorigin = options.delete("crossorigin")
|
94
95
|
crossorigin = "anonymous" if crossorigin == true
|
95
96
|
integrity = options["integrity"]
|
97
|
+
rel = options["type"] == "module" ? "modulepreload" : "preload"
|
96
98
|
|
97
99
|
sources_tags = sources.uniq.map { |source|
|
98
100
|
href = path_to_javascript(source, path_options)
|
99
|
-
if preload_links_header && !options["defer"]
|
100
|
-
preload_link = "<#{href}>; rel
|
101
|
+
if preload_links_header && !options["defer"] && href.present? && !href.start_with?("data:")
|
102
|
+
preload_link = "<#{href}>; rel=#{rel}; as=script"
|
101
103
|
preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
|
102
104
|
preload_link += "; integrity=#{integrity}" unless integrity.nil?
|
103
105
|
preload_link += "; nopush" if nopush
|
@@ -120,24 +122,41 @@ module ActionView
|
|
120
122
|
sources_tags
|
121
123
|
end
|
122
124
|
|
123
|
-
# Returns a stylesheet link tag for the sources specified as arguments.
|
124
|
-
#
|
125
|
+
# Returns a stylesheet link tag for the sources specified as arguments.
|
126
|
+
#
|
127
|
+
# When passing paths, the <tt>.css</tt> extension is optional.
|
128
|
+
# If you don't specify an extension, <tt>.css</tt> will be appended automatically.
|
129
|
+
# If you do not want <tt>.css</tt> appended to the path,
|
130
|
+
# set <tt>extname: false</tt> in the options.
|
125
131
|
# You can modify the link attributes by passing a hash as the last argument.
|
126
|
-
# For historical reasons, the 'media' attribute will always be present and defaults
|
127
|
-
# to "screen", so you must explicitly set it to "all" for the stylesheet(s) to
|
128
|
-
# apply to all media types.
|
129
132
|
#
|
130
133
|
# If the server supports Early Hints header links for these assets will be
|
131
134
|
# automatically pushed.
|
132
135
|
#
|
136
|
+
# ==== Options
|
137
|
+
#
|
138
|
+
# * <tt>:extname</tt> - Append an extension to the generated URL unless the extension
|
139
|
+
# already exists. This only applies for relative URLs.
|
140
|
+
# * <tt>:protocol</tt> - Sets the protocol of the generated URL. This option only
|
141
|
+
# applies when a relative URL and +host+ options are provided.
|
142
|
+
# * <tt>:host</tt> - When a relative URL is provided the host is added to the
|
143
|
+
# that path.
|
144
|
+
# * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline
|
145
|
+
# when it is set to true.
|
146
|
+
#
|
147
|
+
# ==== Examples
|
148
|
+
#
|
133
149
|
# stylesheet_link_tag "style"
|
134
|
-
# # => <link href="/assets/style.css"
|
150
|
+
# # => <link href="/assets/style.css" rel="stylesheet" />
|
135
151
|
#
|
136
152
|
# stylesheet_link_tag "style.css"
|
137
|
-
# # => <link href="/assets/style.css"
|
153
|
+
# # => <link href="/assets/style.css" rel="stylesheet" />
|
138
154
|
#
|
139
155
|
# stylesheet_link_tag "http://www.example.com/style.css"
|
140
|
-
# # => <link href="http://www.example.com/style.css"
|
156
|
+
# # => <link href="http://www.example.com/style.css" rel="stylesheet" />
|
157
|
+
#
|
158
|
+
# stylesheet_link_tag "style.less", extname: false, skip_pipeline: true, rel: "stylesheet/less"
|
159
|
+
# # => <link href="/stylesheets/style.less" rel="stylesheet/less">
|
141
160
|
#
|
142
161
|
# stylesheet_link_tag "style", media: "all"
|
143
162
|
# # => <link href="/assets/style.css" media="all" rel="stylesheet" />
|
@@ -146,11 +165,11 @@ module ActionView
|
|
146
165
|
# # => <link href="/assets/style.css" media="print" rel="stylesheet" />
|
147
166
|
#
|
148
167
|
# stylesheet_link_tag "random.styles", "/css/stylish"
|
149
|
-
# # => <link href="/assets/random.styles"
|
150
|
-
# # <link href="/css/stylish.css"
|
168
|
+
# # => <link href="/assets/random.styles" rel="stylesheet" />
|
169
|
+
# # <link href="/css/stylish.css" rel="stylesheet" />
|
151
170
|
def stylesheet_link_tag(*sources)
|
152
171
|
options = sources.extract_options!.stringify_keys
|
153
|
-
path_options = options.extract!("protocol", "host", "skip_pipeline").symbolize_keys
|
172
|
+
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
|
154
173
|
preload_links = []
|
155
174
|
crossorigin = options.delete("crossorigin")
|
156
175
|
crossorigin = "anonymous" if crossorigin == true
|
@@ -159,7 +178,7 @@ module ActionView
|
|
159
178
|
|
160
179
|
sources_tags = sources.uniq.map { |source|
|
161
180
|
href = path_to_stylesheet(source, path_options)
|
162
|
-
if preload_links_header
|
181
|
+
if preload_links_header && href.present? && !href.start_with?("data:")
|
163
182
|
preload_link = "<#{href}>; rel=preload; as=style"
|
164
183
|
preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
|
165
184
|
preload_link += "; integrity=#{integrity}" unless integrity.nil?
|
@@ -168,10 +187,14 @@ module ActionView
|
|
168
187
|
end
|
169
188
|
tag_options = {
|
170
189
|
"rel" => "stylesheet",
|
171
|
-
"media" => "screen",
|
172
190
|
"crossorigin" => crossorigin,
|
173
191
|
"href" => href
|
174
192
|
}.merge!(options)
|
193
|
+
|
194
|
+
if apply_stylesheet_media_default && tag_options["media"].blank?
|
195
|
+
tag_options["media"] = "screen"
|
196
|
+
end
|
197
|
+
|
175
198
|
tag(:link, tag_options)
|
176
199
|
}.join("\n").html_safe
|
177
200
|
|
@@ -382,6 +405,10 @@ module ActionView
|
|
382
405
|
end
|
383
406
|
|
384
407
|
options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
|
408
|
+
|
409
|
+
options[:loading] ||= image_loading if image_loading
|
410
|
+
options[:decoding] ||= image_decoding if image_decoding
|
411
|
+
|
385
412
|
tag("img", options)
|
386
413
|
end
|
387
414
|
|
@@ -503,24 +530,52 @@ module ActionView
|
|
503
530
|
end
|
504
531
|
|
505
532
|
def resolve_link_as(extname, mime_type)
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
"
|
512
|
-
elsif (type = mime_type.to_s.split("/")[0]) && type.in?(%w(audio video font image))
|
513
|
-
type
|
533
|
+
case extname
|
534
|
+
when "js" then "script"
|
535
|
+
when "css" then "style"
|
536
|
+
when "vtt" then "track"
|
537
|
+
else
|
538
|
+
mime_type.to_s.split("/").first.presence_in(%w(audio video font image))
|
514
539
|
end
|
515
540
|
end
|
516
541
|
|
517
|
-
|
542
|
+
MAX_HEADER_SIZE = 8_000 # Some HTTP client and proxies have a 8kiB header limit
|
543
|
+
def send_preload_links_header(preload_links, max_header_size: MAX_HEADER_SIZE)
|
544
|
+
return if preload_links.empty?
|
545
|
+
return if response.sending?
|
546
|
+
|
518
547
|
if respond_to?(:request) && request
|
519
548
|
request.send_early_hints("Link" => preload_links.join("\n"))
|
520
549
|
end
|
521
550
|
|
522
551
|
if respond_to?(:response) && response
|
523
|
-
|
552
|
+
header = response.headers["Link"]
|
553
|
+
header = header ? header.dup : +""
|
554
|
+
|
555
|
+
# rindex count characters not bytes, but we assume non-ascii characters
|
556
|
+
# are rare in urls, and we have a 192 bytes margin.
|
557
|
+
last_line_offset = header.rindex("\n")
|
558
|
+
last_line_size = if last_line_offset
|
559
|
+
header.bytesize - last_line_offset
|
560
|
+
else
|
561
|
+
header.bytesize
|
562
|
+
end
|
563
|
+
|
564
|
+
preload_links.each do |link|
|
565
|
+
if link.bytesize + last_line_size + 1 < max_header_size
|
566
|
+
unless header.empty?
|
567
|
+
header << ","
|
568
|
+
last_line_size += 1
|
569
|
+
end
|
570
|
+
else
|
571
|
+
header << "\n"
|
572
|
+
last_line_size = 0
|
573
|
+
end
|
574
|
+
header << link
|
575
|
+
last_line_size += link.bytesize
|
576
|
+
end
|
577
|
+
|
578
|
+
response.headers["Link"] = header
|
524
579
|
end
|
525
580
|
end
|
526
581
|
end
|
@@ -4,7 +4,7 @@ require "zlib"
|
|
4
4
|
|
5
5
|
module ActionView
|
6
6
|
# = Action View Asset URL Helpers
|
7
|
-
module Helpers
|
7
|
+
module Helpers # :nodoc:
|
8
8
|
# This module provides methods for generating asset paths and
|
9
9
|
# URLs.
|
10
10
|
#
|
@@ -31,7 +31,7 @@ module ActionView
|
|
31
31
|
# image_tag("rails.png")
|
32
32
|
# # => <img src="http://assets.example.com/assets/rails.png" />
|
33
33
|
# stylesheet_link_tag("application")
|
34
|
-
# # => <link href="http://assets.example.com/assets/application.css"
|
34
|
+
# # => <link href="http://assets.example.com/assets/application.css" rel="stylesheet" />
|
35
35
|
#
|
36
36
|
# Browsers open a limited number of simultaneous connections to a single
|
37
37
|
# host. The exact number varies by browser and version. This limit may cause
|
@@ -44,7 +44,7 @@ module ActionView
|
|
44
44
|
# image_tag("rails.png")
|
45
45
|
# # => <img src="http://assets0.example.com/assets/rails.png" />
|
46
46
|
# stylesheet_link_tag("application")
|
47
|
-
# # => <link href="http://assets2.example.com/assets/application.css"
|
47
|
+
# # => <link href="http://assets2.example.com/assets/application.css" rel="stylesheet" />
|
48
48
|
#
|
49
49
|
# This may improve the asset loading performance of your application.
|
50
50
|
# It is also possible the combination of additional connection overhead
|
@@ -65,12 +65,12 @@ module ActionView
|
|
65
65
|
# +asset_host+ to a proc like this:
|
66
66
|
#
|
67
67
|
# ActionController::Base.asset_host = Proc.new { |source|
|
68
|
-
# "http://assets#{Digest::
|
68
|
+
# "http://assets#{OpenSSL::Digest::SHA256.hexdigest(source).to_i(16) % 2 + 1}.example.com"
|
69
69
|
# }
|
70
70
|
# image_tag("rails.png")
|
71
71
|
# # => <img src="http://assets1.example.com/assets/rails.png" />
|
72
72
|
# stylesheet_link_tag("application")
|
73
|
-
# # => <link href="http://assets2.example.com/assets/application.css"
|
73
|
+
# # => <link href="http://assets2.example.com/assets/application.css" rel="stylesheet" />
|
74
74
|
#
|
75
75
|
# The example above generates "http://assets1.example.com" and
|
76
76
|
# "http://assets2.example.com". This option is useful for example if
|
@@ -89,7 +89,7 @@ module ActionView
|
|
89
89
|
# image_tag("rails.png")
|
90
90
|
# # => <img src="http://assets.example.com/assets/rails.png" />
|
91
91
|
# stylesheet_link_tag("application")
|
92
|
-
# # => <link href="http://stylesheets.example.com/assets/application.css"
|
92
|
+
# # => <link href="http://stylesheets.example.com/assets/application.css" rel="stylesheet" />
|
93
93
|
#
|
94
94
|
# Alternatively you may ask for a second parameter +request+. That one is
|
95
95
|
# particularly useful for serving assets from an SSL-protected page. The
|
@@ -190,7 +190,7 @@ module ActionView
|
|
190
190
|
return "" if source.blank?
|
191
191
|
return source if URI_REGEXP.match?(source)
|
192
192
|
|
193
|
-
tail, source = source[/([
|
193
|
+
tail, source = source[/([?#].+)$/], source.sub(/([?#].+)$/, "")
|
194
194
|
|
195
195
|
if extname = compute_asset_extname(source, options)
|
196
196
|
source = "#{source}#{extname}"
|
@@ -1,11 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "set"
|
4
|
-
require "active_support/core_ext/symbol/starts_ends_with"
|
5
4
|
|
6
5
|
module ActionView
|
7
6
|
# = Action View Atom Feed Helpers
|
8
|
-
module Helpers
|
7
|
+
module Helpers # :nodoc:
|
9
8
|
module AtomFeedHelper
|
10
9
|
# Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
|
11
10
|
# template languages).
|
@@ -127,7 +126,7 @@ module ActionView
|
|
127
126
|
end
|
128
127
|
end
|
129
128
|
|
130
|
-
class AtomBuilder
|
129
|
+
class AtomBuilder # :nodoc:
|
131
130
|
XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set
|
132
131
|
|
133
132
|
def initialize(xml)
|
@@ -161,7 +160,7 @@ module ActionView
|
|
161
160
|
end
|
162
161
|
end
|
163
162
|
|
164
|
-
class AtomFeedBuilder < AtomBuilder
|
163
|
+
class AtomFeedBuilder < AtomBuilder # :nodoc:
|
165
164
|
def initialize(xml, view, feed_options = {})
|
166
165
|
@xml, @view, @feed_options = xml, view, feed_options
|
167
166
|
end
|
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
module ActionView
|
4
4
|
# = Action View Cache Helper
|
5
|
-
module Helpers
|
5
|
+
module Helpers # :nodoc:
|
6
6
|
module CacheHelper
|
7
|
+
class UncacheableFragmentError < StandardError; end
|
8
|
+
|
7
9
|
# This helper exposes a method for caching fragments of a view
|
8
10
|
# rather than an entire action or page. This technique is useful
|
9
11
|
# caching pieces like menus, lists of new topics, static HTML
|
@@ -165,8 +167,10 @@ module ActionView
|
|
165
167
|
# expire the cache.
|
166
168
|
def cache(name = {}, options = {}, &block)
|
167
169
|
if controller.respond_to?(:perform_caching) && controller.perform_caching
|
168
|
-
|
169
|
-
|
170
|
+
CachingRegistry.track_caching do
|
171
|
+
name_options = options.slice(:skip_digest)
|
172
|
+
safe_concat(fragment_for(cache_fragment_name(name, **name_options), options, &block))
|
173
|
+
end
|
170
174
|
else
|
171
175
|
yield
|
172
176
|
end
|
@@ -174,6 +178,34 @@ module ActionView
|
|
174
178
|
nil
|
175
179
|
end
|
176
180
|
|
181
|
+
# Returns whether the current view fragment is within a +cache+ block.
|
182
|
+
#
|
183
|
+
# Useful when certain fragments aren't cacheable:
|
184
|
+
#
|
185
|
+
# <% cache project do %>
|
186
|
+
# <% raise StandardError, "Caching private data!" if caching? %>
|
187
|
+
# <% end %>
|
188
|
+
def caching?
|
189
|
+
CachingRegistry.caching?
|
190
|
+
end
|
191
|
+
|
192
|
+
# Raises +UncacheableFragmentError+ when called from within a +cache+ block.
|
193
|
+
#
|
194
|
+
# Useful to denote helper methods that can't participate in fragment caching:
|
195
|
+
#
|
196
|
+
# def project_name_with_time(project)
|
197
|
+
# uncacheable!
|
198
|
+
# "#{project.name} - #{Time.now}"
|
199
|
+
# end
|
200
|
+
#
|
201
|
+
# # Which will then raise if used within a +cache+ block:
|
202
|
+
# <% cache project do %>
|
203
|
+
# <%= project_name_with_time(project) %>
|
204
|
+
# <% end %>
|
205
|
+
def uncacheable!
|
206
|
+
raise UncacheableFragmentError, "can't be fragment cached" if caching?
|
207
|
+
end
|
208
|
+
|
177
209
|
# Cache fragments of a view if +condition+ is true
|
178
210
|
#
|
179
211
|
# <% cache_if admin?, project do %>
|
@@ -259,6 +291,22 @@ module ActionView
|
|
259
291
|
end
|
260
292
|
controller.write_fragment(name, fragment, options)
|
261
293
|
end
|
294
|
+
|
295
|
+
class CachingRegistry
|
296
|
+
extend ActiveSupport::PerThreadRegistry
|
297
|
+
|
298
|
+
attr_accessor :caching
|
299
|
+
alias caching? caching
|
300
|
+
|
301
|
+
def self.track_caching
|
302
|
+
caching_was = self.caching
|
303
|
+
self.caching = true
|
304
|
+
|
305
|
+
yield
|
306
|
+
ensure
|
307
|
+
self.caching = caching_was
|
308
|
+
end
|
309
|
+
end
|
262
310
|
end
|
263
311
|
end
|
264
312
|
end
|
@@ -4,7 +4,7 @@ require "active_support/core_ext/string/output_safety"
|
|
4
4
|
|
5
5
|
module ActionView
|
6
6
|
# = Action View Capture Helper
|
7
|
-
module Helpers
|
7
|
+
module Helpers # :nodoc:
|
8
8
|
# CaptureHelper exposes methods to let you extract generated markup which
|
9
9
|
# can be used in other parts of a template or layout file.
|
10
10
|
#
|
@@ -198,7 +198,7 @@ module ActionView
|
|
198
198
|
|
199
199
|
# Use an alternate output buffer for the duration of the block.
|
200
200
|
# Defaults to a new empty string.
|
201
|
-
def with_output_buffer(buf = nil)
|
201
|
+
def with_output_buffer(buf = nil) # :nodoc:
|
202
202
|
unless buf
|
203
203
|
buf = ActionView::OutputBuffer.new
|
204
204
|
if output_buffer && output_buffer.respond_to?(:encoding)
|
@@ -3,10 +3,10 @@
|
|
3
3
|
require "active_support/core_ext/module/attr_internal"
|
4
4
|
|
5
5
|
module ActionView
|
6
|
-
module Helpers
|
6
|
+
module Helpers # :nodoc:
|
7
7
|
# This module keeps all methods and behavior in ActionView
|
8
8
|
# that simply delegates to the controller.
|
9
|
-
module ControllerHelper
|
9
|
+
module ControllerHelper # :nodoc:
|
10
10
|
attr_internal :controller, :request
|
11
11
|
|
12
12
|
CONTROLLER_DELEGATES = [:request_forgery_protection_token, :params,
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActionView
|
4
4
|
# = Action View CSRF Helper
|
5
|
-
module Helpers
|
5
|
+
module Helpers # :nodoc:
|
6
6
|
module CsrfHelper
|
7
7
|
# Returns meta tags "csrf-param" and "csrf-token" with the name of the cross-site
|
8
8
|
# request forgery protection parameter and token, respectively.
|
@@ -9,7 +9,7 @@ require "active_support/core_ext/object/acts_like"
|
|
9
9
|
require "active_support/core_ext/object/with_options"
|
10
10
|
|
11
11
|
module ActionView
|
12
|
-
module Helpers
|
12
|
+
module Helpers # :nodoc:
|
13
13
|
# = Action View Date Helpers
|
14
14
|
#
|
15
15
|
# The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
|
@@ -140,7 +140,7 @@ module ActionView
|
|
140
140
|
minute_offset_for_leap_year = leap_years * 1440
|
141
141
|
# Discount the leap year days when calculating year distance.
|
142
142
|
# e.g. if there are 20 leap year days between 2 dates having the same day
|
143
|
-
# and month then
|
143
|
+
# and month then based on 365 days calculation
|
144
144
|
# the distance in years will come out to over 80 years when in written
|
145
145
|
# English it would read better as about 80 years.
|
146
146
|
minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
|
@@ -699,7 +699,7 @@ module ActionView
|
|
699
699
|
end
|
700
700
|
end
|
701
701
|
|
702
|
-
class DateTimeSelector
|
702
|
+
class DateTimeSelector # :nodoc:
|
703
703
|
include ActionView::Helpers::TagHelper
|
704
704
|
|
705
705
|
DEFAULT_PREFIX = "date"
|
@@ -1060,7 +1060,7 @@ module ActionView
|
|
1060
1060
|
(content_tag("select", select_html.html_safe, select_options) + "\n").html_safe
|
1061
1061
|
end
|
1062
1062
|
|
1063
|
-
# Builds the
|
1063
|
+
# Builds the CSS class value for the select element
|
1064
1064
|
# css_class_attribute(:year, 'date optional', { year: 'my-year' })
|
1065
1065
|
# => "date optional my-year"
|
1066
1066
|
def css_class_attribute(type, html_options_class, options) # :nodoc:
|
@@ -1101,8 +1101,7 @@ module ActionView
|
|
1101
1101
|
type: "hidden",
|
1102
1102
|
id: input_id_from_type(type),
|
1103
1103
|
name: input_name_from_type(type),
|
1104
|
-
value: value
|
1105
|
-
autocomplete: "off"
|
1104
|
+
value: value
|
1106
1105
|
}.merge!(@html_options.slice(:disabled))
|
1107
1106
|
select_options[:disabled] = "disabled" if @options[:disabled]
|
1108
1107
|
|
@@ -1126,7 +1125,7 @@ module ActionView
|
|
1126
1125
|
# Returns the id attribute for the input tag.
|
1127
1126
|
# => "post_written_on_1i"
|
1128
1127
|
def input_id_from_type(type)
|
1129
|
-
id = input_name_from_type(type).gsub(/([\[
|
1128
|
+
id = input_name_from_type(type).gsub(/([\[(])|(\]\[)/, "_").gsub(/[\])]/, "")
|
1130
1129
|
id = @options[:namespace] + "_" + id if @options[:namespace]
|
1131
1130
|
|
1132
1131
|
id
|
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "action_view/helpers/tag_helper"
|
4
|
+
|
3
5
|
module ActionView
|
4
6
|
# = Action View Debug Helper
|
5
7
|
#
|
6
8
|
# Provides a set of methods for making it easier to debug Rails objects.
|
7
|
-
module Helpers
|
9
|
+
module Helpers # :nodoc:
|
8
10
|
module DebugHelper
|
9
11
|
include TagHelper
|
10
12
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "cgi"
|
4
4
|
require "action_view/helpers/date_helper"
|
5
|
-
require "action_view/helpers/
|
5
|
+
require "action_view/helpers/url_helper"
|
6
6
|
require "action_view/helpers/form_tag_helper"
|
7
7
|
require "action_view/helpers/active_model_helper"
|
8
8
|
require "action_view/model_naming"
|
@@ -11,11 +11,10 @@ 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"
|
15
14
|
|
16
15
|
module ActionView
|
17
16
|
# = Action View Form Helpers
|
18
|
-
module Helpers
|
17
|
+
module Helpers # :nodoc:
|
19
18
|
# Form helpers are designed to make working with resources much easier
|
20
19
|
# compared to using vanilla HTML.
|
21
20
|
#
|
@@ -454,7 +453,7 @@ module ActionView
|
|
454
453
|
form_tag_with_body(html_options, output)
|
455
454
|
end
|
456
455
|
|
457
|
-
def apply_form_for_options!(record, object, options)
|
456
|
+
def apply_form_for_options!(record, object, options) # :nodoc:
|
458
457
|
object = convert_to_model(object)
|
459
458
|
|
460
459
|
as = options[:as]
|
@@ -604,10 +603,16 @@ module ActionView
|
|
604
603
|
# This is helpful when fragment-caching the form. Remote forms
|
605
604
|
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
606
605
|
# unnecessary unless you support browsers without JavaScript.
|
607
|
-
# * <tt>:local</tt> -
|
608
|
-
#
|
609
|
-
#
|
610
|
-
#
|
606
|
+
# * <tt>:local</tt> - Whether to use standard HTTP form submission.
|
607
|
+
# When set to <tt>true</tt>, the form is submitted via standard HTTP.
|
608
|
+
# When set to <tt>false</tt>, the form is submitted as a "remote form", which
|
609
|
+
# is handled by Rails UJS as an XHR. When unspecified, the behavior is derived
|
610
|
+
# from <tt>config.action_view.form_with_generates_remote_forms</tt> where the
|
611
|
+
# config's value is actually the inverse of what <tt>local</tt>'s value would be.
|
612
|
+
# As of Rails 6.1, that configuration option defaults to <tt>false</tt>
|
613
|
+
# (which has the equivalent effect of passing <tt>local: true</tt>).
|
614
|
+
# In previous versions of Rails, that configuration option defaults to
|
615
|
+
# <tt>true</tt> (the equivalent of passing <tt>local: false</tt>).
|
611
616
|
# * <tt>:skip_enforcing_utf8</tt> - If set to true, a hidden input with name
|
612
617
|
# utf8 is not output.
|
613
618
|
# * <tt>:builder</tt> - Override the object used to build the form.
|
@@ -1403,8 +1408,9 @@ module ActionView
|
|
1403
1408
|
# Returns a text_field of type "time".
|
1404
1409
|
#
|
1405
1410
|
# The default value is generated by trying to call +strftime+ with "%T.%L"
|
1406
|
-
# on the object's value.
|
1407
|
-
# by
|
1411
|
+
# on the object's value. If you pass <tt>include_seconds: false</tt>, it will be
|
1412
|
+
# formatted by trying to call +strftime+ with "%H:%M" on the object's value.
|
1413
|
+
# It is also possible to override this by passing the "value" option.
|
1408
1414
|
#
|
1409
1415
|
# === Options
|
1410
1416
|
# * Accepts same options as time_field_tag
|
@@ -1425,6 +1431,12 @@ module ActionView
|
|
1425
1431
|
# time_field("task", "started_at", min: "01:00:00")
|
1426
1432
|
# # => <input id="task_started_at" name="task[started_at]" type="time" min="01:00:00.000" />
|
1427
1433
|
#
|
1434
|
+
# By default, provided times will be formatted including seconds. You can render just the hour
|
1435
|
+
# and minute by passing <tt>include_seconds: false</tt>. Some browsers will render a simpler UI
|
1436
|
+
# if you exclude seconds in the timestamp format.
|
1437
|
+
#
|
1438
|
+
# time_field("task", "started_at", value: Time.now, include_seconds: false)
|
1439
|
+
# # => <input id="task_started_at" name="task[started_at]" type="time" value="01:00" />
|
1428
1440
|
def time_field(object_name, method, options = {})
|
1429
1441
|
Tags::TimeField.new(object_name, method, self, options).render
|
1430
1442
|
end
|
@@ -1685,6 +1697,47 @@ module ActionView
|
|
1685
1697
|
@index = options[:index] || options[:child_index]
|
1686
1698
|
end
|
1687
1699
|
|
1700
|
+
# Generate an HTML <tt>id</tt> attribute value.
|
1701
|
+
#
|
1702
|
+
# return the <tt><form></tt> element's <tt>id</tt> attribute.
|
1703
|
+
#
|
1704
|
+
# <%= form_for @post do |f| %>
|
1705
|
+
# <%# ... %>
|
1706
|
+
#
|
1707
|
+
# <% content_for :sticky_footer do %>
|
1708
|
+
# <%= form.button(form: f.id) %>
|
1709
|
+
# <% end %>
|
1710
|
+
# <% end %>
|
1711
|
+
#
|
1712
|
+
# In the example above, the <tt>:sticky_footer</tt> content area will
|
1713
|
+
# exist outside of the <tt><form></tt> element. By declaring the
|
1714
|
+
# <tt>form</tt> HTML attribute, we hint to the browser that the generated
|
1715
|
+
# <tt><button></tt> element should be treated as the <tt><form></tt>
|
1716
|
+
# element's submit button, regardless of where it exists in the DOM.
|
1717
|
+
def id
|
1718
|
+
options.dig(:html, :id)
|
1719
|
+
end
|
1720
|
+
|
1721
|
+
# Generate an HTML <tt>id</tt> attribute value for the given field
|
1722
|
+
#
|
1723
|
+
# Return the value generated by the <tt>FormBuilder</tt> for the given
|
1724
|
+
# attribute name.
|
1725
|
+
#
|
1726
|
+
# <%= form_for @post do |f| %>
|
1727
|
+
# <%= f.label :title %>
|
1728
|
+
# <%= f.text_field :title, aria: { describedby: f.field_id(:title, :error) } %>
|
1729
|
+
# <%= tag.span("is blank", id: f.field_id(:title, :error) %>
|
1730
|
+
# <% end %>
|
1731
|
+
#
|
1732
|
+
# In the example above, the <tt><input type="text"></tt> element built by
|
1733
|
+
# the call to <tt>FormBuilder#text_field</tt> declares an
|
1734
|
+
# <tt>aria-describedby</tt> attribute referencing the <tt><span></tt>
|
1735
|
+
# element, sharing a common <tt>id</tt> root (<tt>post_title</tt>, in this
|
1736
|
+
# case).
|
1737
|
+
def field_id(method, *suffixes, index: @index)
|
1738
|
+
@template.field_id(@object_name, method, *suffixes, index: index)
|
1739
|
+
end
|
1740
|
+
|
1688
1741
|
##
|
1689
1742
|
# :method: text_field
|
1690
1743
|
#
|
@@ -2386,7 +2439,7 @@ module ActionView
|
|
2386
2439
|
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
|
2387
2440
|
# shown.
|
2388
2441
|
#
|
2389
|
-
# Using this method inside a +
|
2442
|
+
# Using this method inside a +form_with+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
|
2390
2443
|
#
|
2391
2444
|
# ==== Options
|
2392
2445
|
# * Creates standard HTML attributes for the tag.
|
@@ -2505,6 +2558,11 @@ module ActionView
|
|
2505
2558
|
value = @template.capture { yield(value) }
|
2506
2559
|
end
|
2507
2560
|
|
2561
|
+
formmethod = options[:formmethod]
|
2562
|
+
if formmethod.present? && !/post|get/i.match?(formmethod) && !options.key?(:name) && !options.key?(:value)
|
2563
|
+
options.merge! formmethod: :post, name: "_method", value: formmethod
|
2564
|
+
end
|
2565
|
+
|
2508
2566
|
@template.button_tag(value, options)
|
2509
2567
|
end
|
2510
2568
|
|
@@ -2565,7 +2623,9 @@ module ActionView
|
|
2565
2623
|
else
|
2566
2624
|
options[:child_index] = nested_child_index(name)
|
2567
2625
|
end
|
2568
|
-
|
2626
|
+
if content = fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
|
2627
|
+
output << content
|
2628
|
+
end
|
2569
2629
|
end
|
2570
2630
|
output
|
2571
2631
|
elsif association
|