moxml 0.5.137 → 0.5.138

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: 43beff2cbabe6c8800099bc8f56a76b4c2510c6158cb632984cd31c8c3893a24
4
- data.tar.gz: e7d0540ce919c428a26b55805f0c78fc25de976c08f2639a83161eab6ffc3b4d
3
+ metadata.gz: 20467d4026cf631dd85798aaaa47a53526e1a6c8e26b1583ac896e56bb0fa4b4
4
+ data.tar.gz: 3d3829939332dfd24a1d552308d10fd7c0dbbe6eed998a5292ec5a5d82d53def
5
5
  SHA512:
6
- metadata.gz: 8e33f465faabc1da5cc0c7233f70e60592e0080cb4897b81d98eb1b073fedeea21160d2252170e322c8a66b33f4899e1752f96b30555ba0959f4b6f2e46ecd8b
7
- data.tar.gz: 29dde54bd4af8259207a708175309fdf9ad7fd3c4702eacf1e77a92d5fbfbd03b0993d2730c70ddb0c5b30edc41bbc4871e73d10334814fd51b813f2e6905629
6
+ metadata.gz: 353a615d10c7755a3d03a5dd13988eb57b1f30b2bc68f1506e68bc48d053deca42fd968cbba67f5df10cd14dff7da757bfd15f89a734cd1e607e34d3b319eb0a
7
+ data.tar.gz: bfa917028338657c3c09d91f32a964a653f4e50bc8d5abd1d0c7cf404d284d2efb216b0e62df1f17f1d731ef636c6d60bbe27a5f587ccca2c45bb65aa93c8e39
@@ -36,7 +36,13 @@ module Moxml
36
36
  # Nokogiri-shaped contract, epilog anchoring included (issue
37
37
  # #130). Built documents reflect their parts immediately
38
38
  # since leptris-ruby 1.9.32 (leptris-ruby#91).
39
- children.concat(doc.children.to_a)
39
+ # leptris-ruby#459 lists the declaration among the binding
40
+ # children; the Nokogiri-shaped facade contract never lists
41
+ # it (the declaration is a preamble part, not a child node)
42
+ # — filtered at the listing seam.
43
+ children.concat(
44
+ doc.children.to_a.grep_v(::Leptris::XML::Declaration),
45
+ )
40
46
 
41
47
  texts = attachments.get(doc, :document_text)
42
48
  children.concat(texts) if texts
@@ -156,6 +162,13 @@ module Moxml
156
162
  next
157
163
  end
158
164
 
165
+ # leptris-ruby#459 exposes the declaration as a
166
+ # Document#children entry; the preamble above owns the
167
+ # emission (a second line here duplicates it — and the
168
+ # 1.9.360 0-arity Declaration#to_xml raises under the
169
+ # options-passing call).
170
+ next if child.is_a?(::Leptris::XML::Declaration)
171
+
159
172
  parts << raw_serialize(child, options) << "\n"
160
173
  end
161
174
 
@@ -244,6 +244,9 @@ module Moxml
244
244
  return NATIVE_NODE_TYPE_MAP[node.node_type] || :unknown
245
245
  end
246
246
  return :document if node.is_a?(::LibXML::XML::Document)
247
+ # Attr is a separate class (not ::Node) — attribute-terminal
248
+ # XPath results mint as plain Node without this.
249
+ return :attribute if node.is_a?(::LibXML::XML::Attr)
247
250
 
248
251
  wrapper_type = WRAPPER_NODE_TYPE_MAP[node.class]
249
252
  return wrapper_type if wrapper_type
@@ -949,6 +952,16 @@ module Moxml
949
952
  native_node = unpatch_node(node)
950
953
  return [] unless native_node
951
954
 
955
+ # libxml-ruby's Document#find returns EMPTY for relative
956
+ # paths (nokogiri/libxml2 evaluate them fine): a relative
957
+ # expression from the document node is equivalent to the
958
+ # absolute form — the document's only name-testable child
959
+ # is the root — so rewrite instead of losing the match.
960
+ if native_node.is_a?(::LibXML::XML::Document) &&
961
+ expression.match?(/\A[A-Za-z_*@]/)
962
+ expression = "/#{expression}"
963
+ end
964
+
952
965
  # Build namespace context for LibXML
953
966
  # LibXML requires ALL prefixes in the XPath to be registered
954
967
  ns_context = build_xpath_namespaces(native_node, namespaces)
@@ -626,7 +626,11 @@ module Moxml
626
626
  end
627
627
 
628
628
  def xpath(node, expression, namespaces = nil)
629
- result = node.xpath(expression, namespaces)
629
+ # An explicit bindings hash — even EMPTY — makes nokogiri
630
+ # register a prefix context, which disables libxml2's
631
+ # literal xmlns: default-namespace handling. Only pass the
632
+ # hash when the caller actually bound something.
633
+ result = namespaces.nil? || namespaces.empty? ? node.xpath(expression) : node.xpath(expression, namespaces)
630
634
  # Adapter contract: Array<native> | LazyNodeSet | scalar
631
635
  # (count(), string-length(), boolean functions return
632
636
  # scalars). The native node-set passes through lazily —
@@ -604,10 +604,13 @@ module Moxml
604
604
  end
605
605
 
606
606
  def xpath(node, expression, namespaces = {})
607
+ # XPath.match for every expression: get_elements only
608
+ # handles simple child-name paths — attribute terminals,
609
+ # absolute paths and predicates silently degrade there.
607
610
  if namespaces && !namespaces.empty?
608
- ::REXML::XPath.match(node, expression, namespaces)
611
+ ::REXML::XPath.match(node, expression, namespaces).to_a
609
612
  else
610
- node.get_elements(expression).to_a
613
+ ::REXML::XPath.match(node, expression).to_a
611
614
  end
612
615
  rescue ::REXML::ParseException => e
613
616
  raise Moxml::XPathError.new(
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.137"
4
+ VERSION = "0.5.138"
5
5
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Cross-adapter XPath idioms that nokogiri consumers rely on:
6
+ #
7
+ # - The literal xmlns: prefix resolves against the default namespace
8
+ # (the libxml2 quirk that is nokogiri's documented way to match
9
+ # default-namespaced elements; leptris carries it since engine
10
+ # 1.9.359 / leptris-ruby#469; moxml's own engine always has).
11
+ # - Attribute-terminal steps work in every path position
12
+ # (leptris-ruby#458's table: //a/@id, //@id, r/a/@id,
13
+ # //*[f()]/@id — fixed by the engine's VM path work).
14
+ RSpec.describe "XPath idioms across adapters" do
15
+ let(:default_ns_xml) do
16
+ '<svgmap xmlns="http://www.example.org/doc"><target href="a"/></svgmap>'
17
+ end
18
+ let(:plain_xml) { '<r><a id="x"><b/></a></r>' }
19
+
20
+ Moxml::Adapter::AVAILABLE_ADAPTERS.each do |adapter_name|
21
+ next if adapter_name == :headed_ox # same engine as :ox
22
+
23
+ context "with #{adapter_name}" do
24
+ let(:ctx) { Moxml.new(adapter_name) }
25
+
26
+ before do
27
+ if adapter_name == :leptris &&
28
+ Gem::Version.new(Leptris::VERSION) < Gem::Version.new("1.9.359.0")
29
+ skip "binding #{Leptris::VERSION} predates the xmlns: fix (1.9.359)"
30
+ end
31
+ if adapter_name == :oga
32
+ # oga's own Ruby XPath engine has no xmlns: default-ns
33
+ # support (third-party; explicit bindings work).
34
+ skip "oga's XPath engine does not implement the xmlns: idiom"
35
+ end
36
+ end
37
+
38
+ it "matches default-namespaced elements via the xmlns: prefix" do
39
+ doc = ctx.parse(default_ns_xml)
40
+ targets = doc.root.xpath("./xmlns:target")
41
+ expect(targets.length).to eq(1)
42
+ expect(targets.first.name).to eq("target")
43
+ end
44
+
45
+ it "keeps explicit namespace bindings winning over the idiom" do
46
+ doc = ctx.parse(default_ns_xml)
47
+ targets = doc.root.xpath("./xmlns:target", "xmlns" => "urn:bogus")
48
+ expect(targets).to be_empty
49
+ end
50
+
51
+ it "resolves attribute-terminal steps in every path position" do
52
+ doc = ctx.parse(plain_xml)
53
+ expect(doc.xpath("//a/@id").first&.value).to eq("x")
54
+ expect(doc.xpath("//@id").first&.value).to eq("x")
55
+ expect(doc.xpath("r/a/@id").first&.value).to eq("x")
56
+ expect(
57
+ doc.xpath("//*[local-name()='a']/@id").first&.value,
58
+ ).to eq("x")
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # leptris-ruby#459 exposes the declaration as a Document#children
6
+ # entry (binding 1.9.360). The serialize child walk must skip it —
7
+ # the composed preamble owns the emission — or declaration-bearing
8
+ # round trips duplicate the line (1.9.359) or raise (1.9.360's
9
+ # 0-arity Declaration#to_xml under the options-passing call).
10
+ RSpec.describe "leptris document declaration child" do
11
+ let(:ctx) { Moxml.new(:leptris) }
12
+ let(:xml) { %(<?xml version="1.0" encoding="UTF-8"?>\n<documents></documents>) }
13
+
14
+ before do
15
+ unless Gem::Version.new(Leptris::VERSION) >= Gem::Version.new("1.9.360.0")
16
+ skip "binding #{Leptris::VERSION} predates the declaration child (1.9.360)"
17
+ end
18
+ end
19
+
20
+ it "round trips byte-identical with exactly one declaration" do
21
+ doc = ctx.parse(xml)
22
+ out = doc.to_xml
23
+
24
+ expect(out).to eq("#{xml}\n")
25
+ expect(out.scan("<?xml").length).to eq(1)
26
+ end
27
+
28
+ it "never lists the declaration among document children" do
29
+ doc = ctx.parse(xml)
30
+ first = doc.children.to_a
31
+ second = doc.children.to_a
32
+
33
+ expect(first.map(&:node_type)).to eq(%i[element])
34
+ expect(second.map(&:node_type)).to eq(first.map(&:node_type))
35
+ expect(doc.to_xml).to eq("#{xml}\n")
36
+ end
37
+
38
+ it "keeps declaration-less documents unchanged" do
39
+ doc = ctx.parse("<r/>")
40
+ expect(doc.to_xml).to eq("<r></r>\n")
41
+ end
42
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.137
4
+ version: 0.5.138
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -313,6 +313,7 @@ files:
313
313
  - spec/consistency/eol_normalization_spec.rb
314
314
  - spec/consistency/html_doctype_spec.rb
315
315
  - spec/consistency/round_trip_spec.rb
316
+ - spec/consistency/xpath_idioms_spec.rb
316
317
  - spec/consistency/xslt_spec.rb
317
318
  - spec/examples/README.md
318
319
  - spec/examples/attribute_examples_spec.rb
@@ -452,6 +453,7 @@ files:
452
453
  - spec/moxml/adapter/leptris_attribute_snapshot_spec.rb
453
454
  - spec/moxml/adapter/leptris_binding_cap_spec.rb
454
455
  - spec/moxml/adapter/leptris_doc_parts_spec.rb
456
+ - spec/moxml/adapter/leptris_document_declaration_spec.rb
455
457
  - spec/moxml/adapter/leptris_parse_skips_spec.rb
456
458
  - spec/moxml/adapter/leptris_record_driver_spec.rb
457
459
  - spec/moxml/adapter/leptris_sax_dup_attr_spec.rb