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.

Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +273 -119
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -3
  5. data/lib/action_view/base.rb +81 -15
  6. data/lib/action_view/cache_expiry.rb +52 -0
  7. data/lib/action_view/context.rb +0 -5
  8. data/lib/action_view/dependency_tracker.rb +10 -4
  9. data/lib/action_view/digestor.rb +11 -19
  10. data/lib/action_view/flows.rb +0 -1
  11. data/lib/action_view/gem_version.rb +3 -3
  12. data/lib/action_view/helpers/active_model_helper.rb +0 -1
  13. data/lib/action_view/helpers/asset_tag_helper.rb +62 -22
  14. data/lib/action_view/helpers/asset_url_helper.rb +6 -4
  15. data/lib/action_view/helpers/atom_feed_helper.rb +2 -1
  16. data/lib/action_view/helpers/cache_helper.rb +16 -23
  17. data/lib/action_view/helpers/csp_helper.rb +4 -2
  18. data/lib/action_view/helpers/date_helper.rb +5 -6
  19. data/lib/action_view/helpers/form_helper.rb +70 -34
  20. data/lib/action_view/helpers/form_options_helper.rb +10 -18
  21. data/lib/action_view/helpers/form_tag_helper.rb +12 -9
  22. data/lib/action_view/helpers/javascript_helper.rb +7 -5
  23. data/lib/action_view/helpers/number_helper.rb +9 -8
  24. data/lib/action_view/helpers/output_safety_helper.rb +1 -1
  25. data/lib/action_view/helpers/rendering_helper.rb +17 -7
  26. data/lib/action_view/helpers/sanitize_helper.rb +10 -16
  27. data/lib/action_view/helpers/tag_helper.rb +94 -19
  28. data/lib/action_view/helpers/tags/base.rb +10 -7
  29. data/lib/action_view/helpers/tags/check_box.rb +0 -1
  30. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
  31. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
  32. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
  33. data/lib/action_view/helpers/tags/color_field.rb +0 -1
  34. data/lib/action_view/helpers/tags/date_field.rb +1 -2
  35. data/lib/action_view/helpers/tags/date_select.rb +2 -3
  36. data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
  37. data/lib/action_view/helpers/tags/datetime_local_field.rb +1 -2
  38. data/lib/action_view/helpers/tags/label.rb +4 -1
  39. data/lib/action_view/helpers/tags/month_field.rb +1 -2
  40. data/lib/action_view/helpers/tags/radio_button.rb +0 -1
  41. data/lib/action_view/helpers/tags/select.rb +1 -2
  42. data/lib/action_view/helpers/tags/text_field.rb +0 -1
  43. data/lib/action_view/helpers/tags/time_field.rb +1 -2
  44. data/lib/action_view/helpers/tags/week_field.rb +1 -2
  45. data/lib/action_view/helpers/text_helper.rb +2 -3
  46. data/lib/action_view/helpers/translation_helper.rb +98 -51
  47. data/lib/action_view/helpers/url_helper.rb +124 -16
  48. data/lib/action_view/layouts.rb +8 -10
  49. data/lib/action_view/log_subscriber.rb +26 -11
  50. data/lib/action_view/lookup_context.rb +59 -31
  51. data/lib/action_view/path_set.rb +3 -12
  52. data/lib/action_view/railtie.rb +39 -41
  53. data/lib/action_view/record_identifier.rb +0 -1
  54. data/lib/action_view/renderer/abstract_renderer.rb +142 -11
  55. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  56. data/lib/action_view/renderer/object_renderer.rb +34 -0
  57. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +35 -29
  58. data/lib/action_view/renderer/partial_renderer.rb +21 -273
  59. data/lib/action_view/renderer/renderer.rb +59 -4
  60. data/lib/action_view/renderer/streaming_template_renderer.rb +9 -7
  61. data/lib/action_view/renderer/template_renderer.rb +35 -27
  62. data/lib/action_view/rendering.rb +49 -29
  63. data/lib/action_view/routing_url_for.rb +1 -1
  64. data/lib/action_view/template/error.rb +30 -15
  65. data/lib/action_view/template/handlers/builder.rb +2 -2
  66. data/lib/action_view/template/handlers/erb/erubi.rb +15 -9
  67. data/lib/action_view/template/handlers/erb.rb +14 -19
  68. data/lib/action_view/template/handlers/html.rb +1 -1
  69. data/lib/action_view/template/handlers/raw.rb +2 -2
  70. data/lib/action_view/template/handlers.rb +1 -1
  71. data/lib/action_view/template/html.rb +5 -6
  72. data/lib/action_view/template/inline.rb +22 -0
  73. data/lib/action_view/template/raw_file.rb +25 -0
  74. data/lib/action_view/template/renderable.rb +24 -0
  75. data/lib/action_view/template/resolver.rb +141 -140
  76. data/lib/action_view/template/sources/file.rb +17 -0
  77. data/lib/action_view/template/sources.rb +13 -0
  78. data/lib/action_view/template/text.rb +2 -3
  79. data/lib/action_view/template.rb +49 -75
  80. data/lib/action_view/test_case.rb +20 -28
  81. data/lib/action_view/testing/resolvers.rb +18 -27
  82. data/lib/action_view/unbound_template.rb +31 -0
  83. data/lib/action_view/view_paths.rb +59 -38
  84. data/lib/action_view.rb +7 -2
  85. data/lib/assets/compiled/rails-ujs.js +25 -16
  86. 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. By default this
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 expected default behavior is an XMLHttpRequest in the background instead of the regular
326
- # POST arrangement, but ultimately the behavior is the choice of the JavaScript driver implementor.
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 are remote and unobtrusive XHRs.
615
- # Disable remote submits with <tt>local: true</tt>.
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, &Proc.new)
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 (eg. 1, '1', true, or 'true'):
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
- # (eg. 1, '1', true, or 'true'):
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.to_s.match(/\[\]$/)
1672
- if (object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}")) && object.respond_to?(:to_param)
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 :borned_at %>
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.send( # @template.send(
1908
- #{selector.inspect}, # "text_field",
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 (eg. 1, '1', true, or 'true'):
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
- # (eg. 1, '1', true, or 'true'):
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.sub(/\[\]$/, "")
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.to_s.end_with?("[]")
2184
- record_name = record_name.to_s.sub(/(.*)\[\]$/, "[\\1][#{record_object.id}]")
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
- @template.button_tag(value, options, &block)
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.merge(object: @object))
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.send(association_name).respond_to?(:to_ary)
2554
+ association = [association] if @object.public_send(association_name).respond_to?(:to_ary)
2519
2555
  elsif !association.respond_to?(:to_ary)
2520
- association = @object.send(association_name)
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 an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
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 only requirement
570
- # is that the +model+ parameter be an object that responds to +all+, and
571
- # returns an array of objects that represent time zones.
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 =~ priority_zones }
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
- public_or_deprecated_send(element, value_method) if selected.call(element)
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) : public_or_deprecated_send(item, value)
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 url configured with <tt>url_for_options</tt> just like
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.to_s.ends_with?("[]")) ? "#{name}[]" : 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.delete(:include_blank)
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
- return unless ActionView::Base.automatically_disable_submit_tag
897
- data = tag_options["data"]
900
+ data = tag_options.fetch("data", {})
898
901
 
899
- unless tag_options["data-disable-with"] == false || (data && data["disable_with"] == false)
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"] if data
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!] = "&#x2028;"
@@ -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) { |match| JS_ESCAPE_MAP[match] }
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')", defer: 'defer'
54
+ # javascript_tag "alert('All is good')", type: 'application/javascript'
53
55
  #
54
56
  # Returns:
55
- # <script defer="defer">
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 defer: 'defer' do -%>
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.5 KB). This
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 (eg.: 1200000000 becomes "1.2
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 (eg.: 1500 becomes "1.5
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
- Hash[units.map do |k, v|
428
- [k, ERB::Util.html_escape(v)]
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}[http://api.rubyonrails.org/classes/Array.html#method-i-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 :update specified, the default is to render a partial and use the second parameter
26
- # as the locals hash.
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
- if block_given?
31
- view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
32
- else
33
- view_renderer.render(self, options)
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
- view_renderer.render_partial(self, partial: options, locals: locals, &block)
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::WhiteListSanitizer. See {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.white_list_sanitizer.sanitize(html, options).try(:html_safe)
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.white_list_sanitizer.sanitize_css(style)
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, :white_list_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.white_list_sanitizer.allowed_tags
132
+ sanitizer_vendor.safe_list_sanitizer.allowed_tags
136
133
  end
137
134
 
138
135
  def sanitized_allowed_attributes
139
- sanitizer_vendor.white_list_sanitizer.allowed_attributes
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::WhiteListSanitizer instance used by sanitize and +sanitize_css+.
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.white_list_sanitizer = MySpecialSanitizer.new
163
+ # config.action_view.safe_list_sanitizer = MySpecialSanitizer.new
169
164
  # end
170
- #
171
- def white_list_sanitizer
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