moxml 0.5.146 → 0.5.148

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +35 -0
  3. data/README.adoc +19 -1
  4. data/docs/_pages/adapter-compatibility.adoc +7 -0
  5. data/docs/_pages/divergences.adoc +90 -0
  6. data/docs/_references/index.adoc +9 -0
  7. data/docs/_references/processing-model.adoc +211 -0
  8. data/docs/index.adoc +9 -0
  9. data/lib/moxml/adapter/leptris.rb +100 -3
  10. data/lib/moxml/adapter/libxml.rb +5 -0
  11. data/lib/moxml/adapter/oga.rb +10 -0
  12. data/lib/moxml/document.rb +27 -4
  13. data/lib/moxml/node.rb +12 -0
  14. data/lib/moxml/version.rb +1 -1
  15. data/spec/consistency/divergence_register_spec.rb +109 -0
  16. data/spec/consistency/document_text_spec.rb +30 -0
  17. data/spec/consistency/eol_normalization_spec.rb +8 -8
  18. data/spec/consistency/loud_degradation_spec.rb +44 -0
  19. data/spec/consistency/model_coverage_audit_spec.rb +113 -0
  20. data/spec/consistency/spec_constant_uniqueness_spec.rb +29 -0
  21. data/spec/consistency/xpath_idioms_spec.rb +1 -1
  22. data/spec/moxml/adapter/leptris_contract_reads_spec.rb +29 -0
  23. data/spec/moxml/adapter/leptris_detached_text_spec.rb +41 -0
  24. data/spec/moxml/adapter/leptris_parse_lint_spec.rb +47 -0
  25. data/spec/moxml/adapter/shared_examples/adapter_contract.rb +3 -3
  26. data/spec/moxml/c14n/adapter_delegation_spec.rb +3 -3
  27. data/spec/moxml/c14n/comments_pis_spec.rb +2 -2
  28. data/spec/moxml/c14n/subset_spec.rb +4 -4
  29. data/spec/moxml/c14n/w3c_examples_spec.rb +3 -1
  30. data/spec/moxml/declaration_preservation_spec.rb +2 -2
  31. data/spec/moxml/element_add_element_spec.rb +3 -3
  32. data/spec/moxml/element_attribute_pairs_spec.rb +2 -2
  33. data/spec/moxml/node_spec.rb +1 -1
  34. data/spec/moxml/plan_conformance_spec.rb +3 -3
  35. data/spec/moxml/sax_entity_parity_spec.rb +3 -3
  36. data/spec/moxml/sax_spec.rb +7 -7
  37. data/spec/moxml/struct_plan_spec.rb +2 -2
  38. data/spec/moxml/xpath_capabilities_spec.rb +13 -13
  39. metadata +11 -2
@@ -9,7 +9,7 @@ require "spec_helper"
9
9
  # the contract: a missing base default NoMethodErrors on non-face
10
10
  # adapters, a broken fallback loses attributes or the attach.
11
11
  RSpec.describe Moxml::Element do
12
- ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
12
+ ELEMENT_ADD_ELEMENT_ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
13
13
 
14
14
  def with_adapter(name)
15
15
  ctx = Moxml.new(name)
@@ -19,7 +19,7 @@ RSpec.describe Moxml::Element do
19
19
  end
20
20
 
21
21
  it "attaches a child with all attributes on every available adapter" do
22
- ADAPTERS.each do |name|
22
+ ELEMENT_ADD_ELEMENT_ADAPTERS.each do |name|
23
23
  with_adapter(name) do |ctx|
24
24
  doc = ctx.parse("<catalog/>")
25
25
  root = doc.root
@@ -35,7 +35,7 @@ RSpec.describe Moxml::Element do
35
35
  end
36
36
 
37
37
  it "nests under a non-root element with empty attrs" do
38
- ADAPTERS.each do |name|
38
+ ELEMENT_ADD_ELEMENT_ADAPTERS.each do |name|
39
39
  with_adapter(name) do |ctx|
40
40
  doc = ctx.parse("<catalog><record/></catalog>")
41
41
  record = doc.root.children.first
@@ -12,7 +12,7 @@ RSpec.describe Moxml::Element do
12
12
  '<catalog><record id="r0" kind="k0"><field name="f0" unit="u0">v</field></record></catalog>'
13
13
  end
14
14
 
15
- ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
15
+ ELEMENT_ATTRIBUTE_PAIRS_ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
16
16
 
17
17
  def with_adapter(name)
18
18
  ctx = Moxml.new(name)
@@ -22,7 +22,7 @@ RSpec.describe Moxml::Element do
22
22
  end
23
23
 
24
24
  it "matches the node path on every available adapter" do
25
- results = ADAPTERS.filter_map do |name|
25
+ results = ELEMENT_ATTRIBUTE_PAIRS_ADAPTERS.filter_map do |name|
26
26
  with_adapter(name) do |ctx|
27
27
  root = ctx.parse(xml).root
28
28
  [name, root.attribute_pairs]
@@ -157,7 +157,7 @@ RSpec.describe Moxml::Node do
157
157
 
158
158
  it "locates the node via its path" do
159
159
  # Ox translates only a limited XPath subset (no positional predicates)
160
- skip "ox xpath translation does not support positional predicates" if adapter_name == :ox
160
+ skip "D-12: ox xpath translation does not support positional predicates" if adapter_name == :ox
161
161
 
162
162
  expect(doc.at_xpath(title.path)).to eq(title)
163
163
  end
@@ -27,10 +27,10 @@ RSpec.describe "Moxml::Plan adapter conformance" do
27
27
  end
28
28
  end
29
29
 
30
- ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
30
+ PLAN_CONFORMANCE_ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
31
31
 
32
32
  it "executes on every available adapter" do
33
- available = ADAPTERS.select do |a|
33
+ available = PLAN_CONFORMANCE_ADAPTERS.select do |a|
34
34
  ctx = Moxml.new(a)
35
35
  ctx.parse(xml)
36
36
  true
@@ -63,7 +63,7 @@ RSpec.describe "Moxml::Plan adapter conformance" do
63
63
  on("field") { |attrs, text, _| [attrs["name"], text] }
64
64
  end
65
65
 
66
- ADAPTERS.each do |a|
66
+ PLAN_CONFORMANCE_ADAPTERS.each do |a|
67
67
  begin
68
68
  ctx = Moxml.new(a)
69
69
  ctx.parse(xml)
@@ -32,7 +32,7 @@ module SaxEntityParityFixtures
32
32
  end
33
33
  end
34
34
 
35
- ADAPTERS = %i[nokogiri ox oga rexml libxml headed_ox].freeze
35
+ SAX_ENTITY_PARITY_ADAPTERS = %i[nokogiri ox oga rexml libxml headed_ox].freeze
36
36
 
37
37
  # Each row: [input XML, decoded attribute value, expected attribute
38
38
  # value as it appears in the serialized XML]. The serialized form
@@ -85,7 +85,7 @@ module SaxEntityParityFixtures
85
85
  end
86
86
 
87
87
  RSpec.describe "SAX/DOM entity parity" do
88
- SaxEntityParityFixtures::ADAPTERS.each do |adapter|
88
+ SaxEntityParityFixtures::SAX_ENTITY_PARITY_ADAPTERS.each do |adapter|
89
89
  context "with #{adapter} adapter" do
90
90
  let(:ctx) { Moxml.new(adapter) }
91
91
 
@@ -349,7 +349,7 @@ RSpec.describe "SAX/DOM entity parity" do
349
349
 
350
350
  SaxEntityParityFixtures::ATTRIBUTE_CASES.each do |label, (xml, _expected, expected_serialized)|
351
351
  it "every adapter emits the same attribute form: #{label}" do
352
- results = SaxEntityParityFixtures::ADAPTERS.to_h { |a| [a, serialize_via(a, xml)] }
352
+ results = SaxEntityParityFixtures::SAX_ENTITY_PARITY_ADAPTERS.to_h { |a| [a, serialize_via(a, xml)] }
353
353
  results.each do |a, out|
354
354
  expect(out).to(
355
355
  include(%(x="#{expected_serialized}")),
@@ -413,7 +413,7 @@ RSpec.describe Moxml::SAX do
413
413
  it "documents CDATA limitation" do
414
414
  # Ox SAX does not have separate CDATA events
415
415
  # All CDATA content is delivered as text() events
416
- skip "Ox SAX does not support separate CDATA events"
416
+ skip "D-10: Ox SAX does not support separate CDATA events"
417
417
 
418
418
  xml_with_cdata = "<root><![CDATA[special content]]></root>"
419
419
 
@@ -428,7 +428,7 @@ RSpec.describe Moxml::SAX do
428
428
 
429
429
  it "documents comment limitation" do
430
430
  # Ox SAX does not have separate comment events
431
- skip "Ox SAX does not support separate comment events"
431
+ skip "D-10: Ox SAX does not support separate comment events"
432
432
 
433
433
  xml_with_comment = "<root><!-- comment --><data>content</data></root>"
434
434
 
@@ -443,7 +443,7 @@ RSpec.describe Moxml::SAX do
443
443
 
444
444
  it "documents processing instruction limitation" do
445
445
  # Ox SAX does not have separate PI events
446
- skip "Ox SAX does not support separate processing instruction events"
446
+ skip "D-10: Ox SAX does not support separate processing instruction events"
447
447
 
448
448
  xml_with_pi = '<?xml-stylesheet type="text/xsl" href="style.xsl"?><root/>'
449
449
 
@@ -714,7 +714,7 @@ RSpec.describe Moxml::SAX do
714
714
  end
715
715
 
716
716
  it "handles errors gracefully" do
717
- skip "Oga SAX parser may be more lenient with malformed XML"
717
+ skip "D-11: Oga SAX parser may be more lenient with malformed XML"
718
718
 
719
719
  invalid_xml = "<book><title>Unclosed"
720
720
 
@@ -1024,7 +1024,7 @@ RSpec.describe Moxml::SAX do
1024
1024
 
1025
1025
  it "documents CDATA limitation (inherited from Ox)" do
1026
1026
  # HeadedOx inherits Ox's SAX implementation, which does not have separate CDATA events
1027
- skip "HeadedOx SAX (inherited from Ox) does not support separate CDATA events"
1027
+ skip "D-10: HeadedOx SAX (inherited from Ox) does not support separate CDATA events"
1028
1028
 
1029
1029
  xml_with_cdata = "<root><![CDATA[special content]]></root>"
1030
1030
 
@@ -1038,7 +1038,7 @@ RSpec.describe Moxml::SAX do
1038
1038
 
1039
1039
  it "documents comment limitation (inherited from Ox)" do
1040
1040
  # HeadedOx inherits Ox's SAX implementation, which does not have separate comment events
1041
- skip "HeadedOx SAX (inherited from Ox) does not support separate comment events"
1041
+ skip "D-10: HeadedOx SAX (inherited from Ox) does not support separate comment events"
1042
1042
 
1043
1043
  xml_with_comment = "<root><!-- comment --><data>content</data></root>"
1044
1044
 
@@ -1052,7 +1052,7 @@ RSpec.describe Moxml::SAX do
1052
1052
 
1053
1053
  it "documents processing instruction limitation (inherited from Ox)" do
1054
1054
  # HeadedOx inherits Ox's SAX implementation, which does not have separate PI events
1055
- skip "HeadedOx SAX (inherited from Ox) does not support separate processing instruction events"
1055
+ skip "D-10: HeadedOx SAX (inherited from Ox) does not support separate processing instruction events"
1056
1056
 
1057
1057
  xml_with_pi = '<?xml-stylesheet type="text/xsl" href="style.xsl"?><root/>'
1058
1058
 
@@ -28,7 +28,7 @@ RSpec.describe Moxml::StructPlan do
28
28
  end
29
29
  end
30
30
 
31
- ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
31
+ STRUCT_PLAN_ADAPTERS = %i[leptris nokogiri ox oga rexml libxml].freeze # rubocop:disable Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
32
32
 
33
33
  def with_adapter(name)
34
34
  yield Moxml.new(name)
@@ -36,7 +36,7 @@ RSpec.describe Moxml::StructPlan do
36
36
  skip "#{name} unavailable"
37
37
  end
38
38
 
39
- ADAPTERS.each do |adapter_name|
39
+ STRUCT_PLAN_ADAPTERS.each do |adapter_name|
40
40
  describe "on #{adapter_name}" do
41
41
  it "materializes the same structs as Moxml::Plan" do
42
42
  with_adapter(adapter_name) do |ctx|
@@ -83,7 +83,7 @@ RSpec.describe "XPath Capabilities" do
83
83
  programming = doc.xpath("//book[@category='programming']")
84
84
  expect(programming.length).to be >= 0 # May not work on all
85
85
  rescue Moxml::XPathError
86
- skip "Attribute value predicates not supported on #{adapter_name}"
86
+ skip "D-12: Attribute value predicates not supported on #{adapter_name}"
87
87
  end
88
88
  end
89
89
 
@@ -98,7 +98,7 @@ RSpec.describe "XPath Capabilities" do
98
98
  items = doc.xpath("//book:item", namespaces)
99
99
  expect(items.length).to be >= 0
100
100
  rescue Moxml::XPathError, NoMethodError
101
- skip "Namespace XPath not supported on #{adapter_name}"
101
+ skip "D-12: Namespace XPath not supported on #{adapter_name}"
102
102
  end
103
103
 
104
104
  it "handles default namespaces" do
@@ -108,7 +108,7 @@ RSpec.describe "XPath Capabilities" do
108
108
  library = doc.xpath("//lib:library", namespaces)
109
109
  expect(library.length).to be >= 0
110
110
  rescue Moxml::XPathError, NoMethodError
111
- skip "Default namespace XPath not supported on #{adapter_name}"
111
+ skip "D-12: Default namespace XPath not supported on #{adapter_name}"
112
112
  end
113
113
  end
114
114
 
@@ -118,7 +118,7 @@ RSpec.describe "XPath Capabilities" do
118
118
  first_book = doc.xpath("//book[1]")
119
119
  expect(first_book.length).to be >= 0
120
120
  rescue Moxml::XPathError
121
- skip "Position predicates not supported on #{adapter_name}"
121
+ skip "D-12: Position predicates not supported on #{adapter_name}"
122
122
  end
123
123
 
124
124
  it "supports position() function" do
@@ -126,7 +126,7 @@ RSpec.describe "XPath Capabilities" do
126
126
  first_books = doc.xpath("//book[position()=1]")
127
127
  expect(first_books.length).to be >= 0
128
128
  rescue Moxml::XPathError
129
- skip "position() function not supported on #{adapter_name}"
129
+ skip "D-12: position() function not supported on #{adapter_name}"
130
130
  end
131
131
  end
132
132
 
@@ -135,11 +135,11 @@ RSpec.describe "XPath Capabilities" do
135
135
  doc = Moxml.new.parse(simple_xml)
136
136
  result = doc.xpath("count(//book)")
137
137
  # REXML's XPath has no function support and yields an empty set
138
- skip "count() function not supported on #{adapter_name}" if result.is_a?(Moxml::NodeSet)
138
+ skip "D-12: count() function not supported on #{adapter_name}" if result.is_a?(Moxml::NodeSet)
139
139
 
140
140
  expect(result).to eq(2)
141
141
  rescue Moxml::XPathError, NoMethodError
142
- skip "count() function not supported on #{adapter_name}"
142
+ skip "D-12: count() function not supported on #{adapter_name}"
143
143
  end
144
144
 
145
145
  it "supports text() node selection" do
@@ -147,7 +147,7 @@ RSpec.describe "XPath Capabilities" do
147
147
  texts = doc.xpath("//title/text()")
148
148
  expect(texts.length).to be >= 0
149
149
  rescue Moxml::XPathError
150
- skip "text() not supported on #{adapter_name}"
150
+ skip "D-12: text() not supported on #{adapter_name}"
151
151
  end
152
152
  end
153
153
 
@@ -157,7 +157,7 @@ RSpec.describe "XPath Capabilities" do
157
157
  cheap_books = doc.xpath("//book[@id < 2]")
158
158
  expect(cheap_books.length).to be >= 0
159
159
  rescue Moxml::XPathError
160
- skip "Numeric predicates not supported on #{adapter_name}"
160
+ skip "D-12: Numeric predicates not supported on #{adapter_name}"
161
161
  end
162
162
 
163
163
  it "supports boolean expressions ([cond1 and cond2])" do
@@ -165,7 +165,7 @@ RSpec.describe "XPath Capabilities" do
165
165
  result = doc.xpath("//book[@id and @category]")
166
166
  expect(result.length).to be >= 0
167
167
  rescue Moxml::XPathError
168
- skip "Boolean expressions not supported on #{adapter_name}"
168
+ skip "D-12: Boolean expressions not supported on #{adapter_name}"
169
169
  end
170
170
  end
171
171
 
@@ -175,7 +175,7 @@ RSpec.describe "XPath Capabilities" do
175
175
  children = doc.xpath("/library/child::book")
176
176
  expect(children.length).to be >= 0
177
177
  rescue Moxml::XPathError
178
- skip "child:: axis not supported on #{adapter_name}"
178
+ skip "D-12: child:: axis not supported on #{adapter_name}"
179
179
  end
180
180
 
181
181
  it "supports descendant axis" do
@@ -183,7 +183,7 @@ RSpec.describe "XPath Capabilities" do
183
183
  descendants = doc.xpath("//library/descendant::title")
184
184
  expect(descendants.length).to be >= 0
185
185
  rescue Moxml::XPathError
186
- skip "descendant:: axis not supported on #{adapter_name}"
186
+ skip "D-12: descendant:: axis not supported on #{adapter_name}"
187
187
  end
188
188
  end
189
189
 
@@ -193,7 +193,7 @@ RSpec.describe "XPath Capabilities" do
193
193
  items = doc.xpath("//book | //magazine")
194
194
  expect(items.length).to be >= 0
195
195
  rescue Moxml::XPathError
196
- skip "Union operator not supported on #{adapter_name}"
196
+ skip "D-12: Union operator not supported on #{adapter_name}"
197
197
  end
198
198
  end
199
199
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.146
4
+ version: 0.5.148
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-19 00:00:00.000000000 Z
11
+ date: 2026-10-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Moxml is a unified XML manipulation library that provides a common API
@@ -73,6 +73,7 @@ files:
73
73
  - docs/_pages/compatibility.adoc
74
74
  - docs/_pages/configuration.adoc
75
75
  - docs/_pages/conversion-apis.adoc
76
+ - docs/_pages/divergences.adoc
76
77
  - docs/_pages/error-handling.adoc
77
78
  - docs/_pages/headed-ox-limitations.adoc
78
79
  - docs/_pages/headed-ox.adoc
@@ -84,6 +85,7 @@ files:
84
85
  - docs/_pages/thread-safety.adoc
85
86
  - docs/_references/document-api.adoc
86
87
  - docs/_references/index.adoc
88
+ - docs/_references/processing-model.adoc
87
89
  - docs/_tutorials/basic-usage.adoc
88
90
  - docs/_tutorials/builder-pattern.adoc
89
91
  - docs/_tutorials/index.adoc
@@ -310,9 +312,14 @@ files:
310
312
  - sig/moxml.rbs
311
313
  - spec/consistency/README.md
312
314
  - spec/consistency/adapter_parity_spec.rb
315
+ - spec/consistency/divergence_register_spec.rb
316
+ - spec/consistency/document_text_spec.rb
313
317
  - spec/consistency/eol_normalization_spec.rb
314
318
  - spec/consistency/html_doctype_spec.rb
319
+ - spec/consistency/loud_degradation_spec.rb
320
+ - spec/consistency/model_coverage_audit_spec.rb
315
321
  - spec/consistency/round_trip_spec.rb
322
+ - spec/consistency/spec_constant_uniqueness_spec.rb
316
323
  - spec/consistency/xpath_idioms_spec.rb
317
324
  - spec/consistency/xslt_spec.rb
318
325
  - spec/examples/README.md
@@ -453,9 +460,11 @@ files:
453
460
  - spec/moxml/adapter/leptris_attribute_snapshot_spec.rb
454
461
  - spec/moxml/adapter/leptris_binding_cap_spec.rb
455
462
  - spec/moxml/adapter/leptris_contract_reads_spec.rb
463
+ - spec/moxml/adapter/leptris_detached_text_spec.rb
456
464
  - spec/moxml/adapter/leptris_doc_parts_spec.rb
457
465
  - spec/moxml/adapter/leptris_document_declaration_spec.rb
458
466
  - spec/moxml/adapter/leptris_inbody_comment_spec.rb
467
+ - spec/moxml/adapter/leptris_parse_lint_spec.rb
459
468
  - spec/moxml/adapter/leptris_parse_skips_spec.rb
460
469
  - spec/moxml/adapter/leptris_record_driver_spec.rb
461
470
  - spec/moxml/adapter/leptris_sax_dup_attr_spec.rb