moxml 0.1.7 → 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 (212) 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 +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 -1
  80. data/lib/moxml/adapter/customized_rexml/formatter.rb +11 -6
  81. data/lib/moxml/adapter/headed_ox.rb +161 -0
  82. data/lib/moxml/adapter/libxml.rb +1548 -0
  83. data/lib/moxml/adapter/nokogiri.rb +121 -9
  84. data/lib/moxml/adapter/oga.rb +123 -12
  85. data/lib/moxml/adapter/ox.rb +282 -26
  86. data/lib/moxml/adapter/rexml.rb +127 -20
  87. data/lib/moxml/adapter.rb +21 -4
  88. data/lib/moxml/attribute.rb +6 -0
  89. data/lib/moxml/builder.rb +40 -4
  90. data/lib/moxml/config.rb +8 -3
  91. data/lib/moxml/context.rb +39 -1
  92. data/lib/moxml/doctype.rb +13 -1
  93. data/lib/moxml/document.rb +39 -6
  94. data/lib/moxml/document_builder.rb +27 -5
  95. data/lib/moxml/element.rb +71 -2
  96. data/lib/moxml/error.rb +175 -6
  97. data/lib/moxml/node.rb +94 -3
  98. data/lib/moxml/node_set.rb +34 -0
  99. data/lib/moxml/sax/block_handler.rb +194 -0
  100. data/lib/moxml/sax/element_handler.rb +124 -0
  101. data/lib/moxml/sax/handler.rb +113 -0
  102. data/lib/moxml/sax.rb +31 -0
  103. data/lib/moxml/version.rb +1 -1
  104. data/lib/moxml/xml_utils/encoder.rb +4 -4
  105. data/lib/moxml/xml_utils.rb +7 -4
  106. data/lib/moxml/xpath/ast/node.rb +159 -0
  107. data/lib/moxml/xpath/cache.rb +91 -0
  108. data/lib/moxml/xpath/compiler.rb +1768 -0
  109. data/lib/moxml/xpath/context.rb +26 -0
  110. data/lib/moxml/xpath/conversion.rb +124 -0
  111. data/lib/moxml/xpath/engine.rb +52 -0
  112. data/lib/moxml/xpath/errors.rb +101 -0
  113. data/lib/moxml/xpath/lexer.rb +304 -0
  114. data/lib/moxml/xpath/parser.rb +485 -0
  115. data/lib/moxml/xpath/ruby/generator.rb +269 -0
  116. data/lib/moxml/xpath/ruby/node.rb +193 -0
  117. data/lib/moxml/xpath.rb +37 -0
  118. data/lib/moxml.rb +5 -2
  119. data/moxml.gemspec +3 -1
  120. data/old-specs/moxml/adapter/customized_libxml/.gitkeep +6 -0
  121. data/spec/consistency/README.md +77 -0
  122. data/spec/{moxml/examples/adapter_spec.rb → consistency/adapter_parity_spec.rb} +4 -4
  123. data/spec/examples/README.md +75 -0
  124. data/spec/{support/shared_examples/examples/attribute.rb → examples/attribute_examples_spec.rb} +1 -1
  125. data/spec/{support/shared_examples/examples/basic_usage.rb → examples/basic_usage_spec.rb} +2 -2
  126. data/spec/{support/shared_examples/examples/namespace.rb → examples/namespace_examples_spec.rb} +3 -3
  127. data/spec/{support/shared_examples/examples/readme_examples.rb → examples/readme_examples_spec.rb} +6 -4
  128. data/spec/{support/shared_examples/examples/xpath.rb → examples/xpath_examples_spec.rb} +10 -6
  129. data/spec/integration/README.md +71 -0
  130. data/spec/{moxml/all_with_adapters_spec.rb → integration/all_adapters_spec.rb} +3 -2
  131. data/spec/integration/headed_ox_integration_spec.rb +326 -0
  132. data/spec/{support → integration}/shared_examples/edge_cases.rb +37 -10
  133. data/spec/integration/shared_examples/high_level/.gitkeep +0 -0
  134. data/spec/{support/shared_examples/context.rb → integration/shared_examples/high_level/context_behavior.rb} +2 -1
  135. data/spec/{support/shared_examples/integration.rb → integration/shared_examples/integration_workflows.rb} +23 -6
  136. data/spec/integration/shared_examples/node_wrappers/.gitkeep +0 -0
  137. data/spec/{support/shared_examples/cdata.rb → integration/shared_examples/node_wrappers/cdata_behavior.rb} +6 -1
  138. data/spec/{support/shared_examples/comment.rb → integration/shared_examples/node_wrappers/comment_behavior.rb} +2 -1
  139. data/spec/{support/shared_examples/declaration.rb → integration/shared_examples/node_wrappers/declaration_behavior.rb} +5 -2
  140. data/spec/{support/shared_examples/doctype.rb → integration/shared_examples/node_wrappers/doctype_behavior.rb} +2 -2
  141. data/spec/{support/shared_examples/document.rb → integration/shared_examples/node_wrappers/document_behavior.rb} +1 -1
  142. data/spec/{support/shared_examples/node.rb → integration/shared_examples/node_wrappers/node_behavior.rb} +9 -2
  143. data/spec/{support/shared_examples/node_set.rb → integration/shared_examples/node_wrappers/node_set_behavior.rb} +1 -18
  144. data/spec/{support/shared_examples/processing_instruction.rb → integration/shared_examples/node_wrappers/processing_instruction_behavior.rb} +6 -2
  145. data/spec/moxml/README.md +41 -0
  146. data/spec/moxml/adapter/.gitkeep +0 -0
  147. data/spec/moxml/adapter/README.md +61 -0
  148. data/spec/moxml/adapter/base_spec.rb +27 -0
  149. data/spec/moxml/adapter/headed_ox_spec.rb +311 -0
  150. data/spec/moxml/adapter/libxml_spec.rb +14 -0
  151. data/spec/moxml/adapter/ox_spec.rb +9 -8
  152. data/spec/moxml/adapter/shared_examples/.gitkeep +0 -0
  153. data/spec/{support/shared_examples/xml_adapter.rb → moxml/adapter/shared_examples/adapter_contract.rb} +39 -12
  154. data/spec/moxml/adapter_spec.rb +16 -0
  155. data/spec/moxml/attribute_spec.rb +30 -0
  156. data/spec/moxml/builder_spec.rb +33 -0
  157. data/spec/moxml/cdata_spec.rb +31 -0
  158. data/spec/moxml/comment_spec.rb +31 -0
  159. data/spec/moxml/config_spec.rb +3 -3
  160. data/spec/moxml/context_spec.rb +28 -0
  161. data/spec/moxml/declaration_spec.rb +36 -0
  162. data/spec/moxml/doctype_spec.rb +33 -0
  163. data/spec/moxml/document_builder_spec.rb +30 -0
  164. data/spec/moxml/document_spec.rb +105 -0
  165. data/spec/moxml/element_spec.rb +143 -0
  166. data/spec/moxml/error_spec.rb +266 -22
  167. data/spec/{moxml_spec.rb → moxml/moxml_spec.rb} +9 -9
  168. data/spec/moxml/namespace_spec.rb +32 -0
  169. data/spec/moxml/node_set_spec.rb +39 -0
  170. data/spec/moxml/node_spec.rb +37 -0
  171. data/spec/moxml/processing_instruction_spec.rb +34 -0
  172. data/spec/moxml/sax_spec.rb +1067 -0
  173. data/spec/moxml/text_spec.rb +31 -0
  174. data/spec/moxml/version_spec.rb +14 -0
  175. data/spec/moxml/xml_utils/.gitkeep +0 -0
  176. data/spec/moxml/xml_utils/encoder_spec.rb +27 -0
  177. data/spec/moxml/xml_utils_spec.rb +49 -0
  178. data/spec/moxml/xpath/ast/node_spec.rb +83 -0
  179. data/spec/moxml/xpath/axes_spec.rb +296 -0
  180. data/spec/moxml/xpath/cache_spec.rb +358 -0
  181. data/spec/moxml/xpath/compiler_spec.rb +406 -0
  182. data/spec/moxml/xpath/context_spec.rb +210 -0
  183. data/spec/moxml/xpath/conversion_spec.rb +365 -0
  184. data/spec/moxml/xpath/fixtures/sample.xml +25 -0
  185. data/spec/moxml/xpath/functions/boolean_functions_spec.rb +114 -0
  186. data/spec/moxml/xpath/functions/node_functions_spec.rb +145 -0
  187. data/spec/moxml/xpath/functions/numeric_functions_spec.rb +164 -0
  188. data/spec/moxml/xpath/functions/position_functions_spec.rb +93 -0
  189. data/spec/moxml/xpath/functions/special_functions_spec.rb +89 -0
  190. data/spec/moxml/xpath/functions/string_functions_spec.rb +381 -0
  191. data/spec/moxml/xpath/lexer_spec.rb +488 -0
  192. data/spec/moxml/xpath/parser_integration_spec.rb +210 -0
  193. data/spec/moxml/xpath/parser_spec.rb +364 -0
  194. data/spec/moxml/xpath/ruby/generator_spec.rb +421 -0
  195. data/spec/moxml/xpath/ruby/node_spec.rb +291 -0
  196. data/spec/moxml/xpath_capabilities_spec.rb +199 -0
  197. data/spec/moxml/xpath_spec.rb +77 -0
  198. data/spec/performance/README.md +83 -0
  199. data/spec/performance/benchmark_spec.rb +64 -0
  200. data/spec/{support/shared_examples/examples/memory.rb → performance/memory_usage_spec.rb} +3 -1
  201. data/spec/{support/shared_examples/examples/thread_safety.rb → performance/thread_safety_spec.rb} +3 -1
  202. data/spec/performance/xpath_benchmark_spec.rb +259 -0
  203. data/spec/spec_helper.rb +58 -1
  204. data/spec/support/xml_matchers.rb +1 -1
  205. metadata +176 -34
  206. data/spec/support/shared_examples/examples/benchmark_spec.rb +0 -51
  207. /data/spec/{support/shared_examples/builder.rb → integration/shared_examples/high_level/builder_behavior.rb} +0 -0
  208. /data/spec/{support/shared_examples/document_builder.rb → integration/shared_examples/high_level/document_builder_behavior.rb} +0 -0
  209. /data/spec/{support/shared_examples/attribute.rb → integration/shared_examples/node_wrappers/attribute_behavior.rb} +0 -0
  210. /data/spec/{support/shared_examples/element.rb → integration/shared_examples/node_wrappers/element_behavior.rb} +0 -0
  211. /data/spec/{support/shared_examples/namespace.rb → integration/shared_examples/node_wrappers/namespace_behavior.rb} +0 -0
  212. /data/spec/{support/shared_examples/text.rb → integration/shared_examples/node_wrappers/text_behavior.rb} +0 -0
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Moxml
4
+ module XPath
5
+ module AST
6
+ # Abstract base class for all XPath AST nodes
7
+ #
8
+ # All AST nodes must implement the #evaluate method which takes
9
+ # a context and returns a result (NodeSet, String, Number, or Boolean).
10
+ #
11
+ # @abstract Subclass and override {#evaluate} to implement
12
+ class Node
13
+ attr_reader :type, :children, :value
14
+
15
+ # Initialize a new AST node
16
+ #
17
+ # @param type [Symbol] Node type
18
+ # @param children [Array] Child nodes
19
+ # @param value [Object] Optional value for leaf nodes
20
+ def initialize(type = :node, children = [], value = nil)
21
+ @type = type
22
+ @children = Array(children)
23
+ @value = value
24
+ end
25
+
26
+ # Evaluate this AST node in the given context
27
+ #
28
+ # @param context [Moxml::XPath::Context] Evaluation context
29
+ # @return [Moxml::NodeSet, String, Numeric, Boolean] Result of evaluation
30
+ # @raise [NotImplementedError] if not overridden by subclass
31
+ def evaluate(context)
32
+ raise ::NotImplementedError,
33
+ "#{self.class}#evaluate must be implemented by subclass"
34
+ end
35
+
36
+ # Check if this node is a constant value
37
+ #
38
+ # @return [Boolean] true if node represents a constant value
39
+ def constant?
40
+ false
41
+ end
42
+
43
+ # Get the result type of this node
44
+ #
45
+ # @return [Symbol] One of :node_set, :string, :number, :boolean
46
+ def result_type
47
+ :unknown
48
+ end
49
+
50
+ # String representation for debugging
51
+ #
52
+ # @return [String] Debug representation
53
+ def inspect
54
+ if @value
55
+ "#<#{self.class.name} @type=#{@type} @value=#{@value.inspect}>"
56
+ elsif @children.any?
57
+ "#<#{self.class.name} @type=#{@type} children=#{@children.size}>"
58
+ else
59
+ "#<#{self.class.name} @type=#{@type}>"
60
+ end
61
+ end
62
+
63
+ alias to_s inspect
64
+
65
+ # Factory methods for creating specific node types
66
+
67
+ # Create an absolute path node (starts with / or //)
68
+ def self.absolute_path(descendant_or_self, *steps)
69
+ new(:absolute_path, [descendant_or_self] + steps)
70
+ end
71
+
72
+ # Create a relative path node
73
+ def self.relative_path(*steps)
74
+ new(:relative_path, steps)
75
+ end
76
+
77
+ # Create a path node
78
+ def self.path(*steps)
79
+ new(:path, steps)
80
+ end
81
+
82
+ # Create an axis node
83
+ def self.axis(axis_name, node_test, *predicates)
84
+ new(:axis, [axis_name, node_test] + predicates)
85
+ end
86
+
87
+ # Create a node test
88
+ def self.test(namespace, name)
89
+ new(:test, [], { namespace: namespace, name: name })
90
+ end
91
+
92
+ # Create a wildcard test
93
+ def self.wildcard
94
+ new(:wildcard)
95
+ end
96
+
97
+ # Create a predicate node
98
+ def self.predicate(condition)
99
+ new(:predicate, [condition])
100
+ end
101
+
102
+ # Create a function call node
103
+ def self.function(name, *args)
104
+ new(:function, args, name)
105
+ end
106
+
107
+ # Create a variable reference node
108
+ def self.variable(name)
109
+ new(:variable, [], name)
110
+ end
111
+
112
+ # Create a literal string node
113
+ def self.string(value)
114
+ new(:string, [], value)
115
+ end
116
+
117
+ # Create a literal number node
118
+ def self.number(value)
119
+ new(:number, [], value.to_f)
120
+ end
121
+
122
+ # Create a binary operator node
123
+ def self.binary_op(operator, left, right)
124
+ new(:binary_op, [left, right], operator)
125
+ end
126
+
127
+ # Create a unary operator node
128
+ def self.unary_op(operator, operand)
129
+ new(:unary_op, [operand], operator)
130
+ end
131
+
132
+ # Create a union node (|)
133
+ def self.union(*expressions)
134
+ new(:union, expressions)
135
+ end
136
+
137
+ # Create an attribute node
138
+ def self.attribute(name)
139
+ new(:attribute, [], name)
140
+ end
141
+
142
+ # Create a current node (.)
143
+ def self.current
144
+ new(:current)
145
+ end
146
+
147
+ # Create a parent node (..)
148
+ def self.parent
149
+ new(:parent)
150
+ end
151
+
152
+ # Create a node type test (text(), comment(), etc.)
153
+ def self.node_type(type_name)
154
+ new(:node_type, [], type_name)
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Moxml
4
+ module XPath
5
+ # Simple LRU (Least Recently Used) cache for compiled XPath expressions.
6
+ #
7
+ # @private
8
+ class Cache
9
+ DEFAULT_SIZE = 1000
10
+
11
+ # @param [Integer] max_size Maximum number of entries to cache
12
+ def initialize(max_size = DEFAULT_SIZE)
13
+ @max_size = max_size
14
+ @cache = {}
15
+ @access_order = []
16
+ end
17
+
18
+ # Gets a value from the cache or sets it using the provided block.
19
+ #
20
+ # @param [Object] key Cache key
21
+ # @yield Block to execute if key is not in cache
22
+ # @return [Object] Cached or newly computed value
23
+ def get_or_set(key)
24
+ if @cache.key?(key)
25
+ # Move to end (most recently used)
26
+ @access_order.delete(key)
27
+ @access_order.push(key)
28
+ @cache[key]
29
+ else
30
+ value = yield
31
+ set(key, value)
32
+ value
33
+ end
34
+ end
35
+
36
+ # Sets a value in the cache.
37
+ #
38
+ # @param [Object] key
39
+ # @param [Object] value
40
+ # @return [Object] The value
41
+ def set(key, value)
42
+ if @cache.key?(key)
43
+ @access_order.delete(key)
44
+ elsif @cache.size >= @max_size
45
+ # Remove least recently used
46
+ lru_key = @access_order.shift
47
+ @cache.delete(lru_key)
48
+ end
49
+
50
+ @cache[key] = value
51
+ @access_order.push(key)
52
+ value
53
+ end
54
+
55
+ # Gets a value from the cache.
56
+ #
57
+ # @param [Object] key
58
+ # @return [Object, nil]
59
+ def get(key)
60
+ return unless @cache.key?(key)
61
+
62
+ @access_order.delete(key)
63
+ @access_order.push(key)
64
+ @cache[key]
65
+ end
66
+
67
+ # Clears the cache.
68
+ #
69
+ # @return [void]
70
+ def clear
71
+ @cache.clear
72
+ @access_order.clear
73
+ end
74
+
75
+ # Returns the current size of the cache.
76
+ #
77
+ # @return [Integer]
78
+ def size
79
+ @cache.size
80
+ end
81
+
82
+ # Checks if a key exists in the cache.
83
+ #
84
+ # @param [Object] key
85
+ # @return [Boolean]
86
+ def key?(key)
87
+ @cache.key?(key)
88
+ end
89
+ end
90
+ end
91
+ end