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,349 @@
1
+ ---
2
+ title: Error handling
3
+ nav_order: 8
4
+ ---
5
+
6
+ == Error handling
7
+
8
+ === Purpose
9
+
10
+ Comprehensive guide to handling errors in Moxml including error class
11
+ hierarchy, error context, and best practices for debugging.
12
+
13
+ === Error class hierarchy
14
+
15
+ All Moxml errors inherit from `Moxml::Error`:
16
+
17
+ [source]
18
+ ----
19
+ Moxml::Error (< StandardError)
20
+ ├── ParseError # XML parsing failures
21
+ ├── XPathError # XPath expression errors
22
+ ├── ValidationError # XML validation failures
23
+ ├── NamespaceError # Namespace-related errors
24
+ ├── AdapterError # Adapter loading/operation errors
25
+ ├── SerializationError # XML serialization failures
26
+ ├── DocumentStructureError # Invalid document structure
27
+ ├── AttributeError # Attribute operation errors
28
+ └── NotImplementedError # Unimplemented adapter features
29
+ ----
30
+
31
+ === ParseError
32
+
33
+ Raised when XML parsing fails.
34
+
35
+ [source,ruby]
36
+ ----
37
+ begin
38
+ doc = Moxml.new.parse('<invalid><unclosed>', strict: true)
39
+ rescue Moxml::ParseError => e
40
+ puts "Parse error at line #{e.line}, column #{e.column}"
41
+ puts "Source excerpt: #{e.source}"
42
+ puts e.to_s # Includes helpful hint
43
+ # => "Hint: Check XML syntax and ensure all tags are properly closed"
44
+ end
45
+ ----
46
+
47
+ **Attributes:**
48
+
49
+ * `line` - Line number where error occurred
50
+ * `column` - Column number where error occurred
51
+ * `source` - Excerpt of problematic XML
52
+
53
+ === XPathError
54
+
55
+ Raised when XPath expression evaluation fails.
56
+
57
+ [source,ruby]
58
+ ----
59
+ begin
60
+ results = doc.xpath('//invalid[[[')
61
+ rescue Moxml::XPathError => e
62
+ puts "Expression: #{e.expression}"
63
+ puts "Adapter: #{e.adapter}"
64
+ puts "Node: #{e.node.name}" if e.node
65
+ puts e.to_s
66
+ # => "Hint: Verify XPath syntax and ensure adapter supports the expression"
67
+ end
68
+ ----
69
+
70
+ **Attributes:**
71
+
72
+ * `expression` - The XPath expression that failed
73
+ * `adapter` - Adapter name
74
+ * `node` - Context node (if available)
75
+
76
+ === ValidationError
77
+
78
+ Raised when XML content violates XML specifications.
79
+
80
+ [source,ruby]
81
+ ----
82
+ begin
83
+ doc.version = '2.0' # Invalid XML version
84
+ rescue Moxml::ValidationError => e
85
+ puts "Constraint: #{e.constraint}" # => "version"
86
+ puts "Value: #{e.value}" # => "2.0"
87
+ puts "Allowed: #{e.allowed_values}" # => ["1.0", "1.1"]
88
+ puts e.to_s
89
+ end
90
+ ----
91
+
92
+ **Attributes:**
93
+
94
+ * `constraint` - What was being validated
95
+ * `value` - The invalid value
96
+ * `allowed_values` - Valid options
97
+
98
+ === NamespaceError
99
+
100
+ Raised when namespace operations fail.
101
+
102
+ [source,ruby]
103
+ ----
104
+ begin
105
+ element.add_namespace('invalid:prefix', 'http://example.org')
106
+ rescue Moxml::NamespaceError => e
107
+ puts "Prefix: #{e.prefix}"
108
+ puts "URI: #{e.uri}"
109
+ puts "Element: #{e.element.name}"
110
+ puts e.to_s
111
+ # => "Hint: Check namespace registration and URI validity"
112
+ end
113
+ ----
114
+
115
+ **Attributes:**
116
+
117
+ * `prefix` - Namespace prefix
118
+ * `uri` - Namespace URI
119
+ * `element` - Element where error occurred
120
+
121
+ === AdapterError
122
+
123
+ Raised when adapter loading or operations fail.
124
+
125
+ [source,ruby]
126
+ ----
127
+ begin
128
+ context = Moxml.new
129
+ context.config.adapter = :nonexistent
130
+ rescue Moxml::AdapterError => e
131
+ puts "Adapter: #{e.adapter_name}" # => :nonexistent
132
+ puts "Operation: #{e.operation}" # => "set_adapter"
133
+ puts "Native Error: #{e.native_error}" # Original error
134
+ puts e.to_s
135
+ # => "Hint: Ensure the adapter gem is properly installed"
136
+ end
137
+ ----
138
+
139
+ **Attributes:**
140
+
141
+ * `adapter_name` - Name of adapter
142
+ * `operation` - Operation that failed
143
+ * `native_error` - Underlying error from adapter library
144
+
145
+ === Best practices
146
+
147
+ ==== Catch specific errors
148
+
149
+ [source,ruby]
150
+ ----
151
+ begin
152
+ doc = Moxml.new.parse(xml_string, strict: true)
153
+ results = doc.xpath('//book[@id="1"]')
154
+ rescue Moxml::ParseError => e
155
+ # Handle parsing errors
156
+ logger.error("XML parsing failed: #{e.to_s}")
157
+ rescue Moxml::XPathError => e
158
+ # Handle XPath errors
159
+ logger.error("XPath query failed: #{e.expression}")
160
+ rescue Moxml::NamespaceError => e
161
+ # Handle namespace errors
162
+ logger.error("Namespace error: #{e.prefix}:#{e.uri}")
163
+ rescue Moxml::Error => e
164
+ # Catch-all for other Moxml errors
165
+ logger.error("XML processing error: #{e.message}")
166
+ end
167
+ ----
168
+
169
+ ==== Use error context
170
+
171
+ [source,ruby]
172
+ ----
173
+ begin
174
+ doc = Moxml.new.parse(xml)
175
+ rescue Moxml::ParseError => e
176
+ # Log comprehensive error information
177
+ logger.error <<~ERROR
178
+ XML Parse Error:
179
+ Line: #{e.line}
180
+ Column: #{e.column}
181
+ Source: #{e.source}
182
+ Message: #{e.message}
183
+ Hint: #{e.to_s}
184
+ ERROR
185
+
186
+ # Re-raise or handle
187
+ raise unless development_mode?
188
+ end
189
+ ----
190
+
191
+ ==== Validate before operations
192
+
193
+ [source,ruby]
194
+ ----
195
+ # Validate adapter available
196
+ def ensure_adapter_available(adapter_name)
197
+ context = Moxml.new
198
+ context.config.adapter = adapter_name
199
+ rescue Moxml::AdapterError => e
200
+ raise "Required adapter #{adapter_name} not available: #{e.message}"
201
+ end
202
+
203
+ # Validate document structure
204
+ def validate_structure(doc)
205
+ required = ['title', 'author']
206
+ required.each do |elem|
207
+ unless doc.at_xpath("//#{elem}")
208
+ raise Moxml::ValidationError, "Missing required element: #{elem}"
209
+ end
210
+ end
211
+ end
212
+ ----
213
+
214
+ === Error recovery strategies
215
+
216
+ ==== Graceful degradation
217
+
218
+ [source,ruby]
219
+ ----
220
+ def parse_with_fallback(xml)
221
+ # Try strict parsing first
222
+ begin
223
+ return Moxml.new.parse(xml, strict: true)
224
+ rescue Moxml::ParseError
225
+ logger.warn("Strict parsing failed, trying relaxed mode")
226
+ end
227
+
228
+ # Fall back to relaxed parsing
229
+ begin
230
+ return Moxml.new.parse(xml, strict: false)
231
+ rescue Moxml::ParseError => e
232
+ logger.error("Cannot parse XML: #{e.message}")
233
+ raise
234
+ end
235
+ end
236
+ ----
237
+
238
+ ==== Adapter fallback
239
+
240
+ [source,ruby]
241
+ ----
242
+ def parse_with_adapter_fallback(xml)
243
+ adapters = [:nokogiri, :libxml, :oga, :rexml]
244
+
245
+ adapters.each do |adapter_name|
246
+ begin
247
+ context = Moxml.new
248
+ context.config.adapter = adapter_name
249
+ return context.parse(xml)
250
+ rescue Moxml::AdapterError
251
+ # Adapter not available, try next
252
+ next
253
+ rescue Moxml::ParseError => e
254
+ # Parsing failed with this adapter
255
+ logger.warn("#{adapter_name} failed: #{e.message}")
256
+ next
257
+ end
258
+ end
259
+
260
+ raise "Could not parse XML with any available adapter"
261
+ end
262
+ ----
263
+
264
+ === Common error scenarios
265
+
266
+ ==== Malformed XML
267
+
268
+ [source,ruby]
269
+ ----
270
+ xml = '<root><unclosed>'
271
+
272
+ begin
273
+ doc = Moxml.new.parse(xml, strict: true)
274
+ rescue Moxml::ParseError => e
275
+ puts "Malformed XML detected"
276
+ puts "Error at line #{e.line}" if e.line
277
+ # Handle or fix the XML
278
+ end
279
+ ----
280
+
281
+ ==== Invalid XPath
282
+
283
+ [source,ruby]
284
+ ----
285
+ begin
286
+ doc.xpath('//book[[[invalid]]]')
287
+ rescue Moxml::XPathError => e
288
+ puts "Invalid XPath expression: #{e.expression}"
289
+ # Use corrected expression
290
+ doc.xpath('//book[@id]')
291
+ end
292
+ ----
293
+
294
+ ==== Unsupported features
295
+
296
+ [source,ruby]
297
+ ----
298
+ # Ox doesn't support complex XPath
299
+ context = Moxml.new
300
+ context.config.adapter = :ox
301
+
302
+ begin
303
+ doc.xpath('//book[price < 30]')
304
+ rescue Moxml::NotImplementedError => e
305
+ puts "Feature not supported by #{e.adapter}: #{e.feature}"
306
+ # Use workaround
307
+ books = doc.xpath('//book')
308
+ cheap = books.select { |b| b.at_xpath('.//price').text.to_f < 30 }
309
+ end
310
+ ----
311
+
312
+ === Debugging techniques
313
+
314
+ ==== Enable detailed errors
315
+
316
+ [source,ruby]
317
+ ----
318
+ # Error messages include hints
319
+ begin
320
+ doc = Moxml.new.parse(invalid_xml)
321
+ rescue Moxml::Error => e
322
+ puts e.to_s # Full message with debugging hints
323
+ puts e.class.name # Error type
324
+ puts e.backtrace.first(5) # Stack trace
325
+ end
326
+ ----
327
+
328
+ ==== Inspect error context
329
+
330
+ [source,ruby]
331
+ ----
332
+ begin
333
+ result = doc.xpath(expression)
334
+ rescue Moxml::XPathError => e
335
+ # Debug XPath errors
336
+ puts "Failed expression: #{e.expression}"
337
+ puts "On node: #{e.node.name}" if e.node
338
+ puts "With adapter: #{e.adapter}"
339
+
340
+ # Try simpler expression
341
+ doc.xpath('//book')
342
+ end
343
+ ----
344
+
345
+ === See also
346
+
347
+ * link:../references/error-classes[Error classes reference] - Complete API
348
+ * link:../guides/error-handling[Error handling guide] - Advanced patterns
349
+ * link:adapters/[Adapters] - Adapter-specific error behaviors