class ArchSpec::Facts::Document
Decodes a complete batch before any graph mutation. Both the in-memory builder and external YAML documents must satisfy this contract.
Constants
- ENTRY_LOCATION_KEYS
- ENVELOPE_KEYS
- LEGACY_ENVELOPE_KEYS
- LEGACY_LIST_KEYS
- LOCATION_KEYS
- METHOD_KEYS
- SCOPES
Public Class Methods
Source
# File lib/archspec/facts/document.rb, line 16 def initialize(graph, document, label) @graph = graph @document = document @label = label @lines = {} @stale = Set.new @removed = Set.new end
Public Instance Methods
Source
# File lib/archspec/facts/document.rb, line 42 def compile validate_envelope Operations.new(*LIST_KEYS.map do |key| Array(@document[key]).reject { |entry| stale_entry?(entry) } .flat_map do |entry| validate_dependencies(entry) public_send("decode_#{key}", entry) end end, producer) end
Source
# File lib/archspec/facts/document.rb, line 27 def compile_fresh(excluding:) validate_envelope invalid('version 2 facts require a snapshot') if @document['version'] != 1 && @document['snapshot'].nil? reason = @document['version'] == 1 ? legacy_staleness(excluding) : staleness(excluding) if reason stale_diagnostic(@label, "#{producer} facts are out of date: #{reason}; run `archspec reflect`") return end @stale.each do |path| stale_diagnostic(path, "#{producer} facts are out of date for this file; run `archspec reflect`") end compile end
Compiles an imported document, or returns nil when it no longer describes the source. Stale files are reported and their facts skipped.
Source
# File lib/archspec/facts/document.rb, line 102 def decode_exposures(entry) validate_entry(entry, %w[source owner scope]) source = source_for(entry, 'source') location_for(entry) invalid('method exposure source must be a module') unless source.module? owner = constant_name(entry['owner']) invalid("method exposure owner #{owner} is not analyzed") if @graph.constants_named(owner).empty? Exposure.new(source, owner, enum(entry['scope'], SCOPES, 'exposure scope')) end
Source
# File lib/archspec/facts/document.rb, line 112 def decode_gaps(entry) validate_entry(entry, %w[source message]) Gap.new(source_for(entry, 'source'), string(entry['message'], 'gap message'), location_for(entry)) end
Source
# File lib/archspec/facts/document.rb, line 58 def decode_methods(entry) validate_entry(entry, %w[owner scope names] + (@document['version'] == 1 ? [] : METHOD_KEYS)) owner = source_for(entry, 'owner') location = location_for(entry) scope = enum(entry['scope'], SCOPES, 'method scope') names = string_list(entry['names'], 'method names', nonempty: true) visibility = enum(entry.fetch('visibility', 'public'), %w[public protected private], 'method visibility') mode = enum(entry.fetch('mode', 'if_missing'), %w[if_missing append define], 'method mode') installation = entry['installation'] if mode == :define invalid('define mode requires an installation location') unless installation.is_a?(Hash) invalid('invalid installation fields') unless (installation.keys - LOCATION_KEYS).empty? installation = location_for(installation) elsif !installation.nil? invalid('installation requires define mode') end signatures = entry.fetch('signatures', []) invalid('expected a list of signatures') unless signatures.is_a?(Array) signatures = signatures.map { |signature| signature_for(signature) } alias_target = entry['alias_target'] string(alias_target, 'alias target') unless alias_target.nil? names.map do |name| definition = MethodDefinition.new(owner.name, name.to_sym, scope, location, visibility, signatures, alias_target&.to_sym) Method.new(owner, definition, mode, installation) end end
Source
# File lib/archspec/facts/document.rb, line 85 def decode_mixins(entry) validate_entry(entry, %w[owner target kind]) Mixin.new(source_for(entry, 'owner'), constant_name(entry['target']), enum(entry['kind'], %w[include prepend extend singleton_prepend], 'mixin kind'), location_for(entry)) end
Source
# File lib/archspec/facts/document.rb, line 91 def decode_receivers(entry) validate_entry(entry, %w[source name receiver scope]) source = source_for(entry, 'source') name = string(entry['name'], 'call name') location = location_for(entry) unless receiverless_calls.include?([source.name, source.path, name, location]) invalid('receiver binding must identify an existing receiverless call with its complete source range') end Receiver.new(source, name, location, constant_name(entry['receiver']), enum(entry['scope'], SCOPES, 'receiver scope')) end
Source
# File lib/archspec/facts/document.rb, line 53 def decode_references(entry) validate_entry(entry, %w[source target]) Reference.new(source_for(entry, 'source'), constant_name(entry['target']), location_for(entry)) end
Source
# File lib/archspec/facts/document.rb, line 117 def decode_resolves(entry) validate_entry(entry, %w[source message consumers]) source = source_for(entry, 'source') location = location_for(entry) message = entry['message'].nil? ? nil : string(entry['message'], 'gap message') return [] if new_consumers?(source, entry['consumers']) unless @graph.edges.any? do |edge| edge.type == :dynamic_feature && edge.from_constant == source.name && edge.from_path == source.path && edge.location == location && (message.nil? || edge.to == message) end invalid('a resolution must identify an existing analysis gap with its complete source range') end Resolve.new(source, message, location) end