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.



17
18
19
20
21
# File 'lib/fontist/validator.rb', line 17

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.



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

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:



151
152
153
154
155
# File 'lib/fontist/validator.rb', line 151

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:



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
144
145
# File 'lib/fontist/validator.rb', line 115

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:



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
60
61
# File 'lib/fontist/validator.rb', line 30

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:



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
107
108
# File 'lib/fontist/validator.rb', line 67

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