moxml 0.5.55 → 0.5.57
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/Gemfile +1 -1
- data/lib/moxml/adapter/base.rb +8 -0
- data/lib/moxml/adapter/leptris.rb +67 -3
- data/lib/moxml/context.rb +5 -2
- data/lib/moxml/node.rb +8 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter/leptris_spec.rb +52 -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: d4db82d82671fd5cc6189294198abf6d019cd9a719dd25eff901227ebd6c411e
|
|
4
|
+
data.tar.gz: 34611277972cb9687a3b1350ee4a9d5d3036c432fb2ed89409492a2bae812821
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22ee802575e1c85d3f2a03981ed0fce5ed24e23576d005bbf08861e06051026b23276977a8cdedc3340709ea617fcd8a01cade368f74083d4a05728b6dd5f9f0
|
|
7
|
+
data.tar.gz: 43b6109e181270728559005b64e7e8b728a38403b2230f2c868b125def432ceddc5e9e1e468ad6afebcb2a073c9d5e9f69da48acae2499218dbe7c12a81a61aa
|
data/Gemfile
CHANGED
data/lib/moxml/adapter/base.rb
CHANGED
|
@@ -164,6 +164,14 @@ module Moxml
|
|
|
164
164
|
nil
|
|
165
165
|
end
|
|
166
166
|
|
|
167
|
+
# Source position {line, col_start, col_end} for a node where
|
|
168
|
+
# the engine exposes it (leptris 1.9.181+ source_position);
|
|
169
|
+
# nil elsewhere and for nodes without a position (created
|
|
170
|
+
# nodes answer zeros upstream — pass those through as-is).
|
|
171
|
+
def source_position(_native)
|
|
172
|
+
nil
|
|
173
|
+
end
|
|
174
|
+
|
|
167
175
|
# Subtree walk capability: an adapter with a C-side pre-order
|
|
168
176
|
# traversal (leptris >= 1.9.174.6 visit) walks descendants in
|
|
169
177
|
# one dispatch; nil keeps the recursive children walk. Self
|
|
@@ -103,6 +103,12 @@ module Moxml
|
|
|
103
103
|
NN_ADD_CHILD = NN.instance_method(:add_child)
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
+
# 1.9.181 (lockstep): Node#source_position — {line, col_start,
|
|
107
|
+
# col_end} from the engine's source-position API (upstream
|
|
108
|
+
# #1124); created nodes report zeros.
|
|
109
|
+
NATIVE_SOURCE_POSITION =
|
|
110
|
+
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.181")
|
|
111
|
+
|
|
106
112
|
# Entity-reference preservation (leptris-ruby#212 / upstream
|
|
107
113
|
# #1094): 1.9.177 ships ParseOptions.keep_entity_refs plus
|
|
108
114
|
# first-class EntityReference nodes — the marker machinery
|
|
@@ -592,7 +598,21 @@ module Moxml
|
|
|
592
598
|
# entity-marker splitting applies.
|
|
593
599
|
def parse_fragment(xml, _context = nil)
|
|
594
600
|
doc = parse("<m>#{xml}</m>")
|
|
595
|
-
|
|
601
|
+
# Lifetime seam (#245): the fragment's natives live in the
|
|
602
|
+
# parse document's C tree, and the binding Document is the
|
|
603
|
+
# sole owner with a GC finalizer. The parent_node chain
|
|
604
|
+
# (fragment -> synthetic <m> -> Document wrapper) keeps the
|
|
605
|
+
# owning wrappers strongly reachable from every returned
|
|
606
|
+
# node; the wrappers are minted HERE and returned as-is —
|
|
607
|
+
# re-wrapping at the caller would race the context map's
|
|
608
|
+
# weak values and silently drop the chain.
|
|
609
|
+
root_wrapper = doc.root
|
|
610
|
+
root_wrapper.parent_node = doc
|
|
611
|
+
children(root_wrapper.native).map do |child|
|
|
612
|
+
wrapped = Node.wrap(child, doc.context)
|
|
613
|
+
wrapped.parent_node = root_wrapper
|
|
614
|
+
wrapped
|
|
615
|
+
end
|
|
596
616
|
end
|
|
597
617
|
|
|
598
618
|
def iterparse(xml, mode = :top_level, _context = nil, &block)
|
|
@@ -788,6 +808,10 @@ module Moxml
|
|
|
788
808
|
|
|
789
809
|
element = node
|
|
790
810
|
if namespace.nil?
|
|
811
|
+
# Supported un-prefix (#245, relaton migration): rename
|
|
812
|
+
# the node to its local part (name = local) — the
|
|
813
|
+
# serializer re-attaches the old prefix after namespace=
|
|
814
|
+
# nil on prefixed elements until leptris-ruby#244 lands.
|
|
791
815
|
# Nil-clear contract (issue #164): undeclare the default
|
|
792
816
|
# namespace (xmlns="") and drop any name prefix — the
|
|
793
817
|
# element then reports no namespace, matching the other
|
|
@@ -1238,6 +1262,12 @@ module Moxml
|
|
|
1238
1262
|
r
|
|
1239
1263
|
end
|
|
1240
1264
|
|
|
1265
|
+
def source_position(node)
|
|
1266
|
+
return nil unless NATIVE_SOURCE_POSITION
|
|
1267
|
+
|
|
1268
|
+
to_binding(node).source_position
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1241
1271
|
def line_number(node)
|
|
1242
1272
|
if NATIVE_MUTATIONS_COHERENT &&
|
|
1243
1273
|
node.is_a?(::Leptris::XML::NativeNode)
|
|
@@ -1347,7 +1377,17 @@ module Moxml
|
|
|
1347
1377
|
node.document.add_pi(new_node.target, new_node.content.to_s)
|
|
1348
1378
|
return new_node
|
|
1349
1379
|
end
|
|
1350
|
-
node.add_previous_sibling(new_node)
|
|
1380
|
+
return node.add_previous_sibling(new_node) if node.is_a?(::Leptris::XML::Element)
|
|
1381
|
+
|
|
1382
|
+
# Non-element receivers: raw-FFI anchor (issue #245, same
|
|
1383
|
+
# shape as add_next_sibling).
|
|
1384
|
+
adopt_for_sibling_insert(new_node, node)
|
|
1385
|
+
::Leptris::XML::FFI.check_status(
|
|
1386
|
+
::Leptris::XML::FFI.leptris_element_insert_before(
|
|
1387
|
+
node.c_ptr, new_node.c_ptr
|
|
1388
|
+
),
|
|
1389
|
+
)
|
|
1390
|
+
new_node
|
|
1351
1391
|
end
|
|
1352
1392
|
|
|
1353
1393
|
def add_next_sibling(node, new_node)
|
|
@@ -1355,7 +1395,31 @@ module Moxml
|
|
|
1355
1395
|
node = to_binding(node)
|
|
1356
1396
|
new_node = to_binding(new_node)
|
|
1357
1397
|
end
|
|
1358
|
-
node.add_next_sibling(new_node)
|
|
1398
|
+
return node.add_next_sibling(new_node) if node.is_a?(::Leptris::XML::Element)
|
|
1399
|
+
|
|
1400
|
+
# Non-element receivers (Text/Comment/CDATA): the binding's
|
|
1401
|
+
# method is Element-only, but the C insert anchors on any
|
|
1402
|
+
# node — route through the FFI entry directly, keeping the
|
|
1403
|
+
# binding's namespace-lift adoption for element inserts
|
|
1404
|
+
# (issue #245).
|
|
1405
|
+
adopt_for_sibling_insert(new_node, node)
|
|
1406
|
+
::Leptris::XML::FFI.check_status(
|
|
1407
|
+
::Leptris::XML::FFI.leptris_element_insert_after(
|
|
1408
|
+
node.c_ptr, new_node.c_ptr
|
|
1409
|
+
),
|
|
1410
|
+
)
|
|
1411
|
+
new_node
|
|
1412
|
+
end
|
|
1413
|
+
|
|
1414
|
+
# The binding's add_*_ sibling methods lift an adopted
|
|
1415
|
+
# element's namespace declarations into the target scope;
|
|
1416
|
+
# the raw-FFI path above must preserve that.
|
|
1417
|
+
def adopt_for_sibling_insert(new_node, anchor)
|
|
1418
|
+
return unless new_node.is_a?(::Leptris::XML::Element)
|
|
1419
|
+
return if ::Leptris::XML::Element.skip_adoption_lift?(new_node)
|
|
1420
|
+
|
|
1421
|
+
scope = anchor.parent.is_a?(::Leptris::XML::Element) ? anchor.parent.namespaces : {}
|
|
1422
|
+
::Leptris::XML::Element.lift_namespaces_for_adoption(new_node, scope)
|
|
1359
1423
|
end
|
|
1360
1424
|
|
|
1361
1425
|
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
|
@@ -402,6 +402,14 @@ module Moxml
|
|
|
402
402
|
adapter.line_number(@native)
|
|
403
403
|
end
|
|
404
404
|
|
|
405
|
+
# {line, col_start, col_end} where the engine exposes source
|
|
406
|
+
# positions (leptris 1.9.181+); nil elsewhere. Created nodes
|
|
407
|
+
# answer zeros upstream — distinguishable from nil by callers
|
|
408
|
+
# that care.
|
|
409
|
+
def source_position
|
|
410
|
+
adapter.source_position(@native)
|
|
411
|
+
end
|
|
412
|
+
|
|
405
413
|
# Content-defined Merkle digest of this subtree (issue #173,
|
|
406
414
|
# companion to leptris#869): a u64 Integer where the backend
|
|
407
415
|
# computes one, nil everywhere else. Consumers gate on nil and
|
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)
|
|
@@ -694,6 +727,25 @@ RSpec.describe Moxml::Adapter::Leptris do
|
|
|
694
727
|
end
|
|
695
728
|
end
|
|
696
729
|
|
|
730
|
+
describe "source_position (leptris 1.9.181+, upstream #1124)" do
|
|
731
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
732
|
+
|
|
733
|
+
it "answers engine source positions for parsed nodes" do
|
|
734
|
+
skip "requires leptris 1.9.181" unless described_class::NATIVE_SOURCE_POSITION
|
|
735
|
+
|
|
736
|
+
doc = ctx.parse("<root>\n <child>text</child>\n</root>")
|
|
737
|
+
expect(doc.root.source_position).to include(line: 1)
|
|
738
|
+
expect(doc.root.source_position).to be_a(Hash)
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
it "answers nil below the gate and on other adapters" do
|
|
742
|
+
expect(Moxml::Adapter::Base.source_position(nil)).to be_nil
|
|
743
|
+
expect(Moxml::Adapter::Nokogiri.source_position(nil)).to be_nil
|
|
744
|
+
noko = Moxml.new(:nokogiri).parse("<root><a/></root>")
|
|
745
|
+
expect(noko.root.children.first.source_position).to be_nil
|
|
746
|
+
end
|
|
747
|
+
end
|
|
748
|
+
|
|
697
749
|
describe "subtree digest (issue #173, leptris#869)" do
|
|
698
750
|
let(:ctx) { Moxml.new(:leptris) }
|
|
699
751
|
|
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.57
|
|
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
|