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 }