Class: Canon::Config::DiffConfig

Inherits:
Object
  • Object
show all
Defined in:
lib/canon/config.rb

Overview

Diff configuration for output formatting

Constant Summary collapse

VALID_ENUM_VALUES =

Valid values for enum-like configuration options

{
  mode: %i[by_line by_object pretty_diff],
  show_diffs: %i[all normative informative],
  algorithm: %i[dom semantic],
  parser: %i[sax dom],
  display_preprocessing: %i[none pretty_print normalize_pretty_print
                            c14n],
  display_format: %i[raw canonical],
  pretty_printer_indent_type: %i[space tab],
  character_visualization: [true, false, :content_only],
  theme: %i[light dark retro claude cyberpunk],
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(format = nil) ⇒ DiffConfig

Returns a new instance of DiffConfig.



315
316
317
318
319
# File 'lib/canon/config.rb', line 315

def initialize(format = nil)
  @format = format
  @resolver = build_resolver(format)
  @pretty_printer = PrettyPrinterConfig.new(@resolver)
end

Instance Attribute Details

#pretty_printerObject (readonly)

Returns the value of attribute pretty_printer.



299
300
301
# File 'lib/canon/config.rb', line 299

def pretty_printer
  @pretty_printer
end

Class Method Details

.validate_config_value!(key, value) ⇒ Object

Validate a config value against its allowed enum values

Raises:

  • (ArgumentError)


342
343
344
345
346
347
348
349
350
351
# File 'lib/canon/config.rb', line 342

def self.validate_config_value!(key, value)
  valid = VALID_ENUM_VALUES[key]
  return unless valid

  return if valid.include?(value)

  raise ArgumentError,
        "Invalid value #{value.inspect} for #{key}. " \
        "Valid values: #{valid.map(&:inspect).join(', ')}"
end

Instance Method Details

#algorithmObject



678
679
680
# File 'lib/canon/config.rb', line 678

def algorithm
  @resolver.resolve(:algorithm)
end

#algorithm=(value) ⇒ Object



682
683
684
685
# File 'lib/canon/config.rb', line 682

def algorithm=(value)
  self.class.validate_config_value!(:algorithm, value)
  @resolver.set_programmatic(:algorithm, value)
end

#apply_profile_data(data) ⇒ Object



326
327
328
329
330
331
332
333
334
335
# File 'lib/canon/config.rb', line 326

def apply_profile_data(data)
  return unless data

  data.each do |key, value|
    sym_key = key.to_sym
    coerced = coerce_profile_value(sym_key, value)
    self.class.validate_config_value!(sym_key, coerced)
    @resolver.set_profile(sym_key, coerced)
  end
end

#character_visualizationObject

Controls whether invisible characters (spaces, tabs, non-breaking spaces, etc.) are replaced with visible Unicode symbols in diff output.

Values:

true          - apply the full default visualization map (default)
false         - disable visualization; output plain text
:content_only - apply visualization only to text content, not
                to structural indentation whitespace.


662
663
664
665
666
667
668
669
670
671
# File 'lib/canon/config.rb', line 662

def character_visualization
  val = @resolver.resolve(:character_visualization)
  # Coerce symbol booleans that may arrive via ENV (env_schema uses :symbol type
  # so "true"/"false" env strings become :true/:false symbols)
  case val
  when true, :true then true # rubocop:disable Lint/BooleanSymbol
  when false, :false then false # rubocop:disable Lint/BooleanSymbol
  else val # true/false from programmatic, or :content_only
  end
end

#character_visualization=(value) ⇒ Object



673
674
675
676
# File 'lib/canon/config.rb', line 673

def character_visualization=(value)
  self.class.validate_config_value!(:character_visualization, value)
  @resolver.set_programmatic(:character_visualization, value)
end

#clear_profile!Object



337
338
339
# File 'lib/canon/config.rb', line 337

def clear_profile!
  @resolver.clear_profile!
end

#collapse_whitespace_elementsObject

Element names where whitespace is COLLAPSED (HTML-style behavior). Multiple whitespace chars collapse to single space; boundaries preserved. ENV variable: CANON_<FORMAT>_DIFF_COLLAPSE_WHITESPACE_ELEMENTS



569
570
571
# File 'lib/canon/config.rb', line 569

def collapse_whitespace_elements
  @resolver.resolve(:collapse_whitespace_elements) || []
end

#collapse_whitespace_elements=(value) ⇒ Object



573
574
575
576
# File 'lib/canon/config.rb', line 573

def collapse_whitespace_elements=(value)
  @resolver.set_programmatic(:collapse_whitespace_elements,
                             Array(value).map(&:to_s))
end

#compact_semantic_reportObject

Render element nodes in the Semantic Diff Report as compact inline XML (e.g. <strong>Annex</strong>) instead of the verbose node_info description string (e.g. “name: strong namespace_uri: …”).

Default: false (keep existing verbose format for backwards compatibility) ENV variable: CANON_<FORMAT>_DIFF_COMPACT_SEMANTIC_REPORT



633
634
635
# File 'lib/canon/config.rb', line 633

def compact_semantic_report
  @resolver.resolve(:compact_semantic_report)
end

#compact_semantic_report=(value) ⇒ Object



637
638
639
# File 'lib/canon/config.rb', line 637

def compact_semantic_report=(value)
  @resolver.set_programmatic(:compact_semantic_report, value)
end

#context_linesObject



371
372
373
# File 'lib/canon/config.rb', line 371

def context_lines
  @resolver.resolve(:context_lines)
end

#context_lines=(value) ⇒ Object



375
376
377
# File 'lib/canon/config.rb', line 375

def context_lines=(value)
  @resolver.set_programmatic(:context_lines, value)
end

#custom_themeObject

Full custom theme hash



717
718
719
# File 'lib/canon/config.rb', line 717

def custom_theme
  @resolver.resolve(:custom_theme)
end

#custom_theme=(value) ⇒ Object



721
722
723
# File 'lib/canon/config.rb', line 721

def custom_theme=(value)
  @resolver.set_programmatic(:custom_theme, value)
end

#display_formatObject



528
529
530
# File 'lib/canon/config.rb', line 528

def display_format
  @resolver.resolve(:display_format)
end

#display_format=(value) ⇒ Object



532
533
534
535
# File 'lib/canon/config.rb', line 532

def display_format=(value)
  self.class.validate_config_value!(:display_format, value)
  @resolver.set_programmatic(:display_format, value)
end

#display_preprocessingObject

Controls how documents are normalized *for display* before the line diff. This is independent of FormatConfig#preprocessing, which controls normalization for comparison (equivalence detection).

Values:

:none         - use documents as-is (default, existing behaviour)
:pretty_print - run through Canon::PrettyPrinter::Xml before diffing
:c14n         - run through XML C14N normalization before diffing


545
546
547
# File 'lib/canon/config.rb', line 545

def display_preprocessing
  @resolver.resolve(:display_preprocessing)
end

#display_preprocessing=(value) ⇒ Object



549
550
551
552
# File 'lib/canon/config.rb', line 549

def display_preprocessing=(value)
  self.class.validate_config_value!(:display_preprocessing, value)
  @resolver.set_programmatic(:display_preprocessing, value)
end

#expand_differenceObject

Show the full serialized node content (including children) in element_structure diffs instead of just the tag name.

Default: false (show only the tag name, e.g. <biblio-tag>) ENV variable: CANON_<FORMAT>_DIFF_EXPAND_DIFFERENCE



646
647
648
# File 'lib/canon/config.rb', line 646

def expand_difference
  @resolver.resolve(:expand_difference)
end

#expand_difference=(value) ⇒ Object



650
651
652
# File 'lib/canon/config.rb', line 650

def expand_difference=(value)
  @resolver.set_programmatic(:expand_difference, value)
end

#grouping_linesObject



379
380
381
# File 'lib/canon/config.rb', line 379

def grouping_lines
  @resolver.resolve(:grouping_lines)
end

#grouping_lines=(value) ⇒ Object



383
384
385
# File 'lib/canon/config.rb', line 383

def grouping_lines=(value)
  @resolver.set_programmatic(:grouping_lines, value)
end

#max_diff_linesObject

Maximum diff output lines (default 10,000)



744
745
746
# File 'lib/canon/config.rb', line 744

def max_diff_lines
  @resolver.resolve(:max_diff_lines)
end

#max_diff_lines=(value) ⇒ Object



748
749
750
# File 'lib/canon/config.rb', line 748

def max_diff_lines=(value)
  @resolver.set_programmatic(:max_diff_lines, value)
end

#max_file_sizeObject

File size limit in bytes (default 5MB)



726
727
728
# File 'lib/canon/config.rb', line 726

def max_file_size
  @resolver.resolve(:max_file_size)
end

#max_file_size=(value) ⇒ Object



730
731
732
# File 'lib/canon/config.rb', line 730

def max_file_size=(value)
  @resolver.set_programmatic(:max_file_size, value)
end

#max_node_countObject

Maximum node count in tree (default 10,000)



735
736
737
# File 'lib/canon/config.rb', line 735

def max_node_count
  @resolver.resolve(:max_node_count)
end

#max_node_count=(value) ⇒ Object



739
740
741
# File 'lib/canon/config.rb', line 739

def max_node_count=(value)
  @resolver.set_programmatic(:max_node_count, value)
end

#modeObject

Accessors with ENV override support



354
355
356
# File 'lib/canon/config.rb', line 354

def mode
  @resolver.resolve(:mode)
end

#mode=(value) ⇒ Object



358
359
360
361
# File 'lib/canon/config.rb', line 358

def mode=(value)
  self.class.validate_config_value!(:mode, value)
  @resolver.set_programmatic(:mode, value)
end

#parserObject

XML parser backend (:sax or :dom, default :sax)



688
689
690
# File 'lib/canon/config.rb', line 688

def parser
  @resolver.resolve(:parser)
end

#parser=(value) ⇒ Object



692
693
694
695
# File 'lib/canon/config.rb', line 692

def parser=(value)
  self.class.validate_config_value!(:parser, value)
  @resolver.set_programmatic(:parser, value)
end

#preserve_whitespace_elementsObject

Element names where whitespace is PRESERVED exactly (no manipulation). All whitespace characters are significant in these elements. ENV variable: CANON_<FORMAT>_DIFF_PRESERVE_WHITESPACE_ELEMENTS



557
558
559
# File 'lib/canon/config.rb', line 557

def preserve_whitespace_elements
  @resolver.resolve(:preserve_whitespace_elements) || []
end

#preserve_whitespace_elements=(value) ⇒ Object



561
562
563
564
# File 'lib/canon/config.rb', line 561

def preserve_whitespace_elements=(value)
  @resolver.set_programmatic(:preserve_whitespace_elements,
                             Array(value).map(&:to_s))
end

#pretty_printed_expectedObject

When true, whitespace-only text nodes starting with “n” in :collapse elements of the expected (fixture) document are treated as structural indentation and dropped from both comparison and display. Use this when fixture files are indented but received XML is compact. ENV variable: CANON_<FORMAT>_DIFF_PRETTY_PRINTED_EXPECTED



594
595
596
# File 'lib/canon/config.rb', line 594

def pretty_printed_expected
  @resolver.resolve(:pretty_printed_expected)
end

#pretty_printed_expected=(value) ⇒ Object



598
599
600
# File 'lib/canon/config.rb', line 598

def pretty_printed_expected=(value)
  @resolver.set_programmatic(:pretty_printed_expected, value)
end

#pretty_printed_receivedObject

When true, whitespace-only text nodes starting with “n” in :normalize elements of the received document are treated as structural indentation and dropped from both comparison and display. Use this when received XML may be pretty-printed but the fixture is compact. ENV variable: CANON_<FORMAT>_DIFF_PRETTY_PRINTED_RECEIVED



607
608
609
# File 'lib/canon/config.rb', line 607

def pretty_printed_received
  @resolver.resolve(:pretty_printed_received)
end

#pretty_printed_received=(value) ⇒ Object



611
612
613
# File 'lib/canon/config.rb', line 611

def pretty_printed_received=(value)
  @resolver.set_programmatic(:pretty_printed_received, value)
end

#pretty_printer_sort_attributesObject

When true, attributes on each element are sorted by namespace URI then local name in the pretty-printed display, eliminating spurious diff noise from differing attribute order. ENV variable: CANON_<FORMAT>_DIFF_PRETTY_PRINTER_SORT_ATTRIBUTES



619
620
621
# File 'lib/canon/config.rb', line 619

def pretty_printer_sort_attributes
  @resolver.resolve(:pretty_printer_sort_attributes)
end

#pretty_printer_sort_attributes=(value) ⇒ Object



623
624
625
# File 'lib/canon/config.rb', line 623

def pretty_printer_sort_attributes=(value)
  @resolver.set_programmatic(:pretty_printer_sort_attributes, value)
end

#reset!Object



321
322
323
324
# File 'lib/canon/config.rb', line 321

def reset!
  @resolver = build_resolver(@format)
  @pretty_printer = PrettyPrinterConfig.new(@resolver)
end

#show_diffsObject



387
388
389
# File 'lib/canon/config.rb', line 387

def show_diffs
  @resolver.resolve(:show_diffs)
end

#show_diffs=(value) ⇒ Object



391
392
393
394
# File 'lib/canon/config.rb', line 391

def show_diffs=(value)
  self.class.validate_config_value!(:show_diffs, value)
  @resolver.set_programmatic(:show_diffs, value)
end

#show_line_numbered_inputsObject



520
521
522
# File 'lib/canon/config.rb', line 520

def show_line_numbered_inputs
  @resolver.resolve(:show_line_numbered_inputs)
end

#show_line_numbered_inputs=(value) ⇒ Object



524
525
526
# File 'lib/canon/config.rb', line 524

def show_line_numbered_inputs=(value)
  @resolver.set_programmatic(:show_line_numbered_inputs, value)
end

#show_preprocessed_expectedObject

Show only the EXPECTED (fixture) block in the preprocessed-inputs section. Has no effect unless show_preprocessed_inputs or verbose_diff is also set. Use show_preprocessed_expected: true together with show_preprocessed_received: false to display only the preprocessed fixture while suppressing the preprocessed received output.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_PREPROCESSED_EXPECTED



456
457
458
# File 'lib/canon/config.rb', line 456

def show_preprocessed_expected
  @resolver.resolve(:show_preprocessed_expected)
end

#show_preprocessed_expected=(value) ⇒ Object



460
461
462
# File 'lib/canon/config.rb', line 460

def show_preprocessed_expected=(value)
  @resolver.set_programmatic(:show_preprocessed_expected, value)
end

#show_preprocessed_inputsObject



441
442
443
# File 'lib/canon/config.rb', line 441

def show_preprocessed_inputs
  @resolver.resolve(:show_preprocessed_inputs)
end

#show_preprocessed_inputs=(value) ⇒ Object



445
446
447
# File 'lib/canon/config.rb', line 445

def show_preprocessed_inputs=(value)
  @resolver.set_programmatic(:show_preprocessed_inputs, value)
end

#show_preprocessed_receivedObject

Show only the RECEIVED (actual) block in the preprocessed-inputs section. Combined with show_preprocessed_expected: false (or leaving it at the default false) this suppresses the fixture preprocessing display while still showing what the received document looked like after preprocessing.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_PREPROCESSED_RECEIVED



471
472
473
# File 'lib/canon/config.rb', line 471

def show_preprocessed_received
  @resolver.resolve(:show_preprocessed_received)
end

#show_preprocessed_received=(value) ⇒ Object



475
476
477
# File 'lib/canon/config.rb', line 475

def show_preprocessed_received=(value)
  @resolver.set_programmatic(:show_preprocessed_received, value)
end

#show_prettyprint_expectedObject

Show only the EXPECTED (fixture) block in the pretty-print section. Useful when the fixture is what needs updating and the received side is not needed for copy-pasting.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_PRETTYPRINT_EXPECTED



499
500
501
# File 'lib/canon/config.rb', line 499

def show_prettyprint_expected
  @resolver.resolve(:show_prettyprint_expected)
end

#show_prettyprint_expected=(value) ⇒ Object



503
504
505
# File 'lib/canon/config.rb', line 503

def show_prettyprint_expected=(value)
  @resolver.set_programmatic(:show_prettyprint_expected, value)
end

#show_prettyprint_inputsObject

Show both EXPECTED and RECEIVED blocks in a fixture-ready pretty-printed section. The output uses the same pretty-printer as display_preprocessing: :pretty_print (one tag per line, indentation) but with no character visualization — whitespace appears as plain ASCII so the output can be copy-pasted directly into RSpec fixture heredocs.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_PRETTYPRINT_INPUTS



486
487
488
# File 'lib/canon/config.rb', line 486

def show_prettyprint_inputs
  @resolver.resolve(:show_prettyprint_inputs)
end

#show_prettyprint_inputs=(value) ⇒ Object



490
491
492
# File 'lib/canon/config.rb', line 490

def show_prettyprint_inputs=(value)
  @resolver.set_programmatic(:show_prettyprint_inputs, value)
end

#show_prettyprint_receivedObject

Show only the RECEIVED (actual) block in the pretty-print section. Use this to get a copy-pasteable pretty-printed form of the generated output (the most common fixture-update workflow).

ENV variable: CANON_<FORMAT>_DIFF_SHOW_PRETTYPRINT_RECEIVED



512
513
514
# File 'lib/canon/config.rb', line 512

def show_prettyprint_received
  @resolver.resolve(:show_prettyprint_received)
end

#show_prettyprint_received=(value) ⇒ Object



516
517
518
# File 'lib/canon/config.rb', line 516

def show_prettyprint_received=(value)
  @resolver.set_programmatic(:show_prettyprint_received, value)
end

#show_raw_expectedObject

Show only the EXPECTED (fixture) block in the raw-inputs section. Has no effect unless show_raw_inputs or verbose_diff is also set. Use show_raw_expected: false together with show_raw_received: true (or show_raw_inputs: true) to suppress the fixture display while keeping the received output.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_RAW_EXPECTED



419
420
421
# File 'lib/canon/config.rb', line 419

def show_raw_expected
  @resolver.resolve(:show_raw_expected)
end

#show_raw_expected=(value) ⇒ Object



423
424
425
# File 'lib/canon/config.rb', line 423

def show_raw_expected=(value)
  @resolver.set_programmatic(:show_raw_expected, value)
end

#show_raw_inputsObject



404
405
406
# File 'lib/canon/config.rb', line 404

def show_raw_inputs
  @resolver.resolve(:show_raw_inputs)
end

#show_raw_inputs=(value) ⇒ Object



408
409
410
# File 'lib/canon/config.rb', line 408

def show_raw_inputs=(value)
  @resolver.set_programmatic(:show_raw_inputs, value)
end

#show_raw_receivedObject

Show only the RECEIVED (actual) block in the raw-inputs section. Combined with show_raw_expected: false (or leaving it at the default false) this suppresses the fixture while still displaying the output that was generated.

ENV variable: CANON_<FORMAT>_DIFF_SHOW_RAW_RECEIVED



433
434
435
# File 'lib/canon/config.rb', line 433

def show_raw_received
  @resolver.resolve(:show_raw_received)
end

#show_raw_received=(value) ⇒ Object



437
438
439
# File 'lib/canon/config.rb', line 437

def show_raw_received=(value)
  @resolver.set_programmatic(:show_raw_received, value)
end

#strip_whitespace_elementsObject

Element names where whitespace-only text nodes are STRIPPED. ENV variable: CANON_<FORMAT>_DIFF_STRIP_WHITESPACE_ELEMENTS



580
581
582
# File 'lib/canon/config.rb', line 580

def strip_whitespace_elements
  @resolver.resolve(:strip_whitespace_elements) || []
end

#strip_whitespace_elements=(value) ⇒ Object



584
585
586
587
# File 'lib/canon/config.rb', line 584

def strip_whitespace_elements=(value)
  @resolver.set_programmatic(:strip_whitespace_elements,
                             Array(value).map(&:to_s))
end

#themeObject

Theme name (:light, :dark, :retro, :claude)



698
699
700
# File 'lib/canon/config.rb', line 698

def theme
  @resolver.resolve(:theme)
end

#theme=(value) ⇒ Object



702
703
704
705
# File 'lib/canon/config.rb', line 702

def theme=(value)
  self.class.validate_config_value!(:theme, value)
  @resolver.set_programmatic(:theme, value)
end

#theme_inheritanceObject

Theme inheritance (custom theme with base + overrides)



708
709
710
# File 'lib/canon/config.rb', line 708

def theme_inheritance
  @resolver.resolve(:theme_inheritance)
end

#theme_inheritance=(value) ⇒ Object



712
713
714
# File 'lib/canon/config.rb', line 712

def theme_inheritance=(value)
  @resolver.set_programmatic(:theme_inheritance, value)
end

#to_hObject

Build diff options



753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
# File 'lib/canon/config.rb', line 753

def to_h
  {
    diff: mode,
    use_color: use_color,
    context_lines: context_lines,
    grouping_lines: grouping_lines,
    show_diffs: show_diffs,
    verbose_diff: verbose_diff,
    diff_algorithm: algorithm,
    parser: parser,
    show_raw_inputs: show_raw_inputs,
    show_raw_expected: show_raw_expected,
    show_raw_received: show_raw_received,
    show_preprocessed_inputs: show_preprocessed_inputs,
    show_preprocessed_expected: show_preprocessed_expected,
    show_preprocessed_received: show_preprocessed_received,
    show_prettyprint_inputs: show_prettyprint_inputs,
    show_prettyprint_expected: show_prettyprint_expected,
    show_prettyprint_received: show_prettyprint_received,
    show_line_numbered_inputs: show_line_numbered_inputs,
    character_visualization: character_visualization,
    display_format: display_format,
    display_preprocessing: display_preprocessing,
    pretty_printer_indent: pretty_printer.indent,
    pretty_printer_indent_type: pretty_printer.indent_type,
    preserve_whitespace_elements: preserve_whitespace_elements,
    collapse_whitespace_elements: collapse_whitespace_elements,
    strip_whitespace_elements: strip_whitespace_elements,
    pretty_printed_expected: pretty_printed_expected,
    pretty_printed_received: pretty_printed_received,
    compact_semantic_report: compact_semantic_report,
    expand_difference: expand_difference,
    max_file_size: max_file_size,
    max_node_count: max_node_count,
    max_diff_lines: max_diff_lines,
    theme: theme,
  }
end

#use_colorObject



363
364
365
# File 'lib/canon/config.rb', line 363

def use_color
  @resolver.resolve(:use_color)
end

#use_color=(value) ⇒ Object



367
368
369
# File 'lib/canon/config.rb', line 367

def use_color=(value)
  @resolver.set_programmatic(:use_color, value)
end

#verbose_diffObject



396
397
398
# File 'lib/canon/config.rb', line 396

def verbose_diff
  @resolver.resolve(:verbose_diff)
end

#verbose_diff=(value) ⇒ Object



400
401
402
# File 'lib/canon/config.rb', line 400

def verbose_diff=(value)
  @resolver.set_programmatic(:verbose_diff, value)
end