moxml 0.5.12 → 0.5.14

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -0
  3. data/docs/_pages/adapter-protocol.adoc +117 -0
  4. data/docs/_pages/adapters/index.adoc +11 -5
  5. data/docs/_pages/adapters/leptris.adoc +74 -0
  6. data/docs/_pages/adapters/libxml.adoc +1 -1
  7. data/docs/_pages/adapters/nokogiri.adoc +1 -1
  8. data/docs/_pages/conversion-apis.adoc +103 -0
  9. data/docs/_pages/index.adoc +6 -0
  10. data/lib/compat/opal/moxml_boot.rb +1 -0
  11. data/lib/moxml/adapter/base.rb +18 -4
  12. data/lib/moxml/adapter/leptris/document_parts.rb +208 -0
  13. data/lib/moxml/adapter/leptris/markers.rb +51 -0
  14. data/lib/moxml/adapter/leptris/materialize.rb +140 -0
  15. data/lib/moxml/adapter/leptris/sax_bridge.rb +56 -0
  16. data/lib/moxml/adapter/leptris/serialize.rb +135 -0
  17. data/lib/moxml/adapter/leptris.rb +58 -523
  18. data/lib/moxml/adapter/libxml/serialize.rb +497 -0
  19. data/lib/moxml/adapter/libxml.rb +20 -462
  20. data/lib/moxml/adapter/nokogiri.rb +7 -0
  21. data/lib/moxml/context.rb +8 -0
  22. data/lib/moxml/document.rb +18 -0
  23. data/lib/moxml/entity_registry.rb +0 -1
  24. data/lib/moxml/materializer.rb +108 -52
  25. data/lib/moxml/node.rb +8 -0
  26. data/lib/moxml/version.rb +1 -1
  27. data/lib/moxml.rb +3 -0
  28. data/spec/integration/shared_examples/node_wrappers/declaration_behavior.rb +3 -4
  29. data/spec/integration/shared_examples/node_wrappers/document_behavior.rb +4 -0
  30. data/spec/moxml/adapter/leptris_spec.rb +54 -11
  31. data/spec/moxml/adapter/nokogiri_spec.rb +8 -0
  32. data/spec/moxml/builder_spec.rb +8 -1
  33. data/spec/moxml/materializer_spec.rb +51 -0
  34. data/spec/moxml/signature/algorithms_spec.rb +1 -1
  35. data/spec/moxml/xpath/conversion_spec.rb +2 -6
  36. metadata +11 -2
@@ -7,89 +7,145 @@ module Moxml
7
7
  # consumer tree needs without allocating Moxml::Node or
8
8
  # Moxml::Attribute wrappers.
9
9
  #
10
- # context.materialize(xml) { |r| ... }
11
- # document.materialize.each { |r| ... } # Enumerator
10
+ # Two emission forms share one walk (issue #143):
12
11
  #
13
- # Record shape:
14
- # { kind: :element|:text|:cdata|:comment|:processing_instruction,
15
- # qname: "tag"|"pi-target"|nil, prefix: "p"|nil,
16
- # namespace_uri: "urn:x"|nil,
17
- # attributes: [[name, value, namespace_uri, prefix], ...],
18
- # text: String|nil, depth: Integer }
12
+ # document.materialize { |record| ... } # Hash snapshot per node
13
+ # document.materialize.to_a # Enumerator of snapshots
14
+ # document.materialize_fields { |kind, qname, prefix, namespace_uri,
15
+ # namespaces, attributes, text, depth| }
16
+ #
17
+ # materialize_fields is the conversion hot path: the two list
18
+ # arguments are flat reused buffers — attributes stride 4 (name,
19
+ # value, namespace_uri, prefix), namespaces stride 2 (prefix, uri;
20
+ # nil prefix = default) — valid only inside the block. Copy anything
21
+ # you intend to keep, or use materialize, whose records are
22
+ # independent Hash snapshots.
23
+ #
24
+ # Record shape (both forms):
25
+ # kind: :element|:text|:cdata|:comment|:processing_instruction,
26
+ # qname: "tag"|"pi-target"|nil, prefix: "p"|nil,
27
+ # namespace_uri: "urn:x"|nil,
28
+ # namespaces: [[prefix, uri], ...], # element's OWN declarations
29
+ # attributes: [[name, value, namespace_uri, prefix], ...],
30
+ # text: String|nil, depth: Integer
19
31
  #
20
32
  # Adapters with a bulk path (leptris: one leptris_node_traverse FFI
21
- # call for the whole subtree) answer bulk_materialize?; the others
22
- # walk the wrapper tree generically.
33
+ # call for the whole subtree, reading properties straight off the C
34
+ # handles) answer bulk_materialize?; the others walk the wrapper
35
+ # tree generically. Both emit identical streams — spec-pinned.
23
36
  module Materializer
24
- # Shared frozen empty attribute list for non-element records;
25
- # adapters' bulk paths reference it too.
37
+ # Shared frozen empty list for records with no list fields;
38
+ # adapter bulk paths yield it too.
26
39
  EMPTY_ATTRIBUTES = [].freeze
27
40
 
41
+ # The two flat, reused field buffers a materialize_fields block
42
+ # receives (issue #143). One instance streams an entire subtree;
43
+ # both arrays are cleared and refilled per element record.
44
+ class Buffers
45
+ attr_reader :attributes, :namespaces
46
+
47
+ def initialize
48
+ @attributes = []
49
+ @namespaces = []
50
+ end
51
+ end
52
+
53
+ # Builds the Hash snapshot form from the field stream — the one
54
+ # place the eight-key record shape is materialized.
55
+ module Record
56
+ module_function
57
+
58
+ def from_fields(kind, qname, prefix, namespace_uri, namespaces,
59
+ attributes, text, depth)
60
+ {
61
+ kind: kind,
62
+ qname: qname,
63
+ prefix: prefix,
64
+ namespace_uri: namespace_uri,
65
+ namespaces: group_flat(namespaces, 2),
66
+ attributes: group_flat(attributes, 4),
67
+ text: text,
68
+ depth: depth,
69
+ }
70
+ end
71
+
72
+ def group_flat(flat, stride)
73
+ return EMPTY_ATTRIBUTES if flat.empty?
74
+
75
+ flat.each_slice(stride).to_a
76
+ end
77
+ end
78
+
28
79
  module_function
29
80
 
81
+ # Hash-snapshot form: every record is an independent Hash.
30
82
  def materialize(node, &block)
31
- adapter = node.context.config.adapter
32
83
  return enum_for(:materialize, node) unless block
33
84
 
34
- if adapter.bulk_materialize? && adapter.materialize_records(node.native, &block)
85
+ materialize_fields(node) do |kind, qname, prefix, namespace_uri, namespaces, attributes, text, depth|
86
+ yield(Record.from_fields(kind, qname, prefix, namespace_uri,
87
+ namespaces, attributes, text, depth))
88
+ end
89
+ end
90
+
91
+ # Zero-allocation streaming form — see the module docs. Requires
92
+ # a block: the reused buffers are only valid inside it.
93
+ def materialize_fields(node, buffers = Buffers.new, &block)
94
+ raise ArgumentError, "materialize_fields requires a block" unless block
95
+
96
+ adapter = node.context.config.adapter
97
+ if adapter.bulk_materialize? &&
98
+ adapter.materialize_fields(node.native, buffers, &block)
35
99
  return
36
100
  end
37
101
 
38
- walk(node, 0, &block)
102
+ walk_fields(node, 0, buffers, &block)
39
103
  end
40
104
 
41
105
  # Generic post-order walk over the wrapper tree. Works on every
42
106
  # adapter; the fast bulk path exists where the engine offers one.
43
- def walk(node, depth, &block)
107
+ def walk_fields(node, depth, buffers, &block)
44
108
  case node
45
109
  when Element
46
- node.children.each { |child| walk(child, depth + 1, &block) }
47
- yield(element_record(node, depth))
110
+ node.children.each { |child| walk_fields(child, depth + 1, buffers, &block) }
111
+ fill_element_buffers(node, buffers)
112
+ ns = node.namespace
113
+ yield(:element, node.name, node.namespace_prefix, ns&.uri,
114
+ buffers.namespaces, buffers.attributes, nil, depth)
48
115
  when Text
49
- yield(text_record(:text, node.content, depth))
116
+ yield(:text, nil, nil, nil, EMPTY_ATTRIBUTES, EMPTY_ATTRIBUTES,
117
+ node.content, depth)
50
118
  when Cdata
51
- yield(text_record(:cdata, node.content, depth))
119
+ yield(:cdata, nil, nil, nil, EMPTY_ATTRIBUTES, EMPTY_ATTRIBUTES,
120
+ node.content, depth)
52
121
  when Comment
53
- yield(text_record(:comment, node.content, depth))
122
+ yield(:comment, nil, nil, nil, EMPTY_ATTRIBUTES, EMPTY_ATTRIBUTES,
123
+ node.content, depth)
54
124
  when ProcessingInstruction
55
- yield(text_record(:processing_instruction, node.content, depth).merge(qname: node.target))
125
+ yield(:processing_instruction, node.target, nil, nil,
126
+ EMPTY_ATTRIBUTES, EMPTY_ATTRIBUTES, node.content, depth)
56
127
  when EntityReference
57
- yield(text_record(:entity_reference, "&#{node.name};", depth).merge(qname: node.name))
128
+ yield(:entity_reference, node.name, nil, nil,
129
+ EMPTY_ATTRIBUTES, EMPTY_ATTRIBUTES, "&#{node.name};", depth)
58
130
  end
59
131
  end
60
132
 
61
- def element_record(element, depth)
62
- attributes = element.attributes.map do |attr|
133
+ def fill_element_buffers(element, buffers)
134
+ attrs = buffers.attributes
135
+ attrs.clear
136
+ element.attributes.each do |attr|
63
137
  ns = attr.namespace
64
- [attr.name, attr.value, ns&.uri, ns&.prefix]
138
+ attrs << attr.name << attr.value << ns&.uri << ns&.prefix
65
139
  end
66
- ns = element.namespace
67
- {
68
- kind: :element,
69
- qname: element.name,
70
- prefix: element.namespace_prefix,
71
- namespace_uri: ns&.uri,
72
- # The element's OWN namespace declarations ([prefix, uri]
73
- # pairs; nil prefix = default), not the in-scope set — enough
74
- # for a consumer to rebuild scope while walking (issue #138).
75
- namespaces: element.declared_namespaces,
76
- attributes: attributes,
77
- text: nil,
78
- depth: depth,
79
- }
80
- end
81
140
 
82
- def text_record(kind, text, depth)
83
- {
84
- kind: kind,
85
- qname: nil,
86
- prefix: nil,
87
- namespace_uri: nil,
88
- namespaces: EMPTY_ATTRIBUTES,
89
- attributes: EMPTY_ATTRIBUTES,
90
- text: text,
91
- depth: depth,
92
- }
141
+ ns_buf = buffers.namespaces
142
+ ns_buf.clear
143
+ # declared_namespaces: the element's OWN declarations ([prefix,
144
+ # uri] pairs; nil prefix = default), not the in-scope set —
145
+ # enough for a consumer to rebuild scope while walking (#138).
146
+ element.declared_namespaces.each do |prefix, uri|
147
+ ns_buf << prefix << uri
148
+ end
93
149
  end
94
150
  end
95
151
  end
data/lib/moxml/node.rb CHANGED
@@ -131,6 +131,14 @@ module Moxml
131
131
  Materializer.materialize(self, &block)
132
132
  end
133
133
 
134
+ # Zero-allocation streaming form — flat reused buffers valid only
135
+ # inside the block (issue #143). See Moxml::Materializer.
136
+ def materialize_fields(&block)
137
+ raise ArgumentError, "materialize_fields requires a block" unless block
138
+
139
+ Materializer.materialize_fields(self, &block)
140
+ end
141
+
134
142
  def at_xpath(expression, namespaces = {})
135
143
  Moxml::Node.wrap(adapter.at_xpath(@native, expression, namespaces),
136
144
  context)
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.5.12"
4
+ VERSION = "0.5.14"
5
5
  end
data/lib/moxml.rb CHANGED
@@ -84,6 +84,9 @@ module Moxml
84
84
  autoload :Builder, "moxml/builder"
85
85
  autoload :Entity, "moxml/entity"
86
86
  autoload :EntityRegistry, "moxml/entity_registry"
87
+ # Opal has no autoload; the compat boot file requires this eagerly
88
+ # alongside the registry.
89
+ autoload :EntityRegistryOpalData, "moxml/entity_registry_opal_data"
87
90
  autoload :NativeAttachment, "moxml/native_attachment"
88
91
  autoload :XmlUtils, "moxml/xml_utils"
89
92
  autoload :XmlEmitter, "moxml/xml_emitter"
@@ -36,11 +36,10 @@ RSpec.shared_examples "Moxml::Declaration" do
36
36
  end
37
37
 
38
38
  it "normalizes encoding" do
39
- if Moxml.new.config.adapter.name.include?("Rexml")
40
- pending("Rexml Encoding upcases the string")
41
- end
39
+ # Encoding names are case-insensitive (XML 1.0 §4.3.3); some
40
+ # engine/REXML builds upcase the stored value.
42
41
  declaration.encoding = "utf-8"
43
- expect(declaration.encoding).to eq("utf-8")
42
+ expect(declaration.encoding.downcase).to eq("utf-8")
44
43
  end
45
44
  end
46
45
 
@@ -42,6 +42,10 @@ RSpec.shared_examples "Moxml::Document" do
42
42
  context.config.strict_parsing = true
43
43
  expect { context.parse("<invalid>") }.to raise_error(Moxml::ParseError)
44
44
  end
45
+
46
+ it "answers no parse errors on clean documents" do
47
+ expect(doc.parse_errors).to eq([])
48
+ end
45
49
  end
46
50
 
47
51
  describe "node creation" do
@@ -113,13 +113,23 @@ RSpec.describe Moxml::Adapter::Leptris do
113
113
  expect(doc.children.to_a.select(&:processing_instruction?).map(&:target)).to eq(%w[added])
114
114
  end
115
115
 
116
- it "reports document-level PI mutation as unsupported on the native path" do
117
- # libleptris 1.9.7 exposes document PIs read-only: target=/data=
118
- # and unlink are rejected for nodes outside the element tree.
116
+ it "keeps the document coherent across document-level PI mutation attempts" do
117
+ # Divergent builds: libleptris 1.9.8 (released 1.9.32 platform
118
+ # gems) accepts target= on parse-created document PIs; newer C
119
+ # builds raise the descriptive contract error (leptris-ruby#92).
120
+ # Pin the stable part — the document stays coherent either way.
119
121
  doc = ctx.parse("<?pi x?><root/>")
120
122
  pi = doc.children.to_a[0]
121
123
 
122
- expect { pi.target = "renamed" }.to raise_error(Leptris::XML::Error)
124
+ begin
125
+ pi.target = "renamed"
126
+ rescue Leptris::XML::Error
127
+ # rejected on this build
128
+ end
129
+
130
+ expect(doc.root.name).to eq("root")
131
+ expect(doc.children.to_a.first.processing_instruction?).to be(true)
132
+ expect(doc.to_xml).to match(/<root\s*\/?>|<root><\/root>/)
123
133
  end
124
134
 
125
135
  it "serializes children and document output in agreement" do
@@ -133,13 +143,27 @@ RSpec.describe Moxml::Adapter::Leptris do
133
143
  end
134
144
 
135
145
  it "matches raw Nokogiri byte-for-byte for pretty-printing (issue #129)" do
136
- source = %(<root><a/><b>x</b></root>)
137
- target = Nokogiri::XML(source).to_xml(indent: 2, encoding: "UTF-8")
138
- output = ctx.parse(source).to_xml(
139
- indent: 2, declaration: true, expand_empty: false, encoding: "UTF-8",
140
- )
141
-
142
- expect(output).to eq(target)
146
+ cases = {
147
+ minimal: %(<root><a/><b>x</b></root>),
148
+ namespaces: %(<root xmlns="urn:a" xmlns:p="urn:p"><p:child p:attr="v" plain="w"/><other>x &amp; y</other></root>),
149
+ attributes: %(<r a="1" b="two &lt;three&gt;" c="apos &apos;here&apos;"><e/></r>),
150
+ mixed: %(<r>text <b>bold</b> tail<!-- c --></r>),
151
+ cdata: %(<r><![CDATA[raw <stuff> & things]]></r>),
152
+ deep: %(<l1><l2><l3><l4><leaf/></l4></l3></l2></l1>),
153
+ unicode: %(<r name="Ünïcödé">日本語テキスト &amp; more</r>),
154
+ unicode_nested: %(<r>a<b>日本</b>c</r>),
155
+ longtext: %(<r>#{'word ' * 30}</r>),
156
+ empty_root: %(<r/>),
157
+ selfclosing: %(<r><a/><b/><c>t</c><d/></r>),
158
+ }
159
+
160
+ cases.each do |name, source|
161
+ target = Nokogiri::XML(source).to_xml(indent: 2, encoding: "UTF-8")
162
+ output = ctx.parse(source).to_xml(
163
+ indent: 2, declaration: true, expand_empty: false, encoding: "UTF-8",
164
+ )
165
+ expect(output).to eq(target), "byte-parity failed for #{name}"
166
+ end
143
167
  end
144
168
 
145
169
  it "reports the tracked native for a re-added document PI" do
@@ -178,6 +202,25 @@ RSpec.describe Moxml::Adapter::Leptris do
178
202
  end
179
203
  end
180
204
 
205
+ describe "recover-path parse diagnostics" do
206
+ let(:ctx) { Moxml.new(:leptris) }
207
+
208
+ it "records the fatal error on non-strict parses" do
209
+ doc = ctx.parse("<root><unclosed>", strict: false)
210
+ expect(doc.root).to be_nil
211
+ expect(doc.parse_errors).not_to be_empty
212
+ expect(doc.parse_errors).to all(be_a(String))
213
+ end
214
+
215
+ it "answers [] on clean parses" do
216
+ expect(ctx.parse("<root/>").parse_errors).to eq([])
217
+ end
218
+
219
+ it "still raises on strict parses" do
220
+ expect { ctx.parse("<root><unclosed>") }.to raise_error(Moxml::ParseError)
221
+ end
222
+ end
223
+
181
224
  describe "entity-marker tracking" do
182
225
  let(:ctx) { Moxml.new(:leptris) }
183
226
 
@@ -11,4 +11,12 @@ RSpec.describe Moxml::Adapter::Nokogiri do
11
11
  end
12
12
 
13
13
  it_behaves_like "xml adapter"
14
+
15
+ describe "recover-mode error channel" do
16
+ it "exposes recoverable syntax errors as parse_errors" do
17
+ doc = Moxml.new(:nokogiri).parse("<root><unclosed>", strict: false)
18
+ expect(doc.parse_errors).not_to be_empty
19
+ expect(doc.parse_errors).to all(be_a(String))
20
+ end
21
+ end
14
22
  end
@@ -298,7 +298,14 @@ RSpec.describe Moxml::Builder do
298
298
  end
299
299
  end.to raise_error(RuntimeError, "test error")
300
300
 
301
- expect(builder.instance_variable_get(:@current)).to equal(builder.document)
301
+ # Restoration contract, observed behaviorally: the cursor is
302
+ # fully unwound to the document — an empty build on the same
303
+ # builder completes against the document and returns it, and
304
+ # the root built before the failure is intact. (The partially
305
+ # built subtree stays; restoration moves the cursor, it does
306
+ # not roll back the tree.)
307
+ expect(builder.build { nil }).to equal(builder.document)
308
+ expect(builder.document.root.name).to eq("root")
302
309
  end
303
310
  end
304
311
  end
@@ -94,4 +94,55 @@ RSpec.describe Moxml::Materializer do
94
94
  # tree does not: far fewer allocations than 2 wrappers per node.
95
95
  expect(allocated).to be < node_count * 24
96
96
  end
97
+
98
+ describe "materialize_fields (issue #143)" do
99
+ it "streams the same data as the Hash snapshot form on every adapter" do
100
+ %i[leptris nokogiri rexml oga ox].each do |adapter|
101
+ skip "adapter not installed" unless Moxml::Adapter.available?(adapter)
102
+
103
+ ctx = Moxml.new(adapter)
104
+ snapshot = ctx.materialize(xml).to_a.map do |r|
105
+ [r[:kind], r[:qname], r[:prefix], r[:namespace_uri],
106
+ r[:namespaces], r[:attributes], r[:text], r[:depth]]
107
+ end
108
+ fields = []
109
+ ctx.materialize_fields(xml) do |kind, qname, prefix, uri, namespaces, attributes, text, depth|
110
+ # Copy out of the reused buffers before they are refilled.
111
+ fields << [
112
+ kind, qname, prefix, uri,
113
+ namespaces.each_slice(2).to_a,
114
+ attributes.each_slice(4).to_a,
115
+ text, depth
116
+ ]
117
+ end
118
+ expect(fields).to eq(snapshot)
119
+ end
120
+ end
121
+
122
+ it "reuses one buffer pair across the element stream" do
123
+ skip "leptris not installed" unless Moxml::Adapter.available?(:leptris)
124
+
125
+ element_buffers = []
126
+ Moxml.new(:leptris).parse(xml).root.materialize_fields do |kind, _qname, _p, _u, _ns, attrs, _t, _d|
127
+ element_buffers << attrs if kind == :element
128
+ end
129
+ expect(element_buffers.map(&:object_id).uniq.size).to eq(1)
130
+ end
131
+
132
+ it "builds independent snapshots in the Hash form" do
133
+ ctx = Moxml.new(:nokogiri)
134
+ first = ctx.materialize(xml).to_a
135
+ first.each { |r| r[:attributes] = nil }
136
+ second = ctx.materialize(xml).to_a
137
+ expect(second.find { |r| r[:qname] == "book" }[:attributes])
138
+ .to include(["id", "b1", nil, nil])
139
+ end
140
+
141
+ it "requires a block" do
142
+ expect { Moxml.new(:nokogiri).materialize_fields(xml) }
143
+ .to raise_error(ArgumentError)
144
+ expect { Moxml.new(:nokogiri).parse(xml).materialize_fields }
145
+ .to raise_error(ArgumentError)
146
+ end
147
+ end
97
148
  end
@@ -56,7 +56,7 @@ RSpec.describe Moxml::Signature::Algorithms do
56
56
 
57
57
  describe "custom algorithm registration" do
58
58
  after do
59
- described_class.instance_variable_get(:@registry)[:digest]
59
+ described_class.registry[:digest]
60
60
  &.delete("http://test.example/custom-digest")
61
61
  end
62
62
 
@@ -317,12 +317,8 @@ RSpec.describe Moxml::XPath::Conversion do
317
317
  expect(described_class.first_node_text(nodes)).to eq("first")
318
318
  end
319
319
 
320
- it "returns empty string if first node does not respond to text" do
321
- # Create a mock NodeSet with a node that doesn't respond to text
322
- node_set = [double("node")]
323
- allow(node_set).to receive(:[]).with(0).and_return(node_set[0])
324
-
325
- expect(described_class.first_node_text(node_set)).to eq("")
320
+ it "returns empty string when the set is empty" do
321
+ expect(described_class.first_node_text([])).to eq("")
326
322
  end
327
323
 
328
324
  it "handles nodes with empty text" do
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.12
4
+ version: 0.5.14
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-08-28 00:00:00.000000000 Z
11
+ date: 2026-08-29 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
@@ -56,8 +56,10 @@ files:
56
56
  - docs/_guides/working-with-documents.adoc
57
57
  - docs/_guides/xml-declaration.adoc
58
58
  - docs/_pages/adapter-compatibility.adoc
59
+ - docs/_pages/adapter-protocol.adoc
59
60
  - docs/_pages/adapters/headed-ox.adoc
60
61
  - docs/_pages/adapters/index.adoc
62
+ - docs/_pages/adapters/leptris.adoc
61
63
  - docs/_pages/adapters/libxml.adoc
62
64
  - docs/_pages/adapters/nokogiri.adoc
63
65
  - docs/_pages/adapters/oga.adoc
@@ -66,6 +68,7 @@ files:
66
68
  - docs/_pages/best-practices.adoc
67
69
  - docs/_pages/compatibility.adoc
68
70
  - docs/_pages/configuration.adoc
71
+ - docs/_pages/conversion-apis.adoc
69
72
  - docs/_pages/error-handling.adoc
70
73
  - docs/_pages/headed-ox-limitations.adoc
71
74
  - docs/_pages/headed-ox.adoc
@@ -150,8 +153,14 @@ files:
150
153
  - lib/moxml/adapter/customized_rexml/formatter.rb
151
154
  - lib/moxml/adapter/headed_ox.rb
152
155
  - lib/moxml/adapter/leptris.rb
156
+ - lib/moxml/adapter/leptris/document_parts.rb
157
+ - lib/moxml/adapter/leptris/markers.rb
158
+ - lib/moxml/adapter/leptris/materialize.rb
159
+ - lib/moxml/adapter/leptris/sax_bridge.rb
160
+ - lib/moxml/adapter/leptris/serialize.rb
153
161
  - lib/moxml/adapter/libxml.rb
154
162
  - lib/moxml/adapter/libxml/entity_ref_registry.rb
163
+ - lib/moxml/adapter/libxml/serialize.rb
155
164
  - lib/moxml/adapter/nokogiri.rb
156
165
  - lib/moxml/adapter/oga.rb
157
166
  - lib/moxml/adapter/ox.rb