Class: Fontist::Validator

Inherits:
Object
  • Object
show all
Defined in:
lib/fontist/validator.rb

Overview

Parallel font validator using non-shared state principle.

Validates fonts in parallel without mutex contention:

  • Map: Each thread validates independently using read-only cache lookup

  • Reduce: Results are merged into ValidationReport after completion

  • Cache is updated after validation completes (no shared writes during validation)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeValidator

Returns a new instance of Validator.



15
16
17
18
19
# File 'lib/fontist/validator.rb', line 15

def initialize
  @report = ValidationReport.new
  @report.platform = platform_name
  @report.generated_at = Time.now.to_i
end

Instance Attribute Details

#reportObject (readonly)

Returns the value of attribute report.



13
14
15
# File 'lib/fontist/validator.rb', line 13

def report
  @report
end

Class Method Details

.load_cache(cache_path) ⇒ ValidationCache?

Load validation cache from file.

Parameters:

  • cache_path (String)

    Path to cache file

Returns:



149
150
151
152
153
# File 'lib/fontist/validator.rb', line 149

def self.load_cache(cache_path)
  return nil unless File.exist?(cache_path)

  ValidationCache.from_yaml(File.read(cache_path))
end

Instance Method Details

#build_cache(cache_path, options = {}) ⇒ ValidationCache

Build validation cache in parallel.

Parameters:

  • cache_path (String)

    Path to save/load cache file

  • options (Hash) (defaults to: {})

    Options

Returns:



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/fontist/validator.rb', line 113

def build_cache(cache_path, options = {})
  verbose = options[:verbose]

  if verbose
    Fontist.ui.say("Building validation cache...")
  end

  start_time = Time.now

  # Load existing cache (if any) for incremental updates
  cache = load_cache_if_exists(cache_path)

  # Validate using cache (non-shared state during validation)
  validate_all(parallel: true, verbose: verbose, cache: cache)

  # Update cache with new/changed results
  update_cache_from_report(cache, @report.results)

  # Save to file
  save_cache(cache, cache_path)

  elapsed = Time.now - start_time

  if verbose
    Fontist.ui.success("Validation cache built in #{elapsed.round(2)}s")
    Fontist.ui.success("Cached #{cache.entries.size} font validations")
    Fontist.ui.success("Cache saved to: #{cache_path}")
  end

  cache
end

#validate_all(options = {}) ⇒ ValidationReport

Validate all system fonts in parallel.

Parameters:

  • options (Hash) (defaults to: {})

    Validation options

Options Hash (options):

  • :parallel (Boolean)

    Use parallel processing (default: true)

  • :verbose (Boolean)

    Show detailed progress

  • :cache (ValidationCache)

    Pre-loaded cache to reuse results

Returns:



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/fontist/validator.rb', line 28

def validate_all(options = {})
  use_parallel = options.fetch(:parallel, true)
  verbose = options[:verbose]
  cache = options[:cache]

  # Get all font paths
  font_paths = scan_font_paths

  if verbose
    Fontist.ui.say("Found #{font_paths.size} font files to validate")
    if cache
      Fontist.ui.say("Using cache with #{cache.entries.size} entries")
    end
  end

  # Build read-only lookup from cache (non-shared state)
  cache_lookup = cache&.to_lookup || {}

  # Validate in parallel or sequential
  results = if use_parallel && font_paths.size > 10
              validate_parallel(font_paths, cache_lookup: cache_lookup,
                                            verbose: verbose)
            else
              validate_sequential(font_paths, cache_lookup: cache_lookup,
                                              verbose: verbose)
            end

  @report.results = results
  @report.calculate_summary!

  @report
end

#validate_single(path) ⇒ FontValidationResult

Validate a single font and return result with timing.

Parameters:

  • path (String)

    Font file path

Returns:



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/fontist/validator.rb', line 65

def validate_single(path)
  start_time = Time.now

  # Get file metadata
  file_size = begin
    File.size(path)
  rescue StandardError
    0
  end
  file_mtime = begin
    File.mtime(path).to_i
  rescue StandardError
    0
  end

  # Validate the font
  font_file = FontFile.from_path(path, validate: true,
                                       check_extension: false)

  time_taken = Time.now - start_time

  FontValidationResult.new(
    path: path,
    valid: true,
    family_name: font_file.family,
    full_name: font_file.full_name,
    time_taken: time_taken.round(4),
    file_size: file_size,
    file_mtime: file_mtime,
  )
rescue Errors::FontFileError => e
  time_taken = Time.now - start_time

  FontValidationResult.new(
    path: path,
    valid: false,
    error_message: e.message,
    time_taken: time_taken.round(4),
    file_size: file_size,
    file_mtime: file_mtime,
  )
end