canon 0.2.9 → 0.2.11

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +19 -10
  3. data/Rakefile +22 -2
  4. data/lib/canon/cache.rb +16 -27
  5. data/lib/canon/comparison/html_comparator.rb +2 -0
  6. data/lib/canon/comparison/node_inspector.rb +13 -48
  7. data/lib/canon/comparison/xml_comparator/attribute_comparator.rb +19 -2
  8. data/lib/canon/comparison/xml_comparator/diff_node_builder.rb +30 -0
  9. data/lib/canon/comparison/xml_comparator/node_parser.rb +2 -2
  10. data/lib/canon/comparison/xml_comparator.rb +2 -2
  11. data/lib/canon/comparison.rb +1 -1
  12. data/lib/canon/diff/diff_line_builder.rb +2 -0
  13. data/lib/canon/diff/diff_node_mapper.rb +10 -8
  14. data/lib/canon/diff/formatting_detector.rb +3 -2
  15. data/lib/canon/diff/xml_serialization_formatter.rb +0 -3
  16. data/lib/canon/diff_formatter/by_object/base_formatter.rb +12 -2
  17. data/lib/canon/diff_formatter/by_object/xml_formatter.rb +119 -1
  18. data/lib/canon/diff_formatter/by_object_formatter.rb +1 -5
  19. data/lib/canon/diff_formatter/diff_detail_formatter/dimension_formatter.rb +3 -3
  20. data/lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb +26 -27
  21. data/lib/canon/diff_formatter.rb +15 -11
  22. data/lib/canon/html/data_model.rb +1 -1
  23. data/lib/canon/tree_diff/operation_converter.rb +7 -7
  24. data/lib/canon/tree_diff/operations/operation_detector.rb +4 -0
  25. data/lib/canon/validators/base_validator.rb +5 -8
  26. data/lib/canon/validators/html_validator.rb +2 -7
  27. data/lib/canon/validators/xml_validator.rb +2 -7
  28. data/lib/canon/version.rb +1 -1
  29. data/lib/canon/xml/data_model.rb +5 -5
  30. data/lib/canon/xml/sax_builder.rb +1 -1
  31. data/lib/canon/xml/whitespace_normalizer.rb +2 -2
  32. data/lib/canon/xml_parsing.rb +28 -16
  33. metadata +6 -6
@@ -136,7 +136,16 @@ module Canon
136
136
  theme_color(:changed, :marker) || :yellow,
137
137
  )}"
138
138
  end
139
- when :structural_whitespace, :attribute_whitespace, :attribute_values
139
+ when :attribute_values
140
+ render_attribute_values_diffnode(diff_node, node1, node2, prefix,
141
+ output)
142
+ when :attribute_presence
143
+ render_attribute_presence_diffnode(diff_node, node1, node2, prefix,
144
+ output)
145
+ when :attribute_order
146
+ render_attribute_order_diffnode(diff_node, node1, node2, prefix,
147
+ output)
148
+ when :structural_whitespace, :attribute_whitespace
140
149
  output << "#{prefix}└── #{colorize(
141
150
  "[#{diff_node.dimension}: #{diff_node.reason}]",
142
151
  theme_color(:changed, :marker) || :yellow,
@@ -150,6 +159,115 @@ module Canon
150
159
  end
151
160
  end
152
161
 
162
+ # Render attribute value DiffNode with per-attribute before/after
163
+ def render_attribute_values_diffnode(diff_node, node1, node2, prefix,
164
+ output)
165
+ attrs1 = diff_node.attributes_before ||
166
+ extract_attributes_hash(node1)
167
+ attrs2 = diff_node.attributes_after ||
168
+ extract_attributes_hash(node2)
169
+ element_name = if node1
170
+ node1.name
171
+ else
172
+ (node2 ? node2.name : "?")
173
+ end
174
+
175
+ output << "#{prefix}└── #{colorize(
176
+ "Element: <#{element_name}>",
177
+ theme_color(:changed, :marker) || :yellow,
178
+ )}"
179
+
180
+ differing = find_differing_attributes(attrs1, attrs2)
181
+ differing.each_with_index do |name, i|
182
+ connector = i < differing.size - 1 ? "├──" : "└──"
183
+ val1 = attrs1[name].to_s
184
+ val2 = attrs2[name].to_s
185
+ output << "#{prefix} #{connector} " \
186
+ "#{colorize("#{name}:", :cyan)} " \
187
+ "#{colorize(val1,
188
+ theme_color(:removed, :content) || :red)} " \
189
+ "→ " \
190
+ "#{colorize(val2,
191
+ theme_color(:added, :content) || :green)}"
192
+ end
193
+ end
194
+
195
+ # Render attribute presence DiffNode with added/removed attributes
196
+ def render_attribute_presence_diffnode(diff_node, node1, node2, prefix,
197
+ output)
198
+ attrs1 = diff_node.attributes_before ||
199
+ extract_attributes_hash(node1)
200
+ attrs2 = diff_node.attributes_after ||
201
+ extract_attributes_hash(node2)
202
+ element_name = if node1
203
+ node1.name
204
+ else
205
+ (node2 ? node2.name : "?")
206
+ end
207
+
208
+ output << "#{prefix}└── #{colorize(
209
+ "Element: <#{element_name}>",
210
+ theme_color(:changed, :marker) || :yellow,
211
+ )}"
212
+
213
+ keys1 = attrs1.keys.to_set
214
+ keys2 = attrs2.keys.to_set
215
+ removed = (keys1 - keys2).sort
216
+ added = (keys2 - keys1).sort
217
+ items = removed.map { |k| [:removed, k, attrs1[k]] }
218
+ added.each { |k| items << [:added, k, attrs2[k]] }
219
+
220
+ items.each_with_index do |(type, name, val), i|
221
+ connector = i < items.size - 1 ? "├──" : "└──"
222
+ color = if type == :removed
223
+ theme_color(:removed,
224
+ :content) || :red
225
+ else
226
+ theme_color(
227
+ :added, :content
228
+ ) || :green
229
+ end
230
+ sign = type == :removed ? "-" : "+"
231
+ output << "#{prefix} #{connector} " \
232
+ "#{colorize("#{sign} #{name}=\"#{val}\"", color)}"
233
+ end
234
+ end
235
+
236
+ # Render attribute order DiffNode with before/after order
237
+ def render_attribute_order_diffnode(diff_node, node1, node2, prefix,
238
+ output)
239
+ attrs1 = diff_node.attributes_before ||
240
+ extract_attributes_hash(node1)
241
+ attrs2 = diff_node.attributes_after ||
242
+ extract_attributes_hash(node2)
243
+
244
+ order1 = attrs1.keys.join(", ")
245
+ order2 = attrs2.keys.join(", ")
246
+
247
+ output << "#{prefix}├── #{colorize(
248
+ "- [#{order1}]",
249
+ theme_color(:removed, :content) || :red,
250
+ )}"
251
+ output << "#{prefix}└── #{colorize(
252
+ "+ [#{order2}]",
253
+ theme_color(:added, :content) || :green,
254
+ )}"
255
+ end
256
+
257
+ # Extract attributes hash from a node
258
+ def extract_attributes_hash(node)
259
+ return {} unless node
260
+
261
+ Canon::Diff::NodeSerializer.extract_attributes(node) || {}
262
+ end
263
+
264
+ # Find attributes that differ between two attribute hashes
265
+ def find_differing_attributes(attrs1, attrs2)
266
+ (attrs1.keys | attrs2.keys).reject do |k|
267
+ attrs1[k.to_s] == attrs2[k.to_s]
268
+ end.sort
269
+ end
270
+
153
271
  # Render unequal elements
154
272
  def render_unequal_elements(diff, prefix, output)
155
273
  node1 = diff[:node1]
@@ -21,9 +21,6 @@ module Canon
21
21
  # @param format [Symbol] Document format (:xml, :json, :yaml)
22
22
  # @return [String] Formatted diff output
23
23
  def format(differences, format)
24
- output = []
25
- output << colorize("Visual Diff:", :cyan, :bold)
26
-
27
24
  diffs_array = if differences.is_a?(Canon::Comparison::ComparisonResult)
28
25
  differences.differences
29
26
  else
@@ -37,8 +34,7 @@ module Canon
37
34
  show_diffs: @show_diffs,
38
35
  )
39
36
 
40
- output << formatter.format(diffs_array, format)
41
- output.join("\n")
37
+ formatter.format(diffs_array, format)
42
38
  end
43
39
 
44
40
  private
@@ -707,7 +707,7 @@ expand_difference: false)
707
707
  # @param diff [DiffNode, Hash] Difference node
708
708
  # @return [Symbol] Dimension
709
709
  def self.extract_dimension(diff)
710
- if diff.respond_to?(:dimension)
710
+ if diff.is_a?(Canon::Diff::DiffNode)
711
711
  diff.dimension
712
712
  elsif diff.is_a?(Hash)
713
713
  diff[:dimension] || diff[:diff_code] || :unknown
@@ -721,7 +721,7 @@ expand_difference: false)
721
721
  # @param diff [DiffNode, Hash] Difference node
722
722
  # @return [Object] Node1
723
723
  def self.extract_node1(diff)
724
- if diff.respond_to?(:node1)
724
+ if diff.is_a?(Canon::Diff::DiffNode)
725
725
  diff.node1
726
726
  elsif diff.is_a?(Hash)
727
727
  diff[:node1]
@@ -733,7 +733,7 @@ expand_difference: false)
733
733
  # @param diff [DiffNode, Hash] Difference node
734
734
  # @return [Object] Node2
735
735
  def self.extract_node2(diff)
736
- if diff.respond_to?(:node2)
736
+ if diff.is_a?(Canon::Diff::DiffNode)
737
737
  diff.node2
738
738
  elsif diff.is_a?(Hash)
739
739
  diff[:node2]
@@ -17,12 +17,12 @@ module Canon
17
17
  return "" unless diff
18
18
 
19
19
  # Prefer pre-computed path if available (populated by MetadataEnricher)
20
- if diff.respond_to?(:path) && !diff.path.nil? && !diff.path.empty?
21
- return "Location: #{diff.path}"
20
+ if diff.is_a?(Canon::Diff::DiffNode) && diff.path && !diff.path.empty?
21
+ return diff.path
22
22
  end
23
23
 
24
24
  # Fall back to extracting from nodes
25
- node = if diff.respond_to?(:node1)
25
+ node = if diff.is_a?(Canon::Diff::DiffNode)
26
26
  diff.node1 || diff.node2
27
27
  elsif diff.is_a?(Hash)
28
28
  diff[:node1] || diff[:node2]
@@ -30,8 +30,7 @@ module Canon
30
30
 
31
31
  return "" unless node
32
32
 
33
- xpath = extract_xpath(node)
34
- xpath.empty? ? "" : "Location: #{xpath}"
33
+ extract_xpath(node)
35
34
  end
36
35
 
37
36
  # Extract XPath from a node
@@ -66,24 +65,26 @@ module Canon
66
65
  current = node
67
66
 
68
67
  while current
69
- break unless current.respond_to?(:name)
70
-
71
- name = current.name
68
+ name = case current
69
+ when Canon::Xml::Node, Nokogiri::XML::Node
70
+ current.name
71
+ else
72
+ break
73
+ end
72
74
  break if name.nil? || name.empty?
73
75
 
74
- # Calculate position among siblings
75
76
  index = calculate_sibling_index(current, name)
76
77
  parts.unshift("#{name}[#{index}]")
77
78
 
78
- # Move to parent
79
- current = if current.respond_to?(:parent)
79
+ current = case current
80
+ when Canon::Xml::Node, Nokogiri::XML::Node
80
81
  current.parent
81
- elsif current.respond_to?(:parent_node)
82
- current.parent_node
82
+ else
83
+ break
83
84
  end
84
85
 
85
- # Stop at document root
86
- break if current.respond_to?(:document) && current == current.document
86
+ break if current.is_a?(Nokogiri::XML::Document) ||
87
+ current.is_a?(Canon::Xml::Nodes::RootNode)
87
88
  end
88
89
 
89
90
  parts.empty? ? "" : "/#{parts.join('/')}"
@@ -95,24 +96,22 @@ module Canon
95
96
  # @param name [String] Node name
96
97
  # @return [Integer] 1-based index
97
98
  def self.calculate_sibling_index(node, name)
98
- return 1 unless node.respond_to?(:parent) || node.respond_to?(:parent_node)
99
-
100
- parent = if node.respond_to?(:parent)
99
+ parent = case node
100
+ when Canon::Xml::Node, Nokogiri::XML::Node
101
101
  node.parent
102
- elsif node.respond_to?(:parent_node)
103
- node.parent_node
104
102
  end
105
103
 
106
104
  return 1 unless parent
107
105
 
108
- # Get siblings with same name
109
- siblings = if parent.respond_to?(:children)
106
+ siblings = case parent
107
+ when Canon::Xml::Node, Nokogiri::XML::Node
110
108
  parent.children.select do |n|
111
- n.respond_to?(:name) && n.name == name
112
- end
113
- elsif parent.respond_to?(:child_nodes)
114
- parent.child_nodes.select do |n|
115
- n.respond_to?(:name) && n.name == name
109
+ case n
110
+ when Canon::Xml::Node, Nokogiri::XML::Node
111
+ n.name == name
112
+ else
113
+ false
114
+ end
116
115
  end
117
116
  else
118
117
  [node]
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "paint" unless RUBY_ENGINE == "opal"
4
- require "yaml"
4
+ require "yaml" unless RUBY_ENGINE == "opal"
5
5
  require_relative "comparison"
6
6
  require_relative "diff/diff_block"
7
7
  require_relative "diff/diff_context"
@@ -135,16 +135,17 @@ module Canon
135
135
  end
136
136
 
137
137
  # Default character visualization map (loaded from YAML)
138
- DEFAULT_VISUALIZATION_MAP = character_map_data[:visualization_map].freeze
139
-
140
- # Character category map (loaded from YAML)
141
- CHARACTER_CATEGORY_MAP = character_map_data[:category_map].freeze
142
-
143
- # Category display names (loaded from YAML)
144
- CHARACTER_CATEGORY_NAMES = character_map_data[:category_names].freeze
145
-
146
- # Character metadata including names (loaded from YAML)
147
- CHARACTER_METADATA = character_map_data[:character_metadata].freeze
138
+ if RUBY_ENGINE == "opal"
139
+ DEFAULT_VISUALIZATION_MAP = {}.freeze
140
+ CHARACTER_CATEGORY_MAP = {}.freeze
141
+ CHARACTER_CATEGORY_NAMES = {}.freeze
142
+ CHARACTER_METADATA = {}.freeze
143
+ else
144
+ DEFAULT_VISUALIZATION_MAP = character_map_data[:visualization_map].freeze
145
+ CHARACTER_CATEGORY_MAP = character_map_data[:category_map].freeze
146
+ CHARACTER_CATEGORY_NAMES = character_map_data[:category_names].freeze
147
+ CHARACTER_METADATA = character_map_data[:character_metadata].freeze
148
+ end
148
149
 
149
150
  # Map difference codes to human-readable descriptions
150
151
  DIFF_DESCRIPTIONS = {
@@ -757,6 +758,9 @@ module Canon
757
758
  output.join("\n")
758
759
  end
759
760
 
761
+ public :format_line_numbered_inputs, :format_raw_inputs,
762
+ :format_preprocessed_inputs
763
+
760
764
  # Build the final visualization map from various customization options
761
765
  #
762
766
  # @param visualization_map [Hash, nil] Complete custom visualization map
@@ -89,7 +89,7 @@ module Canon
89
89
  def self.build_from_nokogiri(nokogiri_doc)
90
90
  root = Canon::Xml::Nodes::RootNode.new
91
91
 
92
- if nokogiri_doc.respond_to?(:root) && nokogiri_doc.root
92
+ if nokogiri_doc.is_a?(Nokogiri::XML::Document) && nokogiri_doc.root
93
93
  # For Documents (HTML4, HTML5): process the root element
94
94
  root.add_child(build_element_node(nokogiri_doc.root))
95
95
 
@@ -124,6 +124,8 @@ module Canon
124
124
  end
125
125
  end
126
126
 
127
+ public :convert_operation
128
+
127
129
  # Convert INSERT operation to DiffNode
128
130
  #
129
131
  # @param operation [Operation] Insert operation
@@ -153,7 +155,7 @@ module Canon
153
155
 
154
156
  # Determine dimension for INSERT/DELETE operations based on node type
155
157
  def dimension_for_insert_delete(tree_node)
156
- label = tree_node.respond_to?(:label) ? tree_node.label : nil
158
+ label = tree_node.is_a?(Canon::TreeDiff::Core::TreeNode) ? tree_node.label : nil
157
159
  return :comments if label == "comment"
158
160
 
159
161
  :element_structure
@@ -359,7 +361,7 @@ module Canon
359
361
  def extract_source_node(tree_node)
360
362
  return nil if tree_node.nil?
361
363
 
362
- tree_node.respond_to?(:source_node) ? tree_node.source_node : tree_node
364
+ tree_node.is_a?(Canon::TreeDiff::Core::TreeNode) ? tree_node.source_node : tree_node
363
365
  end
364
366
 
365
367
  # Determine if a diff is normative based on match options
@@ -383,12 +385,10 @@ module Canon
383
385
  return false if node.nil?
384
386
 
385
387
  # Get element name from node
386
- element_name = if node.respond_to?(:label)
387
- node.label # TreeNode
388
- elsif node.respond_to?(:name)
389
- node.name # Nokogiri node
388
+ element_name = if node.is_a?(Canon::TreeDiff::Core::TreeNode)
389
+ node.label
390
390
  else
391
- return false
391
+ Canon::Comparison::NodeInspector.name(node)
392
392
  end
393
393
 
394
394
  # Check if it's in our metadata elements list
@@ -600,6 +600,10 @@ module Canon
600
600
  depth
601
601
  end
602
602
 
603
+ public :normalize_text, :calculate_depth, :text_similarity,
604
+ :extract_text_content, :collect_all_nodes, :nodes_identical?,
605
+ :detect_changes
606
+
603
607
  # Check if a node is in a whitespace-sensitive context
604
608
  #
605
609
  # HTML elements where whitespace is significant: <pre>, <code>, <textarea>, <script>, <style>
@@ -29,17 +29,14 @@ module Canon
29
29
  line = nil
30
30
  column = nil
31
31
 
32
- # Try to extract line/column from error message
33
- if error.respond_to?(:line)
32
+ if error.is_a?(Nokogiri::XML::SyntaxError)
34
33
  line = error.line
34
+ column = error.column
35
35
  elsif error.message =~ /line[:\s]+(\d+)/i
36
36
  line = ::Regexp.last_match(1).to_i
37
- end
38
-
39
- if error.respond_to?(:column)
40
- column = error.column
41
- elsif error.message =~ /column[:\s]+(\d+)/i
42
- column = ::Regexp.last_match(1).to_i
37
+ if error.message =~ /column[:\s]+(\d+)/i
38
+ column = ::Regexp.last_match(1).to_i
39
+ end
43
40
  end
44
41
 
45
42
  { line: line, column: column }
@@ -94,13 +94,8 @@ module Canon
94
94
  #
95
95
  # @param error [Nokogiri::XML::SyntaxError] The syntax error
96
96
  # @return [String, nil] Additional details about the error
97
- def self.extract_details(error)
98
- return nil unless error.respond_to?(:errors)
99
-
100
- details = error.errors.map(&:message).reject do |msg|
101
- msg == error.message
102
- end
103
- details.join("; ") unless details.empty?
97
+ def self.extract_details(_error)
98
+ nil
104
99
  end
105
100
 
106
101
  # Build error details from multiple errors
@@ -38,13 +38,8 @@ module Canon
38
38
  #
39
39
  # @param error [Nokogiri::XML::SyntaxError] The syntax error
40
40
  # @return [String, nil] Additional details about the error
41
- def self.extract_details(error)
42
- return nil unless error.respond_to?(:errors)
43
-
44
- details = error.errors.map(&:message).reject do |msg|
45
- msg == error.message
46
- end
47
- details.join("; ") unless details.empty?
41
+ def self.extract_details(_error)
42
+ nil
48
43
  end
49
44
 
50
45
  private_class_method :extract_details
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.2.9"
4
+ VERSION = "0.2.11"
5
5
  end
@@ -139,7 +139,7 @@ module Canon
139
139
  def self.build_from_nokogiri(nokogiri_doc, preserve_whitespace: false)
140
140
  root = Nodes::RootNode.new
141
141
 
142
- if nokogiri_doc.respond_to?(:root) && nokogiri_doc.root
142
+ if nokogiri_doc.is_a?(Nokogiri::XML::Document) && nokogiri_doc.root
143
143
  root.add_child(build_element_node(nokogiri_doc.root,
144
144
  preserve_whitespace: preserve_whitespace))
145
145
  nokogiri_doc.children.each do |child|
@@ -275,7 +275,7 @@ preserve_whitespace: false)
275
275
  def self.build_from_moxml(moxml_doc, preserve_whitespace: false)
276
276
  root = Nodes::RootNode.new
277
277
 
278
- if moxml_doc.respond_to?(:root) && moxml_doc.root
278
+ if moxml_doc.is_a?(Moxml::Document) && moxml_doc.root
279
279
  root.add_child(build_moxml_element_node(moxml_doc.root,
280
280
  preserve_whitespace: preserve_whitespace))
281
281
  end
@@ -327,7 +327,7 @@ preserve_whitespace: false)
327
327
  element.add_namespace(ns_node)
328
328
  end
329
329
 
330
- unless element.namespaces.any? do |n|
330
+ unless element.namespace_nodes.any? do |n|
331
331
  n.prefix == "xml"
332
332
  end
333
333
  element.add_namespace(Nodes::NamespaceNode.new(
@@ -348,7 +348,7 @@ preserve_whitespace: false)
348
348
  end
349
349
 
350
350
  def self.build_moxml_text_node(moxml_text, preserve_whitespace: false)
351
- content = moxml_text.text
351
+ content = moxml_text.content
352
352
 
353
353
  if !preserve_whitespace && content.strip.empty? && moxml_text.parent.is_a?(Moxml::Element)
354
354
  return nil
@@ -358,7 +358,7 @@ preserve_whitespace: false)
358
358
  end
359
359
 
360
360
  def self.build_moxml_comment_node(moxml_comment)
361
- Nodes::CommentNode.new(value: moxml_comment.text)
361
+ Nodes::CommentNode.new(value: moxml_comment.content)
362
362
  end
363
363
 
364
364
  def self.build_moxml_pi_node(moxml_pi)
@@ -28,7 +28,7 @@ module Canon
28
28
  # For C14N, use strip_doctype: true to avoid DTD default attribute expansion:
29
29
  # root = SaxBuilder.parse(xml_string, strip_doctype: true)
30
30
  #
31
- class SaxBuilder < Nokogiri::XML::SAX::Document
31
+ class SaxBuilder < (RUBY_ENGINE == "opal" ? Object : Nokogiri::XML::SAX::Document)
32
32
  # Parse XML string and return Canon::Xml::Node tree
33
33
  #
34
34
  # @param xml_string [String] XML content to parse
@@ -43,9 +43,9 @@ module Canon
43
43
  # @param node [Moxml::Node] Node to check
44
44
  # @return [Boolean] true if node is whitespace-only and should be ignored
45
45
  def inter_element_whitespace?(node)
46
- return false unless node.respond_to?(:text?) && node.text?
46
+ return false unless node.is_a?(Nokogiri::XML::Text) || node.is_a?(Moxml::Text)
47
47
 
48
- text = node.respond_to?(:content) ? node.content.to_s : node.text.to_s
48
+ text = node.is_a?(Moxml::Text) ? node.content.to_s : node.content.to_s
49
49
  text.strip.empty?
50
50
  end
51
51
 
@@ -14,7 +14,7 @@ module Canon
14
14
  module XmlParsing
15
15
  class << self
16
16
  def moxml_context
17
- @moxml_context ||= Moxml.new(:oga)
17
+ @moxml_context ||= Moxml.new(RUBY_ENGINE == "opal" ? :rexml : :oga)
18
18
  end
19
19
 
20
20
  # --- Parsing ---
@@ -47,10 +47,15 @@ module Canon
47
47
  end
48
48
 
49
49
  # --- Type checks (backend-safe) ---
50
+ #
51
+ # Both Nokogiri and Moxml are loaded as dependencies. XmlBackend
52
+ # determines which is used for *parsing*, but nodes from either
53
+ # library may flow through comparison code (e.g. tests, format
54
+ # detection). Under Nokogiri backend, both types are checked.
50
55
 
51
56
  def document?(obj)
52
57
  if XmlBackend.nokogiri?
53
- obj.is_a?(Nokogiri::XML::Document)
58
+ obj.is_a?(Nokogiri::XML::Document) || obj.is_a?(Moxml::Document)
54
59
  else
55
60
  obj.is_a?(Moxml::Document)
56
61
  end
@@ -58,7 +63,7 @@ module Canon
58
63
 
59
64
  def xml_node?(obj)
60
65
  if XmlBackend.nokogiri?
61
- obj.is_a?(Nokogiri::XML::Node)
66
+ obj.is_a?(Nokogiri::XML::Node) || obj.is_a?(Moxml::Node)
62
67
  else
63
68
  obj.is_a?(Moxml::Node)
64
69
  end
@@ -66,7 +71,7 @@ module Canon
66
71
 
67
72
  def element?(node)
68
73
  if XmlBackend.nokogiri?
69
- node.is_a?(Nokogiri::XML::Element)
74
+ node.is_a?(Nokogiri::XML::Element) || node.is_a?(Moxml::Element)
70
75
  else
71
76
  node.is_a?(Moxml::Element)
72
77
  end
@@ -74,7 +79,7 @@ module Canon
74
79
 
75
80
  def text_node?(node)
76
81
  if XmlBackend.nokogiri?
77
- node.is_a?(Nokogiri::XML::Text)
82
+ node.is_a?(Nokogiri::XML::Text) || node.is_a?(Moxml::Text)
78
83
  else
79
84
  node.is_a?(Moxml::Text)
80
85
  end
@@ -82,7 +87,7 @@ module Canon
82
87
 
83
88
  def comment?(node)
84
89
  if XmlBackend.nokogiri?
85
- node.is_a?(Nokogiri::XML::Comment)
90
+ node.is_a?(Nokogiri::XML::Comment) || node.is_a?(Moxml::Comment)
86
91
  else
87
92
  node.is_a?(Moxml::Comment)
88
93
  end
@@ -90,7 +95,7 @@ module Canon
90
95
 
91
96
  def cdata?(node)
92
97
  if XmlBackend.nokogiri?
93
- node.is_a?(Nokogiri::XML::CDATA)
98
+ node.is_a?(Nokogiri::XML::CDATA) || node.is_a?(Moxml::Cdata)
94
99
  else
95
100
  node.is_a?(Moxml::Cdata)
96
101
  end
@@ -98,7 +103,7 @@ module Canon
98
103
 
99
104
  def processing_instruction?(node)
100
105
  if XmlBackend.nokogiri?
101
- node.is_a?(Nokogiri::XML::ProcessingInstruction)
106
+ node.is_a?(Nokogiri::XML::ProcessingInstruction) || node.is_a?(Moxml::ProcessingInstruction)
102
107
  else
103
108
  node.is_a?(Moxml::ProcessingInstruction)
104
109
  end
@@ -108,7 +113,7 @@ module Canon
108
113
  if XmlBackend.nokogiri?
109
114
  obj.is_a?(Nokogiri::XML::DocumentFragment)
110
115
  else
111
- obj.is_a?(Moxml::DocumentFragment)
116
+ false
112
117
  end
113
118
  end
114
119
 
@@ -142,7 +147,14 @@ module Canon
142
147
  if XmlBackend.nokogiri?
143
148
  node.is_a?(Nokogiri::XML::Node) ? node.content : node.to_s
144
149
  else
145
- node.is_a?(Moxml::Node) ? node.text : node.to_s
150
+ case node
151
+ when Moxml::Text, Moxml::Cdata, Moxml::Comment
152
+ node.content.to_s
153
+ when Moxml::Node
154
+ node.text.to_s
155
+ else
156
+ node.to_s
157
+ end
146
158
  end
147
159
  end
148
160
 
@@ -257,12 +269,12 @@ module Canon
257
269
  end
258
270
 
259
271
  def moxml_node_type(node)
260
- return :element if node.is_a?(Moxml::Element)
261
- return :text if node.is_a?(Moxml::Text)
262
- return :comment if node.is_a?(Moxml::Comment)
263
- return :cdata if node.is_a?(Moxml::Cdata)
264
- return :document if node.is_a?(Moxml::Document)
265
- return :processing_instruction if node.is_a?(Moxml::ProcessingInstruction)
272
+ return :element if node.element?
273
+ return :text if node.text?
274
+ return :comment if node.comment?
275
+ return :cdata if node.cdata?
276
+ return :document if node.document?
277
+ return :processing_instruction if node.processing_instruction?
266
278
 
267
279
  nil
268
280
  end