Module: ArchUnit::Extraction

Defined in:
lib/archunit/extraction/extract_graph.rb,
lib/archunit/extraction/arch_ignore.rb,
lib/archunit/extraction/constant_index.rb,
lib/archunit/extraction/import_visitor.rb,
lib/archunit/extraction/locate_project.rb,
lib/archunit/extraction/resolve_import.rb,
lib/archunit/extraction/extract_imports.rb,
lib/archunit/extraction/import_resolver.rb,
lib/archunit/extraction/resolved_import.rb,
lib/archunit/extraction/constant_visitor.rb,
lib/archunit/extraction/extraction_profile.rb,
lib/archunit/extraction/resolve_load_paths.rb,
lib/archunit/extraction/extract_dependencies.rb,
lib/archunit/extraction/local_string_tracker.rb,
lib/archunit/extraction/enumerate_source_files.rb,
lib/archunit/extraction/conservative_local_flow.rb,
lib/archunit/extraction/validate_extraction_paths.rb,
lib/archunit/extraction/discover_gemspec_load_paths.rb

Overview

Ruby-specific project discovery and source extraction. rubocop:disable-next Metrics/ModuleLength – One source policy keeps graphs and metrics aligned.

Defined Under Namespace

Modules: ArchIgnore, ExtractionPaths, GemspecLoadPaths, LoadPaths Classes: ExtractionProfile, ImportResolver, ResolvedImport

Constant Summary collapse

GRAPH_CACHE_MUTEX =
Mutex.new
GRAPH_INDEPENDENT_CHECK_OPTIONS =
%i[allow_empty_tests logging clear_cache].freeze
IGNORE_DIRECTIVE =
/\A#\s*archunit:\s*ignore(?:\s+(?<modules>.+?))?\s*\z/
DEFAULT_EXCLUDED_DIRECTORIES =
%w[
  .bundle
  .cache
  .git
  .hg
  .ruby-lsp
  .svn
  .yardoc
  build
  coverage
  dist
  log
  node_modules
  pkg
  tmp
  vendor
].freeze

Class Method Summary collapse

Class Method Details

.clear_graph_cache ⇒ Object



39
40
41
42
# File 'lib/archunit/extraction/extract_graph.rb', line 39

def clear_graph_cache
  GRAPH_CACHE_MUTEX.synchronize { graph_cache.clear }
  nil
end

.enumerate_source_files(project_root, exclude_patterns: nil) ⇒ Object



33
34
35
36
37
# File 'lib/archunit/extraction/enumerate_source_files.rb', line 33

def enumerate_source_files(project_root, exclude_patterns: nil)
  root = source_root(project_root)
  patterns = resolve_project_exclude_patterns(root, exclude_patterns)
  enumerate_source_files_with_patterns(root, patterns)
end

.extract_dependencies(project_root, exclude_patterns: nil, options: nil) ⇒ Object



21
22
23
24
25
26
27
28
29
# File 'lib/archunit/extraction/extract_dependencies.rb', line 21

def extract_dependencies(project_root, exclude_patterns: nil, options: nil)
  options = Common::FluentApi::CheckOptions.resolve(options)
  source_files = enumerate_source_files(project_root, exclude_patterns:)
  root = Pathname.new(path_value(project_root)).expand_path.realpath
  load_paths = LoadPaths.resolve(
    root, explicit_paths: options.load_paths, source_files:
  )
  extract_dependencies_from(root, source_files, load_paths:, options:)
end

.extract_graph(locator = nil, exclude_patterns: nil, options: nil, working_directory: Dir.pwd, profile: nil) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/archunit/extraction/extract_graph.rb', line 22

def extract_graph(
  locator = nil, exclude_patterns: nil, options: nil, working_directory: Dir.pwd,
  profile: nil
)
  ExtractionProfile.validate(profile)
  options = Common::FluentApi::CheckOptions.resolve(options)
  ExtractionProfile.measure(profile, :total) do
    root = ExtractionProfile.measure(profile, :project_discovery) do
      Pathname.new(locate_project(locator, working_directory:))
    end
    patterns = resolve_project_exclude_patterns(root, exclude_patterns)
    key = graph_cache_key(root, exclude_patterns: patterns, options:)

    Common::Logging::Inspection.graph(fetch_graph(root, patterns, options, key, profile), root:)
  end
end

.extract_imports(source_file, project_root:, load_paths: nil, profile: nil, resolution_cache: nil) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/archunit/extraction/extract_imports.rb', line 20

def extract_imports(
  source_file, project_root:, load_paths: nil, profile: nil, resolution_cache: nil
)
  source_path = ExtractionPaths.source_file(source_file)
  root = ExtractionPaths.project_root(project_root)
  extract_imports_from(
    source_path, project_root: root, load_paths:, profile:, resolution_cache:
  )
rescue UserError
  raise
rescue SystemCallError => e
  raise TechnicalError, "could not parse Ruby source file: #{e.message}"
end

.locate_project(locator = nil, working_directory: Dir.pwd) ⇒ Object



12
13
14
15
16
17
18
19
20
# File 'lib/archunit/extraction/locate_project.rb', line 12

def locate_project(locator = nil, working_directory: Dir.pwd)
  working_root = existing_directory(working_directory, :working_directory)
  root = project_root(locator, working_root)
  normalize_path(root.realpath)
rescue UserError, TechnicalError
  raise
rescue SystemCallError => e
  raise TechnicalError, "could not locate project: #{e.message}"
end

.resolve_import(module_name, source_file:, project_root:, import_kind:, load_paths: nil) ⇒ Object



11
12
13
14
15
16
17
18
19
20
# File 'lib/archunit/extraction/resolve_import.rb', line 11

def resolve_import(module_name, source_file:, project_root:, import_kind:, load_paths: nil)
  roots = import_search_roots(source_file, project_root, import_kind, load_paths)
  resolved = if import_kind == Common::Extraction::ImportKind::LOAD
               resolve_load(module_name, roots, import_kind)
             else
               resolve_feature(module_name, roots, import_kind)
             end

  resolved&.tr('\\', '/')&.freeze
end