canon 0.3.59 → 0.3.61

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: 59a766120de370a517050c389b3ec809574e8e0f876b6a9b3ae351386cfc7333
4
- data.tar.gz: 6a64cdc990d0d295645ec1f893e37da8b03858c06a2b3992d0e00926d04c939b
3
+ metadata.gz: b5b90b7fc1d45d8f8e58e804f3f67dae934cc225639f3196c64f43cdd84ae005
4
+ data.tar.gz: c6615312bb2ac6eba00700c60b7dfeeb18277b8677532f98f91e4ad24a29c562
5
5
  SHA512:
6
- metadata.gz: 0f659e6d2708dc675e2bc0df68eb57773ca16224083249e95a9906b1f7d967c92a917882f73a810490ca3b11c228afb9b581d1ceb80b0dc674d98d46014aba42
7
- data.tar.gz: 974a7dfa4cdcacb21e76952c193160022ecd83de6887b4a5582c60d5853feb5c89a3e69be559ef36484c37a8f9228da1cad8a9168829d23b3e096d51d09fca6d
6
+ metadata.gz: 75e2773c9597d616988157bdd40f4df3478dfd5695c1b9c7b9fc3709a6b043e22e98d3a1d9f2221ad06f7d4bb249331b1356606dce4acf10ce4bdfd8c254d209
7
+ data.tar.gz: 2877cc2367ac8da5ef68ea9b2ba2cc94883754a47aaf7dba51aef1050d67b9f3d95027238a539e119c92704fa696bdd8de8412bf178098a00e03061fbbb48888
@@ -201,11 +201,17 @@ module Canon
201
201
  # side answers both; any hit falls through to the pipeline.
202
202
  def verbose_report_proven_empty?(n1, n2, opts)
203
203
  return true unless opts[:verbose]
204
+
204
205
  # Identical input parses identically — differences and
205
- # attribute order coincide by construction; only the SAX
206
- # recover-error surface (parse-error banner, issue #130)
207
- # needs its one scan.
208
- return !Xml::Sax.probe(n1).saw_error? if n1 == n2
206
+ # attribute order coincide by construction. Recover errors
207
+ # ride the fingerprint's diag check when the engine exposes
208
+ # them (libleptris 1.9.205+, #1200); otherwise the one SAX
209
+ # scan covers the parse-error banner (issue #130).
210
+ if n1 == n2
211
+ return true if Xml::DigestGate.recover_diags_available?
212
+
213
+ return !Xml::Sax.probe(n1).saw_error?
214
+ end
209
215
 
210
216
  left = Xml::Sax.probe(n1)
211
217
  return false if left.saw_error?
@@ -196,7 +196,7 @@ module Canon
196
196
  return false unless Canon::Comparison::NodeInspector.element_node?(element)
197
197
 
198
198
  # List of HTML elements where whitespace is semantically significant
199
- whitespace_sensitive_tags = %w[pre code textarea script style]
199
+ whitespace_sensitive_tags = Core::TreeNode::WHITESPACE_SENSITIVE_TAGS
200
200
  whitespace_sensitive_tags.include?(element.name.downcase)
201
201
  end
202
202
 
@@ -87,8 +87,9 @@ module Canon
87
87
  children.empty?
88
88
  end
89
89
 
90
- # Elements where whitespace is semantically significant; their
91
- # text value participates in the signature (see NodeSignature).
90
+ # Elements where whitespace is semantically significant (HTML):
91
+ # their text value participates in signatures and matching.
92
+ # Single source for the tree_diff components.
92
93
  WHITESPACE_SENSITIVE_TAGS = %w[pre code textarea script style].freeze
93
94
 
94
95
  def build_signature_path(include_attributes)
@@ -109,9 +110,17 @@ module Canon
109
110
  component = label.to_s
110
111
 
111
112
  # Sorted attributes distinguish same-label nodes while
112
- # ignoring attribute order.
113
+ # ignoring attribute order. Built directly into the
114
+ # component — no intermediate sorted/map/join arrays.
113
115
  if include_attributes && !attributes.empty?
114
- component += "{#{attributes.sort.map { |k, v| "#{k}=#{v}" }.join(',')}}" # rubocop:disable Style/StringConcatenation
116
+ component += "{"
117
+ first = true
118
+ attributes.sort.each do |k, v|
119
+ component << "," unless first
120
+ first = false
121
+ component << k.to_s << "=" << v.to_s
122
+ end
123
+ component << "}"
115
124
  end
116
125
 
117
126
  if include_attributes &&
@@ -165,10 +174,14 @@ module Canon
165
174
  #
166
175
  # @return [Array<TreeNode>]
167
176
  def descendants
177
+ # Iterative pre-order walk — the recursive form allocated an
178
+ # intermediate array per level.
168
179
  result = []
169
- children.each do |child|
170
- result << child
171
- result.concat(child.descendants)
180
+ stack = children.reverse_each.to_a
181
+ until stack.empty?
182
+ node = stack.pop
183
+ result << node
184
+ stack.concat(node.children.reverse_each.to_a) if node.children.any?
172
185
  end
173
186
  result
174
187
  end
@@ -158,7 +158,7 @@ module Canon
158
158
  return false unless node
159
159
 
160
160
  # List of HTML elements where whitespace is semantically significant
161
- whitespace_sensitive_tags = %w[pre code textarea script style]
161
+ whitespace_sensitive_tags = Core::TreeNode::WHITESPACE_SENSITIVE_TAGS
162
162
 
163
163
  # Check if this node is whitespace-sensitive
164
164
  if node.is_a?(Core::TreeNode)
@@ -211,7 +211,7 @@ module Canon
211
211
  return false unless node
212
212
 
213
213
  # List of HTML elements where whitespace is semantically significant
214
- whitespace_sensitive_tags = %w[pre code textarea script style]
214
+ whitespace_sensitive_tags = Core::TreeNode::WHITESPACE_SENSITIVE_TAGS
215
215
 
216
216
  # Check if this node is whitespace-sensitive
217
217
  if node.is_a?(Core::TreeNode)
@@ -205,8 +205,10 @@ module Canon
205
205
  attrs1 = node1.attributes
206
206
  attrs2 = node2.attributes
207
207
 
208
- # Check if attribute values differ (ignoring order)
209
- if attrs1.sort.to_h != attrs2.sort.to_h
208
+ # Check if attribute values differ (ignoring order).
209
+ # Identical raw hashes (order included) are trivially equal
210
+ # after sorting — the overwhelmingly common matched case.
211
+ if attrs1 != attrs2 && attrs1.sort.to_h != attrs2.sort.to_h
210
212
  # Actual attribute value differences
211
213
  changes[:attributes] = {
212
214
  old: attrs1,
@@ -611,15 +613,12 @@ module Canon
611
613
  def whitespace_sensitive?(node)
612
614
  return false unless node
613
615
 
614
- # List of HTML elements where whitespace is semantically significant
615
- whitespace_sensitive_tags = %w[pre code textarea script style]
616
-
617
616
  # Check if this node or any ancestor is whitespace-sensitive
618
617
  current = node
619
618
  while current
620
619
  if current.is_a?(Core::TreeNode)
621
620
  label = current.label.to_s.downcase
622
- return true if whitespace_sensitive_tags.include?(label)
621
+ return true if Core::TreeNode::WHITESPACE_SENSITIVE_TAGS.include?(label)
623
622
  end
624
623
 
625
624
  current = current.is_a?(Core::TreeNode) ? current.parent : nil
data/lib/canon/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Canon
4
- VERSION = "0.3.59"
4
+ VERSION = "0.3.61"
5
5
  end
@@ -21,6 +21,28 @@ module Canon
21
21
  module DigestGate
22
22
  module_function
23
23
 
24
+ # libleptris 1.9.205+ records duplicate-attribute recover
25
+ # events on the parsed document (#1200): read through the
26
+ # FFI diag counter. Evaluated lazily at call time — a load-time
27
+ # constant would freeze before leptris loads (defined? never
28
+ # resolves pending autoloads). When the adapter is not leptris
29
+ # (or the binding predates the surface), the verbose lane
30
+ # falls back to the SAX probe for its error scan.
31
+ def recover_diags_available?
32
+ defined?(::Leptris::XML::FFI) &&
33
+ ::Leptris::XML::FFI.respond_to?(:leptris_document_parse_diag_count)
34
+ rescue StandardError
35
+ false
36
+ end
37
+
38
+ def recover_diags?(doc)
39
+ return false unless recover_diags_available?
40
+
41
+ native = doc.native
42
+ native = native.c_ptr if native.respond_to?(:c_ptr)
43
+ ::Leptris::XML::FFI.leptris_document_parse_diag_count(native).positive?
44
+ end
45
+
24
46
  def available?
25
47
  return false if RUBY_ENGINE == "opal"
26
48
  return false unless Canon::XmlBackend.moxml? &&
@@ -106,7 +128,11 @@ module Canon
106
128
  doc = context.parse(xml, readonly: true, strict: false)
107
129
  root = doc.root
108
130
  return nil unless root
109
- return nil if clean && doc.parse_errors.any?
131
+
132
+ if clean
133
+ return nil if doc.parse_errors.any?
134
+ return nil if recover_diags?(doc)
135
+ end
110
136
 
111
137
  skeleton = doc.children.filter_map do |child|
112
138
  next if Canon::XmlParsing.same_engine_node?(child, root)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.59
4
+ version: 0.3.61
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.