moxml 0.1.3 → 0.1.6

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.rubocop_todo.yml +48 -20
  4. data/Gemfile +3 -0
  5. data/LICENSE.md +33 -0
  6. data/README.adoc +95 -23
  7. data/lib/moxml/adapter/base.rb +20 -2
  8. data/lib/moxml/adapter/customized_ox/attribute.rb +29 -0
  9. data/lib/moxml/adapter/customized_ox/namespace.rb +34 -0
  10. data/lib/moxml/adapter/customized_ox/text.rb +12 -0
  11. data/lib/moxml/adapter/customized_rexml/formatter.rb +195 -0
  12. data/lib/moxml/adapter/nokogiri.rb +4 -2
  13. data/lib/moxml/adapter/oga.rb +25 -9
  14. data/lib/moxml/adapter/ox.rb +238 -92
  15. data/lib/moxml/adapter/rexml.rb +462 -0
  16. data/lib/moxml/adapter.rb +1 -1
  17. data/lib/moxml/attribute.rb +2 -2
  18. data/lib/moxml/cdata.rb +0 -4
  19. data/lib/moxml/comment.rb +0 -4
  20. data/lib/moxml/config.rb +1 -1
  21. data/lib/moxml/context.rb +2 -2
  22. data/lib/moxml/doctype.rb +1 -5
  23. data/lib/moxml/document.rb +1 -1
  24. data/lib/moxml/document_builder.rb +14 -18
  25. data/lib/moxml/element.rb +4 -3
  26. data/lib/moxml/namespace.rb +5 -1
  27. data/lib/moxml/node.rb +17 -2
  28. data/lib/moxml/node_set.rb +8 -1
  29. data/lib/moxml/processing_instruction.rb +0 -4
  30. data/lib/moxml/text.rb +0 -4
  31. data/lib/moxml/version.rb +1 -1
  32. data/lib/ox/node.rb +9 -0
  33. data/spec/fixtures/small.xml +1 -0
  34. data/spec/moxml/adapter/rexml_spec.rb +14 -0
  35. data/spec/moxml/all_with_adapters_spec.rb +2 -3
  36. data/spec/support/shared_examples/builder.rb +19 -2
  37. data/spec/support/shared_examples/cdata.rb +7 -5
  38. data/spec/support/shared_examples/declaration.rb +17 -4
  39. data/spec/support/shared_examples/doctype.rb +2 -1
  40. data/spec/support/shared_examples/document.rb +10 -0
  41. data/spec/support/shared_examples/edge_cases.rb +9 -3
  42. data/spec/support/shared_examples/element.rb +5 -1
  43. data/spec/support/shared_examples/examples/benchmark_spec.rb +51 -0
  44. data/spec/support/shared_examples/examples/memory.rb +30 -17
  45. data/spec/support/shared_examples/examples/readme_examples.rb +5 -0
  46. data/spec/support/shared_examples/examples/thread_safety.rb +2 -0
  47. data/spec/support/shared_examples/examples/xpath.rb +34 -3
  48. data/spec/support/shared_examples/integration.rb +6 -2
  49. data/spec/support/shared_examples/namespace.rb +16 -0
  50. data/spec/support/shared_examples/node.rb +4 -0
  51. data/spec/support/shared_examples/node_set.rb +20 -0
  52. data/spec/support/shared_examples/processing_instruction.rb +1 -1
  53. data/spec/support/shared_examples/text.rb +2 -1
  54. data/spec/support/shared_examples/xml_adapter.rb +169 -7
  55. metadata +13 -3
data/lib/moxml/node.rb CHANGED
@@ -7,11 +7,17 @@ module Moxml
7
7
  class Node
8
8
  include XmlUtils
9
9
 
10
+ TYPES = %i[
11
+ element text cdata comment processing_instruction document
12
+ declaration doctype namespace attribute unknown
13
+ ].freeze
14
+
10
15
  attr_reader :native, :context
11
16
 
12
17
  def initialize(native, context)
13
- @native = native
14
18
  @context = context
19
+ # @native = adapter.patch_node(native)
20
+ @native = native
15
21
  end
16
22
 
17
23
  def document
@@ -23,7 +29,10 @@ module Moxml
23
29
  end
24
30
 
25
31
  def children
26
- NodeSet.new(adapter.children(@native), context)
32
+ NodeSet.new(
33
+ adapter.children(@native).map { adapter.patch_node(_1, @native) },
34
+ context
35
+ )
27
36
  end
28
37
 
29
38
  def next_sibling
@@ -79,6 +88,12 @@ module Moxml
79
88
  self.class == other.class && @native == other.native
80
89
  end
81
90
 
91
+ TYPES.each do |node_type|
92
+ define_method "#{node_type}?" do
93
+ adapter.node_type(native) == node_type
94
+ end
95
+ end
96
+
82
97
  def self.wrap(node, context)
83
98
  return nil if node.nil?
84
99
 
@@ -42,7 +42,6 @@ module Moxml
42
42
  def size
43
43
  nodes.size
44
44
  end
45
-
46
45
  alias length size
47
46
 
48
47
  def to_a
@@ -53,6 +52,14 @@ module Moxml
53
52
  self.class.new(nodes + other.nodes, context)
54
53
  end
55
54
 
55
+ def ==(other)
56
+ self.class == other.class &&
57
+ length == other.length &&
58
+ nodes.each_with_index.all? do |node, index|
59
+ Node.wrap(node, context) == other[index]
60
+ end
61
+ end
62
+
56
63
  def text
57
64
  map(&:text).join
58
65
  end
@@ -17,9 +17,5 @@ module Moxml
17
17
  def content=(new_content)
18
18
  adapter.set_processing_instruction_content(@native, new_content.to_s)
19
19
  end
20
-
21
- def processing_instruction?
22
- true
23
- end
24
20
  end
25
21
  end
data/lib/moxml/text.rb CHANGED
@@ -9,9 +9,5 @@ module Moxml
9
9
  def content=(text)
10
10
  adapter.set_text_content(@native, normalize_xml_value(text))
11
11
  end
12
-
13
- def text?
14
- true
15
- end
16
12
  end
17
13
  end
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.1.3"
4
+ VERSION = "0.1.6"
5
5
  end
data/lib/ox/node.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ox/node"
4
+
5
+ module Ox
6
+ class Node
7
+ attr_accessor :parent
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ <root><item>data</item></root>
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rexml"
4
+ require "moxml/adapter/rexml"
5
+
6
+ RSpec.describe Moxml::Adapter::Rexml do
7
+ around do |example|
8
+ Moxml.with_config(:rexml, true, "UTF-8") do
9
+ example.run
10
+ end
11
+ end
12
+
13
+ it_behaves_like "xml adapter"
14
+ end
@@ -25,12 +25,11 @@ RSpec.describe "Test all shared examples" do
25
25
  "README Examples",
26
26
  "XPath Examples",
27
27
  "Memory Usage Examples",
28
- "Thread Safety Examples"
28
+ "Thread Safety Examples",
29
+ "Performance Examples"
29
30
  ]
30
31
 
31
32
  Moxml::Adapter::AVALIABLE_ADAPTERS.each do |adapter_name|
32
- # [:nokogiri].each do |adapter_name|
33
- # [:oga].each do |adapter_name|
34
33
  context "with #{adapter_name}" do
35
34
  around do |example|
36
35
  Moxml.with_config(adapter_name) do
@@ -5,8 +5,8 @@ RSpec.shared_examples "Moxml::Builder" do
5
5
  let(:builder) { Moxml::Builder.new(context) }
6
6
 
7
7
  describe "#document" do
8
- it "creates a well-formed document" do
9
- doc = builder.build do
8
+ let(:doc) do
9
+ builder.build do
10
10
  declaration version: "1.0", encoding: "UTF-8"
11
11
  element "root" do
12
12
  element "child", id: "1" do
@@ -14,12 +14,29 @@ RSpec.shared_examples "Moxml::Builder" do
14
14
  end
15
15
  end
16
16
  end
17
+ end
17
18
 
19
+ it "creates a well-formed document" do
18
20
  xml = doc.to_xml
19
21
  expect(xml).to include('<?xml version="1.0" encoding="UTF-8"?>')
20
22
  expect(xml).to include("<root>")
21
23
  expect(xml).to include('<child id="1">content</child>')
22
24
  expect(xml).to include("</root>")
23
25
  end
26
+
27
+ it "creates the same document through direct manipulation" do
28
+ doc2 = context.create_document
29
+ doc2.add_child(doc2.create_declaration("1.0", "UTF-8"))
30
+ root = doc2.create_element("root")
31
+ child = doc2.create_element("child")
32
+ child["id"] = "1"
33
+ text = doc2.create_text("content")
34
+
35
+ child.add_child(text)
36
+ root.add_child(child)
37
+ doc2.add_child(root)
38
+
39
+ expect(doc.to_xml).to eq(doc2.to_xml)
40
+ end
24
41
  end
25
42
  end
@@ -23,6 +23,11 @@ RSpec.shared_examples "Moxml::Cdata" do
23
23
  cdata.content = nil
24
24
  expect(cdata.content).to eq("")
25
25
  end
26
+
27
+ it "preserves whitespace" do
28
+ cdata.content = " spaced content\n\t"
29
+ expect(cdata.content).to eq(" spaced content\n\t")
30
+ end
26
31
  end
27
32
 
28
33
  describe "serialization" do
@@ -31,6 +36,8 @@ RSpec.shared_examples "Moxml::Cdata" do
31
36
  end
32
37
 
33
38
  it "escapes CDATA end marker" do
39
+ # pending for Ox: https://github.com/ohler55/ox/issues/377
40
+ pending "Ox doesn't escape the end token" if context.config.adapter_name == :ox
34
41
  cdata.content = "content]]>more"
35
42
  expect(cdata.to_xml).to eq("<![CDATA[content]]]]><![CDATA[>more]]>")
36
43
  end
@@ -39,11 +46,6 @@ RSpec.shared_examples "Moxml::Cdata" do
39
46
  cdata.content = "< > & \" '"
40
47
  expect(cdata.to_xml).to include("< > & \" '")
41
48
  end
42
-
43
- it "preserves whitespace" do
44
- cdata.content = " spaced content\n\t"
45
- expect(cdata.content).to eq(" spaced content\n\t")
46
- end
47
49
  end
48
50
 
49
51
  describe "node operations" do
@@ -36,6 +36,7 @@ RSpec.shared_examples "Moxml::Declaration" do
36
36
  end
37
37
 
38
38
  it "normalizes encoding" do
39
+ pending("Rexml Encoding upcases the string") if Moxml.new.config.adapter.name.include?("Rexml")
39
40
  declaration.encoding = "utf-8"
40
41
  expect(declaration.encoding).to eq("utf-8")
41
42
  end
@@ -64,17 +65,26 @@ RSpec.shared_examples "Moxml::Declaration" do
64
65
 
65
66
  describe "serialization" do
66
67
  it "formats complete declaration" do
67
- expect(declaration.to_xml).to eq('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')
68
+ doc.add_child(declaration)
69
+ expect(doc.to_xml.strip).to end_with('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')
68
70
  end
69
71
 
70
72
  it "formats minimal declaration with empty encoding" do
71
73
  decl = doc.create_declaration("1.0", nil)
72
- expect(decl.to_xml).to eq('<?xml version="1.0"?>')
74
+ if context.config.adapter_name == :rexml
75
+ # REXML sets a default encoding when a declaration is added to the document
76
+ expect(decl.to_xml.strip).to eq('<?xml version="1.0"?>')
77
+ else
78
+ # Ox cannot serialize standalone declaration
79
+ doc.add_child(decl)
80
+ expect(doc.to_xml.strip).to end_with('<?xml version="1.0"?>')
81
+ end
73
82
  end
74
83
 
75
84
  it "formats declaration with encoding only" do
76
85
  decl = doc.create_declaration("1.0", "UTF-8")
77
- expect(decl.to_xml).to eq('<?xml version="1.0" encoding="UTF-8"?>')
86
+ doc.add_child(decl)
87
+ expect(doc.to_xml.strip).to end_with('<?xml version="1.0" encoding="UTF-8"?>')
78
88
  end
79
89
  end
80
90
 
@@ -84,7 +94,10 @@ RSpec.shared_examples "Moxml::Declaration" do
84
94
  expect(doc.to_xml).to start_with("<?xml")
85
95
  end
86
96
 
87
- it "removes from document", skip: "The document contains a default declaration" do
97
+ it "removes from document" do
98
+ if Moxml.new.config.adapter.name.match?(/Nokogiri|Rexml|Ox/)
99
+ pending("The document contains a default declaration")
100
+ end
88
101
  doc.add_child(declaration)
89
102
  declaration.remove
90
103
  expect(doc.to_xml).not_to include("<?xml")
@@ -17,7 +17,8 @@ RSpec.shared_examples "Moxml::Doctype" do
17
17
 
18
18
  describe "serialization" do
19
19
  it "wraps content in doctype markers" do
20
- expect(doctype.to_xml).to eq(
20
+ doc.add_child(doctype)
21
+ expect(doc.to_xml.strip).to end_with(
21
22
  '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
22
23
  )
23
24
  end
@@ -106,5 +106,15 @@ RSpec.shared_examples "Moxml::Document" do
106
106
  "</root>"
107
107
  )
108
108
  end
109
+
110
+ it "prevents multiple roots" do
111
+ xml =
112
+ <<~XML
113
+ <?xml version="1.0" encoding="UTF-8"?>
114
+ <root/><another_root/>
115
+ XML
116
+
117
+ expect { context.parse(xml) }.to raise_error(Moxml::Error)
118
+ end
109
119
  end
110
120
  end
@@ -31,6 +31,7 @@ RSpec.shared_examples "Moxml Edge Cases" do
31
31
 
32
32
  describe "malformed content handling" do
33
33
  it "handles CDATA with nested markers" do
34
+ pending "Ox doesn't escape the end token" if context.config.adapter_name == :ox
34
35
  cdata_text = "]]>]]>]]>"
35
36
  doc = context.create_document
36
37
  cdata = doc.create_cdata(cdata_text)
@@ -76,6 +77,7 @@ RSpec.shared_examples "Moxml Edge Cases" do
76
77
 
77
78
  describe "namespace edge cases" do
78
79
  it "handles default namespace changes" do
80
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
79
81
  xml = <<~XML
80
82
  <root xmlns="http://default1.org">
81
83
  <child xmlns="http://default2.org">
@@ -86,10 +88,12 @@ RSpec.shared_examples "Moxml Edge Cases" do
86
88
 
87
89
  doc = context.parse(xml)
88
90
  grandchild = doc.at_xpath("//xmlns:grandchild", "xmlns" => "")
89
- expect(grandchild.namespace.uri).to eq("")
91
+ expect(grandchild.namespaces.first.uri).to eq("")
90
92
  end
91
93
 
92
94
  it "handles recursive namespace definitions" do
95
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
96
+
93
97
  xml = <<~XML
94
98
  <root xmlns:a="http://a.org">
95
99
  <a:child xmlns:a="http://b.org">
@@ -106,6 +110,8 @@ RSpec.shared_examples "Moxml Edge Cases" do
106
110
 
107
111
  describe "attribute edge cases" do
108
112
  it "handles attributes with same local name but different namespaces" do
113
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
114
+
109
115
  xml = <<~XML
110
116
  <root xmlns:a="http://a.org" xmlns:b="http://b.org">
111
117
  <element a:id="1" b:id="2"/>
@@ -143,13 +149,13 @@ RSpec.shared_examples "Moxml Edge Cases" do
143
149
  current = doc.create_element("root")
144
150
  doc.add_child(current)
145
151
 
146
- 1000.times do |i|
152
+ 10.times do |i|
147
153
  nested = doc.create_element("nested#{i}")
148
154
  current.add_child(nested)
149
155
  current = nested
150
156
  end
151
157
 
152
- expect(doc.to_xml).to include("<nested999>")
158
+ expect(doc.to_xml).to include("<nested9>")
153
159
  end
154
160
 
155
161
  it "handles large number of siblings" do
@@ -6,6 +6,10 @@ RSpec.shared_examples "Moxml::Element" do
6
6
  let(:doc) { context.create_document }
7
7
  let(:element) { doc.create_element("test") }
8
8
 
9
+ it "identifies as element node" do
10
+ expect(element).to be_element
11
+ end
12
+
9
13
  describe "name handling" do
10
14
  it "gets name" do
11
15
  expect(element.name).to eq("test")
@@ -55,7 +59,7 @@ RSpec.shared_examples "Moxml::Element" do
55
59
  end
56
60
 
57
61
  it "sets namespace" do
58
- ns = element.add_namespace("x", "http://example.org").namespace
62
+ ns = element.add_namespace("x", "http://example.org").namespaces.first
59
63
  element.namespace = ns
60
64
  expect(element.namespace).to eq(ns)
61
65
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark"
4
+ require "benchmark/ips"
5
+
6
+ RSpec.shared_examples "Performance Examples" do
7
+ let(:context) { Moxml.new }
8
+
9
+ let(:large_xml) do
10
+ xml = "<root>\n"
11
+ 1000.times do |i|
12
+ xml += "<item id='#{i}'><name>Test #{i}</name><value>#{i}</value></item>\n"
13
+ end
14
+ xml += "</root>"
15
+ xml
16
+ end
17
+
18
+ it "measures parsing performance" do
19
+ doc = nil
20
+
21
+ report = Benchmark.ips do |x|
22
+ x.config(time: 5, warmup: 2)
23
+
24
+ x.report("Parser") do
25
+ doc = context.parse(large_xml)
26
+ end
27
+
28
+ x.report("Serializer") do
29
+ _ = doc.to_xml
30
+ end
31
+
32
+ x.compare!
33
+ end
34
+
35
+ # first - parser, second - serializer
36
+ thresholds = {
37
+ nokogiri: [18, 1200],
38
+ oga: [12, 110],
39
+ rexml: [0, 60],
40
+ ox: [2, 2000]
41
+ }
42
+
43
+ report.entries.each_with_index do |entry, index|
44
+ puts "#{entry.label} performance: #{entry.ips.round(2)} ips"
45
+ threshold = thresholds[context.config.adapter_name][index]
46
+ message = "#{entry.label} performance below threshold: " \
47
+ "got #{entry.ips.round(2)} ips, expected >= #{threshold} ips"
48
+ expect(entry.ips).to be >= threshold, message
49
+ end
50
+ end
51
+ end
@@ -29,26 +29,39 @@ RSpec.shared_examples "Memory Usage Examples" do
29
29
  expect(doc.xpath("//large-node")).to be_empty
30
30
  end
31
31
 
32
- it "handles streaming processing" do
33
- # Create temp file
34
- file = Tempfile.new(["test", ".xml"])
35
- begin
36
- file.write("<root><item>data</item></root>")
37
- file.close
32
+ it "handles deeply nested nodes", skip: "Very flaky tests" do
33
+ doc = context.create_document
34
+ parent = doc
35
+
36
+ 1000.times do |i|
37
+ node = doc.create_element("el_#{i}")
38
+ node.add_child(doc.create_text("Content #{i}"))
39
+ parent.add_child(node)
40
+ parent = node
41
+ end
42
+
43
+ # Process and remove nodes
44
+ memory_before = GetProcessMem.new.bytes
45
+ doc = nil
46
+ GC.start # Oga fails without it
47
+ memory_after = GetProcessMem.new.bytes
48
+
49
+ expect(memory_after).to be <= memory_before
50
+ expect(doc).to be_nil
51
+ end
38
52
 
39
- # Process file
53
+ it "handles streaming processing" do
54
+ pending "Ox has a load_file method, but nothing about a stream" if context.config.adapter_name == :ox
55
+ # Process file
56
+ doc = nil
57
+ File.open("spec/fixtures/small.xml") do |f|
58
+ doc = context.parse(f)
59
+ expect(doc.at_xpath("//item").text).to eq("data")
40
60
  doc = nil
41
- File.open(file.path) do |f|
42
- doc = context.parse(f)
43
- expect(doc.at_xpath("//item").text).to eq("data")
44
- doc = nil
45
- end
46
- GC.start
47
-
48
- expect(doc).to be_nil
49
- ensure
50
- file.unlink
51
61
  end
62
+ GC.start
63
+
64
+ expect(doc).to be_nil
52
65
  end
53
66
  end
54
67
  end
@@ -110,6 +110,7 @@ RSpec.shared_examples "README Examples" do
110
110
  describe "Error handling example" do
111
111
  it "handles errors as shown in README" do
112
112
  context = Moxml.new
113
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
113
114
 
114
115
  expect do
115
116
  context.parse("<invalid>")
@@ -145,6 +146,10 @@ RSpec.shared_examples "README Examples" do
145
146
  def process(xml)
146
147
  @mutex.synchronize do
147
148
  doc = @context.parse(xml)
149
+ # Modify document
150
+ element = doc.create_element("test")
151
+ doc.root.add_child(element)
152
+ doc.root.children.first.remove
148
153
  doc.to_xml
149
154
  end
150
155
  end
@@ -20,6 +20,8 @@ RSpec.shared_examples "Thread Safety Examples" do
20
20
  end
21
21
 
22
22
  it "handles concurrent processing" do
23
+ pending "Ox doesn't have a native XPath" if Moxml.new.config.adapter_name == :ox
24
+
23
25
  processor = processor_class.new
24
26
  threads = []
25
27
  results = Queue.new
@@ -18,22 +18,53 @@ RSpec.shared_examples "XPath Examples" do
18
18
  end
19
19
 
20
20
  it "finds nodes by XPath" do
21
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
22
+
21
23
  books = doc.xpath("//book")
22
24
  expect(books.size).to eq(2)
23
- expect(books.map { |b| b["id"] }).to eq(%w[1 2])
25
+ expect(books.map { _1["id"] }).to eq(%w[1 2])
24
26
  end
25
27
 
26
28
  it "finds nodes with namespaces" do
29
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
30
+
27
31
  titles = doc.xpath("//dc:title",
28
32
  "dc" => "http://purl.org/dc/elements/1.1/")
29
33
  expect(titles.map(&:text)).to eq(%w[First Second])
30
34
  end
31
35
 
32
36
  it "finds nodes by attributes" do
37
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
38
+
33
39
  book = doc.at_xpath('//book[@id="2"]')
34
40
  expect(book).not_to be_nil
35
- expect(book.at_xpath(".//dc:title",
36
- "dc" => "http://purl.org/dc/elements/1.1/").text).to eq("Second")
41
+ title = book.at_xpath(".//dc:title",
42
+ "dc" => "http://purl.org/dc/elements/1.1/")
43
+ expect(title.text).to eq("Second")
44
+ end
45
+
46
+ it "finds nested attributes efficiently" do
47
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
48
+ # More efficient - specific path
49
+ titles1 = doc.xpath("//book/dc:title")
50
+
51
+ # Less efficient - requires full document scan
52
+ titles2 = doc.xpath("//dc:title")
53
+
54
+ # Most efficient - direct child access
55
+ titles3 = doc.root.xpath("./*/dc:title", "dc" => "http://purl.org/dc/elements/1.1/")
56
+
57
+ # Chain queries
58
+ nodes = doc.xpath("//book").map do |book|
59
+ # Each book is a mapped Moxml::Element
60
+ book.at_xpath(".//dc:title", "dc" => "http://purl.org/dc/elements/1.1/").native
61
+ end
62
+ titles4 = ::Moxml::NodeSet.new(nodes, doc.context)
63
+
64
+ expect(titles1.count).to eq(2)
65
+ expect(titles1).to eq(titles2)
66
+ expect(titles1).to eq(titles3)
67
+ expect(titles1).to eq(titles4)
37
68
  end
38
69
  end
39
70
  end
@@ -54,6 +54,7 @@ RSpec.shared_examples "Moxml Integration" do
54
54
  end
55
55
 
56
56
  it "handles xpath queries" do
57
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
57
58
  # Test XPath queries
58
59
  #
59
60
  # XPath with a default namespace is a problem
@@ -68,6 +69,7 @@ RSpec.shared_examples "Moxml Integration" do
68
69
 
69
70
  describe "namespace handling" do
70
71
  it "handles complex namespace scenarios" do
72
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
71
73
  xml = <<~XML
72
74
  <root xmlns="http://default.org" xmlns:a="http://a.org" xmlns:b="http://b.org">
73
75
  <child>
@@ -108,10 +110,12 @@ RSpec.shared_examples "Moxml Integration" do
108
110
  let(:doc) { context.parse("<root><a>1</a><b>2</b></root>") }
109
111
 
110
112
  it "handles complex modifications" do
113
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
114
+
111
115
  # Move nodes
112
116
  b_node = doc.at_xpath("//b")
113
117
  a_node = doc.at_xpath("//a")
114
- b_node.add_previous_sibling(a_node)
118
+ a_node.add_previous_sibling(b_node)
115
119
 
116
120
  # Add nodes
117
121
  c_node = doc.create_element("c")
@@ -125,7 +129,7 @@ RSpec.shared_examples "Moxml Integration" do
125
129
  b_node.add_child(doc.create_comment(" comment "))
126
130
  b_node.add_child(doc.create_cdata("<tag>"))
127
131
 
128
- expect(doc.root.children.map(&:name)).to eq(%w[a b c])
132
+ expect(doc.root.children.map(&:name)).to eq(%w[b c a])
129
133
  expect(doc.to_xml).to include(
130
134
  '<root id="main">',
131
135
  "<b>2<!-- comment --><![CDATA[<tag>]]></b>"
@@ -41,6 +41,22 @@ RSpec.shared_examples "Moxml::Namespace" do
41
41
  element.add_namespace(nil, "http://example.org")
42
42
  expect(element.namespaces.first.to_s).to eq('xmlns="http://example.org"')
43
43
  end
44
+
45
+ it "renders the same xml - a readme example" do
46
+ # chainable operations
47
+ element
48
+ .add_namespace("dc", "http://purl.org/dc/elements/1.1/")
49
+ .add_child(doc.create_text("content"))
50
+
51
+ # clear node type checking
52
+ node = doc.create_element("test")
53
+ if node.element?
54
+ node.add_namespace("dc", "http://purl.org/dc/elements/1.1/")
55
+ node.add_child(doc.create_text("content"))
56
+ end
57
+
58
+ expect(element.to_xml).to eq(node.to_xml)
59
+ end
44
60
  end
45
61
 
46
62
  describe "equality" do
@@ -96,12 +96,16 @@ RSpec.shared_examples "Moxml::Node" do
96
96
  let(:doc) { context.parse("<root><a><b>1</b></a><a><b>2</b></a></root>") }
97
97
 
98
98
  it "finds nodes by xpath" do
99
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
100
+
99
101
  nodes = doc.xpath("//b")
100
102
  expect(nodes.size).to eq(2)
101
103
  expect(nodes.map(&:text)).to eq(%w[1 2])
102
104
  end
103
105
 
104
106
  it "finds first node by xpath" do
107
+ pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
108
+
105
109
  node = doc.at_xpath("//b")
106
110
  expect(node.text).to eq("1")
107
111
  end