moxml 0.5.56 → 0.5.58
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/moxml/adapter/leptris.rb +80 -3
- data/lib/moxml/context.rb +5 -2
- data/lib/moxml/node.rb +8 -2
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter/leptris_spec.rb +61 -0
- 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: 03fd3940f1334b3f417e4847f35e87497581404372209de4426819a8acc6d363
|
|
4
|
+
data.tar.gz: 805f6966495456ff6e36c4f1be3df9fcbbf1c89cfcf2ddc6a4153c37e1bcec71
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 872ba59e6f8b72779d2ea43c8af201052a69be9d75ad1add3fa2618bc58e81cee0503ce352d06c8b46c64cc6340859006b36afeadef4302f29c81e2760285baa
|
|
7
|
+
data.tar.gz: 01eed98fc6ac76efb35d2456c9af57487bbdddf13092a09b0667d11e6e2d48a1905865046aab6a59ca8a89523fc4337a705c1b1cf95d98342d56cb4494c0d91e
|
|
@@ -246,6 +246,31 @@ module Moxml
|
|
|
246
246
|
NATIVE_MUTATIONS_COHERENT &&
|
|
247
247
|
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.174.4")
|
|
248
248
|
|
|
249
|
+
# leptris-ruby#246 klass-propagating reads: the read
|
|
250
|
+
# family (children/element_children/next_sibling/parent)
|
|
251
|
+
# mints the mapped subclass per kind, so children arrive
|
|
252
|
+
# already carrying the contract — wrap_native's is_a?
|
|
253
|
+
# guard short-circuits and the per-visit from() mint
|
|
254
|
+
# (the second allocation of the pair) drops out. Kind
|
|
255
|
+
# order is the C layer's node-type ints: element, text,
|
|
256
|
+
# comment, cdata, pi.
|
|
257
|
+
NATIVE_KLASS_CHILDREN =
|
|
258
|
+
NATIVE_IDENTITY &&
|
|
259
|
+
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.193.2")
|
|
260
|
+
|
|
261
|
+
if NATIVE_KLASS_CHILDREN
|
|
262
|
+
kind_classes = [
|
|
263
|
+
Identity::ELEMENT,
|
|
264
|
+
Identity::TEXT,
|
|
265
|
+
Identity::COMMENT,
|
|
266
|
+
Identity::CDATA,
|
|
267
|
+
Identity::PROCESSING_INSTRUCTION,
|
|
268
|
+
]
|
|
269
|
+
kind_classes.each do |klass|
|
|
270
|
+
klass.install_child_klasses(kind_classes)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
249
274
|
def wrap_native(node, type, _context)
|
|
250
275
|
return nil unless NATIVE_IDENTITY &&
|
|
251
276
|
node.is_a?(::Leptris::XML::NativeNode)
|
|
@@ -598,7 +623,21 @@ module Moxml
|
|
|
598
623
|
# entity-marker splitting applies.
|
|
599
624
|
def parse_fragment(xml, _context = nil)
|
|
600
625
|
doc = parse("<m>#{xml}</m>")
|
|
601
|
-
|
|
626
|
+
# Lifetime seam (#245): the fragment's natives live in the
|
|
627
|
+
# parse document's C tree, and the binding Document is the
|
|
628
|
+
# sole owner with a GC finalizer. The parent_node chain
|
|
629
|
+
# (fragment -> synthetic <m> -> Document wrapper) keeps the
|
|
630
|
+
# owning wrappers strongly reachable from every returned
|
|
631
|
+
# node; the wrappers are minted HERE and returned as-is —
|
|
632
|
+
# re-wrapping at the caller would race the context map's
|
|
633
|
+
# weak values and silently drop the chain.
|
|
634
|
+
root_wrapper = doc.root
|
|
635
|
+
root_wrapper.parent_node = doc
|
|
636
|
+
children(root_wrapper.native).map do |child|
|
|
637
|
+
wrapped = Node.wrap(child, doc.context)
|
|
638
|
+
wrapped.parent_node = root_wrapper
|
|
639
|
+
wrapped
|
|
640
|
+
end
|
|
602
641
|
end
|
|
603
642
|
|
|
604
643
|
def iterparse(xml, mode = :top_level, _context = nil, &block)
|
|
@@ -794,6 +833,10 @@ module Moxml
|
|
|
794
833
|
|
|
795
834
|
element = node
|
|
796
835
|
if namespace.nil?
|
|
836
|
+
# Supported un-prefix (#245, relaton migration): rename
|
|
837
|
+
# the node to its local part (name = local) — the
|
|
838
|
+
# serializer re-attaches the old prefix after namespace=
|
|
839
|
+
# nil on prefixed elements until leptris-ruby#244 lands.
|
|
797
840
|
# Nil-clear contract (issue #164): undeclare the default
|
|
798
841
|
# namespace (xmlns="") and drop any name prefix — the
|
|
799
842
|
# element then reports no namespace, matching the other
|
|
@@ -1359,7 +1402,17 @@ module Moxml
|
|
|
1359
1402
|
node.document.add_pi(new_node.target, new_node.content.to_s)
|
|
1360
1403
|
return new_node
|
|
1361
1404
|
end
|
|
1362
|
-
node.add_previous_sibling(new_node)
|
|
1405
|
+
return node.add_previous_sibling(new_node) if node.is_a?(::Leptris::XML::Element)
|
|
1406
|
+
|
|
1407
|
+
# Non-element receivers: raw-FFI anchor (issue #245, same
|
|
1408
|
+
# shape as add_next_sibling).
|
|
1409
|
+
adopt_for_sibling_insert(new_node, node)
|
|
1410
|
+
::Leptris::XML::FFI.check_status(
|
|
1411
|
+
::Leptris::XML::FFI.leptris_element_insert_before(
|
|
1412
|
+
node.c_ptr, new_node.c_ptr
|
|
1413
|
+
),
|
|
1414
|
+
)
|
|
1415
|
+
new_node
|
|
1363
1416
|
end
|
|
1364
1417
|
|
|
1365
1418
|
def add_next_sibling(node, new_node)
|
|
@@ -1367,7 +1420,31 @@ module Moxml
|
|
|
1367
1420
|
node = to_binding(node)
|
|
1368
1421
|
new_node = to_binding(new_node)
|
|
1369
1422
|
end
|
|
1370
|
-
node.add_next_sibling(new_node)
|
|
1423
|
+
return node.add_next_sibling(new_node) if node.is_a?(::Leptris::XML::Element)
|
|
1424
|
+
|
|
1425
|
+
# Non-element receivers (Text/Comment/CDATA): the binding's
|
|
1426
|
+
# method is Element-only, but the C insert anchors on any
|
|
1427
|
+
# node — route through the FFI entry directly, keeping the
|
|
1428
|
+
# binding's namespace-lift adoption for element inserts
|
|
1429
|
+
# (issue #245).
|
|
1430
|
+
adopt_for_sibling_insert(new_node, node)
|
|
1431
|
+
::Leptris::XML::FFI.check_status(
|
|
1432
|
+
::Leptris::XML::FFI.leptris_element_insert_after(
|
|
1433
|
+
node.c_ptr, new_node.c_ptr
|
|
1434
|
+
),
|
|
1435
|
+
)
|
|
1436
|
+
new_node
|
|
1437
|
+
end
|
|
1438
|
+
|
|
1439
|
+
# The binding's add_*_ sibling methods lift an adopted
|
|
1440
|
+
# element's namespace declarations into the target scope;
|
|
1441
|
+
# the raw-FFI path above must preserve that.
|
|
1442
|
+
def adopt_for_sibling_insert(new_node, anchor)
|
|
1443
|
+
return unless new_node.is_a?(::Leptris::XML::Element)
|
|
1444
|
+
return if ::Leptris::XML::Element.skip_adoption_lift?(new_node)
|
|
1445
|
+
|
|
1446
|
+
scope = anchor.parent.is_a?(::Leptris::XML::Element) ? anchor.parent.namespaces : {}
|
|
1447
|
+
::Leptris::XML::Element.lift_namespaces_for_adoption(new_node, scope)
|
|
1371
1448
|
end
|
|
1372
1449
|
|
|
1373
1450
|
def remove(node)
|
data/lib/moxml/context.rb
CHANGED
|
@@ -124,8 +124,11 @@ module Moxml
|
|
|
124
124
|
# are the result either way.
|
|
125
125
|
def parse_fragment(xml)
|
|
126
126
|
adapter = config.adapter
|
|
127
|
-
adapter.parse_fragment(xml, self).map do |
|
|
128
|
-
|
|
127
|
+
adapter.parse_fragment(xml, self).map do |node|
|
|
128
|
+
# Adapters that tie a parent_node lifetime chain (#245)
|
|
129
|
+
# return wrapped nodes — pass them through untouched;
|
|
130
|
+
# re-wrapping would race the weak wrapper map.
|
|
131
|
+
node.is_a?(Moxml::Node) ? node : Moxml::Node.wrap(node, self)
|
|
129
132
|
end
|
|
130
133
|
end
|
|
131
134
|
|
data/lib/moxml/node.rb
CHANGED
|
@@ -593,10 +593,16 @@ module Moxml
|
|
|
593
593
|
|
|
594
594
|
# Extend-in-place (issue #230): adapters whose natives can
|
|
595
595
|
# carry the contract modules directly (leptris TypedData) mint
|
|
596
|
-
# the native itself as the wrapper — @native is self.
|
|
596
|
+
# the native itself as the wrapper — @native is self. The
|
|
597
|
+
# minted wrapper primes with ITSELF (not the input native):
|
|
598
|
+
# adapter calls then receive the contract-bearing receiver,
|
|
599
|
+
# which klass-propagating reads (#246) dispatch on — and the
|
|
600
|
+
# input native keeps its registration so later wraps of the
|
|
601
|
+
# same base object hit the cache instead of re-minting.
|
|
597
602
|
if (extended = adapter.wrap_native(node, type, context))
|
|
598
|
-
extended.prime_contract(
|
|
603
|
+
extended.prime_contract(extended, context, adapter, type)
|
|
599
604
|
context.register_wrapper(node, extended)
|
|
605
|
+
context.register_wrapper(extended, extended) unless extended.equal?(node)
|
|
600
606
|
return extended
|
|
601
607
|
end
|
|
602
608
|
|
data/lib/moxml/version.rb
CHANGED
|
@@ -672,6 +672,39 @@ RSpec.describe Moxml::Adapter::Leptris do
|
|
|
672
672
|
end
|
|
673
673
|
end
|
|
674
674
|
|
|
675
|
+
describe "adapter seams from the relaton migration (issue #245)" do
|
|
676
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
677
|
+
|
|
678
|
+
it "keeps parse_fragment nodes' owning document alive" do
|
|
679
|
+
nodes = ctx.parse_fragment("<em>x</em>")
|
|
680
|
+
element = nodes.first
|
|
681
|
+
expect(element.parent_node).not_to be_nil
|
|
682
|
+
GC.start
|
|
683
|
+
GC.start
|
|
684
|
+
expect(element.name).to eq("em")
|
|
685
|
+
expect(element.text).to eq("x")
|
|
686
|
+
document = element.document
|
|
687
|
+
expect(document.children.first.name).to eq("m")
|
|
688
|
+
expect(document.children.first.children.first.name).to eq("em")
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
it "inserts siblings after a text node" do
|
|
692
|
+
doc = ctx.parse("<root><p>a<!--c1-->b</p></root>")
|
|
693
|
+
paragraph = doc.root.children.first
|
|
694
|
+
cursor = paragraph.children.first
|
|
695
|
+
cursor.add_next_sibling(doc.create_text("Z"))
|
|
696
|
+
expect(paragraph.to_xml).to eq("<p>aZ<!--c1-->b</p>")
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
it "inserts siblings before a text node" do
|
|
700
|
+
doc = ctx.parse("<root><p><!--c1-->ab</p></root>")
|
|
701
|
+
paragraph = doc.root.children.first
|
|
702
|
+
cursor = paragraph.children.last
|
|
703
|
+
cursor.add_previous_sibling(doc.create_text("Y"))
|
|
704
|
+
expect(paragraph.to_xml).to eq("<p><!--c1-->Yab</p>")
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
|
|
675
708
|
describe "to_binding is defined regardless of the native layer (issue #217)" do
|
|
676
709
|
it "is the identity for binding nodes" do
|
|
677
710
|
ctx = Moxml.new(:leptris)
|
|
@@ -765,4 +798,32 @@ RSpec.describe Moxml::Adapter::Leptris do
|
|
|
765
798
|
expect(pi[2]).to be_a(Integer)
|
|
766
799
|
end
|
|
767
800
|
end
|
|
801
|
+
|
|
802
|
+
describe "klass-propagating children" do
|
|
803
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
804
|
+
|
|
805
|
+
it "mints contract-bearing children in C" do
|
|
806
|
+
skip "requires klass-propagating reads (leptris 1.9.193.2+)" unless described_class.singleton_class.const_get(:NATIVE_KLASS_CHILDREN)
|
|
807
|
+
|
|
808
|
+
doc = ctx.parse(%(<r><e a="1"/>t</r>))
|
|
809
|
+
kids = doc.root.children
|
|
810
|
+
expect(kids.first).to be_a(Moxml::Element)
|
|
811
|
+
expect(kids.last).to be_a(Moxml::Text)
|
|
812
|
+
expect(kids.first["a"]).to eq("1")
|
|
813
|
+
expect(kids.last.text).to eq("t")
|
|
814
|
+
# The C mint IS the wrapper — @native is self, no re-mint.
|
|
815
|
+
expect(kids.first.native).to equal(kids.first)
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
it "keeps identity across access paths" do
|
|
819
|
+
skip "requires klass-propagating reads (leptris 1.9.193.2+)" unless described_class.singleton_class.const_get(:NATIVE_KLASS_CHILDREN)
|
|
820
|
+
|
|
821
|
+
doc = ctx.parse(%(<r><a/>t</r>))
|
|
822
|
+
first = doc.root.children.first
|
|
823
|
+
expect(doc.root.element_children.first).to equal(first)
|
|
824
|
+
expect(first.parent.children.first).to equal(first)
|
|
825
|
+
expect(first.next_sibling.text).to eq("t")
|
|
826
|
+
expect(first.next_sibling.parent).to eq(doc.root)
|
|
827
|
+
end
|
|
828
|
+
end
|
|
768
829
|
end
|
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.58
|
|
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-09-
|
|
11
|
+
date: 2026-09-17 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
|