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