moxml 0.5.15 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '098d93bb8d9951a645e7f49e4031993942b8965368b8dcdbcc6e590514633571'
4
- data.tar.gz: b1f5f35e34a6a458547d7740860d0bdb7d786fb857532050191ac2c188906794
3
+ metadata.gz: 39cfbe08ea2a0e92f2ba89e83beacf564401765e964807dc4d73ca23d867af38
4
+ data.tar.gz: cdd5d499fd4b3233a6c4d2774b188aef2339f341f7c7a7112a82ac392ec4e53b
5
5
  SHA512:
6
- metadata.gz: 4bdb52155bbfeb4f4e8567d2feb3e0b705562340db67955c346a0c1980823743ef882fb44c0d4abd6a42eefa5b1a8fd8bf77f2c20fd72c8074f70e187e0fe199
7
- data.tar.gz: cb5f85126e157a7271c052632bdcd6a42c4f2e99c9096bac69ea50aa5cfd75b98f9bc94f5d502f63298cc2e9c687706cb45a49aeeae17d6abd56d1301b858d8d
6
+ metadata.gz: 894d6bacdd4f26dbb2a59b0684b29e8ca68b39b66f841a5cd11005351eff38404e745b47d58bb860c898d3da92e1e2a769210938f26cac2f88cac34cc887de76
7
+ data.tar.gz: c5b7b7c9de4f2362e910b740ce4cf6185a72773f39f31a3dfc17fbd7436f65f542e66ff099900bf9073e87bd5975568cb3833ac658115ea5ee21a4218719ce90
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ gemspec
9
9
  gem "benchmark"
10
10
  gem "benchmark-ips"
11
11
  gem "get_process_mem"
12
- gem "leptris", "~> 1.1"
12
+ gem "leptris", "~> 1.9.32"
13
13
  gem "libxml-ruby", "~> 5.0"
14
14
  gem "nokogiri", "~> 1.18"
15
15
  gem "openssl", "~> 3.0"
@@ -86,9 +86,9 @@ eight record fields per node, returning nil falls back to the
86
86
  generic wrapper walk. Leptris walks raw C pointers with the batch
87
87
  `leptris_node_children` call — no wrappers, no per-node callback.
88
88
 
89
- Constants like `DOC_NODE_SUPPORTED` / `DTDATTR_SUPPORTED` /
90
- `BULK_FIELD_READS` (Leptris) follow the same idea: one probe
91
- per engine capability, computed at load.
89
+ Version gates like `PREFIXED_ATTR_PREDICATES_NATIVE` and the
90
+ `MINIMUM_BINDING_VERSION` floor (Leptris) follow the same idea: one
91
+ probe per engine capability, computed at load.
92
92
 
93
93
  == Return shapes
94
94
 
@@ -13,11 +13,14 @@ with a native XPath 1.0 engine, SAX, and canonicalization.
13
13
 
14
14
  == Default resolution
15
15
 
16
- Leptris becomes the default when the installed binding supports
17
- programmatic document construction (`Leptris::XML::Document.create`,
18
- leptris >= 1.3). Otherwise the default falls back to Nokogiri. Under
19
- Opal the default is rexml (stock oga requires its C extension and
20
- libleptris does not build for Opal).
16
+ Leptris becomes the default when the installed binding meets the
17
+ adapter's floor `Moxml::Adapter::Leptris::MINIMUM_BINDING_VERSION`
18
+ (leptris >= 1.9.32; issue #149): bounded traverse, built documents
19
+ that reflect their parts immediately, and every capability surface
20
+ the adapter assumes. Below the floor the default falls back to
21
+ Nokogiri rather than driving a binding the adapter no longer
22
+ accommodates. Under Opal the default is rexml (stock oga requires
23
+ its C extension and libleptris does not build for Opal).
21
24
 
22
25
  [source,ruby]
23
26
  ----
@@ -10,7 +10,7 @@ module Moxml
10
10
  # swept too (the context wrapper identity map self-cleans via
11
11
  # its size valve).
12
12
  DOCUMENT_ATTACHMENT_KEYS = %i[
13
- entity_markers doc_pi_nodes declaration doctype
13
+ entity_markers declaration doctype
14
14
  had_source_declaration document_text
15
15
  ].freeze
16
16
 
@@ -29,64 +29,18 @@ module Moxml
29
29
  doctype_wrapper = attachments.get(doc, :doctype)
30
30
  children << doctype_wrapper if doctype_wrapper
31
31
 
32
- if DOC_NODE_SUPPORTED
33
- # The libxml2-model document node lists prolog PIs/comments,
34
- # the root, and epilog PIs/comments in document order —
35
- # the Nokogiri-shaped contract, epilog anchoring included
36
- # (issue #130). Built (programmatic) documents are not yet
37
- # fully reflected by the node (binding gap: an attached
38
- # root does not appear); document_node_children answers
39
- # nil there for the legacy parts path.
40
- doc_children = document_node_children(doc)
41
- if doc_children
42
- children.concat(doc_children)
43
- else
44
- children.concat(document_pi_nodes(doc))
45
- children << doc.root if doc.root
46
- end
47
- else
48
- # Legacy path: document-level PIs live outside the element
49
- # tree in a flat pre-root list (libleptris < 1.9.7 C
50
- # model); epilog anchoring is not representable there.
51
- children.concat(document_pi_nodes(doc))
52
-
53
- children << doc.root if doc.root
54
- end
32
+ # The libxml2-model document node lists prolog PIs/comments,
33
+ # the root, and epilog PIs/comments in document order — the
34
+ # Nokogiri-shaped contract, epilog anchoring included (issue
35
+ # #130). Built documents reflect their parts immediately
36
+ # since leptris-ruby 1.9.32 (leptris-ruby#91).
37
+ children.concat(doc.children.to_a)
55
38
 
56
39
  texts = attachments.get(doc, :document_text)
57
40
  children.concat(texts) if texts
58
41
  children
59
42
  end
60
43
 
61
- # Document-level PI pseudo-nodes, materialized once from the C
62
- # list and cached per document: children and serialization read
63
- # the same objects, so wrapper mutations round-trip. Build the
64
- # cache BEFORE appending a PI with add_pi, or the C-side
65
- # addition would be double-counted.
66
- # The document node's children, or nil when the node does not
67
- # reflect reality: parsed documents always list the root
68
- # element among their children, but programmatically built
69
- # ones do not (binding gap) — those keep the legacy parts
70
- # path.
71
- def document_node_children(doc)
72
- doc_children = doc.children.to_a
73
- has_root = doc_children.any?(::Leptris::XML::Element)
74
- return doc_children if has_root
75
- return doc_children if doc.root.nil?
76
-
77
- nil
78
- end
79
-
80
- def document_pi_nodes(doc)
81
- attachments.get(doc, :doc_pi_nodes) || begin
82
- nodes = doc.processing_instructions.map do |(target, data)|
83
- CustomizedLeptris::DocumentPI.new(target, data, doc)
84
- end
85
- attachments.set(doc, :doc_pi_nodes, nodes)
86
- nodes
87
- end
88
- end
89
-
90
44
  def add_document_child(doc, child)
91
45
  case child
92
46
  when CustomizedLeptris::Declaration
@@ -102,15 +56,9 @@ module Moxml
102
56
  when ::Leptris::XML::Element
103
57
  doc.root = child
104
58
  when ::Leptris::XML::ProcessingInstruction
105
- document_pi_nodes(doc)
106
59
  doc.add_pi(child.target, child.content.to_s)
107
- document_pi_nodes(doc) << CustomizedLeptris::DocumentPI.new(
108
- child.target, child.content.to_s, doc
109
- )
110
60
  when CustomizedLeptris::DocumentPI
111
- document_pi_nodes(doc)
112
61
  doc.add_pi(child.target, child.data)
113
- document_pi_nodes(doc) << child
114
62
  when ::Leptris::XML::Text
115
63
  texts = attachments.get(doc, :document_text) || []
116
64
  texts << child
@@ -149,22 +97,10 @@ module Moxml
149
97
  native = native_doctype_xml(doc)
150
98
  parts << native << "\n" if native
151
99
 
152
- if DOC_NODE_SUPPORTED
153
- # The libxml2-model document node: prolog PIs/comments,
154
- # the root, epilog PIs/comments in document order, so
155
- # epilog parts serialize after the root (issue #130).
156
- doc_children = document_node_children(doc)
157
- if doc_children
158
- doc_children.each { |child| parts << raw_serialize(child, options) << "\n" }
159
- else
160
- document_pi_nodes(doc).each { |pi| parts << pi.to_xml << "\n" }
161
- parts << raw_serialize(doc.root, options) << "\n" if doc.root
162
- end
163
- else
164
- document_pi_nodes(doc).each { |pi| parts << pi.to_xml << "\n" }
165
-
166
- parts << raw_serialize(doc.root, options) << "\n" if doc.root
167
- end
100
+ # The libxml2-model document node: prolog PIs/comments, the
101
+ # root, epilog PIs/comments — in document order, so epilog
102
+ # parts serialize after the root (issue #130).
103
+ doc.children.each { |child| parts << raw_serialize(child, options) << "\n" }
168
104
 
169
105
  texts = attachments.get(doc, :document_text)
170
106
  texts&.each { |text| parts << XmlEmitter.escape_text(text.content.to_s) }
@@ -39,8 +39,6 @@ module Moxml
39
39
  end
40
40
 
41
41
  def materialize_fields(native, buffers, &block)
42
- return nil unless Leptris::BULK_FIELD_READS
43
-
44
42
  doc = native.is_a?(::Leptris::XML::Document) ? native : native.document
45
43
  # Marker-bearing text needs the split pipeline (children-level
46
44
  # ER expansion); the bulk path has no marker handling.
@@ -4,11 +4,6 @@ return if RUBY_ENGINE == "opal"
4
4
 
5
5
  require "stringio"
6
6
  require "leptris"
7
- # leptris-ruby 1.9.0 regression (leptris-ruby#53): the eager ffi load
8
- # shadows the Leptris::XML autoload manifest, leaving Document/Element
9
- # unreachable. Loading the manifest explicitly is a no-op when the gem
10
- # is fixed.
11
- require "leptris/xml"
12
7
 
13
8
  module Moxml
14
9
  module Adapter
@@ -19,29 +14,14 @@ module Moxml
19
14
  # programmatic DOCTYPE, so those live in CustomizedLeptris value
20
15
  # objects stored through NativeAttachment.
21
16
  class Leptris < Base
22
- # libleptris 1.9.7 (leptris-ruby 1.9.26): Document#node exposes
23
- # the libxml2-model document node prolog/epilog parts in
24
- # document order. Older bindings keep the flat pre-root PI path.
25
- DOC_NODE_SUPPORTED = ::Leptris::XML::Document.method_defined?(:node)
26
- # libleptris 1.9.8 (leptris-ruby 1.9.30): plain parse excludes
27
- # DTD ATTLIST defaults, matching libxml2/Nokogiri semantics;
28
- # ParseOptions::DTDATTR opts in (leptris/leptris#606).
29
- DTDATTR_SUPPORTED = ::Leptris::XML::ParseOptions.const_defined?(:DTDATTR)
30
-
31
- # The raw field-read surface for the bulk materializer (issue
32
- # #143). Bindings without the full set fall back to the generic
33
- # wrapper walk.
34
- BULK_FIELD_READS = %i[
35
- leptris_node_children leptris_node_get_type
36
- leptris_element_name leptris_element_prefix leptris_element_namespace
37
- leptris_element_namespace_count leptris_element_namespace_decl_prefix
38
- leptris_element_namespace_decl_uri leptris_element_first_attribute
39
- leptris_attribute_get_name leptris_attribute_get_value
40
- leptris_attribute_next leptris_attribute_namespace_uri
41
- leptris_text_node_get_content leptris_cdata_node_get_content
42
- leptris_comment_node_get_content leptris_pi_node_get_target
43
- leptris_pi_node_get_data
44
- ].all? { |fn| ::Leptris::XML::FFI.respond_to?(fn) }
17
+ # The binding floor (issue #149): 1.9.32 carried the traverse
18
+ # fix (leptris-ruby#89) and made built documents reflect their
19
+ # parts immediately (leptris-ruby#91); the document node
20
+ # (1.9.26), DTDATTR (1.9.8), and the batch child-pointer read
21
+ # (1.7.0) surfaces predate it. Older bindings are not eligible
22
+ # for the default (see Config.leptris_preferred_available?) and
23
+ # the adapter no longer carries accommodation paths for them.
24
+ MINIMUM_BINDING_VERSION = "1.9.32"
45
25
 
46
26
  # leptris-ruby#103: prefixed attribute tests inside predicates
47
27
  # stopped resolving through the document's in-scope declarations
@@ -115,12 +95,11 @@ module Moxml
115
95
  doc
116
96
  end
117
97
 
118
- # nil when DTDATTR is unsupported or not requested the
119
- # binding treats a nil options hash as plain defaults.
98
+ # nil unless DTDATTR is requested the binding treats a nil
99
+ # options hash as plain defaults (defaults exclude ATTLIST
100
+ # defaults, matching libxml2/Nokogiri semantics).
120
101
  def dtdattr_parse_options(options)
121
- return nil unless DTDATTR_SUPPORTED && options[:dtdattr] == true
122
-
123
- ::Leptris::XML::ParseOptions.dtdattr
102
+ options[:dtdattr] == true ? ::Leptris::XML::ParseOptions.dtdattr : nil
124
103
  end
125
104
 
126
105
  def parse_errors(native_doc)
data/lib/moxml/config.rb CHANGED
@@ -58,18 +58,20 @@ module Moxml
58
58
  OPAL_FALLBACK_ADAPTER
59
59
  end
60
60
 
61
- # True when the leptris gem is installed AND new enough for the
62
- # adapter (it needs programmatic document construction, which
63
- # released 1.1.x lacks). Memoized: the require probe runs once.
61
+ # True when the leptris gem is installed AND meets the adapter's
62
+ # binding floor (issue #149; 1.9.32 traverse bounding #89,
63
+ # built-docs parts #91, and every capability surface the adapter
64
+ # now assumes). Memoized: the probe runs once. Below the floor
65
+ # the default falls back to Nokogiri rather than driving a
66
+ # binding the adapter no longer accommodates.
64
67
  def leptris_preferred_available?
65
68
  return @leptris_preferred_available if defined?(@leptris_preferred_available)
66
69
 
67
70
  @leptris_preferred_available = begin
68
71
  require "leptris"
69
- # leptris-ruby 1.9.0 regression (leptris-ruby#53): the XML
70
- # autoload manifest is shadowed; load it explicitly.
71
- require "leptris/xml"
72
- ::Leptris::XML::Document.respond_to?(:create)
72
+ Moxml::Adapter.load(:leptris)
73
+ Gem::Version.new(::Leptris::VERSION) >=
74
+ Gem::Version.new(Moxml::Adapter::Leptris::MINIMUM_BINDING_VERSION)
73
75
  rescue LoadError, NameError
74
76
  false
75
77
  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.5.15"
4
+ VERSION = "0.5.16"
5
5
  end
@@ -186,16 +186,12 @@ RSpec.describe Moxml::Adapter::Leptris do
186
186
  end
187
187
 
188
188
  it "excludes ATTLIST defaults on plain parse" do
189
- skip "requires leptris with no-DTDATTR semantics" unless Moxml::Adapter::Leptris::DTDATTR_SUPPORTED
190
-
191
189
  doc = ctx.parse(dtd_xml)
192
190
  expect(doc.at_xpath("//e9")["attr"]).to be_nil
193
191
  expect(doc.to_xml).not_to include("attr=")
194
192
  end
195
193
 
196
194
  it "materializes ATTLIST defaults with dtdattr: true" do
197
- skip "requires leptris with ParseOptions::DTDATTR" unless Moxml::Adapter::Leptris::DTDATTR_SUPPORTED
198
-
199
195
  doc = ctx.parse(dtd_xml, dtdattr: true)
200
196
  expect(doc.at_xpath("//e9")["attr"]).to eq("default")
201
197
  expect(doc.to_xml).to include(%(attr="default"))
@@ -85,7 +85,7 @@ RSpec.describe Moxml::Adapter do
85
85
  .to include(Moxml::Config.default_adapter)
86
86
  end
87
87
 
88
- it "prefers leptris only when the installed gem supports document construction" do
88
+ it "prefers leptris only when the installed binding meets the floor" do
89
89
  if Moxml::Config.leptris_preferred_available?
90
90
  expect(Moxml::Config.runtime_default_adapter).to eq(:leptris)
91
91
  else
@@ -93,6 +93,15 @@ RSpec.describe Moxml::Adapter do
93
93
  end
94
94
  end
95
95
 
96
+ it "pins the leptris binding floor at 1.9.32 (issue #149)" do
97
+ skip "leptris not installed" unless described_class.available?(:leptris)
98
+
99
+ adapter = described_class.load(:leptris)
100
+ expect(adapter::MINIMUM_BINDING_VERSION).to eq("1.9.32")
101
+ expect(Gem::Version.new(Leptris::VERSION))
102
+ .to be >= Gem::Version.new(adapter::MINIMUM_BINDING_VERSION)
103
+ end
104
+
96
105
  it "OPAL_DEFAULT_ADAPTER is a member of OPAL_AVAILABLE_ADAPTERS" do
97
106
  expect(Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS)
98
107
  .to include(Moxml::Config::OPAL_DEFAULT_ADAPTER)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.15
4
+ version: 0.5.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.