moxml 0.5.100 → 0.5.101

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: a421da0e82158a69de2d25328346153be526d74c6d2bc2bad732427763b175c1
4
- data.tar.gz: c9d982a98b2ce4984b3ee8659e89f2a4d86484d7e39fec4c04221bd2767473eb
3
+ metadata.gz: 9109c62e1dc81916376846aa35756d02e7caa0caedbf96937c6c89fb99687389
4
+ data.tar.gz: cab100fc1e408a0c511d330c81bc5f2b71e8dc0f6fa30e2c1b7a7e094d1c81a1
5
5
  SHA512:
6
- metadata.gz: 2a3c9a536530f058a8c0a7721a3f192d89be195df277e959d746f2b6360db2da3587c2467fc21222aff85b96c8c3483a6a59406554804e073b25336c6d5dfe17
7
- data.tar.gz: 750f9751134a3c077bd103edb9b18b0de7ea179d0c27839f1dff22654d372d7eb989a55c154c09ee155ec64d386547885013320eefa4044cb7dccc994d309806
6
+ metadata.gz: d041b4af64d6fe7d0d87d129ce35053da5405ba23b866e7dd3ef8c7237a21b73ff60d9baefafacf39781d5691e44b95ec295fcfe9eb54fa5e2a091ed01ea6ab7
7
+ data.tar.gz: 2f21edaf882c65e3229c617eb5df04e49b0198de3133d968e898fa9ec9218d34b52c5f165791804aafb328bae55db20fddc80c0976d596c686e66630bbd0293d
@@ -146,6 +146,14 @@ module Moxml
146
146
  nil
147
147
  end
148
148
 
149
+ # Canonical node identity: adapters carrying more than one
150
+ # Ruby representation over one C node answer the representation
151
+ # the tree walk mints, so every wrap path resolves one wrapper
152
+ # per logical node (moxml#311). Default: the node is canonical.
153
+ def canonical_native(node)
154
+ node
155
+ end
156
+
149
157
  # Prefixed-attribute value fast path: adapters whose engine
150
158
  # resolves expanded-name (uri, local) lookups natively answer
151
159
  # the value here; others fall back to the resolver's
@@ -48,7 +48,7 @@ module Moxml
48
48
  # equal?-based exclusion (canon's document-element skip)
49
49
  # silently breaks (issue #219). Mint-on-miss converges
50
50
  # both accessors on the same native object.
51
- children.map! { |child| canonical_native(doc, child) } if NATIVE_READ_LAYER
51
+ children.map! { |child| native_twin(doc, child) } if NATIVE_READ_LAYER
52
52
  children
53
53
  end
54
54
 
@@ -168,6 +168,18 @@ module Moxml
168
168
  BINDING_OF_CAP = 8_192
169
169
 
170
170
  class << self
171
+ # WeakMap has no #clear — 0.5.100's valve crashed every parse
172
+ # crossing the cap on 3.4+ (NoMethodError). Replace the
173
+ # registry wholesale, type-preserving: the WeakMap flavor
174
+ # stays weak, the strong Hash keeps compare_by_identity.
175
+ def fresh_node_registry
176
+ if Context::WEAK_WRAPPERS
177
+ ObjectSpace::WeakMap.new
178
+ else
179
+ {}.compare_by_identity
180
+ end
181
+ end
182
+
171
183
  # native -> bridged binding node. The bridge object is
172
184
  # identity-stable for the native's lifetime (the binding's
173
185
  # per-document wrap cache), so repeat bridges — the write
@@ -187,7 +199,7 @@ module Moxml
187
199
  doc = doc_for(node)
188
200
  ptr = ::FFI::Pointer.new(node.address)
189
201
  bridged = ::Leptris::XML::Node.wrap(ptr, doc)
190
- @binding_of.clear if @binding_of.size >= BINDING_OF_CAP
202
+ @binding_of = fresh_node_registry if @binding_of.size >= BINDING_OF_CAP
191
203
  @binding_of[node] = bridged unless bridged.nil?
192
204
  bridged
193
205
  end
@@ -428,7 +440,7 @@ module Moxml
428
440
  end
429
441
 
430
442
  def record_native_doc(root_native, doc)
431
- @native_doc_roots.clear if @native_doc_roots.size >= NATIVE_DOC_ROOTS_CAP
443
+ @native_doc_roots = fresh_node_registry if @native_doc_roots.size >= NATIVE_DOC_ROOTS_CAP
432
444
  @native_doc_roots[root_native] = doc
433
445
  end
434
446
 
@@ -1511,7 +1523,7 @@ module Moxml
1511
1523
  # accessor converges on one native per node (issue #219).
1512
1524
  # Binding natives (Attribute/Attr, synthetic wrappers) and
1513
1525
  # docless nodes pass through unchanged.
1514
- def canonical_native(doc, node)
1526
+ def native_twin(doc, node)
1515
1527
  return node unless node.is_a?(::Leptris::XML::Element)
1516
1528
 
1517
1529
  cache = doc.native_cache
@@ -1625,7 +1637,7 @@ module Moxml
1625
1637
  # Through the same address-keyed cache as the children
1626
1638
  # path: NativeNode.from alone mints a fresh object per
1627
1639
  # call on 1.9.163.x, splitting the wrappers (issue #219).
1628
- native = canonical_native(document, r)
1640
+ native = native_twin(document, r)
1629
1641
  record_native_doc(native, document)
1630
1642
  return native
1631
1643
  end
@@ -1686,8 +1698,8 @@ module Moxml
1686
1698
  # convergence (#219) hands every accessor — children
1687
1699
  # included — the SAME native, so the wrapper identity
1688
1700
  # holds across later traversals.
1689
- canonical_native(doc,
1690
- ::Leptris::XML::Node.wrap(::FFI::Pointer.new(addr), doc))
1701
+ native_twin(doc,
1702
+ ::Leptris::XML::Node.wrap(::FFI::Pointer.new(addr), doc))
1691
1703
  end
1692
1704
 
1693
1705
  def attributes(element)
@@ -1839,6 +1851,7 @@ module Moxml
1839
1851
  node = to_binding(node)
1840
1852
  new_node = to_binding(new_node)
1841
1853
  end
1854
+ pin_foreign_owner_document(node, new_node)
1842
1855
  # A PI inserted before the root lives at document level in
1843
1856
  # libleptris's model, not in the element tree.
1844
1857
  if new_node.is_a?(::Leptris::XML::ProcessingInstruction) &&
@@ -1864,6 +1877,7 @@ module Moxml
1864
1877
  node = to_binding(node)
1865
1878
  new_node = to_binding(new_node)
1866
1879
  end
1880
+ pin_foreign_owner_document(node, new_node)
1867
1881
  return node.add_next_sibling(new_node) if node.is_a?(::Leptris::XML::Element)
1868
1882
 
1869
1883
  # Non-element receivers (Text/Comment/CDATA): the binding's
@@ -2118,6 +2132,29 @@ module Moxml
2118
2132
  engine_xpath(node, expression, namespaces)
2119
2133
  end
2120
2134
 
2135
+ # Canonical node identity (moxml#311): the native layer and
2136
+ # the binding layer hold separate Ruby objects over one C
2137
+ # node, and only the native cache carries the tree-walk
2138
+ # identity the @parent_node invalidation chain rides on.
2139
+ # Resolve binding-layer nodes (xpath results, engine results)
2140
+ # to the walked instance when one exists — a memoized
2141
+ # children holder implies a prior walk, so a cache miss means
2142
+ # no holder exists and the binding node is safe as-is.
2143
+ def canonical_native(node)
2144
+ return node unless NATIVE_READ_LAYER
2145
+ return node if node.is_a?(::Leptris::XML::NativeNode)
2146
+ return node unless node.is_a?(::Leptris::XML::Node)
2147
+
2148
+ doc = node.document
2149
+ return node if doc.nil?
2150
+
2151
+ # Only a contract-carrying hit is the walked canonical —
2152
+ # plain NN twins (entity references, #219 miss-mints) have
2153
+ # no Identity class and keep the binding representation.
2154
+ hit = doc.native_cache[node.c_ptr.address]
2155
+ hit.is_a?(::Moxml::Node) ? hit : node
2156
+ end
2157
+
2121
2158
  def at_xpath(node, expression, namespaces = {})
2122
2159
  native = native_xpath(node, expression, namespaces, first_only: true)
2123
2160
  return native unless native.nil?
@@ -2194,7 +2231,8 @@ module Moxml
2194
2231
  # and frees in one C dispatch — no NodeSet container, no
2195
2232
  # AutoPointer (leptris-ruby TODO.perf/16). The module
2196
2233
  # object back means a scalar, whose wrap (and free)
2197
- # stays with wrap_xpath_first_result.
2234
+ # stays with wrap_xpath_first_result. Identity resolution
2235
+ # happens at the at_xpath boundary (bridge_xpath_node).
2198
2236
  return ::Leptris::XML::Searchable.wrap_xpath_first_result(
2199
2237
  document, result_ptr
2200
2238
  )
data/lib/moxml/cdata.rb CHANGED
@@ -19,5 +19,8 @@ module Moxml
19
19
  def name
20
20
  "#cdata-section"
21
21
  end
22
+
23
+ # Nokogiri-compatible: renaming non-element nodes is a no-op
24
+ def name=(_value); end
22
25
  end
23
26
  end
data/lib/moxml/comment.rb CHANGED
@@ -21,5 +21,8 @@ module Moxml
21
21
  def name
22
22
  "comment"
23
23
  end
24
+
25
+ # Nokogiri-compatible: renaming non-element nodes is a no-op
26
+ def name=(_value); end
24
27
  end
25
28
  end
data/lib/moxml/context.rb CHANGED
@@ -74,6 +74,18 @@ module Moxml
74
74
  end
75
75
  end
76
76
 
77
+ # Children-memo generation: bumped by every tree mutation so
78
+ # per-wrapper children memos can self-invalidate even when the
79
+ # same logical node is reachable through multiple wrapper
80
+ # representations (native vs binding duality).
81
+ def children_generation
82
+ @children_generation ||= 0
83
+ end
84
+
85
+ def bump_children_generation
86
+ @children_generation = children_generation + 1
87
+ end
88
+
77
89
  def namespace_scope_generation
78
90
  @namespace_scope_generation
79
91
  end
@@ -113,6 +113,14 @@ module Moxml
113
113
  Wrappers::EntityReference.new(native, context)
114
114
  end
115
115
 
116
+ # Nokogiri-compatible: Document#name returns "document"
117
+ def name
118
+ "document"
119
+ end
120
+
121
+ # Nokogiri-compatible: renaming a document node is a no-op
122
+ def name=(_value); end
123
+
116
124
  def add_child(node)
117
125
  node = prepare_node(node)
118
126
 
data/lib/moxml/node.rb CHANGED
@@ -74,6 +74,11 @@ module Moxml
74
74
  end
75
75
 
76
76
  def children
77
+ # Generation-checked memo: mutations bump the context counter, so
78
+ # this self-invalidates even when the same logical node is served
79
+ # by multiple wrapper representations (native vs binding duality).
80
+ @children = nil if @children_gen != context.children_generation
81
+ @children_gen = context.children_generation
77
82
  @children ||= begin
78
83
  # The wrapper's entity memo decides the marker split; the
79
84
  # adapter would otherwise re-derive it per call (a C parent
@@ -100,6 +105,7 @@ module Moxml
100
105
  end
101
106
 
102
107
  def add_child(node)
108
+ context.bump_children_generation
103
109
  node = prepare_node(node)
104
110
  adapter.add_child(@native, node.native)
105
111
  # Refresh native in case adapter changed identity (e.g., LibXML
@@ -116,20 +122,45 @@ module Moxml
116
122
  end
117
123
 
118
124
  def add_previous_sibling(node)
125
+ context.bump_children_generation
119
126
  node = prepare_node(node)
120
127
  adapter.add_previous_sibling(@native, node.native)
128
+ # Invalidate the parent's memoized children list. The wrapper-side
129
+ # @parent_node link is only set when this node was yielded through a
130
+ # parent-aware NodeSet; nodes obtained via at()/xpath() carry no such
131
+ # link, so resolve the parent wrapper from the adapter instead.
132
+ parent&.invalidate_children_cache!
121
133
  invalidate_parent_children_cache!
122
134
  self
123
135
  end
124
136
 
137
+ # Nokogiri-compatible assignment forms: node.next = / node.previous =.
138
+ # String operands are parsed as XML fragments, like Nokogiri.
139
+ def next=(node)
140
+ add_next_sibling(sibling_operand(node))
141
+ end
142
+
143
+ def previous=(node)
144
+ add_previous_sibling(sibling_operand(node))
145
+ end
146
+
147
+ def sibling_operand(node)
148
+ return node unless node.is_a?(String)
149
+
150
+ context.parse_fragment(node).first
151
+ end
152
+
125
153
  def add_next_sibling(node)
154
+ context.bump_children_generation
126
155
  node = prepare_node(node)
127
156
  adapter.add_next_sibling(@native, node.native)
157
+ parent&.invalidate_children_cache!
128
158
  invalidate_parent_children_cache!
129
159
  self
130
160
  end
131
161
 
132
162
  def remove
163
+ context.bump_children_generation
133
164
  invalidate_parent_children_cache!
134
165
  adapter.remove(@native)
135
166
  invalidate_children_cache!
@@ -143,6 +174,7 @@ module Moxml
143
174
  def invalidate_namespace_cache!; end
144
175
 
145
176
  def replace(node)
177
+ context.bump_children_generation
146
178
  node = prepare_node(node)
147
179
  invalidate_parent_children_cache!
148
180
  adapter.replace(@native, node.native)
@@ -215,6 +247,42 @@ module Moxml
215
247
  context)
216
248
  end
217
249
 
250
+ # Nokogiri-compatible sugar: at/search/at_css/css. Nokogiri's
251
+ # at/search auto-detect XPath vs CSS; the XPath-first fallback
252
+ # covers the dominant caller shape (XPath expression strings)
253
+ # while css/at_css serve stylesheet-style selectors.
254
+ def at(expression, namespaces = {})
255
+ at_css_or_xpath(expression, namespaces)
256
+ end
257
+
258
+ def search(expression, namespaces = {})
259
+ xpath(expression, namespaces)
260
+ end
261
+
262
+ def css(_expression)
263
+ raise Moxml::NotImplementedError,
264
+ "CSS selectors are not supported; use search with XPath"
265
+ end
266
+
267
+ def at_css(_expression)
268
+ raise Moxml::NotImplementedError,
269
+ "CSS selectors are not supported; use at with XPath"
270
+ end
271
+
272
+ def at_css_or_xpath(expression, namespaces = {})
273
+ at_xpath(expression, namespaces)
274
+ end
275
+
276
+ # Nokogiri-compatible depth-first traversal: yields self, then
277
+ # children recursively (document-order visitor).
278
+ def traverse(&block)
279
+ return to_enum(:traverse) unless block
280
+
281
+ yield self
282
+ children.each { |child| child.traverse(&block) }
283
+ self
284
+ end
285
+
218
286
  # Convenience find methods (aliases for xpath methods)
219
287
  def find(xpath_expression, namespaces = {})
220
288
  at_xpath(xpath_expression, namespaces)
@@ -591,6 +659,13 @@ module Moxml
591
659
  def self.wrap_with(node, context, adapter)
592
660
  return nil if node.nil?
593
661
 
662
+ # One C node may have several Ruby representations (leptris
663
+ # native/binding duality, moxml#311); every wrap resolves to
664
+ # the canonical one so memo holders share one invalidation
665
+ # chain. A hit-only lookup: a memoized holder implies a prior
666
+ # walk, which is what populates the canonical map.
667
+ node = adapter.canonical_native(node)
668
+
594
669
  # Cache-stable binding nodes carry their wrapper directly
595
670
  # (#312): the binding's document-owned cache hands the SAME
596
671
  # node object for the engine node's whole lifetime, so an
data/lib/moxml/text.rb CHANGED
@@ -33,5 +33,8 @@ module Moxml
33
33
  def name
34
34
  "text"
35
35
  end
36
+
37
+ # Nokogiri-compatible: renaming non-element nodes is a no-op
38
+ def name=(_value); end
36
39
  end
37
40
  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.100"
4
+ VERSION = "0.5.101"
5
5
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # 0.5.100's cap valves called .clear on the registry — an
6
+ # ObjectSpace::WeakMap in weak-wrapper mode (Ruby 3.4+) has no
7
+ # #clear, so the first parse crossing 8,192 bridges crashed with
8
+ # NoMethodError. The valve replaces the registry wholesale now.
9
+ RSpec.describe "leptris binding registry cap valve" do
10
+ let(:ctx) { Moxml.new(:leptris) }
11
+ let(:doc) do
12
+ ctx.parse("<r>#{Array.new(8_300) { |i| "<e#{i}/>" }.join}</r>")
13
+ end
14
+
15
+ it "bridges past the cap" do
16
+ children = doc.root.children.to_a
17
+ expect(children.size).to be > 8_192
18
+
19
+ bridged = children.map { |child| Moxml::Adapter::Leptris.to_binding(child.native) }
20
+ expect(bridged.compact.size).to eq(children.size)
21
+ end
22
+
23
+ it "bridges stay identity-stable across the valve" do
24
+ children = doc.root.children.to_a
25
+ first_pass = children.map { |child| Moxml::Adapter::Leptris.to_binding(child.native) }
26
+ second_pass = children.map { |child| Moxml::Adapter::Leptris.to_binding(child.native) }
27
+ expect(first_pass.zip(second_pass).all? { |one, other| one.equal?(other) }).to be(true)
28
+ end
29
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # moxml#311: the native and binding layers hold separate Ruby objects
6
+ # over one C node. An xpath-resolved node that failed to resolve to
7
+ # the tree-walk identity carried no parent link, so sibling inserts
8
+ # through it never invalidated the memoized children of wrappers
9
+ # handed out before the query — the insert serialized but stayed
10
+ # invisible through parent.children.
11
+ RSpec.describe "leptris xpath nodes resolve canonical identity" do
12
+ let(:ctx) { Moxml.new(:leptris) }
13
+ let(:doc) { ctx.parse("<r><a>t</a>\n<b/></r>") }
14
+
15
+ it "at_xpath returns the walked wrapper" do
16
+ root = doc.root
17
+ walked = root.children.first
18
+ expect(root.at_xpath("a")).to be(walked)
19
+ end
20
+
21
+ it "xpath list results resolve canonical identity" do
22
+ root = doc.root
23
+ walked = root.children.first
24
+ expect(root.xpath("a").first).to be(walked)
25
+ end
26
+
27
+ it "namespace-bound at_xpath resolves canonical identity" do
28
+ root = doc.root
29
+ walked = root.children.first
30
+ expect(root.at_xpath("a", "xmlns" => "http://x")).to be(walked)
31
+ end
32
+
33
+ it "sibling insert through at_xpath resolves in parent.children" do
34
+ root = doc.root
35
+ root.children
36
+ a = root.at_xpath("a")
37
+ a.add_next_sibling(ctx.parse_fragment("<x/>").first)
38
+ expect(root.children.map(&:name)).to eq(%w[a x text b])
39
+ end
40
+
41
+ it "sibling insert through the engine path resolves in parent.children" do
42
+ root = doc.root
43
+ root.children
44
+ walked = root.children.first
45
+ a = walked.at_xpath("self::a[parent::r]")
46
+ a.add_next_sibling(ctx.parse_fragment("<x/>").first)
47
+ expect(root.children.map(&:name)).to eq(%w[a x text b])
48
+ end
49
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "Nokogiri-compatible node sugar" do
6
+ describe "at/search" do
7
+ let(:doc) do
8
+ Moxml.parse(<<~XML)
9
+ <root xmlns="http://example.com/ns">
10
+ <item id="a"><name>one</name></item>
11
+ <item id="b"><name>two</name></item>
12
+ </root>
13
+ XML
14
+ end
15
+ let(:ns) { { "xmlns" => "http://example.com/ns" } }
16
+
17
+ it "at returns the first match like Nokogiri" do
18
+ node = doc.at("//xmlns:item", ns)
19
+ expect(node["id"]).to eq("a")
20
+ end
21
+
22
+ it "at on document delegates to root search" do
23
+ expect(doc.at("//xmlns:name", ns).text).to eq("one")
24
+ end
25
+
26
+ it "search returns all matches" do
27
+ expect(doc.search("//xmlns:item", ns).size).to eq(2)
28
+ end
29
+
30
+ it "at returns nil when nothing matches" do
31
+ expect(doc.at("//xmlns:missing", ns)).to be_nil
32
+ end
33
+
34
+ it "at works on element nodes" do
35
+ item = doc.at("//xmlns:item[@id='b']", ns)
36
+ expect(item.at("xmlns:name", ns).text).to eq("two")
37
+ end
38
+ end
39
+
40
+ describe "Node#traverse" do
41
+ it "yields depth-first in document order from an element" do
42
+ doc = Moxml.parse("<r><a><b/></a><c/></r>")
43
+ names = []
44
+ doc.root.traverse { |n| names << n.name }
45
+ expect(names).to eq(%w[r a b c])
46
+ end
47
+
48
+ it "yields the document first from a document traversal" do
49
+ doc = Moxml.parse("<r><a/></r>")
50
+ names = []
51
+ doc.traverse { |n| names << n.name }
52
+ expect(names).to eq(%w[document r a])
53
+ end
54
+ end
55
+
56
+ describe "Node#next=/previous=" do
57
+ it "insert a sibling after/before via assignment" do
58
+ doc = Moxml.parse("<r><a/><b/></r>")
59
+ a = doc.at("//a")
60
+ a.next = "<x/>"
61
+ expect(doc.root.children.map(&:name)).to eq(%w[a x b])
62
+ b = doc.at("//b")
63
+ b.previous = "<y/>"
64
+ expect(doc.root.children.map(&:name)).to eq(%w[a x y b])
65
+ end
66
+ end
67
+
68
+ describe "sibling inserts invalidate the parent children cache" do
69
+ it "next= is visible through parent.children when siblings are text" do
70
+ doc = Moxml.parse("<r><a/>\n<b/></r>")
71
+ doc.at("//a").next = "<x/>"
72
+ expect(doc.root.children.map(&:name)).to eq(%w[a x text b])
73
+ expect(doc.at("//x")).not_to be_nil
74
+ end
75
+
76
+ it "duality-proof: insert visible through a wrapper that memoized earlier" do
77
+ ctx = Moxml.new(:leptris)
78
+ doc = ctx.parse("<r><a/>\n<b/></r>")
79
+ doc.root.children.map(&:name) # memoize on the root wrapper
80
+ a = doc.at("//a") # possibly a different wrapper instance
81
+ a.next = "<x/>"
82
+ expect(doc.root.children.map(&:name)).to eq(%w[a x text b])
83
+ end
84
+ end
85
+ 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.100
4
+ version: 0.5.101
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-01 00:00:00.000000000 Z
11
+ date: 2026-10-02 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
@@ -441,12 +441,14 @@ files:
441
441
  - spec/moxml/adapter/entity_restoration_spec.rb
442
442
  - spec/moxml/adapter/headed_ox_spec.rb
443
443
  - spec/moxml/adapter/leptris_append_gc_spec.rb
444
+ - spec/moxml/adapter/leptris_binding_cap_spec.rb
444
445
  - spec/moxml/adapter/leptris_doc_parts_spec.rb
445
446
  - spec/moxml/adapter/leptris_parse_skips_spec.rb
446
447
  - spec/moxml/adapter/leptris_record_driver_spec.rb
447
448
  - spec/moxml/adapter/leptris_sax_dup_attr_spec.rb
448
449
  - spec/moxml/adapter/leptris_sax_records_spec.rb
449
450
  - spec/moxml/adapter/leptris_spec.rb
451
+ - spec/moxml/adapter/leptris_xpath_identity_spec.rb
450
452
  - spec/moxml/adapter/libxml_internals_spec.rb
451
453
  - spec/moxml/adapter/libxml_spec.rb
452
454
  - spec/moxml/adapter/nokogiri_spec.rb
@@ -459,6 +461,7 @@ files:
459
461
  - spec/moxml/adapter_spec.rb
460
462
  - spec/moxml/allocation_benchmark_spec.rb
461
463
  - spec/moxml/allocation_guard_spec.rb
464
+ - spec/moxml/at_search_spec.rb
462
465
  - spec/moxml/attribute_resolver_spec.rb
463
466
  - spec/moxml/attribute_spec.rb
464
467
  - spec/moxml/builder_spec.rb