phlex 2.3.1 → 2.4.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/phlex/compiler/class_compiler.rb +35 -0
- data/lib/phlex/compiler/compilation.rb +45 -0
- data/lib/phlex/compiler/file_compiler.rb +46 -0
- data/lib/phlex/compiler/method_compiler.rb +591 -0
- data/lib/phlex/compiler.rb +18 -0
- data/lib/phlex/html.rb +3 -3
- data/lib/phlex/sgml/attributes.rb +280 -0
- data/lib/phlex/sgml/elements.rb +8 -8
- data/lib/phlex/sgml.rb +11 -260
- data/lib/phlex/svg.rb +2 -2
- data/lib/phlex/version.rb +1 -1
- metadata +23 -3
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "prism"
|
4
|
+
require "refract"
|
5
|
+
|
6
|
+
module Phlex::Compiler
|
7
|
+
Error = Class.new(StandardError)
|
8
|
+
|
9
|
+
def self.compile(component)
|
10
|
+
path, line = Object.const_source_location(component.name)
|
11
|
+
return unless File.exist?(path)
|
12
|
+
source = File.read(path)
|
13
|
+
tree = Prism.parse(source).value
|
14
|
+
refract = Refract::Converter.new.visit(tree)
|
15
|
+
|
16
|
+
Compilation.new(component, path, line, source, refract).compile
|
17
|
+
end
|
18
|
+
end
|
data/lib/phlex/html.rb
CHANGED
@@ -58,7 +58,7 @@ class Phlex::HTML < Phlex::SGML
|
|
58
58
|
if (tag = StandardElements.__registered_elements__[name]) || (tag = name.name.tr("_", "-")).include?("-")
|
59
59
|
if attributes.length > 0 # with attributes
|
60
60
|
if block_given # with content block
|
61
|
-
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||=
|
61
|
+
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << ">"
|
62
62
|
if tag == "svg"
|
63
63
|
render Phlex::SVG.new(&)
|
64
64
|
else
|
@@ -66,7 +66,7 @@ class Phlex::HTML < Phlex::SGML
|
|
66
66
|
end
|
67
67
|
buffer << "</#{tag}>"
|
68
68
|
else # without content
|
69
|
-
buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||=
|
69
|
+
buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << "></#{tag}>"
|
70
70
|
end
|
71
71
|
else # without attributes
|
72
72
|
if block_given # with content block
|
@@ -87,7 +87,7 @@ class Phlex::HTML < Phlex::SGML
|
|
87
87
|
end
|
88
88
|
|
89
89
|
if attributes.length > 0 # with attributes
|
90
|
-
buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||=
|
90
|
+
buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << ">"
|
91
91
|
else # without attributes
|
92
92
|
buffer << "<#{tag}>"
|
93
93
|
end
|
@@ -0,0 +1,280 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlex::SGML::Attributes
|
4
|
+
extend self
|
5
|
+
|
6
|
+
UNSAFE_ATTRIBUTES = Set.new(%w[srcdoc sandbox http-equiv]).freeze
|
7
|
+
REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping]).freeze
|
8
|
+
|
9
|
+
def generate_attributes(attributes, buffer = +"")
|
10
|
+
attributes.each do |k, v|
|
11
|
+
next unless v
|
12
|
+
|
13
|
+
name = case k
|
14
|
+
when String then k
|
15
|
+
when Symbol then k.name.tr("_", "-")
|
16
|
+
else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols.")
|
17
|
+
end
|
18
|
+
|
19
|
+
value = case v
|
20
|
+
when true
|
21
|
+
true
|
22
|
+
when String
|
23
|
+
v.gsub('"', """)
|
24
|
+
when Symbol
|
25
|
+
v.name.tr("_", "-").gsub('"', """)
|
26
|
+
when Integer, Float
|
27
|
+
v.to_s
|
28
|
+
when Date
|
29
|
+
v.iso8601
|
30
|
+
when Time
|
31
|
+
v.respond_to?(:iso8601) ? v.iso8601 : v.strftime("%Y-%m-%dT%H:%M:%S%:z")
|
32
|
+
when Hash
|
33
|
+
case k
|
34
|
+
when :style
|
35
|
+
generate_styles(v).gsub('"', """)
|
36
|
+
else
|
37
|
+
generate_nested_attributes(v, "#{name}-", buffer)
|
38
|
+
end
|
39
|
+
when Array
|
40
|
+
case k
|
41
|
+
when :style
|
42
|
+
generate_styles(v).gsub('"', """)
|
43
|
+
else
|
44
|
+
generate_nested_tokens(v)
|
45
|
+
end
|
46
|
+
when Set
|
47
|
+
case k
|
48
|
+
when :style
|
49
|
+
generate_styles(v).gsub('"', """)
|
50
|
+
else
|
51
|
+
generate_nested_tokens(v.to_a)
|
52
|
+
end
|
53
|
+
when Phlex::SGML::SafeObject
|
54
|
+
v.to_s.gsub('"', """)
|
55
|
+
else
|
56
|
+
raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
|
57
|
+
end
|
58
|
+
|
59
|
+
lower_name = name.downcase
|
60
|
+
|
61
|
+
unless Phlex::SGML::SafeObject === v
|
62
|
+
normalized_name = lower_name.delete("^a-z-")
|
63
|
+
|
64
|
+
if value != true && REF_ATTRIBUTES.include?(normalized_name)
|
65
|
+
case value
|
66
|
+
when String
|
67
|
+
if value.downcase.delete("^a-z:").start_with?("javascript:")
|
68
|
+
# We just ignore these because they were likely not specified by the developer.
|
69
|
+
next
|
70
|
+
end
|
71
|
+
else
|
72
|
+
raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
if normalized_name.bytesize > 2 && normalized_name.start_with?("on") && !normalized_name.include?("-")
|
77
|
+
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
78
|
+
end
|
79
|
+
|
80
|
+
if UNSAFE_ATTRIBUTES.include?(normalized_name)
|
81
|
+
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if name.match?(/[<>&"']/)
|
86
|
+
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
87
|
+
end
|
88
|
+
|
89
|
+
if lower_name.to_sym == :id && k != :id
|
90
|
+
raise Phlex::ArgumentError.new(":id attribute should only be passed as a lowercase symbol.")
|
91
|
+
end
|
92
|
+
|
93
|
+
case value
|
94
|
+
when true
|
95
|
+
buffer << " " << name
|
96
|
+
when String
|
97
|
+
buffer << " " << name << '="' << value << '"'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
buffer
|
102
|
+
end
|
103
|
+
|
104
|
+
# Provides the nested-attributes case for serializing out attributes.
|
105
|
+
# This allows us to skip many of the checks the `__attributes__` method must perform.
|
106
|
+
def generate_nested_attributes(attributes, base_name, buffer = +"")
|
107
|
+
attributes.each do |k, v|
|
108
|
+
next unless v
|
109
|
+
|
110
|
+
if (root_key = (:_ == k))
|
111
|
+
name = ""
|
112
|
+
original_base_name = base_name
|
113
|
+
base_name = base_name.delete_suffix("-")
|
114
|
+
else
|
115
|
+
name = case k
|
116
|
+
when String then k
|
117
|
+
when Symbol then k.name.tr("_", "-")
|
118
|
+
else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
|
119
|
+
end
|
120
|
+
|
121
|
+
if name.match?(/[<>&"']/)
|
122
|
+
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
case v
|
127
|
+
when true
|
128
|
+
buffer << " " << base_name << name
|
129
|
+
when String
|
130
|
+
buffer << " " << base_name << name << '="' << v.gsub('"', """) << '"'
|
131
|
+
when Symbol
|
132
|
+
buffer << " " << base_name << name << '="' << v.name.tr("_", "-").gsub('"', """) << '"'
|
133
|
+
when Integer, Float
|
134
|
+
buffer << " " << base_name << name << '="' << v.to_s << '"'
|
135
|
+
when Hash
|
136
|
+
generate_nested_attributes(v, "#{base_name}#{name}-", buffer)
|
137
|
+
when Array
|
138
|
+
if (value = generate_nested_tokens(v))
|
139
|
+
buffer << " " << base_name << name << '="' << value << '"'
|
140
|
+
end
|
141
|
+
when Set
|
142
|
+
if (value = generate_nested_tokens(v.to_a))
|
143
|
+
buffer << " " << base_name << name << '="' << value << '"'
|
144
|
+
end
|
145
|
+
when Phlex::SGML::SafeObject
|
146
|
+
buffer << " " << base_name << name << '="' << v.to_s.gsub('"', """) << '"'
|
147
|
+
else
|
148
|
+
raise Phlex::ArgumentError.new("Invalid attribute value #{v.inspect}.")
|
149
|
+
end
|
150
|
+
|
151
|
+
if root_key
|
152
|
+
base_name = original_base_name
|
153
|
+
end
|
154
|
+
|
155
|
+
buffer
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def generate_nested_tokens(tokens, sep = " ", gsub_from = nil, gsub_to = "")
|
160
|
+
buffer = +""
|
161
|
+
|
162
|
+
i, length = 0, tokens.length
|
163
|
+
|
164
|
+
while i < length
|
165
|
+
token = tokens[i]
|
166
|
+
|
167
|
+
case token
|
168
|
+
when String
|
169
|
+
token = token.gsub(gsub_from, gsub_to) if gsub_from
|
170
|
+
if i > 0
|
171
|
+
buffer << sep << token
|
172
|
+
else
|
173
|
+
buffer << token
|
174
|
+
end
|
175
|
+
when Symbol
|
176
|
+
if i > 0
|
177
|
+
buffer << sep << token.name.tr("_", "-")
|
178
|
+
else
|
179
|
+
buffer << token.name.tr("_", "-")
|
180
|
+
end
|
181
|
+
when Integer, Float, Phlex::SGML::SafeObject
|
182
|
+
if i > 0
|
183
|
+
buffer << sep << token.to_s
|
184
|
+
else
|
185
|
+
buffer << token.to_s
|
186
|
+
end
|
187
|
+
when Array
|
188
|
+
if token.length > 0 && (value = generate_nested_tokens(token, sep, gsub_from, gsub_to))
|
189
|
+
if i > 0
|
190
|
+
buffer << sep << value
|
191
|
+
else
|
192
|
+
buffer << value
|
193
|
+
end
|
194
|
+
end
|
195
|
+
when Set
|
196
|
+
if token.length > 0 && (value = generate_nested_tokens(token.to_a, sep, gsub_from, gsub_to))
|
197
|
+
if i > 0
|
198
|
+
buffer << sep << value
|
199
|
+
else
|
200
|
+
buffer << value
|
201
|
+
end
|
202
|
+
end
|
203
|
+
when nil
|
204
|
+
# Do nothing
|
205
|
+
else
|
206
|
+
raise Phlex::ArgumentError.new("Invalid token type: #{token.class}.")
|
207
|
+
end
|
208
|
+
|
209
|
+
i += 1
|
210
|
+
end
|
211
|
+
|
212
|
+
return if buffer.empty?
|
213
|
+
|
214
|
+
buffer.gsub('"', """)
|
215
|
+
end
|
216
|
+
|
217
|
+
# The result is unsafe so should be escaped.
|
218
|
+
def generate_styles(styles)
|
219
|
+
case styles
|
220
|
+
when Array, Set
|
221
|
+
styles.filter_map do |s|
|
222
|
+
case s
|
223
|
+
when String
|
224
|
+
if s == "" || s.end_with?(";")
|
225
|
+
s
|
226
|
+
else
|
227
|
+
"#{s};"
|
228
|
+
end
|
229
|
+
when Phlex::SGML::SafeObject
|
230
|
+
value = s.to_s
|
231
|
+
value.end_with?(";") ? value : "#{value};"
|
232
|
+
when Hash
|
233
|
+
next generate_styles(s)
|
234
|
+
when nil
|
235
|
+
next nil
|
236
|
+
else
|
237
|
+
raise Phlex::ArgumentError.new("Invalid style: #{s.inspect}.")
|
238
|
+
end
|
239
|
+
end.join(" ")
|
240
|
+
when Hash
|
241
|
+
buffer = +""
|
242
|
+
i = 0
|
243
|
+
styles.each do |k, v|
|
244
|
+
prop = case k
|
245
|
+
when String
|
246
|
+
k
|
247
|
+
when Symbol
|
248
|
+
k.name.tr("_", "-")
|
249
|
+
else
|
250
|
+
raise Phlex::ArgumentError.new("Style keys should be Strings or Symbols.")
|
251
|
+
end
|
252
|
+
|
253
|
+
value = case v
|
254
|
+
when String
|
255
|
+
v
|
256
|
+
when Symbol
|
257
|
+
v.name.tr("_", "-")
|
258
|
+
when Integer, Float, Phlex::SGML::SafeObject
|
259
|
+
v.to_s
|
260
|
+
when nil
|
261
|
+
nil
|
262
|
+
else
|
263
|
+
raise Phlex::ArgumentError.new("Invalid style value: #{v.inspect}")
|
264
|
+
end
|
265
|
+
|
266
|
+
if value
|
267
|
+
if i == 0
|
268
|
+
buffer << prop << ": " << value << ";"
|
269
|
+
else
|
270
|
+
buffer << " " << prop << ": " << value << ";"
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
i += 1
|
275
|
+
end
|
276
|
+
|
277
|
+
buffer
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
data/lib/phlex/sgml/elements.rb
CHANGED
@@ -4,16 +4,16 @@ module Phlex::SGML::Elements
|
|
4
4
|
COMMA_SEPARATED_TOKENS = {
|
5
5
|
img: <<~RUBY,
|
6
6
|
if Array === (srcset_attribute = attributes[:srcset])
|
7
|
-
attributes[:srcset] =
|
7
|
+
attributes[:srcset] = Phlex::SGML::Attributes.generate_nested_tokens(srcset_attribute, ", ", ",", "%2C")
|
8
8
|
end
|
9
9
|
RUBY
|
10
10
|
link: <<~RUBY,
|
11
11
|
if Array === (media_attribute = attributes[:media])
|
12
|
-
attributes[:media] =
|
12
|
+
attributes[:media] = Phlex::SGML::Attributes.generate_nested_tokens(media_attribute, ", ", ",", "%2C")
|
13
13
|
end
|
14
14
|
|
15
15
|
if Array === (sizes_attribute = attributes[:sizes])
|
16
|
-
attributes[:sizes] =
|
16
|
+
attributes[:sizes] = Phlex::SGML::Attributes.generate_nested_tokens(sizes_attribute, ", ", ",", "%2C")
|
17
17
|
end
|
18
18
|
|
19
19
|
if Array === (imagesrcset_attribute = attributes[:imagesrcset])
|
@@ -21,7 +21,7 @@ module Phlex::SGML::Elements
|
|
21
21
|
as_attribute = attributes[:as] || attributes["as"]
|
22
22
|
|
23
23
|
if ("preload" == rel_attribute || :preload == rel_attribute) && ("image" == as_attribute || :image == as_attribute)
|
24
|
-
attributes[:imagesrcset] =
|
24
|
+
attributes[:imagesrcset] = Phlex::SGML::Attributes.generate_nested_tokens(imagesrcset_attribute, ", ", ",", "%2C")
|
25
25
|
end
|
26
26
|
end
|
27
27
|
RUBY
|
@@ -30,7 +30,7 @@ module Phlex::SGML::Elements
|
|
30
30
|
type_attribute = attributes[:type] || attributes["type"]
|
31
31
|
|
32
32
|
if "file" == type_attribute || :file == type_attribute
|
33
|
-
attributes[:accept] =
|
33
|
+
attributes[:accept] = Phlex::SGML::Attributes.generate_nested_tokens(accept_attribute, ", ", ",", "%2C")
|
34
34
|
end
|
35
35
|
end
|
36
36
|
RUBY
|
@@ -59,7 +59,7 @@ module Phlex::SGML::Elements
|
|
59
59
|
buffer << "<#{tag}"
|
60
60
|
begin
|
61
61
|
#{COMMA_SEPARATED_TOKENS[method_name]}
|
62
|
-
buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||=
|
62
|
+
buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
|
63
63
|
ensure
|
64
64
|
buffer << ">"
|
65
65
|
end
|
@@ -90,7 +90,7 @@ module Phlex::SGML::Elements
|
|
90
90
|
buffer << "<#{tag}"
|
91
91
|
begin
|
92
92
|
#{COMMA_SEPARATED_TOKENS[method_name]}
|
93
|
-
buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||=
|
93
|
+
buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
|
94
94
|
ensure
|
95
95
|
buffer << "></#{tag}>"
|
96
96
|
end
|
@@ -152,7 +152,7 @@ module Phlex::SGML::Elements
|
|
152
152
|
buffer << "<#{tag}"
|
153
153
|
begin
|
154
154
|
#{COMMA_SEPARATED_TOKENS[method_name]}
|
155
|
-
buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||=
|
155
|
+
buffer << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
|
156
156
|
ensure
|
157
157
|
buffer << ">"
|
158
158
|
end
|
data/lib/phlex/sgml.rb
CHANGED
@@ -2,9 +2,6 @@
|
|
2
2
|
|
3
3
|
# **Standard Generalized Markup Language** for behaviour common to {HTML} and {SVG}.
|
4
4
|
class Phlex::SGML
|
5
|
-
UNSAFE_ATTRIBUTES = Set.new(%w[srcdoc sandbox http-equiv]).freeze
|
6
|
-
REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping]).freeze
|
7
|
-
|
8
5
|
ERBCompiler = ERB::Compiler.new("<>").tap do |compiler|
|
9
6
|
compiler.pre_cmd = [""]
|
10
7
|
compiler.put_cmd = "@_state.buffer.<<"
|
@@ -462,261 +459,10 @@ class Phlex::SGML
|
|
462
459
|
true
|
463
460
|
end
|
464
461
|
|
465
|
-
private def
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
name = case k
|
470
|
-
when String then k
|
471
|
-
when Symbol then k.name.tr("_", "-")
|
472
|
-
else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols.")
|
473
|
-
end
|
474
|
-
|
475
|
-
value = case v
|
476
|
-
when true
|
477
|
-
true
|
478
|
-
when String
|
479
|
-
v.gsub('"', """)
|
480
|
-
when Symbol
|
481
|
-
v.name.tr("_", "-").gsub('"', """)
|
482
|
-
when Integer, Float
|
483
|
-
v.to_s
|
484
|
-
when Date
|
485
|
-
v.iso8601
|
486
|
-
when Time
|
487
|
-
v.respond_to?(:iso8601) ? v.iso8601 : v.strftime("%Y-%m-%dT%H:%M:%S%:z")
|
488
|
-
when Hash
|
489
|
-
case k
|
490
|
-
when :style
|
491
|
-
__styles__(v).gsub('"', """)
|
492
|
-
else
|
493
|
-
__nested_attributes__(v, "#{name}-", buffer)
|
494
|
-
end
|
495
|
-
when Array
|
496
|
-
case k
|
497
|
-
when :style
|
498
|
-
__styles__(v).gsub('"', """)
|
499
|
-
else
|
500
|
-
__nested_tokens__(v)
|
501
|
-
end
|
502
|
-
when Set
|
503
|
-
case k
|
504
|
-
when :style
|
505
|
-
__styles__(v).gsub('"', """)
|
506
|
-
else
|
507
|
-
__nested_tokens__(v.to_a)
|
508
|
-
end
|
509
|
-
when Phlex::SGML::SafeObject
|
510
|
-
v.to_s.gsub('"', """)
|
511
|
-
else
|
512
|
-
raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
|
513
|
-
end
|
514
|
-
|
515
|
-
lower_name = name.downcase
|
516
|
-
|
517
|
-
unless Phlex::SGML::SafeObject === v
|
518
|
-
normalized_name = lower_name.delete("^a-z-")
|
519
|
-
|
520
|
-
if value != true && REF_ATTRIBUTES.include?(normalized_name)
|
521
|
-
case value
|
522
|
-
when String
|
523
|
-
if value.downcase.delete("^a-z:").start_with?("javascript:")
|
524
|
-
# We just ignore these because they were likely not specified by the developer.
|
525
|
-
next
|
526
|
-
end
|
527
|
-
else
|
528
|
-
raise Phlex::ArgumentError.new("Invalid attribute value for #{k}: #{v.inspect}.")
|
529
|
-
end
|
530
|
-
end
|
531
|
-
|
532
|
-
if normalized_name.bytesize > 2 && normalized_name.start_with?("on") && !normalized_name.include?("-")
|
533
|
-
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
534
|
-
end
|
535
|
-
|
536
|
-
if UNSAFE_ATTRIBUTES.include?(normalized_name)
|
537
|
-
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
538
|
-
end
|
539
|
-
end
|
540
|
-
|
541
|
-
if name.match?(/[<>&"']/)
|
542
|
-
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
543
|
-
end
|
544
|
-
|
545
|
-
if lower_name.to_sym == :id && k != :id
|
546
|
-
raise Phlex::ArgumentError.new(":id attribute should only be passed as a lowercase symbol.")
|
547
|
-
end
|
548
|
-
|
549
|
-
case value
|
550
|
-
when true
|
551
|
-
buffer << " " << name
|
552
|
-
when String
|
553
|
-
buffer << " " << name << '="' << value << '"'
|
554
|
-
end
|
555
|
-
end
|
556
|
-
|
557
|
-
buffer
|
558
|
-
end
|
559
|
-
|
560
|
-
# Provides the nested-attributes case for serializing out attributes.
|
561
|
-
# This allows us to skip many of the checks the `__attributes__` method must perform.
|
562
|
-
private def __nested_attributes__(attributes, base_name, buffer = +"")
|
563
|
-
attributes.each do |k, v|
|
564
|
-
next unless v
|
565
|
-
|
566
|
-
if (root_key = (:_ == k))
|
567
|
-
name = ""
|
568
|
-
original_base_name = base_name
|
569
|
-
base_name = base_name.delete_suffix("-")
|
570
|
-
else
|
571
|
-
name = case k
|
572
|
-
when String then k
|
573
|
-
when Symbol then k.name.tr("_", "-")
|
574
|
-
else raise Phlex::ArgumentError.new("Attribute keys should be Strings or Symbols")
|
575
|
-
end
|
576
|
-
|
577
|
-
if name.match?(/[<>&"']/)
|
578
|
-
raise Phlex::ArgumentError.new("Unsafe attribute name detected: #{k}.")
|
579
|
-
end
|
580
|
-
end
|
581
|
-
|
582
|
-
case v
|
583
|
-
when true
|
584
|
-
buffer << " " << base_name << name
|
585
|
-
when String
|
586
|
-
buffer << " " << base_name << name << '="' << v.gsub('"', """) << '"'
|
587
|
-
when Symbol
|
588
|
-
buffer << " " << base_name << name << '="' << v.name.tr("_", "-").gsub('"', """) << '"'
|
589
|
-
when Integer, Float
|
590
|
-
buffer << " " << base_name << name << '="' << v.to_s << '"'
|
591
|
-
when Hash
|
592
|
-
__nested_attributes__(v, "#{base_name}#{name}-", buffer)
|
593
|
-
when Array
|
594
|
-
buffer << " " << base_name << name << '="' << __nested_tokens__(v) << '"'
|
595
|
-
when Set
|
596
|
-
buffer << " " << base_name << name << '="' << __nested_tokens__(v.to_a) << '"'
|
597
|
-
when Phlex::SGML::SafeObject
|
598
|
-
buffer << " " << base_name << name << '="' << v.to_s.gsub('"', """) << '"'
|
599
|
-
else
|
600
|
-
raise Phlex::ArgumentError.new("Invalid attribute value #{v.inspect}.")
|
601
|
-
end
|
602
|
-
|
603
|
-
if root_key
|
604
|
-
base_name = original_base_name
|
605
|
-
end
|
606
|
-
|
607
|
-
buffer
|
608
|
-
end
|
609
|
-
end
|
610
|
-
|
611
|
-
private def __nested_tokens__(tokens, sep = " ")
|
612
|
-
buffer = +""
|
613
|
-
|
614
|
-
i, length = 0, tokens.length
|
615
|
-
|
616
|
-
while i < length
|
617
|
-
token = tokens[i]
|
618
|
-
|
619
|
-
case token
|
620
|
-
when String
|
621
|
-
if i > 0
|
622
|
-
buffer << sep << token
|
623
|
-
else
|
624
|
-
buffer << token
|
625
|
-
end
|
626
|
-
when Symbol
|
627
|
-
if i > 0
|
628
|
-
buffer << sep << token.name.tr("_", "-")
|
629
|
-
else
|
630
|
-
buffer << token.name.tr("_", "-")
|
631
|
-
end
|
632
|
-
when Integer, Float, Phlex::SGML::SafeObject
|
633
|
-
if i > 0
|
634
|
-
buffer << sep << token.to_s
|
635
|
-
else
|
636
|
-
buffer << token.to_s
|
637
|
-
end
|
638
|
-
when Array
|
639
|
-
if token.length > 0
|
640
|
-
if i > 0
|
641
|
-
buffer << sep << __nested_tokens__(token, sep)
|
642
|
-
else
|
643
|
-
buffer << __nested_tokens__(token, sep)
|
644
|
-
end
|
645
|
-
end
|
646
|
-
when nil
|
647
|
-
# Do nothing
|
648
|
-
else
|
649
|
-
raise Phlex::ArgumentError.new("Invalid token type: #{token.class}.")
|
650
|
-
end
|
651
|
-
|
652
|
-
i += 1
|
653
|
-
end
|
654
|
-
|
655
|
-
buffer.gsub('"', """)
|
656
|
-
end
|
657
|
-
|
658
|
-
# Result is **unsafe**, so it should be escaped!
|
659
|
-
private def __styles__(styles)
|
660
|
-
case styles
|
661
|
-
when Array, Set
|
662
|
-
styles.filter_map do |s|
|
663
|
-
case s
|
664
|
-
when String
|
665
|
-
if s == "" || s.end_with?(";")
|
666
|
-
s
|
667
|
-
else
|
668
|
-
"#{s};"
|
669
|
-
end
|
670
|
-
when Phlex::SGML::SafeObject
|
671
|
-
value = s.to_s
|
672
|
-
value.end_with?(";") ? value : "#{value};"
|
673
|
-
when Hash
|
674
|
-
next __styles__(s)
|
675
|
-
when nil
|
676
|
-
next nil
|
677
|
-
else
|
678
|
-
raise Phlex::ArgumentError.new("Invalid style: #{s.inspect}.")
|
679
|
-
end
|
680
|
-
end.join(" ")
|
681
|
-
when Hash
|
682
|
-
buffer = +""
|
683
|
-
i = 0
|
684
|
-
styles.each do |k, v|
|
685
|
-
prop = case k
|
686
|
-
when String
|
687
|
-
k
|
688
|
-
when Symbol
|
689
|
-
k.name.tr("_", "-")
|
690
|
-
else
|
691
|
-
raise Phlex::ArgumentError.new("Style keys should be Strings or Symbols.")
|
692
|
-
end
|
693
|
-
|
694
|
-
value = case v
|
695
|
-
when String
|
696
|
-
v
|
697
|
-
when Symbol
|
698
|
-
v.name.tr("_", "-")
|
699
|
-
when Integer, Float, Phlex::SGML::SafeObject
|
700
|
-
v.to_s
|
701
|
-
when nil
|
702
|
-
nil
|
703
|
-
else
|
704
|
-
raise Phlex::ArgumentError.new("Invalid style value: #{v.inspect}")
|
705
|
-
end
|
706
|
-
|
707
|
-
if value
|
708
|
-
if i == 0
|
709
|
-
buffer << prop << ": " << value << ";"
|
710
|
-
else
|
711
|
-
buffer << " " << prop << ": " << value << ";"
|
712
|
-
end
|
713
|
-
end
|
714
|
-
|
715
|
-
i += 1
|
716
|
-
end
|
717
|
-
|
718
|
-
buffer
|
719
|
-
end
|
462
|
+
private def __render_attributes__(attributes)
|
463
|
+
state = @_state
|
464
|
+
return unless state.should_render?
|
465
|
+
state.buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
|
720
466
|
end
|
721
467
|
|
722
468
|
private_class_method def self.method_added(method_name)
|
@@ -726,8 +472,13 @@ class Phlex::SGML
|
|
726
472
|
if location[0] in "/" | "."
|
727
473
|
Phlex.__expand_attribute_cache__(location)
|
728
474
|
end
|
729
|
-
else
|
730
|
-
super
|
731
475
|
end
|
476
|
+
|
477
|
+
super
|
478
|
+
end
|
479
|
+
|
480
|
+
def self.__compile__(method_name)
|
481
|
+
path, line = instance_method(method_name).source_location
|
482
|
+
Phlex::Compiler::Method.new(self, path, line, method_name).compile
|
732
483
|
end
|
733
484
|
end
|
data/lib/phlex/svg.rb
CHANGED
@@ -44,11 +44,11 @@ class Phlex::SVG < Phlex::SGML
|
|
44
44
|
if (tag = StandardElements.__registered_elements__[name]) || (tag = name.name.tr("_", "-")).include?("-")
|
45
45
|
if attributes.length > 0 # with attributes
|
46
46
|
if block_given # with content block
|
47
|
-
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||=
|
47
|
+
buffer << "<#{tag}" << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << ">"
|
48
48
|
__yield_content__(&)
|
49
49
|
buffer << "</#{tag}>"
|
50
50
|
else # without content
|
51
|
-
buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||=
|
51
|
+
buffer << "<#{tag}" << (::Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes)) << "></#{tag}>"
|
52
52
|
end
|
53
53
|
else # without attributes
|
54
54
|
if block_given # with content block
|
data/lib/phlex/version.rb
CHANGED