phlex 1.9.3 → 1.10.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of phlex might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.editorconfig +13 -0
- data/.rubocop.yml +27 -0
- data/.ruby-version +1 -0
- data/.solargraph.yml +11 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +62 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/CONTRIBUTING.md +33 -0
- data/Gemfile +21 -0
- data/README.md +3 -1
- data/SECURITY.md +14 -0
- data/bench.rb +21 -0
- data/config/sus.rb +8 -0
- data/fixtures/components/say_hi.rb +15 -0
- data/fixtures/components.rb +7 -0
- data/fixtures/layout.rb +31 -0
- data/fixtures/page.rb +37 -0
- data/fixtures/view_helper.rb +22 -0
- data/gd/phlex/callable.rb +18 -0
- data/gd/phlex/sgml/attributes.rb +107 -0
- data/gd/support/helper.rb +23 -0
- data/lib/phlex/black_hole.rb +1 -1
- data/lib/phlex/context.rb +45 -8
- data/lib/phlex/csv.rb +133 -0
- data/lib/phlex/deferred_render.rb +1 -1
- data/lib/phlex/elements.rb +72 -19
- data/lib/phlex/helpers.rb +17 -5
- data/lib/phlex/html/standard_elements.rb +138 -96
- data/lib/phlex/html/void_elements.rb +17 -17
- data/lib/phlex/html.rb +16 -2
- data/lib/phlex/kit.rb +62 -0
- data/lib/phlex/sgml.rb +110 -62
- data/lib/phlex/svg/standard_elements.rb +64 -64
- data/lib/phlex/svg.rb +10 -0
- data/lib/phlex/version.rb +1 -1
- data/lib/phlex.rb +34 -12
- data/phlex_logo.png +0 -0
- data/sig/phlex.rbs +4 -0
- metadata +29 -48
- data/lib/phlex/overrides/symbol/name.rb +0 -6
@@ -8,71 +8,71 @@ module Phlex::HTML::VoidElements
|
|
8
8
|
# Outputs an `<area>` tag.
|
9
9
|
# @return [nil]
|
10
10
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/area
|
11
|
-
register_void_element :area
|
11
|
+
register_void_element :area
|
12
12
|
|
13
13
|
# @!method br(**attributes, &content)
|
14
14
|
# Outputs a `<br>` tag.
|
15
15
|
# @return [nil]
|
16
16
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/br
|
17
|
-
register_void_element :br
|
17
|
+
register_void_element :br
|
18
|
+
|
19
|
+
# @!method col(**attributes, &content)
|
20
|
+
# Outputs a `<col>` tag.
|
21
|
+
# @return [nil]
|
22
|
+
# @see https://developer.mozilla.org/docs/Web/HTML/Element/col
|
23
|
+
register_void_element :col
|
18
24
|
|
19
25
|
# @!method embed(**attributes, &content)
|
20
26
|
# Outputs an `<embed>` tag.
|
21
27
|
# @return [nil]
|
22
28
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/embed
|
23
|
-
register_void_element :embed
|
29
|
+
register_void_element :embed
|
24
30
|
|
25
31
|
# @!method hr(**attributes, &content)
|
26
32
|
# Outputs an `<hr>` tag.
|
27
33
|
# @return [nil]
|
28
34
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/hr
|
29
|
-
register_void_element :hr
|
35
|
+
register_void_element :hr
|
30
36
|
|
31
37
|
# @!method img(**attributes, &content)
|
32
38
|
# Outputs an `<img>` tag.
|
33
39
|
# @return [nil]
|
34
40
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/img
|
35
|
-
register_void_element :img
|
41
|
+
register_void_element :img
|
36
42
|
|
37
43
|
# @!method input(**attributes, &content)
|
38
44
|
# Outputs an `<input>` tag.
|
39
45
|
# @return [nil]
|
40
46
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/input
|
41
|
-
register_void_element :input
|
47
|
+
register_void_element :input
|
42
48
|
|
43
49
|
# @!method link(**attributes, &content)
|
44
50
|
# Outputs a `<link>` tag.
|
45
51
|
# @return [nil]
|
46
52
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/link
|
47
|
-
register_void_element :link
|
53
|
+
register_void_element :link
|
48
54
|
|
49
55
|
# @!method meta(**attributes, &content)
|
50
56
|
# Outputs a `<meta>` tag.
|
51
57
|
# @return [nil]
|
52
58
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/meta
|
53
|
-
register_void_element :meta
|
59
|
+
register_void_element :meta
|
54
60
|
|
55
61
|
# @!method param(**attributes, &content)
|
56
62
|
# Outputs a `<param>` tag.
|
57
63
|
# @return [nil]
|
58
64
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/param
|
59
|
-
register_void_element :param,
|
65
|
+
register_void_element :param, deprecated: "⚠️ [DEPRECATION] The <param> tag is deprecated. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param"
|
60
66
|
|
61
67
|
# @!method source(**attributes, &content)
|
62
68
|
# Outputs a `<source>` tag.
|
63
69
|
# @return [nil]
|
64
70
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/source
|
65
|
-
register_void_element :source
|
71
|
+
register_void_element :source
|
66
72
|
|
67
73
|
# @!method track(**attributes, &content)
|
68
74
|
# Outputs a `<track>` tag.
|
69
75
|
# @return [nil]
|
70
76
|
# @see https://developer.mozilla.org/docs/Web/HTML/Element/track
|
71
|
-
register_void_element :track
|
72
|
-
|
73
|
-
# @!method col(**attributes, &content)
|
74
|
-
# Outputs a `<col>` tag.
|
75
|
-
# @return [nil]
|
76
|
-
# @see https://developer.mozilla.org/docs/Web/HTML/Element/col
|
77
|
-
register_void_element :col, tag: "col"
|
77
|
+
register_void_element :track
|
78
78
|
end
|
data/lib/phlex/html.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
module Phlex
|
4
4
|
# @abstract Subclass and define {#template} to create an HTML component class.
|
5
5
|
class HTML < SGML
|
6
|
+
autoload :StandardElements, "phlex/html/standard_elements"
|
7
|
+
autoload :VoidElements, "phlex/html/void_elements"
|
8
|
+
|
6
9
|
# A list of HTML attributes that have the potential to execute unsafe JavaScript.
|
7
10
|
EVENT_ATTRIBUTES = %w[onabort onafterprint onbeforeprint onbeforeunload onblur oncanplay oncanplaythrough onchange onclick oncontextmenu oncopy oncuechange oncut ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended onerror onfocus onhashchange oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart onmessage onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onoffline ononline onpagehide onpageshow onpaste onpause onplay onplaying onpopstate onprogress onratechange onreset onresize onscroll onsearch onseeked onseeking onselect onstalled onstorage onsubmit onsuspend ontimeupdate ontoggle onunload onvolumechange onwaiting onwheel].to_h { [_1, true] }.freeze
|
8
11
|
|
@@ -22,11 +25,14 @@ module Phlex
|
|
22
25
|
end
|
23
26
|
|
24
27
|
extend Elements
|
25
|
-
include
|
28
|
+
include VoidElements, StandardElements
|
26
29
|
|
27
30
|
# Output an HTML doctype.
|
28
31
|
def doctype
|
29
|
-
@_context
|
32
|
+
context = @_context
|
33
|
+
return if context.fragments && !context.in_target_fragment
|
34
|
+
|
35
|
+
context.buffer << "<!DOCTYPE html>"
|
30
36
|
nil
|
31
37
|
end
|
32
38
|
|
@@ -46,6 +52,14 @@ module Phlex
|
|
46
52
|
self.class.__unbuffered_class__.new(self)
|
47
53
|
end
|
48
54
|
|
55
|
+
def filename
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def content_type
|
60
|
+
"text/html"
|
61
|
+
end
|
62
|
+
|
49
63
|
# This should be extended after all method definitions
|
50
64
|
extend ElementClobberingGuard
|
51
65
|
end
|
data/lib/phlex/kit.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlex::Kit
|
4
|
+
def self.extended(mod)
|
5
|
+
warn "⚠️ [WARNING] Phlex::Kit is experimental and may be removed from future versions of Phlex."
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
# When a kit is included in a module, we need to load all of its components.
|
10
|
+
def included(mod)
|
11
|
+
constants.each { |c| const_get(c) if autoload?(c) }
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, *args, **kwargs, &block)
|
16
|
+
if name[0] == name[0].upcase && constants.include?(name) && const_get(name) && methods.include?(name)
|
17
|
+
public_send(name, *args, **kwargs, &block)
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def respond_to_missing?(name, include_private = false)
|
24
|
+
if name[0] == name[0].upcase && constants.include?(name) && const_get(name) && methods.include?(name)
|
25
|
+
true
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def const_added(name)
|
32
|
+
return if autoload?(name)
|
33
|
+
|
34
|
+
constant = const_get(name)
|
35
|
+
|
36
|
+
if Class === constant && constant < Phlex::SGML
|
37
|
+
if instance_methods.include?(name)
|
38
|
+
raise NameError, "The instance method `#{name}' is already defined on `#{inspect}`."
|
39
|
+
elsif methods.include?(name)
|
40
|
+
raise NameError, "The method `#{name}' is already defined on `#{inspect}`."
|
41
|
+
end
|
42
|
+
|
43
|
+
constant.include(self)
|
44
|
+
|
45
|
+
define_method(name) do |*args, **kwargs, &block|
|
46
|
+
render(constant.new(*args, **kwargs), &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
define_singleton_method(name) do |*args, **kwargs, &block|
|
50
|
+
if (component = Fiber[:__phlex_component__])
|
51
|
+
component.instance_exec do
|
52
|
+
render(constant.new(*args, **kwargs), &block)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
raise "You can't call `#{name}' outside of a Phlex rendering context."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
super
|
61
|
+
end
|
62
|
+
end
|
data/lib/phlex/sgml.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.0")
|
4
|
-
using Phlex::Overrides::Symbol::Name
|
5
|
-
end
|
6
|
-
|
7
3
|
module Phlex
|
8
4
|
# **Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.
|
9
5
|
class SGML
|
6
|
+
include Helpers
|
7
|
+
|
10
8
|
class << self
|
11
9
|
# Render the view to a String. Arguments are delegated to {.new}.
|
12
10
|
def call(...)
|
@@ -53,33 +51,41 @@ module Phlex
|
|
53
51
|
|
54
52
|
# @abstract Override to define a template for your component.
|
55
53
|
# @example
|
56
|
-
# def
|
54
|
+
# def view_template
|
57
55
|
# h1 { "👋 Hello World!" }
|
58
56
|
# end
|
59
57
|
# @example Your template may yield a content block.
|
60
|
-
# def
|
58
|
+
# def view_template
|
61
59
|
# main {
|
62
60
|
# h1 { "Hello World" }
|
63
61
|
# yield
|
64
62
|
# }
|
65
63
|
# end
|
66
64
|
# @example Alternatively, you can delegate the content block to an element.
|
67
|
-
# def
|
65
|
+
# def view_template(&block)
|
68
66
|
# article(class: "card", &block)
|
69
67
|
# end
|
70
68
|
def template
|
71
69
|
yield
|
72
70
|
end
|
73
71
|
|
74
|
-
|
72
|
+
def self.method_added(method_name)
|
73
|
+
if method_name == :template
|
74
|
+
Kernel.warn "⚠️ [DEPRECATION] Defining the `template` method on a Phlex component will not be supported in Phlex 2.0. Please rename the method to `view_template` instead."
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def view_template(&block)
|
79
|
+
template(&block)
|
80
|
+
end
|
81
|
+
|
75
82
|
def await(task)
|
76
|
-
|
83
|
+
case task
|
84
|
+
when defined?(Concurrent::IVar) && Concurrent::IVar
|
77
85
|
flush if task.pending?
|
78
|
-
|
79
86
|
task.wait.value
|
80
|
-
|
87
|
+
when defined?(Async::Task) && Async::Task
|
81
88
|
flush if task.running?
|
82
|
-
|
83
89
|
task.wait
|
84
90
|
else
|
85
91
|
raise ArgumentError, "Expected an asynchronous task / promise."
|
@@ -87,43 +93,65 @@ module Phlex
|
|
87
93
|
end
|
88
94
|
|
89
95
|
# Renders the view and returns the buffer. The default buffer is a mutable String.
|
90
|
-
def call(
|
91
|
-
__final_call__(
|
96
|
+
def call(...)
|
97
|
+
__final_call__(...).tap do
|
92
98
|
self.class.rendered_at_least_once!
|
93
99
|
end
|
94
100
|
end
|
95
101
|
|
96
102
|
# @api private
|
97
|
-
def __final_call__(buffer = +"", context: Phlex::Context.new, view_context: nil, parent: nil, &block)
|
103
|
+
def __final_call__(buffer = +"", context: Phlex::Context.new, view_context: nil, parent: nil, fragments: nil, &block)
|
98
104
|
@_buffer = buffer
|
99
105
|
@_context = context
|
100
106
|
@_view_context = view_context
|
101
107
|
@_parent = parent
|
108
|
+
if fragments
|
109
|
+
warn "⚠️ [WARNING] Selective Rendering is experimental, incomplete, and may change in future versions."
|
110
|
+
@_context.target_fragments(fragments)
|
111
|
+
end
|
102
112
|
|
103
113
|
block ||= @_content_block
|
104
114
|
|
105
115
|
return "" unless render?
|
106
116
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
117
|
+
if !parent && Phlex::SUPPORTS_FIBER_STORAGE
|
118
|
+
original_fiber_storage = Fiber[:__phlex_component__]
|
119
|
+
Fiber[:__phlex_component__] = self
|
120
|
+
end
|
121
|
+
|
122
|
+
@_context.around_render do
|
123
|
+
around_template do
|
124
|
+
if block
|
125
|
+
if is_a?(DeferredRender)
|
126
|
+
__vanish__(self, &block)
|
127
|
+
view_template
|
128
|
+
else
|
129
|
+
view_template do |*args|
|
130
|
+
if args.length > 0
|
131
|
+
yield_content_with_args(*args, &block)
|
132
|
+
else
|
133
|
+
yield_content(&block)
|
134
|
+
end
|
118
135
|
end
|
119
136
|
end
|
137
|
+
else
|
138
|
+
view_template
|
120
139
|
end
|
121
|
-
else
|
122
|
-
template
|
123
140
|
end
|
124
141
|
end
|
125
142
|
|
126
|
-
|
143
|
+
unless parent
|
144
|
+
if Phlex::SUPPORTS_FIBER_STORAGE
|
145
|
+
Fiber[:__phlex_component__] = original_fiber_storage
|
146
|
+
end
|
147
|
+
buffer << context.buffer
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Access the current render context data
|
152
|
+
# @return the supplied context object, by default a Hash
|
153
|
+
def context
|
154
|
+
@_context.user_context
|
127
155
|
end
|
128
156
|
|
129
157
|
# Output text content. The text will be HTML-escaped.
|
@@ -141,14 +169,17 @@ module Phlex
|
|
141
169
|
# Output a whitespace character. This is useful for getting inline elements to wrap. If you pass a block, a whitespace will be output before and after yielding the block.
|
142
170
|
# @return [nil]
|
143
171
|
# @yield If a block is given, it yields the block with no arguments.
|
144
|
-
def whitespace
|
145
|
-
|
172
|
+
def whitespace(&block)
|
173
|
+
context = @_context
|
174
|
+
return if context.fragments && !context.in_target_fragment
|
146
175
|
|
147
|
-
|
176
|
+
buffer = context.buffer
|
177
|
+
|
178
|
+
buffer << " "
|
148
179
|
|
149
180
|
if block_given?
|
150
|
-
|
151
|
-
|
181
|
+
yield_content(&block)
|
182
|
+
buffer << " "
|
152
183
|
end
|
153
184
|
|
154
185
|
nil
|
@@ -157,11 +188,14 @@ module Phlex
|
|
157
188
|
# Output an HTML comment.
|
158
189
|
# @return [nil]
|
159
190
|
def comment(&block)
|
160
|
-
|
191
|
+
context = @_context
|
192
|
+
return if context.fragments && !context.in_target_fragment
|
193
|
+
|
194
|
+
buffer = context.buffer
|
161
195
|
|
162
|
-
|
196
|
+
buffer << "<!-- "
|
163
197
|
yield_content(&block)
|
164
|
-
|
198
|
+
buffer << " -->"
|
165
199
|
|
166
200
|
nil
|
167
201
|
end
|
@@ -172,7 +206,10 @@ module Phlex
|
|
172
206
|
def unsafe_raw(content = nil)
|
173
207
|
return nil unless content
|
174
208
|
|
175
|
-
@_context
|
209
|
+
context = @_context
|
210
|
+
return if context.fragments && !context.in_target_fragment
|
211
|
+
|
212
|
+
context.buffer << content
|
176
213
|
nil
|
177
214
|
end
|
178
215
|
|
@@ -191,9 +228,9 @@ module Phlex
|
|
191
228
|
def flush
|
192
229
|
return if @_context.capturing
|
193
230
|
|
194
|
-
|
195
|
-
@_buffer <<
|
196
|
-
|
231
|
+
buffer = @_context.buffer
|
232
|
+
@_buffer << buffer.dup
|
233
|
+
buffer.clear
|
197
234
|
end
|
198
235
|
|
199
236
|
# Render another component, block or enumerable
|
@@ -294,11 +331,11 @@ module Phlex
|
|
294
331
|
def yield_content
|
295
332
|
return unless block_given?
|
296
333
|
|
297
|
-
|
334
|
+
buffer = @_context.buffer
|
298
335
|
|
299
|
-
original_length =
|
336
|
+
original_length = buffer.bytesize
|
300
337
|
content = yield(self)
|
301
|
-
__text__(content) if original_length ==
|
338
|
+
__text__(content) if original_length == buffer.bytesize
|
302
339
|
|
303
340
|
nil
|
304
341
|
end
|
@@ -308,11 +345,11 @@ module Phlex
|
|
308
345
|
def yield_content_with_no_args
|
309
346
|
return unless block_given?
|
310
347
|
|
311
|
-
|
348
|
+
buffer = @_context.buffer
|
312
349
|
|
313
|
-
original_length =
|
350
|
+
original_length = buffer.bytesize
|
314
351
|
content = yield
|
315
|
-
__text__(content) if original_length ==
|
352
|
+
__text__(content) if original_length == buffer.bytesize
|
316
353
|
|
317
354
|
nil
|
318
355
|
end
|
@@ -323,11 +360,11 @@ module Phlex
|
|
323
360
|
def yield_content_with_args(*args)
|
324
361
|
return unless block_given?
|
325
362
|
|
326
|
-
|
363
|
+
buffer = @_context.buffer
|
327
364
|
|
328
|
-
original_length =
|
365
|
+
original_length = buffer.bytesize
|
329
366
|
content = yield(*args)
|
330
|
-
__text__(content) if original_length ==
|
367
|
+
__text__(content) if original_length == buffer.bytesize
|
331
368
|
|
332
369
|
nil
|
333
370
|
end
|
@@ -335,16 +372,19 @@ module Phlex
|
|
335
372
|
# Performs the same task as the public method #plain, but does not raise an error if an unformattable object is passed
|
336
373
|
# @api private
|
337
374
|
def __text__(content)
|
375
|
+
context = @_context
|
376
|
+
return true if context.fragments && !context.in_target_fragment
|
377
|
+
|
338
378
|
case content
|
339
379
|
when String
|
340
|
-
@_context.
|
380
|
+
@_context.buffer << Phlex::Escape.html_escape(content)
|
341
381
|
when Symbol
|
342
|
-
@_context.
|
382
|
+
@_context.buffer << Phlex::Escape.html_escape(content.name)
|
343
383
|
when nil
|
344
384
|
nil
|
345
385
|
else
|
346
386
|
if (formatted_object = format_object(content))
|
347
|
-
@_context.
|
387
|
+
@_context.buffer << Phlex::Escape.html_escape(formatted_object)
|
348
388
|
else
|
349
389
|
return false
|
350
390
|
end
|
@@ -384,10 +424,10 @@ module Phlex
|
|
384
424
|
end
|
385
425
|
|
386
426
|
lower_name = name.downcase
|
387
|
-
next if lower_name == "href" && v.
|
427
|
+
next if lower_name == "href" && v.start_with?(/\s*javascript:/i)
|
388
428
|
|
389
429
|
# Detect unsafe attribute names. Attribute names are considered unsafe if they match an event attribute or include unsafe characters.
|
390
|
-
if HTML::EVENT_ATTRIBUTES
|
430
|
+
if HTML::EVENT_ATTRIBUTES[lower_name] || name.match?(/[<>&"']/)
|
391
431
|
raise ArgumentError, "Unsafe attribute name detected: #{k}."
|
392
432
|
end
|
393
433
|
|
@@ -395,26 +435,34 @@ module Phlex
|
|
395
435
|
when true
|
396
436
|
buffer << " " << name
|
397
437
|
when String
|
398
|
-
buffer << " " << name << '="' <<
|
438
|
+
buffer << " " << name << '="' << Phlex::Escape.html_escape(v) << '"'
|
399
439
|
when Symbol
|
400
|
-
buffer << " " << name << '="' <<
|
440
|
+
buffer << " " << name << '="' << Phlex::Escape.html_escape(v.name) << '"'
|
401
441
|
when Integer, Float
|
402
442
|
buffer << " " << name << '="' << v.to_s << '"'
|
403
443
|
when Hash
|
404
444
|
__build_attributes__(
|
405
445
|
v.transform_keys { |subkey|
|
406
446
|
case subkey
|
407
|
-
when Symbol then"#{
|
408
|
-
else "#{
|
447
|
+
when Symbol then"#{name}-#{subkey.name.tr('_', '-')}"
|
448
|
+
else "#{name}-#{subkey}"
|
409
449
|
end
|
410
450
|
}, buffer: buffer
|
411
451
|
)
|
412
452
|
when Array
|
413
|
-
buffer << " " << name << '="' <<
|
453
|
+
buffer << " " << name << '="' << Phlex::Escape.html_escape(v.compact.join(" ")) << '"'
|
414
454
|
when Set
|
415
|
-
buffer << " " << name << '="' <<
|
455
|
+
buffer << " " << name << '="' << Phlex::Escape.html_escape(v.to_a.compact.join(" ")) << '"'
|
416
456
|
else
|
417
|
-
|
457
|
+
value = if v.respond_to?(:to_phlex_attribute_value)
|
458
|
+
v.to_phlex_attribute_value
|
459
|
+
elsif v.respond_to?(:to_str)
|
460
|
+
v.to_str
|
461
|
+
else
|
462
|
+
v.to_s
|
463
|
+
end
|
464
|
+
|
465
|
+
buffer << " " << name << '="' << Phlex::Escape.html_escape(value) << '"'
|
418
466
|
end
|
419
467
|
end
|
420
468
|
|