moxml 0.1.3 → 0.1.4
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 +48 -20
- data/Gemfile +1 -0
- data/LICENSE.md +33 -0
- data/README.adoc +32 -10
- data/lib/moxml/adapter/base.rb +12 -0
- data/lib/moxml/adapter/customized_rexml/formatter.rb +195 -0
- data/lib/moxml/adapter/nokogiri.rb +1 -1
- data/lib/moxml/adapter/oga.rb +21 -7
- data/lib/moxml/adapter/rexml.rb +458 -0
- data/lib/moxml/adapter.rb +1 -1
- data/lib/moxml/attribute.rb +2 -2
- data/lib/moxml/config.rb +1 -1
- data/lib/moxml/document_builder.rb +13 -17
- data/lib/moxml/element.rb +2 -2
- data/lib/moxml/namespace.rb +1 -1
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter/rexml_spec.rb +14 -0
- data/spec/moxml/all_with_adapters_spec.rb +0 -2
- data/spec/support/shared_examples/declaration.rb +1 -0
- data/spec/support/shared_examples/edge_cases.rb +3 -3
- data/spec/support/shared_examples/element.rb +1 -1
- data/spec/support/shared_examples/integration.rb +2 -2
- data/spec/support/shared_examples/xml_adapter.rb +169 -7
- metadata +7 -3
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This test works directly with native nodes which looks quite strange
|
4
|
+
# A better way is to run it through Moxml wrappers
|
3
5
|
RSpec.shared_examples "xml adapter" do
|
4
6
|
let(:xml) do
|
5
7
|
<<~XML
|
@@ -80,7 +82,7 @@ RSpec.shared_examples "xml adapter" do
|
|
80
82
|
expect(children.length).to eq(3)
|
81
83
|
end
|
82
84
|
|
83
|
-
it "gets siblings"
|
85
|
+
it "gets siblings" do
|
84
86
|
children = described_class.children(root)
|
85
87
|
first = children[0]
|
86
88
|
second = children[1]
|
@@ -97,13 +99,16 @@ RSpec.shared_examples "xml adapter" do
|
|
97
99
|
|
98
100
|
it "adds text child" do
|
99
101
|
described_class.add_child(root, "text")
|
100
|
-
|
102
|
+
# a workaround for Rexml until we convert tests to work with Moxml wrappers
|
103
|
+
last_child = described_class.children(root).last
|
104
|
+
last_text = last_child.respond_to?(:text) ? last_child.text : last_child.to_s
|
105
|
+
expect(last_text).to eq("text")
|
101
106
|
end
|
102
107
|
end
|
103
108
|
|
104
109
|
describe "attributes" do
|
105
|
-
let(:doc) { described_class.parse(xml) }
|
106
|
-
let(:element) { described_class.children(described_class.root(doc
|
110
|
+
let(:doc) { described_class.parse(xml).native }
|
111
|
+
let(:element) { described_class.children(described_class.root(doc)).first }
|
107
112
|
|
108
113
|
it "gets attributes" do
|
109
114
|
attrs = described_class.attributes(element)
|
@@ -123,7 +128,9 @@ RSpec.shared_examples "xml adapter" do
|
|
123
128
|
|
124
129
|
it "handles special characters in attributes" do
|
125
130
|
described_class.set_attribute(element, "special", '< > & " \'')
|
126
|
-
|
131
|
+
# a workaround for Rexml until we convert tests to work with Moxml wrappers
|
132
|
+
attr = described_class.get_attribute(element, "special")
|
133
|
+
value = attr.respond_to?(:to_xml) ? attr&.to_xml : attr.to_s
|
127
134
|
expect(value).to match(/< > & ("|") ('|')/)
|
128
135
|
end
|
129
136
|
end
|
@@ -156,7 +163,10 @@ RSpec.shared_examples "xml adapter" do
|
|
156
163
|
expect(result).to include("</root>")
|
157
164
|
end
|
158
165
|
|
159
|
-
it "respects indentation settings"
|
166
|
+
it "respects indentation settings" do
|
167
|
+
pending("Oga does not support indentation settings") if described_class.name.include?("Oga")
|
168
|
+
pending("Postponed for Rexml till better times") if described_class.name.include?("Rexml")
|
169
|
+
|
160
170
|
unindented = described_class.serialize(doc, indent: 0)
|
161
171
|
indented = described_class.serialize(doc, indent: 2)
|
162
172
|
|
@@ -176,7 +186,7 @@ RSpec.shared_examples "xml adapter" do
|
|
176
186
|
end
|
177
187
|
|
178
188
|
describe "xpath" do
|
179
|
-
let(:doc) { described_class.parse(xml) }
|
189
|
+
let(:doc) { described_class.parse(xml).native }
|
180
190
|
|
181
191
|
it "finds nodes by xpath" do
|
182
192
|
nodes = described_class.xpath(doc, "//xmlns:child")
|
@@ -188,4 +198,156 @@ RSpec.shared_examples "xml adapter" do
|
|
188
198
|
expect(described_class.get_attribute_value(node, "id")).to eq("1")
|
189
199
|
end
|
190
200
|
end
|
201
|
+
|
202
|
+
describe "namespace handling" do
|
203
|
+
context "case 1" do
|
204
|
+
let(:xml) do
|
205
|
+
<<~XML
|
206
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
207
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
208
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
209
|
+
width="100" height="100">
|
210
|
+
<defs>
|
211
|
+
<circle id="myCircle" cx="50" cy="50" r="40"/>
|
212
|
+
</defs>
|
213
|
+
<use xlink:href="#myCircle" fill="red"/>
|
214
|
+
<text x="50" y="50" text-anchor="middle">SVG</text>
|
215
|
+
</svg>
|
216
|
+
XML
|
217
|
+
end
|
218
|
+
|
219
|
+
it "preserves and correctly handles multiple namespaces" do
|
220
|
+
pending("Rexml does not respect ZPath namespaces") if described_class.name.include?("Rexml")
|
221
|
+
# Parse original XML
|
222
|
+
doc = described_class.parse(xml).native
|
223
|
+
|
224
|
+
# Test namespace preservation in serialization
|
225
|
+
result = described_class.serialize(doc)
|
226
|
+
expect(result).to include('xmlns="http://www.w3.org/2000/svg"')
|
227
|
+
expect(result).to include('xmlns:xlink="http://www.w3.org/1999/xlink"')
|
228
|
+
|
229
|
+
# Test xpath with namespaces
|
230
|
+
namespaces = {
|
231
|
+
"svg" => "http://www.w3.org/2000/svg",
|
232
|
+
"xlink" => "http://www.w3.org/1999/xlink"
|
233
|
+
}
|
234
|
+
|
235
|
+
# Find use element and verify xlink:href attribute
|
236
|
+
use_elem = described_class.at_xpath(doc, "//svg:use", namespaces)
|
237
|
+
expect(use_elem).not_to be_nil
|
238
|
+
expect(described_class.get_attribute_value(use_elem, "xlink:href")).to eq("#myCircle")
|
239
|
+
|
240
|
+
# Verify circle element exists in defs
|
241
|
+
circle = described_class.at_xpath(doc, "//svg:defs/svg:circle", namespaces)
|
242
|
+
expect(circle).not_to be_nil
|
243
|
+
expect(described_class.get_attribute_value(circle, "id")).to eq("myCircle")
|
244
|
+
|
245
|
+
# Test default SVG namespace
|
246
|
+
text = described_class.at_xpath(doc, "//svg:text", namespaces)
|
247
|
+
expect(described_class.text_content(text)).to eq("SVG")
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context "case 2" do
|
252
|
+
let(:xml) do
|
253
|
+
<<~XML
|
254
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
255
|
+
<rss version="2.0"#{" "}
|
256
|
+
xmlns:atom="http://www.w3.org/2005/Atom"
|
257
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
258
|
+
xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
259
|
+
<channel>
|
260
|
+
<title>Example RSS Feed</title>
|
261
|
+
<atom:link href="http://example.com/feed" rel="self"/>
|
262
|
+
<item>
|
263
|
+
<title>Example Post</title>
|
264
|
+
<dc:creator>John Doe</dc:creator>
|
265
|
+
<content:encoded><![CDATA[<p>Post content</p>]]></content:encoded>
|
266
|
+
</item>
|
267
|
+
</channel>
|
268
|
+
</rss>
|
269
|
+
XML
|
270
|
+
end
|
271
|
+
|
272
|
+
it "preserves and correctly handles multiple namespaces" do
|
273
|
+
# Parse original XML
|
274
|
+
doc = described_class.parse(xml).native
|
275
|
+
|
276
|
+
# Test namespace preservation in serialization
|
277
|
+
result = described_class.serialize(doc)
|
278
|
+
expect(result).to include('xmlns:atom="http://www.w3.org/2005/Atom"')
|
279
|
+
expect(result).to include('xmlns:dc="http://purl.org/dc/elements/1.1/"')
|
280
|
+
expect(result).to include('xmlns:content="http://purl.org/rss/1.0/modules/content/"')
|
281
|
+
|
282
|
+
# Test xpath with namespaces
|
283
|
+
namespaces = {
|
284
|
+
"atom" => "http://www.w3.org/2005/Atom",
|
285
|
+
"dc" => "http://purl.org/dc/elements/1.1/",
|
286
|
+
"content" => "http://purl.org/rss/1.0/modules/content/"
|
287
|
+
}
|
288
|
+
|
289
|
+
# Find creator using namespaced xpath
|
290
|
+
creator = described_class.at_xpath(doc, "//dc:creator", namespaces)
|
291
|
+
expect(described_class.text_content(creator)).to eq("John Doe")
|
292
|
+
|
293
|
+
# Verify atom:link exists
|
294
|
+
link = described_class.at_xpath(doc, "//atom:link", namespaces)
|
295
|
+
expect(link).not_to be_nil
|
296
|
+
expect(described_class.get_attribute_value(link, "href")).to eq("http://example.com/feed")
|
297
|
+
|
298
|
+
# Verify CDATA in content:encoded
|
299
|
+
content = described_class.at_xpath(doc, "//content:encoded", namespaces)
|
300
|
+
expect(described_class.text_content(content)).to eq("<p>Post content</p>")
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
context "case 3" do
|
305
|
+
let(:xml) do
|
306
|
+
<<~XML
|
307
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
308
|
+
<soap:Envelope#{" "}
|
309
|
+
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
|
310
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
311
|
+
xmlns:ns="urn:example:namespace">
|
312
|
+
<soap:Header>
|
313
|
+
<ns:SessionId>12345</ns:SessionId>
|
314
|
+
</soap:Header>
|
315
|
+
<soap:Body>
|
316
|
+
<ns:GetUserRequest>
|
317
|
+
<ns:UserId xsi:type="xsi:string">user123</ns:UserId>
|
318
|
+
</ns:GetUserRequest>
|
319
|
+
</soap:Body>
|
320
|
+
</soap:Envelope>
|
321
|
+
XML
|
322
|
+
end
|
323
|
+
|
324
|
+
it "preserves and correctly handles multiple namespaces" do
|
325
|
+
# Parse original XML
|
326
|
+
doc = described_class.parse(xml).native
|
327
|
+
|
328
|
+
# Test namespace preservation in serialization
|
329
|
+
result = described_class.serialize(doc)
|
330
|
+
expect(result).to include('xmlns:soap="http://www.w3.org/2003/05/soap-envelope"')
|
331
|
+
expect(result).to include('xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"')
|
332
|
+
expect(result).to include('xmlns:ns="urn:example:namespace"')
|
333
|
+
|
334
|
+
# Test xpath with namespaces
|
335
|
+
namespaces = {
|
336
|
+
"soap" => "http://www.w3.org/2003/05/soap-envelope",
|
337
|
+
"ns" => "urn:example:namespace"
|
338
|
+
}
|
339
|
+
|
340
|
+
# Find user ID using namespaced xpath
|
341
|
+
user_id = described_class.at_xpath(doc, "//ns:UserId", namespaces)
|
342
|
+
expect(described_class.text_content(user_id)).to eq("user123")
|
343
|
+
|
344
|
+
# Verify soap:Body exists
|
345
|
+
body = described_class.at_xpath(doc, "//soap:Body", namespaces)
|
346
|
+
expect(body).not_to be_nil
|
347
|
+
|
348
|
+
# Verify attribute with namespace
|
349
|
+
expect(described_class.get_attribute_value(user_id, "xsi:type")).to eq("xsi:string")
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
191
353
|
end
|
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.4
|
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-05-12 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
|
|
@@ -27,6 +27,7 @@ files:
|
|
27
27
|
- ".rubocop_todo.yml"
|
28
28
|
- ".ruby-version"
|
29
29
|
- Gemfile
|
30
|
+
- LICENSE.md
|
30
31
|
- README.adoc
|
31
32
|
- Rakefile
|
32
33
|
- bin/console
|
@@ -36,9 +37,11 @@ files:
|
|
36
37
|
- lib/moxml/adapter/base.rb
|
37
38
|
- lib/moxml/adapter/customized_oga/xml_declaration.rb
|
38
39
|
- lib/moxml/adapter/customized_oga/xml_generator.rb
|
40
|
+
- lib/moxml/adapter/customized_rexml/formatter.rb
|
39
41
|
- lib/moxml/adapter/nokogiri.rb
|
40
42
|
- lib/moxml/adapter/oga.rb
|
41
43
|
- lib/moxml/adapter/ox.rb
|
44
|
+
- lib/moxml/adapter/rexml.rb
|
42
45
|
- lib/moxml/attribute.rb
|
43
46
|
- lib/moxml/builder.rb
|
44
47
|
- lib/moxml/cdata.rb
|
@@ -64,6 +67,7 @@ files:
|
|
64
67
|
- spec/moxml/adapter/nokogiri_spec.rb
|
65
68
|
- spec/moxml/adapter/oga_spec.rb
|
66
69
|
- spec/moxml/adapter/ox_spec.rb
|
70
|
+
- spec/moxml/adapter/rexml_spec.rb
|
67
71
|
- spec/moxml/all_with_adapters_spec.rb
|
68
72
|
- spec/moxml/config_spec.rb
|
69
73
|
- spec/moxml/error_spec.rb
|
@@ -116,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
120
|
- !ruby/object:Gem::Version
|
117
121
|
version: '0'
|
118
122
|
requirements: []
|
119
|
-
rubygems_version: 3.
|
123
|
+
rubygems_version: 3.5.22
|
120
124
|
signing_key:
|
121
125
|
specification_version: 4
|
122
126
|
summary: Unified XML manipulation library
|