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,358 @@
1
+ ---
2
+ title: XPath queries
3
+ nav_order: 3
4
+ ---
5
+
6
+ == XPath queries
7
+
8
+ === Purpose
9
+
10
+ Master XPath querying in Moxml to efficiently find and select XML nodes using
11
+ path expressions, predicates, and functions.
12
+
13
+ === Prerequisites
14
+
15
+ * Basic understanding of XPath syntax
16
+ * Moxml installed with an adapter
17
+ * Familiarity with link:basic-usage[basic Moxml usage]
18
+
19
+ === Step 1: Basic path expressions
20
+
21
+ Start with simple path selection:
22
+
23
+ [source,ruby]
24
+ ----
25
+ require 'moxml'
26
+
27
+ xml = <<~XML
28
+ <library>
29
+ <section name="programming">
30
+ <book id="1">Ruby Basics</book>
31
+ <book id="2">Advanced Ruby</book>
32
+ </section>
33
+ <section name="fiction">
34
+ <book id="3">Ruby Story</book>
35
+ </section>
36
+ </library>
37
+ XML
38
+
39
+ doc = Moxml.new.parse(xml)
40
+
41
+ # Absolute path - from root
42
+ sections = doc.xpath('/library/section')
43
+ puts sections.length # => 2
44
+
45
+ # Descendant-or-self - any depth
46
+ all_books = doc.xpath('//book')
47
+ puts all_books.length # => 3
48
+
49
+ # Relative path - from current node
50
+ section = sections.first
51
+ books = section.xpath('.//book')
52
+ puts books.length # => 2
53
+
54
+ # Parent path
55
+ book = all_books.first
56
+ parent = book.xpath('..').first
57
+ puts parent.name # => "section"
58
+ ----
59
+
60
+ === Step 2: Attribute predicates
61
+
62
+ Filter elements by attributes:
63
+
64
+ [source,ruby]
65
+ ----
66
+ # Elements with specific attribute
67
+ books_with_id = doc.xpath('//book[@id]')
68
+ puts books_with_id.length # => 3 (all have id)
69
+
70
+ # Elements with attribute value
71
+ book1 = doc.at_xpath('//book[@id="1"]')
72
+ puts book1.text # => "Ruby Basics"
73
+
74
+ # Multiple attribute conditions
75
+ sections = doc.xpath('//section[@name="programming"]')
76
+ puts sections.first['name'] # => "programming"
77
+ ----
78
+
79
+ === Step 3: Position predicates
80
+
81
+ Select by position:
82
+
83
+ [source,ruby]
84
+ ----
85
+ # First book
86
+ first = doc.at_xpath('//book[1]')
87
+ puts first.text # => "Ruby Basics"
88
+
89
+ # Last book
90
+ last = doc.at_xpath('//book[last()]')
91
+ puts last.text # => "Ruby Story"
92
+
93
+ # First two books
94
+ first_two = doc.xpath('//book[position() <= 2]')
95
+ puts first_two.length # => 2
96
+
97
+ # Every second book
98
+ even_books = doc.xpath('//book[position() mod 2 = 0]')
99
+ ----
100
+
101
+ === Step 4: Text predicates
102
+
103
+ Filter by text content:
104
+
105
+ [source,ruby]
106
+ ----
107
+ xml = <<~XML
108
+ <library>
109
+ <book><title>Ruby Programming</title></book>
110
+ <book><title>Python Programming</title></book>
111
+ <book><title>Advanced Ruby</title></book>
112
+ </library>
113
+ XML
114
+
115
+ doc = Moxml.new.parse(xml)
116
+
117
+ # Exact text match
118
+ ruby_book = doc.at_xpath('//title[text()="Ruby Programming"]')
119
+ puts ruby_book.text # => "Ruby Programming"
120
+
121
+ # Contains text
122
+ ruby_books = doc.xpath('//title[contains(text(), "Ruby")]')
123
+ puts ruby_books.length # => 2
124
+
125
+ # Starts with
126
+ programming = doc.xpath('//title[starts-with(text(), "Ruby")]')
127
+ ----
128
+
129
+ === Step 5: Logical operators
130
+
131
+ Combine conditions:
132
+
133
+ [source,ruby]
134
+ ----
135
+ xml = <<~XML
136
+ <library>
137
+ <book id="1" category="programming" price="29.99">
138
+ <title>Ruby Basics</title>
139
+ </book>
140
+ <book id="2" category="fiction" price="19.99">
141
+ <title>Ruby Story</title>
142
+ </book>
143
+ <book id="3" category="programming" price="39.99">
144
+ <title>Advanced Ruby</title>
145
+ </book>
146
+ </library>
147
+ XML
148
+
149
+ doc = Moxml.new.parse(xml)
150
+
151
+ # AND condition
152
+ cheap_programming = doc.xpath('//book[@category="programming" and @price < 35]')
153
+ puts cheap_programming.length # => 1
154
+
155
+ # OR condition
156
+ fiction_or_cheap = doc.xpath('//book[@category="fiction" or @price < 25]')
157
+ puts fiction_or_cheap.length # => 2
158
+
159
+ # Complex conditions
160
+ results = doc.xpath('//book[(@category="programming" and @price < 40) or @id="2"]')
161
+ puts results.length # => 2
162
+ ----
163
+
164
+ === Step 6: XPath functions
165
+
166
+ Use built-in XPath functions:
167
+
168
+ [source,ruby]
169
+ ----
170
+ # Count function
171
+ book_count = doc.xpath('count(//book)')
172
+ puts book_count # => 3
173
+
174
+ # String functions
175
+ titles = doc.xpath('//title[string-length(text()) > 10]')
176
+
177
+ # Concat function
178
+ full_title = doc.xpath('concat(//book[1]/title, " - Edition 2")')
179
+
180
+ # Position functions
181
+ middle_books = doc.xpath('//book[position() > 1 and position() < last()]')
182
+ ----
183
+
184
+ === Step 7: Namespace-aware queries
185
+
186
+ Query XML with namespaces:
187
+
188
+ [source,ruby]
189
+ ----
190
+ xml = <<~XML
191
+ <library xmlns="http://example.org/library"
192
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
193
+ <book>
194
+ <dc:title>Ruby Programming</dc:title>
195
+ <dc:creator>Jane Smith</dc:creator>
196
+ </book>
197
+ </library>
198
+ XML
199
+
200
+ doc = Moxml.new.parse(xml)
201
+
202
+ # Define namespace mappings
203
+ namespaces = {
204
+ 'lib' => 'http://example.org/library',
205
+ 'dc' => 'http://purl.org/dc/elements/1.1/'
206
+ }
207
+
208
+ # Query with namespace prefixes
209
+ books = doc.xpath('//lib:book', namespaces)
210
+ titles = doc.xpath('//dc:title', namespaces)
211
+ creators = doc.xpath('//dc:creator', namespaces)
212
+
213
+ puts titles.first.text # => "Ruby Programming"
214
+ puts creators.first.text # => "Jane Smith"
215
+
216
+ # Complex namespace queries
217
+ all_dc_elements = doc.xpath('//dc:*', namespaces)
218
+ puts all_dc_elements.length # => 2 (title + creator)
219
+ ----
220
+
221
+ === Step 8: Axes and advanced selection
222
+
223
+ Use XPath axes for complex traversal:
224
+
225
+ [source,ruby]
226
+ ----
227
+ xml = <<~XML
228
+ <book>
229
+ <chapter id="1">Introduction</chapter>
230
+ <chapter id="2">Basics</chapter>
231
+ <chapter id="3">Advanced</chapter>
232
+ </book>
233
+ XML
234
+
235
+ doc = Moxml.new.parse(xml)
236
+
237
+ # Following sibling
238
+ chapter1 = doc.at_xpath('//chapter[@id="1"]')
239
+ next_chapters = chapter1.xpath('following-sibling::chapter')
240
+ puts next_chapters.length # => 2
241
+
242
+ # Preceding sibling
243
+ chapter3 = doc.at_xpath('//chapter[@id="3"]')
244
+ prev_chapters = chapter3.xpath('preceding-sibling::chapter')
245
+ puts prev_chapters.length # => 2
246
+
247
+ # Ancestor
248
+ chapter = doc.at_xpath('//chapter[@id="2"]')
249
+ book = chapter.xpath('ancestor::book').first
250
+ puts book.name # => "book"
251
+
252
+ # Descendant
253
+ all_text = doc.root.xpath('descendant::text()')
254
+ ----
255
+
256
+ === Adapter-specific considerations
257
+
258
+ ==== Nokogiri, LibXML, Oga
259
+
260
+ Full XPath 1.0 support - all examples above work perfectly.
261
+
262
+ ==== REXML
263
+
264
+ Limited support - namespace queries don't work:
265
+
266
+ [source,ruby]
267
+ ----
268
+ # Works
269
+ doc.xpath('//book')
270
+ doc.xpath('//book[@id="1"]')
271
+
272
+ # Does NOT work
273
+ doc.xpath('//ns:book', namespaces) # ❌
274
+
275
+ # Workaround
276
+ books = doc.xpath('//book')
277
+ books.select { |b| b.namespace == 'http://example.org' }
278
+ ----
279
+
280
+ ==== Ox
281
+
282
+ Basic paths only - use Ruby for complex filtering:
283
+
284
+ [source,ruby]
285
+ ----
286
+ # Works
287
+ all_books = doc.xpath('//book')
288
+
289
+ # Does NOT work - use Ruby instead
290
+ # doc.xpath('//book[@id="1"]') # ❌
291
+
292
+ # Workaround
293
+ books = doc.xpath('//book')
294
+ book1 = books.find { |b| b['id'] == '1' }
295
+ ----
296
+
297
+ === Best practices
298
+
299
+ . **Use specific paths** when possible for better performance
300
+ . **Cache XPath results** if querying multiple times
301
+ . **Choose the right adapter** for your XPath needs
302
+ . **Test namespace queries** with your chosen adapter
303
+ . **Use relative paths** (`.//`) when querying from elements
304
+
305
+ === Common patterns
306
+
307
+ ==== Extract structured data
308
+
309
+ [source,ruby]
310
+ ----
311
+ products = doc.xpath('//product').map do |product|
312
+ {
313
+ id: product['id'],
314
+ name: product.at_xpath('.//name').text,
315
+ price: product.at_xpath('.//price').text.to_f,
316
+ stock: product.at_xpath('.//stock').text.to_i
317
+ }
318
+ end
319
+ ----
320
+
321
+ ==== Conditional processing
322
+
323
+ [source,ruby]
324
+ ----
325
+ # Find and process matching elements
326
+ expensive_books = doc.xpath('//book[price > 30]')
327
+ expensive_books.each do |book|
328
+ # Apply discount
329
+ price = book.at_xpath('.//price')
330
+ current = price.text.to_f
331
+ price.text = (current * 0.9).to_s
332
+ end
333
+ ----
334
+
335
+ ==== Validation
336
+
337
+ [source,ruby]
338
+ ----
339
+ # Check required elements exist
340
+ required = ['title', 'author', 'isbn']
341
+ missing = required.reject { |elem| doc.at_xpath("//#{elem}") }
342
+
343
+ if missing.any?
344
+ raise "Missing elements: #{missing.join(', ')}"
345
+ end
346
+ ----
347
+
348
+ === Next steps
349
+
350
+ * link:namespace-handling[Namespace handling] - Master XML namespaces
351
+ * link:working-with-elements[Working with elements] - Element manipulation
352
+ * link:../guides/xpath-queries[XPath queries guide] - Advanced patterns
353
+
354
+ === See also
355
+
356
+ * link:../pages/adapters/[Adapters] - XPath capabilities per adapter
357
+ * link:../references/xpath-api[XPath API reference]
358
+ * link:../pages/compatibility[Compatibility matrix] - XPath support comparison
data/docs/index.adoc ADDED
@@ -0,0 +1,122 @@
1
+ ---
2
+ title: Home
3
+ nav_order: 1
4
+ ---
5
+ :page-layout: home
6
+
7
+ == Moxml: Modern XML processing for Ruby
8
+
9
+ image:https://github.com/lutaml/moxml/workflows/rake/badge.svg["Build Status", link="https://github.com/lutaml/moxml/actions?workflow=rake"]
10
+ image:https://img.shields.io/gem/v/moxml.svg["Gem Version", link="https://rubygems.org/gems/moxml"]
11
+
12
+ Moxml provides a unified, modern XML processing interface for Ruby
13
+ applications. It offers a consistent API that abstracts away the underlying
14
+ XML implementation details while maintaining high performance through
15
+ efficient node mapping and native XPath querying.
16
+
17
+ == Key features
18
+
19
+ * **Unified interface** - Consistent API across different XML libraries
20
+ * **Multiple adapters** - Support for Nokogiri, LibXML, Oga, REXML, Ox, and HeadedOx
21
+ * **Full XPath support** - Native XPath querying with namespace handling
22
+ * **SAX parsing** - Memory-efficient event-driven XML processing
23
+ * **Ruby-idiomatic** - Natural, expressive API for XML manipulation
24
+ * **High performance** - Efficient node mapping and adapter optimization
25
+ * **Thread-safe** - Safe for concurrent operations
26
+ * **Complete node support** - Handle all XML node types
27
+
28
+ == Quick example
29
+
30
+ [source,ruby]
31
+ ----
32
+ require 'moxml'
33
+
34
+ # Parse XML
35
+ doc = Moxml.new.parse('<library><book id="1">Ruby</book></library>')
36
+
37
+ # Query with XPath
38
+ books = doc.xpath('//book[@id="1"]')
39
+ puts books.first.text # => "Ruby"
40
+
41
+ # Modify content
42
+ books.first.text = 'Advanced Ruby'
43
+ books.first['edition'] = '2nd'
44
+
45
+ # Output formatted XML
46
+ puts doc.to_xml(indent: 2)
47
+ ----
48
+
49
+ == Getting started
50
+
51
+ Choose your path:
52
+
53
+ [.grid]
54
+ --
55
+ link:pages/installation[**Installation**]::
56
+ Install Moxml and choose an XML adapter
57
+
58
+ link:tutorials/[**Tutorials**]::
59
+ Step-by-step guides to learn Moxml
60
+
61
+ link:guides/[**Guides**]::
62
+ Task-oriented documentation for common operations
63
+
64
+ link:pages/adapters/[**Adapters**]::
65
+ Learn about supported XML libraries
66
+ --
67
+
68
+ == Supported adapters
69
+
70
+ [cols="2,3,1"]
71
+ |===
72
+ | Adapter | Best for | Performance
73
+
74
+ | link:pages/adapters/nokogiri[Nokogiri]
75
+ | Industry standard, wide compatibility
76
+ | Fast
77
+
78
+ | link:pages/adapters/libxml[LibXML]
79
+ | Alternative to Nokogiri, full features
80
+ | Very Fast
81
+
82
+ | link:pages/adapters/oga[Oga]
83
+ | Pure Ruby, no C extensions
84
+ | Fast
85
+
86
+ | link:pages/adapters/rexml[REXML]
87
+ | Standard library, maximum portability
88
+ | Medium
89
+
90
+ | link:pages/adapters/ox[Ox]
91
+ | Maximum speed, simple documents
92
+ | Very Fast
93
+
94
+ | link:pages/adapters/headed-ox[HeadedOx]
95
+ | Fast parsing + comprehensive XPath 1.0 (99.20% pass rate)
96
+ | Very Fast
97
+ |===
98
+
99
+ == Why Moxml?
100
+
101
+ **Consistent API**:: Write once, run with any XML library. Switch adapters
102
+ without changing your code.
103
+
104
+ **Full XPath support**:: Powerful querying with namespace awareness across all
105
+ adapters.
106
+
107
+ **Modern design**:: Clean, Ruby-idiomatic interface that feels natural to use.
108
+
109
+ **Production ready**:: Comprehensive error handling, thread safety, and
110
+ performance optimization.
111
+
112
+ == Community
113
+
114
+ * link:https://github.com/lutaml/moxml[GitHub repository]
115
+ * link:https://github.com/lutaml/moxml/issues[Issue tracker]
116
+ * link:https://rubygems.org/gems/moxml[RubyGems page]
117
+
118
+ == License
119
+
120
+ Moxml is released under the Ribose 3-Clause BSD License.
121
+
122
+ Copyright (c) 2025 Ribose Inc.
@@ -0,0 +1,124 @@
1
+ # Moxml Real-World Examples
2
+
3
+ This directory contains practical, runnable examples demonstrating moxml usage in common real-world scenarios.
4
+
5
+ ## Overview
6
+
7
+ Each example demonstrates different aspects of moxml's capabilities:
8
+
9
+ - **RSS Parser**: Parse RSS/Atom feeds with XPath queries and namespace handling
10
+ - **Web Scraper**: Extract data from HTML/XML using DOM navigation
11
+ - **API Client**: Build and parse XML API requests/responses
12
+
13
+ ## Requirements
14
+
15
+ All examples require moxml and at least one XML adapter:
16
+
17
+ ```bash
18
+ gem install moxml nokogiri
19
+ ```
20
+
21
+ ## Running the Examples
22
+
23
+ Each example is self-contained and can be run directly:
24
+
25
+ ```bash
26
+ # RSS Parser Example
27
+ ruby examples/rss_parser/rss_parser.rb
28
+
29
+ # Web Scraper Example
30
+ ruby examples/web_scraper/web_scraper.rb
31
+
32
+ # API Client Example
33
+ ruby examples/api_client/api_client.rb
34
+ ```
35
+
36
+ ## Example Details
37
+
38
+ ### RSS Parser (`rss_parser/`)
39
+
40
+ Demonstrates:
41
+ - Parsing RSS/Atom feed XML
42
+ - XPath queries for data extraction
43
+ - Namespace handling
44
+ - Element traversal
45
+ - Attribute access
46
+
47
+ **Files:**
48
+ - `rss_parser.rb` - Main parser implementation
49
+ - `example_feed.xml` - Sample RSS feed
50
+ - `README.md` - Detailed documentation
51
+
52
+ ### Web Scraper (`web_scraper/`)
53
+
54
+ Demonstrates:
55
+ - HTML/XML document parsing
56
+ - Table data extraction
57
+ - DOM structure navigation
58
+ - Attribute and text content access
59
+ - Error handling
60
+
61
+ **Files:**
62
+ - `web_scraper.rb` - Main scraper implementation
63
+ - `example_page.html` - Sample HTML page
64
+ - `README.md` - Detailed documentation
65
+
66
+ ### API Client (`api_client/`)
67
+
68
+ Demonstrates:
69
+ - Building XML API requests
70
+ - Parsing XML API responses
71
+ - SOAP message handling
72
+ - Authentication elements
73
+ - Error response processing
74
+
75
+ **Files:**
76
+ - `api_client.rb` - Main client implementation
77
+ - `example_response.xml` - Sample API response
78
+ - `README.md` - Detailed documentation
79
+
80
+ ## Key Concepts
81
+
82
+ ### Using require_relative
83
+
84
+ All examples use `require_relative` to load moxml from the local source:
85
+
86
+ ```ruby
87
+ require_relative '../../lib/moxml'
88
+ ```
89
+
90
+ This allows running the examples directly from the repository without installing the gem.
91
+
92
+ ### Error Handling
93
+
94
+ Each example includes comprehensive error handling:
95
+
96
+ ```ruby
97
+ begin
98
+ doc = Moxml.new.parse(xml)
99
+ rescue Moxml::ParseError => e
100
+ puts "Parse error: #{e.message}"
101
+ exit 1
102
+ end
103
+ ```
104
+
105
+ ### Best Practices
106
+
107
+ The examples demonstrate moxml best practices:
108
+ - Proper namespace handling
109
+ - Efficient XPath queries
110
+ - Clean resource management
111
+ - Comprehensive error handling
112
+ - Clear, commented code
113
+
114
+ ## Learning Path
115
+
116
+ 1. **Start with RSS Parser** - Learn basic parsing and XPath
117
+ 2. **Move to Web Scraper** - Understand DOM navigation
118
+ 3. **Finish with API Client** - Master XML generation and complex structures
119
+
120
+ ## Additional Resources
121
+
122
+ - [Main README](../README.adoc) - Complete moxml documentation
123
+ - [API Reference](../docs/) - Detailed API documentation
124
+ - [Guides](../docs/_guides/) - Step-by-step tutorials