1 /**
2  * This module contains objects for defining and scoping dependency registrations.
3  *
4  * Part of the Poodinis Dependency Injection framework.
5  *
6  * Authors:
7  *  Mike Bierlee, m.bierlee@lostmoment.com
8  * Copyright: 2014-2022 Mike Bierlee
9  * License:
10  *  This software is licensed under the terms of the MIT license.
11  *  The full terms of the license can be found in the LICENSE file.
12  */
13 
14 module poodinis.registration;
15 
16 import poodinis.container : DependencyContainer;
17 import poodinis.factory : InstanceFactory, InstanceEventHandler,
18     InstanceCreationException, InstanceFactoryParameters, CreatesSingleton;
19 
20 class Registration
21 {
22     private TypeInfo _registeredType = null;
23     private TypeInfo_Class _instanceType = null;
24     private Registration linkedRegistration;
25     private shared(DependencyContainer) _originatingContainer;
26     private InstanceFactory _instanceFactory;
27     private void delegate() _preDestructor;
28 
29     public @property registeredType()
30     {
31         return _registeredType;
32     }
33 
34     public @property instanceType()
35     {
36         return _instanceType;
37     }
38 
39     public @property originatingContainer()
40     {
41         return _originatingContainer;
42     }
43 
44     public @property instanceFactory()
45     {
46         return _instanceFactory;
47     }
48 
49     public @property preDestructor()
50     {
51         return _preDestructor;
52     }
53 
54     protected @property preDestructor(void delegate() preDestructor)
55     {
56         _preDestructor = preDestructor;
57     }
58 
59     this(TypeInfo registeredType, TypeInfo_Class instanceType,
60             InstanceFactory instanceFactory, shared(DependencyContainer) originatingContainer)
61     {
62         this._registeredType = registeredType;
63         this._instanceType = instanceType;
64         this._originatingContainer = originatingContainer;
65         this._instanceFactory = instanceFactory;
66     }
67 
68     public Object getInstance(InstantiationContext context = new InstantiationContext())
69     {
70         if (linkedRegistration !is null)
71         {
72             return linkedRegistration.getInstance(context);
73         }
74 
75         if (instanceFactory is null)
76         {
77             throw new InstanceCreationException(
78                     "No instance factory defined for registration of type " ~ registeredType.toString());
79         }
80 
81         return instanceFactory.getInstance();
82     }
83 
84     public Registration linkTo(Registration registration)
85     {
86         this.linkedRegistration = registration;
87         return this;
88     }
89 
90     Registration onConstructed(InstanceEventHandler handler)
91     {
92         if (instanceFactory !is null)
93             instanceFactory.onConstructed(handler);
94         return this;
95     }
96 }
97 
98 private InstanceFactoryParameters copyFactoryParameters(Registration registration)
99 {
100     return registration.instanceFactory.factoryParameters;
101 }
102 
103 private void setFactoryParameters(Registration registration, InstanceFactoryParameters newParameters)
104 {
105     registration.instanceFactory.factoryParameters = newParameters;
106 }
107 
108 /**
109  * Sets the registration's instance factory type the same as the registration's.
110  *
111  * This is not a registration scope. Typically used by Poodinis internally only.
112  */
113 public Registration initializeFactoryType(Registration registration)
114 {
115     auto params = registration.copyFactoryParameters();
116     params.instanceType = registration.instanceType;
117     registration.setFactoryParameters(params);
118     return registration;
119 }
120 
121 /**
122  * Scopes registrations to return the same instance every time a given registration is resolved.
123  *
124  * Effectively makes the given registration a singleton.
125  */
126 public Registration singleInstance(Registration registration)
127 {
128     auto params = registration.copyFactoryParameters();
129     params.createsSingleton = CreatesSingleton.yes;
130     registration.setFactoryParameters(params);
131     return registration;
132 }
133 
134 /**
135  * Scopes registrations to return a new instance every time the given registration is resolved.
136  */
137 public Registration newInstance(Registration registration)
138 {
139     auto params = registration.copyFactoryParameters();
140     params.createsSingleton = CreatesSingleton.no;
141     params.existingInstance = null;
142     registration.setFactoryParameters(params);
143     return registration;
144 }
145 
146 /**
147  * Scopes registrations to return the given instance every time the given registration is resolved.
148  */
149 public Registration existingInstance(Registration registration, Object instance)
150 {
151     auto params = registration.copyFactoryParameters();
152     params.createsSingleton = CreatesSingleton.yes;
153     params.existingInstance = instance;
154     registration.setFactoryParameters(params);
155     return registration;
156 }
157 
158 /**
159  * Scopes registrations to create new instances using the given initializer delegate.
160  */
161 public Registration initializedBy(T)(Registration registration, T delegate() initializer)
162         if (is(T == class) || is(T == interface))
163 {
164     auto params = registration.copyFactoryParameters();
165     params.createsSingleton = CreatesSingleton.no;
166     params.factoryMethod = () => cast(Object) initializer();
167     registration.setFactoryParameters(params);
168     return registration;
169 }
170 
171 /**
172  * Scopes registrations to create a new instance using the given initializer delegate. On subsequent resolves the same instance is returned.
173  */
174 public Registration initializedOnceBy(T : Object)(Registration registration, T delegate() initializer)
175 {
176     auto params = registration.copyFactoryParameters();
177     params.createsSingleton = CreatesSingleton.yes;
178     params.factoryMethod = () => cast(Object) initializer();
179     registration.setFactoryParameters(params);
180     return registration;
181 }
182 
183 public string toConcreteTypeListString(Registration[] registrations)
184 {
185     auto concreteTypeListString = "";
186     foreach (registration; registrations)
187     {
188         if (concreteTypeListString.length > 0)
189         {
190             concreteTypeListString ~= ", ";
191         }
192         concreteTypeListString ~= registration.instanceType.toString();
193     }
194     return concreteTypeListString;
195 }
196 
197 class InstantiationContext
198 {
199 }