Class: LazyInit::DependencyResolver

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

Overview

Handles dependency resolution and circular dependency detection for lazy attributes.

This resolver maintains a dependency graph and provides thread-safe resolution with caching to avoid redundant dependency checking. It prevents circular dependencies and optimizes performance through intelligent caching strategies.

Examples:

Basic usage

resolver = DependencyResolver.new(MyClass)
resolver.add_dependency(:database, [:config])
resolver.resolve_dependencies(:database, instance)

Since:

  • 0.1.0

Instance Method Summary collapse

Constructor Details

#initialize(target_class) ⇒ DependencyResolver

Initialize a new dependency resolver for the given class.

Sets up internal data structures for dependency tracking, resolution caching, and thread safety mechanisms.

Parameters:

  • target_class (Class)

    the class that owns the lazy attributes

Since:

  • 0.1.0



23
24
25
26
27
28
29
30
31
32
# File 'lib/lazy_init/dependency_resolver.rb', line 23

def initialize(target_class)
  @target_class = target_class
  @dependency_graph = {}
  @resolution_orders = {}
  @mutex = Mutex.new

  # per-instance caching to avoid redundant dependency resolution
  @instance_resolution_cache = {}
  @cache_mutex = Mutex.new
end

Instance Method Details

#add_dependency(attribute, dependencies) ⇒ void

This method returns an undefined value.

Add a dependency relationship for an attribute.

Records that the given attribute depends on other attributes and pre-computes the resolution order for optimal performance.

Parameters:

  • attribute (Symbol)

    the attribute that has dependencies

  • dependencies (Array<Symbol>, Symbol)

    the attributes it depends on

Since:

  • 0.1.0



42
43
44
45
46
47
48
# File 'lib/lazy_init/dependency_resolver.rb', line 42

def add_dependency(attribute, dependencies)
  @mutex.synchronize do
    @dependency_graph[attribute] = Array(dependencies)
    @resolution_orders[attribute] = compute_resolution_order(attribute)
    invalidate_dependent_orders(attribute)
  end
end

#resolution_order_for(attribute) ⇒ Array<Symbol>?

Get the pre-computed resolution order for an attribute.

Parameters:

  • attribute (Symbol)

    the attribute to get resolution order for

Returns:

  • (Array<Symbol>, nil)

    ordered list of dependencies to resolve

Since:

  • 0.1.0



54
55
56
# File 'lib/lazy_init/dependency_resolver.rb', line 54

def resolution_order_for(attribute)
  @resolution_orders[attribute]
end

#resolve_dependencies(attribute, instance) ⇒ void

This method returns an undefined value.

Resolve all dependencies for an attribute on a specific instance.

Uses intelligent caching to avoid redundant resolution and provides thread-safe dependency resolution with circular dependency detection. The resolution is cached per-instance to optimize repeated access.

Parameters:

  • attribute (Symbol)

    the attribute whose dependencies to resolve

  • instance (Object)

    the instance to resolve dependencies on

Raises:

Since:

  • 0.1.0



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
# File 'lib/lazy_init/dependency_resolver.rb', line 68

def resolve_dependencies(attribute, instance)
  resolution_order = @resolution_orders[attribute]
  return unless resolution_order

  instance_key = instance.object_id
  cache_key = "#{instance_key}_#{attribute}"

  # fast path: if already resolved, skip everything
  return if dependency_resolved_cached?(cache_key)

  # prevent recursive mutex locking in nested dependency chains
  current_thread_resolving = Thread.current[:lazy_init_cache_resolving] ||= false

  if current_thread_resolving
    # we're already inside a resolution chain, skip caching to avoid deadlocks
    resolve_dependencies_direct(attribute, instance, resolution_order)
    return
  end

  # thread-safe cache update for top-level calls only
  @cache_mutex.synchronize do
    # double-check pattern after acquiring lock
    return if dependency_resolved_cached?(cache_key)

    # mark this thread as currently resolving to prevent recursion
    Thread.current[:lazy_init_cache_resolving] = true

    begin
      resolve_dependencies_direct(attribute, instance, resolution_order)
      mark_dependency_resolved(cache_key)
    ensure
      # always clean up thread state
      Thread.current[:lazy_init_cache_resolving] = false
    end
  end
end