moxml 0.1.4 → 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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.rubocop_todo.yml +2 -2
- data/Gemfile +2 -0
- data/README.adoc +71 -21
- data/lib/moxml/adapter/base.rb +8 -2
- data/lib/moxml/adapter/customized_ox/attribute.rb +29 -0
- data/lib/moxml/adapter/customized_ox/namespace.rb +34 -0
- data/lib/moxml/adapter/customized_ox/text.rb +12 -0
- data/lib/moxml/adapter/customized_rexml/formatter.rb +3 -3
- data/lib/moxml/adapter/nokogiri.rb +3 -1
- data/lib/moxml/adapter/oga.rb +4 -2
- data/lib/moxml/adapter/ox.rb +238 -92
- data/lib/moxml/adapter/rexml.rb +10 -6
- data/lib/moxml/adapter.rb +1 -1
- data/lib/moxml/cdata.rb +0 -4
- data/lib/moxml/comment.rb +0 -4
- data/lib/moxml/context.rb +2 -2
- data/lib/moxml/doctype.rb +1 -5
- data/lib/moxml/document.rb +1 -1
- data/lib/moxml/document_builder.rb +1 -1
- data/lib/moxml/element.rb +2 -1
- data/lib/moxml/namespace.rb +4 -0
- data/lib/moxml/node.rb +17 -2
- data/lib/moxml/node_set.rb +8 -1
- data/lib/moxml/processing_instruction.rb +0 -4
- data/lib/moxml/text.rb +0 -4
- data/lib/moxml/version.rb +1 -1
- data/lib/ox/node.rb +9 -0
- data/spec/fixtures/small.xml +1 -0
- data/spec/moxml/all_with_adapters_spec.rb +2 -1
- data/spec/support/shared_examples/builder.rb +19 -2
- data/spec/support/shared_examples/cdata.rb +7 -5
- data/spec/support/shared_examples/declaration.rb +16 -4
- data/spec/support/shared_examples/doctype.rb +2 -1
- data/spec/support/shared_examples/document.rb +10 -0
- data/spec/support/shared_examples/edge_cases.rb +6 -0
- data/spec/support/shared_examples/element.rb +4 -0
- data/spec/support/shared_examples/examples/benchmark_spec.rb +51 -0
- data/spec/support/shared_examples/examples/memory.rb +30 -17
- data/spec/support/shared_examples/examples/readme_examples.rb +5 -0
- data/spec/support/shared_examples/examples/thread_safety.rb +2 -0
- data/spec/support/shared_examples/examples/xpath.rb +34 -3
- data/spec/support/shared_examples/integration.rb +4 -0
- data/spec/support/shared_examples/namespace.rb +16 -0
- data/spec/support/shared_examples/node.rb +4 -0
- data/spec/support/shared_examples/node_set.rb +20 -0
- data/spec/support/shared_examples/processing_instruction.rb +1 -1
- data/spec/support/shared_examples/text.rb +2 -1
- metadata +8 -2
data/lib/moxml/node_set.rb
CHANGED
@@ -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
|
data/lib/moxml/text.rb
CHANGED
data/lib/moxml/version.rb
CHANGED
data/lib/ox/node.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
<root><item>data</item></root>
|
@@ -25,7 +25,8 @@ 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|
|
@@ -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
|
-
|
9
|
-
|
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
|
@@ -65,17 +65,26 @@ RSpec.shared_examples "Moxml::Declaration" do
|
|
65
65
|
|
66
66
|
describe "serialization" do
|
67
67
|
it "formats complete declaration" do
|
68
|
-
|
68
|
+
doc.add_child(declaration)
|
69
|
+
expect(doc.to_xml.strip).to end_with('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')
|
69
70
|
end
|
70
71
|
|
71
72
|
it "formats minimal declaration with empty encoding" do
|
72
73
|
decl = doc.create_declaration("1.0", nil)
|
73
|
-
|
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
|
74
82
|
end
|
75
83
|
|
76
84
|
it "formats declaration with encoding only" do
|
77
85
|
decl = doc.create_declaration("1.0", "UTF-8")
|
78
|
-
|
86
|
+
doc.add_child(decl)
|
87
|
+
expect(doc.to_xml.strip).to end_with('<?xml version="1.0" encoding="UTF-8"?>')
|
79
88
|
end
|
80
89
|
end
|
81
90
|
|
@@ -85,7 +94,10 @@ RSpec.shared_examples "Moxml::Declaration" do
|
|
85
94
|
expect(doc.to_xml).to start_with("<?xml")
|
86
95
|
end
|
87
96
|
|
88
|
-
it "removes from document"
|
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
|
89
101
|
doc.add_child(declaration)
|
90
102
|
declaration.remove
|
91
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
|
-
|
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">
|
@@ -90,6 +92,8 @@ RSpec.shared_examples "Moxml Edge Cases" do
|
|
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"/>
|
@@ -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")
|
@@ -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
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
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 {
|
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
|
-
|
36
|
-
|
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,6 +110,8 @@ 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")
|
@@ -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
|
@@ -25,31 +25,47 @@ RSpec.shared_examples "Moxml::NodeSet" do
|
|
25
25
|
|
26
26
|
describe "enumeration" do
|
27
27
|
it "iterates over nodes" do
|
28
|
+
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
29
|
+
|
28
30
|
texts = []
|
29
31
|
nodes.each { |node| texts << node.text }
|
30
32
|
expect(texts).to eq(%w[First Second Third])
|
31
33
|
end
|
32
34
|
|
33
35
|
it "maps nodes" do
|
36
|
+
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
37
|
+
|
34
38
|
texts = nodes.map(&:text)
|
35
39
|
expect(texts).to eq(%w[First Second Third])
|
36
40
|
end
|
37
41
|
|
38
42
|
it "selects nodes" do
|
43
|
+
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
44
|
+
|
39
45
|
selected = nodes.select { |node| node.text.include?("i") }
|
40
46
|
expect(selected.size).to eq(2)
|
41
47
|
expect(selected.map(&:text)).to eq(%w[First Third])
|
42
48
|
end
|
49
|
+
|
50
|
+
it "compares nodes" do
|
51
|
+
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
52
|
+
|
53
|
+
expect(doc.xpath("//child")).to eq(doc.root.children)
|
54
|
+
end
|
43
55
|
end
|
44
56
|
|
45
57
|
describe "access methods" do
|
46
58
|
it "accesses by index" do
|
59
|
+
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
60
|
+
|
47
61
|
expect(nodes[0].text).to eq("First")
|
48
62
|
expect(nodes[1].text).to eq("Second")
|
49
63
|
expect(nodes[-1].text).to eq("Third")
|
50
64
|
end
|
51
65
|
|
52
66
|
it "accesses by range" do
|
67
|
+
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
68
|
+
|
53
69
|
subset = nodes[0..1]
|
54
70
|
expect(subset).to be_a(described_class)
|
55
71
|
expect(subset.size).to eq(2)
|
@@ -57,6 +73,8 @@ RSpec.shared_examples "Moxml::NodeSet" do
|
|
57
73
|
end
|
58
74
|
|
59
75
|
it "provides first and last" do
|
76
|
+
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
77
|
+
|
60
78
|
expect(nodes.first.text).to eq("First")
|
61
79
|
expect(nodes.last.text).to eq("Third")
|
62
80
|
end
|
@@ -77,6 +95,8 @@ RSpec.shared_examples "Moxml::NodeSet" do
|
|
77
95
|
|
78
96
|
describe "concatenation" do
|
79
97
|
it "combines node sets" do
|
98
|
+
pending "Ox doesn't have a native XPath" if context.config.adapter_name == :ox
|
99
|
+
|
80
100
|
other_doc = context.parse("<root><item>Fourth</item></root>")
|
81
101
|
other_nodes = other_doc.xpath("//item")
|
82
102
|
combined = nodes + other_nodes
|
@@ -43,7 +43,7 @@ RSpec.shared_examples "Moxml::ProcessingInstruction" do
|
|
43
43
|
|
44
44
|
describe "serialization" do
|
45
45
|
it "formats processing instruction" do
|
46
|
-
expect(pi.to_xml).to
|
46
|
+
expect(pi.to_xml.strip).to end_with('<?xml-stylesheet href="style.xsl" type="text/xsl"?>')
|
47
47
|
end
|
48
48
|
|
49
49
|
it "handles special characters" do
|
@@ -33,7 +33,8 @@ RSpec.shared_examples "Moxml::Text" do
|
|
33
33
|
describe "special characters" do
|
34
34
|
it "encodes basic XML entities" do
|
35
35
|
text.content = "< > & \" '"
|
36
|
-
|
36
|
+
doc.add_child(text)
|
37
|
+
expect(doc.to_xml.strip).to end_with("< > & \" '")
|
37
38
|
end
|
38
39
|
|
39
40
|
it "preserves whitespace" do
|
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.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: 'Moxml is a unified XML manipulation library that provides a common API.
|
14
14
|
|
@@ -37,6 +37,9 @@ files:
|
|
37
37
|
- lib/moxml/adapter/base.rb
|
38
38
|
- lib/moxml/adapter/customized_oga/xml_declaration.rb
|
39
39
|
- lib/moxml/adapter/customized_oga/xml_generator.rb
|
40
|
+
- lib/moxml/adapter/customized_ox/attribute.rb
|
41
|
+
- lib/moxml/adapter/customized_ox/namespace.rb
|
42
|
+
- lib/moxml/adapter/customized_ox/text.rb
|
40
43
|
- lib/moxml/adapter/customized_rexml/formatter.rb
|
41
44
|
- lib/moxml/adapter/nokogiri.rb
|
42
45
|
- lib/moxml/adapter/oga.rb
|
@@ -62,8 +65,10 @@ files:
|
|
62
65
|
- lib/moxml/version.rb
|
63
66
|
- lib/moxml/xml_utils.rb
|
64
67
|
- lib/moxml/xml_utils/encoder.rb
|
68
|
+
- lib/ox/node.rb
|
65
69
|
- moxml.gemspec
|
66
70
|
- sig/moxml.rbs
|
71
|
+
- spec/fixtures/small.xml
|
67
72
|
- spec/moxml/adapter/nokogiri_spec.rb
|
68
73
|
- spec/moxml/adapter/oga_spec.rb
|
69
74
|
- spec/moxml/adapter/ox_spec.rb
|
@@ -87,6 +92,7 @@ files:
|
|
87
92
|
- spec/support/shared_examples/element.rb
|
88
93
|
- spec/support/shared_examples/examples/attribute.rb
|
89
94
|
- spec/support/shared_examples/examples/basic_usage.rb
|
95
|
+
- spec/support/shared_examples/examples/benchmark_spec.rb
|
90
96
|
- spec/support/shared_examples/examples/memory.rb
|
91
97
|
- spec/support/shared_examples/examples/namespace.rb
|
92
98
|
- spec/support/shared_examples/examples/readme_examples.rb
|