canon 0.1.7 → 0.1.9

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +69 -92
  3. data/README.adoc +13 -13
  4. data/docs/.lycheeignore +69 -0
  5. data/docs/Gemfile +1 -0
  6. data/docs/_config.yml +90 -1
  7. data/docs/advanced/diff-classification.adoc +82 -2
  8. data/docs/advanced/extending-canon.adoc +193 -0
  9. data/docs/features/match-options/index.adoc +239 -1
  10. data/docs/internals/diffnode-enrichment.adoc +611 -0
  11. data/docs/internals/index.adoc +251 -0
  12. data/docs/lychee.toml +13 -6
  13. data/docs/understanding/architecture.adoc +749 -33
  14. data/docs/understanding/comparison-pipeline.adoc +122 -0
  15. data/lib/canon/cache.rb +129 -0
  16. data/lib/canon/comparison/dimensions/attribute_order_dimension.rb +68 -0
  17. data/lib/canon/comparison/dimensions/attribute_presence_dimension.rb +68 -0
  18. data/lib/canon/comparison/dimensions/attribute_values_dimension.rb +171 -0
  19. data/lib/canon/comparison/dimensions/base_dimension.rb +107 -0
  20. data/lib/canon/comparison/dimensions/comments_dimension.rb +121 -0
  21. data/lib/canon/comparison/dimensions/element_position_dimension.rb +90 -0
  22. data/lib/canon/comparison/dimensions/registry.rb +77 -0
  23. data/lib/canon/comparison/dimensions/structural_whitespace_dimension.rb +119 -0
  24. data/lib/canon/comparison/dimensions/text_content_dimension.rb +96 -0
  25. data/lib/canon/comparison/dimensions.rb +54 -0
  26. data/lib/canon/comparison/format_detector.rb +87 -0
  27. data/lib/canon/comparison/html_comparator.rb +70 -26
  28. data/lib/canon/comparison/html_compare_profile.rb +8 -2
  29. data/lib/canon/comparison/html_parser.rb +80 -0
  30. data/lib/canon/comparison/json_comparator.rb +12 -0
  31. data/lib/canon/comparison/json_parser.rb +19 -0
  32. data/lib/canon/comparison/markup_comparator.rb +293 -0
  33. data/lib/canon/comparison/match_options/base_resolver.rb +150 -0
  34. data/lib/canon/comparison/match_options/json_resolver.rb +82 -0
  35. data/lib/canon/comparison/match_options/xml_resolver.rb +151 -0
  36. data/lib/canon/comparison/match_options/yaml_resolver.rb +87 -0
  37. data/lib/canon/comparison/match_options.rb +68 -463
  38. data/lib/canon/comparison/profile_definition.rb +149 -0
  39. data/lib/canon/comparison/ruby_object_comparator.rb +180 -0
  40. data/lib/canon/comparison/strategies/semantic_tree_match_strategy.rb +7 -10
  41. data/lib/canon/comparison/whitespace_sensitivity.rb +208 -0
  42. data/lib/canon/comparison/xml_comparator/attribute_comparator.rb +177 -0
  43. data/lib/canon/comparison/xml_comparator/attribute_filter.rb +136 -0
  44. data/lib/canon/comparison/xml_comparator/child_comparison.rb +197 -0
  45. data/lib/canon/comparison/xml_comparator/diff_node_builder.rb +115 -0
  46. data/lib/canon/comparison/xml_comparator/namespace_comparator.rb +186 -0
  47. data/lib/canon/comparison/xml_comparator/node_parser.rb +79 -0
  48. data/lib/canon/comparison/xml_comparator/node_type_comparator.rb +102 -0
  49. data/lib/canon/comparison/xml_comparator.rb +97 -684
  50. data/lib/canon/comparison/xml_node_comparison.rb +319 -0
  51. data/lib/canon/comparison/xml_parser.rb +19 -0
  52. data/lib/canon/comparison/yaml_comparator.rb +3 -3
  53. data/lib/canon/comparison.rb +265 -110
  54. data/lib/canon/diff/diff_classifier.rb +101 -2
  55. data/lib/canon/diff/diff_node.rb +32 -2
  56. data/lib/canon/diff/formatting_detector.rb +1 -1
  57. data/lib/canon/diff/node_serializer.rb +191 -0
  58. data/lib/canon/diff/path_builder.rb +143 -0
  59. data/lib/canon/diff_formatter/by_line/base_formatter.rb +251 -0
  60. data/lib/canon/diff_formatter/by_line/html_formatter.rb +6 -248
  61. data/lib/canon/diff_formatter/by_line/xml_formatter.rb +38 -229
  62. data/lib/canon/diff_formatter/diff_detail_formatter/color_helper.rb +30 -0
  63. data/lib/canon/diff_formatter/diff_detail_formatter/dimension_formatter.rb +579 -0
  64. data/lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb +121 -0
  65. data/lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb +253 -0
  66. data/lib/canon/diff_formatter/diff_detail_formatter/text_utils.rb +61 -0
  67. data/lib/canon/diff_formatter/diff_detail_formatter.rb +31 -1028
  68. data/lib/canon/diff_formatter.rb +1 -1
  69. data/lib/canon/rspec_matchers.rb +38 -9
  70. data/lib/canon/tree_diff/operation_converter.rb +92 -338
  71. data/lib/canon/tree_diff/operation_converter_helpers/metadata_enricher.rb +71 -0
  72. data/lib/canon/tree_diff/operation_converter_helpers/post_processor.rb +103 -0
  73. data/lib/canon/tree_diff/operation_converter_helpers/reason_builder.rb +168 -0
  74. data/lib/canon/tree_diff/operation_converter_helpers/update_change_handler.rb +188 -0
  75. data/lib/canon/version.rb +1 -1
  76. data/lib/canon/xml/data_model.rb +24 -13
  77. metadata +48 -2
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "match_options/base_resolver"
4
+ require_relative "match_options/xml_resolver"
5
+ require_relative "match_options/json_resolver"
6
+ require_relative "match_options/yaml_resolver"
7
+
3
8
  module Canon
4
9
  module Comparison
5
10
  # Matching Options for Canon Comparison
@@ -127,226 +132,38 @@ module Canon
127
132
  attribute_values
128
133
  element_position
129
134
  comments
130
- element_structure
131
- element_position
132
- element_hierarchy
133
135
  ].freeze
134
136
 
135
- # Format-specific defaults
136
- FORMAT_DEFAULTS = {
137
- html: {
138
- preprocessing: :rendered,
139
- text_content: :normalize,
140
- structural_whitespace: :normalize,
141
- attribute_presence: :strict,
142
- attribute_order: :ignore,
143
- attribute_values: :strict,
144
- element_position: :ignore,
145
- comments: :ignore,
146
- },
147
- xml: {
148
- preprocessing: :none,
149
- text_content: :strict,
150
- structural_whitespace: :strict,
151
- attribute_presence: :strict,
152
- attribute_order: :ignore,
153
- attribute_values: :strict,
154
- element_position: :strict,
155
- comments: :strict,
156
- },
157
- }.freeze
158
-
159
- # Predefined match profiles for XML/HTML
160
- MATCH_PROFILES = {
161
- # Strict: Match exactly as written in source (XML default)
162
- strict: {
163
- preprocessing: :none,
164
- text_content: :strict,
165
- structural_whitespace: :strict,
166
- attribute_presence: :strict,
167
- attribute_order: :strict,
168
- attribute_values: :strict,
169
- element_position: :strict,
170
- comments: :strict,
171
- },
172
-
173
- # Rendered: Match rendered output (HTML default)
174
- # Mimics CSS whitespace collapsing
175
- rendered: {
176
- preprocessing: :none,
177
- text_content: :normalize,
178
- structural_whitespace: :normalize,
179
- attribute_presence: :strict,
180
- attribute_order: :strict,
181
- attribute_values: :strict,
182
- element_position: :strict,
183
- comments: :ignore,
184
- },
185
-
186
- # HTML4: Match HTML4 rendered output
187
- # HTML4 rendering normalizes attribute whitespace
188
- html4: {
189
- preprocessing: :rendered,
190
- text_content: :normalize,
191
- structural_whitespace: :normalize,
192
- attribute_presence: :strict,
193
- attribute_order: :strict,
194
- attribute_values: :normalize,
195
- element_position: :ignore,
196
- comments: :ignore,
197
- },
198
-
199
- # HTML5: Match HTML5 rendered output (same as rendered)
200
- html5: {
201
- preprocessing: :rendered,
202
- text_content: :normalize,
203
- structural_whitespace: :normalize,
204
- attribute_presence: :strict,
205
- attribute_order: :strict,
206
- attribute_values: :strict,
207
- element_position: :ignore,
208
- comments: :ignore,
209
- },
210
-
211
- # Spec-friendly: Formatting doesn't matter
212
- # Uses :rendered preprocessing for HTML which normalizes via to_html
213
- spec_friendly: {
214
- preprocessing: :rendered,
215
- text_content: :normalize,
216
- structural_whitespace: :ignore,
217
- attribute_presence: :strict,
218
- attribute_order: :ignore,
219
- attribute_values: :normalize,
220
- element_position: :ignore,
221
- comments: :ignore,
222
- },
223
-
224
- # Content-only: Only content matters
225
- content_only: {
226
- preprocessing: :c14n,
227
- text_content: :normalize,
228
- structural_whitespace: :ignore,
229
- attribute_presence: :strict,
230
- attribute_order: :ignore,
231
- attribute_values: :normalize,
232
- element_position: :ignore,
233
- comments: :ignore,
234
- },
235
- }.freeze
137
+ # Expose FORMAT_DEFAULTS from XmlResolver (for backward compatibility)
138
+ FORMAT_DEFAULTS = MatchOptions::XmlResolver.const_get(:FORMAT_DEFAULTS)
139
+
140
+ # Expose MATCH_PROFILES from XmlResolver (for backward compatibility)
141
+ MATCH_PROFILES = MatchOptions::XmlResolver.const_get(:MATCH_PROFILES)
236
142
 
237
143
  class << self
238
- # Resolve match options with precedence handling
239
- #
240
- # Precedence order (highest to lowest):
241
- # 1. Explicit match parameter
242
- # 2. Profile from match_profile parameter
243
- # 3. Global configuration
244
- # 4. Format-specific defaults
245
- #
246
- # @param format [Symbol] Format type (:xml or :html)
247
- # @param match_profile [Symbol, nil] Profile name
248
- # @param match [Hash, nil] Explicit options per dimension
249
- # @param preprocessing [Symbol, nil] Preprocessing option
250
- # @param global_profile [Symbol, nil] Global configured profile
251
- # @param global_options [Hash, nil] Global configured options
252
- # @return [Hash] Resolved options for all dimensions
253
- def resolve(
254
- format:,
255
- match_profile: nil,
256
- match: nil,
257
- preprocessing: nil,
258
- global_profile: nil,
259
- global_options: nil
260
- )
261
- # Start with format-specific defaults
262
- options = FORMAT_DEFAULTS[format]&.dup || FORMAT_DEFAULTS[:xml].dup
263
-
264
- # Apply global profile if specified
265
- if global_profile
266
- profile_opts = get_profile_options(global_profile)
267
- options.merge!(profile_opts)
268
- end
269
-
270
- # Apply global options if specified
271
- if global_options
272
- validate_match_options!(global_options)
273
- options.merge!(global_options)
274
- end
275
-
276
- # Apply per-call profile if specified (overrides global)
277
- if match_profile
278
- profile_opts = get_profile_options(match_profile)
279
- options.merge!(profile_opts)
280
- end
281
-
282
- # Apply per-call preprocessing if specified (overrides profile)
283
- if preprocessing
284
- validate_preprocessing!(preprocessing)
285
- options[:preprocessing] = preprocessing
286
- end
287
-
288
- # Apply per-call explicit options if specified (highest priority)
289
- if match
290
- validate_match_options!(match)
291
- options.merge!(match)
292
- end
293
-
294
- options
144
+ # Delegate to XmlResolver
145
+ def resolve(**kwargs)
146
+ MatchOptions::XmlResolver.resolve(**kwargs)
295
147
  end
296
148
 
297
- # Get options for a named profile
298
- #
299
- # @param profile [Symbol] Profile name
300
- # @return [Hash] Profile options
301
- # @raise [Canon::Error] If profile is unknown
149
+ # Delegate to XmlResolver
302
150
  def get_profile_options(profile)
303
- unless MATCH_PROFILES.key?(profile)
304
- raise Canon::Error,
305
- "Unknown match profile: #{profile}. " \
306
- "Valid profiles: #{MATCH_PROFILES.keys.join(', ')}"
307
- end
308
- MATCH_PROFILES[profile].dup
151
+ MatchOptions::XmlResolver.get_profile_options(profile)
309
152
  end
310
153
 
311
- private
312
-
313
- # Validate preprocessing option
314
- def validate_preprocessing!(preprocessing)
315
- unless MatchOptions::PREPROCESSING_OPTIONS.include?(preprocessing)
316
- raise Canon::Error,
317
- "Unknown preprocessing option: #{preprocessing}. " \
318
- "Valid options: #{MatchOptions::PREPROCESSING_OPTIONS.join(', ')}"
319
- end
154
+ # Get valid match dimensions for XML/HTML
155
+ #
156
+ # @return [Array<Symbol>] Valid dimensions
157
+ def match_dimensions
158
+ MatchOptions::XmlResolver.match_dimensions
320
159
  end
321
160
 
322
- # Validate match options
323
- def validate_match_options!(match_options)
324
- # Special options that don't need validation as dimensions
325
- special_options = %i[
326
- preprocessing
327
- semantic_diff
328
- similarity_threshold
329
- hash_matching
330
- similarity_matching
331
- propagation
332
- ]
333
-
334
- match_options.each do |dimension, behavior|
335
- # Skip special options (validated elsewhere or passed through)
336
- next if special_options.include?(dimension)
337
-
338
- unless MATCH_DIMENSIONS.include?(dimension)
339
- raise Canon::Error,
340
- "Unknown match dimension: #{dimension}. " \
341
- "Valid dimensions: #{MATCH_DIMENSIONS.join(', ')}"
342
- end
343
-
344
- unless MatchOptions::MATCH_BEHAVIORS.include?(behavior)
345
- raise Canon::Error,
346
- "Unknown match behavior: #{behavior} for #{dimension}. " \
347
- "Valid behaviors: #{MatchOptions::MATCH_BEHAVIORS.join(', ')}"
348
- end
349
- end
161
+ # Get format-specific default options
162
+ #
163
+ # @param format [Symbol] Format type
164
+ # @return [Hash] Default options for the format
165
+ def format_defaults(format)
166
+ MatchOptions::XmlResolver.format_defaults(format)
350
167
  end
351
168
  end
352
169
  end
@@ -360,140 +177,36 @@ module Canon
360
177
  key_order
361
178
  ].freeze
362
179
 
363
- # Format defaults for JSON
364
- FORMAT_DEFAULTS = {
365
- json: {
366
- preprocessing: :none,
367
- text_content: :strict,
368
- structural_whitespace: :ignore,
369
- key_order: :strict,
370
- },
371
- }.freeze
372
-
373
- # Predefined match profiles for JSON
374
- MATCH_PROFILES = {
375
- # Strict: Match exactly
376
- strict: {
377
- preprocessing: :none,
378
- text_content: :strict,
379
- structural_whitespace: :strict,
380
- key_order: :strict,
381
- },
382
-
383
- # Spec-friendly: Formatting and order don't matter
384
- spec_friendly: {
385
- preprocessing: :normalize,
386
- text_content: :strict,
387
- structural_whitespace: :ignore,
388
- key_order: :ignore,
389
- },
390
-
391
- # Content-only: Only values matter
392
- content_only: {
393
- preprocessing: :normalize,
394
- text_content: :normalize,
395
- structural_whitespace: :ignore,
396
- key_order: :ignore,
397
- },
398
- }.freeze
180
+ # Expose FORMAT_DEFAULTS from JsonResolver (for backward compatibility)
181
+ FORMAT_DEFAULTS = MatchOptions::JsonResolver.const_get(:FORMAT_DEFAULTS)
182
+
183
+ # Expose MATCH_PROFILES from JsonResolver (for backward compatibility)
184
+ MATCH_PROFILES = MatchOptions::JsonResolver.const_get(:MATCH_PROFILES)
399
185
 
400
186
  class << self
401
- # Resolve match options with precedence handling
402
- #
403
- # @param format [Symbol] Format type (:json)
404
- # @param match_profile [Symbol, nil] Profile name
405
- # @param match [Hash, nil] Explicit options per dimension
406
- # @param preprocessing [Symbol, nil] Preprocessing option
407
- # @param global_profile [Symbol, nil] Global configured profile
408
- # @param global_options [Hash, nil] Global configured options
409
- # @return [Hash] Resolved options for all dimensions
410
- def resolve(
411
- format:,
412
- match_profile: nil,
413
- match: nil,
414
- preprocessing: nil,
415
- global_profile: nil,
416
- global_options: nil
417
- )
418
- # Start with format-specific defaults
419
- options = FORMAT_DEFAULTS[:json].dup
420
-
421
- # Apply global profile if specified
422
- if global_profile
423
- profile_opts = get_profile_options(global_profile)
424
- options.merge!(profile_opts)
425
- end
426
-
427
- # Apply global options if specified
428
- if global_options
429
- validate_match_options!(global_options)
430
- options.merge!(global_options)
431
- end
432
-
433
- # Apply per-call profile if specified (overrides global)
434
- if match_profile
435
- profile_opts = get_profile_options(match_profile)
436
- options.merge!(profile_opts)
437
- end
438
-
439
- # Apply per-call preprocessing if specified (overrides profile)
440
- if preprocessing
441
- validate_preprocessing!(preprocessing)
442
- options[:preprocessing] = preprocessing
443
- end
444
-
445
- # Apply per-call explicit options if specified (highest priority)
446
- if match
447
- validate_match_options!(match)
448
- options.merge!(match)
449
- end
450
-
451
- options
187
+ # Delegate to JsonResolver
188
+ def resolve(**kwargs)
189
+ MatchOptions::JsonResolver.resolve(**kwargs)
452
190
  end
453
191
 
454
- # Get options for a named profile
455
- #
456
- # @param profile [Symbol] Profile name
457
- # @return [Hash] Profile options
458
- # @raise [Canon::Error] If profile is unknown
192
+ # Delegate to JsonResolver
459
193
  def get_profile_options(profile)
460
- unless MATCH_PROFILES.key?(profile)
461
- raise Canon::Error,
462
- "Unknown match profile: #{profile}. " \
463
- "Valid profiles: #{MATCH_PROFILES.keys.join(', ')}"
464
- end
465
- MATCH_PROFILES[profile].dup
194
+ MatchOptions::JsonResolver.get_profile_options(profile)
466
195
  end
467
196
 
468
- private
469
-
470
- # Validate preprocessing option
471
- def validate_preprocessing!(preprocessing)
472
- unless MatchOptions::PREPROCESSING_OPTIONS.include?(preprocessing)
473
- raise Canon::Error,
474
- "Unknown preprocessing option: #{preprocessing}. " \
475
- "Valid options: #{MatchOptions::PREPROCESSING_OPTIONS.join(', ')}"
476
- end
197
+ # Get valid match dimensions for JSON
198
+ #
199
+ # @return [Array<Symbol>] Valid dimensions
200
+ def match_dimensions
201
+ MatchOptions::JsonResolver.match_dimensions
477
202
  end
478
203
 
479
- # Validate match options
480
- def validate_match_options!(match_options)
481
- match_options.each do |dimension, behavior|
482
- # Skip preprocessing as it's validated separately
483
- next if dimension == :preprocessing
484
-
485
- unless MATCH_DIMENSIONS.include?(dimension)
486
- raise Canon::Error,
487
- "Unknown match dimension: #{dimension}. " \
488
- "Valid dimensions: #{MATCH_DIMENSIONS.join(', ')}"
489
- end
490
-
491
- unless MatchOptions::MATCH_BEHAVIORS.include?(behavior)
492
- raise Canon::Error,
493
- "Unknown match behavior: #{behavior} for #{dimension}. " \
494
- "Valid behaviors: #{MatchOptions::MATCH_BEHAVIORS.join(', ')}"
495
- end
496
- end
204
+ # Get format-specific default options
205
+ #
206
+ # @param format [Symbol] Format type
207
+ # @return [Hash] Default options for the format
208
+ def format_defaults(format)
209
+ MatchOptions::JsonResolver.format_defaults(format)
497
210
  end
498
211
  end
499
212
  end
@@ -508,144 +221,36 @@ module Canon
508
221
  comments
509
222
  ].freeze
510
223
 
511
- # Format defaults for YAML
512
- FORMAT_DEFAULTS = {
513
- yaml: {
514
- preprocessing: :none,
515
- text_content: :strict,
516
- structural_whitespace: :ignore,
517
- key_order: :strict,
518
- comments: :ignore,
519
- },
520
- }.freeze
521
-
522
- # Predefined match profiles for YAML
523
- MATCH_PROFILES = {
524
- # Strict: Match exactly
525
- strict: {
526
- preprocessing: :none,
527
- text_content: :strict,
528
- structural_whitespace: :strict,
529
- key_order: :strict,
530
- comments: :strict,
531
- },
532
-
533
- # Spec-friendly: Formatting and order don't matter
534
- spec_friendly: {
535
- preprocessing: :normalize,
536
- text_content: :strict,
537
- structural_whitespace: :ignore,
538
- key_order: :ignore,
539
- comments: :ignore,
540
- },
541
-
542
- # Content-only: Only values matter
543
- content_only: {
544
- preprocessing: :normalize,
545
- text_content: :normalize,
546
- structural_whitespace: :ignore,
547
- key_order: :ignore,
548
- comments: :ignore,
549
- },
550
- }.freeze
224
+ # Expose FORMAT_DEFAULTS from YamlResolver (for backward compatibility)
225
+ FORMAT_DEFAULTS = MatchOptions::YamlResolver.const_get(:FORMAT_DEFAULTS)
226
+
227
+ # Expose MATCH_PROFILES from YamlResolver (for backward compatibility)
228
+ MATCH_PROFILES = MatchOptions::YamlResolver.const_get(:MATCH_PROFILES)
551
229
 
552
230
  class << self
553
- # Resolve match options with precedence handling
554
- #
555
- # @param format [Symbol] Format type (:yaml)
556
- # @param match_profile [Symbol, nil] Profile name
557
- # @param match [Hash, nil] Explicit options per dimension
558
- # @param preprocessing [Symbol, nil] Preprocessing option
559
- # @param global_profile [Symbol, nil] Global configured profile
560
- # @param global_options [Hash, nil] Global configured options
561
- # @return [Hash] Resolved options for all dimensions
562
- def resolve(
563
- format:,
564
- match_profile: nil,
565
- match: nil,
566
- preprocessing: nil,
567
- global_profile: nil,
568
- global_options: nil
569
- )
570
- # Start with format-specific defaults
571
- options = FORMAT_DEFAULTS[:yaml].dup
572
-
573
- # Apply global profile if specified
574
- if global_profile
575
- profile_opts = get_profile_options(global_profile)
576
- options.merge!(profile_opts)
577
- end
578
-
579
- # Apply global options if specified
580
- if global_options
581
- validate_match_options!(global_options)
582
- options.merge!(global_options)
583
- end
584
-
585
- # Apply per-call profile if specified (overrides global)
586
- if match_profile
587
- profile_opts = get_profile_options(match_profile)
588
- options.merge!(profile_opts)
589
- end
590
-
591
- # Apply per-call preprocessing if specified (overrides profile)
592
- if preprocessing
593
- validate_preprocessing!(preprocessing)
594
- options[:preprocessing] = preprocessing
595
- end
596
-
597
- # Apply per-call explicit options if specified (highest priority)
598
- if match
599
- validate_match_options!(match)
600
- options.merge!(match)
601
- end
602
-
603
- options
231
+ # Delegate to YamlResolver
232
+ def resolve(**kwargs)
233
+ MatchOptions::YamlResolver.resolve(**kwargs)
604
234
  end
605
235
 
606
- # Get options for a named profile
607
- #
608
- # @param profile [Symbol] Profile name
609
- # @return [Hash] Profile options
610
- # @raise [Canon::Error] If profile is unknown
236
+ # Delegate to YamlResolver
611
237
  def get_profile_options(profile)
612
- unless MATCH_PROFILES.key?(profile)
613
- raise Canon::Error,
614
- "Unknown match profile: #{profile}. " \
615
- "Valid profiles: #{MATCH_PROFILES.keys.join(', ')}"
616
- end
617
- MATCH_PROFILES[profile].dup
238
+ MatchOptions::YamlResolver.get_profile_options(profile)
618
239
  end
619
240
 
620
- private
621
-
622
- # Validate preprocessing option
623
- def validate_preprocessing!(preprocessing)
624
- unless MatchOptions::PREPROCESSING_OPTIONS.include?(preprocessing)
625
- raise Canon::Error,
626
- "Unknown preprocessing option: #{preprocessing}. " \
627
- "Valid options: #{MatchOptions::PREPROCESSING_OPTIONS.join(', ')}"
628
- end
241
+ # Get valid match dimensions for YAML
242
+ #
243
+ # @return [Array<Symbol>] Valid dimensions
244
+ def match_dimensions
245
+ MatchOptions::YamlResolver.match_dimensions
629
246
  end
630
247
 
631
- # Validate match options
632
- def validate_match_options!(match_options)
633
- match_options.each do |dimension, behavior|
634
- # Skip preprocessing as it's validated separately
635
- next if dimension == :preprocessing
636
-
637
- unless MATCH_DIMENSIONS.include?(dimension)
638
- raise Canon::Error,
639
- "Unknown match dimension: #{dimension}. " \
640
- "Valid dimensions: #{MATCH_DIMENSIONS.join(', ')}"
641
- end
642
-
643
- unless MatchOptions::MATCH_BEHAVIORS.include?(behavior)
644
- raise Canon::Error,
645
- "Unknown match behavior: #{behavior} for #{dimension}. " \
646
- "Valid behaviors: #{MatchOptions::MATCH_BEHAVIORS.join(', ')}"
647
- end
648
- end
248
+ # Get format-specific default options
249
+ #
250
+ # @param format [Symbol] Format type
251
+ # @return [Hash] Default options for the format
252
+ def format_defaults(format)
253
+ MatchOptions::YamlResolver.format_defaults(format)
649
254
  end
650
255
  end
651
256
  end