actionview 5.2.4.4 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionview might be problematic. Click here for more details.

Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +221 -93
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -3
  5. data/lib/action_view.rb +7 -2
  6. data/lib/action_view/base.rb +81 -15
  7. data/lib/action_view/buffers.rb +15 -0
  8. data/lib/action_view/cache_expiry.rb +52 -0
  9. data/lib/action_view/context.rb +5 -9
  10. data/lib/action_view/dependency_tracker.rb +10 -4
  11. data/lib/action_view/digestor.rb +15 -22
  12. data/lib/action_view/flows.rb +0 -1
  13. data/lib/action_view/gem_version.rb +4 -4
  14. data/lib/action_view/helpers.rb +0 -2
  15. data/lib/action_view/helpers/active_model_helper.rb +0 -1
  16. data/lib/action_view/helpers/asset_tag_helper.rb +63 -46
  17. data/lib/action_view/helpers/asset_url_helper.rb +9 -6
  18. data/lib/action_view/helpers/atom_feed_helper.rb +2 -1
  19. data/lib/action_view/helpers/cache_helper.rb +23 -22
  20. data/lib/action_view/helpers/capture_helper.rb +4 -0
  21. data/lib/action_view/helpers/csp_helper.rb +4 -2
  22. data/lib/action_view/helpers/csrf_helper.rb +1 -1
  23. data/lib/action_view/helpers/date_helper.rb +73 -30
  24. data/lib/action_view/helpers/form_helper.rb +305 -37
  25. data/lib/action_view/helpers/form_options_helper.rb +23 -23
  26. data/lib/action_view/helpers/form_tag_helper.rb +19 -16
  27. data/lib/action_view/helpers/javascript_helper.rb +12 -11
  28. data/lib/action_view/helpers/number_helper.rb +14 -8
  29. data/lib/action_view/helpers/output_safety_helper.rb +1 -1
  30. data/lib/action_view/helpers/rendering_helper.rb +17 -7
  31. data/lib/action_view/helpers/sanitize_helper.rb +12 -18
  32. data/lib/action_view/helpers/tag_helper.rb +98 -22
  33. data/lib/action_view/helpers/tags/base.rb +18 -11
  34. data/lib/action_view/helpers/tags/check_box.rb +0 -1
  35. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
  36. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
  37. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
  38. data/lib/action_view/helpers/tags/color_field.rb +1 -2
  39. data/lib/action_view/helpers/tags/date_field.rb +1 -2
  40. data/lib/action_view/helpers/tags/date_select.rb +2 -3
  41. data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
  42. data/lib/action_view/helpers/tags/datetime_local_field.rb +1 -2
  43. data/lib/action_view/helpers/tags/label.rb +4 -1
  44. data/lib/action_view/helpers/tags/month_field.rb +1 -2
  45. data/lib/action_view/helpers/tags/radio_button.rb +0 -1
  46. data/lib/action_view/helpers/tags/select.rb +1 -2
  47. data/lib/action_view/helpers/tags/text_field.rb +0 -1
  48. data/lib/action_view/helpers/tags/time_field.rb +1 -2
  49. data/lib/action_view/helpers/tags/translator.rb +1 -6
  50. data/lib/action_view/helpers/tags/week_field.rb +1 -2
  51. data/lib/action_view/helpers/text_helper.rb +3 -4
  52. data/lib/action_view/helpers/translation_helper.rb +93 -55
  53. data/lib/action_view/helpers/url_helper.rb +121 -27
  54. data/lib/action_view/layouts.rb +8 -10
  55. data/lib/action_view/log_subscriber.rb +30 -15
  56. data/lib/action_view/lookup_context.rb +63 -35
  57. data/lib/action_view/path_set.rb +3 -12
  58. data/lib/action_view/railtie.rb +42 -26
  59. data/lib/action_view/record_identifier.rb +2 -3
  60. data/lib/action_view/renderer/abstract_renderer.rb +142 -11
  61. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  62. data/lib/action_view/renderer/object_renderer.rb +34 -0
  63. data/lib/action_view/renderer/partial_renderer.rb +21 -273
  64. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +61 -16
  65. data/lib/action_view/renderer/renderer.rb +59 -4
  66. data/lib/action_view/renderer/streaming_template_renderer.rb +10 -8
  67. data/lib/action_view/renderer/template_renderer.rb +35 -27
  68. data/lib/action_view/rendering.rb +54 -33
  69. data/lib/action_view/routing_url_for.rb +13 -12
  70. data/lib/action_view/template.rb +66 -75
  71. data/lib/action_view/template/error.rb +30 -15
  72. data/lib/action_view/template/handlers.rb +1 -1
  73. data/lib/action_view/template/handlers/builder.rb +2 -2
  74. data/lib/action_view/template/handlers/erb.rb +16 -11
  75. data/lib/action_view/template/handlers/erb/erubi.rb +15 -9
  76. data/lib/action_view/template/handlers/html.rb +1 -1
  77. data/lib/action_view/template/handlers/raw.rb +2 -2
  78. data/lib/action_view/template/html.rb +5 -6
  79. data/lib/action_view/template/inline.rb +22 -0
  80. data/lib/action_view/template/raw_file.rb +25 -0
  81. data/lib/action_view/template/renderable.rb +24 -0
  82. data/lib/action_view/template/resolver.rb +191 -150
  83. data/lib/action_view/template/sources.rb +13 -0
  84. data/lib/action_view/template/sources/file.rb +17 -0
  85. data/lib/action_view/template/text.rb +2 -3
  86. data/lib/action_view/test_case.rb +21 -29
  87. data/lib/action_view/testing/resolvers.rb +18 -27
  88. data/lib/action_view/unbound_template.rb +31 -0
  89. data/lib/action_view/view_paths.rb +59 -38
  90. data/lib/assets/compiled/rails-ujs.js +29 -3
  91. metadata +32 -21
  92. data/lib/action_view/helpers/record_tag_helper.rb +0 -23
@@ -16,12 +16,12 @@ module ActionView
16
16
  #
17
17
  # * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
18
18
  #
19
- # select("post", "category", Post::CATEGORIES, {include_blank: true})
19
+ # select("post", "category", Post::CATEGORIES, { include_blank: true })
20
20
  #
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>
@@ -30,7 +30,7 @@ module ActionView
30
30
  #
31
31
  # Example with <tt>@post.person_id => 2</tt>:
32
32
  #
33
- # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: 'None'})
33
+ # select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { include_blank: 'None' })
34
34
  #
35
35
  # could become:
36
36
  #
@@ -43,7 +43,7 @@ module ActionView
43
43
  #
44
44
  # * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
45
45
  #
46
- # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {prompt: 'Select Person'})
46
+ # select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { prompt: 'Select Person' })
47
47
  #
48
48
  # could become:
49
49
  #
@@ -69,12 +69,11 @@ module ActionView
69
69
  #
70
70
  # * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output.
71
71
  #
72
- # select("post", "category", Post::CATEGORIES, {disabled: 'restricted'})
72
+ # select("post", "category", Post::CATEGORIES, { disabled: 'restricted' })
73
73
  #
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>
@@ -82,7 +81,7 @@ module ActionView
82
81
  #
83
82
  # When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
84
83
  #
85
- # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: -> (category) { category.archived? }})
84
+ # collection_select(:post, :category_id, Category.all, :id, :name, { disabled: -> (category) { category.archived? } })
86
85
  #
87
86
  # If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
88
87
  # <select name="post[category_id]" id="post_category_id">
@@ -107,12 +106,12 @@ module ActionView
107
106
  #
108
107
  # For example:
109
108
  #
110
- # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true })
109
+ # select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { include_blank: true })
111
110
  #
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
  #
@@ -323,12 +322,12 @@ module ActionView
323
322
  #
324
323
  # You can optionally provide HTML attributes as the last element of the array.
325
324
  #
326
- # options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"])
325
+ # options_for_select([ "Denmark", ["USA", { class: 'bold' }], "Sweden" ], ["USA", "Sweden"])
327
326
  # # => <option value="Denmark">Denmark</option>
328
327
  # # => <option value="USA" class="bold" selected="selected">USA</option>
329
328
  # # => <option value="Sweden" selected="selected">Sweden</option>
330
329
  #
331
- # options_for_select([["Dollar", "$", {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]])
330
+ # options_for_select([["Dollar", "$", { class: "bold" }], ["Kroner", "DKK", { onclick: "alert('HI');" }]])
332
331
  # # => <option value="$" class="bold">Dollar</option>
333
332
  # # => <option value="DKK" onclick="alert('HI');">Kroner</option>
334
333
  #
@@ -463,7 +462,7 @@ module ActionView
463
462
  option_tags = options_from_collection_for_select(
464
463
  value_for_collection(group, group_method), option_key_method, option_value_method, selected_key)
465
464
 
466
- content_tag("optgroup".freeze, option_tags, label: value_for_collection(group, group_label_method))
465
+ content_tag("optgroup", option_tags, label: value_for_collection(group, group_label_method))
467
466
  end.join.html_safe
468
467
  end
469
468
 
@@ -535,7 +534,7 @@ module ActionView
535
534
  body = "".html_safe
536
535
 
537
536
  if prompt
538
- body.safe_concat content_tag("option".freeze, prompt_text(prompt), value: "")
537
+ body.safe_concat content_tag("option", prompt_text(prompt), value: "")
539
538
  end
540
539
 
541
540
  grouped_options.each do |container|
@@ -548,7 +547,7 @@ module ActionView
548
547
  end
549
548
 
550
549
  html_attributes = { label: label }.merge!(html_attributes)
551
- body.safe_concat content_tag("optgroup".freeze, options_for_select(container, selected_key), html_attributes)
550
+ body.safe_concat content_tag("optgroup", options_for_select(container, selected_key), html_attributes)
552
551
  end
553
552
 
554
553
  body
@@ -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,11 +580,11 @@ 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)
587
- zone_options.safe_concat content_tag("option".freeze, "-------------", value: "", disabled: true)
587
+ zone_options.safe_concat content_tag("option", "-------------", value: "", disabled: true)
588
588
  zone_options.safe_concat "\n"
589
589
 
590
590
  zones = zones - priority_zones
@@ -654,7 +654,7 @@ module ActionView
654
654
  #
655
655
  # ==== Gotcha
656
656
  #
657
- # The HTML specification says when nothing is select on a collection of radio buttons
657
+ # The HTML specification says when nothing is selected on a collection of radio buttons
658
658
  # web browsers do not send any value to server.
659
659
  # Unfortunately this introduces a gotcha:
660
660
  # if a +User+ model has a +category_id+ field and in the form no category is selected, no +category_id+ parameter is sent. So,
@@ -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
- element.send(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,7 +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) : item.send(value)
805
+ value.respond_to?(:call) ? value.call(item) : item.public_send(value)
806
806
  end
807
807
 
808
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
@@ -22,7 +23,9 @@ module ActionView
22
23
  mattr_accessor :embed_authenticity_token_in_remote_forms
23
24
  self.embed_authenticity_token_in_remote_forms = nil
24
25
 
25
- # Starts a form tag that points the action to a url configured with <tt>url_for_options</tt> just like
26
+ mattr_accessor :default_enforce_utf8, default: true
27
+
28
+ # Starts a form tag that points the action to a URL configured with <tt>url_for_options</tt> just like
26
29
  # ActionController::Base#url_for. The method for the form defaults to POST.
27
30
  #
28
31
  # ==== Options
@@ -132,10 +135,11 @@ module ActionView
132
135
  # # <option selected="selected">MasterCard</option></select>
133
136
  def select_tag(name, option_tags = nil, options = {})
134
137
  option_tags ||= ""
135
- html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
138
+ html_name = (options[:multiple] == true && !name.end_with?("[]")) ? "#{name}[]" : name
136
139
 
137
140
  if options.include?(:include_blank)
138
- include_blank = options.delete(:include_blank)
141
+ include_blank = options[:include_blank]
142
+ options = options.except(:include_blank)
139
143
  options_for_blank_options_tag = { value: "" }
140
144
 
141
145
  if include_blank == true
@@ -144,15 +148,15 @@ module ActionView
144
148
  end
145
149
 
146
150
  if include_blank
147
- option_tags = content_tag("option".freeze, include_blank, options_for_blank_options_tag).safe_concat(option_tags)
151
+ option_tags = content_tag("option", include_blank, options_for_blank_options_tag).safe_concat(option_tags)
148
152
  end
149
153
  end
150
154
 
151
155
  if prompt = options.delete(:prompt)
152
- option_tags = content_tag("option".freeze, prompt, value: "").safe_concat(option_tags)
156
+ option_tags = content_tag("option", prompt, value: "").safe_concat(option_tags)
153
157
  end
154
158
 
155
- content_tag "select".freeze, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
159
+ content_tag "select", option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
156
160
  end
157
161
 
158
162
  # Creates a standard text field; use these text fields to input smaller chunks of text like a username
@@ -389,8 +393,8 @@ module ActionView
389
393
  # * Any other key creates standard HTML options for the tag.
390
394
  #
391
395
  # ==== Examples
392
- # radio_button_tag 'gender', 'male'
393
- # # => <input id="gender_male" name="gender" type="radio" value="male" />
396
+ # radio_button_tag 'favorite_color', 'maroon'
397
+ # # => <input id="favorite_color_maroon" name="favorite_color" type="radio" value="maroon" />
394
398
  #
395
399
  # radio_button_tag 'receive_updates', 'no', true
396
400
  # # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
@@ -577,7 +581,7 @@ module ActionView
577
581
  # # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
578
582
  def field_set_tag(legend = nil, options = nil, &block)
579
583
  output = tag(:fieldset, options, true)
580
- output.safe_concat(content_tag("legend".freeze, legend)) unless legend.blank?
584
+ output.safe_concat(content_tag("legend", legend)) unless legend.blank?
581
585
  output.concat(capture(&block)) if block_given?
582
586
  output.safe_concat("</fieldset>")
583
587
  end
@@ -869,7 +873,7 @@ module ActionView
869
873
  })
870
874
  end
871
875
 
872
- if html_options.delete("enforce_utf8") { true }
876
+ if html_options.delete("enforce_utf8") { default_enforce_utf8 }
873
877
  utf8_enforcer_tag + method_tag
874
878
  else
875
879
  method_tag
@@ -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")
@@ -17,8 +17,8 @@ module ActionView
17
17
  "$" => "\\$"
18
18
  }
19
19
 
20
- JS_ESCAPE_MAP["\342\200\250".dup.force_encoding(Encoding::UTF_8).encode!] = "&#x2028;"
21
- JS_ESCAPE_MAP["\342\200\251".dup.force_encoding(Encoding::UTF_8).encode!] = "&#x2029;"
20
+ JS_ESCAPE_MAP[(+"\342\200\250").force_encoding(Encoding::UTF_8).encode!] = "&#x2028;"
21
+ JS_ESCAPE_MAP[(+"\342\200\251").force_encoding(Encoding::UTF_8).encode!] = "&#x2029;"
22
22
 
23
23
  # Escapes carriage returns and single and double quotes for JavaScript segments.
24
24
  #
@@ -27,12 +27,13 @@ module ActionView
27
27
  #
28
28
  # $('some_element').replaceWith('<%= j render 'some/element_template' %>');
29
29
  def escape_javascript(javascript)
30
- if javascript
31
- result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u) { |match| JS_ESCAPE_MAP[match] }
32
- javascript.html_safe? ? result.html_safe : result
30
+ javascript = javascript.to_s
31
+ if javascript.empty?
32
+ result = ""
33
33
  else
34
- ""
34
+ result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
35
35
  end
36
+ javascript.html_safe? ? result.html_safe : result
36
37
  end
37
38
 
38
39
  alias_method :j, :escape_javascript
@@ -50,10 +51,10 @@ module ActionView
50
51
  # +html_options+ may be a hash of attributes for the <tt>\<script></tt>
51
52
  # tag.
52
53
  #
53
- # javascript_tag "alert('All is good')", defer: 'defer'
54
+ # javascript_tag "alert('All is good')", type: 'application/javascript'
54
55
  #
55
56
  # Returns:
56
- # <script defer="defer">
57
+ # <script type="application/javascript">
57
58
  # //<![CDATA[
58
59
  # alert('All is good')
59
60
  # //]]>
@@ -62,12 +63,12 @@ module ActionView
62
63
  # Instead of passing the content as an argument, you can also use a block
63
64
  # in which case, you pass your +html_options+ as the first parameter.
64
65
  #
65
- # <%= javascript_tag defer: 'defer' do -%>
66
+ # <%= javascript_tag type: 'application/javascript' do -%>
66
67
  # alert('All is good')
67
68
  # <% end -%>
68
69
  #
69
70
  # If you have a content security policy enabled then you can add an automatic
70
- # nonce value by passing +nonce: true+ as part of +html_options+. Example:
71
+ # nonce value by passing <tt>nonce: true</tt> as part of +html_options+. Example:
71
72
  #
72
73
  # <%= javascript_tag nonce: true do -%>
73
74
  # alert('All is good')
@@ -85,7 +86,7 @@ module ActionView
85
86
  html_options[:nonce] = content_security_policy_nonce
86
87
  end
87
88
 
88
- content_tag("script".freeze, javascript_cdata_section(content), html_options)
89
+ content_tag("script", javascript_cdata_section(content), html_options)
89
90
  end
90
91
 
91
92
  def javascript_cdata_section(content) #:nodoc:
@@ -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
@@ -100,6 +100,9 @@ module ActionView
100
100
  # absolute value of the number.
101
101
  # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
102
102
  # the argument is invalid.
103
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
104
+ # insignificant zeros after the decimal separator (defaults to
105
+ # +false+).
103
106
  #
104
107
  # ==== Examples
105
108
  #
@@ -111,12 +114,16 @@ module ActionView
111
114
  #
112
115
  # number_to_currency("123a456", raise: true) # => InvalidNumberError
113
116
  #
117
+ # number_to_currency(-0.456789, precision: 0)
118
+ # # => "$0"
114
119
  # number_to_currency(-1234567890.50, negative_format: "(%u%n)")
115
120
  # # => ($1,234,567,890.50)
116
121
  # number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "")
117
122
  # # => R$1234567890,50
118
123
  # number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u")
119
124
  # # => 1234567890,50 R$
125
+ # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
126
+ # # => "$1,234,567,890.5"
120
127
  def number_to_currency(number, options = {})
121
128
  delegate_number_helper_method(:number_to_currency, number, options)
122
129
  end
@@ -246,7 +253,7 @@ module ActionView
246
253
  end
247
254
 
248
255
  # Formats the bytes in +number+ into a more understandable
249
- # representation (e.g., giving it 1500 yields 1.5 KB). This
256
+ # representation (e.g., giving it 1500 yields 1.46 KB). This
250
257
  # method is useful for reporting file sizes to users. You can
251
258
  # customize the format in the +options+ hash.
252
259
  #
@@ -292,7 +299,7 @@ module ActionView
292
299
  end
293
300
 
294
301
  # Pretty prints (formats and approximates) a number in a way it
295
- # is more readable by humans (eg.: 1200000000 becomes "1.2
302
+ # is more readable by humans (e.g.: 1200000000 becomes "1.2
296
303
  # Billion"). This is useful for numbers that can get very large
297
304
  # (and too hard to read).
298
305
  #
@@ -300,7 +307,7 @@ module ActionView
300
307
  # size.
301
308
  #
302
309
  # You can also define your own unit-quantifier names if you want
303
- # to use other decimal units (eg.: 1500 becomes "1.5
310
+ # to use other decimal units (e.g.: 1500 becomes "1.5
304
311
  # kilometers", 0.150 becomes "150 milliliters", etc). You may
305
312
  # define a wide range of unit quantifiers, even fractional ones
306
313
  # (centi, deci, mili, etc).
@@ -398,7 +405,6 @@ module ActionView
398
405
  end
399
406
 
400
407
  private
401
-
402
408
  def delegate_number_helper_method(method, number, options)
403
409
  return unless number
404
410
  options = escape_unsafe_options(options.symbolize_keys)
@@ -419,9 +425,9 @@ module ActionView
419
425
  end
420
426
 
421
427
  def escape_units(units)
422
- Hash[units.map do |k, v|
423
- [k, ERB::Util.html_escape(v)]
424
- end]
428
+ units.transform_values do |v|
429
+ ERB::Util.html_escape(v)
430
+ end
425
431
  end
426
432
 
427
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