diff --git a/source/poodinis/registration.d b/source/poodinis/registration.d index 1602192..7cc904c 100644 --- a/source/poodinis/registration.d +++ b/source/poodinis/registration.d @@ -1,151 +1,162 @@ -/** - * This module contains objects for defining and scoping dependency registrations. - * - * Part of the Poodinis Dependency Injection framework. - * - * Authors: - * Mike Bierlee, m.bierlee@lostmoment.com - * Copyright: 2014-2015 Mike Bierlee - * License: - * This software is licensed under the terms of the MIT license. - * The full terms of the license can be found in the LICENSE file. - */ - -module poodinis.registration; - -debug { - import std.stdio; - import std.string; -} - -class Registration { - TypeInfo registeredType = null; - TypeInfo_Class instantiatableType = null; - CreationScope registationScope = null; - - this(TypeInfo registeredType, TypeInfo_Class instantiatableType) { - this.registeredType = registeredType; - this.instantiatableType = instantiatableType; - } - - public Object getInstance(InstantiationContext context = new InstantiationContext()) { - if (registationScope is null) { - throw new NoScopeDefinedException(registeredType); - } - - return registationScope.getInstance(); - } -} - -class NoScopeDefinedException : Exception { - this(TypeInfo type) { - super("No scope defined for registration of type " ~ type.toString()); - } -} - -interface CreationScope { - public Object getInstance(); -} - -class NullScope : CreationScope { - public Object getInstance() { - debug(poodinisVerbose) { - writeln("DEBUG: No instance created (NullScope)"); - } - return null; - } -} - -class SingleInstanceScope : CreationScope { - TypeInfo_Class instantiatableType = null; - Object instance = null; - - this(TypeInfo_Class instantiatableType) { - this.instantiatableType = instantiatableType; - } - - public Object getInstance() { - if (instance is null) { - debug(poodinisVerbose) { - writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString())); - } - instance = instantiatableType.create(); - } else { - debug(poodinisVerbose) { - writeln(format("DEBUG: Existing instance returned of type %s (SingleInstanceScope)", instantiatableType.toString())); - } - } - - - return instance; - } -} - -/** - * Scopes registrations to return the same instance every time a given registration is resolved. - * - * Effectively makes the given registration a singleton. - */ -public Registration singleInstance(Registration registration) { - registration.registationScope = new SingleInstanceScope(registration.instantiatableType); - return registration; -} - -class NewInstanceScope : CreationScope { - TypeInfo_Class instantiatableType = null; - - this(TypeInfo_Class instantiatableType) { - this.instantiatableType = instantiatableType; - } - - public Object getInstance() { - debug(poodinisVerbose) { - writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString())); - } - return instantiatableType.create(); - } -} - -/** - * Scopes registrations to return a new instance every time the given registration is resolved. - */ -public Registration newInstance(Registration registration) { - registration.registationScope = new NewInstanceScope(registration.instantiatableType); - return registration; -} - -class ExistingInstanceScope : CreationScope { - Object instance = null; - - this(Object instance) { - this.instance = instance; - } - - public Object getInstance() { - debug(poodinisVerbose) { - writeln("DEBUG: Existing instance returned (ExistingInstanceScope)"); - } - return instance; - } -} - -/** - * Scopes registrations to return the given instance every time the given registration is resolved. - */ -public Registration existingInstance(Registration registration, Object instance) { - registration.registationScope = new ExistingInstanceScope(instance); - return registration; -} - -public string toConcreteTypeListString(Registration[] registrations) { - auto concreteTypeListString = ""; - foreach (registration ; registrations) { - if (concreteTypeListString.length > 0) { - concreteTypeListString ~= ", "; - } - concreteTypeListString ~= registration.instantiatableType.toString(); - } - return concreteTypeListString; -} - -class InstantiationContext {} +/** + * This module contains objects for defining and scoping dependency registrations. + * + * Part of the Poodinis Dependency Injection framework. + * + * Authors: + * Mike Bierlee, m.bierlee@lostmoment.com + * Copyright: 2014-2015 Mike Bierlee + * License: + * This software is licensed under the terms of the MIT license. + * The full terms of the license can be found in the LICENSE file. + */ + +module poodinis.registration; + +debug { + import std.stdio; + import std.string; +} + +class Registration { + TypeInfo registeredType = null; + TypeInfo_Class instantiatableType = null; + CreationScope registationScope = null; + private Registration linkedRegistration; + + this(TypeInfo registeredType, TypeInfo_Class instantiatableType) { + this.registeredType = registeredType; + this.instantiatableType = instantiatableType; + } + + public Object getInstance(InstantiationContext context = new InstantiationContext()) { + if (linkedRegistration !is null) { + return linkedRegistration.getInstance(context); + } + + + if (registationScope is null) { + throw new NoScopeDefinedException(registeredType); + } + + return registationScope.getInstance(); + } + + public Registration linkTo(Registration registration) { + this.linkedRegistration = registration; + return this; + } +} + +class NoScopeDefinedException : Exception { + this(TypeInfo type) { + super("No scope defined for registration of type " ~ type.toString()); + } +} + +interface CreationScope { + public Object getInstance(); +} + +class NullScope : CreationScope { + public Object getInstance() { + debug(poodinisVerbose) { + writeln("DEBUG: No instance created (NullScope)"); + } + return null; + } +} + +class SingleInstanceScope : CreationScope { + TypeInfo_Class instantiatableType = null; + Object instance = null; + + this(TypeInfo_Class instantiatableType) { + this.instantiatableType = instantiatableType; + } + + public Object getInstance() { + if (instance is null) { + debug(poodinisVerbose) { + writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString())); + } + instance = instantiatableType.create(); + } else { + debug(poodinisVerbose) { + writeln(format("DEBUG: Existing instance returned of type %s (SingleInstanceScope)", instantiatableType.toString())); + } + } + + + return instance; + } +} + +/** + * Scopes registrations to return the same instance every time a given registration is resolved. + * + * Effectively makes the given registration a singleton. + */ +public Registration singleInstance(Registration registration) { + registration.registationScope = new SingleInstanceScope(registration.instantiatableType); + return registration; +} + +class NewInstanceScope : CreationScope { + TypeInfo_Class instantiatableType = null; + + this(TypeInfo_Class instantiatableType) { + this.instantiatableType = instantiatableType; + } + + public Object getInstance() { + debug(poodinisVerbose) { + writeln(format("DEBUG: Creating new instance of type %s (SingleInstanceScope)", instantiatableType.toString())); + } + return instantiatableType.create(); + } +} + +/** + * Scopes registrations to return a new instance every time the given registration is resolved. + */ +public Registration newInstance(Registration registration) { + registration.registationScope = new NewInstanceScope(registration.instantiatableType); + return registration; +} + +class ExistingInstanceScope : CreationScope { + Object instance = null; + + this(Object instance) { + this.instance = instance; + } + + public Object getInstance() { + debug(poodinisVerbose) { + writeln("DEBUG: Existing instance returned (ExistingInstanceScope)"); + } + return instance; + } +} + +/** + * Scopes registrations to return the given instance every time the given registration is resolved. + */ +public Registration existingInstance(Registration registration, Object instance) { + registration.registationScope = new ExistingInstanceScope(instance); + return registration; +} + +public string toConcreteTypeListString(Registration[] registrations) { + auto concreteTypeListString = ""; + foreach (registration ; registrations) { + if (concreteTypeListString.length > 0) { + concreteTypeListString ~= ", "; + } + concreteTypeListString ~= registration.instantiatableType.toString(); + } + return concreteTypeListString; +} + +class InstantiationContext {} diff --git a/test/poodinis/registrationtest.d b/test/poodinis/registrationtest.d index 300ca09..36bc257 100644 --- a/test/poodinis/registrationtest.d +++ b/test/poodinis/registrationtest.d @@ -1,80 +1,94 @@ -/** - * Poodinis Dependency Injection Framework - * Copyright 2014-2015 Mike Bierlee - * This software is licensed under the terms of the MIT license. - * The full terms of the license can be found in the LICENSE file. - */ - -import poodinis.registration; - -import std.exception; - -version(unittest) { - class TestType { - } - - // Test getting instance without scope defined throws exception - unittest { +/** + * Poodinis Dependency Injection Framework + * Copyright 2014-2015 Mike Bierlee + * This software is licensed under the terms of the MIT license. + * The full terms of the license can be found in the LICENSE file. + */ + +import poodinis.registration; + +import std.exception; + +version(unittest) { + class TestType {} + + interface TestInterface {} + + class TestImplementation : TestInterface {} + + // Test getting instance without scope defined throws exception + unittest { Registration registration = new Registration(null, null); registration.registeredType = typeid(TestType); - assertThrown!(NoScopeDefinedException)(registration.getInstance()); - } - - // Test getting instance from single instance scope - unittest { - Registration registration = new Registration(null, null); - registration.registationScope = new SingleInstanceScope(typeid(TestType)); - auto instance1 = registration.getInstance(); - auto instance2 = registration.getInstance(); - assert(instance1 is instance2, "Registration with single instance scope did not return the same instance"); - } - - // Test set single instance scope using scope setter - unittest { - Registration registration = new Registration(null, typeid(TestType)); - auto chainedRegistration = registration.singleInstance(); - auto instance1 = registration.getInstance(); - auto instance2 = registration.getInstance(); - assert(instance1 is instance2, "Registration with single instance scope did not return the same instance"); - assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set"); - } - - // Test getting instance from new instance scope - unittest { - Registration registration = new Registration(null, null); - registration.registationScope = new NewInstanceScope(typeid(TestType)); - auto instance1 = registration.getInstance(); - auto instance2 = registration.getInstance(); - assert(instance1 !is instance2, "Registration with new instance scope did not return a different instance"); - } - - // Test set new instance scope using scope setter - unittest { - Registration registration = new Registration(null, typeid(TestType)); - auto chainedRegistration = registration.newInstance(); - auto instance1 = registration.getInstance(); - auto instance2 = registration.getInstance(); - assert(instance1 !is instance2, "Registration with new instance scope did not return a different instance"); - assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set"); - } - - // Test getting instance from existing instance scope - unittest { - Registration registration = new Registration(null, null); - TestType expectedInstance = new TestType(); - registration.registationScope = new ExistingInstanceScope(expectedInstance); - auto actualInstance = registration.getInstance(); - assert(expectedInstance is actualInstance, "Registration with existing instance did not return given instance"); - } - - // Test set existing instance scope using scope setter - unittest { - Registration registration = new Registration(null, null); - auto expectedInstance = new TestType(); - auto chainedRegistration = registration.existingInstance(expectedInstance); - auto actualInstance = registration.getInstance(); - assert(expectedInstance is expectedInstance, "Registration with existing instance scope did not return the same instance"); - assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set"); - } - -} + assertThrown!(NoScopeDefinedException)(registration.getInstance()); + } + + // Test getting instance from single instance scope + unittest { + Registration registration = new Registration(null, null); + registration.registationScope = new SingleInstanceScope(typeid(TestType)); + auto instance1 = registration.getInstance(); + auto instance2 = registration.getInstance(); + assert(instance1 is instance2, "Registration with single instance scope did not return the same instance"); + } + + // Test set single instance scope using scope setter + unittest { + Registration registration = new Registration(null, typeid(TestType)); + auto chainedRegistration = registration.singleInstance(); + auto instance1 = registration.getInstance(); + auto instance2 = registration.getInstance(); + assert(instance1 is instance2, "Registration with single instance scope did not return the same instance"); + assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set"); + } + + // Test getting instance from new instance scope + unittest { + Registration registration = new Registration(null, null); + registration.registationScope = new NewInstanceScope(typeid(TestType)); + auto instance1 = registration.getInstance(); + auto instance2 = registration.getInstance(); + assert(instance1 !is instance2, "Registration with new instance scope did not return a different instance"); + } + + // Test set new instance scope using scope setter + unittest { + Registration registration = new Registration(null, typeid(TestType)); + auto chainedRegistration = registration.newInstance(); + auto instance1 = registration.getInstance(); + auto instance2 = registration.getInstance(); + assert(instance1 !is instance2, "Registration with new instance scope did not return a different instance"); + assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set"); + } + + // Test getting instance from existing instance scope + unittest { + Registration registration = new Registration(null, null); + TestType expectedInstance = new TestType(); + registration.registationScope = new ExistingInstanceScope(expectedInstance); + auto actualInstance = registration.getInstance(); + assert(expectedInstance is actualInstance, "Registration with existing instance did not return given instance"); + } + + // Test set existing instance scope using scope setter + unittest { + Registration registration = new Registration(null, null); + auto expectedInstance = new TestType(); + auto chainedRegistration = registration.existingInstance(expectedInstance); + auto actualInstance = registration.getInstance(); + assert(expectedInstance is expectedInstance, "Registration with existing instance scope did not return the same instance"); + assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set"); + } + + // Test linking registrations + unittest { + Registration firstRegistration = new Registration(typeid(TestInterface), typeid(TestImplementation)).singleInstance(); + Registration secondRegistration = new Registration(typeid(TestImplementation), typeid(TestImplementation)).singleInstance().linkTo(firstRegistration); + + auto firstInstance = firstRegistration.getInstance(); + auto secondInstance = secondRegistration.getInstance(); + + assert(firstInstance is secondInstance); + } + +}