actionview 5.0.7.2 → 5.1.0.beta1

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 (82) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +92 -384
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/action_view.rb +5 -5
  6. data/lib/action_view/base.rb +19 -19
  7. data/lib/action_view/buffers.rb +1 -1
  8. data/lib/action_view/context.rb +1 -1
  9. data/lib/action_view/dependency_tracker.rb +4 -5
  10. data/lib/action_view/digestor.rb +6 -7
  11. data/lib/action_view/flows.rb +5 -6
  12. data/lib/action_view/gem_version.rb +3 -3
  13. data/lib/action_view/helpers.rb +1 -1
  14. data/lib/action_view/helpers/active_model_helper.rb +8 -8
  15. data/lib/action_view/helpers/asset_tag_helper.rb +62 -36
  16. data/lib/action_view/helpers/asset_url_helper.rb +111 -49
  17. data/lib/action_view/helpers/atom_feed_helper.rb +12 -13
  18. data/lib/action_view/helpers/cache_helper.rb +34 -20
  19. data/lib/action_view/helpers/capture_helper.rb +2 -2
  20. data/lib/action_view/helpers/controller_helper.rb +3 -11
  21. data/lib/action_view/helpers/csrf_helper.rb +3 -3
  22. data/lib/action_view/helpers/date_helper.rb +109 -107
  23. data/lib/action_view/helpers/debug_helper.rb +2 -3
  24. data/lib/action_view/helpers/form_helper.rb +406 -31
  25. data/lib/action_view/helpers/form_options_helper.rb +12 -12
  26. data/lib/action_view/helpers/form_tag_helper.rb +19 -18
  27. data/lib/action_view/helpers/javascript_helper.rb +6 -6
  28. data/lib/action_view/helpers/number_helper.rb +48 -46
  29. data/lib/action_view/helpers/output_safety_helper.rb +8 -8
  30. data/lib/action_view/helpers/rendering_helper.rb +2 -2
  31. data/lib/action_view/helpers/sanitize_helper.rb +6 -8
  32. data/lib/action_view/helpers/tag_helper.rb +194 -77
  33. data/lib/action_view/helpers/tags/base.rb +121 -102
  34. data/lib/action_view/helpers/tags/check_box.rb +17 -17
  35. data/lib/action_view/helpers/tags/collection_check_boxes.rb +8 -8
  36. data/lib/action_view/helpers/tags/collection_helpers.rb +60 -60
  37. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +2 -2
  38. data/lib/action_view/helpers/tags/collection_select.rb +2 -2
  39. data/lib/action_view/helpers/tags/date_select.rb +36 -36
  40. data/lib/action_view/helpers/tags/grouped_collection_select.rb +2 -2
  41. data/lib/action_view/helpers/tags/label.rb +4 -0
  42. data/lib/action_view/helpers/tags/password_field.rb +1 -1
  43. data/lib/action_view/helpers/tags/radio_button.rb +4 -4
  44. data/lib/action_view/helpers/tags/select.rb +9 -9
  45. data/lib/action_view/helpers/tags/text_area.rb +1 -1
  46. data/lib/action_view/helpers/tags/text_field.rb +5 -5
  47. data/lib/action_view/helpers/tags/translator.rb +14 -12
  48. data/lib/action_view/helpers/text_helper.rb +20 -19
  49. data/lib/action_view/helpers/translation_helper.rb +6 -6
  50. data/lib/action_view/helpers/url_helper.rb +42 -38
  51. data/lib/action_view/layouts.rb +51 -47
  52. data/lib/action_view/log_subscriber.rb +24 -9
  53. data/lib/action_view/lookup_context.rb +19 -25
  54. data/lib/action_view/path_set.rb +19 -19
  55. data/lib/action_view/railtie.rb +3 -3
  56. data/lib/action_view/record_identifier.rb +6 -6
  57. data/lib/action_view/renderer/abstract_renderer.rb +17 -17
  58. data/lib/action_view/renderer/partial_renderer.rb +188 -187
  59. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +7 -1
  60. data/lib/action_view/renderer/streaming_template_renderer.rb +45 -47
  61. data/lib/action_view/renderer/template_renderer.rb +64 -66
  62. data/lib/action_view/rendering.rb +4 -5
  63. data/lib/action_view/routing_url_for.rb +9 -13
  64. data/lib/action_view/tasks/cache_digests.rake +7 -7
  65. data/lib/action_view/template.rb +26 -27
  66. data/lib/action_view/template/error.rb +5 -15
  67. data/lib/action_view/template/handlers.rb +4 -4
  68. data/lib/action_view/template/handlers/builder.rb +7 -7
  69. data/lib/action_view/template/handlers/erb.rb +9 -76
  70. data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +9 -0
  71. data/lib/action_view/template/handlers/erb/erubi.rb +81 -0
  72. data/lib/action_view/template/handlers/erb/erubis.rb +81 -0
  73. data/lib/action_view/template/html.rb +2 -4
  74. data/lib/action_view/template/resolver.rb +107 -90
  75. data/lib/action_view/template/text.rb +5 -8
  76. data/lib/action_view/template/types.rb +1 -1
  77. data/lib/action_view/test_case.rb +20 -21
  78. data/lib/action_view/testing/resolvers.rb +29 -30
  79. data/lib/action_view/version.rb +1 -1
  80. data/lib/action_view/view_paths.rb +20 -8
  81. data/lib/assets/compiled/rails-ujs.js +648 -0
  82. metadata +19 -14
@@ -1,9 +1,9 @@
1
- require 'cgi'
2
- require 'erb'
3
- require 'action_view/helpers/form_helper'
4
- require 'active_support/core_ext/string/output_safety'
5
- require 'active_support/core_ext/array/extract_options'
6
- require 'active_support/core_ext/array/wrap'
1
+ require "cgi"
2
+ require "erb"
3
+ require "action_view/helpers/form_helper"
4
+ require "active_support/core_ext/string/output_safety"
5
+ require "active_support/core_ext/array/extract_options"
6
+ require "active_support/core_ext/array/wrap"
7
7
 
8
8
  module ActionView
9
9
  # = Action View Form Option Helpers
@@ -363,7 +363,7 @@ module ActionView
363
363
  html_attributes[:disabled] ||= disabled && option_value_selected?(value, disabled)
364
364
  html_attributes[:value] = value
365
365
 
366
- content_tag_string(:option, text, html_attributes)
366
+ tag_builder.content_tag_string(:option, text, html_attributes)
367
367
  end.join("\n").html_safe
368
368
  end
369
369
 
@@ -578,7 +578,7 @@ module ActionView
578
578
  end
579
579
 
580
580
  zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
581
- zone_options.safe_concat content_tag("option".freeze, '-------------', value: '', disabled: true)
581
+ zone_options.safe_concat content_tag("option".freeze, "-------------", value: "", disabled: true)
582
582
  zone_options.safe_concat "\n"
583
583
 
584
584
  zones = zones - priority_zones
@@ -651,12 +651,12 @@ module ActionView
651
651
  # The HTML specification says when nothing is select on a collection of radio buttons
652
652
  # web browsers do not send any value to server.
653
653
  # Unfortunately this introduces a gotcha:
654
- # if a +User+ model has a +category_id+ field, and in the form none category is selected no +category_id+ parameter is sent. So,
655
- # any strong parameters idiom like
654
+ # if a +User+ model has a +category_id+ field and in the form no category is selected, no +category_id+ parameter is sent. So,
655
+ # any strong parameters idiom like:
656
656
  #
657
657
  # params.require(:user).permit(...)
658
658
  #
659
- # will raise an error since no +{user: ...}+ will be present.
659
+ # will raise an error since no <tt>{user: ...}</tt> will be present.
660
660
  #
661
661
  # To prevent this the helper generates an auxiliary hidden field before
662
662
  # every collection of radio buttons. The hidden field has the same name as collection radio button and blank value.
@@ -800,7 +800,7 @@ module ActionView
800
800
  end
801
801
 
802
802
  def prompt_text(prompt)
803
- prompt.kind_of?(String) ? prompt : I18n.translate('helpers.select.prompt', default: 'Please select')
803
+ prompt.kind_of?(String) ? prompt : I18n.translate("helpers.select.prompt", default: "Please select")
804
804
  end
805
805
  end
806
806
 
@@ -1,7 +1,7 @@
1
- require 'cgi'
2
- require 'action_view/helpers/tag_helper'
3
- require 'active_support/core_ext/string/output_safety'
4
- require 'active_support/core_ext/module/attribute_accessors'
1
+ require "cgi"
2
+ require "action_view/helpers/tag_helper"
3
+ require "active_support/core_ext/string/output_safety"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
5
 
6
6
  module ActionView
7
7
  # = Action View Form Tag Helpers
@@ -134,11 +134,11 @@ 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
+ options_for_blank_options_tag = { value: "" }
138
138
 
139
139
  if include_blank == true
140
- include_blank = ''
141
- options_for_blank_options_tag[:label] = ' '
140
+ include_blank = ""
141
+ options_for_blank_options_tag[:label] = " "
142
142
  end
143
143
 
144
144
  if include_blank
@@ -147,7 +147,7 @@ module ActionView
147
147
  end
148
148
 
149
149
  if prompt = options.delete(:prompt)
150
- option_tags = content_tag("option".freeze, prompt, value: '').safe_concat(option_tags)
150
+ option_tags = content_tag("option".freeze, prompt, value: "").safe_concat(option_tags)
151
151
  end
152
152
 
153
153
  content_tag "select".freeze, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
@@ -449,7 +449,7 @@ module ActionView
449
449
  end
450
450
 
451
451
  # Creates a button element that defines a <tt>submit</tt> button,
452
- # <tt>reset</tt>button or a generic button which can be used in
452
+ # <tt>reset</tt> button or a generic button which can be used in
453
453
  # JavaScript, for example. You can use the button tag as a regular
454
454
  # submit tag but it isn't supported in legacy browsers. However,
455
455
  # the button tag does allow for richer labels such as images and emphasis,
@@ -506,12 +506,12 @@ module ActionView
506
506
  options ||= {}
507
507
  end
508
508
 
509
- options = { 'name' => 'button', 'type' => 'submit' }.merge!(options.stringify_keys)
509
+ options = { "name" => "button", "type" => "submit" }.merge!(options.stringify_keys)
510
510
 
511
511
  if block_given?
512
512
  content_tag :button, options, &block
513
513
  else
514
- content_tag :button, content_or_options || 'Button', options
514
+ content_tag :button, content_or_options || "Button", options
515
515
  end
516
516
  end
517
517
 
@@ -681,7 +681,7 @@ module ActionView
681
681
  # * <tt>:step</tt> - The acceptable value granularity.
682
682
  # * Otherwise accepts the same options as text_field_tag.
683
683
  def datetime_field_tag(name, value = nil, options = {})
684
- text_field_tag(name, value, options.merge(type: 'datetime-local'))
684
+ text_field_tag(name, value, options.merge(type: "datetime-local"))
685
685
  end
686
686
 
687
687
  alias datetime_local_field_tag datetime_field_tag
@@ -845,11 +845,12 @@ module ActionView
845
845
  authenticity_token = html_options.delete("authenticity_token")
846
846
  method = html_options.delete("method").to_s.downcase
847
847
 
848
- method_tag = case method
849
- when 'get'
848
+ method_tag = \
849
+ case method
850
+ when "get"
850
851
  html_options["method"] = "get"
851
- ''
852
- when 'post', ''
852
+ ""
853
+ when "post", ""
853
854
  html_options["method"] = "post"
854
855
  token_tag(authenticity_token, form_options: {
855
856
  action: html_options["action"],
@@ -861,7 +862,7 @@ module ActionView
861
862
  action: html_options["action"],
862
863
  method: method
863
864
  })
864
- end
865
+ end
865
866
 
866
867
  if html_options.delete("enforce_utf8") { true }
867
868
  utf8_enforcer_tag + method_tag
@@ -883,7 +884,7 @@ module ActionView
883
884
 
884
885
  # see http://www.w3.org/TR/html4/types.html#type-name
885
886
  def sanitize_to_id(name)
886
- name.to_s.delete(']').tr('^-a-zA-Z0-9:.', "_")
887
+ name.to_s.delete("]").tr("^-a-zA-Z0-9:.", "_")
887
888
  end
888
889
 
889
890
  def set_default_disable_with(value, tag_options)
@@ -1,11 +1,11 @@
1
- require 'action_view/helpers/tag_helper'
1
+ require "action_view/helpers/tag_helper"
2
2
 
3
3
  module ActionView
4
4
  module Helpers
5
5
  module JavaScriptHelper
6
6
  JS_ESCAPE_MAP = {
7
7
  '\\' => '\\\\',
8
- '</' => '<\/',
8
+ "</" => '<\/',
9
9
  "\r\n" => '\n',
10
10
  "\n" => '\n',
11
11
  "\r" => '\n',
@@ -13,8 +13,8 @@ module ActionView
13
13
  "'" => "\\'"
14
14
  }
15
15
 
16
- JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = '&#x2028;'
17
- JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = '&#x2029;'
16
+ JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = "&#x2028;"
17
+ JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = "&#x2029;"
18
18
 
19
19
  # Escapes carriage returns and single and double quotes for JavaScript segments.
20
20
  #
@@ -24,10 +24,10 @@ module ActionView
24
24
  # $('some_element').replaceWith('<%= j render 'some/element_template' %>');
25
25
  def escape_javascript(javascript)
26
26
  if javascript
27
- result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
27
+ result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) { |match| JS_ESCAPE_MAP[match] }
28
28
  javascript.html_safe? ? result.html_safe : result
29
29
  else
30
- ''
30
+ ""
31
31
  end
32
32
  end
33
33
 
@@ -1,11 +1,10 @@
1
- require 'active_support/core_ext/hash/keys'
2
- require 'active_support/core_ext/string/output_safety'
3
- require 'active_support/number_helper'
1
+ require "active_support/core_ext/hash/keys"
2
+ require "active_support/core_ext/string/output_safety"
3
+ require "active_support/number_helper"
4
4
 
5
5
  module ActionView
6
6
  # = Action View Number Helpers
7
7
  module Helpers #:nodoc:
8
-
9
8
  # Provides methods for converting numbers into formatted strings.
10
9
  # Methods are provided for phone numbers, currency, percentage,
11
10
  # precision, positional notation, file size and pretty printing.
@@ -13,7 +12,6 @@ module ActionView
13
12
  # Most methods expect a +number+ argument, and will return it
14
13
  # unchanged if can't be converted into a valid number.
15
14
  module NumberHelper
16
-
17
15
  # Raised when argument +number+ param given to the helpers is invalid and
18
16
  # the option :raise is set to +true+.
19
17
  class InvalidNumberError < StandardError
@@ -94,7 +92,7 @@ module ActionView
94
92
  # (defaults to "%u%n"). Fields are <tt>%u</tt> for the
95
93
  # currency, and <tt>%n</tt> for the number.
96
94
  # * <tt>:negative_format</tt> - Sets the format for negative
97
- # numbers (defaults to prepending an hyphen to the formatted
95
+ # numbers (defaults to prepending a hyphen to the formatted
98
96
  # number given by <tt>:format</tt>). Accepts the same fields
99
97
  # than <tt>:format</tt>, except <tt>%n</tt> is here the
100
98
  # absolute value of the number.
@@ -173,6 +171,9 @@ module ActionView
173
171
  # to ",").
174
172
  # * <tt>:separator</tt> - Sets the separator between the
175
173
  # fractional and integer digits (defaults to ".").
174
+ # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for
175
+ # deriving the placement of delimiter. Helpful when using currency formats
176
+ # like INR.
176
177
  # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
177
178
  # the argument is invalid.
178
179
  #
@@ -189,6 +190,9 @@ module ActionView
189
190
  # number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
190
191
  # # => 98 765 432,98
191
192
  #
193
+ # number_with_delimiter("123456.78",
194
+ # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/) # => "1,23,456.78"
195
+ #
192
196
  # number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
193
197
  def number_with_delimiter(number, options = {})
194
198
  delegate_number_helper_method(:number_to_delimited, number, options)
@@ -263,8 +267,6 @@ module ActionView
263
267
  # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
264
268
  # insignificant zeros after the decimal separator (defaults to
265
269
  # +true+)
266
- # * <tt>:prefix</tt> - If +:si+ formats the number using the SI
267
- # prefix (defaults to :binary)
268
270
  # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
269
271
  # the argument is invalid.
270
272
  #
@@ -395,53 +397,53 @@ module ActionView
395
397
 
396
398
  private
397
399
 
398
- def delegate_number_helper_method(method, number, options)
399
- return unless number
400
- options = escape_unsafe_options(options.symbolize_keys)
400
+ def delegate_number_helper_method(method, number, options)
401
+ return unless number
402
+ options = escape_unsafe_options(options.symbolize_keys)
401
403
 
402
- wrap_with_output_safety_handling(number, options.delete(:raise)) {
403
- ActiveSupport::NumberHelper.public_send(method, number, options)
404
- }
405
- end
404
+ wrap_with_output_safety_handling(number, options.delete(:raise)) {
405
+ ActiveSupport::NumberHelper.public_send(method, number, options)
406
+ }
407
+ end
406
408
 
407
- def escape_unsafe_options(options)
408
- options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
409
- options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format]
410
- options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
411
- options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter]
412
- options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe?
413
- options[:units] = escape_units(options[:units]) if options[:units] && Hash === options[:units]
414
- options
415
- end
409
+ def escape_unsafe_options(options)
410
+ options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
411
+ options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format]
412
+ options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
413
+ options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter]
414
+ options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe?
415
+ options[:units] = escape_units(options[:units]) if options[:units] && Hash === options[:units]
416
+ options
417
+ end
416
418
 
417
- def escape_units(units)
418
- Hash[units.map do |k, v|
419
- [k, ERB::Util.html_escape(v)]
420
- end]
421
- end
419
+ def escape_units(units)
420
+ Hash[units.map do |k, v|
421
+ [k, ERB::Util.html_escape(v)]
422
+ end]
423
+ end
422
424
 
423
- def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
424
- valid_float = valid_float?(number)
425
- raise InvalidNumberError, number if raise_on_invalid && !valid_float
425
+ def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
426
+ valid_float = valid_float?(number)
427
+ raise InvalidNumberError, number if raise_on_invalid && !valid_float
426
428
 
427
- formatted_number = yield
429
+ formatted_number = yield
428
430
 
429
- if valid_float || number.html_safe?
430
- formatted_number.html_safe
431
- else
432
- formatted_number
431
+ if valid_float || number.html_safe?
432
+ formatted_number.html_safe
433
+ else
434
+ formatted_number
435
+ end
433
436
  end
434
- end
435
437
 
436
- def valid_float?(number)
437
- !parse_float(number, false).nil?
438
- end
438
+ def valid_float?(number)
439
+ !parse_float(number, false).nil?
440
+ end
439
441
 
440
- def parse_float(number, raise_error)
441
- Float(number)
442
- rescue ArgumentError, TypeError
443
- raise InvalidNumberError, number if raise_error
444
- end
442
+ def parse_float(number, raise_error)
443
+ Float(number)
444
+ rescue ArgumentError, TypeError
445
+ raise InvalidNumberError, number if raise_error
446
+ end
445
447
  end
446
448
  end
447
449
  end
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/string/output_safety'
1
+ require "active_support/core_ext/string/output_safety"
2
2
 
3
3
  module ActionView #:nodoc:
4
4
  # = Action View Raw Output Helper
@@ -25,10 +25,10 @@ module ActionView #:nodoc:
25
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([raw("<p>foo</p>"), raw("<p>bar</p>")], raw("<br />")
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
- def safe_join(array, sep=$,)
31
+ def safe_join(array, sep = $,)
32
32
  sep = ERB::Util.unwrapped_html_escape(sep)
33
33
 
34
34
  array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
@@ -42,9 +42,9 @@ module ActionView #:nodoc:
42
42
  options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
43
43
 
44
44
  default_connectors = {
45
- :words_connector => ', ',
46
- :two_words_connector => ' and ',
47
- :last_word_connector => ', and '
45
+ words_connector: ", ",
46
+ two_words_connector: " and ",
47
+ last_word_connector: ", and "
48
48
  }
49
49
  if defined?(I18n)
50
50
  i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
@@ -54,13 +54,13 @@ module ActionView #:nodoc:
54
54
 
55
55
  case array.length
56
56
  when 0
57
- ''.html_safe
57
+ "".html_safe
58
58
  when 1
59
59
  ERB::Util.html_escape(array[0])
60
60
  when 2
61
61
  safe_join([array[0], array[1]], options[:two_words_connector])
62
62
  else
63
- safe_join([safe_join(array[0...-1], options[:words_connector]), options[:last_word_connector], array[-1]])
63
+ safe_join([safe_join(array[0...-1], options[:words_connector]), options[:last_word_connector], array[-1]], nil)
64
64
  end
65
65
  end
66
66
  end
@@ -27,12 +27,12 @@ module ActionView
27
27
  case options
28
28
  when Hash
29
29
  if block_given?
30
- view_renderer.render_partial(self, options.merge(:partial => options[:layout]), &block)
30
+ view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
31
31
  else
32
32
  view_renderer.render(self, options)
33
33
  end
34
34
  else
35
- view_renderer.render_partial(self, :partial => options, :locals => locals, &block)
35
+ view_renderer.render_partial(self, partial: options, locals: locals, &block)
36
36
  end
37
37
  end
38
38
 
@@ -1,5 +1,5 @@
1
- require 'active_support/core_ext/object/try'
2
- require 'rails-html-sanitizer'
1
+ require "active_support/core_ext/object/try"
2
+ require "rails-html-sanitizer"
3
3
 
4
4
  module ActionView
5
5
  # = Action View Sanitize Helpers
@@ -45,17 +45,15 @@ module ActionView
45
45
  # Providing a custom Rails::Html scrubber:
46
46
  #
47
47
  # class CommentScrubber < Rails::Html::PermitScrubber
48
- # def allowed_node?(node)
49
- # !%w(form script comment blockquote).include?(node.name)
48
+ # def initialize
49
+ # super
50
+ # self.tags = %w( form script comment blockquote )
51
+ # self.attributes = %w( style )
50
52
  # end
51
53
  #
52
54
  # def skip_node?(node)
53
55
  # node.text?
54
56
  # end
55
- #
56
- # def scrub_attribute?(name)
57
- # name == 'style'
58
- # end
59
57
  # end
60
58
  #
61
59
  # <%= sanitize @comment.body, scrubber: CommentScrubber.new %>
@@ -1,39 +1,207 @@
1
- require 'active_support/core_ext/string/output_safety'
2
- require 'set'
1
+ # frozen-string-literal: true
2
+
3
+ require "active_support/core_ext/string/output_safety"
4
+ require "set"
3
5
 
4
6
  module ActionView
5
7
  # = Action View Tag Helpers
6
8
  module Helpers #:nodoc:
7
- # Provides methods to generate HTML tags programmatically when you can't use
8
- # a Builder. By default, they output XHTML compliant tags.
9
+ # Provides methods to generate HTML tags programmatically both as a modern
10
+ # HTML5 compliant builder style and legacy XHTML compliant tags.
9
11
  module TagHelper
10
12
  extend ActiveSupport::Concern
11
13
  include CaptureHelper
12
14
  include OutputSafetyHelper
13
15
 
14
- BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
15
- autoplay controls loop selected hidden scoped async
16
- defer reversed ismap seamless muted required
17
- autofocus novalidate formnovalidate open pubdate
18
- itemscope allowfullscreen default inert sortable
19
- truespeed typemustmatch).to_set
16
+ BOOLEAN_ATTRIBUTES = %w(allowfullscreen async autofocus autoplay checked
17
+ compact controls declare default defaultchecked
18
+ defaultmuted defaultselected defer disabled
19
+ enabled formnovalidate hidden indeterminate inert
20
+ ismap itemscope loop multiple muted nohref
21
+ noresize noshade novalidate nowrap open
22
+ pauseonexit readonly required reversed scoped
23
+ seamless selected sortable truespeed typemustmatch
24
+ visible).to_set
20
25
 
21
26
  BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
22
27
 
23
- TAG_PREFIXES = ['aria', 'data', :aria, :data].to_set
28
+ TAG_PREFIXES = ["aria", "data", :aria, :data].to_set
24
29
 
25
- PRE_CONTENT_STRINGS = Hash.new { "".freeze }
30
+ PRE_CONTENT_STRINGS = Hash.new { "" }
26
31
  PRE_CONTENT_STRINGS[:textarea] = "\n"
27
32
  PRE_CONTENT_STRINGS["textarea"] = "\n"
28
33
 
34
+ class TagBuilder #:nodoc:
35
+ include CaptureHelper
36
+ include OutputSafetyHelper
37
+
38
+ VOID_ELEMENTS = %i(area base br col embed hr img input keygen link meta param source track wbr).to_set
39
+
40
+ def initialize(view_context)
41
+ @view_context = view_context
42
+ end
43
+
44
+ def tag_string(name, content = nil, escape_attributes: true, **options, &block)
45
+ content = @view_context.capture(self, &block) if block_given?
46
+ if VOID_ELEMENTS.include?(name) && content.nil?
47
+ "<#{name.to_s.dasherize}#{tag_options(options, escape_attributes)}>".html_safe
48
+ else
49
+ content_tag_string(name.to_s.dasherize, content || "", options, escape_attributes)
50
+ end
51
+ end
52
+
53
+ def content_tag_string(name, content, options, escape = true)
54
+ tag_options = tag_options(options, escape) if options
55
+ content = ERB::Util.unwrapped_html_escape(content) if escape
56
+ "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
57
+ end
58
+
59
+ def tag_options(options, escape = true)
60
+ return if options.blank?
61
+ output = "".dup
62
+ sep = " "
63
+ options.each_pair do |key, value|
64
+ if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
65
+ value.each_pair do |k, v|
66
+ next if v.nil?
67
+ output << sep
68
+ output << prefix_tag_option(key, k, v, escape)
69
+ end
70
+ elsif BOOLEAN_ATTRIBUTES.include?(key)
71
+ if value
72
+ output << sep
73
+ output << boolean_tag_option(key)
74
+ end
75
+ elsif !value.nil?
76
+ output << sep
77
+ output << tag_option(key, value, escape)
78
+ end
79
+ end
80
+ output unless output.empty?
81
+ end
82
+
83
+ def boolean_tag_option(key)
84
+ %(#{key}="#{key}")
85
+ end
29
86
 
30
- # Returns an empty HTML tag of type +name+ which by default is XHTML
87
+ def tag_option(key, value, escape)
88
+ if value.is_a?(Array)
89
+ value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
90
+ else
91
+ value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
92
+ end
93
+ %(#{key}="#{value.gsub('"'.freeze, '&quot;'.freeze)}")
94
+ end
95
+
96
+ private
97
+ def prefix_tag_option(prefix, key, value, escape)
98
+ key = "#{prefix}-#{key.to_s.dasherize}"
99
+ unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
100
+ value = value.to_json
101
+ end
102
+ tag_option(key, value, escape)
103
+ end
104
+
105
+ def respond_to_missing?(*args)
106
+ true
107
+ end
108
+
109
+ def method_missing(called, *args, &block)
110
+ tag_string(called, *args, &block)
111
+ end
112
+ end
113
+
114
+ # Returns an HTML tag.
115
+ #
116
+ # === Building HTML tags
117
+ #
118
+ # Builds HTML5 compliant tags with a tag proxy. Every tag can be built with:
119
+ #
120
+ # tag.<tag name>(optional content, options)
121
+ #
122
+ # where tag name can be e.g. br, div, section, article, or any tag really.
123
+ #
124
+ # ==== Passing content
125
+ #
126
+ # Tags can pass content to embed within it:
127
+ #
128
+ # tag.h1 'All titles fit to print' # => <h1>All titles fit to print</h1>
129
+ #
130
+ # tag.div tag.p('Hello world!') # => <div><p>Hello world!</p></div>
131
+ #
132
+ # Content can also be captured with a block, which is useful in templates:
133
+ #
134
+ # <%= tag.p do %>
135
+ # The next great American novel starts here.
136
+ # <% end %>
137
+ # # => <p>The next great American novel starts here.</p>
138
+ #
139
+ # ==== Options
140
+ #
141
+ # Use symbol keyed options to add attributes to the generated tag.
142
+ #
143
+ # tag.section class: %w( kitties puppies )
144
+ # # => <section class="kitties puppies"></section>
145
+ #
146
+ # tag.section id: dom_id(@post)
147
+ # # => <section id="<generated dom id>"></section>
148
+ #
149
+ # Pass +true+ for any attributes that can render with no values, like +disabled+ and +readonly+.
150
+ #
151
+ # tag.input type: 'text', disabled: true
152
+ # # => <input type="text" disabled="disabled">
153
+ #
154
+ # HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
155
+ # pointing to a hash of sub-attributes.
156
+ #
157
+ # To play nicely with JavaScript conventions, sub-attributes are dasherized.
158
+ #
159
+ # tag.article data: { user_id: 123 }
160
+ # # => <article data-user-id="123"></article>
161
+ #
162
+ # Thus <tt>data-user-id</tt> can be accessed as <tt>dataset.userId</tt>.
163
+ #
164
+ # Data attribute values are encoded to JSON, with the exception of strings, symbols and
165
+ # BigDecimals.
166
+ # This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
167
+ # from 1.4.3.
168
+ #
169
+ # tag.div data: { city_state: %w( Chigaco IL ) }
170
+ # # => <div data-city-state="[&quot;Chicago&quot;,&quot;IL&quot;]"></div>
171
+ #
172
+ # The generated attributes are escaped by default. This can be disabled using
173
+ # +escape_attributes+.
174
+ #
175
+ # tag.img src: 'open & shut.png'
176
+ # # => <img src="open &amp; shut.png">
177
+ #
178
+ # tag.img src: 'open & shut.png', escape_attributes: false
179
+ # # => <img src="open & shut.png">
180
+ #
181
+ # The tag builder respects
182
+ # {HTML5 void elements}[https://www.w3.org/TR/html5/syntax.html#void-elements]
183
+ # if no content is passed, and omits closing tags for those elements.
184
+ #
185
+ # # A standard element:
186
+ # tag.div # => <div></div>
187
+ #
188
+ # # A void element:
189
+ # tag.br # => <br>
190
+ #
191
+ # === Legacy syntax
192
+ #
193
+ # The following format is for legacy syntax support. It will be deprecated in future versions of Rails.
194
+ #
195
+ # tag(name, options = nil, open = false, escape = true)
196
+ #
197
+ # It returns an empty HTML tag of type +name+ which by default is XHTML
31
198
  # compliant. Set +open+ to true to create an open tag compatible
32
199
  # with HTML 4.0 and below. Add HTML attributes by passing an attributes
33
200
  # hash to +options+. Set +escape+ to false to disable attribute value
34
201
  # escaping.
35
202
  #
36
203
  # ==== Options
204
+ #
37
205
  # You can use symbols or strings for the attribute names.
38
206
  #
39
207
  # Use +true+ with boolean attributes that can render with no value, like
@@ -42,16 +210,8 @@ module ActionView
42
210
  # HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
43
211
  # pointing to a hash of sub-attributes.
44
212
  #
45
- # To play nicely with JavaScript conventions sub-attributes are dasherized.
46
- # For example, a key +user_id+ would render as <tt>data-user-id</tt> and
47
- # thus accessed as <tt>dataset.userId</tt>.
48
- #
49
- # Values are encoded to JSON, with the exception of strings, symbols and
50
- # BigDecimals.
51
- # This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
52
- # from 1.4.3.
53
- #
54
213
  # ==== Examples
214
+ #
55
215
  # tag("br")
56
216
  # # => <br />
57
217
  #
@@ -72,8 +232,12 @@ module ActionView
72
232
  #
73
233
  # tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
74
234
  # # => <div data-name="Stephen" data-city-state="[&quot;Chicago&quot;,&quot;IL&quot;]" />
75
- def tag(name, options = nil, open = false, escape = true)
76
- "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
235
+ def tag(name = nil, options = nil, open = false, escape = true)
236
+ if name.nil?
237
+ tag_builder
238
+ else
239
+ "<#{name}#{tag_builder.tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
240
+ end
77
241
  end
78
242
 
79
243
  # Returns an HTML block tag of type +name+ surrounding the +content+. Add
@@ -81,6 +245,7 @@ module ActionView
81
245
  # Instead of passing the content as an argument, you can also use a block
82
246
  # in which case, you pass your +options+ as the second parameter.
83
247
  # Set escape to false to disable attribute value escaping.
248
+ # Note: this is legacy syntax, see +tag+ method description for details.
84
249
  #
85
250
  # ==== Options
86
251
  # The +options+ hash can be used with attributes with no value like (<tt>disabled</tt> and
@@ -104,9 +269,9 @@ module ActionView
104
269
  def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
105
270
  if block_given?
106
271
  options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
107
- content_tag_string(name, capture(&block), options, escape)
272
+ tag_builder.content_tag_string(name, capture(&block), options, escape)
108
273
  else
109
- content_tag_string(name, content_or_options_with_block, options, escape)
274
+ tag_builder.content_tag_string(name, content_or_options_with_block, options, escape)
110
275
  end
111
276
  end
112
277
 
@@ -124,7 +289,7 @@ module ActionView
124
289
  # cdata_section("hello]]>world")
125
290
  # # => <![CDATA[hello]]]]><![CDATA[>world]]>
126
291
  def cdata_section(content)
127
- splitted = content.to_s.gsub(/\]\]\>/, ']]]]><![CDATA[>')
292
+ splitted = content.to_s.gsub(/\]\]\>/, "]]]]><![CDATA[>")
128
293
  "<![CDATA[#{splitted}]]>".html_safe
129
294
  end
130
295
 
@@ -140,56 +305,8 @@ module ActionView
140
305
  end
141
306
 
142
307
  private
143
-
144
- def content_tag_string(name, content, options, escape = true)
145
- tag_options = tag_options(options, escape) if options
146
- content = ERB::Util.unwrapped_html_escape(content) if escape
147
- "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
148
- end
149
-
150
- def tag_options(options, escape = true)
151
- return if options.blank?
152
- output = ""
153
- sep = " ".freeze
154
- options.each_pair do |key, value|
155
- if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
156
- value.each_pair do |k, v|
157
- next if v.nil?
158
- output << sep
159
- output << prefix_tag_option(key, k, v, escape)
160
- end
161
- elsif BOOLEAN_ATTRIBUTES.include?(key)
162
- if value
163
- output << sep
164
- output << boolean_tag_option(key)
165
- end
166
- elsif !value.nil?
167
- output << sep
168
- output << tag_option(key, value, escape)
169
- end
170
- end
171
- output unless output.empty?
172
- end
173
-
174
- def prefix_tag_option(prefix, key, value, escape)
175
- key = "#{prefix}-#{key.to_s.dasherize}"
176
- unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
177
- value = value.to_json
178
- end
179
- tag_option(key, value, escape)
180
- end
181
-
182
- def boolean_tag_option(key)
183
- %(#{key}="#{key}")
184
- end
185
-
186
- def tag_option(key, value, escape)
187
- if value.is_a?(Array)
188
- value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
189
- else
190
- value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
191
- end
192
- %(#{key}="#{value.gsub('"'.freeze, '&quot;'.freeze)}")
308
+ def tag_builder
309
+ @tag_builder ||= TagBuilder.new(self)
193
310
  end
194
311
  end
195
312
  end