moxml 0.5.171 → 0.5.172

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +23 -0
  3. data/conformance/moxml-conformance.json +413 -0
  4. data/lib/moxml/adapter/customized_leptris/declaration.rb +9 -0
  5. data/lib/moxml/adapter/customized_leptris/doctype.rb +8 -0
  6. data/lib/moxml/adapter/customized_leptris/entity_reference.rb +8 -0
  7. data/lib/moxml/adapter/customized_leptris/text_segment.rb +8 -0
  8. data/lib/moxml/adapter/customized_leptris.rb +5 -0
  9. data/lib/moxml/adapter/customized_libxml/cdata.rb +2 -0
  10. data/lib/moxml/adapter/customized_libxml/comment.rb +2 -0
  11. data/lib/moxml/adapter/customized_libxml/declaration.rb +2 -0
  12. data/lib/moxml/adapter/customized_libxml/element.rb +2 -0
  13. data/lib/moxml/adapter/customized_libxml/entity_reference.rb +2 -0
  14. data/lib/moxml/adapter/customized_libxml/node.rb +2 -0
  15. data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +2 -0
  16. data/lib/moxml/adapter/customized_libxml/text.rb +2 -0
  17. data/lib/moxml/adapter/customized_libxml.rb +3 -0
  18. data/lib/moxml/adapter/customized_oga/entity_decoder.rb +8 -0
  19. data/lib/moxml/adapter/customized_oga/raw_value_override.rb +8 -0
  20. data/lib/moxml/adapter/customized_oga/xml_declaration.rb +8 -0
  21. data/lib/moxml/adapter/customized_oga/xml_generator.rb +1 -1
  22. data/lib/moxml/adapter/customized_oga.rb +4 -0
  23. data/lib/moxml/adapter/customized_ox/attribute.rb +5 -0
  24. data/lib/moxml/adapter/customized_ox/entity_reference.rb +5 -0
  25. data/lib/moxml/adapter/customized_ox/namespace.rb +5 -0
  26. data/lib/moxml/adapter/customized_ox/text.rb +6 -0
  27. data/lib/moxml/adapter/customized_ox.rb +3 -0
  28. data/lib/moxml/adapter/customized_rexml/entity_reference.rb +5 -0
  29. data/lib/moxml/adapter/customized_rexml/formatter.rb +6 -0
  30. data/lib/moxml/adapter/customized_rexml.rb +4 -0
  31. data/lib/moxml/adapter.rb +13 -0
  32. data/lib/moxml/version.rb +1 -1
  33. data/lib/moxml/xsd/builtin_lexical.rb +100 -0
  34. data/spec/moxml/adapter/customized_lifecycle_audit_spec.rb +49 -0
  35. data/spec/moxml/adapter_registry_spec.rb +44 -0
  36. data/spec/moxml/customized_lifecycle_spec.rb +47 -0
  37. data/spec/moxml/xsd_conformance_cases_spec.rb +37 -0
  38. metadata +6 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd5e790791e306829e87770a45424b006c9a535c95c825adfc7d0800816ceb7d
4
- data.tar.gz: c3348f9fb7c6c0ccf00ab4bc16bba51b0812331d12d8478d2a77e09b8a600e58
3
+ metadata.gz: f8719e57bb76ceb63baa171d151028d8aff538fc351be7397b6ec5a492c8bdf1
4
+ data.tar.gz: 8fa85d23570f0588da0eb5d4ce5f83d59fb28e325d587f7210d3380a8afd5e33
5
5
  SHA512:
6
- metadata.gz: 2972a94dc0bf75896d29432c1856b949090cbd8547b60c402376f2d715c743c8e029af2181531fbd327d6615a3a6aef5cb83144c5ff2b6389f35bd3ab6dfea59
7
- data.tar.gz: c89e1be189130b17f0a7f61c67c9caba0624f589c29a2407effba7fb50da72304d33db77001185cb7b079729a7a29140d32e8a492269a58cf748a8e99f0276a2
6
+ metadata.gz: b93fc457a9d7435e2a55cb4fb5e3320d37b5949e77b184c640dd65a8b21954aab9987f204c8916411d9bfe6e11f7f0e358faa78f1ee9cfe3477f52b2d209e857
7
+ data.tar.gz: 78c66d6b3bdbb800b8cf23dde851235ab3a621bbb30fd0c413db06f9f856ba245b56f9a2c4a4d8220ca313b3bb2ef447d5737733387052bd82f2151cfd7bcdc5
data/Rakefile CHANGED
@@ -350,4 +350,27 @@ namespace :docs do
350
350
  end
351
351
  end
352
352
 
353
+ namespace :conformance do
354
+ desc "Export the cross-language conformance dataset to JSON (#496)"
355
+ task :export do
356
+ require "json"
357
+ require_relative "lib/moxml"
358
+ dataset = Moxml::XSD::BuiltinLexical::CONFORMANCE_CASES
359
+ payload = {
360
+ schema: 1,
361
+ generator: "moxml #{Moxml::VERSION}",
362
+ cases: {
363
+ "builtin_lexical" => dataset.map do |type, lexical, expected|
364
+ { "type" => type, "lexical" => lexical, "valid" => expected }
365
+ end,
366
+ },
367
+ }
368
+ dir = File.expand_path("conformance", __dir__)
369
+ FileUtils.mkdir_p(dir)
370
+ target = File.join(dir, "moxml-conformance.json")
371
+ File.write(target, "#{JSON.pretty_generate(payload)}\n")
372
+ puts "conformance: exported #{dataset.size} cases to #{target}"
373
+ end
374
+ end
375
+
353
376
  task default: :spec
@@ -0,0 +1,413 @@
1
+ {
2
+ "schema": 1,
3
+ "generator": "moxml 0.5.171",
4
+ "cases": {
5
+ "builtin_lexical": [
6
+ {
7
+ "type": "xs:token",
8
+ "lexical": "a b",
9
+ "valid": true
10
+ },
11
+ {
12
+ "type": "xs:ID",
13
+ "lexical": " id ",
14
+ "valid": true
15
+ },
16
+ {
17
+ "type": "xs:NMTOKEN",
18
+ "lexical": "\tx ",
19
+ "valid": true
20
+ },
21
+ {
22
+ "type": "xs:ID",
23
+ "lexical": "1d",
24
+ "valid": false
25
+ },
26
+ {
27
+ "type": "xs:integer",
28
+ "lexical": "11111111111111111111111111111",
29
+ "valid": true
30
+ },
31
+ {
32
+ "type": "xs:positiveInteger",
33
+ "lexical": "11111111111111111111111111111",
34
+ "valid": true
35
+ },
36
+ {
37
+ "type": "xs:nonNegativeInteger",
38
+ "lexical": "11111111111111111111111111111",
39
+ "valid": true
40
+ },
41
+ {
42
+ "type": "xs:negativeInteger",
43
+ "lexical": "-100000000000000000000",
44
+ "valid": true
45
+ },
46
+ {
47
+ "type": "xs:nonPositiveInteger",
48
+ "lexical": "0",
49
+ "valid": true
50
+ },
51
+ {
52
+ "type": "xs:nonPositiveInteger",
53
+ "lexical": "-0",
54
+ "valid": true
55
+ },
56
+ {
57
+ "type": "xs:long",
58
+ "lexical": "9223372036854775807",
59
+ "valid": true
60
+ },
61
+ {
62
+ "type": "xs:long",
63
+ "lexical": "9223372036854775808",
64
+ "valid": false
65
+ },
66
+ {
67
+ "type": "xs:unsignedLong",
68
+ "lexical": "18446744073709551615",
69
+ "valid": true
70
+ },
71
+ {
72
+ "type": "xs:unsignedLong",
73
+ "lexical": "18446744073709551616",
74
+ "valid": false
75
+ },
76
+ {
77
+ "type": "xs:byte",
78
+ "lexical": "128",
79
+ "valid": false
80
+ },
81
+ {
82
+ "type": "xs:unsignedByte",
83
+ "lexical": "256",
84
+ "valid": false
85
+ },
86
+ {
87
+ "type": "xs:float",
88
+ "lexical": "+INF",
89
+ "valid": true
90
+ },
91
+ {
92
+ "type": "xs:double",
93
+ "lexical": "-INF",
94
+ "valid": true
95
+ },
96
+ {
97
+ "type": "xs:float",
98
+ "lexical": "NaN",
99
+ "valid": true
100
+ },
101
+ {
102
+ "type": "xs:float",
103
+ "lexical": "1.5E-3",
104
+ "valid": true
105
+ },
106
+ {
107
+ "type": "xs:decimal",
108
+ "lexical": "5.",
109
+ "valid": true
110
+ },
111
+ {
112
+ "type": "xs:decimal",
113
+ "lexical": ".5",
114
+ "valid": true
115
+ },
116
+ {
117
+ "type": "xs:decimal",
118
+ "lexical": "1e3",
119
+ "valid": false
120
+ },
121
+ {
122
+ "type": "xs:date",
123
+ "lexical": "2024-02-29",
124
+ "valid": true
125
+ },
126
+ {
127
+ "type": "xs:date",
128
+ "lexical": "1999-02-29",
129
+ "valid": false
130
+ },
131
+ {
132
+ "type": "xs:date",
133
+ "lexical": "2024-04-31",
134
+ "valid": false
135
+ },
136
+ {
137
+ "type": "xs:date",
138
+ "lexical": "2024-13-01",
139
+ "valid": false
140
+ },
141
+ {
142
+ "type": "xs:date",
143
+ "lexical": "not-a-date",
144
+ "valid": false
145
+ },
146
+ {
147
+ "type": "xs:dateTime",
148
+ "lexical": "2024-02-29T23:59:59Z",
149
+ "valid": true
150
+ },
151
+ {
152
+ "type": "xs:dateTime",
153
+ "lexical": "1999-02-29T00:00:00",
154
+ "valid": false
155
+ },
156
+ {
157
+ "type": "xs:dateTime",
158
+ "lexical": "not-a-date",
159
+ "valid": false
160
+ },
161
+ {
162
+ "type": "xs:dateTimeStamp",
163
+ "lexical": "2024-02-29T00:00:00Z",
164
+ "valid": true
165
+ },
166
+ {
167
+ "type": "xs:dateTimeStamp",
168
+ "lexical": "2024-02-29T00:00:00",
169
+ "valid": false
170
+ },
171
+ {
172
+ "type": "xs:time",
173
+ "lexical": "23:59:59.999+05:30",
174
+ "valid": true
175
+ },
176
+ {
177
+ "type": "xs:time",
178
+ "lexical": "24:00:00",
179
+ "valid": false
180
+ },
181
+ {
182
+ "type": "xs:time",
183
+ "lexical": "25:20:00",
184
+ "valid": false
185
+ },
186
+ {
187
+ "type": "xs:time",
188
+ "lexical": "13:20:60",
189
+ "valid": false
190
+ },
191
+ {
192
+ "type": "xs:gYearMonth",
193
+ "lexical": "2024-02Z",
194
+ "valid": true
195
+ },
196
+ {
197
+ "type": "xs:gYearMonth",
198
+ "lexical": "2024-13",
199
+ "valid": false
200
+ },
201
+ {
202
+ "type": "xs:gMonthDay",
203
+ "lexical": "--02-29",
204
+ "valid": true
205
+ },
206
+ {
207
+ "type": "xs:gMonthDay",
208
+ "lexical": "--02-30",
209
+ "valid": false
210
+ },
211
+ {
212
+ "type": "xs:gDay",
213
+ "lexical": "---31",
214
+ "valid": true
215
+ },
216
+ {
217
+ "type": "xs:gDay",
218
+ "lexical": "---32",
219
+ "valid": false
220
+ },
221
+ {
222
+ "type": "xs:duration",
223
+ "lexical": "P1Y2M3DT4H5M6.5S",
224
+ "valid": true
225
+ },
226
+ {
227
+ "type": "xs:duration",
228
+ "lexical": "PT0S",
229
+ "valid": true
230
+ },
231
+ {
232
+ "type": "xs:duration",
233
+ "lexical": "-P1D",
234
+ "valid": true
235
+ },
236
+ {
237
+ "type": "xs:duration",
238
+ "lexical": "P",
239
+ "valid": false
240
+ },
241
+ {
242
+ "type": "xs:duration",
243
+ "lexical": "PT",
244
+ "valid": false
245
+ },
246
+ {
247
+ "type": "xs:duration",
248
+ "lexical": "P1Y2MT",
249
+ "valid": false
250
+ },
251
+ {
252
+ "type": "xs:duration",
253
+ "lexical": "P200.5Y",
254
+ "valid": false
255
+ },
256
+ {
257
+ "type": "xs:dayTimeDuration",
258
+ "lexical": "P1DT2H",
259
+ "valid": true
260
+ },
261
+ {
262
+ "type": "xs:dayTimeDuration",
263
+ "lexical": "P1Y",
264
+ "valid": false
265
+ },
266
+ {
267
+ "type": "xs:yearMonthDuration",
268
+ "lexical": "P1Y2M",
269
+ "valid": true
270
+ },
271
+ {
272
+ "type": "xs:yearMonthDuration",
273
+ "lexical": "P1D",
274
+ "valid": false
275
+ },
276
+ {
277
+ "type": "xs:yearMonthDuration",
278
+ "lexical": "P1YT1H",
279
+ "valid": false
280
+ },
281
+ {
282
+ "type": "xs:base64Binary",
283
+ "lexical": "aGVsbG8=",
284
+ "valid": true
285
+ },
286
+ {
287
+ "type": "xs:base64Binary",
288
+ "lexical": "abcd",
289
+ "valid": true
290
+ },
291
+ {
292
+ "type": "xs:base64Binary",
293
+ "lexical": "abcde",
294
+ "valid": false
295
+ },
296
+ {
297
+ "type": "xs:base64Binary",
298
+ "lexical": "a b",
299
+ "valid": false
300
+ },
301
+ {
302
+ "type": "xs:hexBinary",
303
+ "lexical": "0A",
304
+ "valid": true
305
+ },
306
+ {
307
+ "type": "xs:hexBinary",
308
+ "lexical": "0AB",
309
+ "valid": false
310
+ },
311
+ {
312
+ "type": "xs:QName",
313
+ "lexical": "foo",
314
+ "valid": true
315
+ },
316
+ {
317
+ "type": "xs:QName",
318
+ "lexical": "ns:foo",
319
+ "valid": true
320
+ },
321
+ {
322
+ "type": "xs:QName",
323
+ "lexical": "xmlns:xsi",
324
+ "valid": false
325
+ },
326
+ {
327
+ "type": "xs:QName",
328
+ "lexical": ":foo",
329
+ "valid": false
330
+ },
331
+ {
332
+ "type": "xs:QName",
333
+ "lexical": "a:b:c",
334
+ "valid": false
335
+ },
336
+ {
337
+ "type": "xs:NOTATION",
338
+ "lexical": "foo",
339
+ "valid": true
340
+ },
341
+ {
342
+ "type": "xs:IDREFS",
343
+ "lexical": "a b",
344
+ "valid": true
345
+ },
346
+ {
347
+ "type": "xs:IDREFS",
348
+ "lexical": "a",
349
+ "valid": true
350
+ },
351
+ {
352
+ "type": "xs:IDREFS",
353
+ "lexical": "",
354
+ "valid": false
355
+ },
356
+ {
357
+ "type": "xs:NMTOKENS",
358
+ "lexical": "a.b-c:d e",
359
+ "valid": true
360
+ },
361
+ {
362
+ "type": "xs:ENTITIES",
363
+ "lexical": "a 1",
364
+ "valid": false
365
+ },
366
+ {
367
+ "type": "xs:string",
368
+ "lexical": "anything <>&",
369
+ "valid": true
370
+ },
371
+ {
372
+ "type": "xs:anyURI",
373
+ "lexical": "http://example.com/a b",
374
+ "valid": true
375
+ },
376
+ {
377
+ "type": "xs:boolean",
378
+ "lexical": "true",
379
+ "valid": true
380
+ },
381
+ {
382
+ "type": "xs:boolean",
383
+ "lexical": "0",
384
+ "valid": true
385
+ },
386
+ {
387
+ "type": "xs:boolean",
388
+ "lexical": "TRUE",
389
+ "valid": false
390
+ },
391
+ {
392
+ "type": "xs:language",
393
+ "lexical": "en-US",
394
+ "valid": true
395
+ },
396
+ {
397
+ "type": "xs:language",
398
+ "lexical": "en_US",
399
+ "valid": false
400
+ },
401
+ {
402
+ "type": "xs:Name",
403
+ "lexical": "a:b",
404
+ "valid": true
405
+ },
406
+ {
407
+ "type": "xs:NCName",
408
+ "lexical": "a:b",
409
+ "valid": false
410
+ }
411
+ ]
412
+ }
413
+ }
@@ -1,5 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Wrapper for an XML declaration.
4
+ #
5
+ # Lifecycle record: libleptris models the declaration as document-state
6
+ # flags — setters shipped (leptris#1094) and clear shipped
7
+ # (leptris#1229, binding 1.9.204.0) — but there is still no first-class
8
+ # declaration NODE, so the facade keeps this mutable, removable value
9
+ # object as the node stand-in. Dissolves when libleptris exposes
10
+ # declaration node identity (leptris#1904's successor surface).
11
+
3
12
  module Moxml
4
13
  module Adapter
5
14
  module CustomizedLeptris
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Wrapper for a programmatic DOCTYPE.
4
+ #
5
+ # Lifecycle record: set shipped (leptris#212, binding 1.9.176) and unset
6
+ # shipped (leptris#1229, binding 1.9.204.0 remove_doctype), but there is
7
+ # still no first-class DOCTYPE NODE — the facade keeps this value object
8
+ # as the node stand-in in the document's attachments. Dissolves with
9
+ # declaration node identity (see customized_leptris/declaration.rb).
10
+
3
11
  module Moxml
4
12
  module Adapter
5
13
  module CustomizedLeptris
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Parse-path face: libleptris expands the five built-in entities at
4
+ # parse time and has no custom ones, so parsed references are carried
5
+ # as Moxml::Entity::Reference values over marker-bearing text.
6
+ # Programmatic creation is native (NATIVE_ENTITY_REFS, binding >= 1.9.177).
7
+ #
8
+ # Lifecycle record: dissolves when parse-time entity reference retention
9
+ # ships — leptris#1904 (references as nodes or bulk positions).
10
+
3
11
  module Moxml
4
12
  module Adapter
5
13
  module CustomizedLeptris
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # A text run split out of a native text node that carried entity
4
+ # markers. Value object: the moxml contract exposes text nodes as
5
+ # separate children around entity references, while libleptris stores
6
+ # them as one marker-bearing text node.
7
+ #
8
+ # Lifecycle record: dissolves with entity reference retention —
9
+ # leptris#1904 (see customized_leptris/entity_reference.rb).
10
+
3
11
  module Moxml
4
12
  module Adapter
5
13
  module CustomizedLeptris
@@ -7,6 +7,11 @@ module Moxml
7
7
  # and document-level processing instructions.
8
8
  # The adapter stores them via NativeAttachment and serializes them
9
9
  # from #to_xml.
10
+ #
11
+ # Lifecycle record (#495): a tracked shim, not an adapter-fate
12
+ # case — the parse-time entity-retention gap is the last engine
13
+ # ask of the #275 mutation-completion round; when the engine ships
14
+ # native retention these classes dissolve into Base.
10
15
  module CustomizedLeptris
11
16
  autoload :Declaration, "moxml/adapter/customized_leptris/declaration"
12
17
  autoload :Doctype, "moxml/adapter/customized_leptris/doctype"
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Lifecycle record: the :libxml adapter is deprecated (#491); this wrapper dies with it.
4
+
3
5
  module Moxml
4
6
  module Adapter
5
7
  module CustomizedLibxml
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Lifecycle record: the :libxml adapter is deprecated (#491); this wrapper dies with it.
4
+
3
5
  module Moxml
4
6
  module Adapter
5
7
  module CustomizedLibxml
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Lifecycle record: the :libxml adapter is deprecated (#491); this wrapper dies with it.
4
+
3
5
  module Moxml
4
6
  module Adapter
5
7
  module CustomizedLibxml
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Lifecycle record: the :libxml adapter is deprecated (#491); this wrapper dies with it.
4
+
3
5
  module Moxml
4
6
  module Adapter
5
7
  module CustomizedLibxml
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Lifecycle record: the :libxml adapter is deprecated (#491); this wrapper dies with it.
4
+
3
5
  module Moxml
4
6
  module Adapter
5
7
  module CustomizedLibxml
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Lifecycle record: the :libxml adapter is deprecated (#491); this wrapper dies with it.
4
+
3
5
  module Moxml
4
6
  module Adapter
5
7
  module CustomizedLibxml
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Lifecycle record: the :libxml adapter is deprecated (#491); this wrapper dies with it.
4
+
3
5
  module Moxml
4
6
  module Adapter
5
7
  module CustomizedLibxml
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Lifecycle record: the :libxml adapter is deprecated (#491); this wrapper dies with it.
4
+
3
5
  module Moxml
4
6
  module Adapter
5
7
  module CustomizedLibxml
@@ -2,6 +2,9 @@
2
2
 
3
3
  module Moxml
4
4
  module Adapter
5
+ # Lifecycle record (#495): engine-gap shims for the :libxml
6
+ # adapter, deprecated in moxml#491 -- removed with it after the
7
+ # cycle. No separate upstream ask; the adapter's fate governs.
5
8
  module CustomizedLibxml
6
9
  autoload :Cdata, "moxml/adapter/customized_libxml/cdata"
7
10
  autoload :Comment, "moxml/adapter/customized_libxml/comment"
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Override Oga::EntityDecoder.decode for XML inputs.
4
+ #
5
+ # Engine gap (upstream oga): Oga 3.4's stock Oga::XML::Entities.decode
6
+ # runs multiple passes, turning the well-formed "&amp;#38;" into "&"
7
+ # rather than the correct "&#38;". Upstream tracker:
8
+ # https://github.com/yorickpeterse/oga — the override stays until oga
9
+ # fixes the decoder or the adapter is retired.
10
+
3
11
  require "oga"
4
12
 
5
13
  module Moxml
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Bypass Oga's lazy decoder for user-authored values.
4
+ #
5
+ # Engine gap (upstream oga): Oga::XML::Attribute#value and
6
+ # Oga::XML::Text#text decode @value/@text on first read via
7
+ # Oga::EntityDecoder; EntityDecoderOverride (this directory) must hold
8
+ # user-authored raw values. Upstream tracker:
9
+ # https://github.com/yorickpeterse/oga
10
+
3
11
  require "oga"
4
12
 
5
13
  module Moxml
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Stand-in XML declaration node.
4
+ #
5
+ # Engine gap (upstream oga): Oga::XML::XmlDeclaration is not writable —
6
+ # version/encoding/standalone cannot be set on an instance the way the
7
+ # facade contract requires, so this subclass owns the state and renders
8
+ # the declaration itself. Upstream tracker:
9
+ # https://github.com/yorickpeterse/oga
10
+
3
11
  require "oga"
4
12
 
5
13
  module Moxml
@@ -1,6 +1,6 @@
1
1
  # rubocop:disable Style/FrozenStringLiteralComment
2
2
 
3
- require "oga"
3
+ # Lifecycle record: engine gap (upstream oga); see the patched-class URL below.require "oga"
4
4
 
5
5
  # monkey patch the Oga generator because it's not configurable
6
6
  # https://github.com/yorickpeterse/oga/blob/main/lib/oga/xml/generator.rb
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Moxml
4
4
  module Adapter
5
+ # Lifecycle record (#495): every override serves the :oga adapter,
6
+ # deprecated in moxml#491 and deleted with the Opal surface in
7
+ # moxml#489 phase 4. No separate upstream ask -- the adapter's
8
+ # fate governs this file.
5
9
  module CustomizedOga
6
10
  autoload :EntityDecoderOverride, "moxml/adapter/customized_oga/entity_decoder"
7
11
  autoload :RawValueOverride, "moxml/adapter/customized_oga/raw_value_override"
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Engine gap (upstream ox): an ::Ox::Attribute carries no parent
4
+ # back-reference and no prefix slots, so the facade's attribute node
5
+ # contract needs this Node subclass. Upstream tracker:
6
+ # https://github.com/ohler55/ox
7
+
3
8
  module Moxml
4
9
  module Adapter
5
10
  module CustomizedOx
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Parse-path alias: ox expands the built-in entities at parse time and
4
+ # has no reference nodes, so parsed references are the facade's
5
+ # Moxml::Entity::Reference values. Lifecycle: the :ox adapter is
6
+ # deprecated (#491) — this record dies with it.
7
+
3
8
  module Moxml
4
9
  module Adapter
5
10
  module CustomizedOx
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Engine gap (upstream ox): ox has no Namespace node class — prefixes
4
+ # live as attribute spellings — so the facade's namespace node contract
5
+ # needs this value object. Upstream tracker:
6
+ # https://github.com/ohler55/ox
7
+
3
8
  module Moxml
4
9
  module Adapter
5
10
  module CustomizedOx
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Engine gap (upstream ox): Ox uses Strings for text content, but a
4
+ # String cannot carry a @parent back-reference; this ::Ox::Node subclass
5
+ # gives text nodes node identity. NOTE: an ::Ox::Node subclass that is
6
+ # neither ::Ox::Element nor ::Ox::Document changes Ox's dump behavior.
7
+ # Upstream tracker: https://github.com/ohler55/ox
8
+
3
9
  module Moxml
4
10
  module Adapter
5
11
  module CustomizedOx
@@ -2,6 +2,9 @@
2
2
 
3
3
  module Moxml
4
4
  module Adapter
5
+ # Lifecycle record (#495): engine-gap shims for the :ox adapter,
6
+ # deprecated in moxml#491 -- removed with it after the cycle.
7
+ # No separate upstream ask; the adapter's fate governs.
5
8
  module CustomizedOx
6
9
  autoload :Attribute, "moxml/adapter/customized_ox/attribute"
7
10
  autoload :EntityReference, "moxml/adapter/customized_ox/entity_reference"
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Parse-path alias: rexml expands the built-in entities at parse time,
4
+ # so parsed references are the facade's Moxml::Entity::Reference values.
5
+ # Lifecycle: the :rexml adapter is Opal-only and dies with the Opal
6
+ # surface (#489 phase 4); third-party engine, no upstream ask.
7
+
3
8
  module Moxml
4
9
  module Adapter
5
10
  module CustomizedRexml
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Custom REXML formatter that fixes indentation and wrapping issues.
4
+ # Engine gap (upstream rexml): REXML's Formatters::Pretty cannot be
5
+ # configured to the facade's whitespace contract. Third-party engine,
6
+ # no upstream ask; the :rexml adapter is Opal-only and dies with the
7
+ # Opal surface (#489 phase 4).
8
+
3
9
  require "rexml/formatters/pretty"
4
10
 
5
11
  module Moxml
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Moxml
4
4
  module Adapter
5
+ # Lifecycle record (#495): engine-gap shims for the :rexml
6
+ # adapter (Opal fallback; deleted with the Opal surface in
7
+ # moxml#489 phase 4). No separate upstream ask; the adapter's
8
+ # fate governs this file.
5
9
  module CustomizedRexml
6
10
  autoload :EntityReference,
7
11
  "moxml/adapter/customized_rexml/entity_reference"
data/lib/moxml/adapter.rb CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  module Moxml
4
4
  module Adapter
5
+ # Extension contract (#494): adding an adapter is exactly the trio
6
+ # below — nothing else, anywhere. The dispatch is open for
7
+ # extension and closed against edit: registration is data, the
8
+ # load path is generic, and spec/moxml/adapter_registry_spec.rb
9
+ # walks the registry mechanically so a skipped leg fails there.
10
+ #
11
+ # 1. one entry in AVAILABLE_ADAPTERS,
12
+ # 2. a requireable file at moxml/adapter/<name> (a
13
+ # `Customized<Name>` namespace module is optional),
14
+ # 3. a subclass of Adapter::Base in that file.
15
+ #
16
+ # Deprecated adapters live in DEPRECATED_ADAPTERS (still fully
17
+ # registered and loadable) until their removal milestone.
5
18
  autoload :Base, "moxml/adapter/base"
6
19
  autoload :CustomizedOga, "moxml/adapter/customized_oga"
7
20
  autoload :CustomizedOx, "moxml/adapter/customized_ox"
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.171"
4
+ VERSION = "0.5.172"
5
5
  end
@@ -255,6 +255,106 @@ module Moxml
255
255
  "xs:NOTATION" => ->(lexical) { qname?(collapsed(lexical)) },
256
256
  }.freeze
257
257
 
258
+ # The cross-language conformance dataset (#496): the model's
259
+ # builtin-lexical answers as data, pinned BY the Ruby suite
260
+ # (spec/moxml/xsd_conformance_cases_spec.rb) and exported to
261
+ # conformance/moxml-conformance.json by `rake conformance:export`
262
+ # — moxml-rs and moxml-ts run the same rows. Each row:
263
+ # [type, lexical, expected].
264
+ CONFORMANCE_CASES = [
265
+ # token family / collapse
266
+ ["xs:token", "a b", true],
267
+ ["xs:ID", " id ", true],
268
+ ["xs:NMTOKEN", "\tx ", true],
269
+ ["xs:ID", "1d", false],
270
+ # unbounded integers
271
+ ["xs:integer", "1" * 29, true],
272
+ ["xs:positiveInteger", "1" * 29, true],
273
+ ["xs:nonNegativeInteger", "1" * 29, true],
274
+ ["xs:negativeInteger", "-1#{'0' * 20}", true],
275
+ ["xs:nonPositiveInteger", "0", true],
276
+ ["xs:nonPositiveInteger", "-0", true],
277
+ # bounded integers
278
+ ["xs:long", "9223372036854775807", true],
279
+ ["xs:long", "9223372036854775808", false],
280
+ ["xs:unsignedLong", "18446744073709551615", true],
281
+ ["xs:unsignedLong", "18446744073709551616", false],
282
+ ["xs:byte", "128", false],
283
+ ["xs:unsignedByte", "256", false],
284
+ # float/decimal spellings
285
+ ["xs:float", "+INF", true],
286
+ ["xs:double", "-INF", true],
287
+ ["xs:float", "NaN", true],
288
+ ["xs:float", "1.5E-3", true],
289
+ ["xs:decimal", "5.", true],
290
+ ["xs:decimal", ".5", true],
291
+ ["xs:decimal", "1e3", false],
292
+ # calendar
293
+ ["xs:date", "2024-02-29", true],
294
+ ["xs:date", "1999-02-29", false],
295
+ ["xs:date", "2024-04-31", false],
296
+ ["xs:date", "2024-13-01", false],
297
+ ["xs:date", "not-a-date", false],
298
+ ["xs:dateTime", "2024-02-29T23:59:59Z", true],
299
+ ["xs:dateTime", "1999-02-29T00:00:00", false],
300
+ ["xs:dateTime", "not-a-date", false],
301
+ ["xs:dateTimeStamp", "2024-02-29T00:00:00Z", true],
302
+ ["xs:dateTimeStamp", "2024-02-29T00:00:00", false],
303
+ ["xs:time", "23:59:59.999+05:30", true],
304
+ ["xs:time", "24:00:00", false],
305
+ ["xs:time", "25:20:00", false],
306
+ ["xs:time", "13:20:60", false],
307
+ ["xs:gYearMonth", "2024-02Z", true],
308
+ ["xs:gYearMonth", "2024-13", false],
309
+ ["xs:gMonthDay", "--02-29", true],
310
+ ["xs:gMonthDay", "--02-30", false],
311
+ ["xs:gDay", "---31", true],
312
+ ["xs:gDay", "---32", false],
313
+ # duration
314
+ ["xs:duration", "P1Y2M3DT4H5M6.5S", true],
315
+ ["xs:duration", "PT0S", true],
316
+ ["xs:duration", "-P1D", true],
317
+ ["xs:duration", "P", false],
318
+ ["xs:duration", "PT", false],
319
+ ["xs:duration", "P1Y2MT", false],
320
+ ["xs:duration", "P200.5Y", false],
321
+ ["xs:dayTimeDuration", "P1DT2H", true],
322
+ ["xs:dayTimeDuration", "P1Y", false],
323
+ ["xs:yearMonthDuration", "P1Y2M", true],
324
+ ["xs:yearMonthDuration", "P1D", false],
325
+ ["xs:yearMonthDuration", "P1YT1H", false],
326
+ # binary
327
+ ["xs:base64Binary", "aGVsbG8=", true],
328
+ ["xs:base64Binary", "abcd", true],
329
+ ["xs:base64Binary", "abcde", false],
330
+ ["xs:base64Binary", "a b", false],
331
+ ["xs:hexBinary", "0A", true],
332
+ ["xs:hexBinary", "0AB", false],
333
+ # QName family
334
+ ["xs:QName", "foo", true],
335
+ ["xs:QName", "ns:foo", true],
336
+ ["xs:QName", "xmlns:xsi", false],
337
+ ["xs:QName", ":foo", false],
338
+ ["xs:QName", "a:b:c", false],
339
+ ["xs:NOTATION", "foo", true],
340
+ # lists: items, not characters
341
+ ["xs:IDREFS", "a b", true],
342
+ ["xs:IDREFS", "a", true],
343
+ ["xs:IDREFS", "", false],
344
+ ["xs:NMTOKENS", "a.b-c:d e", true],
345
+ ["xs:ENTITIES", "a 1", false],
346
+ # open lexical spaces
347
+ ["xs:string", "anything <>&", true],
348
+ ["xs:anyURI", "http://example.com/a b", true],
349
+ ["xs:boolean", "true", true],
350
+ ["xs:boolean", "0", true],
351
+ ["xs:boolean", "TRUE", false],
352
+ ["xs:language", "en-US", true],
353
+ ["xs:language", "en_US", false],
354
+ ["xs:Name", "a:b", true],
355
+ ["xs:NCName", "a:b", false],
356
+ ].map(&:freeze).freeze
357
+
258
358
  private_constant :CHECKERS
259
359
  end
260
360
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # #495: the Customized* adapter layer is either a removable-lifecycle
6
+ # record (an engine gap with a tracked upstream issue) or it dissolves
7
+ # into Base. This audit makes the lifecycle record checkable instead of
8
+ # folklore: every override file must cite its issue (an #NNN reference
9
+ # for our engines, or an upstream tracker URL for third-party engines
10
+ # we do not file against), and every Customized module must be wired
11
+ # into exactly its adapter (no dead records).
12
+ RSpec.describe "Customized adapter lifecycle audit" do
13
+ let(:override_files) do
14
+ Dir[File.expand_path("../../lib/moxml/adapter/customized_*/*.rb", __dir__)].sort
15
+ end
16
+
17
+ it "cites a tracked issue or upstream tracker in every override file" do
18
+ broken = override_files.reject do |file|
19
+ head = File.readlines(file).first(25).join
20
+ head.match?(/#\s*#\d{2,5}\b/) || head.include?("github.com/")
21
+ end
22
+
23
+ expect(broken).to be_empty, <<~MSG
24
+ Customized* override files without a lifecycle citation:
25
+ #{broken.join("\n")}
26
+ Each file's header must cite the open upstream issue that explains
27
+ why the override exists (#NNN for leptris; the upstream tracker URL
28
+ for third-party engines), or the #491 deprecation that retires it.
29
+ MSG
30
+ end
31
+
32
+ it "wires every Customized module into exactly its adapter" do
33
+ shims = Dir[File.expand_path("../../lib/moxml/adapter/customized_*.rb", __dir__)]
34
+
35
+ shims.each do |shim|
36
+ module_name = File.read(shim)[/module (Customized\w+)/, 1]
37
+ adapter_file = File.expand_path(
38
+ "../../lib/moxml/adapter/#{module_name.delete_prefix('Customized').downcase}.rb",
39
+ __dir__,
40
+ )
41
+
42
+ expect(File).to exist(adapter_file),
43
+ "#{module_name} has no #{adapter_file} to customize"
44
+
45
+ expect(File.read(adapter_file)).to include(module_name),
46
+ "#{module_name} is not wired into its adapter"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # The registry's extension contract (#494): adding an adapter is the
6
+ # documented trio — one AVAILABLE_ADAPTERS entry, one requireable
7
+ # adapter file, one Base subclass. The dispatch is open for extension:
8
+ # this spec walks the registry mechanically so a new adapter that
9
+ # skips any leg fails here, not in a consumer.
10
+ RSpec.describe "Adapter registry extension contract" do
11
+ it "loads every registered adapter through the public face" do
12
+ Moxml::Adapter::AVAILABLE_ADAPTERS.each do |name|
13
+ adapter = Moxml::Adapter.load(name)
14
+ expect(adapter).not_to be_nil
15
+ end
16
+ end
17
+
18
+ it "resolves every registered adapter to an Adapter::Base subclass" do
19
+ Moxml::Adapter::AVAILABLE_ADAPTERS.each do |name|
20
+ adapter = Moxml::Adapter.load(name)
21
+ expect(adapter).to be <= Moxml::Adapter::Base
22
+ end
23
+ end
24
+
25
+ it "answers available? truthfully for every registered adapter" do
26
+ Moxml::Adapter::AVAILABLE_ADAPTERS.each do |name|
27
+ expect(Moxml::Adapter.available?(name)).to be(true)
28
+ end
29
+ end
30
+
31
+ it "rejects unregistered names before any require runs" do
32
+ expect do
33
+ Moxml::Adapter.load(:definitely_not_an_adapter)
34
+ end.to raise_error(Moxml::AdapterError, /not available on this platform/)
35
+ end
36
+
37
+ it "keeps the deprecated adapters inside the registry with guidance" do
38
+ described = Moxml::Adapter::DEPRECATED_ADAPTERS
39
+ expect(described.keys).to all(satisfy { |name| Moxml::Adapter::AVAILABLE_ADAPTERS.include?(name) })
40
+ described.each_value do |guidance|
41
+ expect(guidance).to match(/:nokogiri|:leptris/)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # The customized_* layer's doctrine (#495): each namespace module is
6
+ # either a tracked shim (an engine gap with a stated resolution) or an
7
+ # adapter-fate case (dies with its deprecated adapter). A Customized*
8
+ # file exists only when its header says which — the record is
9
+ # checkable, not folklore.
10
+ RSpec.describe "customized adapter layer lifecycle" do
11
+ def customized_files
12
+ Dir["lib/moxml/adapter/customized_*.rb"]
13
+ end
14
+
15
+ it "has customized namespaces to govern" do
16
+ expect(customized_files).not_to be_empty
17
+ end
18
+
19
+ it "cites its lifecycle in every module header" do
20
+ customized_files.each do |path|
21
+ header = File.readlines(path).first(30).join
22
+ expect(header).to match(/#\d{3}/),
23
+ "#{path} carries no lifecycle citation (#495)"
24
+ expect(header).to match(/Lifecycle record|no native representation/),
25
+ "#{path} header does not state its lifecycle"
26
+ end
27
+ end
28
+
29
+ it "marks adapter-fate cases as deprecated or Opal-bound" do
30
+ adapter_fate = customized_files.reject { |p| p.include?("leptris") }
31
+ expect(adapter_fate).not_to be_empty
32
+ adapter_fate.each do |path|
33
+ header = File.readlines(path).first(30).join
34
+ expect(header).to match(/moxml#491|moxml#489/),
35
+ "#{path} is an adapter-fate case but cites neither " \
36
+ "its deprecation (#491) nor the Opal removal (#489)"
37
+ end
38
+ end
39
+
40
+ it "keeps the tracked shim pointed at its resolution" do
41
+ header = File.readlines("lib/moxml/adapter/customized_leptris.rb")
42
+ .first(30).join
43
+ expect(header).to include("tracked shim"), "the leptris shim must say so"
44
+ expect(header).to include("dissolve into Base"),
45
+ "the leptris shim must state its end state"
46
+ end
47
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Processing model §Validation (normative): pinned here.
4
+
5
+ require "spec_helper"
6
+
7
+ # The conformance dataset is the model's builtin-lexical answers AS
8
+ # DATA (#496): the Ruby suite pins every row here, and the exported
9
+ # JSON (rake conformance:export) is what moxml-rs and moxml-ts run.
10
+ # A row that fails here breaks the cross-language contract, not just
11
+ # this implementation.
12
+ RSpec.describe "builtin lexical conformance dataset" do
13
+ def valid_lexical?(type, lexical)
14
+ Moxml::XSD.builtin_valid?(Moxml.new(:leptris), type, lexical)
15
+ end
16
+
17
+ it "carries the dataset" do
18
+ expect(Moxml::XSD::BuiltinLexical::CONFORMANCE_CASES.size).to be >= 80
19
+ end
20
+
21
+ it "answers every row exactly as the dataset expects" do
22
+ Moxml::XSD::BuiltinLexical::CONFORMANCE_CASES.each do |type, lexical, expected|
23
+ answer = valid_lexical?(type, lexical)
24
+ expect(answer).to eql(expected),
25
+ "#{type} #{lexical.inspect}: got #{answer.inspect}, " \
26
+ "dataset expects #{expected.inspect}"
27
+ end
28
+ end
29
+
30
+ it "keeps every dataset type in the table" do
31
+ Moxml::XSD::BuiltinLexical::CONFORMANCE_CASES.each do |type, _lexical, _expected|
32
+ expect do
33
+ valid_lexical?(type, "")
34
+ end.not_to raise_error
35
+ end
36
+ end
37
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.171
4
+ version: 0.5.172
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -53,6 +53,7 @@ files:
53
53
  - bin/console
54
54
  - bin/generate-model-coverage
55
55
  - bin/setup
56
+ - conformance/moxml-conformance.json
56
57
  - data/w3c_entities.json
57
58
  - docs/ENTITY_SUPPORT_FOR_LUTAML_MODEL.md
58
59
  - docs/Gemfile
@@ -479,6 +480,7 @@ files:
479
480
  - spec/moxml/adapter/README.md
480
481
  - spec/moxml/adapter/base_spec.rb
481
482
  - spec/moxml/adapter/columnar_invalidation_spec.rb
483
+ - spec/moxml/adapter/customized_lifecycle_audit_spec.rb
482
484
  - spec/moxml/adapter/entity_restoration_spec.rb
483
485
  - spec/moxml/adapter/headed_ox_spec.rb
484
486
  - spec/moxml/adapter/leptris_adoption_spec.rb
@@ -513,6 +515,7 @@ files:
513
515
  - spec/moxml/adapter/shared_examples/.gitkeep
514
516
  - spec/moxml/adapter/shared_examples/adapter_contract.rb
515
517
  - spec/moxml/adapter_deprecation_spec.rb
518
+ - spec/moxml/adapter_registry_spec.rb
516
519
  - spec/moxml/adapter_spec.rb
517
520
  - spec/moxml/allocation_benchmark_spec.rb
518
521
  - spec/moxml/allocation_guard_spec.rb
@@ -538,6 +541,7 @@ files:
538
541
  - spec/moxml/config_spec.rb
539
542
  - spec/moxml/context_spec.rb
540
543
  - spec/moxml/css_spec.rb
544
+ - spec/moxml/customized_lifecycle_spec.rb
541
545
  - spec/moxml/cycle_rejection_spec.rb
542
546
  - spec/moxml/declaration_preservation_spec.rb
543
547
  - spec/moxml/declaration_spec.rb
@@ -634,6 +638,7 @@ files:
634
638
  - spec/moxml/xpath_capabilities_spec.rb
635
639
  - spec/moxml/xpath_spec.rb
636
640
  - spec/moxml/xsd_builtin_lexical_spec.rb
641
+ - spec/moxml/xsd_conformance_cases_spec.rb
637
642
  - spec/moxml/xsd_engine_meta_spec.rb
638
643
  - spec/moxml/xsd_error_contract_spec.rb
639
644
  - spec/moxml/xsd_handle_mismatch_spec.rb