moxml 0.5.113 → 0.5.114

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c61331ea3536aa41170080eb6233643b3b48da707d268da9185ed11fbdcad87d
4
- data.tar.gz: ef1765b2e2651438c8d38012ea374e2a8b9fe6ebcaff63d2afaf476eb0f727ab
3
+ metadata.gz: 6cc0d0436680fb9c46169bc93e45197b20b71bf6970d737750e63fb110a5e75e
4
+ data.tar.gz: 8f71ca799b845b4fb28a977f6515ac1b702b6557259667c5b034f5b612ef6b67
5
5
  SHA512:
6
- metadata.gz: 2058949ca13a105d38f86b5c38acf3fb974ca9445faedda64ea7d857bf8f10561d5b6df42d7b56ede501d0fcb9186f0347349faee29dd66868b83ec47a94ac4c
7
- data.tar.gz: c7c7b4f0b0d529ec793e790072d2115fe6ff7c17a389e08a84abadc2ab324e058f4f49d1e5d5a6e1143b0f688ac893b5ebae083638f1482b9b526b99d7c636fd
6
+ metadata.gz: 1edf604a33151f0079608d52231a1486a252cda307f49fd958e05f41e3229394a952245159939c2f4dac67ecba9f612ef6c4718d7f450c3260e1f78fc4d83135
7
+ data.tar.gz: 92aeae211e0b8abae0ce569c9dbd650f3b2b8426e975a42e3341317ebeb6c09928adbb0265b2ea7cdb726ca40d9ac376be1e537200d2166255fc30f365afd7ed
data/.rubocop.yml CHANGED
@@ -23,6 +23,10 @@ AllCops:
23
23
  # the zero-allocation streaming form (issue #143). One parameter per
24
24
  # record key is the interface; a shorter list would need a wrapper
25
25
  # object, which is the allocation the API exists to avoid.
26
+ Metrics/BlockLength:
27
+ Exclude:
28
+ - 'Rakefile'
29
+
26
30
  Metrics/ParameterLists:
27
31
  Exclude:
28
32
  - 'lib/moxml/materializer.rb'
data/Rakefile CHANGED
@@ -190,7 +190,12 @@ namespace :spec do
190
190
 
191
191
  desc "Run unit tests only"
192
192
  RSpec::Core::RakeTask.new(:unit) do |t|
193
- t.pattern = "spec/unit/**/*_spec.rb"
193
+ # Unit specs live under spec/moxml/ — the stale glob matched
194
+ # nothing (spec/unit/ holds only the rexml_isolated_test.rb
195
+ # entity-era investigation artifact), so this leg ran zero
196
+ # examples and hid the moxml suite from the fast gate.
197
+ t.pattern = "spec/moxml/**/*_spec.rb"
198
+ t.exclude_pattern = "spec/moxml/adapter/**/*_spec.rb"
194
199
  end
195
200
 
196
201
  desc "Run adapter tests only"
@@ -1900,6 +1900,16 @@ module Moxml
1900
1900
  # contract); every other kind carries no cross-document
1901
1901
  # identity worth preserving, so reconstruct it in the parent's
1902
1902
  # document through the binding's create faces.
1903
+ def contains_foreign_child?(parent_nn, children)
1904
+ parent_doc = NN_DOCUMENT.bind_call(parent_nn)
1905
+ return false if parent_doc.nil?
1906
+
1907
+ children.any? do |child|
1908
+ child_doc = NN_DOCUMENT.bind_call(child)
1909
+ child_doc && !child_doc.equal?(parent_doc)
1910
+ end
1911
+ end
1912
+
1903
1913
  def rebuild_foreign_child(parent_nn, child_nn)
1904
1914
  doc = NN_DOCUMENT.bind_call(parent_nn)
1905
1915
  rebuilt =
@@ -2348,6 +2358,32 @@ module Moxml
2348
2358
  Gem::Version.new(::Leptris::VERSION) >= Gem::Version.new("1.9.290.0")
2349
2359
 
2350
2360
  def replace_children(node, new_children)
2361
+ # Cross-pool children: the binding's bulk face adopts per
2362
+ # node and fails on mixed kinds (inner_xml= with
2363
+ # text+elements died with Memory allocation failed —
2364
+ # pre-existing). Rebuild foreign children in this pool
2365
+ # after the clear — fragment children carry no identity
2366
+ # worth preserving; same-document children keep the plain
2367
+ # add, in order.
2368
+ if NATIVE_MUTATIONS_COHERENT && node.is_a?(NN) &&
2369
+ new_children.all?(NN) &&
2370
+ contains_foreign_child?(node, new_children)
2371
+ ::Leptris::XML::FFI.check_status(
2372
+ ::Leptris::XML::FFI.leptris_element_remove_children(
2373
+ ::FFI::Pointer.new(node.address),
2374
+ ),
2375
+ )
2376
+ node_doc = NN_DOCUMENT.bind_call(node)
2377
+ new_children.each do |child|
2378
+ child_doc = NN_DOCUMENT.bind_call(child)
2379
+ if child_doc && !child_doc.equal?(node_doc)
2380
+ rebuild_foreign_child(node, child)
2381
+ else
2382
+ NN_ADD_CHILD.bind_call(node, child)
2383
+ end
2384
+ end
2385
+ return node
2386
+ end
2351
2387
  if NATIVE_READ_LAYER
2352
2388
  node = to_binding(node)
2353
2389
  new_children = new_children.map { |child| to_binding(child) }
data/lib/moxml/element.rb CHANGED
@@ -400,14 +400,17 @@ module Moxml
400
400
  end
401
401
 
402
402
  def inner_xml=(xml)
403
- wrapper = "_moxml_inner_#{Process.pid}_#{object_id}"
404
- doc = context.parse("<#{wrapper}>#{xml}</#{wrapper}>")
403
+ # Fragment parse, not a wrapper element around interpolated
404
+ # input (TODO.refactor/03): the fragment face is the same one
405
+ # add_child(String) uses, so semantics match and no wrapper
406
+ # name can collide with content.
407
+ nodes = context.parse_fragment(xml).to_a
405
408
  @text = nil
406
409
  # Whole-list replacement rides the children generation like
407
410
  # children= does (moxml#310) — text and elements memos across
408
411
  # wrappers recompute on next read.
409
412
  context.bump_children_generation
410
- adapter.replace_children(@native, doc.root.children.map(&:native))
413
+ adapter.replace_children(@native, nodes.map(&:native))
411
414
  invalidate_children_cache!
412
415
  end
413
416
 
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.113"
4
+ VERSION = "0.5.114"
5
5
  end
@@ -89,6 +89,25 @@ RSpec.shared_examples "Moxml::Element" do
89
89
  expect(element.children.first.name).to eq("child")
90
90
  expect(element.children.first.text).to eq("text")
91
91
  end
92
+
93
+ it "sets inner XML with mixed top-level nodes" do
94
+ # Structure, not serialization spelling: Ox renders bare
95
+ # text as <s> nodes and rexml/oga expand empty tags.
96
+ element.inner_xml = "a<c1/>b<c2 v='1'/>c"
97
+ kids = element.children
98
+ expect(
99
+ kids.map { |c| c.element? ? :element : :content },
100
+ ).to eq(%i[content element content element content])
101
+ expect(kids[0].text).to eq("a")
102
+ expect(kids[2].text).to eq("b")
103
+ expect(kids[4].text).to eq("c")
104
+ expect(kids[3]["v"]).to eq("1")
105
+ end
106
+
107
+ it "sets inner XML containing element-like text and entities" do
108
+ element.inner_xml = "<c>&lt;/element&gt; &amp; &#65;</c>"
109
+ expect(element.children.first.text).to eq("</element> & A")
110
+ end
92
111
  end
93
112
 
94
113
  describe "node manipulation" do
@@ -168,9 +168,10 @@ RSpec.describe Moxml::Node do
168
168
  end
169
169
  end
170
170
 
171
- it "raises for node types without a path representation" do
171
+ it "locates non-element nodes through typed node tests" do
172
172
  text = doc.root.children.first.children.first
173
- expect { text.path }.to raise_error(Moxml::NotImplementedError, /element and document/)
173
+ expect(text.path).to end_with("/text()")
174
+ expect(doc.at_xpath(text.path)).to eq(text)
174
175
  end
175
176
  end
176
177
 
@@ -3,12 +3,18 @@
3
3
  require "spec_helper"
4
4
  require "moxml/adapter/shared_examples/adapter_contract"
5
5
 
6
- RSpec.describe Moxml::Adapter::Oga, if: RUBY_ENGINE == "opal" do
7
- around do |example|
8
- Moxml.with_config(:oga, true, "UTF-8") do
9
- example.run
6
+ # The describe resolves the adapter constant at load time — under
7
+ # MRI nothing loads the Opal compat shim, so the whole describe must
8
+ # sit behind the engine check (the `if:` metadata alone still
9
+ # evaluates the constant reference).
10
+ if RUBY_ENGINE == "opal"
11
+ RSpec.describe Moxml::Adapter::Oga do
12
+ around do |example|
13
+ Moxml.with_config(:oga, true, "UTF-8") do
14
+ example.run
15
+ end
10
16
  end
11
- end
12
17
 
13
- it_behaves_like "xml adapter"
18
+ it_behaves_like "xml adapter"
19
+ end
14
20
  end
@@ -164,7 +164,11 @@ RSpec.describe "SAX/DOM entity parity" do
164
164
  # double-escaping on round-trip.
165
165
  doc = ctx.create_document
166
166
  el = doc.create_element("doc")
167
- el.add_child("a & b")
167
+ # Programmatically-built: a literal Text node, no markup parse.
168
+ # (add_child with a String operand is MARKUP since the nokogiri
169
+ # compat round — a bare "&" is ill-formed there and each
170
+ # engine recovers differently.)
171
+ el.add_child(doc.create_text("a & b"))
168
172
  doc.add_child(el)
169
173
 
170
174
  serialized = doc.to_xml.sub(/\A<\?[^>]*\?>\s*/, "").strip
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.113
4
+ version: 0.5.114
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-06 00:00:00.000000000 Z
11
+ date: 2026-10-07 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