lutaml-model 0.8.36 → 0.8.38
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/bench/bench_sts.rb +9 -3
- data/bench/gate_config.rb +5 -4
- data/lib/compat/opal/lutaml_model_boot.rb +1 -0
- data/lib/lutaml/key_value/transform.rb +52 -5
- data/lib/lutaml/key_value/transformation.rb +10 -2
- data/lib/lutaml/model/adapter_resolver.rb +14 -2
- data/lib/lutaml/model/attribute.rb +15 -9
- data/lib/lutaml/model/choice.rb +6 -5
- data/lib/lutaml/model/format_registry.rb +3 -1
- data/lib/lutaml/model/liquefiable.rb +23 -2
- data/lib/lutaml/model/mapping/mapping_rule.rb +46 -11
- data/lib/lutaml/model/sequence.rb +47 -2
- data/lib/lutaml/model/serialize/attribute_definition.rb +176 -47
- data/lib/lutaml/model/serialize/deserialization_context.rb +1 -0
- data/lib/lutaml/model/serialize.rb +54 -10
- data/lib/lutaml/model/services/transformer.rb +30 -11
- data/lib/lutaml/model/store.rb +70 -32
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/xml/format.rb +1 -0
- data/lib/lutaml/xml/html_entities.rb +290 -0
- data/lib/lutaml/xml/mapping.rb +72 -0
- data/lib/lutaml/xml/model_transform.rb +210 -72
- data/lib/lutaml/xml/parse_session.rb +38 -0
- data/lib/lutaml/yaml/format.rb +1 -0
- data/spec/lutaml/model/adapter_resolver_configured_spec.rb +38 -0
- data/spec/lutaml/model/custom_method_context_spec.rb +62 -0
- data/spec/lutaml/model/liquefiable_lazy_spec.rb +36 -0
- data/spec/lutaml/model/mapping_type_validation_spec.rb +61 -0
- data/spec/lutaml/model/sequence_choice_compositor_spec.rb +66 -0
- data/spec/lutaml/model/store_spec.rb +13 -11
- data/spec/lutaml/model/yaml_stream_spec.rb +70 -0
- data/spec/lutaml/xml/html_entities_spec.rb +45 -0
- metadata +9 -1
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# lutaml-model#154: HTML 4.01 named entity table (plus apos) for opt-in
|
|
4
|
+
# decoding of HTML entities in XML text (MathML and friends rely on
|
|
5
|
+
# them although XML itself defines none beyond the predefined five).
|
|
6
|
+
|
|
7
|
+
module Lutaml
|
|
8
|
+
module Xml
|
|
9
|
+
module HtmlEntities
|
|
10
|
+
# The table is the data: a gem dependency or data file for a static
|
|
11
|
+
# W3C list would cost more than the frozen literal itself.
|
|
12
|
+
# The table is the data: a gem dependency or data file for a static
|
|
13
|
+
# W3C list would cost more than the frozen literal itself.
|
|
14
|
+
NAMED_TO_CODEPOINT = { # rubocop:disable Metrics/CollectionLiteralLength
|
|
15
|
+
"AElig" => 198,
|
|
16
|
+
"Aacute" => 193,
|
|
17
|
+
"Acirc" => 194,
|
|
18
|
+
"Agrave" => 192,
|
|
19
|
+
"Alpha" => 913,
|
|
20
|
+
"Aring" => 197,
|
|
21
|
+
"Atilde" => 195,
|
|
22
|
+
"Auml" => 196,
|
|
23
|
+
"Beta" => 914,
|
|
24
|
+
"Ccedil" => 199,
|
|
25
|
+
"Chi" => 935,
|
|
26
|
+
"Dagger" => 8225,
|
|
27
|
+
"Delta" => 916,
|
|
28
|
+
"ETH" => 208,
|
|
29
|
+
"Eacute" => 201,
|
|
30
|
+
"Ecirc" => 202,
|
|
31
|
+
"Egrave" => 200,
|
|
32
|
+
"Epsilon" => 917,
|
|
33
|
+
"Eta" => 919,
|
|
34
|
+
"Euml" => 203,
|
|
35
|
+
"Gamma" => 915,
|
|
36
|
+
"Iacute" => 205,
|
|
37
|
+
"Icirc" => 206,
|
|
38
|
+
"Igrave" => 204,
|
|
39
|
+
"Iota" => 921,
|
|
40
|
+
"Iuml" => 207,
|
|
41
|
+
"Kappa" => 922,
|
|
42
|
+
"Lambda" => 923,
|
|
43
|
+
"Mu" => 924,
|
|
44
|
+
"Ntilde" => 209,
|
|
45
|
+
"Nu" => 925,
|
|
46
|
+
"OElig" => 338,
|
|
47
|
+
"Oacute" => 211,
|
|
48
|
+
"Ocirc" => 212,
|
|
49
|
+
"Ograve" => 210,
|
|
50
|
+
"Omega" => 937,
|
|
51
|
+
"Omicron" => 927,
|
|
52
|
+
"Oslash" => 216,
|
|
53
|
+
"Otilde" => 213,
|
|
54
|
+
"Ouml" => 214,
|
|
55
|
+
"Phi" => 934,
|
|
56
|
+
"Pi" => 928,
|
|
57
|
+
"Prime" => 8243,
|
|
58
|
+
"Psi" => 936,
|
|
59
|
+
"Rho" => 929,
|
|
60
|
+
"Scaron" => 352,
|
|
61
|
+
"Sigma" => 931,
|
|
62
|
+
"THORN" => 222,
|
|
63
|
+
"Tau" => 932,
|
|
64
|
+
"Theta" => 920,
|
|
65
|
+
"Uacute" => 218,
|
|
66
|
+
"Ucirc" => 219,
|
|
67
|
+
"Ugrave" => 217,
|
|
68
|
+
"Upsilon" => 933,
|
|
69
|
+
"Uuml" => 220,
|
|
70
|
+
"Xi" => 926,
|
|
71
|
+
"Yacute" => 221,
|
|
72
|
+
"Yuml" => 376,
|
|
73
|
+
"Zeta" => 918,
|
|
74
|
+
"aacute" => 225,
|
|
75
|
+
"acirc" => 226,
|
|
76
|
+
"acute" => 180,
|
|
77
|
+
"aelig" => 230,
|
|
78
|
+
"agrave" => 224,
|
|
79
|
+
"alefsym" => 8501,
|
|
80
|
+
"alpha" => 945,
|
|
81
|
+
"amp" => 38,
|
|
82
|
+
"and" => 8743,
|
|
83
|
+
"ang" => 8736,
|
|
84
|
+
"apos" => 39,
|
|
85
|
+
"aring" => 229,
|
|
86
|
+
"asymp" => 8776,
|
|
87
|
+
"atilde" => 227,
|
|
88
|
+
"auml" => 228,
|
|
89
|
+
"bdquo" => 8222,
|
|
90
|
+
"beta" => 946,
|
|
91
|
+
"brvbar" => 166,
|
|
92
|
+
"bull" => 8226,
|
|
93
|
+
"cap" => 8745,
|
|
94
|
+
"ccedil" => 231,
|
|
95
|
+
"cedil" => 184,
|
|
96
|
+
"cent" => 162,
|
|
97
|
+
"chi" => 967,
|
|
98
|
+
"circ" => 710,
|
|
99
|
+
"clubs" => 9827,
|
|
100
|
+
"cong" => 8773,
|
|
101
|
+
"copy" => 169,
|
|
102
|
+
"crarr" => 8629,
|
|
103
|
+
"cup" => 8746,
|
|
104
|
+
"curren" => 164,
|
|
105
|
+
"dArr" => 8659,
|
|
106
|
+
"dagger" => 8224,
|
|
107
|
+
"darr" => 8595,
|
|
108
|
+
"deg" => 176,
|
|
109
|
+
"delta" => 948,
|
|
110
|
+
"diams" => 9830,
|
|
111
|
+
"divide" => 247,
|
|
112
|
+
"eacute" => 233,
|
|
113
|
+
"ecirc" => 234,
|
|
114
|
+
"egrave" => 232,
|
|
115
|
+
"empty" => 8709,
|
|
116
|
+
"emsp" => 8195,
|
|
117
|
+
"ensp" => 8194,
|
|
118
|
+
"epsilon" => 949,
|
|
119
|
+
"equiv" => 8801,
|
|
120
|
+
"eta" => 951,
|
|
121
|
+
"eth" => 240,
|
|
122
|
+
"euml" => 235,
|
|
123
|
+
"euro" => 8364,
|
|
124
|
+
"exist" => 8707,
|
|
125
|
+
"fnof" => 402,
|
|
126
|
+
"forall" => 8704,
|
|
127
|
+
"frac12" => 189,
|
|
128
|
+
"frac14" => 188,
|
|
129
|
+
"frac34" => 190,
|
|
130
|
+
"frasl" => 8260,
|
|
131
|
+
"gamma" => 947,
|
|
132
|
+
"ge" => 8805,
|
|
133
|
+
"gt" => 62,
|
|
134
|
+
"hArr" => 8660,
|
|
135
|
+
"harr" => 8596,
|
|
136
|
+
"hearts" => 9829,
|
|
137
|
+
"hellip" => 8230,
|
|
138
|
+
"iacute" => 237,
|
|
139
|
+
"icirc" => 238,
|
|
140
|
+
"iexcl" => 161,
|
|
141
|
+
"igrave" => 236,
|
|
142
|
+
"image" => 8465,
|
|
143
|
+
"infin" => 8734,
|
|
144
|
+
"int" => 8747,
|
|
145
|
+
"iota" => 953,
|
|
146
|
+
"iquest" => 191,
|
|
147
|
+
"isin" => 8712,
|
|
148
|
+
"iuml" => 239,
|
|
149
|
+
"kappa" => 954,
|
|
150
|
+
"lArr" => 8656,
|
|
151
|
+
"lambda" => 955,
|
|
152
|
+
"lang" => 9001,
|
|
153
|
+
"laquo" => 171,
|
|
154
|
+
"larr" => 8592,
|
|
155
|
+
"lceil" => 8968,
|
|
156
|
+
"ldquo" => 8220,
|
|
157
|
+
"le" => 8804,
|
|
158
|
+
"lfloor" => 8970,
|
|
159
|
+
"lowast" => 8727,
|
|
160
|
+
"loz" => 9674,
|
|
161
|
+
"lrm" => 8206,
|
|
162
|
+
"lsaquo" => 8249,
|
|
163
|
+
"lsquo" => 8216,
|
|
164
|
+
"lt" => 60,
|
|
165
|
+
"macr" => 175,
|
|
166
|
+
"mdash" => 8212,
|
|
167
|
+
"micro" => 181,
|
|
168
|
+
"middot" => 183,
|
|
169
|
+
"minus" => 8722,
|
|
170
|
+
"mu" => 956,
|
|
171
|
+
"nabla" => 8711,
|
|
172
|
+
"nbsp" => 160,
|
|
173
|
+
"ndash" => 8211,
|
|
174
|
+
"ne" => 8800,
|
|
175
|
+
"ni" => 8715,
|
|
176
|
+
"not" => 172,
|
|
177
|
+
"notin" => 8713,
|
|
178
|
+
"nsub" => 8836,
|
|
179
|
+
"ntilde" => 241,
|
|
180
|
+
"nu" => 957,
|
|
181
|
+
"oacute" => 243,
|
|
182
|
+
"ocirc" => 244,
|
|
183
|
+
"oelig" => 339,
|
|
184
|
+
"ograve" => 242,
|
|
185
|
+
"oline" => 8254,
|
|
186
|
+
"omega" => 969,
|
|
187
|
+
"omicron" => 959,
|
|
188
|
+
"oplus" => 8853,
|
|
189
|
+
"or" => 8744,
|
|
190
|
+
"ordf" => 170,
|
|
191
|
+
"ordm" => 186,
|
|
192
|
+
"oslash" => 248,
|
|
193
|
+
"otilde" => 245,
|
|
194
|
+
"otimes" => 8855,
|
|
195
|
+
"ouml" => 246,
|
|
196
|
+
"para" => 182,
|
|
197
|
+
"part" => 8706,
|
|
198
|
+
"permil" => 8240,
|
|
199
|
+
"perp" => 8869,
|
|
200
|
+
"phi" => 966,
|
|
201
|
+
"pi" => 960,
|
|
202
|
+
"piv" => 982,
|
|
203
|
+
"plusmn" => 177,
|
|
204
|
+
"pound" => 163,
|
|
205
|
+
"prime" => 8242,
|
|
206
|
+
"prod" => 8719,
|
|
207
|
+
"prop" => 8733,
|
|
208
|
+
"psi" => 968,
|
|
209
|
+
"quot" => 34,
|
|
210
|
+
"rArr" => 8658,
|
|
211
|
+
"radic" => 8730,
|
|
212
|
+
"rang" => 9002,
|
|
213
|
+
"raquo" => 187,
|
|
214
|
+
"rarr" => 8594,
|
|
215
|
+
"rceil" => 8969,
|
|
216
|
+
"rdquo" => 8221,
|
|
217
|
+
"real" => 8476,
|
|
218
|
+
"reg" => 174,
|
|
219
|
+
"rfloor" => 8971,
|
|
220
|
+
"rho" => 961,
|
|
221
|
+
"rlm" => 8207,
|
|
222
|
+
"rsaquo" => 8250,
|
|
223
|
+
"rsquo" => 8217,
|
|
224
|
+
"sbquo" => 8218,
|
|
225
|
+
"scaron" => 353,
|
|
226
|
+
"sdot" => 8901,
|
|
227
|
+
"sect" => 167,
|
|
228
|
+
"shy" => 173,
|
|
229
|
+
"sigma" => 963,
|
|
230
|
+
"sigmaf" => 962,
|
|
231
|
+
"sim" => 8764,
|
|
232
|
+
"spades" => 9824,
|
|
233
|
+
"sub" => 8834,
|
|
234
|
+
"sube" => 8838,
|
|
235
|
+
"sum" => 8721,
|
|
236
|
+
"sup" => 8835,
|
|
237
|
+
"sup1" => 185,
|
|
238
|
+
"sup2" => 178,
|
|
239
|
+
"sup3" => 179,
|
|
240
|
+
"supe" => 8839,
|
|
241
|
+
"szlig" => 223,
|
|
242
|
+
"tau" => 964,
|
|
243
|
+
"there4" => 8756,
|
|
244
|
+
"theta" => 952,
|
|
245
|
+
"thetasym" => 977,
|
|
246
|
+
"thinsp" => 8201,
|
|
247
|
+
"thorn" => 254,
|
|
248
|
+
"tilde" => 732,
|
|
249
|
+
"times" => 215,
|
|
250
|
+
"trade" => 8482,
|
|
251
|
+
"uArr" => 8657,
|
|
252
|
+
"uacute" => 250,
|
|
253
|
+
"uarr" => 8593,
|
|
254
|
+
"ucirc" => 251,
|
|
255
|
+
"ugrave" => 249,
|
|
256
|
+
"uml" => 168,
|
|
257
|
+
"upsih" => 978,
|
|
258
|
+
"upsilon" => 965,
|
|
259
|
+
"uuml" => 252,
|
|
260
|
+
"weierp" => 8472,
|
|
261
|
+
"xi" => 958,
|
|
262
|
+
"yacute" => 253,
|
|
263
|
+
"yen" => 165,
|
|
264
|
+
"yuml" => 255,
|
|
265
|
+
"zeta" => 950,
|
|
266
|
+
"zwj" => 8205,
|
|
267
|
+
"zwnj" => 8204,
|
|
268
|
+
}.freeze
|
|
269
|
+
|
|
270
|
+
NAME_PATTERN = /&(#x?[0-9A-Fa-f]+|[A-Za-z][A-Za-z0-9]*);/
|
|
271
|
+
|
|
272
|
+
module_function
|
|
273
|
+
|
|
274
|
+
def decode(value)
|
|
275
|
+
value.gsub(NAME_PATTERN) do |match|
|
|
276
|
+
body = match[1..-2]
|
|
277
|
+
case body[0]
|
|
278
|
+
when "x", "X"
|
|
279
|
+
body[1..].to_i(16).chr(Encoding::UTF_8)
|
|
280
|
+
when "#"
|
|
281
|
+
body[1..].to_i.chr(Encoding::UTF_8)
|
|
282
|
+
else
|
|
283
|
+
codepoint = NAMED_TO_CODEPOINT[body]
|
|
284
|
+
codepoint ? codepoint.chr(Encoding::UTF_8) : match
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
data/lib/lutaml/xml/mapping.rb
CHANGED
|
@@ -84,6 +84,7 @@ module Lutaml
|
|
|
84
84
|
@cached_elements = {}
|
|
85
85
|
@cached_attributes = {}
|
|
86
86
|
@cached_mappings = {}
|
|
87
|
+
@cached_attribute_name_counts = {}
|
|
87
88
|
@finalized = false
|
|
88
89
|
end
|
|
89
90
|
|
|
@@ -98,6 +99,17 @@ module Lutaml
|
|
|
98
99
|
# Resolve any deferred mapping imports before finalizing
|
|
99
100
|
ensure_mappings_imported!
|
|
100
101
|
|
|
102
|
+
# lutaml-model#687: nested sequence choices tag their target
|
|
103
|
+
# model attributes now that the mapper class is known.
|
|
104
|
+
@element_sequence.each do |seq|
|
|
105
|
+
seq.bind_choice_compositors!(mapper_class)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# lutaml-model#296: a mapped rule whose attribute type can never
|
|
109
|
+
# accept the serialization shape fails here, with the attribute
|
|
110
|
+
# and type named, instead of surfacing mid-parse as a cast oddity.
|
|
111
|
+
validate_mapped_attribute_types!(mapper_class)
|
|
112
|
+
|
|
101
113
|
# Validate mixed content requires collection attribute for content mapping
|
|
102
114
|
validate_mixed_content_collection!(mapper_class)
|
|
103
115
|
|
|
@@ -108,9 +120,17 @@ module Lutaml
|
|
|
108
120
|
@cached_elements.clear
|
|
109
121
|
@cached_attributes.clear
|
|
110
122
|
@cached_mappings.clear
|
|
123
|
+
@cached_attribute_name_counts.clear
|
|
124
|
+
@rule_records_version = (@rule_records_version || 0) + 1
|
|
111
125
|
@finalized = true
|
|
112
126
|
end
|
|
113
127
|
|
|
128
|
+
# Bumped at finalize; ModelTransform's compiled rule records are
|
|
129
|
+
# keyed on it so late mapping changes rebuild instead of going stale.
|
|
130
|
+
def rule_records_version
|
|
131
|
+
@rule_records_version ||= 0
|
|
132
|
+
end
|
|
133
|
+
|
|
114
134
|
def finalized?
|
|
115
135
|
@finalized
|
|
116
136
|
end
|
|
@@ -1028,6 +1048,26 @@ module Lutaml
|
|
|
1028
1048
|
end
|
|
1029
1049
|
end
|
|
1030
1050
|
|
|
1051
|
+
# Fire InvalidAttributeTypeError at definition time for mapped
|
|
1052
|
+
# rules whose target attribute type is neither a Type::Value nor
|
|
1053
|
+
# Serializable (lutaml-model#296). Undeclared types are left to
|
|
1054
|
+
# deferred-import resolution.
|
|
1055
|
+
def validate_mapped_attribute_types!(mapper_class)
|
|
1056
|
+
(elements + attributes).each do |rule|
|
|
1057
|
+
target = mapper_class.attributes[rule.to]
|
|
1058
|
+
next unless target
|
|
1059
|
+
|
|
1060
|
+
begin
|
|
1061
|
+
resolved = target.type(Lutaml::Model::Config.default_register)
|
|
1062
|
+
rescue Lutaml::Model::UnknownTypeError
|
|
1063
|
+
next
|
|
1064
|
+
end
|
|
1065
|
+
next unless resolved.is_a?(Class)
|
|
1066
|
+
|
|
1067
|
+
target.validate_attr_type!(resolved)
|
|
1068
|
+
end
|
|
1069
|
+
end
|
|
1070
|
+
|
|
1031
1071
|
def elements(register_id = nil)
|
|
1032
1072
|
reg_key = register_id || :default
|
|
1033
1073
|
cached = @cached_elements[reg_key]
|
|
@@ -1060,6 +1100,26 @@ module Lutaml
|
|
|
1060
1100
|
@raw_mapping
|
|
1061
1101
|
end
|
|
1062
1102
|
|
|
1103
|
+
# lutaml-model#790/#791: whether `rule` is the only attribute rule
|
|
1104
|
+
# claiming its exact name spelling. The answer is a property of the
|
|
1105
|
+
# mapping, not the document, so it is counted once per register
|
|
1106
|
+
# (rules sharing a spelling make each other non-sole).
|
|
1107
|
+
def sole_local_attribute_claimant?(rule, register_id = nil)
|
|
1108
|
+
reg_key = register_id || :default
|
|
1109
|
+
counts = @cached_attribute_name_counts[reg_key]
|
|
1110
|
+
unless counts
|
|
1111
|
+
counts = {}
|
|
1112
|
+
attributes(register_id).each do |r|
|
|
1113
|
+
key = r.name_strings
|
|
1114
|
+
counts[key] = (counts[key] || 0) + 1
|
|
1115
|
+
end
|
|
1116
|
+
counts.freeze if @finalized
|
|
1117
|
+
@cached_attribute_name_counts[reg_key] = counts
|
|
1118
|
+
end
|
|
1119
|
+
|
|
1120
|
+
(counts[rule.name_strings] || 1) == 1
|
|
1121
|
+
end
|
|
1122
|
+
|
|
1063
1123
|
def mappings(register_id = nil)
|
|
1064
1124
|
reg_key = register_id || :default
|
|
1065
1125
|
cached = @cached_mappings[reg_key]
|
|
@@ -1281,6 +1341,18 @@ module Lutaml
|
|
|
1281
1341
|
!@namespace_class.nil?
|
|
1282
1342
|
end
|
|
1283
1343
|
|
|
1344
|
+
# lutaml-model#154: opt-in decoding of HTML named/numeric entities
|
|
1345
|
+
# (e.g. ©, —) in mapped text and attribute values. XML
|
|
1346
|
+
# documents are otherwise kept byte-faithful: HTML entities are not
|
|
1347
|
+
# defined in XML without a DTD.
|
|
1348
|
+
def html_entities(enabled: true)
|
|
1349
|
+
@decode_html_entities = enabled
|
|
1350
|
+
end
|
|
1351
|
+
|
|
1352
|
+
def decode_html_entities?
|
|
1353
|
+
!!@decode_html_entities
|
|
1354
|
+
end
|
|
1355
|
+
|
|
1284
1356
|
# Whether namespace was explicitly set via DSL
|
|
1285
1357
|
def namespace_set?
|
|
1286
1358
|
!!@namespace_set
|