moxml 0.1.14 → 0.1.16

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +117 -66
  3. data/Gemfile +1 -0
  4. data/README.adoc +11 -9
  5. data/Rakefile +34 -1
  6. data/TODO.remaining/1-entity-reference-adapter-support.md +157 -0
  7. data/TODO.remaining/2-entity-restoration-model-driven.md +169 -0
  8. data/TODO.remaining/3-entity-reference-test-coverage.md +170 -0
  9. data/TODO.remaining/4-lenient-entities-mode.md +106 -0
  10. data/TODO.remaining/5-fixture-integrity.md +65 -0
  11. data/TODO.remaining/6-ox-element-ordering-bug.md +36 -0
  12. data/TODO.remaining/7-headed-ox-limitations.md +95 -0
  13. data/TODO.remaining/8-xpath-predicate-gaps.md +68 -0
  14. data/TODO.remaining/9-cleanup-hygiene.md +42 -0
  15. data/TODO.remaining/README.md +54 -0
  16. data/benchmarks/generate_report.rb +1 -1
  17. data/docs/_pages/configuration.adoc +22 -19
  18. data/docs/_tutorials/namespace-handling.adoc +5 -5
  19. data/lib/moxml/adapter/base.rb +22 -3
  20. data/lib/moxml/adapter/customized_libxml/declaration.rb +1 -1
  21. data/lib/moxml/adapter/customized_libxml/entity_reference.rb +23 -0
  22. data/lib/moxml/adapter/customized_libxml.rb +18 -0
  23. data/lib/moxml/adapter/customized_oga.rb +10 -0
  24. data/lib/moxml/adapter/customized_ox/entity_reference.rb +25 -0
  25. data/lib/moxml/adapter/customized_ox.rb +12 -0
  26. data/lib/moxml/adapter/customized_rexml/entity_reference.rb +19 -0
  27. data/lib/moxml/adapter/customized_rexml/formatter.rb +44 -20
  28. data/lib/moxml/adapter/customized_rexml.rb +11 -0
  29. data/lib/moxml/adapter/headed_ox.rb +37 -14
  30. data/lib/moxml/adapter/libxml.rb +233 -119
  31. data/lib/moxml/adapter/nokogiri.rb +22 -11
  32. data/lib/moxml/adapter/oga.rb +64 -25
  33. data/lib/moxml/adapter/ox.rb +198 -42
  34. data/lib/moxml/adapter/rexml.rb +64 -13
  35. data/lib/moxml/attribute.rb +3 -0
  36. data/lib/moxml/builder.rb +78 -24
  37. data/lib/moxml/config.rb +24 -7
  38. data/lib/moxml/declaration.rb +4 -2
  39. data/lib/moxml/document.rb +8 -1
  40. data/lib/moxml/document_builder.rb +44 -37
  41. data/lib/moxml/element.rb +18 -5
  42. data/lib/moxml/entity_registry.rb +51 -1
  43. data/lib/moxml/native_attachment.rb +65 -0
  44. data/lib/moxml/node.rb +39 -50
  45. data/lib/moxml/node_set.rb +43 -15
  46. data/lib/moxml/version.rb +1 -1
  47. data/lib/moxml/xml_utils.rb +1 -1
  48. data/lib/moxml/xpath/compiler.rb +4 -1
  49. data/lib/moxml.rb +1 -0
  50. data/scripts/format_xml.rb +16 -0
  51. data/scripts/pretty_format_xml.rb +14 -0
  52. data/spec/consistency/round_trip_spec.rb +3 -30
  53. data/spec/integration/all_adapters_spec.rb +1 -0
  54. data/spec/integration/headed_ox_integration_spec.rb +0 -2
  55. data/spec/integration/shared_examples/edge_cases.rb +7 -4
  56. data/spec/integration/shared_examples/integration_workflows.rb +3 -3
  57. data/spec/integration/shared_examples/node_wrappers/cdata_behavior.rb +1 -1
  58. data/spec/integration/shared_examples/node_wrappers/entity_reference_behavior.rb +224 -0
  59. data/spec/integration/shared_examples/node_wrappers/node_behavior.rb +1 -1
  60. data/spec/moxml/adapter/headed_ox_spec.rb +8 -8
  61. data/spec/moxml/adapter/oga_spec.rb +46 -0
  62. data/spec/moxml/adapter/shared_examples/adapter_contract.rb +1 -12
  63. data/spec/moxml/allocation_benchmark_spec.rb +96 -0
  64. data/spec/moxml/allocation_guard_spec.rb +282 -0
  65. data/spec/moxml/builder_spec.rb +256 -0
  66. data/spec/moxml/config_spec.rb +11 -11
  67. data/spec/moxml/doctype_spec.rb +41 -0
  68. data/spec/moxml/lazy_parse_spec.rb +115 -0
  69. data/spec/moxml/namespace_uri_validation_spec.rb +11 -3
  70. data/spec/moxml/node_cache_spec.rb +110 -0
  71. data/spec/moxml/node_set_cache_spec.rb +90 -0
  72. data/spec/moxml/xml_utils_spec.rb +32 -0
  73. data/spec/moxml/xpath/axes_spec.rb +1 -1
  74. data/spec/moxml/xpath/compiler_spec.rb +2 -2
  75. data/spec/moxml/xpath/functions/position_functions_spec.rb +5 -5
  76. data/spec/moxml/xpath/functions/special_functions_spec.rb +1 -1
  77. data/spec/performance/memory_usage_spec.rb +0 -4
  78. data/spec/support/allocation_helper.rb +165 -0
  79. data/spec/support/w3c_namespace_helpers.rb +2 -1
  80. metadata +29 -2
data/lib/moxml/node.rb CHANGED
@@ -16,8 +16,14 @@ module Moxml
16
16
 
17
17
  def initialize(native, context)
18
18
  @context = context
19
- # @native = adapter.patch_node(native)
20
19
  @native = native
20
+ @parent_node = nil
21
+ end
22
+
23
+ # Update native reference after identity-changing operations
24
+ # (e.g., LibXML doc.root= creates a new Ruby wrapper)
25
+ def refresh_native!(new_native)
26
+ @native = new_native
21
27
  end
22
28
 
23
29
  def document
@@ -29,9 +35,10 @@ module Moxml
29
35
  end
30
36
 
31
37
  def children
32
- NodeSet.new(
38
+ @children ||= NodeSet.new(
33
39
  adapter.children(@native).map { adapter.patch_node(_1, @native) },
34
40
  context,
41
+ self,
35
42
  )
36
43
  end
37
44
 
@@ -46,29 +53,40 @@ module Moxml
46
53
  def add_child(node)
47
54
  node = prepare_node(node)
48
55
  adapter.add_child(@native, node.native)
56
+ # Refresh native in case adapter changed identity (e.g., LibXML doc.root=)
57
+ refreshed = adapter.actual_native(node.native, @native)
58
+ node.refresh_native!(refreshed) if refreshed && refreshed != node.native
59
+ node.parent_node = self
60
+ invalidate_children_cache!
49
61
  self
50
62
  end
51
63
 
52
64
  def add_previous_sibling(node)
53
65
  node = prepare_node(node)
54
66
  adapter.add_previous_sibling(@native, node.native)
67
+ invalidate_parent_children_cache!
55
68
  self
56
69
  end
57
70
 
58
71
  def add_next_sibling(node)
59
72
  node = prepare_node(node)
60
73
  adapter.add_next_sibling(@native, node.native)
74
+ invalidate_parent_children_cache!
61
75
  self
62
76
  end
63
77
 
64
78
  def remove
79
+ invalidate_parent_children_cache!
65
80
  adapter.remove(@native)
81
+ invalidate_children_cache!
66
82
  self
67
83
  end
68
84
 
69
85
  def replace(node)
70
86
  node = prepare_node(node)
87
+ invalidate_parent_children_cache!
71
88
  adapter.replace(@native, node.native)
89
+ invalidate_children_cache!
72
90
  self
73
91
  end
74
92
 
@@ -219,6 +237,11 @@ module Moxml
219
237
  klass.new(node, context)
220
238
  end
221
239
 
240
+ # Internal: Set the parent node for cache invalidation tracking.
241
+ # Called by NodeSet, Document, Element when establishing parent-child
242
+ # relationships. Public to allow cross-class usage within Moxml internals.
243
+ attr_writer :parent_node
244
+
222
245
  protected
223
246
 
224
247
  def adapter
@@ -229,6 +252,18 @@ module Moxml
229
252
  context.config.adapter
230
253
  end
231
254
 
255
+ # Invalidate cached children. Called by mutation methods
256
+ # and by Element attribute/namespace caches.
257
+ def invalidate_children_cache!
258
+ @children = nil
259
+ end
260
+
261
+ # Invalidate parent's cached children when this node
262
+ # is removed/replaced from its parent's child list.
263
+ def invalidate_parent_children_cache!
264
+ @parent_node&.invalidate_children_cache!
265
+ end
266
+
232
267
  private
233
268
 
234
269
  def prepare_node(node)
@@ -260,54 +295,8 @@ module Moxml
260
295
  return options[:declaration] if options.key?(:declaration)
261
296
  return options.fetch(:declaration, false) unless is_a?(Document)
262
297
 
263
- # For Document nodes, check both wrapper flag and native state
264
- # Wrapper flag is set by Context.parse for parsed documents
265
- # Native state reflects programmatic changes (e.g., add/remove)
266
-
267
- adapter_name = adapter.to_s.split("::").last
268
-
269
- case adapter_name
270
- when "Nokogiri"
271
- # Nokogiri: if @xml_decl is explicitly set, use that state
272
- # Otherwise, trust wrapper flag (for parsed documents)
273
- if native.respond_to?(:instance_variable_defined?) &&
274
- native.instance_variable_defined?(:@xml_decl)
275
- # Explicitly set (programmatically added) - check if nil
276
- !native.instance_variable_get(:@xml_decl).nil?
277
- else
278
- # Not set (parsed document) - trust wrapper flag
279
- has_xml_declaration
280
- end
281
- when "Rexml"
282
- # REXML: check @xml_declaration instance variable
283
- # If not defined (parsed doc), trust wrapper flag
284
- if native.respond_to?(:instance_variable_defined?) &&
285
- native.instance_variable_defined?(:@xml_declaration)
286
- # Explicitly set - check if nil
287
- !native.instance_variable_get(:@xml_declaration).nil?
288
- else
289
- # Not set (parsed document) - trust wrapper flag
290
- has_xml_declaration
291
- end
292
- when "Oga"
293
- native.respond_to?(:xml_declaration) && !native.xml_declaration.nil?
294
- when "Ox", "HeadedOx"
295
- # Ox stores declaration in document attributes
296
- native[:version] || native[:encoding] || native[:standalone]
297
- when "Libxml"
298
- # LibXML stores declaration wrapper as instance variable
299
- if native.respond_to?(:instance_variable_defined?) &&
300
- native.instance_variable_defined?(:@moxml_declaration)
301
- # Explicitly set - check if nil
302
- !native.instance_variable_get(:@moxml_declaration).nil?
303
- else
304
- # Not set - trust wrapper flag
305
- has_xml_declaration
306
- end
307
- else
308
- # Fallback - trust wrapper flag
309
- has_xml_declaration
310
- end
298
+ # For Document nodes, delegate to adapter for native state check
299
+ adapter.has_declaration?(@native, self)
311
300
  end
312
301
  end
313
302
  end
@@ -6,60 +6,72 @@ module Moxml
6
6
 
7
7
  attr_reader :nodes, :context
8
8
 
9
- def initialize(nodes, context)
9
+ def initialize(nodes, context, parent_node = nil)
10
10
  @nodes = Array(nodes)
11
11
  @context = context
12
+ @wrapped = Array.new(@nodes.size)
13
+ @parent_node = parent_node
12
14
  end
13
15
 
14
16
  def each
15
17
  return to_enum(:each) unless block_given?
16
18
 
17
- nodes.each { |node| yield Moxml::Node.wrap(node, context) }
19
+ @nodes.each_with_index do |node, i|
20
+ @wrapped[i] ||= wrap_with_parent(node)
21
+ yield @wrapped[i]
22
+ end
18
23
  self
19
24
  end
20
25
 
21
26
  def [](index)
22
27
  case index
23
28
  when Integer
24
- Moxml::Node.wrap(nodes[index], context)
29
+ actual = index.negative? ? @nodes.size + index : index
30
+ return nil unless actual >= 0 && actual < @nodes.size
31
+
32
+ @wrapped[actual] ||= wrap_with_parent(@nodes[actual])
25
33
  when Range
26
- NodeSet.new(nodes[index], context)
34
+ self.class.new(@nodes[index], @context)
27
35
  end
28
36
  end
29
37
 
30
38
  def first(n = nil)
31
39
  if n.nil?
32
- Moxml::Node.wrap(nodes.first, context)
40
+ @nodes.empty? ? nil : self[0]
33
41
  else
34
- nodes.first(n).map { |node| Moxml::Node.wrap(node, context) }
42
+ n.times.filter_map { |i| self[i] }
35
43
  end
36
44
  end
37
45
 
38
46
  def last
39
- Moxml::Node.wrap(nodes.last, context)
47
+ @nodes.empty? ? nil : self[@nodes.size - 1]
40
48
  end
41
49
 
42
50
  def empty?
43
- nodes.empty?
51
+ @nodes.empty?
44
52
  end
45
53
 
46
54
  def size
47
- nodes.size
55
+ @nodes.size
48
56
  end
49
57
  alias length size
50
58
 
51
59
  def to_a
52
- map { |node| node }
60
+ @nodes.each_with_index do |_node, i|
61
+ @wrapped[i] ||= wrap_with_parent(@nodes[i])
62
+ end
63
+ @wrapped.compact
53
64
  end
54
65
 
55
66
  def +(other)
56
- self.class.new(nodes + other.nodes, context)
67
+ self.class.new(@nodes + other.nodes, @context)
57
68
  end
58
69
 
59
70
  def <<(node)
60
71
  # If it's a wrapped Moxml node, unwrap to native before storing
61
72
  native_node = node.respond_to?(:native) ? node.native : node
62
73
  @nodes << native_node
74
+ @wrapped << nil
63
75
  self
64
76
  end
65
77
  alias push <<
@@ -78,14 +90,14 @@ module Moxml
78
90
  true
79
91
  end
80
92
  end
81
- self.class.new(unique_natives, context)
93
+ self.class.new(unique_natives, @context)
82
94
  end
83
95
 
84
96
  def ==(other)
85
97
  self.class == other.class &&
86
98
  length == other.length &&
87
- nodes.each_with_index.all? do |node, index|
88
- Moxml::Node.wrap(node, context) == other[index]
99
+ @nodes.each_with_index.all? do |_node, index|
100
+ self[index] == other[index]
89
101
  end
90
102
  end
91
103
 
@@ -103,8 +115,24 @@ module Moxml
103
115
  def delete(node)
104
116
  # If it's a wrapped Moxml node, unwrap to native
105
117
  native_node = node.respond_to?(:native) ? node.native : node
106
- @nodes.delete(native_node)
118
+ idx = @nodes.index(native_node)
119
+ if idx
120
+ @nodes.delete_at(idx)
121
+ @wrapped.delete_at(idx)
122
+ else
123
+ @nodes.delete(native_node)
124
+ end
107
125
  self
108
126
  end
127
+
128
+ private
129
+
130
+ def wrap_with_parent(native_node)
131
+ wrapped = Moxml::Node.wrap(native_node, @context)
132
+ if @parent_node && wrapped
133
+ wrapped.parent_node = @parent_node
134
+ end
135
+ wrapped
136
+ end
109
137
  end
110
138
  end
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.1.14"
4
+ VERSION = "0.1.16"
5
5
  end
@@ -81,7 +81,7 @@ module Moxml
81
81
  end
82
82
 
83
83
  def validate_prefix(prefix)
84
- return if prefix.match?(/\A[a-zA-Z_][\w-]*\z/)
84
+ return if prefix.match?(/\A[a-zA-Z_][\w.-]*\z/)
85
85
 
86
86
  raise ValidationError, "Invalid namespace prefix: #{prefix}"
87
87
  end
@@ -1738,7 +1738,10 @@ module Moxml
1738
1738
  until visit.empty?
1739
1739
  current = visit.pop
1740
1740
 
1741
- return true if current.type == :call && current.children[0] == name
1741
+ # Function name is stored in :value field, not children
1742
+ if (current.type == :call || current.type == :function) && current.value == name
1743
+ return true
1744
+ end
1742
1745
 
1743
1746
  current.children.each do |child|
1744
1747
  visit << child if child.is_a?(AST::Node)
data/lib/moxml.rb CHANGED
@@ -43,6 +43,7 @@ require_relative "moxml/builder"
43
43
  require_relative "moxml/config"
44
44
  require_relative "moxml/context"
45
45
  require_relative "moxml/entity_registry"
46
+ require_relative "moxml/native_attachment"
46
47
  require_relative "moxml/adapter"
47
48
  require_relative "moxml/xpath"
48
49
  require_relative "moxml/sax"
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "nokogiri"
5
+
6
+ input = ARGV[0]
7
+ output = ARGV[1] || input
8
+
9
+ abort "Usage: #{$PROGRAM_NAME} <input.xml> [output.xml]" unless input
10
+ abort "File not found: #{input}" unless File.exist?(input)
11
+
12
+ doc = Nokogiri::XML(File.read(input), &:noblanks)
13
+ formatted = doc.to_xml(indent: 2)
14
+
15
+ File.write(output, formatted)
16
+ puts "Formatted #{input}#{" -> #{output}" if output != input}"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "nokogiri"
5
+
6
+ input = ARGV[0] or abort "Usage: #{$0} <input.xml> [output.xml]"
7
+ output = ARGV[1] || input
8
+
9
+ xml = File.read(input)
10
+ doc = Nokogiri::XML(xml, &:noblanks)
11
+ formatted = doc.to_xml(indent: 2)
12
+
13
+ File.write(output, formatted)
14
+ puts "Written to #{output}"
@@ -62,14 +62,9 @@ def traverse_with_consistent_order(element, elements_array)
62
62
  end
63
63
 
64
64
  if element.respond_to?(:children)
65
- # ENHANCED: More robust child selection and sorting
65
+ # Only traverse element children (skip text, comment, cdata nodes)
66
66
  children = element.children.select do |child|
67
- # Only process element nodes with valid names
68
- child.respond_to?(:name) &&
69
- child.name &&
70
- !child.name.empty? &&
71
- child.name != "text" &&
72
- child.name != "comment"
67
+ child.respond_to?(:element?) && child.element?
73
68
  end
74
69
 
75
70
  # CRITICAL: Enhanced sorting with multiple criteria for stability
@@ -324,29 +319,7 @@ EXAMPLE_TIMEOUT = ENV.fetch("MOXML_ROUNDTRIP_TIMEOUT", 120).to_i
324
319
  # Fixture cache — loaded once, shared across all examples.
325
320
  FIXTURE_CACHE = {}
326
321
 
327
- # Known element ordering issues with Ox adapter.
328
- # These (fixture_relative_path, source_adapter, target_adapter) tuples fail the
329
- # elements_with_attributes comparison because Ox produces elements in a different
330
- # order. The semantic equivalence check (double round-trip) still passes.
331
- # TODO: Investigate and fix the root cause in ox adapter element ordering.
332
- KNOWN_ELEMENT_ORDERING_ISSUES = Set.new([
333
- # niso-jats/element_citation.xml - Ox produces different element ordering
334
- ["niso-jats/element_citation.xml", :nokogiri, :ox],
335
- ["niso-jats/element_citation.xml", :ox, :nokogiri],
336
- ["niso-jats/element_citation.xml", :ox, :oga],
337
- ["niso-jats/element_citation.xml", :oga, :ox],
338
- ["niso-jats/element_citation.xml", :rexml, :ox],
339
- ["niso-jats/element_citation.xml", :ox, :rexml],
340
- ["niso-jats/pnas_sample.xml", :nokogiri, :rexml],
341
- ["niso-jats/pnas_sample.xml", :rexml, :nokogiri],
342
- # metanorma fixtures with similar issues
343
- ["metanorma/collection1nested.xml", :nokogiri, :ox],
344
- ["metanorma/collection1nested.xml", :ox, :nokogiri],
345
- ["metanorma/collection1nested.xml", :ox, :oga],
346
- ["metanorma/collection1nested.xml", :oga, :ox],
347
- ["metanorma/collection1nested.xml", :rexml, :ox],
348
- ["metanorma/collection1nested.xml", :ox, :rexml],
349
- ])
322
+ KNOWN_ELEMENT_ORDERING_ISSUES = Set.new([])
350
323
 
351
324
  RSpec.describe "Round-trip XML Testing", :round_trip do
352
325
  # Explicit adapter names for clarity and maintainability.
@@ -15,6 +15,7 @@ RSpec.describe "Cross-adapter integration" do
15
15
  "Moxml::Declaration",
16
16
  "Moxml::Doctype",
17
17
  "Moxml::Document",
18
+ "Moxml::EntityReference",
18
19
  "Moxml::Context",
19
20
  "Moxml::Builder",
20
21
  "Moxml::DocumentBuilder",
@@ -187,7 +187,6 @@ RSpec.describe "HeadedOx Integration" do
187
187
  end
188
188
 
189
189
  it "selects last node with last()" do
190
- skip "HeadedOx limitation: last() in predicate context needs temporary nodeset. See docs/HEADED_OX_LIMITATIONS.md"
191
190
  last_book = doc.xpath("//book[position() = last()]")
192
191
 
193
192
  expect(last_book.size).to eq(1)
@@ -198,7 +197,6 @@ RSpec.describe "HeadedOx Integration" do
198
197
 
199
198
  describe "real-world use cases" do
200
199
  it "finds books by author and price range" do
201
- skip "HeadedOx limitation: Text content access from nested elements needs investigation. See docs/HEADED_OX_LIMITATIONS.md"
202
200
  results = doc.xpath(
203
201
  "//book[contains(author, 'Alice') and @price < 20]",
204
202
  )
@@ -36,7 +36,7 @@ RSpec.shared_examples "Moxml Edge Cases" do
36
36
  pending "Ox doesn't escape the end token"
37
37
  end
38
38
  if context.config.adapter_name == :headed_ox
39
- skip "HeadedOx limitation: Ox doesn't escape CDATA end markers. See docs/HEADED_OX_LIMITATIONS.md"
39
+ skip "HeadedOx limitation: Ox doesn't escape CDATA end markers. See docs/_pages/headed-ox-limitations.adoc"
40
40
  end
41
41
  cdata_text = "]]>]]>]]>"
42
42
  doc = context.create_document
@@ -90,11 +90,14 @@ RSpec.shared_examples "Moxml Edge Cases" do
90
90
  pending "Ox doesn't have a native XPath"
91
91
  end
92
92
  if context.config.adapter_name == :headed_ox
93
- skip "HeadedOx limitation: Namespace methods not implemented in adapter. Requires Ox namespace API enhancement. See docs/HEADED_OX_LIMITATIONS.md"
93
+ skip "HeadedOx limitation: Namespace methods not implemented in adapter. Requires Ox namespace API enhancement. See docs/_pages/headed-ox-limitations.adoc"
94
94
  end
95
95
  if context.config.adapter_name == :libxml
96
96
  skip "LibXML cannot query empty default namespace with XPath (documented limitation)"
97
97
  end
98
+ if context.config.adapter_name == :nokogiri
99
+ skip "Nokogiri XPath does not support querying empty namespace with xmlns prefix mapping"
100
+ end
98
101
  xml = <<~XML
99
102
  <root xmlns="http://default1.org">
100
103
  <child xmlns="http://default2.org">
@@ -113,7 +116,7 @@ RSpec.shared_examples "Moxml Edge Cases" do
113
116
  pending "Ox doesn't have a native XPath"
114
117
  end
115
118
  if context.config.adapter_name == :headed_ox
116
- skip "HeadedOx limitation: Namespace methods not implemented in adapter. Requires Ox namespace API enhancement. See docs/HEADED_OX_LIMITATIONS.md"
119
+ skip "HeadedOx limitation: Namespace methods not implemented in adapter. Requires Ox namespace API enhancement. See docs/_pages/headed-ox-limitations.adoc"
117
120
  end
118
121
 
119
122
  xml = <<~XML
@@ -133,7 +136,7 @@ RSpec.shared_examples "Moxml Edge Cases" do
133
136
  describe "attribute edge cases" do
134
137
  it "handles attributes with same local name but different namespaces" do
135
138
  if context.config.adapter_name == :headed_ox
136
- skip "HeadedOx limitation: Namespace-prefixed attribute access needs Ox namespace API enhancement. See docs/HEADED_OX_LIMITATIONS.md"
139
+ skip "HeadedOx limitation: Namespace-prefixed attribute access needs Ox namespace API enhancement. See docs/_pages/headed-ox-limitations.adoc"
137
140
  end
138
141
  if context.config.adapter_name == :ox
139
142
  skip "Ox doesn't have a native XPath"
@@ -60,7 +60,7 @@ RSpec.shared_examples "Moxml Integration" do
60
60
  pending "Ox doesn't support namespace-aware XPath with predicates"
61
61
  end
62
62
  if context.config.adapter_name == :headed_ox
63
- skip "HeadedOx limitation: Namespace-aware XPath with predicates needs investigation. See docs/HEADED_OX_LIMITATIONS.md"
63
+ skip "HeadedOx limitation: Namespace-aware XPath with predicates needs investigation. See docs/_pages/headed-ox-limitations.adoc"
64
64
  end
65
65
  # Test XPath queries
66
66
  #
@@ -80,7 +80,7 @@ RSpec.shared_examples "Moxml Integration" do
80
80
  pending "Ox doesn't have a native XPath"
81
81
  end
82
82
  if context.config.adapter_name == :headed_ox
83
- skip "HeadedOx limitation: Namespace methods not implemented in adapter. Requires Ox namespace API enhancement. See docs/HEADED_OX_LIMITATIONS.md"
83
+ skip "HeadedOx limitation: Namespace methods not implemented in adapter. Requires Ox namespace API enhancement. See docs/_pages/headed-ox-limitations.adoc"
84
84
  end
85
85
  xml = <<~XML
86
86
  <root xmlns="http://default.org" xmlns:a="http://a.org" xmlns:b="http://b.org">
@@ -123,7 +123,7 @@ RSpec.shared_examples "Moxml Integration" do
123
123
 
124
124
  it "handles complex modifications" do
125
125
  if context.config.adapter_name == :headed_ox
126
- skip "HeadedOx limitation: Parent setter not implemented. Requires Ox node reparenting API. See docs/HEADED_OX_LIMITATIONS.md"
126
+ skip "HeadedOx limitation: Parent setter not implemented. Requires Ox node reparenting API. See docs/_pages/headed-ox-limitations.adoc"
127
127
  end
128
128
  if context.config.adapter_name == :ox
129
129
  skip "Ox doesn't have a native XPath"
@@ -41,7 +41,7 @@ RSpec.shared_examples "Moxml::Cdata" do
41
41
  pending "Ox doesn't escape the end token"
42
42
  end
43
43
  if context.config.adapter_name == :headed_ox
44
- skip "HeadedOx limitation: Ox doesn't escape CDATA end markers. See docs/HEADED_OX_LIMITATIONS.md"
44
+ skip "HeadedOx limitation: Ox doesn't escape CDATA end markers. See docs/_pages/headed-ox-limitations.adoc"
45
45
  end
46
46
  cdata.content = "content]]>more"
47
47
  expect(cdata.to_xml).to eq("<![CDATA[content]]]]><![CDATA[>more]]>")