actionview 6.0.0.beta1 → 6.1.4
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 +273 -119
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -3
- data/lib/action_view/base.rb +81 -15
- data/lib/action_view/cache_expiry.rb +52 -0
- data/lib/action_view/context.rb +0 -5
- data/lib/action_view/dependency_tracker.rb +10 -4
- data/lib/action_view/digestor.rb +11 -19
- data/lib/action_view/flows.rb +0 -1
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/active_model_helper.rb +0 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +62 -22
- data/lib/action_view/helpers/asset_url_helper.rb +6 -4
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -1
- data/lib/action_view/helpers/cache_helper.rb +16 -23
- data/lib/action_view/helpers/csp_helper.rb +4 -2
- data/lib/action_view/helpers/date_helper.rb +5 -6
- data/lib/action_view/helpers/form_helper.rb +70 -34
- data/lib/action_view/helpers/form_options_helper.rb +10 -18
- data/lib/action_view/helpers/form_tag_helper.rb +12 -9
- data/lib/action_view/helpers/javascript_helper.rb +7 -5
- data/lib/action_view/helpers/number_helper.rb +9 -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 +10 -16
- data/lib/action_view/helpers/tag_helper.rb +94 -19
- data/lib/action_view/helpers/tags/base.rb +10 -7
- 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 +0 -1
- 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/week_field.rb +1 -2
- data/lib/action_view/helpers/text_helper.rb +2 -3
- data/lib/action_view/helpers/translation_helper.rb +98 -51
- data/lib/action_view/helpers/url_helper.rb +124 -16
- data/lib/action_view/layouts.rb +8 -10
- data/lib/action_view/log_subscriber.rb +26 -11
- data/lib/action_view/lookup_context.rb +59 -31
- data/lib/action_view/path_set.rb +3 -12
- data/lib/action_view/railtie.rb +39 -41
- data/lib/action_view/record_identifier.rb +0 -1
- 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/collection_caching.rb +35 -29
- data/lib/action_view/renderer/partial_renderer.rb +21 -273
- data/lib/action_view/renderer/renderer.rb +59 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +9 -7
- data/lib/action_view/renderer/template_renderer.rb +35 -27
- data/lib/action_view/rendering.rb +49 -29
- data/lib/action_view/routing_url_for.rb +1 -1
- data/lib/action_view/template/error.rb +30 -15
- data/lib/action_view/template/handlers/builder.rb +2 -2
- data/lib/action_view/template/handlers/erb/erubi.rb +15 -9
- data/lib/action_view/template/handlers/erb.rb +14 -19
- 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/handlers.rb +1 -1
- 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 +141 -140
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +2 -3
- data/lib/action_view/template.rb +49 -75
- data/lib/action_view/test_case.rb +20 -28
- 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/action_view.rb +7 -2
- data/lib/assets/compiled/rails-ujs.js +25 -16
- metadata +30 -18
@@ -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
|
@@ -611,8 +604,10 @@ module ActionView
|
|
611
604
|
# This is helpful when fragment-caching the form. Remote forms
|
612
605
|
# get the authenticity token from the <tt>meta</tt> tag, so embedding is
|
613
606
|
# unnecessary unless you support browsers without JavaScript.
|
614
|
-
# * <tt>:local</tt> - By default form submits
|
615
|
-
#
|
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>.
|
616
611
|
# * <tt>:skip_enforcing_utf8</tt> - If set to true, a hidden input with name
|
617
612
|
# utf8 is not output.
|
618
613
|
# * <tt>:builder</tt> - Override the object used to build the form.
|
@@ -739,7 +734,7 @@ module ActionView
|
|
739
734
|
# def labelled_form_with(**options, &block)
|
740
735
|
# form_with(**options.merge(builder: LabellingFormBuilder), &block)
|
741
736
|
# end
|
742
|
-
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)
|
743
738
|
options[:allow_method_names_outside_object] = true
|
744
739
|
options[:skip_default_ids] = !form_with_generates_ids
|
745
740
|
|
@@ -752,13 +747,13 @@ module ActionView
|
|
752
747
|
|
753
748
|
if block_given?
|
754
749
|
builder = instantiate_builder(scope, model, options)
|
755
|
-
output = capture(builder, &
|
750
|
+
output = capture(builder, &block)
|
756
751
|
options[:multipart] ||= builder.multipart?
|
757
752
|
|
758
|
-
html_options = html_options_for_form_with(url, model, options)
|
753
|
+
html_options = html_options_for_form_with(url, model, **options)
|
759
754
|
form_tag_with_body(html_options, output)
|
760
755
|
else
|
761
|
-
html_options = html_options_for_form_with(url, model, options)
|
756
|
+
html_options = html_options_for_form_with(url, model, **options)
|
762
757
|
form_tag_html(html_options)
|
763
758
|
end
|
764
759
|
end
|
@@ -888,7 +883,7 @@ module ActionView
|
|
888
883
|
#
|
889
884
|
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
|
890
885
|
# with a value that evaluates to +true+, you will destroy the associated
|
891
|
-
# model (
|
886
|
+
# model (e.g. 1, '1', true, or 'true'):
|
892
887
|
#
|
893
888
|
# <%= form_for @person do |person_form| %>
|
894
889
|
# ...
|
@@ -977,7 +972,7 @@ module ActionView
|
|
977
972
|
# This will allow you to specify which models to destroy in the
|
978
973
|
# attributes hash by adding a form element for the <tt>_destroy</tt>
|
979
974
|
# parameter with a value that evaluates to +true+
|
980
|
-
# (
|
975
|
+
# (e.g. 1, '1', true, or 'true'):
|
981
976
|
#
|
982
977
|
# <%= form_for @person do |person_form| %>
|
983
978
|
# ...
|
@@ -1110,6 +1105,16 @@ module ActionView
|
|
1110
1105
|
# label(:post, :privacy, "Public Post", value: "public")
|
1111
1106
|
# # => <label for="post_privacy_public">Public Post</label>
|
1112
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
|
+
#
|
1113
1118
|
# label(:post, :terms) do
|
1114
1119
|
# raw('Accept <a href="/terms">Terms</a>.')
|
1115
1120
|
# end
|
@@ -1668,8 +1673,8 @@ module ActionView
|
|
1668
1673
|
|
1669
1674
|
convert_to_legacy_options(@options)
|
1670
1675
|
|
1671
|
-
if @object_name
|
1672
|
-
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)
|
1673
1678
|
@auto_index = object.to_param
|
1674
1679
|
else
|
1675
1680
|
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
|
@@ -1792,7 +1797,7 @@ module ActionView
|
|
1792
1797
|
# Wraps ActionView::Helpers::FormHelper#time_field for form builders:
|
1793
1798
|
#
|
1794
1799
|
# <%= form_with model: @user do |f| %>
|
1795
|
-
# <%= f.time_field :
|
1800
|
+
# <%= f.time_field :born_at %>
|
1796
1801
|
# <% end %>
|
1797
1802
|
#
|
1798
1803
|
# Please refer to the documentation of the base helper for details.
|
@@ -1904,8 +1909,8 @@ module ActionView
|
|
1904
1909
|
(field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
|
1905
1910
|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
1906
1911
|
def #{selector}(method, options = {}) # def text_field(method, options = {})
|
1907
|
-
@template.
|
1908
|
-
#{selector.inspect}, #
|
1912
|
+
@template.public_send( # @template.public_send(
|
1913
|
+
#{selector.inspect}, # :text_field,
|
1909
1914
|
@object_name, # @object_name,
|
1910
1915
|
method, # method,
|
1911
1916
|
objectify_options(options)) # objectify_options(options))
|
@@ -2038,7 +2043,7 @@ module ActionView
|
|
2038
2043
|
#
|
2039
2044
|
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
|
2040
2045
|
# with a value that evaluates to +true+, you will destroy the associated
|
2041
|
-
# model (
|
2046
|
+
# model (e.g. 1, '1', true, or 'true'):
|
2042
2047
|
#
|
2043
2048
|
# <%= form_for @person do |person_form| %>
|
2044
2049
|
# ...
|
@@ -2127,7 +2132,7 @@ module ActionView
|
|
2127
2132
|
# This will allow you to specify which models to destroy in the
|
2128
2133
|
# attributes hash by adding a form element for the <tt>_destroy</tt>
|
2129
2134
|
# parameter with a value that evaluates to +true+
|
2130
|
-
# (
|
2135
|
+
# (e.g. 1, '1', true, or 'true'):
|
2131
2136
|
#
|
2132
2137
|
# <%= form_for @person do |person_form| %>
|
2133
2138
|
# ...
|
@@ -2174,15 +2179,14 @@ module ActionView
|
|
2174
2179
|
index = if options.has_key?(:index)
|
2175
2180
|
options[:index]
|
2176
2181
|
elsif defined?(@auto_index)
|
2177
|
-
object_name = object_name.to_s.
|
2182
|
+
object_name = object_name.to_s.delete_suffix("[]")
|
2178
2183
|
@auto_index
|
2179
2184
|
end
|
2180
2185
|
|
2181
2186
|
record_name = if index
|
2182
2187
|
"#{object_name}[#{index}][#{record_name}]"
|
2183
|
-
elsif record_name.
|
2184
|
-
record_name
|
2185
|
-
"#{object_name}#{record_name}"
|
2188
|
+
elsif record_name.end_with?("[]")
|
2189
|
+
"#{object_name}[#{record_name[0..-3]}][#{record_object.id}]"
|
2186
2190
|
else
|
2187
2191
|
"#{object_name}[#{record_name}]"
|
2188
2192
|
end
|
@@ -2245,6 +2249,24 @@ module ActionView
|
|
2245
2249
|
# label(:privacy, "Public Post", value: "public")
|
2246
2250
|
# # => <label for="post_privacy_public">Public Post</label>
|
2247
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
|
+
#
|
2248
2270
|
# label(:terms) do
|
2249
2271
|
# raw('Accept <a href="/terms">Terms</a>.')
|
2250
2272
|
# end
|
@@ -2468,10 +2490,22 @@ module ActionView
|
|
2468
2490
|
# # <strong>Ask me!</strong>
|
2469
2491
|
# # </button>
|
2470
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
|
+
#
|
2471
2500
|
def button(value = nil, options = {}, &block)
|
2472
2501
|
value, options = nil, value if value.is_a?(Hash)
|
2473
2502
|
value ||= submit_default_value
|
2474
|
-
|
2503
|
+
|
2504
|
+
if block_given?
|
2505
|
+
value = @template.capture { yield(value) }
|
2506
|
+
end
|
2507
|
+
|
2508
|
+
@template.button_tag(value, options)
|
2475
2509
|
end
|
2476
2510
|
|
2477
2511
|
def emitted_hidden_id? # :nodoc:
|
@@ -2480,7 +2514,9 @@ module ActionView
|
|
2480
2514
|
|
2481
2515
|
private
|
2482
2516
|
def objectify_options(options)
|
2483
|
-
@default_options.merge(options
|
2517
|
+
result = @default_options.merge(options)
|
2518
|
+
result[:object] = @object
|
2519
|
+
result
|
2484
2520
|
end
|
2485
2521
|
|
2486
2522
|
def submit_default_value
|
@@ -2515,9 +2551,9 @@ module ActionView
|
|
2515
2551
|
association = convert_to_model(association)
|
2516
2552
|
|
2517
2553
|
if association.respond_to?(:persisted?)
|
2518
|
-
association = [association] if @object.
|
2554
|
+
association = [association] if @object.public_send(association_name).respond_to?(:to_ary)
|
2519
2555
|
elsif !association.respond_to?(:to_ary)
|
2520
|
-
association = @object.
|
2556
|
+
association = @object.public_send(association_name)
|
2521
2557
|
end
|
2522
2558
|
|
2523
2559
|
if association.respond_to?(:to_ary)
|
@@ -21,7 +21,7 @@ module ActionView
|
|
21
21
|
# could become:
|
22
22
|
#
|
23
23
|
# <select name="post[category]" id="post_category">
|
24
|
-
# <option value=""></option>
|
24
|
+
# <option value="" label=" "></option>
|
25
25
|
# <option value="joke">joke</option>
|
26
26
|
# <option value="poem">poem</option>
|
27
27
|
# </select>
|
@@ -74,7 +74,6 @@ module ActionView
|
|
74
74
|
# could become:
|
75
75
|
#
|
76
76
|
# <select name="post[category]" id="post_category">
|
77
|
-
# <option value=""></option>
|
78
77
|
# <option value="joke">joke</option>
|
79
78
|
# <option value="poem">poem</option>
|
80
79
|
# <option disabled="disabled" value="restricted">restricted</option>
|
@@ -112,7 +111,7 @@ module ActionView
|
|
112
111
|
# would become:
|
113
112
|
#
|
114
113
|
# <select name="post[person_id]" id="post_person_id">
|
115
|
-
# <option value=""></option>
|
114
|
+
# <option value="" label=" "></option>
|
116
115
|
# <option value="1" selected="selected">David</option>
|
117
116
|
# <option value="2">Eileen</option>
|
118
117
|
# <option value="3">Rafael</option>
|
@@ -143,7 +142,7 @@ module ActionView
|
|
143
142
|
#
|
144
143
|
# The HTML specification says when +multiple+ parameter passed to select and all options got deselected
|
145
144
|
# web browsers do not send any value to server. Unfortunately this introduces a gotcha:
|
146
|
-
# if
|
145
|
+
# if a +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
|
147
146
|
# the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
|
148
147
|
# any mass-assignment idiom like
|
149
148
|
#
|
@@ -566,9 +565,10 @@ module ActionView
|
|
566
565
|
# an ActiveSupport::TimeZone.
|
567
566
|
#
|
568
567
|
# By default, +model+ is the ActiveSupport::TimeZone constant (which can
|
569
|
-
# be obtained in Active Record as a value object). The
|
570
|
-
#
|
571
|
-
#
|
568
|
+
# be obtained in Active Record as a value object). The +model+ parameter
|
569
|
+
# must respond to +all+ and return an array of objects that represent time
|
570
|
+
# zones; each object must respond to +name+. If a Regexp is given it will
|
571
|
+
# attempt to match the zones using <code>match?</code> method.
|
572
572
|
#
|
573
573
|
# NOTE: Only the option tags are returned, you have to wrap this call in
|
574
574
|
# a regular HTML select tag.
|
@@ -580,7 +580,7 @@ module ActionView
|
|
580
580
|
|
581
581
|
if priority_zones
|
582
582
|
if priority_zones.is_a?(Regexp)
|
583
|
-
priority_zones = zones.select { |z| z
|
583
|
+
priority_zones = zones.select { |z| z.match?(priority_zones) }
|
584
584
|
end
|
585
585
|
|
586
586
|
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
|
@@ -794,7 +794,7 @@ module ActionView
|
|
794
794
|
def extract_values_from_collection(collection, value_method, selected)
|
795
795
|
if selected.is_a?(Proc)
|
796
796
|
collection.map do |element|
|
797
|
-
|
797
|
+
element.public_send(value_method) if selected.call(element)
|
798
798
|
end.compact
|
799
799
|
else
|
800
800
|
selected
|
@@ -802,15 +802,7 @@ module ActionView
|
|
802
802
|
end
|
803
803
|
|
804
804
|
def value_for_collection(item, value)
|
805
|
-
value.respond_to?(:call) ? value.call(item) :
|
806
|
-
end
|
807
|
-
|
808
|
-
def public_or_deprecated_send(item, value)
|
809
|
-
item.public_send(value)
|
810
|
-
rescue NoMethodError
|
811
|
-
raise unless item.respond_to?(value, true) && !item.respond_to?(value)
|
812
|
-
ActiveSupport::Deprecation.warn "Using private methods from view helpers is deprecated (calling private #{item.class}##{value})"
|
813
|
-
item.send(value)
|
805
|
+
value.respond_to?(:call) ? value.call(item) : item.public_send(value)
|
814
806
|
end
|
815
807
|
|
816
808
|
def prompt_text(prompt)
|
@@ -4,6 +4,7 @@ require "cgi"
|
|
4
4
|
require "action_view/helpers/tag_helper"
|
5
5
|
require "active_support/core_ext/string/output_safety"
|
6
6
|
require "active_support/core_ext/module/attribute_accessors"
|
7
|
+
require "active_support/core_ext/symbol/starts_ends_with"
|
7
8
|
|
8
9
|
module ActionView
|
9
10
|
# = Action View Form Tag Helpers
|
@@ -24,7 +25,7 @@ module ActionView
|
|
24
25
|
|
25
26
|
mattr_accessor :default_enforce_utf8, default: true
|
26
27
|
|
27
|
-
# Starts a form tag that points the action to a
|
28
|
+
# Starts a form tag that points the action to a URL configured with <tt>url_for_options</tt> just like
|
28
29
|
# ActionController::Base#url_for. The method for the form defaults to POST.
|
29
30
|
#
|
30
31
|
# ==== Options
|
@@ -134,10 +135,11 @@ module ActionView
|
|
134
135
|
# # <option selected="selected">MasterCard</option></select>
|
135
136
|
def select_tag(name, option_tags = nil, options = {})
|
136
137
|
option_tags ||= ""
|
137
|
-
html_name = (options[:multiple] == true && !name.
|
138
|
+
html_name = (options[:multiple] == true && !name.end_with?("[]")) ? "#{name}[]" : name
|
138
139
|
|
139
140
|
if options.include?(:include_blank)
|
140
|
-
include_blank = options
|
141
|
+
include_blank = options[:include_blank]
|
142
|
+
options = options.except(:include_blank)
|
141
143
|
options_for_blank_options_tag = { value: "" }
|
142
144
|
|
143
145
|
if include_blank == true
|
@@ -165,6 +167,8 @@ module ActionView
|
|
165
167
|
# * <tt>:size</tt> - The number of visible characters that will fit in the input.
|
166
168
|
# * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
|
167
169
|
# * <tt>:placeholder</tt> - The text contained in the field by default which is removed when the field receives focus.
|
170
|
+
# If set to true, use a translation is found in the current I18n locale
|
171
|
+
# (through helpers.placeholders.<modelname>.<attribute>).
|
168
172
|
# * Any other key creates standard HTML attributes for the tag.
|
169
173
|
#
|
170
174
|
# ==== Examples
|
@@ -893,16 +897,15 @@ module ActionView
|
|
893
897
|
end
|
894
898
|
|
895
899
|
def set_default_disable_with(value, tag_options)
|
896
|
-
|
897
|
-
data = tag_options["data"]
|
900
|
+
data = tag_options.fetch("data", {})
|
898
901
|
|
899
|
-
|
902
|
+
if tag_options["data-disable-with"] == false || data["disable_with"] == false
|
903
|
+
data.delete("disable_with")
|
904
|
+
elsif ActionView::Base.automatically_disable_submit_tag
|
900
905
|
disable_with_text = tag_options["data-disable-with"]
|
901
|
-
disable_with_text ||= data["disable_with"]
|
906
|
+
disable_with_text ||= data["disable_with"]
|
902
907
|
disable_with_text ||= value.to_s.clone
|
903
908
|
tag_options.deep_merge!("data" => { "disable_with" => disable_with_text })
|
904
|
-
else
|
905
|
-
data.delete("disable_with") if data
|
906
909
|
end
|
907
910
|
|
908
911
|
tag_options.delete("data-disable-with")
|
@@ -12,7 +12,9 @@ module ActionView
|
|
12
12
|
"\n" => '\n',
|
13
13
|
"\r" => '\n',
|
14
14
|
'"' => '\\"',
|
15
|
-
"'" => "\\'"
|
15
|
+
"'" => "\\'",
|
16
|
+
"`" => "\\`",
|
17
|
+
"$" => "\\$"
|
16
18
|
}
|
17
19
|
|
18
20
|
JS_ESCAPE_MAP[(+"\342\200\250").force_encoding(Encoding::UTF_8).encode!] = "
"
|
@@ -29,7 +31,7 @@ module ActionView
|
|
29
31
|
if javascript.empty?
|
30
32
|
result = ""
|
31
33
|
else
|
32
|
-
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u
|
34
|
+
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
|
33
35
|
end
|
34
36
|
javascript.html_safe? ? result.html_safe : result
|
35
37
|
end
|
@@ -49,10 +51,10 @@ module ActionView
|
|
49
51
|
# +html_options+ may be a hash of attributes for the <tt>\<script></tt>
|
50
52
|
# tag.
|
51
53
|
#
|
52
|
-
# javascript_tag "alert('All is good')",
|
54
|
+
# javascript_tag "alert('All is good')", type: 'application/javascript'
|
53
55
|
#
|
54
56
|
# Returns:
|
55
|
-
# <script
|
57
|
+
# <script type="application/javascript">
|
56
58
|
# //<![CDATA[
|
57
59
|
# alert('All is good')
|
58
60
|
# //]]>
|
@@ -61,7 +63,7 @@ module ActionView
|
|
61
63
|
# Instead of passing the content as an argument, you can also use a block
|
62
64
|
# in which case, you pass your +html_options+ as the first parameter.
|
63
65
|
#
|
64
|
-
# <%= javascript_tag
|
66
|
+
# <%= javascript_tag type: 'application/javascript' do -%>
|
65
67
|
# alert('All is good')
|
66
68
|
# <% end -%>
|
67
69
|
#
|
@@ -57,7 +57,7 @@ module ActionView
|
|
57
57
|
#
|
58
58
|
# number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
|
59
59
|
# # => "(755) 6123-4567"
|
60
|
-
# number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
|
60
|
+
# number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
|
61
61
|
# # => "133-1234-5678"
|
62
62
|
def number_to_phone(number, options = {})
|
63
63
|
return unless number
|
@@ -114,6 +114,8 @@ module ActionView
|
|
114
114
|
#
|
115
115
|
# number_to_currency("123a456", raise: true) # => InvalidNumberError
|
116
116
|
#
|
117
|
+
# number_to_currency(-0.456789, precision: 0)
|
118
|
+
# # => "$0"
|
117
119
|
# number_to_currency(-1234567890.50, negative_format: "(%u%n)")
|
118
120
|
# # => ($1,234,567,890.50)
|
119
121
|
# number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "")
|
@@ -251,7 +253,7 @@ module ActionView
|
|
251
253
|
end
|
252
254
|
|
253
255
|
# Formats the bytes in +number+ into a more understandable
|
254
|
-
# representation (e.g., giving it 1500 yields 1.
|
256
|
+
# representation (e.g., giving it 1500 yields 1.46 KB). This
|
255
257
|
# method is useful for reporting file sizes to users. You can
|
256
258
|
# customize the format in the +options+ hash.
|
257
259
|
#
|
@@ -297,7 +299,7 @@ module ActionView
|
|
297
299
|
end
|
298
300
|
|
299
301
|
# Pretty prints (formats and approximates) a number in a way it
|
300
|
-
# is more readable by humans (
|
302
|
+
# is more readable by humans (e.g.: 1200000000 becomes "1.2
|
301
303
|
# Billion"). This is useful for numbers that can get very large
|
302
304
|
# (and too hard to read).
|
303
305
|
#
|
@@ -305,7 +307,7 @@ module ActionView
|
|
305
307
|
# size.
|
306
308
|
#
|
307
309
|
# You can also define your own unit-quantifier names if you want
|
308
|
-
# to use other decimal units (
|
310
|
+
# to use other decimal units (e.g.: 1500 becomes "1.5
|
309
311
|
# kilometers", 0.150 becomes "150 milliliters", etc). You may
|
310
312
|
# define a wide range of unit quantifiers, even fractional ones
|
311
313
|
# (centi, deci, mili, etc).
|
@@ -403,7 +405,6 @@ module ActionView
|
|
403
405
|
end
|
404
406
|
|
405
407
|
private
|
406
|
-
|
407
408
|
def delegate_number_helper_method(method, number, options)
|
408
409
|
return unless number
|
409
410
|
options = escape_unsafe_options(options.symbolize_keys)
|
@@ -424,9 +425,9 @@ module ActionView
|
|
424
425
|
end
|
425
426
|
|
426
427
|
def escape_units(units)
|
427
|
-
|
428
|
-
|
429
|
-
end
|
428
|
+
units.transform_values do |v|
|
429
|
+
ERB::Util.html_escape(v)
|
430
|
+
end
|
430
431
|
end
|
431
432
|
|
432
433
|
def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
|
@@ -38,7 +38,7 @@ module ActionView #:nodoc:
|
|
38
38
|
|
39
39
|
# Converts the array to a comma-separated sentence where the last element is
|
40
40
|
# joined by the connector word. This is the html_safe-aware version of
|
41
|
-
# ActiveSupport's {Array#to_sentence}[
|
41
|
+
# ActiveSupport's {Array#to_sentence}[https://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
|
42
42
|
#
|
43
43
|
def to_sentence(array, options = {})
|
44
44
|
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
|
@@ -22,18 +22,28 @@ module ActionView
|
|
22
22
|
# type of <tt>text/plain</tt> from <tt>ActionDispatch::Response</tt>
|
23
23
|
# object.
|
24
24
|
#
|
25
|
-
# If no options hash is passed or
|
26
|
-
#
|
25
|
+
# If no <tt>options</tt> hash is passed or if <tt>:update</tt> is specified, then:
|
26
|
+
#
|
27
|
+
# If an object responding to +render_in+ is passed, +render_in+ is called on the object,
|
28
|
+
# passing in the current view context.
|
29
|
+
#
|
30
|
+
# Otherwise, a partial is rendered using the second parameter as the locals hash.
|
27
31
|
def render(options = {}, locals = {}, &block)
|
28
32
|
case options
|
29
33
|
when Hash
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
+
in_rendering_context(options) do |renderer|
|
35
|
+
if block_given?
|
36
|
+
view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
|
37
|
+
else
|
38
|
+
view_renderer.render(self, options)
|
39
|
+
end
|
34
40
|
end
|
35
41
|
else
|
36
|
-
|
42
|
+
if options.respond_to?(:render_in)
|
43
|
+
options.render_in(self, &block)
|
44
|
+
else
|
45
|
+
view_renderer.render_partial(self, partial: options, locals: locals, &block)
|
46
|
+
end
|
37
47
|
end
|
38
48
|
end
|
39
49
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/object/try"
|
4
3
|
require "rails-html-sanitizer"
|
5
4
|
|
6
5
|
module ActionView
|
@@ -17,7 +16,7 @@ module ActionView
|
|
17
16
|
# ASCII, and hex character references to work around these protocol filters.
|
18
17
|
# All special characters will be escaped.
|
19
18
|
#
|
20
|
-
# The default sanitizer is Rails::Html::
|
19
|
+
# The default sanitizer is Rails::Html::SafeListSanitizer. See {Rails HTML
|
21
20
|
# Sanitizers}[https://github.com/rails/rails-html-sanitizer] for more information.
|
22
21
|
#
|
23
22
|
# Custom sanitization rules can also be provided.
|
@@ -80,12 +79,12 @@ module ActionView
|
|
80
79
|
# config.action_view.sanitized_allowed_tags = ['strong', 'em', 'a']
|
81
80
|
# config.action_view.sanitized_allowed_attributes = ['href', 'title']
|
82
81
|
def sanitize(html, options = {})
|
83
|
-
self.class.
|
82
|
+
self.class.safe_list_sanitizer.sanitize(html, options)&.html_safe
|
84
83
|
end
|
85
84
|
|
86
85
|
# Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
|
87
86
|
def sanitize_css(style)
|
88
|
-
self.class.
|
87
|
+
self.class.safe_list_sanitizer.sanitize_css(style)
|
89
88
|
end
|
90
89
|
|
91
90
|
# Strips all HTML tags from +html+, including comments and special characters.
|
@@ -123,20 +122,18 @@ module ActionView
|
|
123
122
|
end
|
124
123
|
|
125
124
|
module ClassMethods #:nodoc:
|
126
|
-
attr_writer :full_sanitizer, :link_sanitizer, :
|
125
|
+
attr_writer :full_sanitizer, :link_sanitizer, :safe_list_sanitizer
|
127
126
|
|
128
|
-
# Vendors the full, link and white list sanitizers.
|
129
|
-
# Provided strictly for compatibility and can be removed in Rails 6.
|
130
127
|
def sanitizer_vendor
|
131
128
|
Rails::Html::Sanitizer
|
132
129
|
end
|
133
130
|
|
134
131
|
def sanitized_allowed_tags
|
135
|
-
sanitizer_vendor.
|
132
|
+
sanitizer_vendor.safe_list_sanitizer.allowed_tags
|
136
133
|
end
|
137
134
|
|
138
135
|
def sanitized_allowed_attributes
|
139
|
-
sanitizer_vendor.
|
136
|
+
sanitizer_vendor.safe_list_sanitizer.allowed_attributes
|
140
137
|
end
|
141
138
|
|
142
139
|
# Gets the Rails::Html::FullSanitizer instance used by +strip_tags+. Replace with
|
@@ -145,7 +142,6 @@ module ActionView
|
|
145
142
|
# class Application < Rails::Application
|
146
143
|
# config.action_view.full_sanitizer = MySpecialSanitizer.new
|
147
144
|
# end
|
148
|
-
#
|
149
145
|
def full_sanitizer
|
150
146
|
@full_sanitizer ||= sanitizer_vendor.full_sanitizer.new
|
151
147
|
end
|
@@ -156,20 +152,18 @@ module ActionView
|
|
156
152
|
# class Application < Rails::Application
|
157
153
|
# config.action_view.link_sanitizer = MySpecialSanitizer.new
|
158
154
|
# end
|
159
|
-
#
|
160
155
|
def link_sanitizer
|
161
156
|
@link_sanitizer ||= sanitizer_vendor.link_sanitizer.new
|
162
157
|
end
|
163
158
|
|
164
|
-
# Gets the Rails::Html::
|
159
|
+
# Gets the Rails::Html::SafeListSanitizer instance used by sanitize and +sanitize_css+.
|
165
160
|
# Replace with any object that responds to +sanitize+.
|
166
161
|
#
|
167
162
|
# class Application < Rails::Application
|
168
|
-
# config.action_view.
|
163
|
+
# config.action_view.safe_list_sanitizer = MySpecialSanitizer.new
|
169
164
|
# end
|
170
|
-
|
171
|
-
|
172
|
-
@white_list_sanitizer ||= sanitizer_vendor.white_list_sanitizer.new
|
165
|
+
def safe_list_sanitizer
|
166
|
+
@safe_list_sanitizer ||= sanitizer_vendor.safe_list_sanitizer.new
|
173
167
|
end
|
174
168
|
end
|
175
169
|
end
|