moxml 0.1.8 → 0.1.10
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/.rubocop_todo.yml +22 -39
- data/README.adoc +51 -20
- data/docs/_config.yml +3 -3
- data/docs/_guides/index.adoc +15 -7
- data/docs/_guides/modifying-xml.adoc +0 -1
- data/docs/_guides/node-api-consistency.adoc +572 -0
- data/docs/_guides/parsing-xml.adoc +0 -1
- data/docs/_guides/xml-declaration.adoc +450 -0
- data/docs/_pages/adapter-compatibility.adoc +1 -1
- data/docs/_pages/adapters/headed-ox.adoc +9 -9
- data/docs/_pages/adapters/index.adoc +0 -1
- data/docs/_pages/adapters/libxml.adoc +1 -2
- data/docs/_pages/adapters/nokogiri.adoc +1 -2
- data/docs/_pages/adapters/oga.adoc +1 -2
- data/docs/_pages/adapters/ox.adoc +2 -1
- data/docs/_pages/adapters/rexml.adoc +2 -3
- data/docs/_pages/best-practices.adoc +0 -1
- data/docs/_pages/compatibility.adoc +0 -1
- data/docs/_pages/configuration.adoc +0 -1
- data/docs/_pages/error-handling.adoc +0 -1
- data/docs/_pages/headed-ox-limitations.adoc +16 -0
- data/docs/_pages/installation.adoc +0 -1
- data/docs/_pages/node-api-reference.adoc +93 -4
- data/docs/_pages/performance.adoc +0 -1
- data/docs/_pages/quick-start.adoc +0 -1
- data/docs/_pages/thread-safety.adoc +0 -1
- data/docs/_references/document-api.adoc +0 -1
- data/docs/_tutorials/basic-usage.adoc +0 -1
- data/docs/_tutorials/builder-pattern.adoc +0 -1
- data/docs/_tutorials/namespace-handling.adoc +0 -1
- data/docs/_tutorials/xpath-queries.adoc +0 -1
- data/lib/moxml/adapter/customized_rexml/formatter.rb +2 -2
- data/lib/moxml/adapter/libxml.rb +34 -4
- data/lib/moxml/adapter/nokogiri.rb +50 -2
- data/lib/moxml/adapter/oga.rb +80 -3
- data/lib/moxml/adapter/ox.rb +70 -7
- data/lib/moxml/adapter/rexml.rb +45 -10
- data/lib/moxml/attribute.rb +6 -0
- data/lib/moxml/context.rb +18 -1
- data/lib/moxml/declaration.rb +9 -0
- data/lib/moxml/doctype.rb +33 -0
- data/lib/moxml/document.rb +14 -0
- data/lib/moxml/document_builder.rb +7 -0
- data/lib/moxml/element.rb +6 -0
- data/lib/moxml/error.rb +5 -5
- data/lib/moxml/node.rb +73 -1
- data/lib/moxml/processing_instruction.rb +6 -0
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xpath/compiler.rb +2 -0
- data/lib/moxml/xpath/errors.rb +1 -1
- data/spec/integration/shared_examples/node_wrappers/declaration_behavior.rb +0 -3
- data/spec/moxml/declaration_preservation_spec.rb +217 -0
- data/spec/moxml/doctype_spec.rb +19 -3
- data/spec/performance/memory_usage_spec.rb +3 -2
- metadata +5 -3
- data/.ruby-version +0 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "XML Declaration Preservation" do
|
|
6
|
+
# Test with all available adapters
|
|
7
|
+
ADAPTERS = %i[nokogiri oga rexml ox libxml headed_ox].freeze
|
|
8
|
+
|
|
9
|
+
ADAPTERS.each do |adapter_name|
|
|
10
|
+
context "with #{adapter_name} adapter" do
|
|
11
|
+
let(:context) { Moxml.new(adapter_name) }
|
|
12
|
+
|
|
13
|
+
describe "automatic preservation" do
|
|
14
|
+
context "when input has no XML declaration" do
|
|
15
|
+
let(:xml_without_decl) { '<svg xmlns="http://www.w3.org/2000/svg"><rect/></svg>' }
|
|
16
|
+
|
|
17
|
+
it "does not add XML declaration to output" do
|
|
18
|
+
doc = context.parse(xml_without_decl)
|
|
19
|
+
output = doc.to_xml
|
|
20
|
+
|
|
21
|
+
expect(output).not_to include("<?xml")
|
|
22
|
+
expect(output).to include("<svg")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "sets has_xml_declaration to false" do
|
|
26
|
+
doc = context.parse(xml_without_decl)
|
|
27
|
+
expect(doc.has_xml_declaration).to be false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context "when input has XML declaration" do
|
|
32
|
+
let(:xml_with_decl) do
|
|
33
|
+
'<?xml version="1.0" encoding="UTF-8"?><root><child/></root>'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "preserves XML declaration in output" do
|
|
37
|
+
doc = context.parse(xml_with_decl)
|
|
38
|
+
output = doc.to_xml
|
|
39
|
+
|
|
40
|
+
expect(output).to include("<?xml")
|
|
41
|
+
expect(output).to include('version="1.0"')
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "sets has_xml_declaration to true" do
|
|
45
|
+
doc = context.parse(xml_with_decl)
|
|
46
|
+
expect(doc.has_xml_declaration).to be true
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context "when input has declaration with standalone attribute" do
|
|
51
|
+
let(:xml_with_standalone) do
|
|
52
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?><root/>'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "preserves the declaration" do
|
|
56
|
+
doc = context.parse(xml_with_standalone)
|
|
57
|
+
output = doc.to_xml
|
|
58
|
+
|
|
59
|
+
expect(output).to include("<?xml")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe "explicit override" do
|
|
65
|
+
let(:xml_without_decl) { "<root><child/></root>" }
|
|
66
|
+
let(:xml_with_decl) { '<?xml version="1.0"?><root><child/></root>' }
|
|
67
|
+
|
|
68
|
+
context "when forcing declaration on document without one" do
|
|
69
|
+
it "adds declaration when declaration: true" do
|
|
70
|
+
doc = context.parse(xml_without_decl)
|
|
71
|
+
output = doc.to_xml(declaration: true)
|
|
72
|
+
|
|
73
|
+
expect(output).to include("<?xml")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context "when removing declaration from document with one" do
|
|
78
|
+
it "removes declaration when declaration: false" do
|
|
79
|
+
doc = context.parse(xml_with_decl)
|
|
80
|
+
output = doc.to_xml(declaration: false)
|
|
81
|
+
|
|
82
|
+
expect(output).not_to include("<?xml")
|
|
83
|
+
expect(output).to include("<root")
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
context "when explicitly preserving declaration" do
|
|
88
|
+
it "keeps declaration when declaration: true" do
|
|
89
|
+
doc = context.parse(xml_with_decl)
|
|
90
|
+
output = doc.to_xml(declaration: true)
|
|
91
|
+
|
|
92
|
+
expect(output).to include("<?xml")
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe "round-trip fidelity" do
|
|
98
|
+
context "for document without declaration" do
|
|
99
|
+
let(:original) { "<root><item id=\"1\"/></root>" }
|
|
100
|
+
|
|
101
|
+
it "maintains absence of declaration through parse and serialize" do
|
|
102
|
+
doc = context.parse(original)
|
|
103
|
+
output = doc.to_xml
|
|
104
|
+
|
|
105
|
+
expect(output).not_to include("<?xml")
|
|
106
|
+
|
|
107
|
+
# Parse again and verify
|
|
108
|
+
doc2 = context.parse(output)
|
|
109
|
+
expect(doc2.has_xml_declaration).to be false
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
context "for document with declaration" do
|
|
114
|
+
let(:original) do
|
|
115
|
+
'<?xml version="1.0" encoding="UTF-8"?><root><item id="1"/></root>'
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "maintains presence of declaration through parse and serialize" do
|
|
119
|
+
doc = context.parse(original)
|
|
120
|
+
output = doc.to_xml
|
|
121
|
+
|
|
122
|
+
expect(output).to include("<?xml")
|
|
123
|
+
|
|
124
|
+
# Parse again and verify
|
|
125
|
+
doc2 = context.parse(output)
|
|
126
|
+
expect(doc2.has_xml_declaration).to be true
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe "edge cases" do
|
|
132
|
+
context "with empty document" do
|
|
133
|
+
it "does not add declaration to empty document" do
|
|
134
|
+
doc = context.create_document
|
|
135
|
+
|
|
136
|
+
# Empty documents should not have declaration by default
|
|
137
|
+
expect(doc.has_xml_declaration).to be false
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
context "with built document" do
|
|
142
|
+
it "does not add declaration to programmatically built document" do
|
|
143
|
+
doc = context.create_document
|
|
144
|
+
root = doc.create_element("root")
|
|
145
|
+
doc.root = root
|
|
146
|
+
|
|
147
|
+
output = doc.to_xml
|
|
148
|
+
|
|
149
|
+
expect(output).not_to include("<?xml")
|
|
150
|
+
expect(doc.has_xml_declaration).to be false
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "can explicitly add declaration to built document" do
|
|
154
|
+
doc = context.create_document
|
|
155
|
+
root = doc.create_element("root")
|
|
156
|
+
doc.root = root
|
|
157
|
+
|
|
158
|
+
output = doc.to_xml(declaration: true)
|
|
159
|
+
|
|
160
|
+
expect(output).to include("<?xml")
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
describe "non-document nodes" do
|
|
166
|
+
let(:xml) { '<?xml version="1.0"?><root><child>text</child></root>' }
|
|
167
|
+
|
|
168
|
+
it "does not add declaration when serializing element nodes" do
|
|
169
|
+
doc = context.parse(xml)
|
|
170
|
+
root = doc.root
|
|
171
|
+
output = root.to_xml
|
|
172
|
+
|
|
173
|
+
expect(output).not_to include("<?xml")
|
|
174
|
+
expect(output).to include("<root>")
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
describe "integration with svg_conform use case" do
|
|
181
|
+
let(:context) { Moxml.new }
|
|
182
|
+
|
|
183
|
+
context "remediating SVG without declaration" do
|
|
184
|
+
let(:svg_input) { '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><rect x="10" y="10" width="80" height="80"/></svg>' }
|
|
185
|
+
|
|
186
|
+
it "does not add declaration to remediated output" do
|
|
187
|
+
doc = context.parse(svg_input)
|
|
188
|
+
|
|
189
|
+
# Simulate remediation: add viewport
|
|
190
|
+
root = doc.root
|
|
191
|
+
root["viewBox"] = "0 0 100 100"
|
|
192
|
+
|
|
193
|
+
output = doc.to_xml
|
|
194
|
+
|
|
195
|
+
expect(output).not_to include("<?xml")
|
|
196
|
+
expect(output).to include('viewBox="0 0 100 100"')
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
context "remediating SVG with declaration" do
|
|
201
|
+
let(:svg_input) { '<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg"><rect/></svg>' }
|
|
202
|
+
|
|
203
|
+
it "preserves declaration in remediated output" do
|
|
204
|
+
doc = context.parse(svg_input)
|
|
205
|
+
|
|
206
|
+
# Simulate remediation
|
|
207
|
+
root = doc.root
|
|
208
|
+
root["viewBox"] = "0 0 100 100"
|
|
209
|
+
|
|
210
|
+
output = doc.to_xml
|
|
211
|
+
|
|
212
|
+
expect(output).to include("<?xml")
|
|
213
|
+
expect(output).to include('viewBox="0 0 100 100"')
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
data/spec/moxml/doctype_spec.rb
CHANGED
|
@@ -8,7 +8,6 @@ RSpec.describe Moxml::Doctype do
|
|
|
8
8
|
|
|
9
9
|
describe "#name" do
|
|
10
10
|
it "returns doctype name" do
|
|
11
|
-
skip "Doctype accessor methods not yet implemented in all adapters"
|
|
12
11
|
doctype = doc.create_doctype("root", nil, "test.dtd")
|
|
13
12
|
expect(doctype.name).to eq("root")
|
|
14
13
|
end
|
|
@@ -16,15 +15,32 @@ RSpec.describe Moxml::Doctype do
|
|
|
16
15
|
|
|
17
16
|
describe "#system_id" do
|
|
18
17
|
it "returns system identifier" do
|
|
19
|
-
skip "Doctype accessor methods not yet implemented in all adapters"
|
|
20
18
|
doctype = doc.create_doctype("root", nil, "test.dtd")
|
|
21
19
|
expect(doctype.system_id).to eq("test.dtd")
|
|
22
20
|
end
|
|
23
21
|
end
|
|
24
22
|
|
|
23
|
+
describe "#external_id" do
|
|
24
|
+
it "returns external identifier when present" do
|
|
25
|
+
doctype = doc.create_doctype("html", "-//W3C//DTD HTML 4.01//EN", "http://www.w3.org/TR/html4/strict.dtd")
|
|
26
|
+
expect(doctype.external_id).to eq("-//W3C//DTD HTML 4.01//EN")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "returns nil when not present" do
|
|
30
|
+
doctype = doc.create_doctype("root", nil, "test.dtd")
|
|
31
|
+
expect(doctype.external_id).to be_nil
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "#identifier" do
|
|
36
|
+
it "returns the doctype name" do
|
|
37
|
+
doctype = doc.create_doctype("html", nil, nil)
|
|
38
|
+
expect(doctype.identifier).to eq("html")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
25
42
|
describe "creation" do
|
|
26
43
|
it "creates a doctype" do
|
|
27
|
-
skip "Doctype accessor methods not yet implemented in all adapters"
|
|
28
44
|
doctype = doc.create_doctype("html", nil, nil)
|
|
29
45
|
expect(doctype).to be_a(described_class)
|
|
30
46
|
expect(doctype.name).to eq("html")
|
|
@@ -51,9 +51,10 @@ RSpec.shared_examples "Memory Usage Examples" do
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
it "handles streaming processing" do
|
|
54
|
-
if
|
|
55
|
-
pending "
|
|
54
|
+
if context.config.adapter_name == :headed_ox
|
|
55
|
+
pending "HeadedOx double-wrapping issue with file-based parsing"
|
|
56
56
|
end
|
|
57
|
+
|
|
57
58
|
# Process file
|
|
58
59
|
doc = nil
|
|
59
60
|
File.open("spec/fixtures/small.xml") do |f|
|
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.10
|
|
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-
|
|
11
|
+
date: 2025-11-26 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
|
|
@@ -29,7 +29,6 @@ files:
|
|
|
29
29
|
- ".rspec"
|
|
30
30
|
- ".rubocop.yml"
|
|
31
31
|
- ".rubocop_todo.yml"
|
|
32
|
-
- ".ruby-version"
|
|
33
32
|
- Gemfile
|
|
34
33
|
- LICENSE.md
|
|
35
34
|
- README.adoc
|
|
@@ -44,9 +43,11 @@ files:
|
|
|
44
43
|
- docs/_guides/development-testing.adoc
|
|
45
44
|
- docs/_guides/index.adoc
|
|
46
45
|
- docs/_guides/modifying-xml.adoc
|
|
46
|
+
- docs/_guides/node-api-consistency.adoc
|
|
47
47
|
- docs/_guides/parsing-xml.adoc
|
|
48
48
|
- docs/_guides/sax-parsing.adoc
|
|
49
49
|
- docs/_guides/working-with-documents.adoc
|
|
50
|
+
- docs/_guides/xml-declaration.adoc
|
|
50
51
|
- docs/_pages/adapter-compatibility.adoc
|
|
51
52
|
- docs/_pages/adapters/headed-ox.adoc
|
|
52
53
|
- docs/_pages/adapters/index.adoc
|
|
@@ -203,6 +204,7 @@ files:
|
|
|
203
204
|
- spec/moxml/comment_spec.rb
|
|
204
205
|
- spec/moxml/config_spec.rb
|
|
205
206
|
- spec/moxml/context_spec.rb
|
|
207
|
+
- spec/moxml/declaration_preservation_spec.rb
|
|
206
208
|
- spec/moxml/declaration_spec.rb
|
|
207
209
|
- spec/moxml/doctype_spec.rb
|
|
208
210
|
- spec/moxml/document_builder_spec.rb
|
data/.ruby-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.0.7@moxml
|