moxml 0.1.7 → 0.1.9
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/.github/workflows/dependent-repos.json +5 -0
- data/.github/workflows/dependent-tests.yml +20 -0
- data/.github/workflows/docs.yml +59 -0
- data/.github/workflows/rake.yml +10 -10
- data/.github/workflows/release.yml +5 -3
- data/.gitignore +37 -0
- data/.rubocop.yml +15 -7
- data/.rubocop_todo.yml +224 -43
- data/Gemfile +14 -9
- data/LICENSE.md +6 -2
- data/README.adoc +535 -373
- data/Rakefile +53 -0
- data/benchmarks/.gitignore +6 -0
- data/benchmarks/generate_report.rb +550 -0
- data/docs/Gemfile +13 -0
- data/docs/_config.yml +138 -0
- data/docs/_guides/advanced-features.adoc +87 -0
- data/docs/_guides/development-testing.adoc +165 -0
- data/docs/_guides/index.adoc +51 -0
- data/docs/_guides/modifying-xml.adoc +292 -0
- data/docs/_guides/parsing-xml.adoc +230 -0
- data/docs/_guides/sax-parsing.adoc +603 -0
- data/docs/_guides/working-with-documents.adoc +118 -0
- data/docs/_guides/xml-declaration.adoc +450 -0
- data/docs/_pages/adapter-compatibility.adoc +369 -0
- data/docs/_pages/adapters/headed-ox.adoc +237 -0
- data/docs/_pages/adapters/index.adoc +97 -0
- data/docs/_pages/adapters/libxml.adoc +285 -0
- data/docs/_pages/adapters/nokogiri.adoc +251 -0
- data/docs/_pages/adapters/oga.adoc +291 -0
- data/docs/_pages/adapters/ox.adoc +56 -0
- data/docs/_pages/adapters/rexml.adoc +292 -0
- data/docs/_pages/best-practices.adoc +429 -0
- data/docs/_pages/compatibility.adoc +467 -0
- data/docs/_pages/configuration.adoc +250 -0
- data/docs/_pages/error-handling.adoc +349 -0
- data/docs/_pages/headed-ox-limitations.adoc +574 -0
- data/docs/_pages/headed-ox.adoc +1025 -0
- data/docs/_pages/index.adoc +35 -0
- data/docs/_pages/installation.adoc +140 -0
- data/docs/_pages/node-api-reference.adoc +49 -0
- data/docs/_pages/performance.adoc +35 -0
- data/docs/_pages/quick-start.adoc +243 -0
- data/docs/_pages/thread-safety.adoc +28 -0
- data/docs/_references/document-api.adoc +407 -0
- data/docs/_references/index.adoc +48 -0
- data/docs/_tutorials/basic-usage.adoc +267 -0
- data/docs/_tutorials/builder-pattern.adoc +342 -0
- data/docs/_tutorials/index.adoc +33 -0
- data/docs/_tutorials/namespace-handling.adoc +324 -0
- data/docs/_tutorials/xpath-queries.adoc +358 -0
- data/docs/index.adoc +122 -0
- data/examples/README.md +124 -0
- data/examples/api_client/README.md +424 -0
- data/examples/api_client/api_client.rb +394 -0
- data/examples/api_client/example_response.xml +48 -0
- data/examples/headed_ox_example/README.md +90 -0
- data/examples/headed_ox_example/headed_ox_demo.rb +71 -0
- data/examples/rss_parser/README.md +194 -0
- data/examples/rss_parser/example_feed.xml +93 -0
- data/examples/rss_parser/rss_parser.rb +189 -0
- data/examples/sax_parsing/README.md +50 -0
- data/examples/sax_parsing/data_extractor.rb +75 -0
- data/examples/sax_parsing/example.xml +21 -0
- data/examples/sax_parsing/large_file.rb +78 -0
- data/examples/sax_parsing/simple_parser.rb +55 -0
- data/examples/web_scraper/README.md +352 -0
- data/examples/web_scraper/example_page.html +201 -0
- data/examples/web_scraper/web_scraper.rb +312 -0
- data/lib/moxml/adapter/base.rb +107 -28
- data/lib/moxml/adapter/customized_libxml/cdata.rb +28 -0
- data/lib/moxml/adapter/customized_libxml/comment.rb +24 -0
- data/lib/moxml/adapter/customized_libxml/declaration.rb +85 -0
- data/lib/moxml/adapter/customized_libxml/element.rb +39 -0
- data/lib/moxml/adapter/customized_libxml/node.rb +44 -0
- data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +31 -0
- data/lib/moxml/adapter/customized_libxml/text.rb +27 -0
- data/lib/moxml/adapter/customized_oga/xml_generator.rb +1 -1
- data/lib/moxml/adapter/customized_ox/attribute.rb +28 -1
- data/lib/moxml/adapter/customized_rexml/formatter.rb +13 -8
- data/lib/moxml/adapter/headed_ox.rb +161 -0
- data/lib/moxml/adapter/libxml.rb +1564 -0
- data/lib/moxml/adapter/nokogiri.rb +156 -9
- data/lib/moxml/adapter/oga.rb +190 -15
- data/lib/moxml/adapter/ox.rb +322 -28
- data/lib/moxml/adapter/rexml.rb +157 -28
- data/lib/moxml/adapter.rb +21 -4
- data/lib/moxml/attribute.rb +6 -0
- data/lib/moxml/builder.rb +40 -4
- data/lib/moxml/config.rb +8 -3
- data/lib/moxml/context.rb +57 -2
- data/lib/moxml/declaration.rb +9 -0
- data/lib/moxml/doctype.rb +13 -1
- data/lib/moxml/document.rb +53 -6
- data/lib/moxml/document_builder.rb +34 -5
- data/lib/moxml/element.rb +71 -2
- data/lib/moxml/error.rb +175 -6
- data/lib/moxml/node.rb +155 -4
- data/lib/moxml/node_set.rb +34 -0
- data/lib/moxml/sax/block_handler.rb +194 -0
- data/lib/moxml/sax/element_handler.rb +124 -0
- data/lib/moxml/sax/handler.rb +113 -0
- data/lib/moxml/sax.rb +31 -0
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xml_utils/encoder.rb +4 -4
- data/lib/moxml/xml_utils.rb +7 -4
- data/lib/moxml/xpath/ast/node.rb +159 -0
- data/lib/moxml/xpath/cache.rb +91 -0
- data/lib/moxml/xpath/compiler.rb +1770 -0
- data/lib/moxml/xpath/context.rb +26 -0
- data/lib/moxml/xpath/conversion.rb +124 -0
- data/lib/moxml/xpath/engine.rb +52 -0
- data/lib/moxml/xpath/errors.rb +101 -0
- data/lib/moxml/xpath/lexer.rb +304 -0
- data/lib/moxml/xpath/parser.rb +485 -0
- data/lib/moxml/xpath/ruby/generator.rb +269 -0
- data/lib/moxml/xpath/ruby/node.rb +193 -0
- data/lib/moxml/xpath.rb +37 -0
- data/lib/moxml.rb +5 -2
- data/moxml.gemspec +3 -1
- data/old-specs/moxml/adapter/customized_libxml/.gitkeep +6 -0
- data/spec/consistency/README.md +77 -0
- data/spec/{moxml/examples/adapter_spec.rb → consistency/adapter_parity_spec.rb} +4 -4
- data/spec/examples/README.md +75 -0
- data/spec/{support/shared_examples/examples/attribute.rb → examples/attribute_examples_spec.rb} +1 -1
- data/spec/{support/shared_examples/examples/basic_usage.rb → examples/basic_usage_spec.rb} +2 -2
- data/spec/{support/shared_examples/examples/namespace.rb → examples/namespace_examples_spec.rb} +3 -3
- data/spec/{support/shared_examples/examples/readme_examples.rb → examples/readme_examples_spec.rb} +6 -4
- data/spec/{support/shared_examples/examples/xpath.rb → examples/xpath_examples_spec.rb} +10 -6
- data/spec/integration/README.md +71 -0
- data/spec/{moxml/all_with_adapters_spec.rb → integration/all_adapters_spec.rb} +3 -2
- data/spec/integration/headed_ox_integration_spec.rb +326 -0
- data/spec/{support → integration}/shared_examples/edge_cases.rb +37 -10
- data/spec/integration/shared_examples/high_level/.gitkeep +0 -0
- data/spec/{support/shared_examples/context.rb → integration/shared_examples/high_level/context_behavior.rb} +2 -1
- data/spec/{support/shared_examples/integration.rb → integration/shared_examples/integration_workflows.rb} +23 -6
- data/spec/integration/shared_examples/node_wrappers/.gitkeep +0 -0
- data/spec/{support/shared_examples/cdata.rb → integration/shared_examples/node_wrappers/cdata_behavior.rb} +6 -1
- data/spec/{support/shared_examples/comment.rb → integration/shared_examples/node_wrappers/comment_behavior.rb} +2 -1
- data/spec/{support/shared_examples/declaration.rb → integration/shared_examples/node_wrappers/declaration_behavior.rb} +5 -5
- data/spec/{support/shared_examples/doctype.rb → integration/shared_examples/node_wrappers/doctype_behavior.rb} +2 -2
- data/spec/{support/shared_examples/document.rb → integration/shared_examples/node_wrappers/document_behavior.rb} +1 -1
- data/spec/{support/shared_examples/node.rb → integration/shared_examples/node_wrappers/node_behavior.rb} +9 -2
- data/spec/{support/shared_examples/node_set.rb → integration/shared_examples/node_wrappers/node_set_behavior.rb} +1 -18
- data/spec/{support/shared_examples/processing_instruction.rb → integration/shared_examples/node_wrappers/processing_instruction_behavior.rb} +6 -2
- data/spec/moxml/README.md +41 -0
- data/spec/moxml/adapter/.gitkeep +0 -0
- data/spec/moxml/adapter/README.md +61 -0
- data/spec/moxml/adapter/base_spec.rb +27 -0
- data/spec/moxml/adapter/headed_ox_spec.rb +311 -0
- data/spec/moxml/adapter/libxml_spec.rb +14 -0
- data/spec/moxml/adapter/ox_spec.rb +9 -8
- data/spec/moxml/adapter/shared_examples/.gitkeep +0 -0
- data/spec/{support/shared_examples/xml_adapter.rb → moxml/adapter/shared_examples/adapter_contract.rb} +39 -12
- data/spec/moxml/adapter_spec.rb +16 -0
- data/spec/moxml/attribute_spec.rb +30 -0
- data/spec/moxml/builder_spec.rb +33 -0
- data/spec/moxml/cdata_spec.rb +31 -0
- data/spec/moxml/comment_spec.rb +31 -0
- data/spec/moxml/config_spec.rb +3 -3
- data/spec/moxml/context_spec.rb +28 -0
- data/spec/moxml/declaration_preservation_spec.rb +217 -0
- data/spec/moxml/declaration_spec.rb +36 -0
- data/spec/moxml/doctype_spec.rb +33 -0
- data/spec/moxml/document_builder_spec.rb +30 -0
- data/spec/moxml/document_spec.rb +105 -0
- data/spec/moxml/element_spec.rb +143 -0
- data/spec/moxml/error_spec.rb +266 -22
- data/spec/{moxml_spec.rb → moxml/moxml_spec.rb} +9 -9
- data/spec/moxml/namespace_spec.rb +32 -0
- data/spec/moxml/node_set_spec.rb +39 -0
- data/spec/moxml/node_spec.rb +37 -0
- data/spec/moxml/processing_instruction_spec.rb +34 -0
- data/spec/moxml/sax_spec.rb +1067 -0
- data/spec/moxml/text_spec.rb +31 -0
- data/spec/moxml/version_spec.rb +14 -0
- data/spec/moxml/xml_utils/.gitkeep +0 -0
- data/spec/moxml/xml_utils/encoder_spec.rb +27 -0
- data/spec/moxml/xml_utils_spec.rb +49 -0
- data/spec/moxml/xpath/ast/node_spec.rb +83 -0
- data/spec/moxml/xpath/axes_spec.rb +296 -0
- data/spec/moxml/xpath/cache_spec.rb +358 -0
- data/spec/moxml/xpath/compiler_spec.rb +406 -0
- data/spec/moxml/xpath/context_spec.rb +210 -0
- data/spec/moxml/xpath/conversion_spec.rb +365 -0
- data/spec/moxml/xpath/fixtures/sample.xml +25 -0
- data/spec/moxml/xpath/functions/boolean_functions_spec.rb +114 -0
- data/spec/moxml/xpath/functions/node_functions_spec.rb +145 -0
- data/spec/moxml/xpath/functions/numeric_functions_spec.rb +164 -0
- data/spec/moxml/xpath/functions/position_functions_spec.rb +93 -0
- data/spec/moxml/xpath/functions/special_functions_spec.rb +89 -0
- data/spec/moxml/xpath/functions/string_functions_spec.rb +381 -0
- data/spec/moxml/xpath/lexer_spec.rb +488 -0
- data/spec/moxml/xpath/parser_integration_spec.rb +210 -0
- data/spec/moxml/xpath/parser_spec.rb +364 -0
- data/spec/moxml/xpath/ruby/generator_spec.rb +421 -0
- data/spec/moxml/xpath/ruby/node_spec.rb +291 -0
- data/spec/moxml/xpath_capabilities_spec.rb +199 -0
- data/spec/moxml/xpath_spec.rb +77 -0
- data/spec/performance/README.md +83 -0
- data/spec/performance/benchmark_spec.rb +64 -0
- data/spec/{support/shared_examples/examples/memory.rb → performance/memory_usage_spec.rb} +4 -1
- data/spec/{support/shared_examples/examples/thread_safety.rb → performance/thread_safety_spec.rb} +3 -1
- data/spec/performance/xpath_benchmark_spec.rb +259 -0
- data/spec/spec_helper.rb +58 -1
- data/spec/support/xml_matchers.rb +1 -1
- metadata +178 -34
- data/spec/support/shared_examples/examples/benchmark_spec.rb +0 -51
- /data/spec/{support/shared_examples/builder.rb → integration/shared_examples/high_level/builder_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/document_builder.rb → integration/shared_examples/high_level/document_builder_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/attribute.rb → integration/shared_examples/node_wrappers/attribute_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/element.rb → integration/shared_examples/node_wrappers/element_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/namespace.rb → integration/shared_examples/node_wrappers/namespace_behavior.rb} +0 -0
- /data/spec/{support/shared_examples/text.rb → integration/shared_examples/node_wrappers/text_behavior.rb} +0 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Builder pattern
|
|
3
|
+
nav_order: 5
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
== Builder pattern
|
|
7
|
+
|
|
8
|
+
=== Purpose
|
|
9
|
+
|
|
10
|
+
Learn how to use Moxml's builder pattern to create XML documents with a clean,
|
|
11
|
+
Ruby-idiomatic DSL that makes complex XML structures easy to construct and
|
|
12
|
+
maintain.
|
|
13
|
+
|
|
14
|
+
=== Prerequisites
|
|
15
|
+
|
|
16
|
+
* Moxml installed
|
|
17
|
+
* Familiarity with link:basic-usage[basic XML operations]
|
|
18
|
+
|
|
19
|
+
=== Step 1: Basic builder usage
|
|
20
|
+
|
|
21
|
+
Create simple documents with the builder:
|
|
22
|
+
|
|
23
|
+
[source,ruby]
|
|
24
|
+
----
|
|
25
|
+
require 'moxml'
|
|
26
|
+
|
|
27
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
28
|
+
xml.library do
|
|
29
|
+
xml.book do
|
|
30
|
+
xml.title "Ruby Programming"
|
|
31
|
+
xml.author "Jane Smith"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
puts doc.to_xml(indent: 2)
|
|
37
|
+
----
|
|
38
|
+
|
|
39
|
+
Output:
|
|
40
|
+
|
|
41
|
+
[source,xml]
|
|
42
|
+
----
|
|
43
|
+
<library>
|
|
44
|
+
<book>
|
|
45
|
+
<title>Ruby Programming</title>
|
|
46
|
+
<author>Jane Smith</author>
|
|
47
|
+
</book>
|
|
48
|
+
</library>
|
|
49
|
+
----
|
|
50
|
+
|
|
51
|
+
=== Step 2: Add XML declaration
|
|
52
|
+
|
|
53
|
+
Include XML declaration:
|
|
54
|
+
|
|
55
|
+
[source,ruby]
|
|
56
|
+
----
|
|
57
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
58
|
+
xml.declaration version: "1.0", encoding: "UTF-8"
|
|
59
|
+
|
|
60
|
+
xml.library do
|
|
61
|
+
xml.book "Ruby Programming"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
----
|
|
65
|
+
|
|
66
|
+
Output:
|
|
67
|
+
|
|
68
|
+
[source,xml]
|
|
69
|
+
----
|
|
70
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
71
|
+
<library>
|
|
72
|
+
<book>Ruby Programming</book>
|
|
73
|
+
</library>
|
|
74
|
+
----
|
|
75
|
+
|
|
76
|
+
=== Step 3: Add attributes
|
|
77
|
+
|
|
78
|
+
Set element attributes in the builder:
|
|
79
|
+
|
|
80
|
+
[source,ruby]
|
|
81
|
+
----
|
|
82
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
83
|
+
xml.library location: "Main", established: "2020" do
|
|
84
|
+
xml.book id: "1", category: "programming" do
|
|
85
|
+
xml.title "Ruby Basics"
|
|
86
|
+
xml.author "Jane Smith"
|
|
87
|
+
xml.price currency: "USD", "29.99"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
----
|
|
92
|
+
|
|
93
|
+
Output:
|
|
94
|
+
|
|
95
|
+
[source,xml]
|
|
96
|
+
----
|
|
97
|
+
<library location="Main" established="2020">
|
|
98
|
+
<book id="1" category="programming">
|
|
99
|
+
<title>Ruby Basics</title>
|
|
100
|
+
<author>Jane Smith</author>
|
|
101
|
+
<price currency="USD">29.99</price>
|
|
102
|
+
</book>
|
|
103
|
+
</library>
|
|
104
|
+
----
|
|
105
|
+
|
|
106
|
+
=== Step 4: Nested structures
|
|
107
|
+
|
|
108
|
+
Create complex nested documents:
|
|
109
|
+
|
|
110
|
+
[source,ruby]
|
|
111
|
+
----
|
|
112
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
113
|
+
xml.declaration version: "1.0", encoding: "UTF-8"
|
|
114
|
+
|
|
115
|
+
xml.library do
|
|
116
|
+
xml.section name: "programming" do
|
|
117
|
+
xml.book id: "1" do
|
|
118
|
+
xml.title "Ruby Basics"
|
|
119
|
+
xml.author "Jane Smith"
|
|
120
|
+
xml.chapters do
|
|
121
|
+
xml.chapter number: "1", "Introduction"
|
|
122
|
+
xml.chapter number: "2", "Getting Started"
|
|
123
|
+
xml.chapter number: "3", "Advanced Topics"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
xml.section name: "fiction" do
|
|
129
|
+
xml.book id: "2" do
|
|
130
|
+
xml.title "Ruby Story"
|
|
131
|
+
xml.author "John Doe"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
----
|
|
137
|
+
|
|
138
|
+
=== Step 5: Add comments and special content
|
|
139
|
+
|
|
140
|
+
Include comments, CDATA, and processing instructions:
|
|
141
|
+
|
|
142
|
+
[source,ruby]
|
|
143
|
+
----
|
|
144
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
145
|
+
xml.declaration version: "1.0", encoding: "UTF-8"
|
|
146
|
+
|
|
147
|
+
# Add processing instruction
|
|
148
|
+
xml.processing_instruction "xml-stylesheet",
|
|
149
|
+
'type="text/xsl" href="style.xsl"'
|
|
150
|
+
|
|
151
|
+
xml.data do
|
|
152
|
+
# Add comment
|
|
153
|
+
xml.comment "This section contains book data"
|
|
154
|
+
|
|
155
|
+
xml.book do
|
|
156
|
+
xml.title "Ruby Programming"
|
|
157
|
+
|
|
158
|
+
# Add CDATA for raw content
|
|
159
|
+
xml.cdata "<raw>HTML content</raw>"
|
|
160
|
+
|
|
161
|
+
xml.description do
|
|
162
|
+
xml.cdata "Contains <em>emphasis</em> and other markup"
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
----
|
|
168
|
+
|
|
169
|
+
=== Step 6: Builder with namespaces
|
|
170
|
+
|
|
171
|
+
Create namespaced elements with the builder:
|
|
172
|
+
|
|
173
|
+
[source,ruby]
|
|
174
|
+
----
|
|
175
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
176
|
+
xml.declaration version: "1.0", encoding: "UTF-8"
|
|
177
|
+
|
|
178
|
+
xml.element 'library',
|
|
179
|
+
'xmlns' => 'http://example.org/library',
|
|
180
|
+
'xmlns:dc' => 'http://purl.org/dc/elements/1.1/' do
|
|
181
|
+
|
|
182
|
+
xml.book do
|
|
183
|
+
xml.element 'dc:title', 'Ruby Programming'
|
|
184
|
+
xml.element 'dc:creator', 'Jane Smith'
|
|
185
|
+
xml.element 'dc:date', '2024'
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
----
|
|
190
|
+
|
|
191
|
+
=== Step 7: Dynamic content generation
|
|
192
|
+
|
|
193
|
+
Generate XML from data structures:
|
|
194
|
+
|
|
195
|
+
[source,ruby]
|
|
196
|
+
----
|
|
197
|
+
books_data = [
|
|
198
|
+
{ id: 1, title: 'Ruby Basics', author: 'Smith', price: 29.99 },
|
|
199
|
+
{ id: 2, title: 'Advanced Ruby', author: 'Doe', price: 39.99 },
|
|
200
|
+
{ id: 3, title: 'Ruby Patterns', author: 'Johnson', price: 34.99 }
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
204
|
+
xml.declaration version: "1.0", encoding: "UTF-8"
|
|
205
|
+
|
|
206
|
+
xml.library do
|
|
207
|
+
books_data.each do |book_data|
|
|
208
|
+
xml.book id: book_data[:id] do
|
|
209
|
+
xml.title book_data[:title]
|
|
210
|
+
xml.author book_data[:author]
|
|
211
|
+
xml.price book_data[:price].to_s
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
puts doc.to_xml(indent: 2)
|
|
218
|
+
----
|
|
219
|
+
|
|
220
|
+
=== Step 8: Conditional builder logic
|
|
221
|
+
|
|
222
|
+
Add elements conditionally:
|
|
223
|
+
|
|
224
|
+
[source,ruby]
|
|
225
|
+
----
|
|
226
|
+
book_data = {
|
|
227
|
+
title: 'Ruby Programming',
|
|
228
|
+
author: 'Jane Smith',
|
|
229
|
+
isbn: '978-0-123456-78-9',
|
|
230
|
+
edition: nil, # Not set
|
|
231
|
+
price: 29.99
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
235
|
+
xml.book do
|
|
236
|
+
xml.title book_data[:title]
|
|
237
|
+
xml.author book_data[:author]
|
|
238
|
+
|
|
239
|
+
# Conditional elements
|
|
240
|
+
if book_data[:isbn]
|
|
241
|
+
xml.isbn book_data[:isbn]
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
if book_data[:edition]
|
|
245
|
+
xml.edition book_data[:edition]
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
xml.price book_data[:price].to_s
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
----
|
|
252
|
+
|
|
253
|
+
=== Builder vs direct manipulation
|
|
254
|
+
|
|
255
|
+
Compare the two approaches:
|
|
256
|
+
|
|
257
|
+
**Using Builder (preferred for creation):**
|
|
258
|
+
|
|
259
|
+
[source,ruby]
|
|
260
|
+
----
|
|
261
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
262
|
+
xml.library do
|
|
263
|
+
xml.book id: "1" do
|
|
264
|
+
xml.title "Ruby Programming"
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
----
|
|
269
|
+
|
|
270
|
+
**Direct manipulation (preferred for modification):**
|
|
271
|
+
|
|
272
|
+
[source,ruby]
|
|
273
|
+
----
|
|
274
|
+
doc = Moxml.new.create_document
|
|
275
|
+
root = doc.create_element('library')
|
|
276
|
+
doc.add_child(root)
|
|
277
|
+
|
|
278
|
+
book = doc.create_element('book')
|
|
279
|
+
book['id'] = '1'
|
|
280
|
+
root.add_child(book)
|
|
281
|
+
|
|
282
|
+
title = doc.create_element('title')
|
|
283
|
+
title.text = 'Ruby Programming'
|
|
284
|
+
book.add_child(title)
|
|
285
|
+
----
|
|
286
|
+
|
|
287
|
+
=== Best practices
|
|
288
|
+
|
|
289
|
+
. **Use builder for new documents** - cleaner and more maintainable
|
|
290
|
+
. **Use direct manipulation for modifications** - more control
|
|
291
|
+
. **Extract complex builders** into methods for reusability
|
|
292
|
+
. **Validate generated XML** after building
|
|
293
|
+
. **Keep builders focused** - one logical structure per builder
|
|
294
|
+
|
|
295
|
+
=== Common patterns
|
|
296
|
+
|
|
297
|
+
==== Reusable builder methods
|
|
298
|
+
|
|
299
|
+
[source,ruby]
|
|
300
|
+
----
|
|
301
|
+
def build_book(xml, book_data)
|
|
302
|
+
xml.book id: book_data[:id] do
|
|
303
|
+
xml.title book_data[:title]
|
|
304
|
+
xml.author book_data[:author]
|
|
305
|
+
xml.price book_data[:price].to_s
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
310
|
+
xml.library do
|
|
311
|
+
books_data.each { |book| build_book(xml, book) }
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
----
|
|
315
|
+
|
|
316
|
+
==== Mixed content
|
|
317
|
+
|
|
318
|
+
[source,ruby]
|
|
319
|
+
----
|
|
320
|
+
doc = Moxml::Builder.build(Moxml.new) do |xml|
|
|
321
|
+
xml.article do
|
|
322
|
+
xml.title "Introduction"
|
|
323
|
+
xml.para do
|
|
324
|
+
xml.text "This is "
|
|
325
|
+
xml.emphasis "important"
|
|
326
|
+
xml.text " content."
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
----
|
|
331
|
+
|
|
332
|
+
=== Next steps
|
|
333
|
+
|
|
334
|
+
* link:../guides/creating-documents[Creating documents guide] - More document
|
|
335
|
+
creation patterns
|
|
336
|
+
* link:working-with-elements[Working with elements] - Element manipulation
|
|
337
|
+
* link:../references/builder-api[Builder API reference]
|
|
338
|
+
|
|
339
|
+
=== See also
|
|
340
|
+
|
|
341
|
+
* link:../references/document-api[Document API] - Direct manipulation methods
|
|
342
|
+
* link:namespace-handling[Namespace handling] - Namespaced builders
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Overview
|
|
3
|
+
nav_order: 1
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
== Tutorials
|
|
7
|
+
|
|
8
|
+
Step-by-step learning paths to master Moxml's features and capabilities.
|
|
9
|
+
|
|
10
|
+
=== Getting started
|
|
11
|
+
|
|
12
|
+
link:basic-usage[Basic usage]::
|
|
13
|
+
Learn the fundamentals of parsing, creating, and manipulating XML documents
|
|
14
|
+
with Moxml.
|
|
15
|
+
|
|
16
|
+
link:working-with-elements[Working with elements]::
|
|
17
|
+
Master element creation, manipulation, and attribute handling.
|
|
18
|
+
|
|
19
|
+
link:xpath-queries[XPath queries]::
|
|
20
|
+
Learn how to query XML documents effectively using XPath expressions.
|
|
21
|
+
|
|
22
|
+
=== Advanced topics
|
|
23
|
+
|
|
24
|
+
link:namespace-handling[Namespace handling]::
|
|
25
|
+
Work with XML namespaces including prefixes, URIs, and namespace-aware
|
|
26
|
+
queries.
|
|
27
|
+
|
|
28
|
+
link:builder-pattern[Builder pattern]::
|
|
29
|
+
Use Moxml's DSL to create complex XML documents cleanly and efficiently.
|
|
30
|
+
|
|
31
|
+
link:adapter-switching[Adapter switching]::
|
|
32
|
+
Understand how to choose and switch between different XML adapters based on
|
|
33
|
+
your requirements.
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Namespace handling
|
|
3
|
+
nav_order: 4
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
== Namespace handling
|
|
7
|
+
|
|
8
|
+
=== Purpose
|
|
9
|
+
|
|
10
|
+
Learn how to work with XML namespaces in Moxml including creating namespaced
|
|
11
|
+
elements, querying with namespace prefixes, and understanding namespace
|
|
12
|
+
inheritance.
|
|
13
|
+
|
|
14
|
+
=== Prerequisites
|
|
15
|
+
|
|
16
|
+
* Understanding of XML namespace concepts
|
|
17
|
+
* Moxml installed with an adapter supporting namespaces
|
|
18
|
+
* Familiarity with link:xpath-queries[XPath queries]
|
|
19
|
+
|
|
20
|
+
=== Step 1: Understanding XML namespaces
|
|
21
|
+
|
|
22
|
+
XML namespaces prevent element name conflicts:
|
|
23
|
+
|
|
24
|
+
[source,xml]
|
|
25
|
+
----
|
|
26
|
+
<!-- Without namespaces - ambiguous -->
|
|
27
|
+
<title>Book Title</title>
|
|
28
|
+
<title>Page Title</title>
|
|
29
|
+
|
|
30
|
+
<!-- With namespaces - clear distinction -->
|
|
31
|
+
<book:title xmlns:book="http://example.org/book">Book Title</book:title>
|
|
32
|
+
<page:title xmlns:page="http://example.org/page">Page Title</page:title>
|
|
33
|
+
----
|
|
34
|
+
|
|
35
|
+
In Moxml:
|
|
36
|
+
|
|
37
|
+
[source,ruby]
|
|
38
|
+
----
|
|
39
|
+
xml = <<~XML
|
|
40
|
+
<library xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
41
|
+
<book>
|
|
42
|
+
<dc:title>Ruby Programming</dc:title>
|
|
43
|
+
<dc:creator>Jane Smith</dc:creator>
|
|
44
|
+
</book>
|
|
45
|
+
</library>
|
|
46
|
+
XML
|
|
47
|
+
|
|
48
|
+
doc = Moxml.new.parse(xml)
|
|
49
|
+
|
|
50
|
+
# Namespaces are preserved
|
|
51
|
+
book = doc.at_xpath('//book')
|
|
52
|
+
title = book.children.find { |n| n.name.include?('title') }
|
|
53
|
+
|
|
54
|
+
# Access namespace
|
|
55
|
+
puts title.namespace.uri if title.namespace
|
|
56
|
+
# => "http://purl.org/dc/elements/1.1/"
|
|
57
|
+
----
|
|
58
|
+
|
|
59
|
+
=== Step 2: Create namespaced elements
|
|
60
|
+
|
|
61
|
+
Add namespaces to elements:
|
|
62
|
+
|
|
63
|
+
[source,ruby]
|
|
64
|
+
----
|
|
65
|
+
doc = Moxml.new.create_document
|
|
66
|
+
|
|
67
|
+
# Create root with default namespace
|
|
68
|
+
root = doc.create_element('library')
|
|
69
|
+
root.add_namespace(nil, 'http://example.org/library')
|
|
70
|
+
doc.add_child(root)
|
|
71
|
+
|
|
72
|
+
# Create element with prefix namespace
|
|
73
|
+
book = doc.create_element('book')
|
|
74
|
+
book.add_namespace('dc', 'http://purl.org/dc/elements/1.1/')
|
|
75
|
+
root.add_child(book)
|
|
76
|
+
|
|
77
|
+
# Create namespaced child elements
|
|
78
|
+
title = doc.create_element('dc:title')
|
|
79
|
+
title.text = 'Ruby Programming'
|
|
80
|
+
book.add_child(title)
|
|
81
|
+
|
|
82
|
+
creator = doc.create_element('dc:creator')
|
|
83
|
+
creator.text = 'Jane Smith'
|
|
84
|
+
book.add_child(creator)
|
|
85
|
+
|
|
86
|
+
puts doc.to_xml(indent: 2)
|
|
87
|
+
----
|
|
88
|
+
|
|
89
|
+
Output:
|
|
90
|
+
|
|
91
|
+
[source,xml]
|
|
92
|
+
----
|
|
93
|
+
<library xmlns="http://example.org/library">
|
|
94
|
+
<book xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
95
|
+
<dc:title>Ruby Programming</dc:title>
|
|
96
|
+
<dc:creator>Jane Smith</dc:creator>
|
|
97
|
+
</book>
|
|
98
|
+
</library>
|
|
99
|
+
----
|
|
100
|
+
|
|
101
|
+
=== Step 3: Query with namespaces
|
|
102
|
+
|
|
103
|
+
Use namespace mappings in XPath queries:
|
|
104
|
+
|
|
105
|
+
[source,ruby]
|
|
106
|
+
----
|
|
107
|
+
xml = <<~XML
|
|
108
|
+
<library xmlns="http://example.org/library"
|
|
109
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
110
|
+
xmlns:isbn="http://example.org/isbn">
|
|
111
|
+
<book isbn:value="978-0-123456-78-9">
|
|
112
|
+
<dc:title>Ruby Programming</dc:title>
|
|
113
|
+
<dc:creator>Jane Smith</dc:creator>
|
|
114
|
+
</book>
|
|
115
|
+
</library>
|
|
116
|
+
XML
|
|
117
|
+
|
|
118
|
+
doc = Moxml.new.parse(xml)
|
|
119
|
+
|
|
120
|
+
# Define namespace prefixes for XPath
|
|
121
|
+
namespaces = {
|
|
122
|
+
'lib' => 'http://example.org/library',
|
|
123
|
+
'dc' => 'http://purl.org/dc/elements/1.1/',
|
|
124
|
+
'isbn' => 'http://example.org/isbn'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Query with namespace prefixes
|
|
128
|
+
books = doc.xpath('//lib:book', namespaces)
|
|
129
|
+
titles = doc.xpath('//dc:title', namespaces)
|
|
130
|
+
creators = doc.xpath('//dc:creator', namespaces)
|
|
131
|
+
|
|
132
|
+
puts books.length # => 1
|
|
133
|
+
puts titles.first.text # => "Ruby Programming"
|
|
134
|
+
puts creators.first.text # => "Jane Smith"
|
|
135
|
+
|
|
136
|
+
# Query namespaced attributes
|
|
137
|
+
book_with_isbn = doc.at_xpath('//lib:book[@isbn:value]', namespaces)
|
|
138
|
+
puts book_with_isbn['isbn:value'] # => "978-0-123456-78-9"
|
|
139
|
+
----
|
|
140
|
+
|
|
141
|
+
=== Step 4: Default namespaces
|
|
142
|
+
|
|
143
|
+
Handle default (unprefixed) namespaces:
|
|
144
|
+
|
|
145
|
+
[source,ruby]
|
|
146
|
+
----
|
|
147
|
+
xml = <<~XML
|
|
148
|
+
<library xmlns="http://example.org/library">
|
|
149
|
+
<book>
|
|
150
|
+
<title>Ruby Programming</title>
|
|
151
|
+
</book>
|
|
152
|
+
</library>
|
|
153
|
+
XML
|
|
154
|
+
|
|
155
|
+
doc = Moxml.new.parse(xml)
|
|
156
|
+
|
|
157
|
+
# Map default namespace to a prefix for XPath
|
|
158
|
+
namespaces = {
|
|
159
|
+
'lib' => 'http://example.org/library'
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Query elements in default namespace
|
|
163
|
+
books = doc.xpath('//lib:book', namespaces)
|
|
164
|
+
titles = doc.xpath('//lib:title', namespaces)
|
|
165
|
+
|
|
166
|
+
puts books.length # => 1
|
|
167
|
+
puts titles.first.text # => "Ruby Programming"
|
|
168
|
+
----
|
|
169
|
+
|
|
170
|
+
=== Step 5: Multiple namespaces
|
|
171
|
+
|
|
172
|
+
Work with documents using multiple namespaces:
|
|
173
|
+
|
|
174
|
+
[source,ruby]
|
|
175
|
+
----
|
|
176
|
+
xml = <<~XML
|
|
177
|
+
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/"
|
|
178
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
179
|
+
<item>
|
|
180
|
+
<title>Article Title</title>
|
|
181
|
+
<dc:creator>Author Name</dc:creator>
|
|
182
|
+
<content:encoded><![CDATA[Article content]]></content:encoded>
|
|
183
|
+
</item>
|
|
184
|
+
</rss>
|
|
185
|
+
XML
|
|
186
|
+
|
|
187
|
+
doc = Moxml.new.parse(xml)
|
|
188
|
+
|
|
189
|
+
# Map all namespaces
|
|
190
|
+
ns = {
|
|
191
|
+
'content' => 'http://purl.org/rss/1.0/modules/content/',
|
|
192
|
+
'dc' => 'http://purl.org/dc/elements/1.1/'
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# Query across namespaces
|
|
196
|
+
items = doc.xpath('//item')
|
|
197
|
+
creators = doc.xpath('//dc:creator', ns)
|
|
198
|
+
content = doc.xpath('//content:encoded', ns)
|
|
199
|
+
|
|
200
|
+
puts creators.first.text # => "Author Name"
|
|
201
|
+
puts content.first.text # => "Article content"
|
|
202
|
+
----
|
|
203
|
+
|
|
204
|
+
=== Step 6: Namespace inheritance
|
|
205
|
+
|
|
206
|
+
Child elements inherit parent namespaces:
|
|
207
|
+
|
|
208
|
+
[source,ruby]
|
|
209
|
+
----
|
|
210
|
+
doc = Moxml.new.create_document
|
|
211
|
+
|
|
212
|
+
# Parent with namespace
|
|
213
|
+
parent = doc.create_element('parent')
|
|
214
|
+
parent.add_namespace('ex', 'http://example.org')
|
|
215
|
+
doc.add_child(parent)
|
|
216
|
+
|
|
217
|
+
# Child inherits namespace
|
|
218
|
+
child = doc.create_element('ex:child')
|
|
219
|
+
child.text = 'Inherits ex namespace'
|
|
220
|
+
parent.add_child(child)
|
|
221
|
+
|
|
222
|
+
# Grandchild also inherits
|
|
223
|
+
grandchild = doc.create_element('ex:grandchild')
|
|
224
|
+
grandchild.text = 'Also inherits'
|
|
225
|
+
child.add_child(grandchild)
|
|
226
|
+
|
|
227
|
+
# Query with inherited namespace
|
|
228
|
+
ns = { 'ex' => 'http://example.org' }
|
|
229
|
+
descendants = doc.xpath('//ex:*', ns)
|
|
230
|
+
puts descendants.length # => 3 (parent, child, grandchild)
|
|
231
|
+
----
|
|
232
|
+
|
|
233
|
+
=== Step 7: Namespace scope and context
|
|
234
|
+
|
|
235
|
+
Understand namespace scope:
|
|
236
|
+
|
|
237
|
+
[source,ruby]
|
|
238
|
+
----
|
|
239
|
+
xml = <<~XML
|
|
240
|
+
<root xmlns:a="http://a.org">
|
|
241
|
+
<a:parent>
|
|
242
|
+
<a:child id="1"/>
|
|
243
|
+
<b:child xmlns:b="http://b.org" id="2"/>
|
|
244
|
+
</a:parent>
|
|
245
|
+
</root>
|
|
246
|
+
XML
|
|
247
|
+
|
|
248
|
+
doc = Moxml.new.parse(xml)
|
|
249
|
+
|
|
250
|
+
# Query with appropriate namespace scope
|
|
251
|
+
ns_a = { 'a' => 'http://a.org' }
|
|
252
|
+
ns_b = { 'b' => 'http://b.org' }
|
|
253
|
+
|
|
254
|
+
a_children = doc.xpath('//a:child', ns_a)
|
|
255
|
+
b_children = doc.xpath('//b:child', ns_b)
|
|
256
|
+
|
|
257
|
+
puts a_children.length # => 1 (only <a:child>)
|
|
258
|
+
puts b_children.length # => 1 (only <b:child>)
|
|
259
|
+
|
|
260
|
+
# Both namespaces in one query
|
|
261
|
+
ns_both = ns_a.merge(ns_b)
|
|
262
|
+
all_children = doc.xpath('//a:child | //b:child', ns_both)
|
|
263
|
+
puts all_children.length # => 2
|
|
264
|
+
----
|
|
265
|
+
|
|
266
|
+
=== Adapter compatibility
|
|
267
|
+
|
|
268
|
+
**Full namespace support:**
|
|
269
|
+
|
|
270
|
+
* link:../pages/adapters/nokogiri[Nokogiri] - ✅ All features
|
|
271
|
+
* link:../pages/adapters/libxml[LibXML] - ✅ All features
|
|
272
|
+
* link:../pages/adapters/oga[Oga] - ✅ All features
|
|
273
|
+
|
|
274
|
+
**Limited namespace support:**
|
|
275
|
+
|
|
276
|
+
* link:../pages/adapters/rexml[REXML] - ⚠️ No namespace XPath
|
|
277
|
+
* link:../pages/adapters/ox[Ox] - ⚠️ Basic only, no XPath
|
|
278
|
+
|
|
279
|
+
=== Troubleshooting
|
|
280
|
+
|
|
281
|
+
**Namespace XPath not working:**
|
|
282
|
+
|
|
283
|
+
[source,ruby]
|
|
284
|
+
----
|
|
285
|
+
# If namespace query returns nothing, check:
|
|
286
|
+
|
|
287
|
+
# 1. Namespace mapping is correct
|
|
288
|
+
puts doc.root.namespace.uri # Verify actual URI
|
|
289
|
+
|
|
290
|
+
# 2. Using correct prefix in XPath
|
|
291
|
+
# Must match your mapping, not the document's prefix
|
|
292
|
+
|
|
293
|
+
# 3. Adapter supports namespace XPath
|
|
294
|
+
puts context.config.adapter.name
|
|
295
|
+
# REXML and Ox have limited support
|
|
296
|
+
----
|
|
297
|
+
|
|
298
|
+
**Namespace not inherited:**
|
|
299
|
+
|
|
300
|
+
[source,ruby]
|
|
301
|
+
----
|
|
302
|
+
# Ox doesn't support namespace inheritance
|
|
303
|
+
# Workaround: explicitly set namespace on each element
|
|
304
|
+
child.add_namespace('ex', 'http://example.org')
|
|
305
|
+
----
|
|
306
|
+
|
|
307
|
+
=== Best practices
|
|
308
|
+
|
|
309
|
+
. **Always define namespace mappings** for XPath queries
|
|
310
|
+
. **Use consistent prefix names** across your application
|
|
311
|
+
. **Check adapter compatibility** for namespace features
|
|
312
|
+
. **Test namespace operations** with your chosen adapter
|
|
313
|
+
. **Document namespace URIs** used in your XML
|
|
314
|
+
|
|
315
|
+
=== Next steps
|
|
316
|
+
|
|
317
|
+
* link:builder-pattern[Builder pattern] - Create complex namespaced documents
|
|
318
|
+
* link:../guides/namespace-management[Namespace management guide]
|
|
319
|
+
* link:../references/namespace-api[Namespace API reference]
|
|
320
|
+
|
|
321
|
+
=== See also
|
|
322
|
+
|
|
323
|
+
* link:../pages/compatibility[Compatibility matrix] - Namespace support comparison
|
|
324
|
+
* link:../guides/xpath-queries[XPath queries guide] - Advanced namespace queries
|