actionview 4.2.11.1 → 6.1.5.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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +243 -186
  3. data/MIT-LICENSE +1 -2
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +115 -39
  6. data/lib/action_view/buffers.rb +18 -1
  7. data/lib/action_view/cache_expiry.rb +52 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +61 -21
  10. data/lib/action_view/digestor.rb +89 -85
  11. data/lib/action_view/flows.rb +11 -12
  12. data/lib/action_view/gem_version.rb +5 -3
  13. data/lib/action_view/helpers/active_model_helper.rb +16 -11
  14. data/lib/action_view/helpers/asset_tag_helper.rb +282 -83
  15. data/lib/action_view/helpers/asset_url_helper.rb +175 -69
  16. data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
  17. data/lib/action_view/helpers/cache_helper.rb +107 -43
  18. data/lib/action_view/helpers/capture_helper.rb +20 -13
  19. data/lib/action_view/helpers/controller_helper.rb +15 -4
  20. data/lib/action_view/helpers/csp_helper.rb +26 -0
  21. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  22. data/lib/action_view/helpers/date_helper.rb +232 -130
  23. data/lib/action_view/helpers/debug_helper.rb +7 -6
  24. data/lib/action_view/helpers/form_helper.rb +808 -146
  25. data/lib/action_view/helpers/form_options_helper.rb +124 -78
  26. data/lib/action_view/helpers/form_tag_helper.rb +120 -74
  27. data/lib/action_view/helpers/javascript_helper.rb +33 -17
  28. data/lib/action_view/helpers/number_helper.rb +87 -62
  29. data/lib/action_view/helpers/output_safety_helper.rb +36 -4
  30. data/lib/action_view/helpers/rendering_helper.rb +21 -10
  31. data/lib/action_view/helpers/sanitize_helper.rb +30 -31
  32. data/lib/action_view/helpers/tag_helper.rb +302 -69
  33. data/lib/action_view/helpers/tags/base.rb +141 -97
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -19
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  41. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  43. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
  50. data/lib/action_view/helpers/tags/label.rb +7 -2
  51. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  58. data/lib/action_view/helpers/tags/select.rb +11 -10
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  61. data/lib/action_view/helpers/tags/text_field.rb +8 -8
  62. data/lib/action_view/helpers/tags/time_field.rb +3 -2
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +15 -16
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  68. data/lib/action_view/helpers/tags.rb +3 -1
  69. data/lib/action_view/helpers/text_helper.rb +56 -38
  70. data/lib/action_view/helpers/translation_helper.rb +150 -68
  71. data/lib/action_view/helpers/url_helper.rb +284 -117
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +68 -63
  74. data/lib/action_view/log_subscriber.rb +77 -10
  75. data/lib/action_view/lookup_context.rb +134 -91
  76. data/lib/action_view/model_naming.rb +3 -1
  77. data/lib/action_view/path_set.rb +26 -24
  78. data/lib/action_view/railtie.rb +62 -13
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +151 -14
  81. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  82. data/lib/action_view/renderer/object_renderer.rb +34 -0
  83. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  84. data/lib/action_view/renderer/partial_renderer.rb +55 -303
  85. data/lib/action_view/renderer/renderer.rb +66 -9
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
  87. data/lib/action_view/renderer/template_renderer.rb +82 -73
  88. data/lib/action_view/rendering.rb +71 -45
  89. data/lib/action_view/routing_url_for.rb +34 -23
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template/error.rb +44 -29
  92. data/lib/action_view/template/handlers/builder.rb +12 -13
  93. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  94. data/lib/action_view/template/handlers/erb.rb +23 -89
  95. data/lib/action_view/template/handlers/html.rb +11 -0
  96. data/lib/action_view/template/handlers/raw.rb +4 -4
  97. data/lib/action_view/template/handlers.rb +12 -8
  98. data/lib/action_view/template/html.rb +10 -11
  99. data/lib/action_view/template/inline.rb +22 -0
  100. data/lib/action_view/template/raw_file.rb +25 -0
  101. data/lib/action_view/template/renderable.rb +24 -0
  102. data/lib/action_view/template/resolver.rb +263 -197
  103. data/lib/action_view/template/sources/file.rb +17 -0
  104. data/lib/action_view/template/sources.rb +13 -0
  105. data/lib/action_view/template/text.rb +8 -10
  106. data/lib/action_view/template/types.rb +18 -18
  107. data/lib/action_view/template.rb +108 -92
  108. data/lib/action_view/test_case.rb +66 -53
  109. data/lib/action_view/testing/resolvers.rb +24 -33
  110. data/lib/action_view/unbound_template.rb +31 -0
  111. data/lib/action_view/version.rb +3 -1
  112. data/lib/action_view/view_paths.rb +73 -58
  113. data/lib/action_view.rb +14 -8
  114. data/lib/assets/compiled/rails-ujs.js +746 -0
  115. metadata +45 -32
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,38 +1,271 @@
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 allowpaymentrequest async autofocus
17
+ autoplay checked compact controls declare default
18
+ defaultchecked defaultmuted defaultselected defer
19
+ disabled enabled formnovalidate hidden indeterminate
20
+ inert ismap itemscope loop multiple muted nohref
21
+ nomodule noresize noshade novalidate nowrap open
22
+ pauseonexit playsinline readonly required reversed
23
+ scoped seamless selected sortable truespeed
24
+ typemustmatch visible).to_set
25
+
26
+ BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
27
+ BOOLEAN_ATTRIBUTES.freeze
28
+
29
+ ARIA_PREFIXES = ["aria", :aria].to_set.freeze
30
+ DATA_PREFIXES = ["data", :data].to_set.freeze
31
+
32
+ TAG_TYPES = {}
33
+ TAG_TYPES.merge! BOOLEAN_ATTRIBUTES.index_with(:boolean)
34
+ TAG_TYPES.merge! DATA_PREFIXES.index_with(:data)
35
+ TAG_TYPES.merge! ARIA_PREFIXES.index_with(:aria)
36
+ TAG_TYPES.freeze
37
+
38
+ PRE_CONTENT_STRINGS = Hash.new { "" }
39
+ PRE_CONTENT_STRINGS[:textarea] = "\n"
40
+ PRE_CONTENT_STRINGS["textarea"] = "\n"
41
+
42
+ class TagBuilder #:nodoc:
43
+ include CaptureHelper
44
+ include OutputSafetyHelper
45
+
46
+ VOID_ELEMENTS = %i(area base br col embed hr img input keygen link meta param source track wbr).to_set
47
+
48
+ def initialize(view_context)
49
+ @view_context = view_context
50
+ end
51
+
52
+ def p(*arguments, **options, &block)
53
+ tag_string(:p, *arguments, **options, &block)
54
+ end
55
+
56
+ def tag_string(name, content = nil, **options, &block)
57
+ escape = handle_deprecated_escape_options(options)
58
+
59
+ content = @view_context.capture(self, &block) if block_given?
60
+ if VOID_ELEMENTS.include?(name) && content.nil?
61
+ "<#{name.to_s.dasherize}#{tag_options(options, escape)}>".html_safe
62
+ else
63
+ content_tag_string(name.to_s.dasherize, content || "", options, escape)
64
+ end
65
+ end
66
+
67
+ def content_tag_string(name, content, options, escape = true)
68
+ tag_options = tag_options(options, escape) if options
20
69
 
21
- BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym })
70
+ if escape
71
+ name = ERB::Util.xml_name_escape(name)
72
+ content = ERB::Util.unwrapped_html_escape(content)
73
+ end
22
74
 
23
- TAG_PREFIXES = ['aria', 'data', :aria, :data].to_set
75
+ "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
76
+ end
24
77
 
25
- PRE_CONTENT_STRINGS = {
26
- :textarea => "\n"
27
- }
78
+ def tag_options(options, escape = true)
79
+ return if options.blank?
80
+ output = +""
81
+ sep = " "
82
+ options.each_pair do |key, value|
83
+ type = TAG_TYPES[key]
84
+ if type == :data && value.is_a?(Hash)
85
+ value.each_pair do |k, v|
86
+ next if v.nil?
87
+ output << sep
88
+ output << prefix_tag_option(key, k, v, escape)
89
+ end
90
+ elsif type == :aria && value.is_a?(Hash)
91
+ value.each_pair do |k, v|
92
+ next if v.nil?
93
+
94
+ case v
95
+ when Array, Hash
96
+ tokens = TagHelper.build_tag_values(v)
97
+ next if tokens.none?
28
98
 
29
- # Returns an empty HTML tag of type +name+ which by default is XHTML
99
+ v = safe_join(tokens, " ")
100
+ else
101
+ v = v.to_s
102
+ end
103
+
104
+ output << sep
105
+ output << prefix_tag_option(key, k, v, escape)
106
+ end
107
+ elsif type == :boolean
108
+ if value
109
+ output << sep
110
+ output << boolean_tag_option(key)
111
+ end
112
+ elsif !value.nil?
113
+ output << sep
114
+ output << tag_option(key, value, escape)
115
+ end
116
+ end
117
+ output unless output.empty?
118
+ end
119
+
120
+ def boolean_tag_option(key)
121
+ %(#{key}="#{key}")
122
+ end
123
+
124
+ def tag_option(key, value, escape)
125
+ key = ERB::Util.xml_name_escape(key) if escape
126
+
127
+ case value
128
+ when Array, Hash
129
+ value = TagHelper.build_tag_values(value) if key.to_s == "class"
130
+ value = escape ? safe_join(value, " ") : value.join(" ")
131
+ else
132
+ value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
133
+ end
134
+ value = value.gsub('"', "&quot;") if value.include?('"')
135
+
136
+ %(#{key}="#{value}")
137
+ end
138
+
139
+ private
140
+ def prefix_tag_option(prefix, key, value, escape)
141
+ key = "#{prefix}-#{key.to_s.dasherize}"
142
+ unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
143
+ value = value.to_json
144
+ end
145
+ tag_option(key, value, escape)
146
+ end
147
+
148
+ def respond_to_missing?(*args)
149
+ true
150
+ end
151
+
152
+ def handle_deprecated_escape_options(options)
153
+ # The option :escape_attributes has been merged into the options hash to be
154
+ # able to warn when it is used, so we need to handle default values here.
155
+ escape_option_provided = options.has_key?(:escape)
156
+ escape_attributes_option_provided = options.has_key?(:escape_attributes)
157
+
158
+ if escape_attributes_option_provided
159
+ ActiveSupport::Deprecation.warn(<<~MSG)
160
+ Use of the option :escape_attributes is deprecated. It currently \
161
+ escapes both names and values of tags and attributes and it is \
162
+ equivalent to :escape. If any of them are enabled, the escaping \
163
+ is fully enabled.
164
+ MSG
165
+ end
166
+
167
+ return true unless escape_option_provided || escape_attributes_option_provided
168
+ escape_option = options.delete(:escape)
169
+ escape_attributes_option = options.delete(:escape_attributes)
170
+ escape_option || escape_attributes_option
171
+ end
172
+
173
+ def method_missing(called, *args, **options, &block)
174
+ tag_string(called, *args, **options, &block)
175
+ end
176
+ end
177
+
178
+ # Returns an HTML tag.
179
+ #
180
+ # === Building HTML tags
181
+ #
182
+ # Builds HTML5 compliant tags with a tag proxy. Every tag can be built with:
183
+ #
184
+ # tag.<tag name>(optional content, options)
185
+ #
186
+ # where tag name can be e.g. br, div, section, article, or any tag really.
187
+ #
188
+ # ==== Passing content
189
+ #
190
+ # Tags can pass content to embed within it:
191
+ #
192
+ # tag.h1 'All titles fit to print' # => <h1>All titles fit to print</h1>
193
+ #
194
+ # tag.div tag.p('Hello world!') # => <div><p>Hello world!</p></div>
195
+ #
196
+ # Content can also be captured with a block, which is useful in templates:
197
+ #
198
+ # <%= tag.p do %>
199
+ # The next great American novel starts here.
200
+ # <% end %>
201
+ # # => <p>The next great American novel starts here.</p>
202
+ #
203
+ # ==== Options
204
+ #
205
+ # Use symbol keyed options to add attributes to the generated tag.
206
+ #
207
+ # tag.section class: %w( kitties puppies )
208
+ # # => <section class="kitties puppies"></section>
209
+ #
210
+ # tag.section id: dom_id(@post)
211
+ # # => <section id="<generated dom id>"></section>
212
+ #
213
+ # Pass +true+ for any attributes that can render with no values, like +disabled+ and +readonly+.
214
+ #
215
+ # tag.input type: 'text', disabled: true
216
+ # # => <input type="text" disabled="disabled">
217
+ #
218
+ # HTML5 <tt>data-*</tt> and <tt>aria-*</tt> attributes can be set with a
219
+ # single +data+ or +aria+ key pointing to a hash of sub-attributes.
220
+ #
221
+ # To play nicely with JavaScript conventions, sub-attributes are dasherized.
222
+ #
223
+ # tag.article data: { user_id: 123 }
224
+ # # => <article data-user-id="123"></article>
225
+ #
226
+ # Thus <tt>data-user-id</tt> can be accessed as <tt>dataset.userId</tt>.
227
+ #
228
+ # Data attribute values are encoded to JSON, with the exception of strings, symbols and
229
+ # BigDecimals.
230
+ # This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
231
+ # from 1.4.3.
232
+ #
233
+ # tag.div data: { city_state: %w( Chicago IL ) }
234
+ # # => <div data-city-state="[&quot;Chicago&quot;,&quot;IL&quot;]"></div>
235
+ #
236
+ # The generated tag names and attributes are escaped by default. This can be disabled using
237
+ # +escape+.
238
+ #
239
+ # tag.img src: 'open & shut.png'
240
+ # # => <img src="open &amp; shut.png">
241
+ #
242
+ # tag.img src: 'open & shut.png', escape: false
243
+ # # => <img src="open & shut.png">
244
+ #
245
+ # The tag builder respects
246
+ # {HTML5 void elements}[https://www.w3.org/TR/html5/syntax.html#void-elements]
247
+ # if no content is passed, and omits closing tags for those elements.
248
+ #
249
+ # # A standard element:
250
+ # tag.div # => <div></div>
251
+ #
252
+ # # A void element:
253
+ # tag.br # => <br>
254
+ #
255
+ # === Legacy syntax
256
+ #
257
+ # The following format is for legacy syntax support. It will be deprecated in future versions of Rails.
258
+ #
259
+ # tag(name, options = nil, open = false, escape = true)
260
+ #
261
+ # It returns an empty HTML tag of type +name+ which by default is XHTML
30
262
  # compliant. Set +open+ to true to create an open tag compatible
31
263
  # with HTML 4.0 and below. Add HTML attributes by passing an attributes
32
264
  # hash to +options+. Set +escape+ to false to disable attribute value
33
265
  # escaping.
34
266
  #
35
267
  # ==== Options
268
+ #
36
269
  # You can use symbols or strings for the attribute names.
37
270
  #
38
271
  # Use +true+ with boolean attributes that can render with no value, like
@@ -41,16 +274,8 @@ module ActionView
41
274
  # HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
42
275
  # pointing to a hash of sub-attributes.
43
276
  #
44
- # To play nicely with JavaScript conventions sub-attributes are dasherized.
45
- # For example, a key +user_id+ would render as <tt>data-user-id</tt> and
46
- # thus accessed as <tt>dataset.userId</tt>.
47
- #
48
- # Values are encoded to JSON, with the exception of strings, symbols and
49
- # BigDecimals.
50
- # This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
51
- # from 1.4.3.
52
- #
53
277
  # ==== Examples
278
+ #
54
279
  # tag("br")
55
280
  # # => <br />
56
281
  #
@@ -66,20 +291,29 @@ module ActionView
66
291
  # tag("img", src: "open & shut.png")
67
292
  # # => <img src="open &amp; shut.png" />
68
293
  #
69
- # tag("img", {src: "open &amp; shut.png"}, false, false)
294
+ # tag("img", { src: "open &amp; shut.png" }, false, false)
70
295
  # # => <img src="open &amp; shut.png" />
71
296
  #
72
- # tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
297
+ # tag("div", data: { name: 'Stephen', city_state: %w(Chicago IL) })
73
298
  # # => <div data-name="Stephen" data-city-state="[&quot;Chicago&quot;,&quot;IL&quot;]" />
74
- def tag(name, options = nil, open = false, escape = true)
75
- "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
299
+ #
300
+ # tag("div", class: { highlight: current_user.admin? })
301
+ # # => <div class="highlight" />
302
+ def tag(name = nil, options = nil, open = false, escape = true)
303
+ if name.nil?
304
+ tag_builder
305
+ else
306
+ name = ERB::Util.xml_name_escape(name) if escape
307
+ "<#{name}#{tag_builder.tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
308
+ end
76
309
  end
77
310
 
78
311
  # Returns an HTML block tag of type +name+ surrounding the +content+. Add
79
312
  # HTML attributes by passing an attributes hash to +options+.
80
313
  # Instead of passing the content as an argument, you can also use a block
81
314
  # in which case, you pass your +options+ as the second parameter.
82
- # Set escape to false to disable attribute value escaping.
315
+ # Set escape to false to disable escaping.
316
+ # Note: this is legacy syntax, see +tag+ method description for details.
83
317
  #
84
318
  # ==== Options
85
319
  # The +options+ hash can be used with attributes with no value like (<tt>disabled</tt> and
@@ -93,6 +327,8 @@ module ActionView
93
327
  # # => <div class="strong"><p>Hello world!</p></div>
94
328
  # content_tag(:div, "Hello world!", class: ["strong", "highlight"])
95
329
  # # => <div class="strong highlight">Hello world!</div>
330
+ # content_tag(:div, "Hello world!", class: ["strong", { highlight: current_user.admin? }])
331
+ # # => <div class="strong highlight">Hello world!</div>
96
332
  # content_tag("select", options, multiple: true)
97
333
  # # => <select multiple="multiple">...options...</select>
98
334
  #
@@ -103,12 +339,30 @@ module ActionView
103
339
  def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
104
340
  if block_given?
105
341
  options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
106
- content_tag_string(name, capture(&block), options, escape)
342
+ tag_builder.content_tag_string(name, capture(&block), options, escape)
107
343
  else
108
- content_tag_string(name, content_or_options_with_block, options, escape)
344
+ tag_builder.content_tag_string(name, content_or_options_with_block, options, escape)
109
345
  end
110
346
  end
111
347
 
348
+ # Returns a string of tokens built from +args+.
349
+ #
350
+ # ==== Examples
351
+ # token_list("foo", "bar")
352
+ # # => "foo bar"
353
+ # token_list("foo", "foo bar")
354
+ # # => "foo bar"
355
+ # token_list({ foo: true, bar: false })
356
+ # # => "foo"
357
+ # token_list(nil, false, 123, "", "foo", { bar: true })
358
+ # # => "123 foo bar"
359
+ def token_list(*args)
360
+ tokens = build_tag_values(*args).flat_map { |value| value.to_s.split(/\s+/) }.uniq
361
+
362
+ safe_join(tokens, " ")
363
+ end
364
+ alias_method :class_names, :token_list
365
+
112
366
  # Returns a CDATA section with the given +content+. CDATA sections
113
367
  # are used to escape blocks of text containing characters which would
114
368
  # otherwise be recognized as markup. CDATA sections begin with the string
@@ -123,7 +377,7 @@ module ActionView
123
377
  # cdata_section("hello]]>world")
124
378
  # # => <![CDATA[hello]]]]><![CDATA[>world]]>
125
379
  def cdata_section(content)
126
- splitted = content.to_s.gsub(/\]\]\>/, ']]]]><![CDATA[>')
380
+ splitted = content.to_s.gsub(/\]\]\>/, "]]]]><![CDATA[>")
127
381
  "<![CDATA[#{splitted}]]>".html_safe
128
382
  end
129
383
 
@@ -139,49 +393,28 @@ module ActionView
139
393
  end
140
394
 
141
395
  private
396
+ def build_tag_values(*args)
397
+ tag_values = []
142
398
 
143
- def content_tag_string(name, content, options, escape = true)
144
- tag_options = tag_options(options, escape) if options
145
- content = ERB::Util.unwrapped_html_escape(content) if escape
146
- "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
147
- end
148
-
149
- def tag_options(options, escape = true)
150
- return if options.blank?
151
- attrs = []
152
- options.each_pair do |key, value|
153
- if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
154
- value.each_pair do |k, v|
155
- attrs << prefix_tag_option(key, k, v, escape)
399
+ args.each do |tag_value|
400
+ case tag_value
401
+ when Hash
402
+ tag_value.each do |key, val|
403
+ tag_values << key.to_s if val && key.present?
156
404
  end
157
- elsif BOOLEAN_ATTRIBUTES.include?(key)
158
- attrs << boolean_tag_option(key) if value
159
- elsif !value.nil?
160
- attrs << tag_option(key, value, escape)
405
+ when Array
406
+ tag_values.concat build_tag_values(*tag_value)
407
+ else
408
+ tag_values << tag_value.to_s if tag_value.present?
161
409
  end
162
410
  end
163
- " #{attrs * ' '}" unless attrs.empty?
164
- end
165
-
166
- def prefix_tag_option(prefix, key, value, escape)
167
- key = "#{prefix}-#{key.to_s.dasherize}"
168
- unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
169
- value = value.to_json
170
- end
171
- tag_option(key, value, escape)
172
- end
173
411
 
174
- def boolean_tag_option(key)
175
- %(#{key}="#{key}")
412
+ tag_values
176
413
  end
414
+ module_function :build_tag_values
177
415
 
178
- def tag_option(key, value, escape)
179
- if value.is_a?(Array)
180
- value = escape ? safe_join(value, " ") : value.join(" ")
181
- else
182
- value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
183
- end
184
- %(#{key}="#{value.gsub('"'.freeze, '&quot;'.freeze)}")
416
+ def tag_builder
417
+ @tag_builder ||= TagBuilder.new(self)
185
418
  end
186
419
  end
187
420
  end