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
data/docs/_config.yml ADDED
@@ -0,0 +1,138 @@
1
+ # Site settings
2
+ title: Moxml
3
+ description: >-
4
+ Modern XML processing for Ruby with unified adapter interface
5
+ baseurl: "/moxml"
6
+ url: "https://lutaml.github.io"
7
+
8
+ # Theme
9
+ theme: just-the-docs
10
+ remote_theme: just-the-docs/just-the-docs@v0.7.0
11
+
12
+ # Color scheme
13
+ color_scheme: light
14
+
15
+ # Search
16
+ search_enabled: true
17
+ search:
18
+ heading_level: 2
19
+ previews: 3
20
+ preview_words_before: 5
21
+ preview_words_after: 10
22
+ tokenizer_separator: /[\s/]+/
23
+ rel_url: true
24
+ button: false
25
+
26
+ # Heading anchors
27
+ heading_anchors: true
28
+
29
+ # Aux links for the upper right navigation
30
+ aux_links:
31
+ "GitHub":
32
+ - "https://github.com/lutaml/moxml"
33
+ "RubyGems":
34
+ - "https://rubygems.org/gems/moxml"
35
+
36
+ # Makes Aux links open in a new tab
37
+ aux_links_new_tab: true
38
+
39
+ # Back to top link
40
+ back_to_top: true
41
+ back_to_top_text: "Back to top"
42
+
43
+ # Footer content
44
+ footer_content: >-
45
+ Copyright © 2025 Ribose. Distributed by a
46
+ <a href="https://github.com/lutaml/moxml/blob/main/LICENSE.md">BSD-2-Clause license</a>.
47
+
48
+ # Footer last edit timestamp
49
+ last_edit_timestamp: true
50
+ last_edit_time_format: "%b %e %Y at %I:%M %p"
51
+
52
+ # Navigation structure
53
+ nav_sort: order
54
+
55
+ # Collections for organizing content
56
+ collections:
57
+ pages:
58
+ permalink: "/:path/"
59
+ output: true
60
+ tutorials:
61
+ permalink: "/:collection/:path/"
62
+ output: true
63
+ guides:
64
+ permalink: "/:collection/:path/"
65
+ output: true
66
+ references:
67
+ permalink: "/:collection/:path/"
68
+ output: true
69
+
70
+ # Just the Docs collection configuration
71
+ just_the_docs:
72
+ collections:
73
+ pages:
74
+ name: Core topics
75
+ nav_fold: false
76
+ tutorials:
77
+ name: Tutorials
78
+ nav_fold: true
79
+ guides:
80
+ name: Guides
81
+ nav_fold: true
82
+ references:
83
+ name: Reference
84
+ nav_fold: true
85
+
86
+ # Plugins
87
+ plugins:
88
+ - jekyll-asciidoc
89
+ - jekyll-seo-tag
90
+ - jekyll-sitemap
91
+
92
+ # AsciiDoc configuration
93
+ asciidoc: {}
94
+ asciidoctor:
95
+ attributes:
96
+ - source-highlighter=rouge
97
+ - rouge-style=github
98
+ - icons=font
99
+ - sectanchors=true
100
+ - idprefix=
101
+ - idseparator=-
102
+ - experimental=true
103
+
104
+ include:
105
+ - '*.adoc'
106
+
107
+ # Exclude from processing
108
+ exclude:
109
+ - Gemfile
110
+ - Gemfile.lock
111
+ - _site
112
+ - node_modules
113
+ - vendor/bundle/
114
+ - vendor/cache/
115
+ - vendor/gems/
116
+ - vendor/ruby/
117
+ - Rakefile
118
+
119
+ # Enable code copy button
120
+ enable_copy_code_button: true
121
+
122
+ # Callouts
123
+ callouts_level: quiet
124
+ callouts:
125
+ highlight:
126
+ color: yellow
127
+ important:
128
+ title: Important
129
+ color: blue
130
+ new:
131
+ title: New
132
+ color: green
133
+ note:
134
+ title: Note
135
+ color: purple
136
+ warning:
137
+ title: Warning
138
+ color: red
@@ -0,0 +1,87 @@
1
+ ---
2
+ title: Advanced features
3
+ parent: Guides
4
+ nav_order: 8
5
+ ---
6
+
7
+ == Advanced features
8
+
9
+ == Advanced features
10
+
11
+ === XPath querying and node mapping
12
+
13
+ ==== Nokogiri, Oga, REXML, LibXML
14
+
15
+ Moxml provides efficient XPath querying by leveraging the native XML library's
16
+ implementation while maintaining consistent node mapping:
17
+
18
+ [source,ruby]
19
+ ----
20
+ # Find all book elements
21
+ books = doc.xpath('//book')
22
+ # Returns Moxml::Element objects mapped to native nodes
23
+
24
+ # Find with namespaces
25
+ titles = doc.xpath('//dc:title',
26
+ 'dc' => 'http://purl.org/dc/elements/1.1/')
27
+
28
+ # Find first matching node
29
+ first_book = doc.at_xpath('//book')
30
+
31
+ # Chain queries
32
+ doc.xpath('//book').each do |book|
33
+ # Each book is a mapped Moxml::Element
34
+ title = book.at_xpath('.//title')
35
+ puts "#{book['id']}: #{title.text}"
36
+ end
37
+ ----
38
+
39
+ ==== Ox
40
+
41
+ The native Ox's query method
42
+ https://www.ohler.com/ox/Ox/Element.html#method-i-locate[`locate`] resembles
43
+ XPath but has a different syntax.
44
+
45
+ ==== HeadedOx
46
+
47
+ HeadedOx provides comprehensive (but not fully) XPath 1.0 support via a pure
48
+ Ruby XPath engine layered on top of Ox.
49
+
50
+
51
+ === Namespace handling
52
+
53
+ [source,ruby]
54
+ ----
55
+ # Add namespace to element
56
+ element.add_namespace('dc', 'http://purl.org/dc/elements/1.1/')
57
+
58
+ # Create element in namespace
59
+ title = doc.create_element('dc:title')
60
+ title.text = 'Document Title'
61
+
62
+ # Query with namespaces
63
+ doc.xpath('//dc:title',
64
+ 'dc' => 'http://purl.org/dc/elements/1.1/')
65
+ ----
66
+
67
+ === Accessing native implementation
68
+
69
+ While not typically needed, you can access the underlying XML library's nodes:
70
+
71
+ [source,ruby]
72
+ ----
73
+ # Get native node
74
+ native_node = element.native
75
+
76
+ # Get adapter being used
77
+ adapter = element.context.config.adapter
78
+
79
+ # Create from native node
80
+ element = Moxml::Element.new(native_node, context)
81
+ ----
82
+
83
+
84
+ See also:
85
+
86
+ * link:../pages/compatibility[Adapter compatibility]
87
+ * link:../pages/adapters/[Individual adapter documentation]
@@ -0,0 +1,165 @@
1
+ ---
2
+ title: Development and testing
3
+ parent: Guides
4
+ nav_order: 10
5
+ ---
6
+
7
+
8
+ == Development and testing
9
+
10
+ === Skipping benchmarks
11
+
12
+ Benchmark tests can be slow and are not needed for regular test runs. To
13
+ speed up local development, you can skip benchmark tests using the
14
+ `SKIP_BENCHMARKS` environment variable.
15
+
16
+ Syntax:
17
+
18
+ [source,shell]
19
+ ----
20
+ SKIP_BENCHMARKS=1 bundle exec rspec
21
+ ----
22
+
23
+ This will skip all benchmark tests while running the regular test suite.
24
+
25
+ To run benchmarks explicitly:
26
+
27
+ [source,shell]
28
+ ----
29
+ bundle exec rspec spec/moxml/examples/xpath_benchmark_spec.rb
30
+ ----
31
+
32
+ Or use the rake task:
33
+
34
+ [source,shell]
35
+ ----
36
+ rake benchmark:xpath
37
+ ----
38
+
39
+ NOTE: The `rake benchmark:xpath` task always runs benchmarks regardless of the
40
+ `SKIP_BENCHMARKS` environment variable setting.
41
+
42
+ === Running tests with coverage
43
+
44
+ To run the test suite with code coverage tracking:
45
+
46
+ [source,shell]
47
+ ----
48
+ COVERAGE=true bundle exec rspec
49
+ ----
50
+
51
+ After running, view the coverage report:
52
+
53
+ [source,shell]
54
+ ----
55
+ open coverage/index.html
56
+ ----
57
+
58
+ The coverage configuration includes:
59
+
60
+ * Minimum overall coverage: 90%
61
+ * Minimum per-file coverage: 80%
62
+ * Organized groups for Core, Adapters, and Utilities
63
+ * Filters for spec and vendor directories
64
+
65
+ === Generating performance benchmark reports
66
+
67
+ Moxml provides a comprehensive benchmark reporting system that measures and
68
+ compares all adapters across multiple dimensions.
69
+
70
+ ==== Running the benchmark report
71
+
72
+ To generate a complete performance report for all adapters:
73
+
74
+ [source,shell]
75
+ ----
76
+ rake benchmark:report
77
+ ----
78
+
79
+ Or run the script directly:
80
+
81
+ [source,shell]
82
+ ----
83
+ bundle exec ruby benchmarks/generate_report.rb
84
+ ----
85
+
86
+ This will benchmark all available adapters and generate a detailed report at
87
+ [`benchmarks/PERFORMANCE_REPORT.md`](benchmarks/PERFORMANCE_REPORT.md).
88
+
89
+ ==== Benchmark categories
90
+
91
+ The report includes the following benchmark categories:
92
+
93
+ **Parsing benchmarks:**
94
+
95
+ * Simple XML (< 1KB)
96
+ * Medium XML (10KB, 50 elements with namespaces)
97
+ * Large XML (145KB, 500 elements)
98
+ * Complex nested structures
99
+
100
+ **Serialization benchmarks:**
101
+
102
+ * Simple documents
103
+ * Documents with namespaces
104
+ * Documents with mixed content
105
+
106
+ **XPath benchmarks:**
107
+
108
+ * Simple queries (`//element`)
109
+ * Complex queries with predicates (`//element[@attribute]`)
110
+ * Namespace-aware queries (`//ns:element`)
111
+
112
+ **Memory benchmarks:**
113
+
114
+ * Memory usage per document parse
115
+ * Memory usage for large documents
116
+
117
+ ==== Report contents
118
+
119
+ The generated report includes:
120
+
121
+ * Summary table comparing all adapters with grades
122
+ * Detailed performance metrics for each benchmark
123
+ * ASCII performance visualization charts
124
+ * Adapter selection recommendations based on results
125
+ * Complete test environment details (Ruby version, platform, gem versions)
126
+ * Best performers for each category
127
+
128
+ ==== Viewing the report
129
+
130
+ After generation, the report is available at:
131
+
132
+ [source,shell]
133
+ ----
134
+ cat benchmarks/PERFORMANCE_REPORT.md
135
+ ----
136
+
137
+ Or open it in your preferred Markdown viewer.
138
+
139
+ NOTE: The generated report is machine-specific and excluded from git via
140
+ `.gitignore`. Results will vary based on your hardware, OS, and Ruby version.
141
+
142
+ ==== Example output
143
+
144
+ The summary table shows comparative performance:
145
+
146
+ [source,markdown]
147
+ ----
148
+ | Adapter | Parse (ips) | Serialize (ips) | XPath (ips) | Memory (MB) | Grade |
149
+ |---------|-------------|-----------------|-------------|-------------|-------|
150
+ | Nokogiri | 76 | 13900 | 64958 | -0.1 ⭐ | A |
151
+ | Ox | 289 | 39203 | 9640 | 0.0 ⭐⭐⭐⭐⭐ | A+ |
152
+ ...
153
+ ----
154
+
155
+ Performance visualizations help quickly identify the best adapter for specific
156
+ needs:
157
+
158
+ [source]
159
+ ----
160
+ Parsing (Medium XML):
161
+ Nokogiri █████████████ 76 ips
162
+ Ox ██████████████████████████████████████████████████ 289 ips
163
+ ...
164
+ ----
165
+
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: Overview
3
+ nav_order: 1
4
+ ---
5
+
6
+ == Guides
7
+
8
+ Task-oriented guides for common XML processing operations with Moxml.
9
+
10
+ === Document operations
11
+
12
+ link:parsing-xml[Parsing XML]::
13
+ Parse XML from strings, files, and IO streams with different adapters.
14
+
15
+ link:creating-documents[Creating documents]::
16
+ Build XML documents from scratch using the builder pattern or direct
17
+ manipulation.
18
+
19
+ link:modifying-xml[Modifying XML]::
20
+ Add, remove, and modify elements, attributes, and content in XML documents.
21
+
22
+ link:serializing-xml[Serializing XML]::
23
+ Convert XML documents to strings with formatting and encoding options.
24
+
25
+ === Querying and traversal
26
+
27
+ link:xpath-queries[XPath queries]::
28
+ Query XML documents using XPath expressions with namespaces and predicates.
29
+
30
+ link:node-traversal[Node traversal]::
31
+ Navigate XML document structure using parent, child, and sibling methods.
32
+
33
+ link:finding-nodes[Finding nodes]::
34
+ Different approaches to locate specific nodes in XML documents.
35
+
36
+ === Advanced operations
37
+
38
+ link:namespace-management[Namespace management]::
39
+ Work with XML namespaces including declarations, prefixes, and resolution.
40
+
41
+ link:error-handling[Error handling]::
42
+ Handle XML processing errors effectively with Moxml's error classes.
43
+
44
+ link:performance-optimization[Performance optimization]::
45
+ Tips and techniques for optimizing XML processing performance.
@@ -0,0 +1,293 @@
1
+ ---
2
+ title: Modifying XML
3
+ parent: Overview
4
+ nav_order: 3
5
+ ---
6
+
7
+ == Modifying XML
8
+
9
+ === Purpose
10
+
11
+ Learn how to modify existing XML documents by adding, updating, and removing
12
+ elements, attributes, and content.
13
+
14
+ === Modifying element content
15
+
16
+ Update text content of elements:
17
+
18
+ [source,ruby]
19
+ ----
20
+ xml = '<library><book><title>Old Title</title></book></library>'
21
+ doc = Moxml.new.parse(xml)
22
+
23
+ # Find and update
24
+ title = doc.at_xpath('//title')
25
+ title.text = 'New Title'
26
+
27
+ puts doc.to_xml
28
+ # => <library><book><title>New Title</title></book></library>
29
+ ----
30
+
31
+ === Adding elements
32
+
33
+ Add new elements to existing documents:
34
+
35
+ [source,ruby]
36
+ ----
37
+ xml = '<library><book id="1"><title>Ruby Basics</title></book></library>'
38
+ doc = Moxml.new.parse(xml)
39
+
40
+ book = doc.at_xpath('//book[@id="1"]')
41
+
42
+ # Add author element
43
+ author = doc.create_element('author')
44
+ author.text = 'Jane Smith'
45
+ book.add_child(author)
46
+
47
+ # Add price element
48
+ price = doc.create_element('price')
49
+ price.text = '29.99'
50
+ price['currency'] = 'USD'
51
+ book.add_child(price)
52
+
53
+ # Add ISBN element
54
+ isbn = doc.create_element('isbn')
55
+ isbn.text = '978-0-123456-78-9'
56
+ book.add_child(isbn)
57
+
58
+ puts doc.to_xml(indent: 2)
59
+ ----
60
+
61
+ === Removing elements
62
+
63
+ Remove elements from documents:
64
+
65
+ [source,ruby]
66
+ ----
67
+ xml = <<~XML
68
+ <library>
69
+ <book id="1">
70
+ <title>Ruby Basics</title>
71
+ <author>Jane Smith</author>
72
+ <draft>true</draft>
73
+ </book>
74
+ </library>
75
+ XML
76
+
77
+ doc = Moxml.new.parse(xml)
78
+
79
+ # Find and remove element
80
+ draft = doc.at_xpath('//draft')
81
+ draft.remove
82
+
83
+ # Remove by parent
84
+ book = doc.at_xpath('//book')
85
+ author = book.at_xpath('.//author')
86
+ book.remove_child(author)
87
+
88
+ puts doc.to_xml
89
+ ----
90
+
91
+ === Modifying attributes
92
+
93
+ Update, add, and remove attributes:
94
+
95
+ [source,ruby]
96
+ ----
97
+ xml = '<book id="1" status="draft">Ruby Basics</book>'
98
+ doc = Moxml.new.parse(xml)
99
+
100
+ book = doc.root
101
+
102
+ # Update existing attribute
103
+ book['id'] = '100'
104
+
105
+ # Add new attribute
106
+ book['edition'] = '2nd'
107
+ book['category'] = 'programming'
108
+
109
+ # Remove attribute
110
+ book.remove_attribute('status')
111
+
112
+ # Get all attributes
113
+ book.attributes.each do |attr|
114
+ puts "#{attr.name}=#{attr.value}"
115
+ end
116
+ # => id=100
117
+ # => edition=2nd
118
+ # => category=programming
119
+ ----
120
+
121
+ === Replacing nodes
122
+
123
+ Replace elements with new content:
124
+
125
+ [source,ruby]
126
+ ----
127
+ xml = '<book><title>Old Title</title><author>Old Author</author></book>'
128
+ doc = Moxml.new.parse(xml)
129
+
130
+ # Replace title element
131
+ old_title = doc.at_xpath('//title')
132
+ new_title = doc.create_element('title')
133
+ new_title.text = 'New Title'
134
+ new_title['lang'] = 'en'
135
+
136
+ old_title.replace(new_title)
137
+
138
+ # Replace text node
139
+ author = doc.at_xpath('//author')
140
+ author.children.first.replace(doc.create_text('New Author'))
141
+
142
+ puts doc.to_xml
143
+ ----
144
+
145
+ === Adding siblings
146
+
147
+ Insert elements relative to existing nodes:
148
+
149
+ [source,ruby]
150
+ ----
151
+ xml = <<~XML
152
+ <book>
153
+ <title>Ruby Programming</title>
154
+ <price>29.99</price>
155
+ </book>
156
+ XML
157
+
158
+ doc = Moxml.new.parse(xml)
159
+
160
+ # Add before price
161
+ price = doc.at_xpath('//price')
162
+ author = doc.create_element('author')
163
+ author.text = 'Jane Smith'
164
+ price.add_previous_sibling(author)
165
+
166
+ # Add after title
167
+ title = doc.at_xpath('//title')
168
+ subtitle = doc.create_element('subtitle')
169
+ subtitle.text = 'A Comprehensive Guide'
170
+ title.add_next_sibling(subtitle)
171
+
172
+ puts doc.to_xml(indent: 2)
173
+ ----
174
+
175
+ Output:
176
+
177
+ [source,xml]
178
+ ----
179
+ <book>
180
+ <title>Ruby Programming</title>
181
+ <subtitle>A Comprehensive Guide</subtitle>
182
+ <author>Jane Smith</author>
183
+ <price>29.99</price>
184
+ </book>
185
+ ----
186
+
187
+ === Batch modifications
188
+
189
+ Update multiple elements at once:
190
+
191
+ [source,ruby]
192
+ ----
193
+ xml = <<~XML
194
+ <library>
195
+ <book><price currency="USD">29.99</price></book>
196
+ <book><price currency="USD">39.99</price></book>
197
+ <book><price currency="USD">19.99</price></book>
198
+ </library>
199
+ XML
200
+
201
+ doc = Moxml.new.parse(xml)
202
+
203
+ # Apply 10% discount to all books
204
+ doc.xpath('//price').each do |price|
205
+ current = price.text.to_f
206
+ discounted = (current * 0.9).round(2)
207
+ price.text = discounted.to_s
208
+ price['original'] = current.to_s
209
+ end
210
+
211
+ puts doc.to_xml(indent: 2)
212
+ ----
213
+
214
+ === Preserving structure
215
+
216
+ Maintain document structure during modifications:
217
+
218
+ [source,ruby]
219
+ ----
220
+ xml = <<~XML
221
+ <?xml version="1.0" encoding="UTF-8"?>
222
+ <library>
223
+ <book id="1">Ruby Basics</book>
224
+ </library>
225
+ XML
226
+
227
+ doc = Moxml.new.parse(xml)
228
+
229
+ # Modifications preserve declaration
230
+ book = doc.at_xpath('//book')
231
+ book['edition'] = '2nd'
232
+
233
+ # Original structure maintained
234
+ puts doc.to_xml(indent: 2)
235
+ # => Still has <?xml ... ?> declaration
236
+ ----
237
+
238
+ === Common modification patterns
239
+
240
+ ==== Update or create pattern
241
+
242
+ [source,ruby]
243
+ ----
244
+ def ensure_element(parent, name, text)
245
+ elem = parent.at_xpath(".//#{name}")
246
+
247
+ if elem
248
+ # Update existing
249
+ elem.text = text
250
+ else
251
+ # Create new
252
+ elem = parent.document.create_element(name)
253
+ elem.text = text
254
+ parent.add_child(elem)
255
+ end
256
+
257
+ elem
258
+ end
259
+
260
+ book = doc.at_xpath('//book')
261
+ ensure_element(book, 'author', 'Jane Smith')
262
+ ensure_element(book, 'price', '29.99')
263
+ ----
264
+
265
+ ==== Conditional modification
266
+
267
+ [source,ruby]
268
+ ----
269
+ doc.xpath('//book').each do |book|
270
+ price = book.at_xpath('.//price')
271
+ next unless price
272
+
273
+ # Add discount for expensive books
274
+ if price.text.to_f > 30
275
+ book['discount'] = '10%'
276
+ price.text = (price.text.to_f * 0.9).to_s
277
+ end
278
+ end
279
+ ----
280
+
281
+ === Best practices
282
+
283
+ . **Find before modifying** - always locate elements first
284
+ . **Check element exists** before calling methods on it
285
+ . **Use transactions** for complex modifications if needed
286
+ . **Validate structure** after major changes
287
+ . **Preserve document metadata** (declarations, encoding)
288
+
289
+ === See also
290
+
291
+ * link:creating-documents[Creating documents] - Build from scratch
292
+ * link:../tutorials/basic-usage[Basic usage] - Fundamentals
293
+ * link:../references/element-api[Element API] - Complete method reference