moxml 0.1.2 → 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.
@@ -55,7 +55,7 @@ RSpec.shared_examples "Moxml::Element" do
55
55
  end
56
56
 
57
57
  it "sets namespace" do
58
- ns = element.add_namespace("x", "http://example.org").namespace
58
+ ns = element.add_namespace("x", "http://example.org").namespaces.first
59
59
  element.namespace = ns
60
60
  expect(element.namespace).to eq(ns)
61
61
  end
@@ -79,8 +79,8 @@ RSpec.shared_examples "Moxml::Element" do
79
79
  expect(element.text).to eq("firstsecond")
80
80
  end
81
81
 
82
- it "sets inner HTML" do
83
- element.inner_html = "<child>text</child>"
82
+ it "sets inner XML" do
83
+ element.inner_xml = "<child>text</child>"
84
84
  expect(element.children.size).to eq(1)
85
85
  expect(element.children.first.name).to eq("child")
86
86
  expect(element.children.first.text).to eq("text")
@@ -1,20 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.shared_examples "README Examples" do
4
- describe "Quick Start example" do
4
+ describe "Basic document creation" do
5
5
  it "builds document as shown in README" do
6
- context = Moxml.new
7
- doc = context.create_document
6
+ doc = Moxml.new.create_document
7
+
8
+ # Add XML declaration
9
+ doc.add_child(doc.create_declaration("1.0", "UTF-8"))
8
10
 
11
+ # Create root element with namespace
9
12
  root = doc.create_element("book")
13
+ root.add_namespace("dc", "http://purl.org/dc/elements/1.1/")
10
14
  doc.add_child(root)
11
15
 
12
- root.add_namespace("dc", "http://purl.org/dc/elements/1.1/")
16
+ # Add content
13
17
  title = doc.create_element("dc:title")
14
- title.add_child(doc.create_text("XML Processing with Ruby"))
18
+ title.text = "XML Processing with Ruby"
15
19
  root.add_child(title)
16
20
 
17
21
  expect(doc.to_xml).to include(
22
+ '<?xml version="1.0" encoding="UTF-8"?>',
18
23
  '<book xmlns:dc="http://purl.org/dc/elements/1.1/">',
19
24
  "<dc:title>XML Processing with Ruby</dc:title>",
20
25
  "</book>"
@@ -22,7 +27,42 @@ RSpec.shared_examples "README Examples" do
22
27
  end
23
28
  end
24
29
 
25
- describe "Complex document example" do
30
+ describe "Using the builder pattern" do
31
+ it "builds a correct document" do
32
+ doc = Moxml::Builder.new(Moxml.new).build do
33
+ declaration version: "1.0", encoding: "UTF-8"
34
+
35
+ element "library", xmlns: "http://example.org/library" do
36
+ element "book" do
37
+ element "title" do
38
+ text "Ruby Programming"
39
+ end
40
+
41
+ element "author" do
42
+ text "Jane Smith"
43
+ end
44
+
45
+ comment "Publication details"
46
+ element "published", year: "2024"
47
+
48
+ cdata "<custom>metadata</custom>"
49
+ end
50
+ end
51
+ end
52
+
53
+ expect(doc.to_xml).to include(
54
+ '<?xml version="1.0" encoding="UTF-8"?>',
55
+ '<library xmlns="http://example.org/library">',
56
+ "<title>Ruby Programming</title>",
57
+ "<!--Publication details-->",
58
+ '<published year="2024"></published>',
59
+ "<![CDATA[<custom>metadata</custom>]]>",
60
+ "</book>"
61
+ )
62
+ end
63
+ end
64
+
65
+ describe "Direct document manipulation" do
26
66
  it "builds document with all features" do
27
67
  doc = Moxml.new.create_document
28
68
 
@@ -38,10 +78,12 @@ RSpec.shared_examples "README Examples" do
38
78
  # Add books
39
79
  %w[Ruby XML].each do |title|
40
80
  book = doc.create_element("book")
81
+ book["id"] = "b1-#{title}"
41
82
 
42
- # Add metadata
83
+ # Add mixed content
84
+ book.add_child(doc.create_comment("Book details"))
43
85
  dc_title = doc.create_element("dc:title")
44
- dc_title.add_child(doc.create_text(title))
86
+ dc_title.text = title
45
87
  book.add_child(dc_title)
46
88
 
47
89
  # Add description
@@ -52,13 +94,16 @@ RSpec.shared_examples "README Examples" do
52
94
  root.add_child(book)
53
95
  end
54
96
 
55
- xml = doc.to_xml
56
- expect(xml).to include('<?xml version="1.0" encoding="UTF-8"?>')
57
- expect(xml).to include('<library xmlns="http://example.org/library" xmlns:dc="http://purl.org/dc/elements/1.1/">')
58
- expect(xml).to include("<dc:title>Ruby</dc:title>")
59
- expect(xml).to include("<![CDATA[About Ruby...]]>")
60
- expect(xml).to include("<dc:title>XML</dc:title>")
61
- expect(xml).to include("<![CDATA[About XML...]]>")
97
+ expect(doc.to_xml).to include(
98
+ '<?xml version="1.0" encoding="UTF-8"?>',
99
+ '<library xmlns="http://example.org/library" xmlns:dc="http://purl.org/dc/elements/1.1/">',
100
+ '<book id="b1-Ruby">',
101
+ "<!--Book details-->",
102
+ "<dc:title>Ruby</dc:title>",
103
+ "<![CDATA[About Ruby...]]>",
104
+ "<dc:title>XML</dc:title>",
105
+ "<![CDATA[About XML...]]>"
106
+ )
62
107
  end
63
108
  end
64
109
 
@@ -70,6 +115,18 @@ RSpec.shared_examples "README Examples" do
70
115
  context.parse("<invalid>")
71
116
  end.to raise_error(Moxml::ParseError)
72
117
 
118
+ expect do
119
+ doc = context.parse("<root/>")
120
+ root = doc.root
121
+ root.add_namespace("n", "wrong.url")
122
+ end.to raise_error(Moxml::NamespaceError)
123
+
124
+ expect do
125
+ doc = context.parse("<root/>")
126
+ element = doc.create_element("wrong,name")
127
+ doc.add_child(element)
128
+ end.to raise_error(Moxml::ValidationError)
129
+
73
130
  doc = context.parse("<root/>")
74
131
  expect do
75
132
  doc.xpath("///")
@@ -111,7 +111,7 @@ RSpec.shared_examples "Moxml Integration" do
111
111
  # Move nodes
112
112
  b_node = doc.at_xpath("//b")
113
113
  a_node = doc.at_xpath("//a")
114
- b_node.add_previous_sibling(a_node)
114
+ a_node.add_previous_sibling(b_node)
115
115
 
116
116
  # Add nodes
117
117
  c_node = doc.create_element("c")
@@ -125,7 +125,7 @@ RSpec.shared_examples "Moxml Integration" do
125
125
  b_node.add_child(doc.create_comment(" comment "))
126
126
  b_node.add_child(doc.create_cdata("<tag>"))
127
127
 
128
- expect(doc.root.children.map(&:name)).to eq(%w[a b c])
128
+ expect(doc.root.children.map(&:name)).to eq(%w[b c a])
129
129
  expect(doc.to_xml).to include(
130
130
  '<root id="main">',
131
131
  "<b>2<!-- comment --><![CDATA[<tag>]]></b>"
@@ -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", skip: "Oga includes text nodes into siblings" do
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
- expect(described_class.children(root).last.text).to eq("text")
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.native)).first }
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
- value = described_class.get_attribute(element, "special")&.to_xml
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(/&lt; &gt; &amp; (&quot;|") ('|&apos;)/)
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", skip: "Indent cannot be negative, and zero indent doesn't remove newlines" do
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.2
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-02-07 00:00:00.000000000 Z
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.3.27
123
+ rubygems_version: 3.5.22
120
124
  signing_key:
121
125
  specification_version: 4
122
126
  summary: Unified XML manipulation library