moxml 0.5.119 → 0.5.121

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a30730e871854c7b88fc3b6a3498b4f4d5504ac47caa2aabcc530f2bf0eec96
4
- data.tar.gz: 8d4f301d7d100cab15cc55b4f0e3db36bc5e50916775adfd34f64b816546d466
3
+ metadata.gz: 3e70385e5a2ffc34bc69d65011c6fead0dfdb3311ac44d68893990b714aa750b
4
+ data.tar.gz: 1d7d88feac80fdef1a56a5e09d333ee350bd9e2cc19c42a26e57a5283e8ab8ef
5
5
  SHA512:
6
- metadata.gz: 4b61a839f7399e0b9e9236b1d921cfc161714b114d73da1b737a80c98795d8c88b96f8e85e71c3a71f58a090578b2ba6ffa4ec57cb9a6309cb53296bb000b277
7
- data.tar.gz: b42fa624031b28da813039d441024be7766f8322fc54f247075b0c205cb79ec57052ababc789ab90f1c3b91b04a0ced4de85760af3178c1675aedd390fdd8046
6
+ metadata.gz: 846a93feb34b6de7dab678e91b72cfe8691b17fab1987605906919225ce406204b4beb2dbae3d8e95b54bdf42f1ec9895598c22e49d3816d866241f5cb6e0fad
7
+ data.tar.gz: 915721b24c27eef6790fa31f22f82dd1f6b3d2998b9c5824c4515a50f4d0500ff2bd40874b414f8a02dce433d90455eda067dd07f35ad127df46182dac8133a1
@@ -143,10 +143,10 @@ class MoxmlBenchmarkReport
143
143
  end
144
144
 
145
145
  def benchmark_parse(context, xml)
146
- result = nil
146
+ nil
147
147
  Benchmark.ips do |x|
148
148
  x.config(time: 2, warmup: 1)
149
- x.report("parse") { result = context.parse(xml) }
149
+ x.report("parse") { context.parse(xml) }
150
150
  end.entries.first.ips.round(2)
151
151
  rescue StandardError => e
152
152
  @errors[context.config.adapter_name] << "Parse error: #{e.message}"
@@ -272,6 +272,30 @@ module Moxml
272
272
  "XSD validation requires an adapter with an XSD engine"
273
273
  end
274
274
 
275
+ def xsd_compile_file(_path)
276
+ raise Moxml::NotImplementedError,
277
+ "XSD validation requires an adapter with an XSD engine"
278
+ end
279
+
280
+ def xsd_builtin_valid?(_builtin, _lexical)
281
+ raise Moxml::NotImplementedError,
282
+ "XSD validation requires an adapter with an XSD engine"
283
+ end
284
+
285
+ def xsd_declarations(_xsd_handle); end
286
+
287
+ def xsd_compile_error(_xsd_handle); end
288
+
289
+ def xsd_simple_valid?(_xsd_handle, _type_name, _lexical)
290
+ raise Moxml::NotImplementedError,
291
+ "XSD validation requires an adapter with an XSD engine"
292
+ end
293
+
294
+ def xsd_content_valid?(_xsd_handle, _element_name, _children)
295
+ raise Moxml::NotImplementedError,
296
+ "XSD validation requires an adapter with an XSD engine"
297
+ end
298
+
275
299
  # Apply a compiled stylesheet; returns the engine's result
276
300
  # document, or nil when the output method cannot produce one
277
301
  # (text). @params is the flat quoted name/value list.
@@ -239,6 +239,44 @@ module Moxml
239
239
  ::Leptris::XML::XSD.compile(schema_source)
240
240
  end
241
241
 
242
+ def xsd_compile_file(path)
243
+ unless XSD_SUPPORTED && ::Leptris::XML::XSD.respond_to?(:compile_file)
244
+ raise Moxml::NotImplementedError,
245
+ "XSD validation requires the leptris 1.9.326+ engine"
246
+ end
247
+
248
+ ::Leptris::XML::XSD.compile_file(path)
249
+ end
250
+
251
+ def xsd_builtin_valid?(builtin, lexical)
252
+ unless XSD_SUPPORTED
253
+ raise Moxml::NotImplementedError,
254
+ "XSD validation requires an adapter with an XSD engine"
255
+ end
256
+
257
+ ::Leptris::XML::XSD.builtin_valid?(builtin, lexical)
258
+ end
259
+
260
+ def xsd_declarations(xsd_handle)
261
+ xsd_handle.declaration_count
262
+ end
263
+
264
+ def xsd_compile_error(xsd_handle)
265
+ xsd_handle.error
266
+ end
267
+
268
+ def xsd_simple_valid?(xsd_handle, type_name, lexical)
269
+ xsd_handle.simple_valid?(type_name, lexical)
270
+ end
271
+
272
+ def xsd_content_valid?(xsd_handle, element_name, children)
273
+ # The binding reads each child's name + effective
274
+ # namespace through its own node API — pass the natives.
275
+ xsd_handle.content_valid?(
276
+ element_name, children.map(&:native)
277
+ )
278
+ end
279
+
242
280
  def xsd_validate(xsd_handle, native_document)
243
281
  # +xsd_handle+ is the binding's Leptris::XML::XSD::Schema;
244
282
  # validate_errors takes the binding document and returns
@@ -2220,11 +2258,22 @@ module Moxml
2220
2258
  # absorbed-source nodes upstream.
2221
2259
  child_doc = NN_DOCUMENT.bind_call(child)
2222
2260
  parent_doc = NN_DOCUMENT.bind_call(parent)
2223
- if child_doc && parent_doc &&
2224
- !child_doc.equal?(parent_doc) && child.node_type == :element
2225
- absorb_source_document(to_binding(parent_doc), to_binding(child_doc))
2226
- return structural_adopt(parent, child) unless
2227
- source_absorbed_into?(parent, child)
2261
+ if child_doc && parent_doc && !child_doc.equal?(parent_doc)
2262
+ if child.node_type == :element
2263
+ absorb_source_document(to_binding(parent_doc), to_binding(child_doc))
2264
+ return structural_adopt(parent, child) unless
2265
+ source_absorbed_into?(parent, child)
2266
+ else
2267
+ # moxml#347: the C add_child's foreign-child
2268
+ # fallback dispatches parent.add_child through
2269
+ # RUBY — on Identity natives the contract modules
2270
+ # shadow the C method, so that dispatch re-enters
2271
+ # Moxml's add_child forever. Foreign content kinds
2272
+ # (text/comment/CDATA/PI) rebuild in the parent's
2273
+ # pool directly; they carry no cross-document
2274
+ # identity worth a move.
2275
+ return rebuild_foreign_child(parent, child)
2276
+ end
2228
2277
  end
2229
2278
 
2230
2279
  doc = NN_DOCUMENT.bind_call(parent)
data/lib/moxml/context.rb CHANGED
@@ -156,6 +156,14 @@ module Moxml
156
156
  Moxml::XSD::Schema.new(handle, self)
157
157
  end
158
158
 
159
+ # Compile an XSD schema from a file path — <xs:include href>
160
+ # resolves relative to the file's directory (the engine
161
+ # resolves it; reading the file into #xsd would lose that).
162
+ def xsd_file(path)
163
+ handle = config.adapter.xsd_compile_file(path.to_s)
164
+ Moxml::XSD::Schema.new(handle, self)
165
+ end
166
+
159
167
  def xslt(stylesheet)
160
168
  source = case stylesheet
161
169
  when Moxml::Document then stylesheet.to_xml
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.119"
4
+ VERSION = "0.5.121"
5
5
  end
data/lib/moxml/xsd.rb CHANGED
@@ -38,6 +38,14 @@ module Moxml
38
38
  adapter.const_defined?(:XSD_SUPPORTED) &&
39
39
  adapter.const_get(:XSD_SUPPORTED)
40
40
  end
41
+
42
+ # Lexical validation against the built-in type table (the
43
+ # "xs:NAME" reference spelling). Raises ArgumentError when the
44
+ # name is not in the table; NotImplementedError on adapters
45
+ # without an engine.
46
+ def builtin_valid?(context, builtin, lexical)
47
+ context.config.adapter.xsd_builtin_valid?(builtin, lexical)
48
+ end
41
49
  end
42
50
 
43
51
  # A compiled schema (adapter-owned handle). Validation results
@@ -61,6 +69,35 @@ module Moxml
61
69
  def valid?(node_or_xml)
62
70
  validate(node_or_xml).empty?
63
71
  end
72
+
73
+ # Number of top-level schema declarations the compiler
74
+ # recognized.
75
+ def declarations
76
+ @context.config.adapter.xsd_declarations(@handle)
77
+ end
78
+
79
+ # The engine's schema-level compile error, or nil when the
80
+ # schema compiled cleanly.
81
+ def compile_error
82
+ @context.config.adapter.xsd_compile_error(@handle)
83
+ end
84
+
85
+ # Lexical validation of +lexical+ against the schema's user
86
+ # simpleType +type_name+. Unknown type names raise.
87
+ def simple_valid?(type_name, lexical)
88
+ @context.config.adapter.xsd_simple_valid?(
89
+ @handle, type_name, lexical
90
+ )
91
+ end
92
+
93
+ # Content-model check for one element's children. +children+
94
+ # are Moxml nodes (names + effective namespaces are read
95
+ # engine-side). Unknown element names raise.
96
+ def content_valid?(element_name, children)
97
+ @context.config.adapter.xsd_content_valid?(
98
+ @handle, element_name, children
99
+ )
100
+ end
64
101
  end
65
102
  end
66
103
  end
@@ -146,4 +146,46 @@ RSpec.describe "leptris cross-document adoption" do
146
146
  expect(doc.at("//r/e").text).to eq("t")
147
147
  end
148
148
  end
149
+
150
+ # moxml#347: the C add_child's foreign-child fallback dispatches
151
+ # parent.add_child through Ruby — on Identity natives the contract
152
+ # modules shadow the C method and the dispatch re-enters Moxml's
153
+ # add_child (SystemStackError). Foreign content kinds rebuild in
154
+ # the parent's pool; foreign elements absorb/move.
155
+ context "when adding foreign children (moxml#347)" do
156
+ let(:fragment_xml) do
157
+ "<__root__>hello<b attr=\"1\"/>world<!-- note --><?tgt data?><![CDATA[x < y]]></__root__>"
158
+ end
159
+
160
+ it "appends every node kind from a same-context fragment" do
161
+ doc = ctx.parse("<bibitem/>")
162
+ frag = ctx.parse(fragment_xml)
163
+ frag.root.children.each { |c| doc.root.add_child(c) }
164
+ expect(doc.root.to_xml).to include("<b attr=\"1\">")
165
+ expect(doc.root.text).to include("hello")
166
+ expect(doc.root.to_xml).to include("<!-- note -->")
167
+ expect(doc.root.to_xml).to include("<?tgt data?>")
168
+ expect(doc.root.to_xml).to include("<![CDATA[x < y]]>")
169
+ end
170
+
171
+ it "appends every node kind across contexts without recursing" do
172
+ doc = ctx.parse("<bibitem/>")
173
+ frag = Moxml.new(:leptris).parse(fragment_xml)
174
+ frag.root.children.each { |c| doc.root.add_child(c) }
175
+ expect(doc.root.to_xml).to include("<b attr=\"1\">")
176
+ expect(doc.root.to_xml).to include("<!-- note -->")
177
+ expect(doc.root.to_xml).to include("<?tgt data?>")
178
+ expect(doc.root.to_xml).to include("<![CDATA[x < y]]>")
179
+ end
180
+
181
+ it "moves foreign elements by reference (the absorb path stays first)" do
182
+ doc = ctx.parse("<r><a/></r>")
183
+ other = Moxml.new(:leptris).parse("<other><new/></other>")
184
+ child = other.root.children.first
185
+ doc.root.add_child(child)
186
+ expect(doc.root.children.last).to eq(child)
187
+ expect(child["probe"] = "1").to eq("1")
188
+ expect(doc.at("//r/new")["probe"]).to eq("1")
189
+ end
190
+ end
149
191
  end
@@ -14,9 +14,12 @@ RSpec.describe Moxml::XSD do
14
14
  <xs:element name="note" type="t_note"/>
15
15
  <xs:complexType name="t_note">
16
16
  <xs:sequence>
17
- <xs:element name="to" type="xs:string"/>
17
+ <xs:element name="to" type="t_string"/>
18
18
  </xs:sequence>
19
19
  </xs:complexType>
20
+ <xs:simpleType name="t_string">
21
+ <xs:restriction base="xs:string"/>
22
+ </xs:simpleType>
20
23
  </xs:schema>
21
24
  XSD
22
25
  end
@@ -55,6 +58,49 @@ RSpec.describe Moxml::XSD do
55
58
  expect(schema.valid?("<note><wrong/></note>")).to be(false)
56
59
  end
57
60
 
61
+ it "surfaces the lexical, census, and content faces" do
62
+ schema = ctx.xsd(schema_text)
63
+ expect(schema.declarations).to be >= 2
64
+ expect(schema.compile_error).to be_nil
65
+ expect(schema.simple_valid?("t_string", "abc")).to be(true)
66
+ end
67
+
68
+ it "checks built-in lexical forms" do
69
+ expect(described_class.builtin_valid?(ctx, "xs:integer", "3")).to be(true)
70
+ expect(described_class.builtin_valid?(ctx, "xs:integer", "x")).to be(false)
71
+ expect do
72
+ described_class.builtin_valid?(ctx, "xs:nope", "3")
73
+ end.to raise_error(ArgumentError)
74
+ end
75
+
76
+ it "checks content models through moxml nodes" do
77
+ schema = ctx.xsd(schema_text)
78
+ doc = ctx.parse("<note><to>You</to></note>")
79
+ expect(schema.content_valid?("note", [doc.at("//to")])).to be(true)
80
+ expect(schema.content_valid?("note", [])).to be(false)
81
+ end
82
+
83
+ it "compiles from a file with relative includes" do
84
+ require "tmpdir"
85
+ Dir.mktmpdir do |dir|
86
+ File.write(File.join(dir, "main.xsd"), <<~XSD)
87
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
88
+ <xs:include schemaLocation="inc.xsd"/>
89
+ </xs:schema>
90
+ XSD
91
+ File.write(File.join(dir, "inc.xsd"), <<~XSD)
92
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
93
+ <xs:element name="note" type="xs:string"/>
94
+ </xs:schema>
95
+ XSD
96
+ schema = ctx.xsd_file(File.join(dir, "main.xsd"))
97
+ # The include pulled the declaration in (both files' schema
98
+ # elements census).
99
+ expect(schema.declarations).to be >= 1
100
+ expect(schema.valid?("<note>x</note>")).to be(true)
101
+ end
102
+ end
103
+
58
104
  it "derives XSD_SUPPORTED from the engine face gate" do
59
105
  # The capability flag must not outrun the engine faces: a
60
106
  # misreleased lockstep (NATIVE_PLAN_STRUCTS lesson) would arm
@@ -46,10 +46,10 @@ RSpec.shared_examples "Performance Examples" do
46
46
  end
47
47
 
48
48
  it "meets Parser performance threshold", :performance do
49
- result = nil
49
+ nil
50
50
  report = Benchmark.ips do |x|
51
51
  x.config(time: 5, warmup: 2)
52
- x.report("Parser") { result = context.parse(large_xml) }
52
+ x.report("Parser") { context.parse(large_xml) }
53
53
  end
54
54
 
55
55
  threshold = thresholds.dig(context.config.adapter_name, :parser) || 1
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.119
4
+ version: 0.5.121
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-08 00:00:00.000000000 Z
11
+ date: 2026-10-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Moxml is a unified XML manipulation library that provides a common API