canon 0.2.11 → 0.2.12

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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +12 -22
  3. data/Rakefile +5 -2
  4. data/lib/canon/cache.rb +3 -1
  5. data/lib/canon/cli.rb +0 -3
  6. data/lib/canon/commands/diff_command.rb +0 -6
  7. data/lib/canon/commands/format_command.rb +0 -4
  8. data/lib/canon/commands.rb +9 -0
  9. data/lib/canon/comparison/child_realignment.rb +0 -2
  10. data/lib/canon/comparison/compare_profile.rb +30 -36
  11. data/lib/canon/comparison/comparison_result.rb +0 -2
  12. data/lib/canon/comparison/diff_node_builder.rb +353 -0
  13. data/lib/canon/comparison/dimensions/dimension.rb +51 -0
  14. data/lib/canon/comparison/dimensions/dimension_set.rb +49 -0
  15. data/lib/canon/comparison/dimensions/registry.rb +101 -60
  16. data/lib/canon/comparison/dimensions.rb +15 -46
  17. data/lib/canon/comparison/html_comparator.rb +18 -141
  18. data/lib/canon/comparison/html_compare_profile.rb +15 -18
  19. data/lib/canon/comparison/json_comparator.rb +4 -165
  20. data/lib/canon/comparison/json_parser.rb +0 -2
  21. data/lib/canon/comparison/markup_comparator.rb +14 -210
  22. data/lib/canon/comparison/match_options/base_resolver.rb +18 -29
  23. data/lib/canon/comparison/match_options/json_resolver.rb +4 -28
  24. data/lib/canon/comparison/match_options/xml_resolver.rb +4 -45
  25. data/lib/canon/comparison/match_options/yaml_resolver.rb +4 -30
  26. data/lib/canon/comparison/match_options.rb +13 -88
  27. data/lib/canon/comparison/pipeline.rb +269 -0
  28. data/lib/canon/comparison/profile_definition.rb +0 -2
  29. data/lib/canon/comparison/ruby_object_comparator.rb +1 -1
  30. data/lib/canon/comparison/strategies/match_strategy_factory.rb +9 -58
  31. data/lib/canon/comparison/strategies/semantic_tree_match_strategy.rb +4 -11
  32. data/lib/canon/comparison/strategies.rb +16 -0
  33. data/lib/canon/comparison/xml_comparator/attribute_comparator.rb +0 -3
  34. data/lib/canon/comparison/xml_comparator/attribute_filter.rb +0 -3
  35. data/lib/canon/comparison/xml_comparator/child_comparison.rb +0 -6
  36. data/lib/canon/comparison/xml_comparator/namespace_comparator.rb +1 -6
  37. data/lib/canon/comparison/xml_comparator/node_parser.rb +0 -4
  38. data/lib/canon/comparison/xml_comparator.rb +4 -492
  39. data/lib/canon/comparison/xml_comparator_helpers.rb +21 -0
  40. data/lib/canon/comparison/xml_node_comparison.rb +4 -119
  41. data/lib/canon/comparison/yaml_comparator.rb +0 -3
  42. data/lib/canon/comparison.rb +143 -266
  43. data/lib/canon/config/config_dsl.rb +159 -0
  44. data/lib/canon/config/env_provider.rb +0 -3
  45. data/lib/canon/config/env_schema.rb +48 -58
  46. data/lib/canon/config/profile_loader.rb +0 -1
  47. data/lib/canon/config.rb +116 -468
  48. data/lib/canon/diff/diff_block_builder.rb +0 -2
  49. data/lib/canon/diff/diff_classifier.rb +0 -5
  50. data/lib/canon/diff/diff_context.rb +0 -2
  51. data/lib/canon/diff/diff_context_builder.rb +0 -2
  52. data/lib/canon/diff/diff_line_builder.rb +0 -3
  53. data/lib/canon/diff/diff_node_enricher.rb +0 -4
  54. data/lib/canon/diff/diff_node_mapper.rb +0 -4
  55. data/lib/canon/diff/diff_report_builder.rb +0 -4
  56. data/lib/canon/diff/formatting_detector.rb +0 -1
  57. data/lib/canon/diff/node_serializer.rb +0 -7
  58. data/lib/canon/diff.rb +39 -0
  59. data/lib/canon/diff_formatter/by_line/base_formatter.rb +4 -17
  60. data/lib/canon/diff_formatter/by_line/html_formatter.rb +7 -19
  61. data/lib/canon/diff_formatter/by_line/json_formatter.rb +0 -3
  62. data/lib/canon/diff_formatter/by_line/simple_formatter.rb +0 -3
  63. data/lib/canon/diff_formatter/by_line/xml_formatter.rb +7 -26
  64. data/lib/canon/diff_formatter/by_line/yaml_formatter.rb +0 -3
  65. data/lib/canon/diff_formatter/by_object/base_formatter.rb +8 -15
  66. data/lib/canon/diff_formatter/by_object/json_formatter.rb +0 -2
  67. data/lib/canon/diff_formatter/by_object/xml_formatter.rb +0 -2
  68. data/lib/canon/diff_formatter/by_object/yaml_formatter.rb +0 -2
  69. data/lib/canon/diff_formatter/debug_output.rb +0 -2
  70. data/lib/canon/diff_formatter/diff_detail_formatter/dimension_formatter.rb +24 -58
  71. data/lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb +0 -2
  72. data/lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb +1 -2
  73. data/lib/canon/diff_formatter/diff_detail_formatter/text_utils.rb +1 -7
  74. data/lib/canon/diff_formatter/diff_detail_formatter.rb +0 -7
  75. data/lib/canon/diff_formatter/diff_detail_formatter_helpers.rb +23 -0
  76. data/lib/canon/diff_formatter.rb +11 -9
  77. data/lib/canon/formatters/html4_formatter.rb +0 -2
  78. data/lib/canon/formatters/html5_formatter.rb +0 -2
  79. data/lib/canon/formatters/html_formatter.rb +0 -3
  80. data/lib/canon/formatters/json_formatter.rb +0 -1
  81. data/lib/canon/formatters/xml_formatter.rb +0 -4
  82. data/lib/canon/formatters/yaml_formatter.rb +0 -1
  83. data/lib/canon/formatters.rb +16 -0
  84. data/lib/canon/html/data_model.rb +0 -10
  85. data/lib/canon/html.rb +4 -3
  86. data/lib/canon/options/cli_generator.rb +0 -2
  87. data/lib/canon/options/registry.rb +0 -2
  88. data/lib/canon/options.rb +9 -0
  89. data/lib/canon/pretty_printer/html.rb +0 -1
  90. data/lib/canon/pretty_printer/xml_normalized.rb +0 -2
  91. data/lib/canon/pretty_printer.rb +12 -0
  92. data/lib/canon/tree_diff/adapters/html_adapter.rb +1 -1
  93. data/lib/canon/tree_diff/adapters.rb +14 -0
  94. data/lib/canon/tree_diff/core/attribute_comparator.rb +0 -6
  95. data/lib/canon/tree_diff/core/node_signature.rb +1 -1
  96. data/lib/canon/tree_diff/core/tree_node.rb +12 -5
  97. data/lib/canon/tree_diff/core.rb +17 -0
  98. data/lib/canon/tree_diff/matchers/hash_matcher.rb +0 -7
  99. data/lib/canon/tree_diff/matchers/similarity_matcher.rb +1 -5
  100. data/lib/canon/tree_diff/matchers/structural_propagator.rb +1 -5
  101. data/lib/canon/tree_diff/matchers.rb +15 -0
  102. data/lib/canon/tree_diff/operation_converter.rb +0 -8
  103. data/lib/canon/tree_diff/operation_converter_helpers/metadata_enricher.rb +2 -12
  104. data/lib/canon/tree_diff/operation_converter_helpers/post_processor.rb +13 -7
  105. data/lib/canon/tree_diff/operation_converter_helpers/reason_builder.rb +2 -2
  106. data/lib/canon/tree_diff/operation_converter_helpers/update_change_handler.rb +4 -6
  107. data/lib/canon/tree_diff/operation_converter_helpers.rb +18 -0
  108. data/lib/canon/tree_diff/operations/operation_detector.rb +2 -5
  109. data/lib/canon/tree_diff/operations.rb +13 -0
  110. data/lib/canon/tree_diff.rb +26 -27
  111. data/lib/canon/validators/base_validator.rb +0 -2
  112. data/lib/canon/validators/html_validator.rb +0 -1
  113. data/lib/canon/validators/json_validator.rb +0 -1
  114. data/lib/canon/validators/xml_validator.rb +0 -1
  115. data/lib/canon/validators/yaml_validator.rb +0 -1
  116. data/lib/canon/validators.rb +12 -0
  117. data/lib/canon/version.rb +1 -1
  118. data/lib/canon/xml/c14n.rb +0 -4
  119. data/lib/canon/xml/data_model.rb +0 -10
  120. data/lib/canon/xml/line_range_mapper.rb +0 -2
  121. data/lib/canon/xml/nodes/attribute_node.rb +0 -2
  122. data/lib/canon/xml/nodes/comment_node.rb +0 -2
  123. data/lib/canon/xml/nodes/element_node.rb +0 -2
  124. data/lib/canon/xml/nodes/namespace_node.rb +0 -2
  125. data/lib/canon/xml/nodes/processing_instruction_node.rb +0 -2
  126. data/lib/canon/xml/nodes/root_node.rb +0 -2
  127. data/lib/canon/xml/nodes/text_node.rb +0 -2
  128. data/lib/canon/xml/nodes.rb +19 -0
  129. data/lib/canon/xml/processor.rb +0 -5
  130. data/lib/canon/xml/sax_builder.rb +0 -7
  131. data/lib/canon/xml.rb +33 -0
  132. data/lib/canon/xml_backend.rb +50 -14
  133. data/lib/canon/xml_parsing.rb +4 -2
  134. data/lib/canon.rb +25 -15
  135. data/lib/tasks/performance.rake +0 -58
  136. data/lib/tasks/performance_comparator.rb +132 -65
  137. data/lib/tasks/performance_helpers.rb +4 -249
  138. data/lib/tasks/performance_report.rb +309 -0
  139. metadata +24 -11
  140. data/lib/canon/comparison/dimensions/attribute_order_dimension.rb +0 -64
  141. data/lib/canon/comparison/dimensions/attribute_presence_dimension.rb +0 -64
  142. data/lib/canon/comparison/dimensions/attribute_values_dimension.rb +0 -167
  143. data/lib/canon/comparison/dimensions/base_dimension.rb +0 -107
  144. data/lib/canon/comparison/dimensions/comments_dimension.rb +0 -117
  145. data/lib/canon/comparison/dimensions/element_position_dimension.rb +0 -86
  146. data/lib/canon/comparison/dimensions/structural_whitespace_dimension.rb +0 -115
  147. data/lib/canon/comparison/dimensions/text_content_dimension.rb +0 -102
  148. data/lib/canon/comparison/xml_comparator/diff_node_builder.rb +0 -300
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "diff_line"
4
3
  require "set"
5
4
 
6
5
  module Canon
@@ -159,8 +158,6 @@ module Canon
159
158
  # @param diff_lines [Array<DiffLine>] The diff lines to update
160
159
  # @param lcs_diffs [Array<Diff::LCS::Change>] The LCS changes
161
160
  def apply_block_formatting!(diff_lines, lcs_diffs)
162
- require_relative "formatting_detector"
163
-
164
161
  blocks = group_change_blocks(lcs_diffs)
165
162
 
166
163
  blocks.each do |block|
@@ -265,7 +262,6 @@ module Canon
265
262
  # @param line2 [String] Second line
266
263
  # @return [Boolean] true if formatting-only difference
267
264
  def formatting_only_line?(line1, line2)
268
- require_relative "formatting_detector"
269
265
  FormattingDetector.formatting_only?(line1, line2)
270
266
  end
271
267
 
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "diff_report"
4
- require_relative "diff_block_builder"
5
- require_relative "diff_context_builder"
6
-
7
3
  module Canon
8
4
  module Diff
9
5
  # Builds a complete DiffReport from DiffLines
@@ -99,7 +99,6 @@ module Canon
99
99
  def self.decode_xml_entities(text)
100
100
  return text unless text.include?("&")
101
101
 
102
- require_relative "../tree_diff/core/xml_entity_decoder"
103
102
  Canon::TreeDiff::Core::XmlEntityDecoder.decode_xml_entities(text)
104
103
  end
105
104
 
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../xml/data_model"
4
- require_relative "../xml/nodes/text_node"
5
- require_relative "../xml/nodes/comment_node"
6
- require_relative "../xml/nodes/element_node"
7
- require_relative "../xml/nodes/processing_instruction_node"
8
- require_relative "../xml/nodes/root_node"
9
-
10
3
  module Canon
11
4
  module Diff
12
5
  # Serializes nodes from different parsing libraries into canonical strings
data/lib/canon/diff.rb ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Canon
4
+ # Diff representation and pipeline construction.
5
+ #
6
+ # This namespace holds the structural artifacts produced by the diff
7
+ # pipeline — DiffNode, DiffLine, DiffBlock, DiffContext, DiffReport —
8
+ # plus the builders that assemble them (DiffNodeMapper, DiffLineBuilder,
9
+ # DiffBlockBuilder, DiffContextBuilder, DiffReportBuilder) and the
10
+ # supporting services (PathBuilder, NodeSerializer, DiffClassifier,
11
+ # FormattingDetector, SourceLocator, TextDecomposer, DiffCharRange,
12
+ # DiffNodeEnricher, XmlSerializationFormatter).
13
+ #
14
+ # All children are autoloaded from this file — never `require_relative`
15
+ # them from sibling files. Reference the constant and let autoload
16
+ # resolve it on first use.
17
+ module Diff
18
+ autoload :DiffBlock, "canon/diff/diff_block"
19
+ autoload :DiffBlockBuilder, "canon/diff/diff_block_builder"
20
+ autoload :DiffCharRange, "canon/diff/diff_char_range"
21
+ autoload :DiffClassifier, "canon/diff/diff_classifier"
22
+ autoload :DiffContext, "canon/diff/diff_context"
23
+ autoload :DiffContextBuilder, "canon/diff/diff_context_builder"
24
+ autoload :DiffLine, "canon/diff/diff_line"
25
+ autoload :DiffLineBuilder, "canon/diff/diff_line_builder"
26
+ autoload :DiffNode, "canon/diff/diff_node"
27
+ autoload :DiffNodeEnricher, "canon/diff/diff_node_enricher"
28
+ autoload :DiffNodeMapper, "canon/diff/diff_node_mapper"
29
+ autoload :DiffReport, "canon/diff/diff_report"
30
+ autoload :DiffReportBuilder, "canon/diff/diff_report_builder"
31
+ autoload :FormattingDetector, "canon/diff/formatting_detector"
32
+ autoload :NodeSerializer, "canon/diff/node_serializer"
33
+ autoload :PathBuilder, "canon/diff/path_builder"
34
+ autoload :SourceLocator, "canon/diff/source_locator"
35
+ autoload :TextDecomposer, "canon/diff/text_decomposer"
36
+ autoload :XmlSerializationFormatter,
37
+ "canon/diff/xml_serialization_formatter"
38
+ end
39
+ end
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "diff/lcs" unless RUBY_ENGINE == "opal"
4
4
  require "diff/lcs/hunk" unless RUBY_ENGINE == "opal"
5
- require_relative "../debug_output"
6
- require_relative "../theme"
7
5
 
8
6
  module Canon
9
7
  class DiffFormatter
@@ -22,10 +20,8 @@ module Canon
22
20
  def self.for_format(format, **options)
23
21
  case format
24
22
  when :xml
25
- require_relative "xml_formatter"
26
23
  XmlFormatter.new(**options)
27
24
  when :html, :html4, :html5
28
- require_relative "html_formatter"
29
25
  # Determine HTML version from format
30
26
  version = case format
31
27
  when :html5 then :html5
@@ -34,13 +30,10 @@ module Canon
34
30
  end
35
31
  HtmlFormatter.new(html_version: version, **options)
36
32
  when :json
37
- require_relative "json_formatter"
38
33
  JsonFormatter.new(**options)
39
34
  when :yaml
40
- require_relative "yaml_formatter"
41
35
  YamlFormatter.new(**options)
42
36
  else
43
- require_relative "simple_formatter"
44
37
  SimpleFormatter.new(**options)
45
38
  end
46
39
  end
@@ -279,7 +272,7 @@ module Canon
279
272
 
280
273
  differences.select do |diff|
281
274
  # Handle both DiffNode objects and legacy Hash format
282
- is_normative = if diff.respond_to?(:normative?)
275
+ is_normative = if diff.is_a?(Canon::Diff::DiffNode)
283
276
  diff.normative?
284
277
  elsif diff.is_a?(Hash) && diff.key?(:normative)
285
278
  diff[:normative]
@@ -394,8 +387,6 @@ module Canon
394
387
  # @param diffs [Array] LCS diff array
395
388
  # @return [Array<Canon::Diff::DiffBlock>] Array of diff blocks
396
389
  def identify_diff_blocks(diffs)
397
- require_relative "../../diff/diff_block"
398
-
399
390
  blocks = []
400
391
  current_start = nil
401
392
  current_types = []
@@ -467,8 +458,6 @@ module Canon
467
458
  # @return [Array<Canon::Diff::DiffContext>] Array of diff contexts
468
459
  def expand_contexts_with_context_lines(contexts, context_lines,
469
460
  total_lines)
470
- require_relative "../../diff/diff_context"
471
-
472
461
  contexts.map do |context|
473
462
  first_block = context.first
474
463
  last_block = context.last
@@ -736,10 +725,8 @@ module Canon
736
725
  next unless match.status == :matched
737
726
 
738
727
  [match.elem1, match.elem2].compact.each do |elem|
739
- next unless elem.respond_to?(:children)
740
-
741
728
  elem.children.each do |child|
742
- children.add(child) if child.respond_to?(:name)
729
+ children.add(child) if Canon::Comparison::NodeInspector.element_node?(child)
743
730
  end
744
731
  end
745
732
  end
@@ -778,8 +765,8 @@ module Canon
778
765
  return true if elements_with_semantic_diffs.include?(element)
779
766
 
780
767
  # Check all descendants
781
- if element.respond_to?(:children)
782
- element.children.any? do |child|
768
+ if Canon::Comparison::NodeInspector.element_node?(element)
769
+ Canon::Comparison::NodeInspector.children(element).any? do |child|
783
770
  has_semantic_diff_in_subtree?(child, elements_with_semantic_diffs)
784
771
  end
785
772
  else
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_formatter"
4
- require_relative "../legend"
5
3
  require "set"
6
4
 
7
5
  module Canon
@@ -53,11 +51,6 @@ module Canon
53
51
  return ""
54
52
  end
55
53
 
56
- require_relative "../../html/data_model"
57
- require_relative "../../xml/element_matcher"
58
- require_relative "../../xml/line_range_mapper"
59
- require_relative "../../pretty_printer/html"
60
-
61
54
  output = []
62
55
 
63
56
  begin
@@ -107,7 +100,6 @@ module Canon
107
100
  end
108
101
 
109
102
  output << ""
110
- require_relative "simple_formatter"
111
103
  simple = SimpleFormatter.new(
112
104
  use_color: @use_color,
113
105
  context_lines: @context_lines,
@@ -122,9 +114,6 @@ module Canon
122
114
 
123
115
  # Format using new DiffReportBuilder pipeline
124
116
  def format_with_pipeline(doc1, doc2)
125
- require_relative "../../diff/diff_node_mapper"
126
- require_relative "../../diff/diff_report_builder"
127
-
128
117
  # Layer 2: Map DiffNodes to DiffLines
129
118
  diff_lines = Canon::Diff::DiffNodeMapper.map(@differences, doc1, doc2)
130
119
 
@@ -350,15 +339,14 @@ module Canon
350
339
 
351
340
  # Second pass: skip children of elements with diffs
352
341
  elements_with_diffs.each do |elem|
353
- if elem.respond_to?(:parent)
354
- current = elem.parent
355
- while current
356
- if current.respond_to?(:name) && elements_with_diffs.include?(current)
357
- elements_to_skip.add(elem)
358
- break
359
- end
360
- current = current.respond_to?(:parent) ? current.parent : nil
342
+ current = Canon::Comparison::NodeInspector.parent(elem)
343
+ while current
344
+ if Canon::Comparison::NodeInspector.element_node?(current) &&
345
+ elements_with_diffs.include?(current)
346
+ elements_to_skip.add(elem)
347
+ break
361
348
  end
349
+ current = Canon::Comparison::NodeInspector.parent(current)
362
350
  end
363
351
  end
364
352
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_formatter"
4
- require_relative "../legend"
5
3
  require "strscan"
6
4
 
7
5
  module Canon
@@ -38,7 +36,6 @@ module Canon
38
36
  output << colorize(
39
37
  "Warning: JSON parsing failed (#{e.message}), using simple diff", :yellow
40
38
  )
41
- require_relative "simple_formatter"
42
39
  simple = SimpleFormatter.new(
43
40
  use_color: @use_color,
44
41
  context_lines: @context_lines,
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_formatter"
4
- require_relative "../legend"
5
-
6
3
  module Canon
7
4
  class DiffFormatter
8
5
  module ByLine
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_formatter"
4
- require_relative "../legend"
5
- require_relative "../../tree_diff/core/xml_entity_decoder"
6
3
  require "set"
7
4
  require "strscan"
8
5
 
@@ -44,10 +41,6 @@ module Canon
44
41
  return ""
45
42
  end
46
43
 
47
- require_relative "../../diff/diff_node_enricher"
48
- require_relative "../../diff/diff_line_builder"
49
- require_relative "../../diff/diff_report_builder"
50
-
51
44
  # Compute line number width BEFORE formatting
52
45
  compute_line_num_width(doc1, doc2)
53
46
 
@@ -201,10 +194,6 @@ module Canon
201
194
  return ""
202
195
  end
203
196
 
204
- require_relative "../../xml/data_model"
205
- require_relative "../../xml/element_matcher"
206
- require_relative "../../xml/line_range_mapper"
207
-
208
197
  output = []
209
198
 
210
199
  begin
@@ -247,7 +236,6 @@ module Canon
247
236
  end
248
237
 
249
238
  output << ""
250
- require_relative "simple_formatter"
251
239
  simple = SimpleFormatter.new(
252
240
  use_color: @use_color,
253
241
  context_lines: @context_lines,
@@ -622,15 +610,14 @@ module Canon
622
610
 
623
611
  # Second pass: skip children of elements with diffs
624
612
  elements_with_diffs.each do |elem|
625
- if elem.respond_to?(:parent)
626
- current = elem.parent
627
- while current
628
- if current.respond_to?(:name) && elements_with_diffs.include?(current)
629
- elements_to_skip.add(elem)
630
- break
631
- end
632
- current = current.respond_to?(:parent) ? current.parent : nil
613
+ current = Canon::Comparison::NodeInspector.parent(elem)
614
+ while current
615
+ if Canon::Comparison::NodeInspector.element_node?(current) &&
616
+ elements_with_diffs.include?(current)
617
+ elements_to_skip.add(elem)
618
+ break
633
619
  end
620
+ current = Canon::Comparison::NodeInspector.parent(current)
634
621
  end
635
622
  end
636
623
 
@@ -721,8 +708,6 @@ module Canon
721
708
 
722
709
  # Identify contiguous diff blocks
723
710
  def identify_diff_blocks(diffs)
724
- require_relative "../../diff/diff_block"
725
-
726
711
  blocks = []
727
712
  current_start = nil
728
713
  current_types = []
@@ -784,8 +769,6 @@ module Canon
784
769
  # Expand contexts with context lines
785
770
  def expand_contexts_with_context_lines(contexts, context_lines,
786
771
  total_lines)
787
- require_relative "../../diff/diff_context"
788
-
789
772
  contexts.map do |context|
790
773
  first_block = context.first
791
774
  last_block = context.last
@@ -803,8 +786,6 @@ module Canon
803
786
 
804
787
  # Format a context
805
788
  def format_context(context, diffs, base_line1, base_line2)
806
- require_relative "../../diff/formatting_detector"
807
-
808
789
  # Pre-compute block-level formatting for multi-line changes
809
790
  formatting_indices = detect_block_formatting(context, diffs)
810
791
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_formatter"
4
- require_relative "../legend"
5
3
  require "strscan"
6
4
 
7
5
  module Canon
@@ -37,7 +35,6 @@ module Canon
37
35
  output << colorize(
38
36
  "Warning: YAML parsing failed (#{e.message}), using simple diff", :yellow
39
37
  )
40
- require_relative "simple_formatter"
41
38
  simple = SimpleFormatter.new(
42
39
  use_color: @use_color,
43
40
  context_lines: @context_lines,
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "set"
4
- require_relative "../theme"
5
4
 
6
5
  module Canon
7
6
  class DiffFormatter
@@ -76,19 +75,16 @@ show_diffs: :all, theme: nil)
76
75
  show_diffs: :all, theme: nil)
77
76
  case format
78
77
  when :xml, :html
79
- require_relative "xml_formatter"
80
78
  XmlFormatter.new(use_color: use_color,
81
79
  visualization_map: visualization_map,
82
80
  show_diffs: show_diffs,
83
81
  theme: theme)
84
82
  when :json
85
- require_relative "json_formatter"
86
83
  JsonFormatter.new(use_color: use_color,
87
84
  visualization_map: visualization_map,
88
85
  show_diffs: show_diffs,
89
86
  theme: theme)
90
87
  when :yaml
91
- require_relative "yaml_formatter"
92
88
  YamlFormatter.new(use_color: use_color,
93
89
  visualization_map: visualization_map,
94
90
  show_diffs: show_diffs,
@@ -221,23 +217,20 @@ show_diffs: :all, theme: nil)
221
217
  current = node
222
218
  visited = Set.new
223
219
 
224
- while current.respond_to?(:name)
225
- # Prevent infinite loops by tracking visited nodes
220
+ while current
226
221
  break if visited.include?(current.object_id)
227
222
 
228
223
  visited << current.object_id
229
224
 
230
- parts.unshift(current.name) if current.name
225
+ break if Canon::Comparison::NodeInspector.document?(current) ||
226
+ Canon::Comparison::NodeInspector.document_fragment?(current)
227
+
228
+ name = Canon::Comparison::NodeInspector.name(current)
229
+ break if name.nil?
231
230
 
232
- # Stop at document or fragment roots
233
- break if current.is_a?(Nokogiri::XML::Document) ||
234
- current.is_a?(Nokogiri::HTML4::Document) ||
235
- current.is_a?(Nokogiri::HTML5::Document) ||
236
- current.is_a?(Nokogiri::XML::DocumentFragment) ||
237
- current.is_a?(Nokogiri::HTML4::DocumentFragment) ||
238
- current.is_a?(Nokogiri::HTML5::DocumentFragment)
231
+ parts.unshift(name) unless name.empty?
239
232
 
240
- current = current.parent if current.respond_to?(:parent)
233
+ current = Canon::Comparison::NodeInspector.parent(current)
241
234
  end
242
235
 
243
236
  parts.empty? ? diff_node.dimension.to_s : parts.join(".")
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_formatter"
4
-
5
3
  module Canon
6
4
  class DiffFormatter
7
5
  module ByObject
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_formatter"
4
-
5
3
  module Canon
6
4
  class DiffFormatter
7
5
  module ByObject
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "json_formatter"
4
-
5
3
  module Canon
6
4
  class DiffFormatter
7
5
  module ByObject
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "diff_detail_formatter"
4
-
5
3
  module Canon
6
4
  class DiffFormatter
7
5
  # Verbose diff output helper for CANON_VERBOSE mode
@@ -53,9 +53,6 @@ expand_difference: false)
53
53
  # @param use_color [Boolean] Whether to use colors
54
54
  # @return [Array] Tuple of [detail1, detail2, changes]
55
55
  def self.format_namespace_uri_details(diff, use_color)
56
- require_relative "color_helper"
57
- require_relative "node_utils"
58
-
59
56
  node1 = extract_node1(diff)
60
57
  node2 = extract_node2(diff)
61
58
 
@@ -87,10 +84,6 @@ expand_difference: false)
87
84
  # @param use_color [Boolean] Whether to use colors
88
85
  # @return [Array] Tuple of [detail1, detail2, changes]
89
86
  def self.format_namespace_declarations_details(diff, use_color)
90
- require_relative "color_helper"
91
- require_relative "node_utils"
92
- require_relative "text_utils"
93
-
94
87
  node1 = extract_node1(diff)
95
88
  node2 = extract_node2(diff)
96
89
 
@@ -174,9 +167,6 @@ expand_difference: false)
174
167
  # @param use_color [Boolean] Whether to use colors
175
168
  # @return [Array] Tuple of [detail1, detail2, changes]
176
169
  def self.format_element_structure_details(diff, use_color)
177
- require_relative "color_helper"
178
- require_relative "node_utils"
179
-
180
170
  node1 = extract_node1(diff)
181
171
  node2 = extract_node2(diff)
182
172
 
@@ -236,9 +226,6 @@ expand_difference: false)
236
226
  # @param use_color [Boolean] Whether to use colors
237
227
  # @return [Array] Tuple of [detail1, detail2, changes]
238
228
  def self.format_attribute_presence_details(diff, use_color)
239
- require_relative "color_helper"
240
- require_relative "node_utils"
241
-
242
229
  node1 = extract_node1(diff)
243
230
  node2 = extract_node2(diff)
244
231
 
@@ -291,9 +278,6 @@ expand_difference: false)
291
278
  # @param use_color [Boolean] Whether to use colors
292
279
  # @return [Array] Tuple of [detail1, detail2, changes]
293
280
  def self.format_attribute_values_details(diff, use_color)
294
- require_relative "color_helper"
295
- require_relative "node_utils"
296
-
297
281
  node1 = extract_node1(diff)
298
282
  node2 = extract_node2(diff)
299
283
 
@@ -330,9 +314,6 @@ expand_difference: false)
330
314
  # @param use_color [Boolean] Whether to use colors
331
315
  # @return [Array] Tuple of [detail1, detail2, changes]
332
316
  def self.format_attribute_order_details(diff, use_color)
333
- require_relative "color_helper"
334
- require_relative "node_utils"
335
-
336
317
  node1 = extract_node1(diff)
337
318
  node2 = extract_node2(diff)
338
319
 
@@ -361,10 +342,6 @@ expand_difference: false)
361
342
  # @param compact [Boolean] Whether to serialize element nodes as compact XML
362
343
  # @return [Array] Tuple of [detail1, detail2, changes]
363
344
  def self.format_text_content_details(diff, use_color, compact: false)
364
- require_relative "color_helper"
365
- require_relative "node_utils"
366
- require_relative "text_utils"
367
-
368
345
  node1 = extract_node1(diff)
369
346
  node2 = extract_node2(diff)
370
347
 
@@ -465,10 +442,6 @@ expand_difference: false)
465
442
  # @param use_color [Boolean] Whether to apply ANSI colours
466
443
  # @return [Array<String>] Tuple of [detail1, detail2, changes]
467
444
  def self.format_text_content_one_sided(node1, node2, use_color)
468
- require_relative "color_helper"
469
- require_relative "node_utils"
470
- require_relative "text_utils"
471
-
472
445
  present = node1 || node2
473
446
 
474
447
  # Defensive: if a one-sided text-content diff carries an
@@ -515,10 +488,6 @@ expand_difference: false)
515
488
  # @param use_color [Boolean] Whether to use colors
516
489
  # @return [Array] Tuple of [detail1, detail2, changes]
517
490
  def self.format_whitespace_adjacency_details(diff, use_color)
518
- require_relative "color_helper"
519
- require_relative "node_utils"
520
- require_relative "text_utils"
521
-
522
491
  node1 = extract_node1(diff)
523
492
  node2 = extract_node2(diff)
524
493
 
@@ -568,10 +537,6 @@ expand_difference: false)
568
537
  # @param use_color [Boolean] Whether to use colors
569
538
  # @return [Array] Tuple of [detail1, detail2, changes]
570
539
  def self.format_structural_whitespace_details(diff, use_color)
571
- require_relative "color_helper"
572
- require_relative "node_utils"
573
- require_relative "text_utils"
574
-
575
540
  node1 = extract_node1(diff)
576
541
  node2 = extract_node2(diff)
577
542
 
@@ -594,9 +559,6 @@ expand_difference: false)
594
559
  # @param use_color [Boolean] Whether to use colors
595
560
  # @return [Array] Tuple of [detail1, detail2, changes]
596
561
  def self.format_comments_details(diff, use_color)
597
- require_relative "color_helper"
598
- require_relative "node_utils"
599
-
600
562
  node1 = extract_node1(diff)
601
563
  node2 = extract_node2(diff)
602
564
 
@@ -619,8 +581,6 @@ expand_difference: false)
619
581
  # @param use_color [Boolean] Whether to use colors
620
582
  # @return [Array] Tuple of [detail1, detail2, changes]
621
583
  def self.format_hash_diff_details(diff, use_color)
622
- require_relative "color_helper"
623
-
624
584
  detail1 = if diff.is_a?(Hash) && diff[:value1]
625
585
  ColorHelper.colorize(format_json_value(diff[:value1]),
626
586
  :red, use_color)
@@ -653,9 +613,6 @@ expand_difference: false)
653
613
  # @param compact [Boolean] Whether to serialize element nodes as compact XML
654
614
  # @return [Array] Tuple of [detail1, detail2, changes]
655
615
  def self.format_fallback_details(diff, use_color, compact: false)
656
- require_relative "color_helper"
657
- require_relative "node_utils"
658
-
659
616
  node1 = extract_node1(diff)
660
617
  node2 = extract_node2(diff)
661
618
 
@@ -749,8 +706,7 @@ expand_difference: false)
749
706
 
750
707
  declarations = {}
751
708
 
752
- # Handle Canon::Xml::Node (uses namespace_nodes)
753
- if node.respond_to?(:namespace_nodes)
709
+ if node.is_a?(Canon::Xml::Node)
754
710
  node.namespace_nodes.each do |ns|
755
711
  next if ns.prefix == "xml" && ns.uri == "http://www.w3.org/XML/1998/namespace"
756
712
 
@@ -760,30 +716,28 @@ expand_difference: false)
760
716
  return declarations
761
717
  end
762
718
 
763
- # Handle Nokogiri/Moxml nodes (use attributes)
764
- raw_attrs = node.respond_to?(:attribute_nodes) ? node.attribute_nodes : node.attributes
719
+ # Handle Nokogiri/Moxml nodes (use attribute_nodes or attributes).
720
+ raw_attrs = if node.is_a?(Nokogiri::XML::Node)
721
+ node.attribute_nodes
722
+ else
723
+ node.attributes
724
+ end
765
725
 
766
726
  if raw_attrs.is_a?(Array)
767
727
  raw_attrs.each do |attr|
768
- name = attr.respond_to?(:name) ? attr.name : attr.to_s
769
- value = attr.respond_to?(:value) ? attr.value : attr.to_s
728
+ name = attr.is_a?(Nokogiri::XML::Attr) ? attr.name : attr.to_s
729
+ value = attr.is_a?(Nokogiri::XML::Attr) ? attr.value : attr.to_s
770
730
 
771
731
  if namespace_declaration?(name)
772
732
  prefix = name == "xmlns" ? "" : name.split(":", 2)[1]
773
733
  declarations[prefix] = value
774
734
  end
775
735
  end
776
- elsif raw_attrs.respond_to?(:each)
736
+ elsif raw_attrs.is_a?(Hash)
777
737
  raw_attrs.each do |key, val|
778
- name = if key.is_a?(String)
779
- key
780
- else
781
- (key.respond_to?(:name) ? key.name : key.to_s)
782
- end
783
- value = if val.respond_to?(:value)
738
+ name = hash_key_to_name(key)
739
+ value = if val.is_a?(Nokogiri::XML::Attr)
784
740
  val.value
785
- elsif val.respond_to?(:content)
786
- val.content
787
741
  else
788
742
  val.to_s
789
743
  end
@@ -805,6 +759,18 @@ expand_difference: false)
805
759
  def self.namespace_declaration?(name)
806
760
  name == "xmlns" || name.to_s.start_with?("xmlns:")
807
761
  end
762
+
763
+ # Coerce a hash key (from a backend-supplied attribute hash) into a
764
+ # plain String attribute name.
765
+ #
766
+ # @param key [String, Nokogiri::XML::Attr, Object]
767
+ # @return [String]
768
+ def self.hash_key_to_name(key)
769
+ return key if key.is_a?(String)
770
+ return key.name if key.is_a?(Nokogiri::XML::Attr)
771
+
772
+ key.to_s
773
+ end
808
774
  end
809
775
  end
810
776
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../xml/namespace_helper"
4
-
5
3
  module Canon
6
4
  class DiffFormatter
7
5
  module DiffDetailFormatterHelpers