actionview 4.2.11 → 5.0.7

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 (68) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +304 -184
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/action_view.rb +1 -1
  6. data/lib/action_view/base.rb +14 -2
  7. data/lib/action_view/dependency_tracker.rb +51 -18
  8. data/lib/action_view/digestor.rb +83 -81
  9. data/lib/action_view/flows.rb +4 -5
  10. data/lib/action_view/gem_version.rb +3 -3
  11. data/lib/action_view/helpers/asset_tag_helper.rb +15 -5
  12. data/lib/action_view/helpers/asset_url_helper.rb +51 -12
  13. data/lib/action_view/helpers/atom_feed_helper.rb +6 -5
  14. data/lib/action_view/helpers/cache_helper.rb +62 -21
  15. data/lib/action_view/helpers/capture_helper.rb +5 -4
  16. data/lib/action_view/helpers/controller_helper.rb +11 -2
  17. data/lib/action_view/helpers/date_helper.rb +59 -13
  18. data/lib/action_view/helpers/debug_helper.rb +1 -1
  19. data/lib/action_view/helpers/form_helper.rb +74 -72
  20. data/lib/action_view/helpers/form_options_helper.rb +79 -39
  21. data/lib/action_view/helpers/form_tag_helper.rb +74 -44
  22. data/lib/action_view/helpers/javascript_helper.rb +4 -4
  23. data/lib/action_view/helpers/number_helper.rb +28 -13
  24. data/lib/action_view/helpers/output_safety_helper.rb +32 -2
  25. data/lib/action_view/helpers/record_tag_helper.rb +12 -99
  26. data/lib/action_view/helpers/rendering_helper.rb +2 -2
  27. data/lib/action_view/helpers/sanitize_helper.rb +1 -2
  28. data/lib/action_view/helpers/tag_helper.rb +19 -11
  29. data/lib/action_view/helpers/tags/base.rb +45 -29
  30. data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -28
  31. data/lib/action_view/helpers/tags/collection_helpers.rb +32 -0
  32. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -9
  33. data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
  34. data/lib/action_view/helpers/tags/label.rb +1 -1
  35. data/lib/action_view/helpers/tags/placeholderable.rb +1 -1
  36. data/lib/action_view/helpers/tags/search_field.rb +12 -9
  37. data/lib/action_view/helpers/tags/text_field.rb +0 -1
  38. data/lib/action_view/helpers/tags/translator.rb +1 -1
  39. data/lib/action_view/helpers/text_helper.rb +27 -11
  40. data/lib/action_view/helpers/translation_helper.rb +56 -26
  41. data/lib/action_view/helpers/url_helper.rb +108 -79
  42. data/lib/action_view/layouts.rb +11 -10
  43. data/lib/action_view/log_subscriber.rb +35 -1
  44. data/lib/action_view/lookup_context.rb +69 -48
  45. data/lib/action_view/model_naming.rb +1 -1
  46. data/lib/action_view/path_set.rb +9 -0
  47. data/lib/action_view/railtie.rb +18 -3
  48. data/lib/action_view/record_identifier.rb +45 -19
  49. data/lib/action_view/renderer/abstract_renderer.rb +7 -3
  50. data/lib/action_view/renderer/partial_renderer.rb +38 -37
  51. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +49 -0
  52. data/lib/action_view/renderer/renderer.rb +2 -6
  53. data/lib/action_view/renderer/streaming_template_renderer.rb +1 -1
  54. data/lib/action_view/renderer/template_renderer.rb +11 -10
  55. data/lib/action_view/rendering.rb +15 -7
  56. data/lib/action_view/routing_url_for.rb +18 -6
  57. data/lib/action_view/tasks/{dependencies.rake → cache_digests.rake} +2 -2
  58. data/lib/action_view/template.rb +36 -12
  59. data/lib/action_view/template/error.rb +20 -9
  60. data/lib/action_view/template/handlers.rb +6 -4
  61. data/lib/action_view/template/handlers/html.rb +9 -0
  62. data/lib/action_view/template/handlers/raw.rb +1 -3
  63. data/lib/action_view/template/resolver.rb +49 -42
  64. data/lib/action_view/template/types.rb +14 -16
  65. data/lib/action_view/test_case.rb +15 -9
  66. data/lib/action_view/testing/resolvers.rb +1 -2
  67. data/lib/action_view/view_paths.rb +6 -24
  68. metadata +16 -20
@@ -20,7 +20,7 @@ module ActionView
20
20
  mattr_accessor :embed_authenticity_token_in_remote_forms
21
21
  self.embed_authenticity_token_in_remote_forms = false
22
22
 
23
- # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
23
+ # Starts a form tag that points the action to a url configured with <tt>url_for_options</tt> just like
24
24
  # ActionController::Base#url_for. The method for the form defaults to POST.
25
25
  #
26
26
  # ==== Options
@@ -80,7 +80,7 @@ module ActionView
80
80
  # associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
81
81
  #
82
82
  # ==== Options
83
- # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
83
+ # * <tt>:multiple</tt> - If set to true, the selection will allow multiple choices.
84
84
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
85
85
  # * <tt>:include_blank</tt> - If set to true, an empty option will be created. If set to a string, the string will be used as the option's content and the value will be empty.
86
86
  # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something.
@@ -93,22 +93,22 @@ module ActionView
93
93
  # select_tag "people", options_from_collection_for_select(@people, "id", "name", "1")
94
94
  # # <select id="people" name="people"><option value="1" selected="selected">David</option></select>
95
95
  #
96
- # select_tag "people", "<option>David</option>".html_safe
96
+ # select_tag "people", raw("<option>David</option>")
97
97
  # # => <select id="people" name="people"><option>David</option></select>
98
98
  #
99
- # select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>".html_safe
99
+ # select_tag "count", raw("<option>1</option><option>2</option><option>3</option><option>4</option>")
100
100
  # # => <select id="count" name="count"><option>1</option><option>2</option>
101
101
  # # <option>3</option><option>4</option></select>
102
102
  #
103
- # select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, multiple: true
103
+ # select_tag "colors", raw("<option>Red</option><option>Green</option><option>Blue</option>"), multiple: true
104
104
  # # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
105
105
  # # <option>Green</option><option>Blue</option></select>
106
106
  #
107
- # select_tag "locations", "<option>Home</option><option selected='selected'>Work</option><option>Out</option>".html_safe
107
+ # select_tag "locations", raw("<option>Home</option><option selected='selected'>Work</option><option>Out</option>")
108
108
  # # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
109
109
  # # <option>Out</option></select>
110
110
  #
111
- # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input', id: 'unique_id'
111
+ # select_tag "access", raw("<option>Read</option><option>Write</option>"), multiple: true, class: 'form_input', id: 'unique_id'
112
112
  # # => <select class="form_input" id="unique_id" multiple="multiple" name="access[]"><option>Read</option>
113
113
  # # <option>Write</option></select>
114
114
  #
@@ -121,7 +121,7 @@ module ActionView
121
121
  # select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something"
122
122
  # # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
123
123
  #
124
- # select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>".html_safe, disabled: true
124
+ # select_tag "destination", raw("<option>NYC</option><option>Paris</option><option>Rome</option>"), disabled: true
125
125
  # # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
126
126
  # # <option>Paris</option><option>Rome</option></select>
127
127
  #
@@ -134,21 +134,23 @@ module ActionView
134
134
 
135
135
  if options.include?(:include_blank)
136
136
  include_blank = options.delete(:include_blank)
137
+ options_for_blank_options_tag = { value: '' }
137
138
 
138
139
  if include_blank == true
139
140
  include_blank = ''
141
+ options_for_blank_options_tag[:label] = ' '
140
142
  end
141
143
 
142
144
  if include_blank
143
- option_tags = content_tag(:option, include_blank, value: '').safe_concat(option_tags)
145
+ option_tags = content_tag("option".freeze, include_blank, options_for_blank_options_tag).safe_concat(option_tags)
144
146
  end
145
147
  end
146
148
 
147
149
  if prompt = options.delete(:prompt)
148
- option_tags = content_tag(:option, prompt, value: '').safe_concat(option_tags)
150
+ option_tags = content_tag("option".freeze, prompt, value: '').safe_concat(option_tags)
149
151
  end
150
152
 
151
- content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
153
+ content_tag "select".freeze, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
152
154
  end
153
155
 
154
156
  # Creates a standard text field; use these text fields to input smaller chunks of text like a username
@@ -414,42 +416,45 @@ module ActionView
414
416
  # the form is processed normally, otherwise no action is taken.
415
417
  # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
416
418
  # disabled version of the submit button when the form is submitted. This feature is
417
- # provided by the unobtrusive JavaScript driver.
419
+ # provided by the unobtrusive JavaScript driver. To disable this feature for a single submit tag
420
+ # pass <tt>:data => { disable_with: false }</tt> Defaults to value attribute.
418
421
  #
419
422
  # ==== Examples
420
423
  # submit_tag
421
- # # => <input name="commit" type="submit" value="Save changes" />
424
+ # # => <input name="commit" data-disable-with="Save changes" type="submit" value="Save changes" />
422
425
  #
423
426
  # submit_tag "Edit this article"
424
- # # => <input name="commit" type="submit" value="Edit this article" />
427
+ # # => <input name="commit" data-disable-with="Edit this article" type="submit" value="Edit this article" />
425
428
  #
426
429
  # submit_tag "Save edits", disabled: true
427
- # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
430
+ # # => <input disabled="disabled" name="commit" data-disable-with="Save edits" type="submit" value="Save edits" />
428
431
  #
429
- # submit_tag "Complete sale", data: { disable_with: "Please wait..." }
430
- # # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
432
+ # submit_tag "Complete sale", data: { disable_with: "Submitting..." }
433
+ # # => <input name="commit" data-disable-with="Submitting..." type="submit" value="Complete sale" />
431
434
  #
432
435
  # submit_tag nil, class: "form_submit"
433
436
  # # => <input class="form_submit" name="commit" type="submit" />
434
437
  #
435
438
  # submit_tag "Edit", class: "edit_button"
436
- # # => <input class="edit_button" name="commit" type="submit" value="Edit" />
439
+ # # => <input class="edit_button" data-disable-with="Edit" name="commit" type="submit" value="Edit" />
437
440
  #
438
441
  # submit_tag "Save", data: { confirm: "Are you sure?" }
439
- # # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
442
+ # # => <input name='commit' type='submit' value='Save' data-disable-with="Save" data-confirm="Are you sure?" />
440
443
  #
441
444
  def submit_tag(value = "Save changes", options = {})
442
- options = options.stringify_keys
443
-
444
- tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
445
+ options = options.deep_stringify_keys
446
+ tag_options = { "type" => "submit", "name" => "commit", "value" => value }.update(options)
447
+ set_default_disable_with value, tag_options
448
+ tag :input, tag_options
445
449
  end
446
450
 
447
451
  # Creates a button element that defines a <tt>submit</tt> button,
448
452
  # <tt>reset</tt>button or a generic button which can be used in
449
453
  # JavaScript, for example. You can use the button tag as a regular
450
454
  # submit tag but it isn't supported in legacy browsers. However,
451
- # the button tag allows richer labels such as images and emphasis,
452
- # so this helper will also accept a block.
455
+ # the button tag does allow for richer labels such as images and emphasis,
456
+ # so this helper will also accept a block. By default, it will create
457
+ # a button tag with type `submit`, if type is not given.
453
458
  #
454
459
  # ==== Options
455
460
  # * <tt>:data</tt> - This option can be used to add custom data attributes.
@@ -472,6 +477,15 @@ module ActionView
472
477
  # button_tag
473
478
  # # => <button name="button" type="submit">Button</button>
474
479
  #
480
+ # button_tag 'Reset', type: 'reset'
481
+ # # => <button name="button" type="reset">Reset</button>
482
+ #
483
+ # button_tag 'Button', type: 'button'
484
+ # # => <button name="button" type="button">Button</button>
485
+ #
486
+ # button_tag 'Reset', type: 'reset', disabled: true
487
+ # # => <button name="button" type="reset" disabled="disabled">Reset</button>
488
+ #
475
489
  # button_tag(type: 'button') do
476
490
  # content_tag(:strong, 'Ask me!')
477
491
  # end
@@ -479,6 +493,9 @@ module ActionView
479
493
  # # <strong>Ask me!</strong>
480
494
  # # </button>
481
495
  #
496
+ # button_tag "Save", data: { confirm: "Are you sure?" }
497
+ # # => <button name="button" type="submit" data-confirm="Are you sure?">Save</button>
498
+ #
482
499
  # button_tag "Checkout", data: { disable_with: "Please wait..." }
483
500
  # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
484
501
  #
@@ -555,7 +572,7 @@ module ActionView
555
572
  # # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
556
573
  def field_set_tag(legend = nil, options = nil, &block)
557
574
  output = tag(:fieldset, options, true)
558
- output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
575
+ output.safe_concat(content_tag("legend".freeze, legend)) unless legend.blank?
559
576
  output.concat(capture(&block)) if block_given?
560
577
  output.safe_concat("</fieldset>")
561
578
  end
@@ -656,17 +673,6 @@ module ActionView
656
673
  text_field_tag(name, value, options.merge(type: :time))
657
674
  end
658
675
 
659
- # Creates a text field of type "datetime".
660
- #
661
- # === Options
662
- # * <tt>:min</tt> - The minimum acceptable value.
663
- # * <tt>:max</tt> - The maximum acceptable value.
664
- # * <tt>:step</tt> - The acceptable value granularity.
665
- # * Otherwise accepts the same options as text_field_tag.
666
- def datetime_field_tag(name, value = nil, options = {})
667
- text_field_tag(name, value, options.merge(type: :datetime))
668
- end
669
-
670
676
  # Creates a text field of type "datetime-local".
671
677
  #
672
678
  # === Options
@@ -674,10 +680,12 @@ module ActionView
674
680
  # * <tt>:max</tt> - The maximum acceptable value.
675
681
  # * <tt>:step</tt> - The acceptable value granularity.
676
682
  # * Otherwise accepts the same options as text_field_tag.
677
- def datetime_local_field_tag(name, value = nil, options = {})
683
+ def datetime_field_tag(name, value = nil, options = {})
678
684
  text_field_tag(name, value, options.merge(type: 'datetime-local'))
679
685
  end
680
686
 
687
+ alias datetime_local_field_tag datetime_field_tag
688
+
681
689
  # Creates a text field of type "month".
682
690
  #
683
691
  # === Options
@@ -776,10 +784,10 @@ module ActionView
776
784
  # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
777
785
  #
778
786
  # number_field_tag 'quantity', nil, min: 1, max: 10
779
- # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
787
+ # # => <input id="quantity" name="quantity" min="1" max="10" type="number" />
780
788
  #
781
789
  # number_field_tag 'quantity', nil, min: 1, max: 10, step: 2
782
- # # => <input id="quantity" name="quantity" min="1" max="9" step="2" type="number" />
790
+ # # => <input id="quantity" name="quantity" min="1" max="10" step="2" type="number" />
783
791
  #
784
792
  # number_field_tag 'quantity', '1', class: 'special_input', disabled: true
785
793
  # # => <input disabled="disabled" class="special_input" id="quantity" name="quantity" type="number" value="1" />
@@ -835,18 +843,24 @@ module ActionView
835
843
 
836
844
  def extra_tags_for_form(html_options)
837
845
  authenticity_token = html_options.delete("authenticity_token")
838
- method = html_options.delete("method").to_s
846
+ method = html_options.delete("method").to_s.downcase
839
847
 
840
848
  method_tag = case method
841
- when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
849
+ when 'get'
842
850
  html_options["method"] = "get"
843
851
  ''
844
- when /^post$/i, "", nil
852
+ when 'post', ''
845
853
  html_options["method"] = "post"
846
- token_tag(authenticity_token)
854
+ token_tag(authenticity_token, form_options: {
855
+ action: html_options["action"],
856
+ method: "post"
857
+ })
847
858
  else
848
859
  html_options["method"] = "post"
849
- method_tag(method) + token_tag(authenticity_token)
860
+ method_tag(method) + token_tag(authenticity_token, form_options: {
861
+ action: html_options["action"],
862
+ method: method
863
+ })
850
864
  end
851
865
 
852
866
  if html_options.delete("enforce_utf8") { true }
@@ -871,6 +885,22 @@ module ActionView
871
885
  def sanitize_to_id(name)
872
886
  name.to_s.delete(']').tr('^-a-zA-Z0-9:.', "_")
873
887
  end
888
+
889
+ def set_default_disable_with(value, tag_options)
890
+ return unless ActionView::Base.automatically_disable_submit_tag
891
+ data = tag_options["data"]
892
+
893
+ unless tag_options["data-disable-with"] == false || (data && data["disable_with"] == false)
894
+ disable_with_text = tag_options["data-disable-with"]
895
+ disable_with_text ||= data["disable_with"] if data
896
+ disable_with_text ||= value.to_s.clone
897
+ tag_options.deep_merge!("data" => { "disable_with" => disable_with_text })
898
+ else
899
+ data.delete("disable_with") if data
900
+ end
901
+
902
+ tag_options.delete("data-disable-with")
903
+ end
874
904
  end
875
905
  end
876
906
  end
@@ -21,7 +21,7 @@ module ActionView
21
21
  # Also available through the alias j(). This is particularly helpful in JavaScript
22
22
  # responses, like:
23
23
  #
24
- # $('some_element').replaceWith('<%=j render 'some/element_template' %>');
24
+ # $('some_element').replaceWith('<%= j render 'some/element_template' %>');
25
25
  def escape_javascript(javascript)
26
26
  if javascript
27
27
  result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
@@ -47,8 +47,8 @@ module ActionView
47
47
  # tag.
48
48
  #
49
49
  # javascript_tag "alert('All is good')", defer: 'defer'
50
- #
51
- # Returns:
50
+ #
51
+ # Returns:
52
52
  # <script defer="defer">
53
53
  # //<![CDATA[
54
54
  # alert('All is good')
@@ -70,7 +70,7 @@ module ActionView
70
70
  content_or_options_with_block
71
71
  end
72
72
 
73
- content_tag(:script, javascript_cdata_section(content), html_options)
73
+ content_tag("script".freeze, javascript_cdata_section(content), html_options)
74
74
  end
75
75
 
76
76
  def javascript_cdata_section(content) #:nodoc:
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  require 'active_support/core_ext/hash/keys'
4
2
  require 'active_support/core_ext/string/output_safety'
5
3
  require 'active_support/number_helper'
@@ -25,7 +23,7 @@ module ActionView
25
23
  end
26
24
  end
27
25
 
28
- # Formats a +number+ into a US phone number (e.g., (555)
26
+ # Formats a +number+ into a phone number (US by default e.g., (555)
29
27
  # 123-9876). You can customize the format in the +options+ hash.
30
28
  #
31
29
  # ==== Options
@@ -37,6 +35,8 @@ module ActionView
37
35
  # end of the generated number.
38
36
  # * <tt>:country_code</tt> - Sets the country code for the phone
39
37
  # number.
38
+ # * <tt>:pattern</tt> - Specifies how the number is divided into three
39
+ # groups with the custom regexp to override the default format.
40
40
  # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
41
41
  # the argument is invalid.
42
42
  #
@@ -54,6 +54,11 @@ module ActionView
54
54
  #
55
55
  # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
56
56
  # # => +1.123.555.1234 x 1343
57
+ #
58
+ # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
59
+ # # => "(755) 6123-4567"
60
+ # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/))
61
+ # # => "133-1234-5678"
57
62
  def number_to_phone(number, options = {})
58
63
  return unless number
59
64
  options = options.symbolize_keys
@@ -65,6 +70,14 @@ module ActionView
65
70
  # Formats a +number+ into a currency string (e.g., $13.65). You
66
71
  # can customize the format in the +options+ hash.
67
72
  #
73
+ # The currency unit and number formatting of the current locale will be used
74
+ # unless otherwise specified in the provided options. No currency conversion
75
+ # is performed. If the user is given a way to change their locale, they will
76
+ # also be able to change the relative value of the currency displayed with
77
+ # this helper. If your application will ever support multiple locales, you
78
+ # may want to specify a constant <tt>:locale</tt> option or consider
79
+ # using a library capable of currency conversion.
80
+ #
68
81
  # ==== Options
69
82
  #
70
83
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
@@ -117,8 +130,8 @@ module ActionView
117
130
  # (defaults to current locale).
118
131
  # * <tt>:precision</tt> - Sets the precision of the number
119
132
  # (defaults to 3).
120
- # * <tt>:significant</tt> - If +true+, precision will be the #
121
- # of significant_digits. If +false+, the # of fractional
133
+ # * <tt>:significant</tt> - If +true+, precision will be the number
134
+ # of significant_digits. If +false+, the number of fractional
122
135
  # digits (defaults to +false+).
123
136
  # * <tt>:separator</tt> - Sets the separator between the
124
137
  # fractional and integer digits (defaults to ".").
@@ -141,7 +154,7 @@ module ActionView
141
154
  # number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
142
155
  # number_to_percentage(1000, locale: :fr) # => 1 000,000%
143
156
  # number_to_percentage("98a") # => 98a%
144
- # number_to_percentage(100, format: "%n %") # => 100 %
157
+ # number_to_percentage(100, format: "%n %") # => 100.000 %
145
158
  #
146
159
  # number_to_percentage("98a", raise: true) # => InvalidNumberError
147
160
  def number_to_percentage(number, options = {})
@@ -192,8 +205,8 @@ module ActionView
192
205
  # (defaults to current locale).
193
206
  # * <tt>:precision</tt> - Sets the precision of the number
194
207
  # (defaults to 3).
195
- # * <tt>:significant</tt> - If +true+, precision will be the #
196
- # of significant_digits. If +false+, the # of fractional
208
+ # * <tt>:significant</tt> - If +true+, precision will be the number
209
+ # of significant_digits. If +false+, the number of fractional
197
210
  # digits (defaults to +false+).
198
211
  # * <tt>:separator</tt> - Sets the separator between the
199
212
  # fractional and integer digits (defaults to ".").
@@ -240,8 +253,8 @@ module ActionView
240
253
  # (defaults to current locale).
241
254
  # * <tt>:precision</tt> - Sets the precision of the number
242
255
  # (defaults to 3).
243
- # * <tt>:significant</tt> - If +true+, precision will be the #
244
- # of significant_digits. If +false+, the # of fractional
256
+ # * <tt>:significant</tt> - If +true+, precision will be the number
257
+ # of significant_digits. If +false+, the number of fractional
245
258
  # digits (defaults to +true+)
246
259
  # * <tt>:separator</tt> - Sets the separator between the
247
260
  # fractional and integer digits (defaults to ".").
@@ -263,6 +276,8 @@ module ActionView
263
276
  # number_to_human_size(1234567) # => 1.18 MB
264
277
  # number_to_human_size(1234567890) # => 1.15 GB
265
278
  # number_to_human_size(1234567890123) # => 1.12 TB
279
+ # number_to_human_size(1234567890123456) # => 1.1 PB
280
+ # number_to_human_size(1234567890123456789) # => 1.07 EB
266
281
  # number_to_human_size(1234567, precision: 2) # => 1.2 MB
267
282
  # number_to_human_size(483989, precision: 2) # => 470 KB
268
283
  # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
@@ -280,7 +295,7 @@ module ActionView
280
295
  # See <tt>number_to_human_size</tt> if you want to print a file
281
296
  # size.
282
297
  #
283
- # You can also define you own unit-quantifier names if you want
298
+ # You can also define your own unit-quantifier names if you want
284
299
  # to use other decimal units (eg.: 1500 becomes "1.5
285
300
  # kilometers", 0.150 becomes "150 milliliters", etc). You may
286
301
  # define a wide range of unit quantifiers, even fractional ones
@@ -292,8 +307,8 @@ module ActionView
292
307
  # (defaults to current locale).
293
308
  # * <tt>:precision</tt> - Sets the precision of the number
294
309
  # (defaults to 3).
295
- # * <tt>:significant</tt> - If +true+, precision will be the #
296
- # of significant_digits. If +false+, the # of fractional
310
+ # * <tt>:significant</tt> - If +true+, precision will be the number
311
+ # of significant_digits. If +false+, the number of fractional
297
312
  # digits (defaults to +true+)
298
313
  # * <tt>:separator</tt> - Sets the separator between the
299
314
  # fractional and integer digits (defaults to ".").
@@ -22,10 +22,10 @@ module ActionView #:nodoc:
22
22
  # the supplied separator, are HTML escaped unless they are HTML
23
23
  # safe, and the returned string is marked as HTML safe.
24
24
  #
25
- # safe_join(["<p>foo</p>".html_safe, "<p>bar</p>"], "<br />")
25
+ # safe_join([raw("<p>foo</p>"), "<p>bar</p>"], "<br />")
26
26
  # # => "<p>foo</p>&lt;br /&gt;&lt;p&gt;bar&lt;/p&gt;"
27
27
  #
28
- # safe_join(["<p>foo</p>".html_safe, "<p>bar</p>".html_safe], "<br />".html_safe)
28
+ # safe_join([raw("<p>foo</p>"), raw("<p>bar</p>")], raw("<br />")
29
29
  # # => "<p>foo</p><br /><p>bar</p>"
30
30
  #
31
31
  def safe_join(array, sep=$,)
@@ -33,6 +33,36 @@ module ActionView #:nodoc:
33
33
 
34
34
  array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
35
35
  end
36
+
37
+ # Converts the array to a comma-separated sentence where the last element is
38
+ # joined by the connector word. This is the html_safe-aware version of
39
+ # ActiveSupport's {Array#to_sentence}[http://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
40
+ #
41
+ def to_sentence(array, options = {})
42
+ options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
43
+
44
+ default_connectors = {
45
+ :words_connector => ', ',
46
+ :two_words_connector => ' and ',
47
+ :last_word_connector => ', and '
48
+ }
49
+ if defined?(I18n)
50
+ i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
51
+ default_connectors.merge!(i18n_connectors)
52
+ end
53
+ options = default_connectors.merge!(options)
54
+
55
+ case array.length
56
+ when 0
57
+ ''.html_safe
58
+ when 1
59
+ ERB::Util.html_escape(array[0])
60
+ when 2
61
+ safe_join([array[0], array[1]], options[:two_words_connector])
62
+ else
63
+ safe_join([safe_join(array[0...-1], options[:words_connector]), options[:last_word_connector], array[-1]])
64
+ end
65
+ end
36
66
  end
37
67
  end
38
68
  end