module ArchSpec::Facts
Shared semantic facts and versioned snapshots. Static framework passes and explicit runtime producers use Builder; checks import data without booting.
Constants
- DEFAULT_INPUTS
-
Files outside the analyzed source that can change what any producer observes. A change to one of them invalidates a whole document.
- Exposure
- Gap
- LIST_KEYS
- Method
- Mixin
- Operations
-
One document’s facts. The producer is recorded on imported edges; facts applied in memory by a static pass have none.
- Receiver
- Reference
- Resolve
- STALE_RULE
- VERSION
Public Instance Methods
Source
# File lib/archspec/facts.rb, line 52 def digest(path) Digest::SHA256.file(path).hexdigest if File.file?(path) end
# File lib/archspec/facts.rb, line 30 def fingerprint(graph, inputs:, sources:, cited:, excluding:) paths = input_files(graph, inputs, excluding: excluding) + source_files(graph, sources) + cited.select { |path| File.file?(path) } digests(graph, paths) end
A version 2 fingerprint: the producer’s global inputs, the analyzed files its source patterns cover, and every file the facts cite.
# File lib/archspec/facts.rb, line 36 def input_files(graph, patterns, excluding:) patterns.flat_map { |pattern| Dir.glob(File.join(graph.root, pattern)) } .select { |path| File.file?(path) }.map { |path| File.expand_path(path) } .reject { |path| excluded?(graph, path, excluding) } end
# File lib/archspec/facts.rb, line 61 def load_into(graph, directory, producers: []) paths = Dir.glob(File.join(File.expand_path(directory, graph.root), '*.yml')).sort raise Error, "no facts found in #{directory}; run `archspec reflect` or your facts producer" if paths.empty? missing = producers.map(&:to_s) - paths.map { |path| File.basename(path, '.yml') } unless missing.empty? raise Error, "no facts from #{missing.join(', ')} in #{directory}; run `archspec reflect`" end batches = paths.filter_map do |path| document = Document.new(graph, YAML.safe_load_file(path, permitted_classes: [], aliases: false), path) document.compile_fresh(excluding: directory) end Application.apply(graph, batches) rescue Psych::Exception, SystemCallError => error raise Error, "could not load facts: #{error.message}" end
Loads every document in the directory. Stale documents and the facts of stale files are skipped and reported as diagnostics; everything else is validated before any of it is applied. Every named producers must have written its document, so a producer that never succeeded cannot pass unnoticed.
# File lib/archspec/facts.rb, line 23 def snapshot(graph, excluding: 'archspec_facts') paths = graph.files.keys + input_files(graph, DEFAULT_INPUTS, excluding: excluding) digests(graph, paths.reject { |path| excluded?(graph, path, excluding) }) end
The exact input map of a version 1 document: every analyzed file plus the default inputs.
# File lib/archspec/facts.rb, line 44 def source_files(graph, patterns) return [] if patterns.empty? graph.files.values.select do |file| patterns.any? { |pattern| File.fnmatch?(pattern, file.relative_path, File::FNM_PATHNAME | File::FNM_EXTGLOB) } end.map(&:path) end
Source patterns match analyzed files only, so covering a directory never walks the file system.
Source
# File lib/archspec/facts.rb, line 79 def write(path, document) FileUtils.mkdir_p(File.dirname(path)) Tempfile.create(['.archspec-facts-', '.yml'], File.dirname(path)) do |file| file.write(document.to_yaml) file.flush file.close File.chmod(0o666 & ~File.umask, file.path) File.rename(file.path, path) end rescue SystemCallError => error raise Error, "could not write facts #{path}: #{error.message}" end