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.
Files changed (215) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-repos.json +5 -0
  3. data/.github/workflows/dependent-tests.yml +20 -0
  4. data/.github/workflows/docs.yml +59 -0
  5. data/.github/workflows/rake.yml +10 -10
  6. data/.github/workflows/release.yml +5 -3
  7. data/.gitignore +37 -0
  8. data/.rubocop.yml +15 -7
  9. data/.rubocop_todo.yml +224 -43
  10. data/Gemfile +14 -9
  11. data/LICENSE.md +6 -2
  12. data/README.adoc +535 -373
  13. data/Rakefile +53 -0
  14. data/benchmarks/.gitignore +6 -0
  15. data/benchmarks/generate_report.rb +550 -0
  16. data/docs/Gemfile +13 -0
  17. data/docs/_config.yml +138 -0
  18. data/docs/_guides/advanced-features.adoc +87 -0
  19. data/docs/_guides/development-testing.adoc +165 -0
  20. data/docs/_guides/index.adoc +51 -0
  21. data/docs/_guides/modifying-xml.adoc +292 -0
  22. data/docs/_guides/parsing-xml.adoc +230 -0
  23. data/docs/_guides/sax-parsing.adoc +603 -0
  24. data/docs/_guides/working-with-documents.adoc +118 -0
  25. data/docs/_guides/xml-declaration.adoc +450 -0
  26. data/docs/_pages/adapter-compatibility.adoc +369 -0
  27. data/docs/_pages/adapters/headed-ox.adoc +237 -0
  28. data/docs/_pages/adapters/index.adoc +97 -0
  29. data/docs/_pages/adapters/libxml.adoc +285 -0
  30. data/docs/_pages/adapters/nokogiri.adoc +251 -0
  31. data/docs/_pages/adapters/oga.adoc +291 -0
  32. data/docs/_pages/adapters/ox.adoc +56 -0
  33. data/docs/_pages/adapters/rexml.adoc +292 -0
  34. data/docs/_pages/best-practices.adoc +429 -0
  35. data/docs/_pages/compatibility.adoc +467 -0
  36. data/docs/_pages/configuration.adoc +250 -0
  37. data/docs/_pages/error-handling.adoc +349 -0
  38. data/docs/_pages/headed-ox-limitations.adoc +574 -0
  39. data/docs/_pages/headed-ox.adoc +1025 -0
  40. data/docs/_pages/index.adoc +35 -0
  41. data/docs/_pages/installation.adoc +140 -0
  42. data/docs/_pages/node-api-reference.adoc +49 -0
  43. data/docs/_pages/performance.adoc +35 -0
  44. data/docs/_pages/quick-start.adoc +243 -0
  45. data/docs/_pages/thread-safety.adoc +28 -0
  46. data/docs/_references/document-api.adoc +407 -0
  47. data/docs/_references/index.adoc +48 -0
  48. data/docs/_tutorials/basic-usage.adoc +267 -0
  49. data/docs/_tutorials/builder-pattern.adoc +342 -0
  50. data/docs/_tutorials/index.adoc +33 -0
  51. data/docs/_tutorials/namespace-handling.adoc +324 -0
  52. data/docs/_tutorials/xpath-queries.adoc +358 -0
  53. data/docs/index.adoc +122 -0
  54. data/examples/README.md +124 -0
  55. data/examples/api_client/README.md +424 -0
  56. data/examples/api_client/api_client.rb +394 -0
  57. data/examples/api_client/example_response.xml +48 -0
  58. data/examples/headed_ox_example/README.md +90 -0
  59. data/examples/headed_ox_example/headed_ox_demo.rb +71 -0
  60. data/examples/rss_parser/README.md +194 -0
  61. data/examples/rss_parser/example_feed.xml +93 -0
  62. data/examples/rss_parser/rss_parser.rb +189 -0
  63. data/examples/sax_parsing/README.md +50 -0
  64. data/examples/sax_parsing/data_extractor.rb +75 -0
  65. data/examples/sax_parsing/example.xml +21 -0
  66. data/examples/sax_parsing/large_file.rb +78 -0
  67. data/examples/sax_parsing/simple_parser.rb +55 -0
  68. data/examples/web_scraper/README.md +352 -0
  69. data/examples/web_scraper/example_page.html +201 -0
  70. data/examples/web_scraper/web_scraper.rb +312 -0
  71. data/lib/moxml/adapter/base.rb +107 -28
  72. data/lib/moxml/adapter/customized_libxml/cdata.rb +28 -0
  73. data/lib/moxml/adapter/customized_libxml/comment.rb +24 -0
  74. data/lib/moxml/adapter/customized_libxml/declaration.rb +85 -0
  75. data/lib/moxml/adapter/customized_libxml/element.rb +39 -0
  76. data/lib/moxml/adapter/customized_libxml/node.rb +44 -0
  77. data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +31 -0
  78. data/lib/moxml/adapter/customized_libxml/text.rb +27 -0
  79. data/lib/moxml/adapter/customized_oga/xml_generator.rb +1 -1
  80. data/lib/moxml/adapter/customized_ox/attribute.rb +28 -1
  81. data/lib/moxml/adapter/customized_rexml/formatter.rb +13 -8
  82. data/lib/moxml/adapter/headed_ox.rb +161 -0
  83. data/lib/moxml/adapter/libxml.rb +1564 -0
  84. data/lib/moxml/adapter/nokogiri.rb +156 -9
  85. data/lib/moxml/adapter/oga.rb +190 -15
  86. data/lib/moxml/adapter/ox.rb +322 -28
  87. data/lib/moxml/adapter/rexml.rb +157 -28
  88. data/lib/moxml/adapter.rb +21 -4
  89. data/lib/moxml/attribute.rb +6 -0
  90. data/lib/moxml/builder.rb +40 -4
  91. data/lib/moxml/config.rb +8 -3
  92. data/lib/moxml/context.rb +57 -2
  93. data/lib/moxml/declaration.rb +9 -0
  94. data/lib/moxml/doctype.rb +13 -1
  95. data/lib/moxml/document.rb +53 -6
  96. data/lib/moxml/document_builder.rb +34 -5
  97. data/lib/moxml/element.rb +71 -2
  98. data/lib/moxml/error.rb +175 -6
  99. data/lib/moxml/node.rb +155 -4
  100. data/lib/moxml/node_set.rb +34 -0
  101. data/lib/moxml/sax/block_handler.rb +194 -0
  102. data/lib/moxml/sax/element_handler.rb +124 -0
  103. data/lib/moxml/sax/handler.rb +113 -0
  104. data/lib/moxml/sax.rb +31 -0
  105. data/lib/moxml/version.rb +1 -1
  106. data/lib/moxml/xml_utils/encoder.rb +4 -4
  107. data/lib/moxml/xml_utils.rb +7 -4
  108. data/lib/moxml/xpath/ast/node.rb +159 -0
  109. data/lib/moxml/xpath/cache.rb +91 -0
  110. data/lib/moxml/xpath/compiler.rb +1770 -0
  111. data/lib/moxml/xpath/context.rb +26 -0
  112. data/lib/moxml/xpath/conversion.rb +124 -0
  113. data/lib/moxml/xpath/engine.rb +52 -0
  114. data/lib/moxml/xpath/errors.rb +101 -0
  115. data/lib/moxml/xpath/lexer.rb +304 -0
  116. data/lib/moxml/xpath/parser.rb +485 -0
  117. data/lib/moxml/xpath/ruby/generator.rb +269 -0
  118. data/lib/moxml/xpath/ruby/node.rb +193 -0
  119. data/lib/moxml/xpath.rb +37 -0
  120. data/lib/moxml.rb +5 -2
  121. data/moxml.gemspec +3 -1
  122. data/old-specs/moxml/adapter/customized_libxml/.gitkeep +6 -0
  123. data/spec/consistency/README.md +77 -0
  124. data/spec/{moxml/examples/adapter_spec.rb → consistency/adapter_parity_spec.rb} +4 -4
  125. data/spec/examples/README.md +75 -0
  126. data/spec/{support/shared_examples/examples/attribute.rb → examples/attribute_examples_spec.rb} +1 -1
  127. data/spec/{support/shared_examples/examples/basic_usage.rb → examples/basic_usage_spec.rb} +2 -2
  128. data/spec/{support/shared_examples/examples/namespace.rb → examples/namespace_examples_spec.rb} +3 -3
  129. data/spec/{support/shared_examples/examples/readme_examples.rb → examples/readme_examples_spec.rb} +6 -4
  130. data/spec/{support/shared_examples/examples/xpath.rb → examples/xpath_examples_spec.rb} +10 -6
  131. data/spec/integration/README.md +71 -0
  132. data/spec/{moxml/all_with_adapters_spec.rb → integration/all_adapters_spec.rb} +3 -2
  133. data/spec/integration/headed_ox_integration_spec.rb +326 -0
  134. data/spec/{support → integration}/shared_examples/edge_cases.rb +37 -10
  135. data/spec/integration/shared_examples/high_level/.gitkeep +0 -0
  136. data/spec/{support/shared_examples/context.rb → integration/shared_examples/high_level/context_behavior.rb} +2 -1
  137. data/spec/{support/shared_examples/integration.rb → integration/shared_examples/integration_workflows.rb} +23 -6
  138. data/spec/integration/shared_examples/node_wrappers/.gitkeep +0 -0
  139. data/spec/{support/shared_examples/cdata.rb → integration/shared_examples/node_wrappers/cdata_behavior.rb} +6 -1
  140. data/spec/{support/shared_examples/comment.rb → integration/shared_examples/node_wrappers/comment_behavior.rb} +2 -1
  141. data/spec/{support/shared_examples/declaration.rb → integration/shared_examples/node_wrappers/declaration_behavior.rb} +5 -5
  142. data/spec/{support/shared_examples/doctype.rb → integration/shared_examples/node_wrappers/doctype_behavior.rb} +2 -2
  143. data/spec/{support/shared_examples/document.rb → integration/shared_examples/node_wrappers/document_behavior.rb} +1 -1
  144. data/spec/{support/shared_examples/node.rb → integration/shared_examples/node_wrappers/node_behavior.rb} +9 -2
  145. data/spec/{support/shared_examples/node_set.rb → integration/shared_examples/node_wrappers/node_set_behavior.rb} +1 -18
  146. data/spec/{support/shared_examples/processing_instruction.rb → integration/shared_examples/node_wrappers/processing_instruction_behavior.rb} +6 -2
  147. data/spec/moxml/README.md +41 -0
  148. data/spec/moxml/adapter/.gitkeep +0 -0
  149. data/spec/moxml/adapter/README.md +61 -0
  150. data/spec/moxml/adapter/base_spec.rb +27 -0
  151. data/spec/moxml/adapter/headed_ox_spec.rb +311 -0
  152. data/spec/moxml/adapter/libxml_spec.rb +14 -0
  153. data/spec/moxml/adapter/ox_spec.rb +9 -8
  154. data/spec/moxml/adapter/shared_examples/.gitkeep +0 -0
  155. data/spec/{support/shared_examples/xml_adapter.rb → moxml/adapter/shared_examples/adapter_contract.rb} +39 -12
  156. data/spec/moxml/adapter_spec.rb +16 -0
  157. data/spec/moxml/attribute_spec.rb +30 -0
  158. data/spec/moxml/builder_spec.rb +33 -0
  159. data/spec/moxml/cdata_spec.rb +31 -0
  160. data/spec/moxml/comment_spec.rb +31 -0
  161. data/spec/moxml/config_spec.rb +3 -3
  162. data/spec/moxml/context_spec.rb +28 -0
  163. data/spec/moxml/declaration_preservation_spec.rb +217 -0
  164. data/spec/moxml/declaration_spec.rb +36 -0
  165. data/spec/moxml/doctype_spec.rb +33 -0
  166. data/spec/moxml/document_builder_spec.rb +30 -0
  167. data/spec/moxml/document_spec.rb +105 -0
  168. data/spec/moxml/element_spec.rb +143 -0
  169. data/spec/moxml/error_spec.rb +266 -22
  170. data/spec/{moxml_spec.rb → moxml/moxml_spec.rb} +9 -9
  171. data/spec/moxml/namespace_spec.rb +32 -0
  172. data/spec/moxml/node_set_spec.rb +39 -0
  173. data/spec/moxml/node_spec.rb +37 -0
  174. data/spec/moxml/processing_instruction_spec.rb +34 -0
  175. data/spec/moxml/sax_spec.rb +1067 -0
  176. data/spec/moxml/text_spec.rb +31 -0
  177. data/spec/moxml/version_spec.rb +14 -0
  178. data/spec/moxml/xml_utils/.gitkeep +0 -0
  179. data/spec/moxml/xml_utils/encoder_spec.rb +27 -0
  180. data/spec/moxml/xml_utils_spec.rb +49 -0
  181. data/spec/moxml/xpath/ast/node_spec.rb +83 -0
  182. data/spec/moxml/xpath/axes_spec.rb +296 -0
  183. data/spec/moxml/xpath/cache_spec.rb +358 -0
  184. data/spec/moxml/xpath/compiler_spec.rb +406 -0
  185. data/spec/moxml/xpath/context_spec.rb +210 -0
  186. data/spec/moxml/xpath/conversion_spec.rb +365 -0
  187. data/spec/moxml/xpath/fixtures/sample.xml +25 -0
  188. data/spec/moxml/xpath/functions/boolean_functions_spec.rb +114 -0
  189. data/spec/moxml/xpath/functions/node_functions_spec.rb +145 -0
  190. data/spec/moxml/xpath/functions/numeric_functions_spec.rb +164 -0
  191. data/spec/moxml/xpath/functions/position_functions_spec.rb +93 -0
  192. data/spec/moxml/xpath/functions/special_functions_spec.rb +89 -0
  193. data/spec/moxml/xpath/functions/string_functions_spec.rb +381 -0
  194. data/spec/moxml/xpath/lexer_spec.rb +488 -0
  195. data/spec/moxml/xpath/parser_integration_spec.rb +210 -0
  196. data/spec/moxml/xpath/parser_spec.rb +364 -0
  197. data/spec/moxml/xpath/ruby/generator_spec.rb +421 -0
  198. data/spec/moxml/xpath/ruby/node_spec.rb +291 -0
  199. data/spec/moxml/xpath_capabilities_spec.rb +199 -0
  200. data/spec/moxml/xpath_spec.rb +77 -0
  201. data/spec/performance/README.md +83 -0
  202. data/spec/performance/benchmark_spec.rb +64 -0
  203. data/spec/{support/shared_examples/examples/memory.rb → performance/memory_usage_spec.rb} +4 -1
  204. data/spec/{support/shared_examples/examples/thread_safety.rb → performance/thread_safety_spec.rb} +3 -1
  205. data/spec/performance/xpath_benchmark_spec.rb +259 -0
  206. data/spec/spec_helper.rb +58 -1
  207. data/spec/support/xml_matchers.rb +1 -1
  208. metadata +178 -34
  209. data/spec/support/shared_examples/examples/benchmark_spec.rb +0 -51
  210. /data/spec/{support/shared_examples/builder.rb → integration/shared_examples/high_level/builder_behavior.rb} +0 -0
  211. /data/spec/{support/shared_examples/document_builder.rb → integration/shared_examples/high_level/document_builder_behavior.rb} +0 -0
  212. /data/spec/{support/shared_examples/attribute.rb → integration/shared_examples/node_wrappers/attribute_behavior.rb} +0 -0
  213. /data/spec/{support/shared_examples/element.rb → integration/shared_examples/node_wrappers/element_behavior.rb} +0 -0
  214. /data/spec/{support/shared_examples/namespace.rb → integration/shared_examples/node_wrappers/namespace_behavior.rb} +0 -0
  215. /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