moxml 0.5.147 → 0.5.148
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 +70 -1
- data/lib/moxml/document.rb +18 -4
- data/lib/moxml/node.rb +12 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter/leptris_contract_reads_spec.rb +29 -0
- data/spec/moxml/adapter/leptris_detached_text_spec.rb +10 -3
- data/spec/moxml/adapter/leptris_parse_lint_spec.rb +12 -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: 1b51347e90c3dae3f4b0b41268b4ee8cd92fa91a4210a17b1ea75da85db0f82a
|
|
4
|
+
data.tar.gz: d251705fd512baed6295dad6a56c12e7db531dfa4845b2f77983c9c305bfaffd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8ffcc8a80a304b656005d171627c40a982ba1b1594bbd2cb9090f390150f799b69041e338a54d2e79245a3fdf526639406938f4fdd82b290f55092c43ac20f34
|
|
7
|
+
data.tar.gz: 764dd4cd2fa5346a701224b7841ff0fb7cfced542b68ad914f4200acda168c5060813cb737429a5bcfd67eca12e74870b8bc4449bc83214678f5c86beaf3aa8c
|
|
@@ -105,6 +105,9 @@ module Moxml
|
|
|
105
105
|
::Leptris::XML::Document.method(:parse)
|
|
106
106
|
.parameters.any? { |_, key| key == :strict }
|
|
107
107
|
|
|
108
|
+
ADD_CHILD_NATIVE =
|
|
109
|
+
NATIVE_READ_LAYER && NN.method_defined?(:add_child_native)
|
|
110
|
+
|
|
108
111
|
if NATIVE_READ_LAYER
|
|
109
112
|
NN_ATTRIBUTE = NN.instance_method(:attribute)
|
|
110
113
|
NN_NAME = NN.instance_method(:name)
|
|
@@ -624,6 +627,31 @@ module Moxml
|
|
|
624
627
|
end
|
|
625
628
|
TEXT.include(TextContractReads)
|
|
626
629
|
end
|
|
630
|
+
|
|
631
|
+
# leptris-ruby#507: children_read joins the alias set. The
|
|
632
|
+
# generation-checked memo STAYS — re-walks of the same
|
|
633
|
+
# document are the common harness/consumer shape and the
|
|
634
|
+
# memo serves them allocation-free (dropping it regressed
|
|
635
|
+
# the walk 2x in GC). Only the MISS gets cheaper: the
|
|
636
|
+
# klass-children face plain-dispatches and returns the
|
|
637
|
+
# already-minted wrappers, so the adapter crossing
|
|
638
|
+
# (bind_call + to_a + keep-refs probe) drops out, and
|
|
639
|
+
# entity-marker documents take super for the split.
|
|
640
|
+
if NN.method_defined?(:children_read)
|
|
641
|
+
module ChildReads
|
|
642
|
+
def children
|
|
643
|
+
@children = nil if @children_gen != context.children_generation
|
|
644
|
+
@children_gen = context.children_generation
|
|
645
|
+
@children ||= if doc_entity_bearing?
|
|
646
|
+
super
|
|
647
|
+
else
|
|
648
|
+
NodeSet.new(children_read, context, self)
|
|
649
|
+
end
|
|
650
|
+
end
|
|
651
|
+
end
|
|
652
|
+
ELEMENT.include(ChildReads)
|
|
653
|
+
TEXT.include(ChildReads)
|
|
654
|
+
end
|
|
627
655
|
end
|
|
628
656
|
|
|
629
657
|
# leptris-ruby#246 klass-propagating reads: the read
|
|
@@ -1464,6 +1492,14 @@ module Moxml
|
|
|
1464
1492
|
count = ffi.leptris_document_parse_diag_count(doc)
|
|
1465
1493
|
return [] if count.zero?
|
|
1466
1494
|
|
|
1495
|
+
# leptris#1771: one face carries the diag's machine-readable
|
|
1496
|
+
# code plus line/column with the entry (nil when the engine
|
|
1497
|
+
# has no token code — pre-#1771 records). The face probe
|
|
1498
|
+
# keeps older bindings on kind+message (issue #408).
|
|
1499
|
+
lint_code = if ffi.respond_to?(:leptris_document_parse_diag_code)
|
|
1500
|
+
ffi.method(:leptris_document_parse_diag_code)
|
|
1501
|
+
end
|
|
1502
|
+
|
|
1467
1503
|
kind_ptr = ::FFI::MemoryPointer.new(:int)
|
|
1468
1504
|
diagnostics = []
|
|
1469
1505
|
count.times do |i|
|
|
@@ -1473,7 +1509,19 @@ module Moxml
|
|
|
1473
1509
|
) == 1
|
|
1474
1510
|
|
|
1475
1511
|
kind = DIAG_KINDS[kind_ptr.read_int] || :unknown
|
|
1476
|
-
|
|
1512
|
+
diag = { kind: kind, message: buffer.read_string }
|
|
1513
|
+
if lint_code
|
|
1514
|
+
code_buf = ::FFI::MemoryPointer.new(:char, 128)
|
|
1515
|
+
line_ptr = ::FFI::MemoryPointer.new(:int)
|
|
1516
|
+
column_ptr = ::FFI::MemoryPointer.new(:int)
|
|
1517
|
+
lint_code.call(doc, i, code_buf, 128, line_ptr, column_ptr)
|
|
1518
|
+
code = code_buf.read_string
|
|
1519
|
+
diag[:code] = code.empty? ? nil : code
|
|
1520
|
+
diag[:line] = line_ptr.read_int.zero? ? nil : line_ptr.read_int
|
|
1521
|
+
diag[:column] =
|
|
1522
|
+
column_ptr.read_int.zero? ? nil : column_ptr.read_int
|
|
1523
|
+
end
|
|
1524
|
+
diagnostics << diag
|
|
1477
1525
|
end
|
|
1478
1526
|
diagnostics
|
|
1479
1527
|
end
|
|
@@ -2496,6 +2544,27 @@ module Moxml
|
|
|
2496
2544
|
end
|
|
2497
2545
|
|
|
2498
2546
|
def add_child(parent, child)
|
|
2547
|
+
# Same-document Identity append — the builder hot path. One
|
|
2548
|
+
# document pair decides the verdict: the pin, the absorb
|
|
2549
|
+
# checks and the binding bridges only exist for
|
|
2550
|
+
# cross-document work, and the plain C append (version bump
|
|
2551
|
+
# inside) is the whole job. bind_call, not dispatch — the
|
|
2552
|
+
# contract modules shadow add_child on Identity receivers
|
|
2553
|
+
# (moxml#2560 note below).
|
|
2554
|
+
if NATIVE_MUTATIONS_COHERENT && parent.is_a?(NN) && child.is_a?(NN) &&
|
|
2555
|
+
!child.is_a?(CustomizedLeptris::EntityReference)
|
|
2556
|
+
parent_doc = NN_DOCUMENT.bind_call(parent)
|
|
2557
|
+
if parent_doc
|
|
2558
|
+
child_doc = NN_DOCUMENT.bind_call(child)
|
|
2559
|
+
if child_doc && parent_doc.equal?(child_doc)
|
|
2560
|
+
# add_child_native (leptris-ruby#509) plain-dispatches
|
|
2561
|
+
# on Identity receivers — one frame, no bind_call.
|
|
2562
|
+
return parent.add_child_native(child) if ADD_CHILD_NATIVE
|
|
2563
|
+
|
|
2564
|
+
return NN_ADD_CHILD.bind_call(parent, child)
|
|
2565
|
+
end
|
|
2566
|
+
end
|
|
2567
|
+
end
|
|
2499
2568
|
# 1.9.311: binding-family children (created PIs, texts...) —
|
|
2500
2569
|
# a DIRECT absorb makes the binding add below move the node
|
|
2501
2570
|
# by reference. A source already owned elsewhere would need
|
data/lib/moxml/document.rb
CHANGED
|
@@ -163,11 +163,21 @@ module Moxml
|
|
|
163
163
|
end
|
|
164
164
|
|
|
165
165
|
def create_element(name)
|
|
166
|
-
|
|
166
|
+
native = adapter.create_element(name, owner_doc: @native)
|
|
167
|
+
# leptris-ruby#508 mints registered klasses at create: the node
|
|
168
|
+
# IS a contract-carrying identity (@native is self) — prime it
|
|
169
|
+
# in place instead of shelling a second wrapper (a raw mint
|
|
170
|
+
# reaching ==/adapter dies on the nil @context).
|
|
171
|
+
return native.prime_contract(native, context, adapter) if native.is_a?(Moxml::Node)
|
|
172
|
+
|
|
173
|
+
Wrappers::Element.new(native, context)
|
|
167
174
|
end
|
|
168
175
|
|
|
169
176
|
def create_text(content)
|
|
170
|
-
|
|
177
|
+
native = adapter.create_text(content, owner_doc: @native)
|
|
178
|
+
return native.prime_contract(native, context, adapter) if native.is_a?(Moxml::Node)
|
|
179
|
+
|
|
180
|
+
Wrappers::Text.new(native, context)
|
|
171
181
|
end
|
|
172
182
|
|
|
173
183
|
def create_cdata(content)
|
|
@@ -227,9 +237,13 @@ module Moxml
|
|
|
227
237
|
raise Error, "Document already has a root element"
|
|
228
238
|
else
|
|
229
239
|
adapter.add_child(@native, node.native)
|
|
230
|
-
# Refresh native for adapters where identity changes (e.g., LibXML doc.root=)
|
|
240
|
+
# Refresh native for adapters where identity changes (e.g., LibXML doc.root=).
|
|
241
|
+
# Native equality via the adapter, not wrapper !=: re-mints can
|
|
242
|
+
# arrive without context primed (1.9.373 actual_native), and
|
|
243
|
+
# wrapper == dispatches through the adapter (issue #409-class).
|
|
231
244
|
refreshed = adapter.actual_native(node.native, @native)
|
|
232
|
-
node.refresh_native!(refreshed) if refreshed &&
|
|
245
|
+
node.refresh_native!(refreshed) if refreshed &&
|
|
246
|
+
!adapter.same_node?(refreshed, node.native)
|
|
233
247
|
end
|
|
234
248
|
node.parent_node = self
|
|
235
249
|
invalidate_children_cache!
|
data/lib/moxml/node.rb
CHANGED
|
@@ -847,6 +847,18 @@ module Moxml
|
|
|
847
847
|
def self.wrap_with(node, context, adapter)
|
|
848
848
|
return nil if node.nil?
|
|
849
849
|
|
|
850
|
+
# Identity natives ARE the canonical wrapper (leptris C-mint):
|
|
851
|
+
# @native is self, so canonicalization, the cache probes and
|
|
852
|
+
# the re-mint below all answer the receiver. One native ivar
|
|
853
|
+
# read decides; shells (@native != self) take the full path.
|
|
854
|
+
# The C mint leaves the node un-primed (@context unset) — prime
|
|
855
|
+
# on first wrap. Conditionally: an already-primed identity (a
|
|
856
|
+
# walked node) must not lose @parent_node to a re-prime.
|
|
857
|
+
if node.is_a?(Node) && node.instance_variable_get(:@native).equal?(node)
|
|
858
|
+
node.prime_contract(node, context, adapter) unless node.instance_variable_defined?(:@context)
|
|
859
|
+
return node
|
|
860
|
+
end
|
|
861
|
+
|
|
850
862
|
# One C node may have several Ruby representations (leptris
|
|
851
863
|
# native/binding duality, moxml#311); every wrap resolves to
|
|
852
864
|
# the canonical one so memo holders share one invalidation
|
data/lib/moxml/version.rb
CHANGED
|
@@ -130,4 +130,33 @@ RSpec.describe "leptris Identity contract reads" do
|
|
|
130
130
|
expect(el.text).to eq("v")
|
|
131
131
|
end
|
|
132
132
|
end
|
|
133
|
+
|
|
134
|
+
describe "children" do
|
|
135
|
+
it "answers without the adapter children crossing (children_read)" do
|
|
136
|
+
unless Leptris::XML::NativeNode.method_defined?(:children_read)
|
|
137
|
+
skip "binding #{Leptris::VERSION} has no children_read face"
|
|
138
|
+
end
|
|
139
|
+
doc = ctx.parse("<root><item>v</item></root>")
|
|
140
|
+
el = doc.root.children.first
|
|
141
|
+
|
|
142
|
+
singleton = Moxml::Adapter::Leptris.singleton_class
|
|
143
|
+
singleton.send(:alias_method, :children_without_walk, :children)
|
|
144
|
+
singleton.send(:define_method, :children) { |*| raise "adapter children crossing" }
|
|
145
|
+
begin
|
|
146
|
+
kids = el.children
|
|
147
|
+
expect(kids.to_a.map(&:name)).to eq(["text"])
|
|
148
|
+
ensure
|
|
149
|
+
singleton.send(:alias_method, :children, :children_without_walk)
|
|
150
|
+
singleton.send(:remove_method, :children_without_walk)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "returns a NodeSet carrying the contract wrappers" do
|
|
155
|
+
doc = ctx.parse("<root><item>v</item></root>")
|
|
156
|
+
kids = doc.root.children.first.children
|
|
157
|
+
expect(kids).to be_a(Moxml::NodeSet)
|
|
158
|
+
expect(kids.to_a.first.name).to eq("text")
|
|
159
|
+
expect(kids.to_a.first.content).to eq("v")
|
|
160
|
+
end
|
|
161
|
+
end
|
|
133
162
|
end
|
|
@@ -10,13 +10,20 @@ require "spec_helper"
|
|
|
10
10
|
RSpec.describe "detached text content= names the cause" do
|
|
11
11
|
let(:ctx) { Moxml.new(:leptris) }
|
|
12
12
|
|
|
13
|
-
it "
|
|
13
|
+
it "answers removed text nodes: mutable on fixed engines (1.9.374+), named error before" do
|
|
14
14
|
doc = ctx.parse("<r><a>x</a><b>y</b></r>")
|
|
15
15
|
text = doc.root.children[0].children[0]
|
|
16
16
|
text.remove
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
if Gem::Version.new(Leptris::VERSION) >= Gem::Version.new("1.9.374")
|
|
19
|
+
# leptris/leptris#1785 fixed the engine root cause: detached
|
|
20
|
+
# text resolves its owning document, so the mutation succeeds.
|
|
21
|
+
text.content = "z"
|
|
22
|
+
expect(text.content).to eq("z")
|
|
23
|
+
else
|
|
24
|
+
expect { text.content = "z" }
|
|
25
|
+
.to raise_error(Moxml::Error, /detached \(parentless\).*issue #401/)
|
|
26
|
+
end
|
|
20
27
|
end
|
|
21
28
|
|
|
22
29
|
it "keeps created-unattached nodes mutable" do
|
|
@@ -23,6 +23,18 @@ RSpec.describe "parse_html collect_errors forwarding" do
|
|
|
23
23
|
expect(diags.first).to have_key(:message)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
# Issue #408: the #1771 reader fields ride the diag hash — the
|
|
27
|
+
# token code and position canon's HtmlValidator maps onto
|
|
28
|
+
# validation errors (nil where the engine has no token code).
|
|
29
|
+
it "carries the token code and position (issue #408)" do
|
|
30
|
+
doc = ctx.parse_html(src, collect_errors: true)
|
|
31
|
+
eof = doc.parse_diagnostics.find { |d| d[:message].include?("EOF") }
|
|
32
|
+
expect(eof).not_to be_nil
|
|
33
|
+
expect(eof[:code]).to eq("eof-in-tag")
|
|
34
|
+
expect(eof[:line]).to eq(1)
|
|
35
|
+
expect(eof[:column]).to eq(1)
|
|
36
|
+
end
|
|
37
|
+
|
|
26
38
|
it "keeps the default lane silent (no option, no diagnostics)" do
|
|
27
39
|
doc = ctx.parse_html(src)
|
|
28
40
|
expect(doc.parse_diagnostics).to be_empty
|
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.148
|
|
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-20 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
|