1 /**
2  * Contains the implementation of application context setup.
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.context;
15 
16 import poodinis.container : DependencyContainer;
17 import poodinis.registration : Registration, existingInstance;
18 import poodinis.factory : CreatesSingleton, InstanceFactoryParameters;
19 import poodinis.autowire : autowire;
20 
21 import std.traits : hasUDA, ReturnType;
22 
23 class ApplicationContext
24 {
25     public void registerDependencies(shared(DependencyContainer) container)
26     {
27     }
28 }
29 
30 /**
31 * A component annotation is used for specifying which factory methods produce components in
32 * an application context.
33 */
34 struct Component
35 {
36 }
37 
38 /**
39 * This annotation allows you to specify by which super type the component should be registered. This
40 * enables you to use type-qualified alternatives for dependencies.
41 */
42 struct RegisterByType(Type)
43 {
44     Type type;
45 }
46 
47 /**
48 * Components with the prototype registration will be scoped as dependencies which will create
49 * new instances every time they are resolved. The factory method will be called repeatedly.
50 */
51 struct Prototype
52 {
53 }
54 
55 /**
56 * Register dependencies through an application context.
57 *
58 * An application context allows you to fine-tune dependency set-up and instantiation.
59 * It is mostly used for dependencies which come from an external library or when you don't
60 * want to use annotations to set-up dependencies in your classes.
61 */
62 public void registerContext(Context : ApplicationContext)(shared(DependencyContainer) container)
63 {
64     auto context = new Context();
65     context.registerDependencies(container);
66     context.registerContextComponents(container);
67     container.register!(ApplicationContext, Context)().existingInstance(context);
68     autowire(container, context);
69 }
70 
71 public void registerContextComponents(ApplicationContextType : ApplicationContext)(
72         ApplicationContextType context, shared(DependencyContainer) container)
73 {
74     foreach (member; __traits(allMembers, ApplicationContextType))
75     {
76         static if (__traits(getProtection, __traits(getMember, context,
77                 member)) == "public" && hasUDA!(__traits(getMember, context, member), Component))
78         {
79             auto factoryMethod = &__traits(getMember, context, member);
80             Registration registration = null;
81             auto createsSingleton = CreatesSingleton.yes;
82 
83             foreach (attribute; __traits(getAttributes, __traits(getMember, context, member)))
84             {
85                 static if (is(attribute == RegisterByType!T, T))
86                 {
87                     registration = container.register!(typeof(attribute.type),
88                             ReturnType!factoryMethod);
89                 }
90                 else static if (__traits(isSame, attribute, Prototype))
91                 {
92                     createsSingleton = CreatesSingleton.no;
93                 }
94             }
95 
96             if (registration is null)
97             {
98                 registration = container.register!(ReturnType!factoryMethod);
99             }
100 
101             registration.instanceFactory.factoryParameters = InstanceFactoryParameters(
102                     registration.instanceType, createsSingleton, null, factoryMethod);
103         }
104     }
105 }