1 /**
2 Aedi, a dependency injection library.
3 
4 Aedi is a dependency injection library. It does provide a set of containers that do
5 IoC, and an interface to configure application components (structs, objects, etc.)
6 
7 Aim:
8 
9 The aim of library is to provide a dependency injection solution that is
10 feature rich, easy to use, easy to learn, and easy to extend up to your needs.
11 
12 Usage:
13 
14 For an end consumer of Aedi library, it is rarely needed to extend it's logic
15 with something new. But for a developer that wants to integrate Aedi library
16 with his/her framework, sometimes it is required to extend library to integrate it
17 with rest of framework. Aedi was designed with purpose of extending it in two directions:
18 
19 $(OL
20     $(LI Containers -> responsible for created components. )
21     $(LI Component factories -> responsible for creating components )
22     )
23 
24 A component factory, as name states is responsible for creation and assembly of components
25 registered in container, as well as destroying them. Containers are using them to instantiate components,
26 that are requested. In all tutorials up to this registering a component, was equivalent to creating a
27 factory for it, and registering it in container. Afterwards factory was returned to the client code, were it was
28 further configured, with construction and setter injections by code.
29 
30 When the default features of framework are not enough, it is possible to extend it, in several
31 directions mentioned above. For defining a new way of creating components, a factory interface
32 in listing below is shown, which is minimal requirement for having the custom factories useable by
33 containers. A factory encapsulates the logic required as to create and destroy a component.
34 
35 ------------------
36 interface Factory(T) : LocatorAware!(), AllocatorAware!() {
37 
38 	public {
39 
40 		T factory();
41         void destruct(ref T component);
42 
43 		@property {
44     		TypeInfo type();
45 		}
46 	}
47 }
48 ------------------
49 
50 For example purposes, during development of car simulation app, logging of car assembly
51 should be done. Example below shows a simple logging component factory that creates components
52 using zero argument constructor, and logs the construction using default logging facilities present in
53 D programming language.
54 
55 ------------------
56 class LoggingFactory(T) : Factory!T {
57     private {
58         Locator!() locator_;
59         IAllocator allocator_;
60     }
61 
62     public {
63         @property {
64             typeof(this) allocator(IAllocator allocator) @safe nothrow pure {
65                 this.allocator_ = allocator;
66 
67                 return this;
68             }
69 
70             IAllocator allocator() @safe nothrow pure {
71                 return this.allocator_;
72             }
73         }
74 
75         T factory() {
76             import std.experimental.logger;
77 
78             info("Creating component");
79             static if (is(T : Object)) {
80                 auto t = this.allocator.make!T;
81             } else {
82                 auto t = T();
83             }
84             info("Ended component creation");
85 
86             return t;
87         }
88 
89         void destruct(ref T component) {
90             import std.experimental.logger;
91 
92             info("Destroying component");
93             static if (is(T : Object)) {
94                 this.allocator.dispose(component);
95                 info("Done destroying component");
96             } else {
97                 info("Value component nothing to destroy");
98             }
99         }
100 
101         TypeInfo type() {
102             return typeid(T);
103         }
104 
105         LoggingFactory!T locator(Locator!() locator) @safe nothrow {
106         	this.locator_ = locator;
107 
108         	return this;
109         }
110     }
111 }
112 ------------------
113 
114 A design decision in framework was made, that all containers must store only objects rooted
115 in Object class. Since the language in which framework is developed supports not only object, but
116 structs, unions and a multitude of value based types, those types should be wrapped into an object
117 to be stored into containers. By convention, it is assumed that value based data is wrapped in an
118 implementation of $(D_INLINECODE Wrapper) interface which roots into Object class. Knowing this, factories that
119 attempt to supply value based dependencies to components, fetch the wrapped components from
120 container, extracts the component from wrapper and pass it to the component.
121 
122 From the constraint that containers apply on accepted types (rooted in Object), the same requirements are propagated
123 to component factories which are stored in containers. To leverage this problem, framework
124 provides a decorating factory, that wraps up another factory, and exposes an compatible interface
125 for containers. It will automatically wrap any component that is not rooted in Object class into a
126 $(D_INLINECODE Wrapper) implementation and give it further to container.
127 
128 For aesthetic purposes it is recommended any creation of a factory to be wrapped in a function,
129 which itself can serve as a facade to implementation, and be integrated seamlessly in code api.
130 Example below shows an example of such a wrapper, that handles creation of a component factory and
131 wrapping it in a compatible interface for containers.
132 
133 ------------------
134 auto registerLogged(Type)(Storage!(ObjectFactory, string) container, string identity) {
135     auto factory = new LoggingFactory!Type;
136     container.set(new WrappingFactory!(LoggingFactory!Type)(factory), identity);
137 
138     return factory;
139 }
140 ------------------
141 
142 To test custom component factory example below shows how it can be used seamlessly and
143 unknowingly from the point of view of a user of library.
144 
145 ------------------
146 void main() {
147 
148     SingletonContainer container = singleton();
149 
150     container.registerLogged!Tire("logging.tire");
151     container.registerLogged!int("logging.int");
152 
153     container.instantiate();
154 
155     container.locate!Tire("logging.tire").writeln;
156     container.locate!int("logging.int").writeln;
157 }
158 ------------------
159 
160 Running the container we will get following result:
161 ------------------
162 2017-03-04T00:34:33.401:app.d:factory:161 Creating our little component
163 2017-03-04T00:34:33.401:app.d:factory:167 Ended creation of component
164 2017-03-04T00:34:33.401:app.d:factory:161 Creating our little component
165 2017-03-04T00:34:33.401:app.d:factory:167 Ended creation of component
166 Tire(0 inch, nan atm, )
167 0
168 ------------------
169 
170 As we can see our logging factory is doing designed job.
171 Try to implement your own factory, to understand how Aedi works behind the scenes.
172 
173 License:
174 	Boost Software License - Version 1.0 - August 17th, 2003
175 
176 	Permission is hereby granted, free of charge, to any person or organization
177 	obtaining a copy of the software and accompanying documentation covered by
178 	this license (the "Software") to use, reproduce, display, distribute,
179 	execute, and transmit the Software, and to prepare derivative works of the
180 	Software, and to permit third-parties to whom the Software is furnished to
181 	do so, all subject to the following:
182 
183 	The copyright notices in the Software and this entire statement, including
184 	the above license grant, this restriction and the following disclaimer,
185 	must be included in all copies of the Software, in whole or in part, and
186 	all derivative works of the Software, unless such copies or derivative
187 	works are solely in the form of machine-executable object code generated by
188 	a source language processor.
189 
190 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
191 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
192 	FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
193 	SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
194 	FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
195 	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
196 	DEALINGS IN THE SOFTWARE.
197 
198 Authors:
199 	aermicioi
200 **/
201 
202 module extending_factory;
203 
204 import aermicioi.aedi;
205 import std.stdio;
206 import std.experimental.allocator;
207 
208 class LoggingFactory(T) : Factory!T {
209     private {
210         Locator!() locator_;
211         IAllocator allocator_;
212     }
213 
214     public {
215         @property {
216             /**
217             Set allocator
218 
219             Params:
220                 allocator = allocator that is used to allocate the component
221 
222             Returns:
223                 typeof(this)
224             **/
225             typeof(this) allocator(IAllocator allocator) @safe nothrow pure {
226                 this.allocator_ = allocator;
227 
228                 return this;
229             }
230 
231             /**
232             Get allocator
233 
234             Returns:
235                 IAllocator
236             **/
237             inout(IAllocator) allocator() @safe nothrow pure inout {
238                 return this.allocator_;
239             }
240         }
241 
242         T factory() {
243             import std.experimental.logger;
244 
245             info("Creating component");
246             static if (is(T : Object)) {
247                 auto t = this.allocator.make!T;
248             } else {
249                 auto t = T();
250             }
251             info("Ended component creation");
252 
253             return t;
254         }
255 
256         void destruct(ref T component) {
257             import std.experimental.logger;
258 
259             info("Destroying component");
260             static if (is(T : Object)) {
261                 this.allocator.dispose(component);
262                 info("Done destroying component");
263             } else {
264                 info("Value component nothing to destroy");
265             }
266         }
267 
268         TypeInfo type() const {
269             return typeid(T);
270         }
271 
272         LoggingFactory!T locator(Locator!() locator) @safe nothrow {
273         	this.locator_ = locator;
274 
275         	return this;
276         }
277     }
278 }
279 
280 class Tire {
281     private {
282         int size_;
283         float pressure_;
284         string vendor_;
285     }
286 
287     public @property {
288         Tire size(int size) @safe nothrow {
289         	this.size_ = size;
290 
291         	return this;
292         }
293 
294         int size() @safe nothrow {
295         	return this.size_;
296         }
297 
298         Tire pressure(float pressure) @safe nothrow {
299         	this.pressure_ = pressure;
300 
301         	return this;
302         }
303 
304         float pressure() @safe nothrow {
305         	return this.pressure_;
306         }
307 
308         Tire vendor(string vendor) @safe nothrow {
309         	this.vendor_ = vendor;
310 
311         	return this;
312         }
313 
314         string vendor() @safe nothrow {
315         	return this.vendor_;
316         }
317     }
318 
319     public override string toString() {
320         import std.algorithm;
321         import std.range;
322         import std.conv;
323         import std.utf;
324 
325         return only("Tire(", this.size.to!string, " inch, ", this.pressure.to!string, " atm, ", this.vendor, ")")
326             .joiner
327             .byChar
328             .array;
329     }
330 }
331 
332 auto registerLogged(Type)(Storage!(ObjectFactory, string) container, string identity) {
333     auto factory = new LoggingFactory!Type;
334     container.set(new WrappingFactory!(LoggingFactory!Type)(factory), identity);
335 
336     return factory;
337 }
338 
339 void main() {
340 
341     SingletonContainer container = singleton();
342     scope (exit) container.terminate();
343 
344     container.registerLogged!Tire("logging.tire");
345     container.registerLogged!int("logging.int");
346 
347     container.instantiate();
348 
349     container.locate!Tire("logging.tire").writeln;
350     container.locate!int("logging.int").writeln;
351 }