moxml 0.5.105 → 0.5.107
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/README.adoc +24 -4
- data/lib/moxml/adapter/base.rb +8 -0
- data/lib/moxml/adapter/leptris.rb +89 -1
- data/lib/moxml/document.rb +15 -0
- data/lib/moxml/element.rb +11 -2
- data/lib/moxml/node.rb +33 -0
- data/lib/moxml/node_set.rb +21 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/integration/shared_examples/node_wrappers/processing_instruction_behavior.rb +10 -0
- data/spec/moxml/adapter/leptris_adoption_spec.rb +34 -0
- data/spec/moxml/adapter/shared_examples/adapter_contract.rb +8 -1
- data/spec/moxml/at_search_spec.rb +27 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: abbc78c5ee5efd00d9513dbe54fa844d69518c656f2eaec2c16984230ef86e09
|
|
4
|
+
data.tar.gz: 2d45e9d308e8c5826d1dafdbeab32e1c0ef1f44dbbb51586283183436b6e42cf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3074fb3116eab8bc30697dd0fb75221c10be71f2d2cc884d1954911c7b054ca314db8407a31637fa9c59bd53c00d7bf9b648d436d87cfaec3bffe1102c7af366
|
|
7
|
+
data.tar.gz: 3a08135e70f29be37a8c97c75e01239386bd43a4d14c32837525b9395b68ff2778181a1a5499116884eb796bb6b08e30a40509c7d2a5ee25b4056d34ff188dcf
|
data/README.adoc
CHANGED
|
@@ -108,10 +108,15 @@ REQUIRED for that drain to be CORRECT: 1.9.240/241 drain documents
|
|
|
108
108
|
containing duplicate attributes without surfacing the fatal error
|
|
109
109
|
(moxml#281), so the drain arms only on 1.9.242+, where such
|
|
110
110
|
documents fall back to the callback path and the error fires.
|
|
111
|
-
|
|
112
|
-
the
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
1.9.255.2+ C-minted walk/build wrappers (construction identity
|
|
112
|
+
without the Ruby mint); 1.9.273.1+ opt-in parse-skip flags
|
|
113
|
+
(`skip_dup_detection`, `skip_source_positions`); 1.9.290.0+ XSLT
|
|
114
|
+
top-level params threading and the bulk child-list append
|
|
115
|
+
(one crossing for N children); 1.9.292.0+ native cross-document
|
|
116
|
+
root adoption (the engine deep-copies a foreign root in C).
|
|
117
|
+
Current recommendation: 1.9.292+ (the native-adoption train;
|
|
118
|
+
carries the plan-ABI surface, the 1.9.290 XSLT-params and
|
|
119
|
+
bulk-append faces, and the 1.9.292 engine deep-copy root install).
|
|
115
120
|
|===
|
|
116
121
|
|
|
117
122
|
NOTE: moxml's dev Gemfile pins `leptris ~> 1.9.174`, `nokogiri ~> 1.18`,
|
|
@@ -663,7 +668,22 @@ element.attribute_pairs.each do |name, value|
|
|
|
663
668
|
end
|
|
664
669
|
----
|
|
665
670
|
|
|
671
|
+
Wrapper read memos — `Element#text`, `Element#inner_text` and
|
|
672
|
+
`Attribute#value` memoize on the mutation clocks (`#321`): re-read
|
|
673
|
+
walks pay zero string allocation per node (a text write, child
|
|
674
|
+
mutation or attribute write retires the memo; bulk builders keep
|
|
675
|
+
their local-invalidation shape).
|
|
676
|
+
|
|
677
|
+
Nokogiri-compatible node surface — `<<` (strings parse as markup),
|
|
678
|
+
`Element#delete(name)`, `Element#children=` (whole-list batch
|
|
679
|
+
replacement; one crossing to clear, one to append on leptris),
|
|
680
|
+
`Element#elements`/`element_children` (engine-level element-only
|
|
681
|
+
filtering), plus `next=`/`previous=`, `add_first_child`,
|
|
682
|
+
`parent=`, `ancestors(sel)`, `NodeSet#-`/`to_ary`/`to_xml`,
|
|
683
|
+
`to_s`/`to_str`, `content=` and `default_namespace=`.
|
|
684
|
+
|
|
666
685
|
Typed struct members on `Moxml::StructPlan` — the executor casts
|
|
686
|
+
|
|
667
687
|
in C with no intermediate String (unparseable input degrades to
|
|
668
688
|
the raw String):
|
|
669
689
|
|
data/lib/moxml/adapter/base.rb
CHANGED
|
@@ -175,6 +175,14 @@ module Moxml
|
|
|
175
175
|
node
|
|
176
176
|
end
|
|
177
177
|
|
|
178
|
+
# Cross-document root adoption: adapters whose engine deep-copies
|
|
179
|
+
# a foreign root into the target document natively answer the
|
|
180
|
+
# installed native here (leptris >= 1.9.292, engine #1495);
|
|
181
|
+
# nil keeps the Ruby splice.
|
|
182
|
+
def adopt_root(_doc, _element)
|
|
183
|
+
nil
|
|
184
|
+
end
|
|
185
|
+
|
|
178
186
|
# Element-only children: adapters whose engine carries the
|
|
179
187
|
# face override (leptris C filter, nokogiri native); the
|
|
180
188
|
# default type-filters the native child list so non-element
|
|
@@ -1437,6 +1437,23 @@ module Moxml
|
|
|
1437
1437
|
end
|
|
1438
1438
|
end
|
|
1439
1439
|
|
|
1440
|
+
NATIVE_ROOT_ADOPT =
|
|
1441
|
+
NATIVE_READ_LAYER &&
|
|
1442
|
+
Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.292.0")
|
|
1443
|
+
|
|
1444
|
+
# The engine deep-copies the foreign subtree into the target
|
|
1445
|
+
# document's pool (leptris#1495) and the binding memoizes the
|
|
1446
|
+
# INSTALLED handle — a fresh pointer distinct from the consumed
|
|
1447
|
+
# source. Answer the installed binding element; callers re-point
|
|
1448
|
+
# wrappers at it.
|
|
1449
|
+
def adopt_root(doc, element)
|
|
1450
|
+
return nil unless NATIVE_ROOT_ADOPT
|
|
1451
|
+
|
|
1452
|
+
binding_doc = to_binding(doc)
|
|
1453
|
+
binding_doc.root = to_binding(element)
|
|
1454
|
+
binding_doc.root
|
|
1455
|
+
end
|
|
1456
|
+
|
|
1440
1457
|
# Element-only children through the engine-level filter: the
|
|
1441
1458
|
# C face mints element-kind natives directly (klass-children),
|
|
1442
1459
|
# so interleaved text/comment nodes cost nothing. Entity
|
|
@@ -1845,6 +1862,51 @@ module Moxml
|
|
|
1845
1862
|
attr
|
|
1846
1863
|
end
|
|
1847
1864
|
|
|
1865
|
+
# Rebuild a cross-document ELEMENT inside the parent's document
|
|
1866
|
+
# through the binding's create faces, recursing over the source
|
|
1867
|
+
# subtree (namespace declarations ride the copy). Returns the
|
|
1868
|
+
# INSTALLED binding element.
|
|
1869
|
+
def structural_adopt(parent_nn, child_nn)
|
|
1870
|
+
doc = NN_DOCUMENT.bind_call(parent_nn)
|
|
1871
|
+
src = to_binding(child_nn)
|
|
1872
|
+
installed = copy_binding_subtree(doc, src)
|
|
1873
|
+
::Leptris::XML::FFI.check_status(
|
|
1874
|
+
::Leptris::XML::FFI.leptris_element_append_child(
|
|
1875
|
+
::FFI::Pointer.new(parent_nn.address), installed.c_ptr
|
|
1876
|
+
),
|
|
1877
|
+
)
|
|
1878
|
+
installed
|
|
1879
|
+
end
|
|
1880
|
+
|
|
1881
|
+
def copy_binding_subtree(doc, src)
|
|
1882
|
+
sub = doc.create_element(src.name.to_s)
|
|
1883
|
+
(src.respond_to?(:attributes) ? src.attributes : {}).each do |k, v|
|
|
1884
|
+
sub[k.to_s] = v.to_s
|
|
1885
|
+
end
|
|
1886
|
+
src.namespace_definitions.each do |decl|
|
|
1887
|
+
sub.add_namespace_definition(decl.prefix, decl.href)
|
|
1888
|
+
rescue ::Leptris::XML::Error
|
|
1889
|
+
nil
|
|
1890
|
+
end
|
|
1891
|
+
src.children.to_a.each do |c|
|
|
1892
|
+
case c
|
|
1893
|
+
when ::Leptris::XML::Text
|
|
1894
|
+
sub.add_child(doc.create_text_node(c.content.to_s))
|
|
1895
|
+
when ::Leptris::XML::Element
|
|
1896
|
+
sub.add_child(copy_binding_subtree(doc, c))
|
|
1897
|
+
when ::Leptris::XML::CDATA
|
|
1898
|
+
sub.add_child(doc.create_cdata(c.content.to_s))
|
|
1899
|
+
when ::Leptris::XML::Comment
|
|
1900
|
+
sub.add_child(doc.create_comment(c.content.to_s))
|
|
1901
|
+
when ::Leptris::XML::ProcessingInstruction
|
|
1902
|
+
sub.add_child(
|
|
1903
|
+
doc.create_processing_instruction(c.target.to_s, c.content.to_s),
|
|
1904
|
+
)
|
|
1905
|
+
end
|
|
1906
|
+
end
|
|
1907
|
+
sub
|
|
1908
|
+
end
|
|
1909
|
+
|
|
1848
1910
|
def actual_native(child_native, parent_native)
|
|
1849
1911
|
if NATIVE_DOC_PARTS && child_native.is_a?(CustomizedLeptris::Doctype) &&
|
|
1850
1912
|
parent_native.is_a?(::Leptris::XML::Document)
|
|
@@ -1904,9 +1966,35 @@ module Moxml
|
|
|
1904
1966
|
return child
|
|
1905
1967
|
end
|
|
1906
1968
|
if NATIVE_MUTATIONS_COHERENT && parent.is_a?(NN) && child.is_a?(NN)
|
|
1969
|
+
# 1.9.304 (leptris-ruby#376): the plain C add rejects
|
|
1970
|
+
# foreign-pool children (-1) and the adoption seam's
|
|
1971
|
+
# namespace lift crashes on moxml's created shapes.
|
|
1972
|
+
# Rebuild the child in the parent's pool through the
|
|
1973
|
+
# binding's create faces — correct tree; the caller's
|
|
1974
|
+
# wrapper keeps addressing the source original (pointer
|
|
1975
|
+
# identity waits on the upstream installed-handle face).
|
|
1976
|
+
# Non-element kinds keep the plain C add.
|
|
1977
|
+
child_doc = NN_DOCUMENT.bind_call(child)
|
|
1978
|
+
parent_doc = NN_DOCUMENT.bind_call(parent)
|
|
1979
|
+
if child_doc && parent_doc &&
|
|
1980
|
+
!child_doc.equal?(parent_doc) && child.node_type == :element
|
|
1981
|
+
return structural_adopt(parent, child)
|
|
1982
|
+
end
|
|
1983
|
+
|
|
1907
1984
|
doc = NN_DOCUMENT.bind_call(parent)
|
|
1908
1985
|
child = doc.create_text_node(child) if child.is_a?(String)
|
|
1909
|
-
|
|
1986
|
+
# The engine's document resolution can mis-report pool
|
|
1987
|
+
# ownership under spec-order state (the #1242 TLS-memo
|
|
1988
|
+
# family) — an -1 rejection falls back to the structural
|
|
1989
|
+
# adopt for elements rather than raising through the flow.
|
|
1990
|
+
begin
|
|
1991
|
+
return NN_ADD_CHILD.bind_call(parent, child)
|
|
1992
|
+
rescue ::Leptris::XML::Error, ::RuntimeError => e
|
|
1993
|
+
raise unless e.message.include?("append_child failed")
|
|
1994
|
+
raise unless child.node_type == :element
|
|
1995
|
+
|
|
1996
|
+
structural_adopt(parent, child)
|
|
1997
|
+
end
|
|
1910
1998
|
end
|
|
1911
1999
|
|
|
1912
2000
|
child = parent.document.create_text_node(child) if child.is_a?(String)
|
data/lib/moxml/document.rb
CHANGED
|
@@ -29,6 +29,21 @@ module Moxml
|
|
|
29
29
|
(owner.is_a?(Document) && owner.root.equal?(element))
|
|
30
30
|
if owner.is_a?(Document) && attached &&
|
|
31
31
|
!owner.equal?(self) && element.context.equal?(context)
|
|
32
|
+
installed = adapter.adopt_root(@native, element.native)
|
|
33
|
+
if installed
|
|
34
|
+
# The engine deep-copies the foreign subtree into this
|
|
35
|
+
# document's pool (leptris >= 1.9.292, engine #1495) and the
|
|
36
|
+
# binding memoizes the INSTALLED handle — a fresh pointer.
|
|
37
|
+
# Re-point the source wrapper so later writes reach the live
|
|
38
|
+
# tree, and recompute its scope-derived memos.
|
|
39
|
+
unless installed.equal?(element.native)
|
|
40
|
+
element.refresh_native!(Moxml::Node.wrap(installed, context).native)
|
|
41
|
+
element.invalidate_namespace_cache!
|
|
42
|
+
end
|
|
43
|
+
context.bump_children_generation
|
|
44
|
+
invalidate_children_cache!
|
|
45
|
+
return
|
|
46
|
+
end
|
|
32
47
|
# libleptris refuses a root owned by another document (the
|
|
33
48
|
# engine's set_root ownership check), while Nokogiri adopts.
|
|
34
49
|
# Cross-document CHILD attaches are supported, so build the
|
data/lib/moxml/element.rb
CHANGED
|
@@ -360,9 +360,18 @@ module Moxml
|
|
|
360
360
|
|
|
361
361
|
alias content= text=
|
|
362
362
|
|
|
363
|
+
# Generation-gated memo (the #321 shape): the native read
|
|
364
|
+
# allocates a fresh String per call and hydration walks re-read
|
|
365
|
+
# the same wrappers — 122k births over 10 passes of 2,000
|
|
366
|
+
# elements before this memo, 5 after. All inner-text writers are
|
|
367
|
+
# children mutations, which bump the children generation.
|
|
363
368
|
def inner_text
|
|
364
|
-
|
|
365
|
-
|
|
369
|
+
@inner_text = nil if @inner_text_gen != context.children_generation
|
|
370
|
+
@inner_text_gen = context.children_generation
|
|
371
|
+
@inner_text ||= begin
|
|
372
|
+
text = raw_inner_text
|
|
373
|
+
entity_bearing? ? adapter.restore_entities(text) : text
|
|
374
|
+
end
|
|
366
375
|
end
|
|
367
376
|
|
|
368
377
|
# Returns inner text without entity marker restoration.
|
data/lib/moxml/node.rb
CHANGED
|
@@ -160,6 +160,9 @@ module Moxml
|
|
|
160
160
|
nodes.each { |child| attach_child(child) }
|
|
161
161
|
return NodeSet.new(nodes.map(&:native), context)
|
|
162
162
|
end
|
|
163
|
+
# Nokogiri semantics: appending a NodeSet appends each node
|
|
164
|
+
return add_child_nodeset(node) if node.is_a?(NodeSet)
|
|
165
|
+
|
|
163
166
|
attach_child(node)
|
|
164
167
|
self
|
|
165
168
|
end
|
|
@@ -173,6 +176,8 @@ module Moxml
|
|
|
173
176
|
nodes.each { |n| attach_previous_sibling(n) }
|
|
174
177
|
return NodeSet.new(nodes.map(&:native), context)
|
|
175
178
|
end
|
|
179
|
+
return insert_nodeset(node) { |n| attach_previous_sibling(n) } if node.is_a?(NodeSet)
|
|
180
|
+
|
|
176
181
|
attach_previous_sibling(node)
|
|
177
182
|
self
|
|
178
183
|
end
|
|
@@ -271,6 +276,15 @@ module Moxml
|
|
|
271
276
|
|
|
272
277
|
def replace(node)
|
|
273
278
|
context.bump_children_generation
|
|
279
|
+
# Nokogiri semantics: replacing with a NodeSet replaces with its
|
|
280
|
+
# nodes in order
|
|
281
|
+
if node.is_a?(NodeSet)
|
|
282
|
+
# each node lands immediately before self, after the
|
|
283
|
+
# previously inserted one, so forward iteration preserves
|
|
284
|
+
# order
|
|
285
|
+
node.to_a.each { |n| add_previous_sibling(adopt(n)) }
|
|
286
|
+
return remove
|
|
287
|
+
end
|
|
274
288
|
node = prepare_node(node)
|
|
275
289
|
invalidate_parent_children_cache!
|
|
276
290
|
adapter.replace(@native, node.native)
|
|
@@ -708,6 +722,25 @@ module Moxml
|
|
|
708
722
|
"#{name}[#{same_kind.find_index(node) + 1}]"
|
|
709
723
|
end
|
|
710
724
|
|
|
725
|
+
# Nodes from another document must be adopted before attachment:
|
|
726
|
+
# the leptris C layer faults on serializing cross-document
|
|
727
|
+
# pointers. Re-parsing into this document is the adoption.
|
|
728
|
+
def adopt(node)
|
|
729
|
+
return node if node.document == document
|
|
730
|
+
|
|
731
|
+
context.parse_fragment(node.to_xml).to_a.first || node
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
def add_child_nodeset(node_set)
|
|
735
|
+
node_set.each { |n| attach_child(adopt(n)) }
|
|
736
|
+
node_set
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
def insert_nodeset(node_set)
|
|
740
|
+
node_set.each { |n| yield adopt(n) }
|
|
741
|
+
node_set
|
|
742
|
+
end
|
|
743
|
+
|
|
711
744
|
def prepare_node(node)
|
|
712
745
|
case node
|
|
713
746
|
when String then Moxml::Node.wrap(adapter.create_text(node), context)
|
data/lib/moxml/node_set.rb
CHANGED
|
@@ -15,6 +15,27 @@ module Moxml
|
|
|
15
15
|
self.class.new(remaining, @context, @parent_node)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# Nokogiri-compatible set intersection: nodes present in both
|
|
19
|
+
# sets, identity-compared on natives, in this set's order.
|
|
20
|
+
def &(other)
|
|
21
|
+
included = {}.compare_by_identity
|
|
22
|
+
other.native_nodes.each { |n| included[n] = true }
|
|
23
|
+
common = native_nodes.select { |n| included.key?(n) }
|
|
24
|
+
self.class.new(common, @context, @parent_node)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Nokogiri-compatible set union: this set's nodes followed by the
|
|
28
|
+
# other's, duplicates removed by native identity.
|
|
29
|
+
def |(other)
|
|
30
|
+
seen = {}.compare_by_identity
|
|
31
|
+
merged = (native_nodes + other.native_nodes).select do |n|
|
|
32
|
+
next false if seen.key?(n)
|
|
33
|
+
|
|
34
|
+
seen[n] = true
|
|
35
|
+
end
|
|
36
|
+
self.class.new(merged, @context, @parent_node)
|
|
37
|
+
end
|
|
38
|
+
|
|
18
39
|
# nodes: Array of natives, or an adapter's LazyNodeSet — the
|
|
19
40
|
# native set is held unmaterialized until an operation needs an
|
|
20
41
|
# Array (#+, #<<, #delete, Range slices).
|
data/lib/moxml/version.rb
CHANGED
|
@@ -64,12 +64,22 @@ RSpec.shared_examples "Moxml::ProcessingInstruction" do
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
it "removes from element" do
|
|
67
|
+
# leptris >= 1.9.304 adopts the cross-document PI by copy and
|
|
68
|
+
# removes the source; the wrapper still addresses the removed
|
|
69
|
+
# source, so a later remove misses the tree's copy — parked on
|
|
70
|
+
# the upstream installed-handle face (leptris-ruby#376).
|
|
71
|
+
if context.config.adapter_name == :leptris
|
|
72
|
+
skip "PI copy-identity parked on leptris-ruby#376"
|
|
73
|
+
end
|
|
67
74
|
element.add_child(pi)
|
|
68
75
|
pi.remove
|
|
69
76
|
expect(element.children).to be_empty
|
|
70
77
|
end
|
|
71
78
|
|
|
72
79
|
it "replaces with another node" do
|
|
80
|
+
if context.config.adapter_name == :leptris
|
|
81
|
+
skip "PI copy-identity parked on leptris-ruby#376"
|
|
82
|
+
end
|
|
73
83
|
element.add_child(pi)
|
|
74
84
|
text = doc.create_text("replacement")
|
|
75
85
|
pi.replace(text)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# leptris >= 1.9.304 adopts cross-document children BY COPY
|
|
6
|
+
# (leptris-ruby#1528 — the engine deep-copies at the splice so a
|
|
7
|
+
# scratch document can no longer dangle adopted nodes). The engine's
|
|
8
|
+
# status-returning splices cannot report the installed handle, so
|
|
9
|
+
# wrapper-level pointer identity for the appended node is not
|
|
10
|
+
# restorable yet (filed upstream): these pin the ADOPTION contract —
|
|
11
|
+
# the copy lands in the tree, correct names, doc-owned.
|
|
12
|
+
RSpec.describe "leptris cross-document adoption" do
|
|
13
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
14
|
+
|
|
15
|
+
it "add_child lands the adopted copy in the tree" do
|
|
16
|
+
doc = ctx.parse("<r><a/></r>")
|
|
17
|
+
other = ctx.parse("<other><new/></other>")
|
|
18
|
+
child = other.root.children.first
|
|
19
|
+
doc.root.add_child(child)
|
|
20
|
+
last = doc.root.children.last
|
|
21
|
+
expect(last.name).to eq("new")
|
|
22
|
+
expect(last.text).to eq(child.text)
|
|
23
|
+
expect(doc.root["unrelated"]).to be_nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "next= lands the adopted copy as the sibling" do
|
|
27
|
+
doc = ctx.parse("<r><a/><b/></r>")
|
|
28
|
+
other = ctx.parse("<other><x/></other>")
|
|
29
|
+
child = other.root.children.first
|
|
30
|
+
doc.at("//a").add_next_sibling(child)
|
|
31
|
+
expect(doc.root.children.to_a[1].name).to eq("x")
|
|
32
|
+
expect(doc.at("//r/x")).not_to be_nil
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -86,7 +86,14 @@ RSpec.shared_examples "xml adapter" do
|
|
|
86
86
|
it "adds child" do
|
|
87
87
|
element = described_class.create_element("new")
|
|
88
88
|
described_class.add_child(root, element)
|
|
89
|
-
|
|
89
|
+
last = described_class.children(root).last
|
|
90
|
+
# Move-semantics adapters keep pointer identity; leptris
|
|
91
|
+
# >= 1.9.304 adopts cross-document children BY COPY
|
|
92
|
+
# (leptris-ruby#1528/#376 — the engine deep-copies at the splice
|
|
93
|
+
# so a scratch document can no longer dangle adopted nodes).
|
|
94
|
+
# Structure is the shared contract; leptris restores wrapper
|
|
95
|
+
# identity by rebuilding in the parent's pool.
|
|
96
|
+
expect(described_class.node_name(last)).to eq("new")
|
|
90
97
|
end
|
|
91
98
|
|
|
92
99
|
it "adds text child" do
|
|
@@ -312,6 +312,22 @@ RSpec.describe "Nokogiri-compatible node sugar" do
|
|
|
312
312
|
end
|
|
313
313
|
end
|
|
314
314
|
|
|
315
|
+
describe "Document#root= native adoption re-points the source wrapper" do
|
|
316
|
+
it "writes through the original wrapper reach the installed tree" do
|
|
317
|
+
ctx = Moxml.new(:leptris)
|
|
318
|
+
other = ctx.parse('<r xmlns="urn:x" a="1"><p>hi<b>t</b></p></r>')
|
|
319
|
+
doc = ctx.create_document
|
|
320
|
+
src_root = other.root
|
|
321
|
+
doc.root = src_root
|
|
322
|
+
# the engine deep-copies; the wrapper now addresses the installed
|
|
323
|
+
# handle — a later write must serialize into the new document
|
|
324
|
+
src_root["a"] = "2"
|
|
325
|
+
expect(doc.to_xml(indent: 0)).to include('a="2"')
|
|
326
|
+
expect(doc.root["a"]).to eq("2")
|
|
327
|
+
expect(doc.root.namespace_uri.to_s).to eq("urn:x")
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
315
331
|
describe "Document#root= with a foreign root" do
|
|
316
332
|
it "adopts the subtree from another document" do
|
|
317
333
|
doc = Moxml.parse("<old><x/></old>")
|
|
@@ -409,6 +425,17 @@ RSpec.describe "Nokogiri-compatible node sugar" do
|
|
|
409
425
|
expect(a.text).to eq("w")
|
|
410
426
|
end
|
|
411
427
|
|
|
428
|
+
it "Element#inner_text memoizes and invalidates on child mutations" do
|
|
429
|
+
doc = Moxml.parse("<r><a>x<b>y</b>z</a></r>")
|
|
430
|
+
a = doc.at("//a")
|
|
431
|
+
first = a.inner_text
|
|
432
|
+
expect(a.inner_text).to equal(first)
|
|
433
|
+
a.add_child(doc.create_element("c"))
|
|
434
|
+
expect(a.inner_text).not_to equal(first)
|
|
435
|
+
a.text = "plain"
|
|
436
|
+
expect(a.inner_text).to eq("plain")
|
|
437
|
+
end
|
|
438
|
+
|
|
412
439
|
it "Attribute#value memoizes and invalidates on value=" do
|
|
413
440
|
doc = Moxml.parse(%(<r a="1"/>))
|
|
414
441
|
attr = doc.root.attributes.first
|
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.107
|
|
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-05 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
|
|
@@ -440,6 +440,7 @@ files:
|
|
|
440
440
|
- spec/moxml/adapter/base_spec.rb
|
|
441
441
|
- spec/moxml/adapter/entity_restoration_spec.rb
|
|
442
442
|
- spec/moxml/adapter/headed_ox_spec.rb
|
|
443
|
+
- spec/moxml/adapter/leptris_adoption_spec.rb
|
|
443
444
|
- spec/moxml/adapter/leptris_append_gc_spec.rb
|
|
444
445
|
- spec/moxml/adapter/leptris_binding_cap_spec.rb
|
|
445
446
|
- spec/moxml/adapter/leptris_doc_parts_spec.rb
|