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,35 @@
1
+ ---
2
+ title: Overview
3
+ nav_order: 1
4
+ ---
5
+
6
+ == Core topics
7
+
8
+ This section covers the fundamental concepts and essential information for
9
+ working with Moxml.
10
+
11
+ === Topics covered
12
+
13
+ link:installation[Installation]::
14
+ How to install Moxml and select an XML adapter for your needs.
15
+
16
+ link:quick-start[Quick start]::
17
+ Get up and running with Moxml in minutes with practical examples.
18
+
19
+ link:architecture[Architecture]::
20
+ Understand how Moxml's unified interface and adapter system works.
21
+
22
+ link:adapters/[Adapters]::
23
+ Learn about the supported XML libraries and how to choose the right one.
24
+
25
+ link:compatibility[Compatibility]::
26
+ Compare features, performance, and compatibility across adapters.
27
+
28
+ link:configuration[Configuration]::
29
+ Configure Moxml for your application's requirements.
30
+
31
+ link:error-handling[Error handling]::
32
+ Comprehensive guide to Moxml's error classes and debugging.
33
+
34
+ link:best-practices[Best practices]::
35
+ Recommended patterns and practices for using Moxml effectively.
@@ -0,0 +1,140 @@
1
+ ---
2
+ title: Installation
3
+ nav_order: 2
4
+ ---
5
+
6
+ == Installation
7
+
8
+ === Purpose
9
+
10
+ This guide covers how to install Moxml and set up your chosen XML adapter for
11
+ Ruby applications.
12
+
13
+ === Requirements
14
+
15
+ * Ruby >= 3.0.0
16
+ * At least one supported XML library (Nokogiri, LibXML, Oga, REXML, or Ox)
17
+
18
+ === Basic installation
19
+
20
+ Add Moxml and your chosen XML adapter to your Gemfile:
21
+
22
+ [source,ruby]
23
+ ----
24
+ # Gemfile
25
+ gem 'moxml'
26
+
27
+ # Choose one or more XML adapters:
28
+ gem 'nokogiri' # Recommended: Fast, widely used
29
+ gem 'libxml-ruby' # Alternative: Native performance
30
+ gem 'oga' # Pure Ruby: No C extensions
31
+ gem 'ox' # Fastest: Best for simple documents
32
+ # REXML is included in Ruby standard library
33
+ ----
34
+
35
+ Then install:
36
+
37
+ [source,shell]
38
+ ----
39
+ bundle install
40
+ ----
41
+
42
+ === Installing without Bundler
43
+
44
+ Install directly using gem:
45
+
46
+ [source,shell]
47
+ ----
48
+ gem install moxml
49
+ gem install nokogiri # Or your preferred adapter
50
+ ----
51
+
52
+ === Adapter selection
53
+
54
+ Moxml automatically detects and uses available XML libraries. The default
55
+ adapter priority order is:
56
+
57
+ . Nokogiri (if available)
58
+ . LibXML (if available)
59
+ . Oga (if available)
60
+ . REXML (always available)
61
+ . Ox (if available)
62
+
63
+ You can override the default adapter in your application:
64
+
65
+ [source,ruby]
66
+ ----
67
+ # Set default adapter globally
68
+ Moxml::Config.default_adapter = :libxml
69
+
70
+ # Or configure per instance
71
+ moxml = Moxml.new
72
+ moxml.config.adapter = :oga
73
+ ----
74
+
75
+ === Verifying installation
76
+
77
+ Test your installation:
78
+
79
+ [source,ruby]
80
+ ----
81
+ require 'moxml'
82
+
83
+ # Check version
84
+ puts Moxml::VERSION
85
+
86
+ # Verify adapter loaded
87
+ context = Moxml.new
88
+ puts context.config.adapter.name
89
+ # => "Moxml::Adapter::Nokogiri" (or your adapter)
90
+
91
+ # Parse simple XML
92
+ doc = context.parse('<root><child>test</child></root>')
93
+ puts doc.root.name
94
+ # => "root"
95
+ ----
96
+
97
+ === Troubleshooting
98
+
99
+ **Gem not found:**
100
+
101
+ Ensure bundler is using the correct gem source:
102
+
103
+ [source,ruby]
104
+ ----
105
+ # Add to top of Gemfile
106
+ source 'https://rubygems.org'
107
+ ----
108
+
109
+ **Adapter not loading:**
110
+
111
+ If your chosen adapter isn't loading:
112
+
113
+ . Verify the adapter gem is installed: `bundle list | grep nokogiri`
114
+ . Check for version conflicts: `bundle update moxml`
115
+ . Try explicitly requiring: `require 'nokogiri'` before `require 'moxml'`
116
+
117
+ **C extension compilation errors:**
118
+
119
+ If Nokogiri or LibXML fail to install on your system, consider using pure
120
+ Ruby adapters:
121
+
122
+ [source,ruby]
123
+ ----
124
+ gem 'moxml'
125
+ gem 'oga' # Pure Ruby alternative
126
+ ----
127
+
128
+ Or use REXML which requires no additional gems:
129
+
130
+ [source,ruby]
131
+ ----
132
+ gem 'moxml'
133
+ # REXML is included in Ruby standard library
134
+ ----
135
+
136
+ === Next steps
137
+
138
+ * link:../quick-start[Quick start guide] - Get started with basic usage
139
+ * link:adapters/[Learn about adapters] - Understand adapter differences
140
+ * link:../tutorials/[Tutorials] - Step-by-step learning paths
@@ -0,0 +1,49 @@
1
+ ---
2
+ title: Node API reference
3
+ nav_order: 5
4
+ ---
5
+
6
+ == Node API reference
7
+
8
+ == XML objects and their methods
9
+
10
+ Each node type provides methods for traversing the document structure:
11
+
12
+ [source,ruby]
13
+ ----
14
+ node.parent # Get parent node
15
+ node.children # Get child nodes
16
+ node.next_sibling # Get next sibling
17
+ node.previous_sibling # Get previous sibling
18
+
19
+ # Convenience accessors
20
+ node.first_child # Get first child
21
+ node.last_child # Get last child
22
+ node.has_children? # Check if node has children
23
+
24
+ # Node manipulation
25
+ node.clone # Deep copy of node
26
+ node.dup # Alias for clone
27
+
28
+ # Query methods
29
+ node.find(xpath) # Alias for at_xpath
30
+ node.find_all(xpath) # Returns array of matching elements
31
+
32
+ # Type checking
33
+ node.element? # Is it an element?
34
+ node.text? # Is it a text node?
35
+ node.cdata? # Is it a CDATA section?
36
+ node.comment? # Is it a comment?
37
+ node.processing_instruction? # Is it a PI?
38
+ node.attribute? # Is it an attribute?
39
+ node.namespace? # Is it a namespace?
40
+
41
+ # Node information
42
+ node.document # Get owning document
43
+ ----
44
+
45
+
46
+ See also:
47
+
48
+ * link:../guides/working-with-documents[Working with documents guide]
49
+ * link:../guides/advanced-features[Advanced features guide]
@@ -0,0 +1,35 @@
1
+ ---
2
+ title: Performance considerations
3
+ nav_order: 11
4
+ ---
5
+
6
+ == Performance considerations
7
+
8
+ === Memory management
9
+
10
+ Moxml maintains a node registry to ensure consistent object mapping:
11
+
12
+ [source,ruby]
13
+ ----
14
+ doc = context.parse(large_xml)
15
+ # Process document
16
+ doc = nil # Allow garbage collection of document and registry
17
+ GC.start # Force garbage collection if needed
18
+ ----
19
+
20
+ === Efficient querying
21
+
22
+ Use specific XPath expressions for better performance:
23
+
24
+ [source,ruby]
25
+ ----
26
+ # More efficient - specific path
27
+ doc.xpath('//book/title')
28
+
29
+ # Less efficient - requires full document scan
30
+ doc.xpath('//title')
31
+
32
+ # Most efficient - direct child access
33
+ root.xpath('./*/title')
34
+ ----
35
+
@@ -0,0 +1,243 @@
1
+ ---
2
+ title: Quick start
3
+ nav_order: 3
4
+ ---
5
+
6
+ == Quick start
7
+
8
+ === Purpose
9
+
10
+ Get up and running with Moxml in minutes through practical examples covering
11
+ parsing, creating, querying, and modifying XML documents.
12
+
13
+ === Prerequisites
14
+
15
+ * Moxml installed with at least one adapter
16
+ * Basic familiarity with XML concepts
17
+ * Ruby 3.0 or higher
18
+
19
+ === Parse and query XML
20
+
21
+ The most common operation is parsing existing XML and querying it:
22
+
23
+ [source,ruby]
24
+ ----
25
+ require 'moxml'
26
+
27
+ xml = <<~XML
28
+ <library>
29
+ <book id="1">
30
+ <title>Ruby Programming</title>
31
+ <author>Jane Smith</author>
32
+ <price>29.99</price>
33
+ </book>
34
+ <book id="2">
35
+ <title>Advanced Ruby</title>
36
+ <author>John Doe</author>
37
+ <price>39.99</price>
38
+ </book>
39
+ </library>
40
+ XML
41
+
42
+ # Parse the XML
43
+ doc = Moxml.new.parse(xml)
44
+
45
+ # Query using XPath
46
+ books = doc.xpath('//book')
47
+ puts "Found #{books.length} books" # => Found 2 books
48
+
49
+ # Access specific book
50
+ first_book = doc.at_xpath('//book[@id="1"]')
51
+ puts first_book['id'] # => "1"
52
+ puts first_book.at_xpath('.//title').text # => "Ruby Programming"
53
+
54
+ # Find all titles
55
+ titles = doc.xpath('//book/title')
56
+ titles.each { |title| puts title.text }
57
+ # => Ruby Programming
58
+ # => Advanced Ruby
59
+ ----
60
+
61
+ === Create XML documents
62
+
63
+ Build XML documents from scratch using the builder pattern:
64
+
65
+ [source,ruby]
66
+ ----
67
+ require 'moxml'
68
+
69
+ doc = Moxml::Builder.build(Moxml.new) do |xml|
70
+ xml.declaration version: "1.0", encoding: "UTF-8"
71
+
72
+ xml.library do
73
+ xml.book id: "1" do
74
+ xml.title "Ruby Programming"
75
+ xml.author "Jane Smith"
76
+ xml.price "29.99"
77
+ end
78
+
79
+ xml.book id: "2" do
80
+ xml.title "Advanced Ruby"
81
+ xml.author "John Doe"
82
+ xml.price "39.99"
83
+ end
84
+ end
85
+ end
86
+
87
+ puts doc.to_xml(indent: 2)
88
+ ----
89
+
90
+ Output:
91
+
92
+ [source,xml]
93
+ ----
94
+ <?xml version="1.0" encoding="UTF-8"?>
95
+ <library>
96
+ <book id="1">
97
+ <title>Ruby Programming</title>
98
+ <author>Jane Smith</author>
99
+ <price>29.99</price>
100
+ </book>
101
+ <book id="2">
102
+ <title>Advanced Ruby</title>
103
+ <author>John Doe</author>
104
+ <price>39.99</price>
105
+ </book>
106
+ </library>
107
+ ----
108
+
109
+ === Modify existing XML
110
+
111
+ Add, update, and remove elements and attributes:
112
+
113
+ [source,ruby]
114
+ ----
115
+ require 'moxml'
116
+
117
+ # Parse existing XML
118
+ xml = '<library><book id="1"><title>Old Title</title></book></library>'
119
+ doc = Moxml.new.parse(xml)
120
+
121
+ # Find and modify element
122
+ book = doc.at_xpath('//book[@id="1"]')
123
+
124
+ # Update text content
125
+ book.at_xpath('.//title').text = 'New Title'
126
+
127
+ # Update attribute
128
+ book['id'] = '100'
129
+ book['edition'] = '2nd'
130
+
131
+ # Add new child element
132
+ author = doc.create_element('author')
133
+ author.text = 'Jane Smith'
134
+ book.add_child(author)
135
+
136
+ # Add comment
137
+ book.add_child(doc.create_comment('Updated book'))
138
+
139
+ # Remove attribute
140
+ book.remove_attribute('edition')
141
+
142
+ puts doc.to_xml(indent: 2)
143
+ ----
144
+
145
+ === Work with namespaces
146
+
147
+ Handle XML namespaces properly:
148
+
149
+ [source,ruby]
150
+ ----
151
+ require 'moxml'
152
+
153
+ xml = <<~XML
154
+ <library xmlns="http://example.org/library"
155
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
156
+ <book>
157
+ <dc:title>Ruby Programming</dc:title>
158
+ <dc:creator>Jane Smith</dc:creator>
159
+ </book>
160
+ </library>
161
+ XML
162
+
163
+ doc = Moxml.new.parse(xml)
164
+
165
+ # Query with namespace mapping
166
+ namespaces = {
167
+ 'lib' => 'http://example.org/library',
168
+ 'dc' => 'http://purl.org/dc/elements/1.1/'
169
+ }
170
+
171
+ books = doc.xpath('//lib:book', namespaces)
172
+ titles = doc.xpath('//dc:title', namespaces)
173
+
174
+ puts titles.first.text # => "Ruby Programming"
175
+
176
+ # Create namespaced elements
177
+ new_book = doc.create_element('book')
178
+ new_book.add_namespace('dc', 'http://purl.org/dc/elements/1.1/')
179
+
180
+ title = doc.create_element('dc:title')
181
+ title.text = 'Advanced Ruby'
182
+ new_book.add_child(title)
183
+ ----
184
+
185
+ === Switch adapters
186
+
187
+ Choose a different XML adapter based on your needs:
188
+
189
+ [source,ruby]
190
+ ----
191
+ require 'moxml'
192
+
193
+ # Use Nokogiri (default)
194
+ context_nokogiri = Moxml.new
195
+
196
+ # Use LibXML for native performance
197
+ context_libxml = Moxml.new
198
+ context_libxml.config.adapter = :libxml
199
+
200
+ # Use Oga for pure Ruby
201
+ context_oga = Moxml.new
202
+ context_oga.config.adapter = :oga
203
+
204
+ # Use REXML for standard library only
205
+ context_rexml = Moxml.new
206
+ context_rexml.config.adapter = :rexml
207
+
208
+ # Parse with chosen adapter
209
+ doc = context_libxml.parse(xml)
210
+ ----
211
+
212
+ === Handle errors
213
+
214
+ Moxml provides comprehensive error handling:
215
+
216
+ [source,ruby]
217
+ ----
218
+ require 'moxml'
219
+
220
+ begin
221
+ # Parse malformed XML
222
+ doc = Moxml.new.parse('<invalid><unclosed>', strict: true)
223
+ rescue Moxml::ParseError => e
224
+ puts "Parse error at line #{e.line}: #{e.message}"
225
+ end
226
+
227
+ begin
228
+ # Invalid XPath expression
229
+ doc.xpath('//invalid[[[')
230
+ rescue Moxml::XPathError => e
231
+ puts "XPath error: #{e.expression}"
232
+ puts e.to_s # Includes helpful hints
233
+ end
234
+ ----
235
+
236
+ === Next steps
237
+
238
+ Now that you've seen the basics, explore more:
239
+
240
+ * link:../tutorials/[Tutorials] - Learn Moxml step by step
241
+ * link:adapters/[Adapters] - Choose the right adapter for your needs
242
+ * link:../guides/[Guides] - Task-oriented documentation
243
+ * link:../references/[API Reference] - Complete technical documentation
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Thread safety
3
+ nav_order: 10
4
+ ---
5
+
6
+ == Thread safety
7
+
8
+ Moxml is thread-safe when used properly. Each instance maintains its own state
9
+ and can be used safely in concurrent operations:
10
+
11
+ [source,ruby]
12
+ ----
13
+ class XmlProcessor
14
+ def initialize
15
+ @mutex = Mutex.new
16
+ @context = Moxml.new
17
+ end
18
+
19
+ def process(xml)
20
+ @mutex.synchronize do
21
+ doc = @context.parse(xml)
22
+ # Modify document
23
+ doc.to_xml
24
+ end
25
+ end
26
+ end
27
+ ----
28
+