Class: Expressir::Model::DependencyResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/expressir/model/dependency_resolver.rb

Overview

Resolves dependencies between EXPRESS schemas Handles USE FROM and REFERENCE FROM interfaces Detects circular dependencies

Defined Under Namespace

Classes: CircularDependencyError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_dirs: Dir.pwd, schema_registry: {}, verbose: false) ⇒ DependencyResolver

Initialize resolver

Parameters:

  • base_dirs (String, Array<String>) (defaults to: Dir.pwd)

    Base directories to search

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

    Manual schema name => path mappings

  • verbose (Boolean) (defaults to: false)

    Enable verbose logging



25
26
27
28
29
30
31
32
33
34
35
# File 'lib/expressir/model/dependency_resolver.rb', line 25

def initialize(base_dirs: Dir.pwd, schema_registry: {}, verbose: false)
  @base_dirs = Array(base_dirs).map { |d| File.expand_path(d) }
  @schema_registry = schema_registry || {}
  @verbose = verbose
  @resolved_schemas = Set.new
  @resolution_stack = []
  @unresolved = []
  @last_found_base_index = nil
  @last_found_base_dir = nil
  @multiple_matches = []
end

Instance Attribute Details

#base_dirsObject (readonly)

Returns the value of attribute base_dirs.



19
20
21
# File 'lib/expressir/model/dependency_resolver.rb', line 19

def base_dirs
  @base_dirs
end

#schema_registryObject (readonly)

Returns the value of attribute schema_registry.



19
20
21
# File 'lib/expressir/model/dependency_resolver.rb', line 19

def schema_registry
  @schema_registry
end

#unresolvedObject (readonly)

Returns the value of attribute unresolved.



19
20
21
# File 'lib/expressir/model/dependency_resolver.rb', line 19

def unresolved
  @unresolved
end

#verboseObject (readonly)

Returns the value of attribute verbose.



19
20
21
# File 'lib/expressir/model/dependency_resolver.rb', line 19

def verbose
  @verbose
end

Instance Method Details

#extract_interfaces(schema_path) ⇒ Array<Hash>

Extract interfaces from schema file using proper parsing

Parameters:

  • schema_path (String)

    Path to schema file

Returns:

  • (Array<Hash>)

    List of interfaces with :kind, :schema_name



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/expressir/model/dependency_resolver.rb', line 52

def extract_interfaces(schema_path)
  require_relative "../express/parser"

  interfaces = []

  # Parse the schema file properly
  repo = Expressir::Express::Parser.from_file(schema_path)

  # Extract interfaces from first schema (file should contain one schema)
  schema = repo.schemas.first
  return interfaces unless schema&.interfaces

  schema.interfaces.each do |interface|
    kind = case interface.kind
           when Expressir::Model::Declarations::Interface::USE
             "USE"
           when Expressir::Model::Declarations::Interface::REFERENCE
             "REFERENCE"
           else
             interface.kind.first
           end

    interfaces << {
      kind: kind,
      schema_name: interface.schema.id,
    }
  end

  interfaces
end

#resolve_dependencies(root_schema_path) ⇒ Array<String>

Resolve all dependencies starting from root schema

Parameters:

  • root_schema_path (String)

    Path to root schema file

Returns:

  • (Array<String>)

    Ordered list of schema file paths



40
41
42
43
44
45
46
47
# File 'lib/expressir/model/dependency_resolver.rb', line 40

def resolve_dependencies(root_schema_path)
  @resolved_schemas.clear
  @resolution_stack.clear
  @unresolved.clear

  resolve_recursive(File.expand_path(root_schema_path))
  @resolved_schemas.to_a
end

#resolve_schema_location(schema_name, _kind, _current_path) ⇒ String?

Resolve schema location

Parameters:

  • schema_name (String)

    Schema name to find

  • kind (String)

    Interface kind (“USE” or “REFERENCE”)

  • current_path (String)

    Path of schema requesting the reference

Returns:

  • (String, nil)

    Path to schema file, or nil if not found



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/expressir/model/dependency_resolver.rb', line 88

def resolve_schema_location(schema_name, _kind, _current_path)
  # Check schema_registry first
  if @schema_registry[schema_name]
    registry_path = File.expand_path(@schema_registry[schema_name])
    return registry_path if File.exist?(registry_path)
  end

  # Search ONLY in base_dirs recursively and collect ALL matches
  all_matches = []
  @base_dirs.each_with_index do |base_dir, index|
    candidate = find_schema_in_directory(schema_name, base_dir)
    if candidate
      all_matches << { path: candidate, base_dir: base_dir, index: index }
    end
  end

  # If no matches found, return nil
  return nil if all_matches.empty?

  # If multiple matches found, record for warning
  if all_matches.size > 1
    @multiple_matches << {
      schema_name: schema_name,
      matches: all_matches,
    }
  end

  # Use the first match
  first_match = all_matches.first
  @last_found_base_index = first_match[:index]
  @last_found_base_dir = first_match[:base_dir]

  first_match[:path]
end

#statisticsHash

Get resolution statistics

Returns:

  • (Hash)

    Statistics about resolved schemas



125
126
127
128
129
130
131
132
# File 'lib/expressir/model/dependency_resolver.rb', line 125

def statistics
  {
    total_schemas: @resolved_schemas.size,
    schemas: @resolved_schemas.to_a,
    base_dirs: @base_dirs,
    multiple_matches: @multiple_matches,
  }
end