moxml 0.5.112 → 0.5.114
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/.rubocop.yml +4 -0
- data/Rakefile +6 -1
- data/lib/moxml/adapter/leptris.rb +78 -13
- data/lib/moxml/element.rb +6 -3
- data/lib/moxml/version.rb +1 -1
- data/spec/integration/shared_examples/node_wrappers/element_behavior.rb +19 -0
- data/spec/moxml/adapter/leptris_adoption_spec.rb +46 -0
- data/spec/moxml/node_spec.rb +3 -2
- data/spec/moxml/opal_oga_adapter_spec.rb +12 -6
- data/spec/moxml/sax_entity_parity_spec.rb +5 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6cc0d0436680fb9c46169bc93e45197b20b71bf6970d737750e63fb110a5e75e
|
|
4
|
+
data.tar.gz: 8f71ca799b845b4fb28a977f6515ac1b702b6557259667c5b034f5b612ef6b67
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1edf604a33151f0079608d52231a1486a252cda307f49fd958e05f41e3229394a952245159939c2f4dac67ecba9f612ef6c4718d7f450c3260e1f78fc4d83135
|
|
7
|
+
data.tar.gz: 92aeae211e0b8abae0ce569c9dbd650f3b2b8426e975a42e3341317ebeb6c09928adbb0265b2ea7cdb726ca40d9ac376be1e537200d2166255fc30f365afd7ed
|
data/.rubocop.yml
CHANGED
|
@@ -23,6 +23,10 @@ AllCops:
|
|
|
23
23
|
# the zero-allocation streaming form (issue #143). One parameter per
|
|
24
24
|
# record key is the interface; a shorter list would need a wrapper
|
|
25
25
|
# object, which is the allocation the API exists to avoid.
|
|
26
|
+
Metrics/BlockLength:
|
|
27
|
+
Exclude:
|
|
28
|
+
- 'Rakefile'
|
|
29
|
+
|
|
26
30
|
Metrics/ParameterLists:
|
|
27
31
|
Exclude:
|
|
28
32
|
- 'lib/moxml/materializer.rb'
|
data/Rakefile
CHANGED
|
@@ -190,7 +190,12 @@ namespace :spec do
|
|
|
190
190
|
|
|
191
191
|
desc "Run unit tests only"
|
|
192
192
|
RSpec::Core::RakeTask.new(:unit) do |t|
|
|
193
|
-
|
|
193
|
+
# Unit specs live under spec/moxml/ — the stale glob matched
|
|
194
|
+
# nothing (spec/unit/ holds only the rexml_isolated_test.rb
|
|
195
|
+
# entity-era investigation artifact), so this leg ran zero
|
|
196
|
+
# examples and hid the moxml suite from the fast gate.
|
|
197
|
+
t.pattern = "spec/moxml/**/*_spec.rb"
|
|
198
|
+
t.exclude_pattern = "spec/moxml/adapter/**/*_spec.rb"
|
|
194
199
|
end
|
|
195
200
|
|
|
196
201
|
desc "Run adapter tests only"
|
|
@@ -1893,6 +1893,54 @@ module Moxml
|
|
|
1893
1893
|
installed
|
|
1894
1894
|
end
|
|
1895
1895
|
|
|
1896
|
+
# moxml#335: the engine's document resolution can mis-report
|
|
1897
|
+
# pool ownership under spec-order state (the #1242 TLS-memo
|
|
1898
|
+
# family), so a plain add can -1 for ANY node kind — not just
|
|
1899
|
+
# elements and text. Elements rebuild structurally (the #376
|
|
1900
|
+
# contract); every other kind carries no cross-document
|
|
1901
|
+
# identity worth preserving, so reconstruct it in the parent's
|
|
1902
|
+
# document through the binding's create faces.
|
|
1903
|
+
def contains_foreign_child?(parent_nn, children)
|
|
1904
|
+
parent_doc = NN_DOCUMENT.bind_call(parent_nn)
|
|
1905
|
+
return false if parent_doc.nil?
|
|
1906
|
+
|
|
1907
|
+
children.any? do |child|
|
|
1908
|
+
child_doc = NN_DOCUMENT.bind_call(child)
|
|
1909
|
+
child_doc && !child_doc.equal?(parent_doc)
|
|
1910
|
+
end
|
|
1911
|
+
end
|
|
1912
|
+
|
|
1913
|
+
def rebuild_foreign_child(parent_nn, child_nn)
|
|
1914
|
+
doc = NN_DOCUMENT.bind_call(parent_nn)
|
|
1915
|
+
rebuilt =
|
|
1916
|
+
case child_nn.node_type
|
|
1917
|
+
when :element
|
|
1918
|
+
return structural_adopt(parent_nn, child_nn)
|
|
1919
|
+
when :text
|
|
1920
|
+
doc.create_text_node(child_nn.text)
|
|
1921
|
+
when :comment
|
|
1922
|
+
doc.create_comment(child_nn.text)
|
|
1923
|
+
when :cdata
|
|
1924
|
+
doc.create_cdata(child_nn.text)
|
|
1925
|
+
when :processing_instruction, :pi
|
|
1926
|
+
doc.create_processing_instruction(
|
|
1927
|
+
child_nn.target, child_nn.content.to_s
|
|
1928
|
+
)
|
|
1929
|
+
else
|
|
1930
|
+
raise Moxml::DocumentStructureError.new(
|
|
1931
|
+
"cannot rebuild a #{child_nn.node_type} node",
|
|
1932
|
+
)
|
|
1933
|
+
end
|
|
1934
|
+
# The raw engine add rejects binding-family children —
|
|
1935
|
+
# append through the FFI face like structural_adopt.
|
|
1936
|
+
::Leptris::XML::FFI.check_status(
|
|
1937
|
+
::Leptris::XML::FFI.leptris_element_append_child(
|
|
1938
|
+
::FFI::Pointer.new(parent_nn.address), rebuilt.c_ptr
|
|
1939
|
+
),
|
|
1940
|
+
)
|
|
1941
|
+
rebuilt
|
|
1942
|
+
end
|
|
1943
|
+
|
|
1896
1944
|
def copy_binding_subtree(doc, src)
|
|
1897
1945
|
sub = doc.create_element(src.name.to_s)
|
|
1898
1946
|
(src.respond_to?(:attributes) ? src.attributes : {}).each do |k, v|
|
|
@@ -2107,24 +2155,15 @@ module Moxml
|
|
|
2107
2155
|
child = doc.create_text_node(child) if child.is_a?(String)
|
|
2108
2156
|
# The engine's document resolution can mis-report pool
|
|
2109
2157
|
# ownership under spec-order state (the #1242 TLS-memo
|
|
2110
|
-
# family) — an -1 rejection
|
|
2111
|
-
#
|
|
2158
|
+
# family) — an -1 rejection rebuilds the child in the
|
|
2159
|
+
# parent's document for every reconstructible kind
|
|
2160
|
+
# (moxml#335), rather than raising through the flow.
|
|
2112
2161
|
begin
|
|
2113
2162
|
return NN_ADD_CHILD.bind_call(parent, child)
|
|
2114
2163
|
rescue ::Leptris::XML::Error, ::RuntimeError => e
|
|
2115
2164
|
raise unless e.message.include?("append_child failed")
|
|
2116
|
-
raise unless %i[element text].include?(child.node_type)
|
|
2117
2165
|
|
|
2118
|
-
|
|
2119
|
-
structural_adopt(parent, child)
|
|
2120
|
-
else
|
|
2121
|
-
# A text node carries no cross-document identity worth
|
|
2122
|
-
# preserving: rebuild it in the parent's document
|
|
2123
|
-
NN_ADD_CHILD.bind_call(
|
|
2124
|
-
parent,
|
|
2125
|
-
doc.create_text_node(child.text),
|
|
2126
|
-
)
|
|
2127
|
-
end
|
|
2166
|
+
rebuild_foreign_child(parent, child)
|
|
2128
2167
|
end
|
|
2129
2168
|
end
|
|
2130
2169
|
|
|
@@ -2319,6 +2358,32 @@ module Moxml
|
|
|
2319
2358
|
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.290.0")
|
|
2320
2359
|
|
|
2321
2360
|
def replace_children(node, new_children)
|
|
2361
|
+
# Cross-pool children: the binding's bulk face adopts per
|
|
2362
|
+
# node and fails on mixed kinds (inner_xml= with
|
|
2363
|
+
# text+elements died with Memory allocation failed —
|
|
2364
|
+
# pre-existing). Rebuild foreign children in this pool
|
|
2365
|
+
# after the clear — fragment children carry no identity
|
|
2366
|
+
# worth preserving; same-document children keep the plain
|
|
2367
|
+
# add, in order.
|
|
2368
|
+
if NATIVE_MUTATIONS_COHERENT && node.is_a?(NN) &&
|
|
2369
|
+
new_children.all?(NN) &&
|
|
2370
|
+
contains_foreign_child?(node, new_children)
|
|
2371
|
+
::Leptris::XML::FFI.check_status(
|
|
2372
|
+
::Leptris::XML::FFI.leptris_element_remove_children(
|
|
2373
|
+
::FFI::Pointer.new(node.address),
|
|
2374
|
+
),
|
|
2375
|
+
)
|
|
2376
|
+
node_doc = NN_DOCUMENT.bind_call(node)
|
|
2377
|
+
new_children.each do |child|
|
|
2378
|
+
child_doc = NN_DOCUMENT.bind_call(child)
|
|
2379
|
+
if child_doc && !child_doc.equal?(node_doc)
|
|
2380
|
+
rebuild_foreign_child(node, child)
|
|
2381
|
+
else
|
|
2382
|
+
NN_ADD_CHILD.bind_call(node, child)
|
|
2383
|
+
end
|
|
2384
|
+
end
|
|
2385
|
+
return node
|
|
2386
|
+
end
|
|
2322
2387
|
if NATIVE_READ_LAYER
|
|
2323
2388
|
node = to_binding(node)
|
|
2324
2389
|
new_children = new_children.map { |child| to_binding(child) }
|
data/lib/moxml/element.rb
CHANGED
|
@@ -400,14 +400,17 @@ module Moxml
|
|
|
400
400
|
end
|
|
401
401
|
|
|
402
402
|
def inner_xml=(xml)
|
|
403
|
-
wrapper
|
|
404
|
-
|
|
403
|
+
# Fragment parse, not a wrapper element around interpolated
|
|
404
|
+
# input (TODO.refactor/03): the fragment face is the same one
|
|
405
|
+
# add_child(String) uses, so semantics match and no wrapper
|
|
406
|
+
# name can collide with content.
|
|
407
|
+
nodes = context.parse_fragment(xml).to_a
|
|
405
408
|
@text = nil
|
|
406
409
|
# Whole-list replacement rides the children generation like
|
|
407
410
|
# children= does (moxml#310) — text and elements memos across
|
|
408
411
|
# wrappers recompute on next read.
|
|
409
412
|
context.bump_children_generation
|
|
410
|
-
adapter.replace_children(@native,
|
|
413
|
+
adapter.replace_children(@native, nodes.map(&:native))
|
|
411
414
|
invalidate_children_cache!
|
|
412
415
|
end
|
|
413
416
|
|
data/lib/moxml/version.rb
CHANGED
|
@@ -89,6 +89,25 @@ RSpec.shared_examples "Moxml::Element" do
|
|
|
89
89
|
expect(element.children.first.name).to eq("child")
|
|
90
90
|
expect(element.children.first.text).to eq("text")
|
|
91
91
|
end
|
|
92
|
+
|
|
93
|
+
it "sets inner XML with mixed top-level nodes" do
|
|
94
|
+
# Structure, not serialization spelling: Ox renders bare
|
|
95
|
+
# text as <s> nodes and rexml/oga expand empty tags.
|
|
96
|
+
element.inner_xml = "a<c1/>b<c2 v='1'/>c"
|
|
97
|
+
kids = element.children
|
|
98
|
+
expect(
|
|
99
|
+
kids.map { |c| c.element? ? :element : :content },
|
|
100
|
+
).to eq(%i[content element content element content])
|
|
101
|
+
expect(kids[0].text).to eq("a")
|
|
102
|
+
expect(kids[2].text).to eq("b")
|
|
103
|
+
expect(kids[4].text).to eq("c")
|
|
104
|
+
expect(kids[3]["v"]).to eq("1")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "sets inner XML containing element-like text and entities" do
|
|
108
|
+
element.inner_xml = "<c></element> & A</c>"
|
|
109
|
+
expect(element.children.first.text).to eq("</element> & A")
|
|
110
|
+
end
|
|
92
111
|
end
|
|
93
112
|
|
|
94
113
|
describe "node manipulation" do
|
|
@@ -10,6 +10,7 @@ require "spec_helper"
|
|
|
10
10
|
# names, doc-owned).
|
|
11
11
|
RSpec.describe "leptris cross-document adoption" do
|
|
12
12
|
let(:ctx) { Moxml.new(:leptris) }
|
|
13
|
+
let(:ad) { Moxml::Adapter::Leptris }
|
|
13
14
|
|
|
14
15
|
it "add_child lands the adopted copy in the tree" do
|
|
15
16
|
doc = ctx.parse("<r><a/></r>")
|
|
@@ -100,4 +101,49 @@ RSpec.describe "leptris cross-document adoption" do
|
|
|
100
101
|
expect(mid.root.to_xml).not_to include("<deep")
|
|
101
102
|
end
|
|
102
103
|
end
|
|
104
|
+
|
|
105
|
+
# moxml#335: the -1 fallback must not re-raise by node kind — the
|
|
106
|
+
# engine's document resolution can mis-report pool ownership for
|
|
107
|
+
# ANY child (the #1242 TLS-memo family), and only elements have a
|
|
108
|
+
# cross-document identity worth a structural rebuild. The helper
|
|
109
|
+
# is specced directly: the engine failure is CI-only, so forcing
|
|
110
|
+
# the -1 locally is not reproducible.
|
|
111
|
+
context "when the add_child -1 rebuild fallback fires" do
|
|
112
|
+
it "rebuilds comments in the parent's document" do
|
|
113
|
+
doc = ctx.parse("<r/>")
|
|
114
|
+
other = ctx.parse("<other><!-- note --></other>")
|
|
115
|
+
ad.rebuild_foreign_child(doc.root.native, other.root.children.first.native)
|
|
116
|
+
expect(doc.root.to_xml).to include("<!-- note -->")
|
|
117
|
+
expect(other.root.to_xml).to include("<!-- note -->")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "rebuilds CDATA in the parent's document" do
|
|
121
|
+
doc = ctx.parse("<r/>")
|
|
122
|
+
other = ctx.parse("<other><![CDATA[x < y]]></other>")
|
|
123
|
+
ad.rebuild_foreign_child(doc.root.native, other.root.children.first.native)
|
|
124
|
+
expect(doc.root.to_xml).to include("<![CDATA[x < y]]>")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "rebuilds processing instructions in the parent's document" do
|
|
128
|
+
doc = ctx.parse("<r/>")
|
|
129
|
+
other = ctx.parse("<other><?tgt data='1'?></other>")
|
|
130
|
+
ad.rebuild_foreign_child(doc.root.native, other.root.children.first.native)
|
|
131
|
+
expect(doc.root.to_xml).to include("<?tgt data='1'?>")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "rebuilds text in the parent's document" do
|
|
135
|
+
doc = ctx.parse("<r/>")
|
|
136
|
+
other = ctx.parse("<other>t</other>")
|
|
137
|
+
ad.rebuild_foreign_child(doc.root.native, other.root.children.first.native)
|
|
138
|
+
expect(doc.root.to_xml).to include(">t<")
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "rebuilds elements structurally" do
|
|
142
|
+
doc = ctx.parse("<r/>")
|
|
143
|
+
other = ctx.parse("<other><e k='v'>t</e></other>")
|
|
144
|
+
ad.rebuild_foreign_child(doc.root.native, other.root.children.first.native)
|
|
145
|
+
expect(doc.at("//r/e")["k"]).to eq("v")
|
|
146
|
+
expect(doc.at("//r/e").text).to eq("t")
|
|
147
|
+
end
|
|
148
|
+
end
|
|
103
149
|
end
|
data/spec/moxml/node_spec.rb
CHANGED
|
@@ -168,9 +168,10 @@ RSpec.describe Moxml::Node do
|
|
|
168
168
|
end
|
|
169
169
|
end
|
|
170
170
|
|
|
171
|
-
it "
|
|
171
|
+
it "locates non-element nodes through typed node tests" do
|
|
172
172
|
text = doc.root.children.first.children.first
|
|
173
|
-
expect
|
|
173
|
+
expect(text.path).to end_with("/text()")
|
|
174
|
+
expect(doc.at_xpath(text.path)).to eq(text)
|
|
174
175
|
end
|
|
175
176
|
end
|
|
176
177
|
|
|
@@ -3,12 +3,18 @@
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
require "moxml/adapter/shared_examples/adapter_contract"
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
# The describe resolves the adapter constant at load time — under
|
|
7
|
+
# MRI nothing loads the Opal compat shim, so the whole describe must
|
|
8
|
+
# sit behind the engine check (the `if:` metadata alone still
|
|
9
|
+
# evaluates the constant reference).
|
|
10
|
+
if RUBY_ENGINE == "opal"
|
|
11
|
+
RSpec.describe Moxml::Adapter::Oga do
|
|
12
|
+
around do |example|
|
|
13
|
+
Moxml.with_config(:oga, true, "UTF-8") do
|
|
14
|
+
example.run
|
|
15
|
+
end
|
|
10
16
|
end
|
|
11
|
-
end
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
it_behaves_like "xml adapter"
|
|
19
|
+
end
|
|
14
20
|
end
|
|
@@ -164,7 +164,11 @@ RSpec.describe "SAX/DOM entity parity" do
|
|
|
164
164
|
# double-escaping on round-trip.
|
|
165
165
|
doc = ctx.create_document
|
|
166
166
|
el = doc.create_element("doc")
|
|
167
|
-
|
|
167
|
+
# Programmatically-built: a literal Text node, no markup parse.
|
|
168
|
+
# (add_child with a String operand is MARKUP since the nokogiri
|
|
169
|
+
# compat round — a bare "&" is ill-formed there and each
|
|
170
|
+
# engine recovers differently.)
|
|
171
|
+
el.add_child(doc.create_text("a & b"))
|
|
168
172
|
doc.add_child(el)
|
|
169
173
|
|
|
170
174
|
serialized = doc.to_xml.sub(/\A<\?[^>]*\?>\s*/, "").strip
|
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.
|
|
4
|
+
version: 0.5.114
|
|
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-
|
|
11
|
+
date: 2026-10-07 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
|