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
@@ -0,0 +1,309 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark/ips"
4
+ require "json"
5
+ require "securerandom"
6
+ require "stringio"
7
+ require "time"
8
+ require "yaml"
9
+
10
+ lib_path = File.expand_path(File.join(__dir__, "..", "..", "lib"))
11
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
12
+
13
+ require "canon"
14
+
15
+ begin
16
+ require "canon/xml/sax_builder"
17
+ SAX_AVAILABLE = true
18
+ rescue LoadError
19
+ SAX_AVAILABLE = false
20
+ end
21
+
22
+ # Standalone benchmark runner that emits JSON results on stdout.
23
+ #
24
+ # Used by performance_comparator.rb to run benchmarks in a clean Ruby
25
+ # process per branch — sharing no state with the comparator itself.
26
+ # This isolates each branch's Canon implementation from the other.
27
+ module Performance
28
+ REPORT_ID = SecureRandom.hex(4).freeze
29
+
30
+ DEFAULT_RUN_TIME = Integer(ENV.fetch("CANON_PERF_RUN_TIME", "5"))
31
+ DEFAULT_WARMUP = Integer(ENV.fetch("CANON_PERF_WARMUP", "2"))
32
+ DEFAULT_ITEMS = Integer(ENV.fetch("CANON_PERF_ITEMS", "50"))
33
+
34
+ BENCHMARKS = {
35
+ xml_parse_dom_simple: { category: "xml_parsing", name: "DOM (simple)" },
36
+ xml_parse_sax_simple: { category: "xml_parsing", name: "SAX (simple)" },
37
+ xml_parse_dom_large: { category: "xml_parsing", name: "DOM (large)" },
38
+ xml_parse_sax_large: { category: "xml_parsing", name: "SAX (large)" },
39
+ html_parse_simple: { category: "html_parsing", name: "Simple HTML" },
40
+ html_parse_complex: { category: "html_parsing", name: "Complex HTML" },
41
+ xml_compare_identical: { category: "xml_comparison", name: "Identical XML" },
42
+ xml_compare_similar: { category: "xml_comparison", name: "Similar XML" },
43
+ xml_compare_different: { category: "xml_comparison", name: "Different XML" },
44
+ html_compare_identical: { category: "html_comparison", name: "Identical HTML" },
45
+ html_compare_similar: { category: "html_comparison", name: "Similar HTML" },
46
+ html_compare_different: { category: "html_comparison", name: "Different HTML" },
47
+ xml_c14n_format: { category: "formatting", name: "XML C14N" },
48
+ json_format: { category: "formatting", name: "JSON" },
49
+ yaml_format: { category: "formatting", name: "YAML" },
50
+ }.freeze
51
+
52
+ module DataGenerator
53
+ DEFAULT_ITEMS = Performance::DEFAULT_ITEMS
54
+
55
+ class << self
56
+ def generate_xml(items: DEFAULT_ITEMS, depth: 1, with_namespaces: false,
57
+ with_attributes: true)
58
+ ns = with_namespaces ? 'xmlns:ns="http://example.org"' : ""
59
+ prefix = with_namespaces ? "ns:" : ""
60
+
61
+ build_xml_element(items, depth, prefix, with_attributes, ns)
62
+ end
63
+
64
+ def build_xml_element(items, depth, prefix, with_attrs, ns_decl)
65
+ attrs = with_attrs ? " id=\"#{rand(1000)}\" status=\"active\"" : ""
66
+ ns_attr = ns_decl.empty? ? "" : " #{ns_decl}"
67
+
68
+ if depth <= 1
69
+ children = Array.new(items) do |i|
70
+ inner_attrs = with_attrs ? " index=\"#{i}\"" : ""
71
+ "<#{prefix}item#{inner_attrs}>Item #{i} content with some text</#{prefix}item>"
72
+ end.join
73
+ "<#{prefix}root#{ns_attr}#{attrs}>#{children}</#{prefix}root>"
74
+ else
75
+ child = build_xml_element(items / 2, depth - 1, prefix, with_attrs,
76
+ "")
77
+ "<#{prefix}root#{ns_attr}#{attrs}>#{child}</#{prefix}root>"
78
+ end
79
+ end
80
+
81
+ def generate_html(items: DEFAULT_ITEMS, with_scripts: false,
82
+ with_tables: false)
83
+ scripts = if with_scripts
84
+ <<~HTML
85
+ <script type="text/javascript">
86
+ function test() { return true; }
87
+ </script>
88
+ <style>
89
+ body { margin: 0; }
90
+ </style>
91
+ HTML
92
+ else
93
+ ""
94
+ end
95
+
96
+ tables = if with_tables
97
+ rows = Array.new(items) do |i|
98
+ "<tr><td>Cell #{i}A</td><td>Cell #{i}B</td></tr>"
99
+ end.join
100
+ "<table>#{rows}</table>"
101
+ else
102
+ ""
103
+ end
104
+
105
+ list_items = Array.new(items) do |i|
106
+ "<li class=\"item-#{i}\">List item #{i} with bold text</li>"
107
+ end.join("\n ")
108
+
109
+ nav_count = [(items / 5), 2].max
110
+ nav_items = items.times.first(nav_count).map do |i|
111
+ "<li class=\"nav-#{i}\">Nav #{i}</li>"
112
+ end.join("\n ")
113
+
114
+ <<~HTML
115
+ <!DOCTYPE html>
116
+ <html lang="en">
117
+ <head>
118
+ <meta charset="UTF-8">
119
+ <title>Benchmark Document</title>
120
+ #{scripts}
121
+ </head>
122
+ <body>
123
+ <header>
124
+ <h1>Benchmark Test Document</h1>
125
+ <nav>
126
+ <ul>
127
+ #{nav_items}
128
+ </ul>
129
+ </nav>
130
+ </header>
131
+ <main>
132
+ <section id="content">
133
+ <p>This is a paragraph with emphasized and strong text.</p>
134
+ <ul>
135
+ #{list_items}
136
+ </ul>
137
+ #{tables}
138
+ </section>
139
+ </main>
140
+ <footer>
141
+ <p>Copyright 2024</p>
142
+ </footer>
143
+ </body>
144
+ </html>
145
+ HTML
146
+ end
147
+
148
+ def generate_json(items: DEFAULT_ITEMS)
149
+ data = {
150
+ metadata: { version: "1.0", generated: Time.now.iso8601 },
151
+ items: Array.new(items) do |i|
152
+ {
153
+ id: i,
154
+ name: "Item #{i}",
155
+ value: rand * 1000,
156
+ tags: ["tag1", "tag2", "tag#{i % 10}"],
157
+ nested: {
158
+ level1: { level2: { data: "deeply nested value #{i}" } },
159
+ },
160
+ }
161
+ end,
162
+ }
163
+ JSON.generate(data)
164
+ end
165
+
166
+ def generate_yaml(items: DEFAULT_ITEMS)
167
+ JSON.parse(generate_json(items: items)).to_yaml
168
+ end
169
+
170
+ def generate_large_xml(items: 500)
171
+ generate_xml(items: items, depth: 3, with_namespaces: true,
172
+ with_attributes: true)
173
+ end
174
+ end
175
+ end
176
+
177
+ class Report
178
+ def initialize(run_time: DEFAULT_RUN_TIME, warmup: DEFAULT_WARMUP,
179
+ items: DEFAULT_ITEMS)
180
+ @run_time = run_time
181
+ @warmup = warmup
182
+ @items = items
183
+ @results = {}
184
+ end
185
+
186
+ def run_all
187
+ BENCHMARKS.each do |method, meta|
188
+ next if method.to_s.start_with?("xml_parse_sax") && !SAX_AVAILABLE
189
+
190
+ label = "#{meta[:category]}: #{meta[:name]}"
191
+ @results[label] = run_benchmark(method)
192
+ end
193
+ @results
194
+ end
195
+
196
+ def as_json
197
+ {
198
+ report_id: REPORT_ID,
199
+ ruby_version: RUBY_VERSION,
200
+ platform: RUBY_PLATFORM,
201
+ run_time: @run_time,
202
+ warmup: @warmup,
203
+ items: @items,
204
+ sax_available: SAX_AVAILABLE,
205
+ benchmarks: @results,
206
+ }
207
+ end
208
+
209
+ private
210
+
211
+ def run_benchmark(method)
212
+ original_stdout = $stdout
213
+ $stdout = StringIO.new
214
+ begin
215
+ job = Benchmark::IPS::Job.new
216
+ job.config(time: @run_time, warmup: @warmup)
217
+ job.report("test") { dispatch(method) }
218
+ job.run
219
+
220
+ entry = job.full_report.entries.first
221
+ ensure
222
+ $stdout = original_stdout
223
+ end
224
+
225
+ samples = entry.stats.samples
226
+ return { lower: 0, upper: 0 } if samples.empty?
227
+
228
+ mean = samples.sum.to_f / samples.size
229
+ variance = samples.sum { |x| (x - mean)**2 } / (samples.size - 1)
230
+ std_dev = Math.sqrt(variance)
231
+ error_margin = std_dev / mean
232
+ error_pct = error_margin.round(4)
233
+
234
+ # Clamp lower bound to positive — for very fast operations the
235
+ # error margin can exceed the mean, producing nonsensical
236
+ # negative IPS values that break the regression comparison.
237
+ lower = (mean * (1 - error_pct)).round(4)
238
+ lower = (mean * 0.5).round(4) if lower <= 0
239
+ upper = (mean * (1 + error_pct)).round(4)
240
+
241
+ { lower: lower, upper: upper }
242
+ end
243
+
244
+ def dispatch(method)
245
+ case method
246
+ when :xml_parse_dom_simple
247
+ Canon::Xml::DataModel.from_xml(DataGenerator.generate_xml(items: @items))
248
+ when :xml_parse_sax_simple
249
+ Canon::Xml::SaxBuilder.parse(DataGenerator.generate_xml(items: @items))
250
+ when :xml_parse_dom_large
251
+ Canon::Xml::DataModel.from_xml(DataGenerator.generate_large_xml(items: @items * 5))
252
+ when :xml_parse_sax_large
253
+ Canon::Xml::SaxBuilder.parse(DataGenerator.generate_large_xml(items: @items * 5))
254
+ when :html_parse_simple
255
+ Canon.parse_html(DataGenerator.generate_html(items: @items))
256
+ when :html_parse_complex
257
+ Canon.parse_html(DataGenerator.generate_html(items: @items,
258
+ with_scripts: true,
259
+ with_tables: true))
260
+ when :xml_compare_identical
261
+ xml = DataGenerator.generate_xml(items: @items)
262
+ Canon::Comparison.equivalent?(xml, xml, format: :xml)
263
+ when :xml_compare_similar
264
+ Canon::Comparison.equivalent?(
265
+ DataGenerator.generate_xml(items: @items),
266
+ DataGenerator.generate_xml(items: @items),
267
+ format: :xml,
268
+ )
269
+ when :xml_compare_different
270
+ Canon::Comparison.equivalent?(
271
+ DataGenerator.generate_xml(items: @items),
272
+ DataGenerator.generate_xml(items: @items, with_namespaces: true),
273
+ format: :xml,
274
+ )
275
+ when :html_compare_identical
276
+ html = DataGenerator.generate_html(items: @items)
277
+ Canon::Comparison.equivalent?(html, html, format: :html)
278
+ when :html_compare_similar
279
+ Canon::Comparison.equivalent?(
280
+ DataGenerator.generate_html(items: @items),
281
+ DataGenerator.generate_html(items: @items),
282
+ format: :html,
283
+ )
284
+ when :html_compare_different
285
+ Canon::Comparison.equivalent?(
286
+ DataGenerator.generate_html(items: @items),
287
+ DataGenerator.generate_html(items: @items, with_tables: true),
288
+ format: :html,
289
+ )
290
+ when :xml_c14n_format
291
+ Canon.format_xml(DataGenerator.generate_xml(items: @items,
292
+ with_namespaces: true))
293
+ when :json_format
294
+ Canon.format_json(JSON.parse(DataGenerator.generate_json(items: @items)))
295
+ when :yaml_format
296
+ Canon.format_yaml(YAML.safe_load(DataGenerator.generate_yaml(items: @items),
297
+ permitted_classes: [Time]))
298
+ else
299
+ raise "Unknown benchmark: #{method}"
300
+ end
301
+ end
302
+ end
303
+ end
304
+
305
+ if $PROGRAM_NAME == __FILE__
306
+ report = Performance::Report.new
307
+ report.run_all
308
+ puts JSON.generate(report.as_json)
309
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.11
4
+ version: 0.2.12
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-05-29 00:00:00.000000000 Z
11
+ date: 2026-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -219,6 +219,7 @@ files:
219
219
  - lib/canon/cache.rb
220
220
  - lib/canon/cli.rb
221
221
  - lib/canon/color_detector.rb
222
+ - lib/canon/commands.rb
222
223
  - lib/canon/commands/diff_command.rb
223
224
  - lib/canon/commands/format_command.rb
224
225
  - lib/canon/comparison.rb
@@ -226,16 +227,11 @@ files:
226
227
  - lib/canon/comparison/child_realignment.rb
227
228
  - lib/canon/comparison/compare_profile.rb
228
229
  - lib/canon/comparison/comparison_result.rb
230
+ - lib/canon/comparison/diff_node_builder.rb
229
231
  - lib/canon/comparison/dimensions.rb
230
- - lib/canon/comparison/dimensions/attribute_order_dimension.rb
231
- - lib/canon/comparison/dimensions/attribute_presence_dimension.rb
232
- - lib/canon/comparison/dimensions/attribute_values_dimension.rb
233
- - lib/canon/comparison/dimensions/base_dimension.rb
234
- - lib/canon/comparison/dimensions/comments_dimension.rb
235
- - lib/canon/comparison/dimensions/element_position_dimension.rb
232
+ - lib/canon/comparison/dimensions/dimension.rb
233
+ - lib/canon/comparison/dimensions/dimension_set.rb
236
234
  - lib/canon/comparison/dimensions/registry.rb
237
- - lib/canon/comparison/dimensions/structural_whitespace_dimension.rb
238
- - lib/canon/comparison/dimensions/text_content_dimension.rb
239
235
  - lib/canon/comparison/format_detector.rb
240
236
  - lib/canon/comparison/html_comparator.rb
241
237
  - lib/canon/comparison/html_compare_profile.rb
@@ -249,8 +245,10 @@ files:
249
245
  - lib/canon/comparison/match_options/xml_resolver.rb
250
246
  - lib/canon/comparison/match_options/yaml_resolver.rb
251
247
  - lib/canon/comparison/node_inspector.rb
248
+ - lib/canon/comparison/pipeline.rb
252
249
  - lib/canon/comparison/profile_definition.rb
253
250
  - lib/canon/comparison/ruby_object_comparator.rb
251
+ - lib/canon/comparison/strategies.rb
254
252
  - lib/canon/comparison/strategies/base_match_strategy.rb
255
253
  - lib/canon/comparison/strategies/match_strategy_factory.rb
256
254
  - lib/canon/comparison/strategies/semantic_tree_match_strategy.rb
@@ -259,14 +257,15 @@ files:
259
257
  - lib/canon/comparison/xml_comparator/attribute_comparator.rb
260
258
  - lib/canon/comparison/xml_comparator/attribute_filter.rb
261
259
  - lib/canon/comparison/xml_comparator/child_comparison.rb
262
- - lib/canon/comparison/xml_comparator/diff_node_builder.rb
263
260
  - lib/canon/comparison/xml_comparator/namespace_comparator.rb
264
261
  - lib/canon/comparison/xml_comparator/node_parser.rb
265
262
  - lib/canon/comparison/xml_comparator/node_type_comparator.rb
263
+ - lib/canon/comparison/xml_comparator_helpers.rb
266
264
  - lib/canon/comparison/xml_node_comparison.rb
267
265
  - lib/canon/comparison/xml_parser.rb
268
266
  - lib/canon/comparison/yaml_comparator.rb
269
267
  - lib/canon/config.rb
268
+ - lib/canon/config/config_dsl.rb
270
269
  - lib/canon/config/env_provider.rb
271
270
  - lib/canon/config/env_schema.rb
272
271
  - lib/canon/config/override_resolver.rb
@@ -275,6 +274,7 @@ files:
275
274
  - lib/canon/config/profiles/metanorma_debug.yml
276
275
  - lib/canon/config/type_converter.rb
277
276
  - lib/canon/data_model.rb
277
+ - lib/canon/diff.rb
278
278
  - lib/canon/diff/diff_block.rb
279
279
  - lib/canon/diff/diff_block_builder.rb
280
280
  - lib/canon/diff/diff_char_range.rb
@@ -315,10 +315,12 @@ files:
315
315
  - lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb
316
316
  - lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb
317
317
  - lib/canon/diff_formatter/diff_detail_formatter/text_utils.rb
318
+ - lib/canon/diff_formatter/diff_detail_formatter_helpers.rb
318
319
  - lib/canon/diff_formatter/legend.rb
319
320
  - lib/canon/diff_formatter/pretty_diff_formatter.rb
320
321
  - lib/canon/diff_formatter/theme.rb
321
322
  - lib/canon/errors.rb
323
+ - lib/canon/formatters.rb
322
324
  - lib/canon/formatters/html4_formatter.rb
323
325
  - lib/canon/formatters/html5_formatter.rb
324
326
  - lib/canon/formatters/html_formatter.rb
@@ -328,8 +330,10 @@ files:
328
330
  - lib/canon/formatters/yaml_formatter.rb
329
331
  - lib/canon/html.rb
330
332
  - lib/canon/html/data_model.rb
333
+ - lib/canon/options.rb
331
334
  - lib/canon/options/cli_generator.rb
332
335
  - lib/canon/options/registry.rb
336
+ - lib/canon/pretty_printer.rb
333
337
  - lib/canon/pretty_printer/html.rb
334
338
  - lib/canon/pretty_printer/html_void_elements.rb
335
339
  - lib/canon/pretty_printer/json.rb
@@ -337,34 +341,41 @@ files:
337
341
  - lib/canon/pretty_printer/xml_normalized.rb
338
342
  - lib/canon/rspec_matchers.rb
339
343
  - lib/canon/tree_diff.rb
344
+ - lib/canon/tree_diff/adapters.rb
340
345
  - lib/canon/tree_diff/adapters/html_adapter.rb
341
346
  - lib/canon/tree_diff/adapters/json_adapter.rb
342
347
  - lib/canon/tree_diff/adapters/xml_adapter.rb
343
348
  - lib/canon/tree_diff/adapters/yaml_adapter.rb
349
+ - lib/canon/tree_diff/core.rb
344
350
  - lib/canon/tree_diff/core/attribute_comparator.rb
345
351
  - lib/canon/tree_diff/core/matching.rb
346
352
  - lib/canon/tree_diff/core/node_signature.rb
347
353
  - lib/canon/tree_diff/core/node_weight.rb
348
354
  - lib/canon/tree_diff/core/tree_node.rb
349
355
  - lib/canon/tree_diff/core/xml_entity_decoder.rb
356
+ - lib/canon/tree_diff/matchers.rb
350
357
  - lib/canon/tree_diff/matchers/hash_matcher.rb
351
358
  - lib/canon/tree_diff/matchers/similarity_matcher.rb
352
359
  - lib/canon/tree_diff/matchers/structural_propagator.rb
353
360
  - lib/canon/tree_diff/matchers/universal_matcher.rb
354
361
  - lib/canon/tree_diff/operation_converter.rb
362
+ - lib/canon/tree_diff/operation_converter_helpers.rb
355
363
  - lib/canon/tree_diff/operation_converter_helpers/metadata_enricher.rb
356
364
  - lib/canon/tree_diff/operation_converter_helpers/post_processor.rb
357
365
  - lib/canon/tree_diff/operation_converter_helpers/reason_builder.rb
358
366
  - lib/canon/tree_diff/operation_converter_helpers/update_change_handler.rb
367
+ - lib/canon/tree_diff/operations.rb
359
368
  - lib/canon/tree_diff/operations/operation.rb
360
369
  - lib/canon/tree_diff/operations/operation_detector.rb
361
370
  - lib/canon/tree_diff/tree_diff_integrator.rb
371
+ - lib/canon/validators.rb
362
372
  - lib/canon/validators/base_validator.rb
363
373
  - lib/canon/validators/html_validator.rb
364
374
  - lib/canon/validators/json_validator.rb
365
375
  - lib/canon/validators/xml_validator.rb
366
376
  - lib/canon/validators/yaml_validator.rb
367
377
  - lib/canon/version.rb
378
+ - lib/canon/xml.rb
368
379
  - lib/canon/xml/attribute_handler.rb
369
380
  - lib/canon/xml/c14n.rb
370
381
  - lib/canon/xml/character_encoder.rb
@@ -374,6 +385,7 @@ files:
374
385
  - lib/canon/xml/namespace_handler.rb
375
386
  - lib/canon/xml/namespace_helper.rb
376
387
  - lib/canon/xml/node.rb
388
+ - lib/canon/xml/nodes.rb
377
389
  - lib/canon/xml/nodes/attribute_node.rb
378
390
  - lib/canon/xml/nodes/comment_node.rb
379
391
  - lib/canon/xml/nodes/element_node.rb
@@ -392,6 +404,7 @@ files:
392
404
  - lib/tasks/performance.rake
393
405
  - lib/tasks/performance_comparator.rb
394
406
  - lib/tasks/performance_helpers.rb
407
+ - lib/tasks/performance_report.rb
395
408
  - lib/xml-c14n.rb
396
409
  - sig/xml/c14n.rbs
397
410
  homepage: https://github.com/lutaml/canon
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base_dimension"
4
-
5
- module Canon
6
- module Comparison
7
- module Dimensions
8
- # Attribute order dimension
9
- #
10
- # Handles comparison of attribute ordering.
11
- # Supports :strict and :ignore behaviors.
12
- #
13
- # Behaviors:
14
- # - :strict - Attributes must appear in the same order
15
- # - :ignore - Attribute order doesn't matter
16
- class AttributeOrderDimension < BaseDimension
17
- # Extract attribute order from a node
18
- #
19
- # @param node [Moxml::Node, Nokogiri::XML::Node] Node to extract from
20
- # @return [Array<Symbol>] Array of attribute names in order
21
- def extract_data(node)
22
- return [] unless node
23
-
24
- if Canon::XmlBackend.nokogiri?
25
- extract_from_nokogiri(node)
26
- else
27
- extract_from_moxml(node)
28
- end
29
- end
30
-
31
- # Strict attribute order comparison
32
- #
33
- # @param order1 [Array<Symbol>] First attribute order
34
- # @param order2 [Array<Symbol>] Second attribute order
35
- # @return [Boolean] true if attribute order is exactly the same
36
- def compare_strict(order1, order2) # rubocop:disable Naming/PredicateMethod
37
- order1 == order2
38
- end
39
-
40
- private
41
-
42
- # Extract attribute order from Moxml node
43
- #
44
- # @param node [Moxml::Node] Moxml node
45
- # @return [Array<Symbol>] Array of attribute names in order
46
- def extract_from_moxml(node)
47
- return [] unless node.node_type == :element
48
-
49
- node.attributes.map { |attr| attr.name.to_sym }
50
- end
51
-
52
- # Extract attribute order from Nokogiri node
53
- #
54
- # @param node [Nokogiri::XML::Node] Nokogiri node
55
- # @return [Array<Symbol>] Array of attribute names in order
56
- def extract_from_nokogiri(node)
57
- return [] unless node.node_type == Nokogiri::XML::Node::ELEMENT_NODE
58
-
59
- node.attribute_nodes.map { |attr| attr.name.to_sym }
60
- end
61
- end
62
- end
63
- end
64
- end
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base_dimension"
4
-
5
- module Canon
6
- module Comparison
7
- module Dimensions
8
- # Attribute presence dimension
9
- #
10
- # Handles comparison of attribute presence (which attributes exist).
11
- # Supports :strict and :ignore behaviors.
12
- #
13
- # Behaviors:
14
- # - :strict - Attribute names must match exactly
15
- # - :ignore - Skip attribute presence comparison
16
- class AttributePresenceDimension < BaseDimension
17
- # Extract attribute names from a node
18
- #
19
- # @param node [Moxml::Node, Nokogiri::XML::Node] Node to extract from
20
- # @return [Array<Symbol>] Array of attribute names
21
- def extract_data(node)
22
- return [] unless node
23
-
24
- if Canon::XmlBackend.nokogiri?
25
- extract_from_nokogiri(node)
26
- else
27
- extract_from_moxml(node)
28
- end
29
- end
30
-
31
- # Strict attribute presence comparison
32
- #
33
- # @param names1 [Array<Symbol>] First attribute names
34
- # @param names2 [Array<Symbol>] Second attribute names
35
- # @return [Boolean] true if attribute names are exactly equal
36
- def compare_strict(names1, names2) # rubocop:disable Naming/PredicateMethod
37
- names1.sort == names2.sort
38
- end
39
-
40
- private
41
-
42
- # Extract attribute names from Moxml node
43
- #
44
- # @param node [Moxml::Node] Moxml node
45
- # @return [Array<Symbol>] Array of attribute names
46
- def extract_from_moxml(node)
47
- return [] unless node.node_type == :element
48
-
49
- node.attributes.map { |attr| attr.name.to_sym }
50
- end
51
-
52
- # Extract attribute names from Nokogiri node
53
- #
54
- # @param node [Nokogiri::XML::Node] Nokogiri node
55
- # @return [Array<Symbol>] Array of attribute names
56
- def extract_from_nokogiri(node)
57
- return [] unless node.node_type == Nokogiri::XML::Node::ELEMENT_NODE
58
-
59
- node.attribute_nodes.map { |attr| attr.name.to_sym }
60
- end
61
- end
62
- end
63
- end
64
- end