moxml 0.1.6 → 0.1.8

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