moxml 0.5.153 → 0.5.154
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/.github/workflows/performance-release.yml +9 -5
- data/docs/_pages/divergences.adoc +9 -0
- data/lib/moxml/adapter/leptris/document_parts.rb +10 -2
- data/lib/moxml/adapter/leptris.rb +68 -3
- data/lib/moxml/lazy_bulk_rows.rb +4 -4
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter/leptris_contract_reads_spec.rb +3 -0
- data/spec/moxml/adapter/leptris_document_children_older_binding_spec.rb +39 -0
- data/spec/moxml/adapter/leptris_spec.rb +6 -0
- data/spec/moxml/adapter/leptris_xpath_bulk_rows_spec.rb +2 -2
- 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: 05a5f1f4e5475fe30f9d821cc2630f8c6d9d95aa38fa5d2e46e25e51605d1504
|
|
4
|
+
data.tar.gz: c47ee78ffb35d119b4a5c470e27e4dd0b9d3c4dfedba2c7d8bcc3aa17901d01b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 012cb442259cd2496eacf056b75e8ed050203eb92673b608b379305de4215f8c00c65b83b72d70027c7b2adbd9fd90d5eb2f41701de62305e291f7118fa9a44a
|
|
7
|
+
data.tar.gz: ceed6daf6824f3de929c14586b130fb014afc66b261bb3c76a92be0e09e1e33e4e41c8729f7a091dc80e71dbe36a67ffdffd66d454406e5a9a47494d634b294e
|
|
@@ -78,11 +78,15 @@ jobs:
|
|
|
78
78
|
env:
|
|
79
79
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
80
80
|
run: |
|
|
81
|
-
# The
|
|
82
|
-
# NOT "latest"
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
# The notes target is the release created by the triggering
|
|
82
|
+
# run — NOT "latest" (a rerun of an old run would mis-aim)
|
|
83
|
+
# and NOT the gemspec at the pinned tree (the checkout is
|
|
84
|
+
# the pre-bump tree; the bump lands on main during the
|
|
85
|
+
# release run itself).
|
|
86
|
+
TRIGGERED_AT="${{ github.event.workflow_run.created_at }}"
|
|
87
|
+
TAG=$(gh api "repos/${GITHUB_REPOSITORY}/releases" \
|
|
88
|
+
--jq "map(select(.created_at >= \"${TRIGGERED_AT}\")) | sort_by(.created_at) | last | .tag_name")
|
|
89
|
+
TAG=${TAG:-$(gh release view --json tagName --jq .tagName)}
|
|
86
90
|
echo "Reporting against $TAG"
|
|
87
91
|
gh release view "$TAG" --json body --jq .body > /tmp/body.md
|
|
88
92
|
ruby -e '
|
|
@@ -137,3 +137,12 @@ parse (the engine's instruction content is trimmed); leading
|
|
|
137
137
|
separator runs collapse per the model. Pinned in the taxonomy
|
|
138
138
|
differential (`pi trailing whitespace`, face `pi_content`).
|
|
139
139
|
|===
|
|
140
|
+
|
|
141
|
+
| D-21
|
|
142
|
+
| leptris (bindings before 1.9.360)
|
|
143
|
+
| The binding exposes no `Leptris::XML::Declaration` class, so the
|
|
144
|
+
document-children declaration filter resolves conditionally and is a
|
|
145
|
+
no-op there (declaration nodes cannot occur). On such bindings the
|
|
146
|
+
class is also absent for consumers. Pinned in
|
|
147
|
+
`spec/moxml/adapter/leptris_document_children_older_binding_spec.rb`.
|
|
148
|
+
|===
|
|
@@ -39,9 +39,17 @@ module Moxml
|
|
|
39
39
|
# leptris-ruby#459 lists the declaration among the binding
|
|
40
40
|
# children; the Nokogiri-shaped facade contract never lists
|
|
41
41
|
# it (the declaration is a preamble part, not a child node)
|
|
42
|
-
# — filtered at the listing seam.
|
|
42
|
+
# — filtered at the listing seam. The constant is resolved
|
|
43
|
+
# conditionally: leptris releases before the declaration
|
|
44
|
+
# node class landed (e.g. 1.9.345) carry no Declaration at
|
|
45
|
+
# all, and referencing the constant unconditionally raises
|
|
46
|
+
# NameError on every document-children listing.
|
|
47
|
+
declaration_class =
|
|
48
|
+
if ::Leptris::XML.const_defined?(:Declaration, false)
|
|
49
|
+
::Leptris::XML::Declaration
|
|
50
|
+
end
|
|
43
51
|
children.concat(
|
|
44
|
-
doc.children.to_a.grep_v(
|
|
52
|
+
doc.children.to_a.grep_v(declaration_class),
|
|
45
53
|
)
|
|
46
54
|
|
|
47
55
|
texts = attachments.get(doc, :document_text)
|
|
@@ -749,7 +749,16 @@ module Moxml
|
|
|
749
749
|
@children = nil
|
|
750
750
|
@children_gen = generation
|
|
751
751
|
end
|
|
752
|
-
@children ||= if doc_entity_bearing?
|
|
752
|
+
@children ||= if doc_entity_bearing? ||
|
|
753
|
+
!::Moxml::Adapter::Leptris
|
|
754
|
+
.children_read_splice_safe?
|
|
755
|
+
# Splice-unsafe builds (1.9.381.0,
|
|
756
|
+
# leptris-ruby#545): the cached face's
|
|
757
|
+
# child list no longer reflects
|
|
758
|
+
# splices — the adapter crossing is
|
|
759
|
+
# the correctness path, and the
|
|
760
|
+
# no-crossing contract is probed off
|
|
761
|
+
# for those builds.
|
|
753
762
|
super
|
|
754
763
|
else
|
|
755
764
|
NodeSet.new(children_read, ctx, self,
|
|
@@ -1276,6 +1285,29 @@ module Moxml
|
|
|
1276
1285
|
end
|
|
1277
1286
|
end
|
|
1278
1287
|
|
|
1288
|
+
# 1.9.381.0: the children_read face stopped reflecting
|
|
1289
|
+
# post-splice state — Element#add_next_sibling lands the node
|
|
1290
|
+
# in the tree (native bulk reads and walk_columnar see it) but
|
|
1291
|
+
# leaves the face's child list stale. Probe the exact repro
|
|
1292
|
+
# once: prime the face, splice, and require children_read to
|
|
1293
|
+
# see the inserted node. Unsafe builds take the adapter
|
|
1294
|
+
# children path; fixed builds restore the fast path
|
|
1295
|
+
# automatically.
|
|
1296
|
+
def children_read_splice_safe?
|
|
1297
|
+
return @children_read_splice_safe unless @children_read_splice_safe.nil?
|
|
1298
|
+
|
|
1299
|
+
@children_read_splice_safe = begin
|
|
1300
|
+
native_doc = ::Leptris::XML::Document.parse("<r><a/>\n<b/></r>")
|
|
1301
|
+
root = native_doc.root
|
|
1302
|
+
root.children_read.to_a # prime the face's child list
|
|
1303
|
+
a = root.children.to_a.first
|
|
1304
|
+
a.add_next_sibling(native_doc.create_element("x"))
|
|
1305
|
+
root.children_read.to_a.map { |n| n.name.to_s } == %w[a x b]
|
|
1306
|
+
rescue StandardError
|
|
1307
|
+
false
|
|
1308
|
+
end
|
|
1309
|
+
end
|
|
1310
|
+
|
|
1279
1311
|
def xpath_index_document(node)
|
|
1280
1312
|
return node if node.is_a?(::Leptris::XML::Document)
|
|
1281
1313
|
|
|
@@ -2719,6 +2751,7 @@ module Moxml
|
|
|
2719
2751
|
doc = NN_DOCUMENT.bind_call(parent_nn)
|
|
2720
2752
|
src = to_binding(child_nn)
|
|
2721
2753
|
installed = copy_binding_subtree(doc, src)
|
|
2754
|
+
advance_mutation_version(parent_nn)
|
|
2722
2755
|
::Leptris::XML::FFI.check_status(
|
|
2723
2756
|
::Leptris::XML::FFI.leptris_element_append_child(
|
|
2724
2757
|
::FFI::Pointer.new(parent_nn.address), installed.c_ptr
|
|
@@ -2767,6 +2800,7 @@ module Moxml
|
|
|
2767
2800
|
end
|
|
2768
2801
|
# The raw engine add rejects binding-family children —
|
|
2769
2802
|
# append through the FFI face like structural_adopt.
|
|
2803
|
+
advance_mutation_version(parent_nn)
|
|
2770
2804
|
::Leptris::XML::FFI.check_status(
|
|
2771
2805
|
::Leptris::XML::FFI.leptris_element_append_child(
|
|
2772
2806
|
::FFI::Pointer.new(parent_nn.address), rebuilt.c_ptr
|
|
@@ -3090,7 +3124,13 @@ module Moxml
|
|
|
3090
3124
|
|
|
3091
3125
|
# Non-element receivers: raw-FFI anchor (issue #245, same
|
|
3092
3126
|
# shape as add_next_sibling).
|
|
3127
|
+
# leptris-ruby#543: the raw-FFI splice is a wrapper-visible
|
|
3128
|
+
# mutation — advance the binding document's version or the
|
|
3129
|
+
# walk-read memos warmed before it serve stale children.
|
|
3130
|
+
node.document&.advance_version if
|
|
3131
|
+
node.document.respond_to?(:advance_version)
|
|
3093
3132
|
adopt_for_sibling_insert(new_node, node)
|
|
3133
|
+
advance_mutation_version(node)
|
|
3094
3134
|
::Leptris::XML::FFI.check_status(
|
|
3095
3135
|
::Leptris::XML::FFI.leptris_element_insert_before(
|
|
3096
3136
|
node.c_ptr, new_node.c_ptr
|
|
@@ -3134,7 +3174,13 @@ module Moxml
|
|
|
3134
3174
|
# node — route through the FFI entry directly, keeping the
|
|
3135
3175
|
# binding's namespace-lift adoption for element inserts
|
|
3136
3176
|
# (issue #245).
|
|
3177
|
+
# leptris-ruby#543: the raw-FFI splice is a wrapper-visible
|
|
3178
|
+
# mutation — advance the binding document's version or the
|
|
3179
|
+
# walk-read memos warmed before it serve stale children.
|
|
3180
|
+
node.document&.advance_version if
|
|
3181
|
+
node.document.respond_to?(:advance_version)
|
|
3137
3182
|
adopt_for_sibling_insert(new_node, node)
|
|
3183
|
+
advance_mutation_version(node)
|
|
3138
3184
|
::Leptris::XML::FFI.check_status(
|
|
3139
3185
|
::Leptris::XML::FFI.leptris_element_insert_after(
|
|
3140
3186
|
node.c_ptr, new_node.c_ptr
|
|
@@ -3154,6 +3200,23 @@ module Moxml
|
|
|
3154
3200
|
::Leptris::XML::Element.lift_namespaces_for_adoption(new_node, scope)
|
|
3155
3201
|
end
|
|
3156
3202
|
|
|
3203
|
+
# The binding's version-stamped read memos (the #530
|
|
3204
|
+
# walk-reads, the C content/attribute memos) key their
|
|
3205
|
+
# validity on the owning Document's mutation version. The
|
|
3206
|
+
# gated Ruby faces advance it (Node#ensure_writable!) and
|
|
3207
|
+
# the C-bound faces bump in C — a RAW FFI write bypasses
|
|
3208
|
+
# both, so every raw write in this adapter bumps here,
|
|
3209
|
+
# before the call, mirroring ensure_writable!'s
|
|
3210
|
+
# conservative discipline (a failed write merely discards
|
|
3211
|
+
# memos). Without it, a children memo warmed before the
|
|
3212
|
+
# insert serves the pre-insert tree forever.
|
|
3213
|
+
def advance_mutation_version(node)
|
|
3214
|
+
doc = node.respond_to?(:document) ? node.document : nil
|
|
3215
|
+
doc.advance_version if doc.respond_to?(:advance_version)
|
|
3216
|
+
rescue ::Leptris::XML::Error
|
|
3217
|
+
nil
|
|
3218
|
+
end
|
|
3219
|
+
|
|
3157
3220
|
def remove(node)
|
|
3158
3221
|
drop_columnar_children(node)
|
|
3159
3222
|
node = to_binding(node) if NATIVE_READ_LAYER
|
|
@@ -3225,12 +3288,14 @@ module Moxml
|
|
|
3225
3288
|
prev = node.previous_sibling
|
|
3226
3289
|
node.unlink
|
|
3227
3290
|
if prev.is_a?(::Leptris::XML::Element)
|
|
3291
|
+
advance_mutation_version(prev)
|
|
3228
3292
|
::Leptris::XML::FFI.check_status(
|
|
3229
3293
|
::Leptris::XML::FFI.leptris_element_insert_after(
|
|
3230
3294
|
prev.c_ptr, new_node.c_ptr
|
|
3231
3295
|
),
|
|
3232
3296
|
)
|
|
3233
3297
|
else
|
|
3298
|
+
advance_mutation_version(parent)
|
|
3234
3299
|
::Leptris::XML::FFI.check_status(
|
|
3235
3300
|
::Leptris::XML::FFI.leptris_element_append_child(
|
|
3236
3301
|
parent.c_ptr, new_node.c_ptr
|
|
@@ -3645,8 +3710,8 @@ module Moxml
|
|
|
3645
3710
|
# nil and leaves the pointer — fall through to the scalar
|
|
3646
3711
|
# wrap, whose free owns it.
|
|
3647
3712
|
if BULK_XPATH_ROWS &&
|
|
3648
|
-
|
|
3649
|
-
|
|
3713
|
+
::Leptris::XML::FFI.leptris_xpath_result_type(result_ptr) ==
|
|
3714
|
+
::Leptris::XML::FFI::XPATH_NODESET
|
|
3650
3715
|
# The one-crossing mint is deferred: a caller that only
|
|
3651
3716
|
# asks #size never pays for the rows (#527). The holder
|
|
3652
3717
|
# runs Document#xpath_result_rows_bulk on first read.
|
data/lib/moxml/lazy_bulk_rows.rb
CHANGED
|
@@ -37,10 +37,10 @@ module Moxml
|
|
|
37
37
|
materialize[index]
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def each
|
|
41
|
-
return enum_for(:each) unless
|
|
40
|
+
def each(&block)
|
|
41
|
+
return enum_for(:each) unless block
|
|
42
42
|
|
|
43
|
-
materialize.each
|
|
43
|
+
materialize.each(&block)
|
|
44
44
|
self
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -51,7 +51,7 @@ module Moxml
|
|
|
51
51
|
def self.finalizer(address)
|
|
52
52
|
proc do
|
|
53
53
|
::Leptris::XML::FFI.leptris_xpath_result_free(
|
|
54
|
-
::FFI::Pointer.new(address)
|
|
54
|
+
::FFI::Pointer.new(address),
|
|
55
55
|
)
|
|
56
56
|
end
|
|
57
57
|
end
|
data/lib/moxml/version.rb
CHANGED
|
@@ -136,6 +136,9 @@ RSpec.describe "leptris Identity contract reads" do
|
|
|
136
136
|
unless Leptris::XML::NativeNode.method_defined?(:children_read)
|
|
137
137
|
skip "binding #{Leptris::VERSION} has no children_read face"
|
|
138
138
|
end
|
|
139
|
+
unless Moxml::Adapter::Leptris.children_read_splice_safe?
|
|
140
|
+
skip "binding #{Leptris::VERSION} lacks the splice-safe children_read face (leptris-ruby#545) — the crossing is the correctness path there"
|
|
141
|
+
end
|
|
139
142
|
doc = ctx.parse("<root><item>v</item></root>")
|
|
140
143
|
el = doc.root.children.first
|
|
141
144
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# The document-children listing filters the declaration node, but on
|
|
6
|
+
# leptris releases that predate the Declaration class entirely
|
|
7
|
+
# (< 1.9.360) referencing the constant raised NameError on every
|
|
8
|
+
# document-children enumeration — the first `doc.children` on any
|
|
9
|
+
# document. The filter must resolve the constant conditionally.
|
|
10
|
+
RSpec.describe "leptris document children on older leptris" do
|
|
11
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
12
|
+
|
|
13
|
+
it "lists document children when the release has no Declaration class" do
|
|
14
|
+
leptris_xml = Leptris::XML
|
|
15
|
+
had_declaration = leptris_xml.const_defined?(:Declaration, false)
|
|
16
|
+
declaration = had_declaration ? leptris_xml.const_get(:Declaration) : nil
|
|
17
|
+
# stub_const cannot express a MISSING constant; simulating the
|
|
18
|
+
# older binding requires the constant to be absent
|
|
19
|
+
if had_declaration
|
|
20
|
+
leptris_xml.send(:remove_const, :Declaration) # rubocop:disable RSpec/RemoveConst
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
doc = ctx.create_document
|
|
25
|
+
doc.add_child(doc.create_element("root"))
|
|
26
|
+
expect(doc.children.map(&:name)).to eq(%w[root])
|
|
27
|
+
ensure
|
|
28
|
+
leptris_xml.const_set(:Declaration, declaration) if had_declaration
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "keeps filtering the declaration on releases that have the class" do
|
|
33
|
+
skip "binding #{Leptris::VERSION} has no Declaration class (D-21)" unless
|
|
34
|
+
Leptris::XML.const_defined?(:Declaration, false)
|
|
35
|
+
|
|
36
|
+
doc = ctx.parse(%(<?xml version="1.0" encoding="UTF-8"?>\n<documents></documents>))
|
|
37
|
+
expect(doc.children.map(&:node_type)).to eq(%i[element])
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -490,6 +490,12 @@ RSpec.describe Moxml::Adapter::Leptris do
|
|
|
490
490
|
# The identity map must hold wrappers weakly: parse-and-drop
|
|
491
491
|
# workloads otherwise pin wrappers, natives, and (via the
|
|
492
492
|
# binding finalizer never running) the C subtrees.
|
|
493
|
+
# Splice-unsafe builds (leptris-ruby#545) walk through the
|
|
494
|
+
# adapter crossing, whose wrappers ride the strong registry —
|
|
495
|
+
# the weak-release contract is probed off there.
|
|
496
|
+
unless described_class.children_read_splice_safe?
|
|
497
|
+
skip "binding #{Leptris::VERSION} requires a splice-safe children_read face (leptris-ruby#545)"
|
|
498
|
+
end
|
|
493
499
|
xml = %(<r>#{Array.new(50) { |i| "<e id=\"i#{i}\">x</e>" }.join}</r>)
|
|
494
500
|
ctx = Moxml.new(:leptris)
|
|
495
501
|
count = -> {
|
|
@@ -72,12 +72,12 @@ RSpec.describe "leptris xpath bulk rows routing" do
|
|
|
72
72
|
# leptris-ruby#527: the count row is one evaluation plus a size
|
|
73
73
|
# read — it must not pay the per-row mint.
|
|
74
74
|
describe "row materialization is deferred" do
|
|
75
|
-
def bulk_calls
|
|
75
|
+
def bulk_calls(&block)
|
|
76
76
|
calls = 0
|
|
77
77
|
tp = TracePoint.new(:c_call) do |t|
|
|
78
78
|
calls += 1 if t.method_id == :xpath_result_rows_bulk
|
|
79
79
|
end
|
|
80
|
-
tp.enable
|
|
80
|
+
tp.enable(&block)
|
|
81
81
|
calls
|
|
82
82
|
end
|
|
83
83
|
|
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.154
|
|
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-23 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
|
|
@@ -486,6 +486,7 @@ files:
|
|
|
486
486
|
- spec/moxml/adapter/leptris_contract_reads_spec.rb
|
|
487
487
|
- spec/moxml/adapter/leptris_detached_text_spec.rb
|
|
488
488
|
- spec/moxml/adapter/leptris_doc_parts_spec.rb
|
|
489
|
+
- spec/moxml/adapter/leptris_document_children_older_binding_spec.rb
|
|
489
490
|
- spec/moxml/adapter/leptris_document_declaration_spec.rb
|
|
490
491
|
- spec/moxml/adapter/leptris_inbody_comment_spec.rb
|
|
491
492
|
- spec/moxml/adapter/leptris_parse_lint_spec.rb
|