moxml 0.5.27 → 0.5.29

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: b9d156e4f8dfd71fc334b5626690f325a78abf95496bf19b6141bf649beeee58
4
- data.tar.gz: 5f3c24b3211e8381d14a34c7c23781a808e2f55b1d73d06af3be0fe7d3640a75
3
+ metadata.gz: df62f6f6a27c7ff63c2893ae7d85547d09645d059b187fd4235111c18ad2467f
4
+ data.tar.gz: a9c0e9ee0a3f7f2c8775ba5dfa49563e048ef3e2aac506eaef9135c2a2390139
5
5
  SHA512:
6
- metadata.gz: 1c5cd5d01ece3fcced5049148d2df776a1dcb9b1e95b0fe9a145caef05c0e2d5080b32214c7eb9f8f7bb277412827b413b3c59e38a49162241208944791a6ed6
7
- data.tar.gz: 33faf5c9269c3c74a6def61ef3933a9bdc2caedf484e0144ca1c8e6465aeb1640bb47c3f44ce9c777ed5bbe8e9682efd840cb87d4eceedac764f4c496519fff3
6
+ metadata.gz: 705a551074ea195562d7af4b726a388e8c3517ec4366cf9dff3d3b0828a79f46b6a50d79501d9c5ed1df871855f90bc5d5b69b35e22eb006b558e26c0da5626b
7
+ data.tar.gz: 2e5b4b7da9018d41434c23c70ea4ced99e626ef813dc92b6d2e1df8d9f8f81e89ac536dd52f684bd95de9fcb610ba673eaa9914de5048a491ee0f217bdfa1975
@@ -91,17 +91,23 @@ module Moxml
91
91
  # reparsing truncates at it. Valid markup NEVER puts "<"
92
92
  # before a non-name, non-delimiter character, so this is
93
93
  # deterministic in markup segments (CDATA is literal and
94
- # excluded by the segment walker).
94
+ # excluded by the segment walker). One class-anchored scan:
95
+ # multi-char literal probes cost ~30x a single regex pass on
96
+ # CRuby (measured include?("< ") at 31µs vs this regex at
97
+ # 64µs for 14 probes' worth of coverage on 32KB).
95
98
  RAW_LT_RE = /<(?![A-Za-z_:\/?!])/
99
+ RAW_LT_TRIGGER_RE = /<[^A-Za-z_:\/?!]/
96
100
 
97
101
  def normalize_serialization(xml, options)
98
102
  # The libxml2-layout serializer (>= 1.9.42) keeps attribute
99
103
  # apostrophes literal; older engines escaped them.
100
104
  needs_apos = !LIBXML2_LAYOUT_PARITY && xml.include?("&apos;")
101
105
  needs_expand = options[:expand_empty] && xml.include?("/>")
102
- # One scan each: both regexes fail fast on clean output.
103
- needs_amp = xml.match?(RAW_AMP_RE)
104
- needs_lt = xml.match?(RAW_LT_RE)
106
+ # Corruption guards: the 1-char ampersand probe is ~1µs
107
+ # (memchr-class); both regexes only run when their cheap
108
+ # necessary condition holds.
109
+ needs_amp = xml.include?("&") && xml.match?(RAW_AMP_RE)
110
+ needs_lt = xml.match?(RAW_LT_TRIGGER_RE)
105
111
  return xml unless needs_apos || needs_expand || needs_amp || needs_lt
106
112
 
107
113
  out = +""
@@ -288,6 +288,19 @@ module Moxml
288
288
  return set_attribute_namespace(node, namespace) if node.is_a?(::Leptris::XML::Attr)
289
289
 
290
290
  element = node
291
+ if namespace.nil?
292
+ # Nil-clear contract (issue #164): undeclare the default
293
+ # namespace (xmlns="") and drop any name prefix — the
294
+ # element then reports no namespace, matching the other
295
+ # adapters. Elements that carried a PREFIX cannot fully
296
+ # detach the engine's namespace link (leptris-ruby#132):
297
+ # the name is unqualified and the undeclaration added, but
298
+ # the serializer may re-attach the old prefix until the
299
+ # engine grows a clear entry.
300
+ element.name = element.name.split(":", 2)[-1] if element.name.include?(":")
301
+ element.default_namespace = ""
302
+ return element
303
+ end
291
304
  prefix = namespace.is_a?(String) ? nil : namespace.prefix
292
305
  uri = namespace.is_a?(String) ? namespace : namespace.href
293
306
  if prefix.nil? || prefix.empty?
@@ -170,6 +170,12 @@ module Moxml
170
170
  end
171
171
 
172
172
  def set_namespace(element, ns_or_string)
173
+ if ns_or_string.nil?
174
+ element.namespace_name = nil
175
+ set_attribute(element, "xmlns", "")
176
+ return element
177
+ end
178
+
173
179
  element.namespace_name = ns_or_string.to_s
174
180
  element
175
181
  end
@@ -149,6 +149,12 @@ module Moxml
149
149
  def set_namespace(element, ns)
150
150
  return element unless element.is_a?(::Ox::Element) || element.is_a?(::Ox::Node)
151
151
 
152
+ if ns.nil?
153
+ set_attribute(element, "xmlns", "")
154
+ element.name = element.name.to_s.sub(/\A(?:\w+):/, "")
155
+ return element
156
+ end
157
+
152
158
  prefix = ns.prefix
153
159
  # attributes don't have attributes but can have a namespace prefix
154
160
  if element.is_a?(::Ox::Element)
@@ -485,6 +485,14 @@ module Moxml
485
485
 
486
486
  # add a namespace prefix to the element name AND a namespace definition
487
487
  def set_namespace(element, ns)
488
+ if ns.nil?
489
+ # Nil-clear contract (issue #164): the XML Namespaces 1.0
490
+ # undeclaration, plus an unqualified name.
491
+ element.add_namespace(nil, "") if element.is_a?(::REXML::Element)
492
+ element.name = element.name.to_s.sub(/\A(?:\w+):/, "")
493
+ return element
494
+ end
495
+
488
496
  prefix = ns.name.to_s.empty? ? "xmlns" : ns.name.to_s
489
497
  if element.is_a?(::REXML::Element)
490
498
  element.add_namespace(prefix,
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.27"
4
+ VERSION = "0.5.29"
5
5
  end
@@ -280,6 +280,12 @@ RSpec.shared_examples "Moxml::Namespace" do
280
280
  expect(root.namespace.uri).to eq("http://example.org/1")
281
281
  expect(child.namespace.uri).to eq("http://example.org/2")
282
282
  end
283
+
284
+ it "accepts namespace = nil without raising (issue #164)" do
285
+ cleared = context.parse(%(<r xmlns="urn:clear"><c>t</c></r>))
286
+ child = cleared.root.children.first
287
+ expect { child.namespace = nil }.not_to raise_error
288
+ end
283
289
  end
284
290
  end
285
291
  end
@@ -273,6 +273,25 @@ RSpec.describe Moxml::Adapter::Leptris do
273
273
  end
274
274
  end
275
275
 
276
+ it "clears the namespace with nil — full contract (issue #164)" do
277
+ doc = ctx.parse(%(<r xmlns="urn:clear"><c>t</c></r>))
278
+ child = doc.root.children.first
279
+ child.namespace = nil
280
+ expect(child.namespace).to be_nil
281
+ expect(doc.to_xml).to include(%(<c xmlns="">t</c>))
282
+ reparsed = ctx.parse(doc.to_xml)
283
+ expect(reparsed.root.children.first.namespace).to be_nil
284
+ end
285
+
286
+ it "does not raise clearing a namespace from a prefixed element (issue #164)" do
287
+ # The engine cannot detach a prefix element's namespace link
288
+ # (leptris-ruby#132); the name is unqualified and the
289
+ # undeclaration added meanwhile.
290
+ doc = ctx.parse(%(<r xmlns:p="urn:p"><p:c>t</p:c></r>))
291
+ child = doc.root.children.first
292
+ expect { child.namespace = nil }.not_to raise_error
293
+ end
294
+
276
295
  it "refuses noblanks on readonly parses while the strip path is active" do
277
296
  # The strip mutates the tree; readonly freezes it at parse. On
278
297
  # bindings whose engine flag is libxml2-safe (probe), the flag
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.27
4
+ version: 0.5.29
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-03 00:00:00.000000000 Z
11
+ date: 2026-09-04 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