Class: Wirer::ConstructionSession

Inherits:
Object
  • Object
show all
Defined in:
lib/wirer/construction_session.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(container) ⇒ ConstructionSession

Returns a new instance of ConstructionSession.



6
7
8
9
# File 'lib/wirer/construction_session.rb', line 6

def initialize(container)
  @construction_mutex = Mutex.new
  @container = container
end

Instance Attribute Details

#containerObject (readonly)

Returns the value of attribute container.



4
5
6
# File 'lib/wirer/construction_session.rb', line 4

def container
  @container
end

Instance Method Details

#construct_and_inject_setter_dependencies(factory, instance) ⇒ Object



111
112
113
114
115
116
# File 'lib/wirer/construction_session.rb', line 111

def construct_and_inject_setter_dependencies(factory, instance)
  setter_deps = construct_dependencies(factory.setter_dependencies(instance))
  setter_deps.each do |dep_name, dep|
    factory.inject_dependency(instance, dep_name, dep)
  end
end

#construct_dependencies(dependencies) ⇒ Object



40
41
42
43
44
45
46
# File 'lib/wirer/construction_session.rb', line 40

def construct_dependencies(dependencies)
  result = {}
  dependencies.each do |arg_name, dependency|
    result[arg_name] = construct_dependency(dependency)
  end
  result
end

#construct_dependency(dependency) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/wirer/construction_session.rb', line 48

def construct_dependency(dependency)
  dependency.match_factories(@container.factories) do |factory|
    if dependency.factory?
      if @container.singleton_factories_instances.has_key?(factory)
        raise Error, "Problem with :factory => true dependency: #{factory} was added to the container as a singleton, so only a singleton instance can be supplied, not a wrapped factory"
      end
      curry_factory_with_constructed_dependencies(factory)
    else
      construct_factory_without_args(factory)
    end
  end
end

#construct_factory(factory, *args, &block_arg) ⇒ Object



61
62
63
64
65
66
67
# File 'lib/wirer/construction_session.rb', line 61

def construct_factory(factory, *args, &block_arg)
  if args.empty? && !block_arg
    construct_factory_without_args(factory)
  else
    construct_factory_with_args(factory, *args, &block_arg)
  end
end

#construct_factory_with_args(factory, *args, &block_arg) ⇒ Object



88
89
90
91
92
# File 'lib/wirer/construction_session.rb', line 88

def construct_factory_with_args(factory, *args, &block_arg)
  result = construct_with_constructor_dependencies(factory, *args, &block_arg)
  @queued_for_phase_2.push([factory, result])
  result
end

#construct_factory_without_args(factory) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/wirer/construction_session.rb', line 69

def construct_factory_without_args(factory)
  instance = @container.singleton_factories_instances[factory] and return instance

  if @phase_1_in_progress.include?(factory)
    cycle = @phase_1_in_progress[@phase_1_in_progress.index(factory)..-1] + [factory]
    raise CyclicDependencyError, "Cyclic constructor dependencies. Break the cycle by changing some into setter dependencies:\n#{cycle.map(&:inspect).join("\n")}"
  end
  @phase_1_in_progress.push(factory)
  result = construct_with_constructor_dependencies(factory)
  @phase_1_in_progress.pop

  if @container.singleton_factories_instances.has_key?(factory)
    @container.singleton_factories_instances[factory] = result
  end

  @queued_for_phase_2.push([factory, result])
  result
end

#construct_with_constructor_dependencies(factory, *args, &block_arg) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
# File 'lib/wirer/construction_session.rb', line 94

def construct_with_constructor_dependencies(factory, *args, &block_arg)
  deps = construct_dependencies(factory.constructor_dependencies)
  begin
    factory.new_from_dependencies(deps, *args, &block_arg)
  rescue Wirer::Error
    raise
  rescue => e
    wrapped = DependencyConstructionError.new("Unable to construct factory: #{factory.inspect}", e)
    raise wrapped
  end
end

#construction_sessionObject

N.B. all calls to the private construct_ methods below must be wrapped with this at the top-level entry point. It wraps with a mutex, to avoid race conditions with concurrent attempts to construct a singleton factory, and also ensures that everything constructed in this session gets post_initialized at the end.



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

def construction_session
  @construction_mutex.synchronize do
    begin
      @phase_1_in_progress = []
      @queued_for_phase_2 = []
      @queued_for_post_initialize = []

      result = yield

      until @queued_for_phase_2.empty?
        factory_instance = @queued_for_phase_2.pop
        construct_and_inject_setter_dependencies(*factory_instance)
        @queued_for_post_initialize.push(factory_instance)
      end

      result
    ensure
      post_initialize(@queued_for_post_initialize)
      remove_instance_variable(:@phase_1_in_progress)
      remove_instance_variable(:@queued_for_post_initialize)
    end
  end
end

#curry_factory_with_constructed_dependencies(factory) ⇒ Object



106
107
108
109
# File 'lib/wirer/construction_session.rb', line 106

def curry_factory_with_constructed_dependencies(factory)
  deps = construct_dependencies(factory.constructor_dependencies)
  factory.curry_with_dependencies(deps, self)
end

#post_initialize(factories_instances) ⇒ Object



118
119
120
# File 'lib/wirer/construction_session.rb', line 118

def post_initialize(factories_instances)
  factories_instances.each {|factory, instance| factory.post_initialize(instance)}
end