Class: Whoosh::DependencyInjection

Inherits:
Object
  • Object
show all
Defined in:
lib/whoosh/dependency_injection.rb

Instance Method Summary collapse

Constructor Details

#initializeDependencyInjection

Returns a new instance of DependencyInjection.



5
6
7
8
9
# File 'lib/whoosh/dependency_injection.rb', line 5

def initialize
  @providers = {}
  @singletons = {}
  @mutex = Mutex.new
end

Instance Method Details

#close_allObject



71
72
73
74
75
76
# File 'lib/whoosh/dependency_injection.rb', line 71

def close_all
  @singletons.each_value do |instance|
    instance.close if instance.respond_to?(:close)
  end
  @singletons.clear
end

#inject_for(names, request: nil) ⇒ Object



38
39
40
41
42
# File 'lib/whoosh/dependency_injection.rb', line 38

def inject_for(names, request: nil)
  names.each_with_object({}) do |name, hash|
    hash[name] = resolve(name, request: request)
  end
end

#provide(name, scope: :singleton, &block) ⇒ Object



11
12
13
14
# File 'lib/whoosh/dependency_injection.rb', line 11

def provide(name, scope: :singleton, &block)
  @providers[name] = { block: block, scope: scope }
  @singletons.delete(name) # Clear cached value on re-register
end

#registered?(name) ⇒ Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/whoosh/dependency_injection.rb', line 67

def registered?(name)
  @providers.key?(name)
end

#resolve(name, request: nil, resolving: []) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/whoosh/dependency_injection.rb', line 16

def resolve(name, request: nil, resolving: [])
  provider = @providers[name]
  raise Errors::DependencyError, "Unknown dependency: #{name}" unless provider

  if resolving.include?(name)
    raise Errors::DependencyError, "Circular dependency detected: #{(resolving + [name]).join(' -> ')}"
  end

  case provider[:scope]
  when :singleton
    # Check cache without lock first (double-checked locking pattern)
    return @singletons[name] if @singletons.key?(name)

    # Compute outside the lock to allow recursive singleton resolution
    value = call_provider(provider[:block], request: request, resolving: resolving + [name])
    @mutex.synchronize { @singletons[name] ||= value }
    @singletons[name]
  when :request
    call_provider(provider[:block], request: request, resolving: resolving + [name])
  end
end

#validate!Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/whoosh/dependency_injection.rb', line 44

def validate!
  # Topological sort to detect circular deps and unknown refs at boot
  visited = {}
  sorted = []

  visit = ->(name, path) do
    return if visited[name] == :done
    raise Errors::DependencyError, "Circular dependency detected: #{(path + [name]).join(' -> ')}" if visited[name] == :visiting

    provider = @providers[name]
    raise Errors::DependencyError, "Unknown dependency: #{name} (referenced by #{path.last})" unless provider

    visited[name] = :visiting
    deps = extract_deps(provider[:block])
    deps.each { |dep| visit.call(dep, path + [name]) }
    visited[name] = :done
    sorted << name
  end

  @providers.each_key { |name| visit.call(name, []) unless visited[name] }
  sorted
end