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 Code api, offers a rich set of functionality, and flexibility. Though alternatives to
13 configuring a container with components should be provided along with their strengths
14 and disadvantages. For this purpose as an alternative to code api, the framework implements
15 annotation based configuration of components.
16 
17 The main idea of annotation based configuration of components is that,
18 information about component's dependencies and configuration properties are
19 stored in form of annotations right in it. The framework does provide
20 annotation based counterparts of almost all configuration primitives available
21 in code api. Example below shows a component configured using annotation based information:
22 
23 --------------------
24 @component // Mark component to be registered in container
25 @qualifier("custom.identity") // Specify custom identity for component in container instead of it's type as identity
26 class Car {
27 
28     // ...
29 
30     public {
31 
32         @constructor(lref!Size, lref!Engine) // Construct this component using arguments passed to constructor annotation
33         this(Size size, Engine engine) {
34             this.size_ = size;
35             this.engine = engine;
36         }
37 
38         @property {
39 
40             @setter("color.green".lref) // Set the property to arguments passed in setter
41             Car color(Color color) @safe nothrow {
42             	this.color_ = color;
43 
44             	return this;
45             }
46 
47             // ...
48 
49             @setter(lref!Engine) // Set the property to arguments passed in setter
50             Car engine(Engine engine) @safe nothrow {
51             	this.engine_ = engine;
52 
53             	return this;
54             }
55 
56             // ...
57 
58             @autowired // Automatically wire property using components from container identified by their types
59             Car frontLeft(Tire frontLeft) @safe nothrow {
60             	this.frontLeft_ = frontLeft;
61 
62             	return this;
63             }
64 
65             // ...
66 
67             @callback(
68                 function (Locator!() locator, ref Car configured) {
69                     configured.frontRight = locator.locate!Tire;
70                 }
71             ) // Use a callback to configure the property, or entire object. Can be attached anywhere on component
72             Car frontRight(Tire frontRight) @safe nothrow {
73             	this.frontRight_ = frontRight;
74 
75             	return this;
76             }
77 
78             // ...
79 
80             @autowired // Automatically wire property using components from container identified by their types
81             Car backLeft(Tire backLeft) @safe nothrow {
82             	this.backLeft_ = backLeft;
83 
84             	return this;
85             }
86 
87             // ...
88 
89             @autowired // Automatically wire property using components from container identified by their types
90             Car backRight(Tire backRight) @safe nothrow {
91             	this.backRight_ = backRight;
92 
93             	return this;
94             }
95 
96             // ...
97 
98         }
99 
100         // ...
101     }
102 }
103 --------------------
104 
105 As seen above describing a component using annotation consists of
106 annotating it with $(D_INLINECODE @component), optionally specyfing
107 an identity using $(D_INLINECODE @qualifier), and annotating it
108 with construction and configuration annotations such as:
109 $(UL
110     $(LI $(D_INLINECODE @constructor ) (only on constructors) - annotates component with it's construction dependencies )
111     $(LI $(D_INLINECODE @setter ) (only on setters) - annotates component with it's setter based dependencies )
112     $(LI $(D_INLINECODE @autowired ) (not on constructors) - annotates component with it's setter based dependencies automatically )
113     $(LI $(D_INLINECODE @autowired ) (on constructors) - annotates component with it's construction dependencies automatically )
114     $(LI $(D_INLINECODE @callback ) (not on constructors) - annotates component with a custom function used to configure component )
115     $(LI $(D_INLINECODE @callback ) (on constructors) - annotates component with a custom function used to create component )
116     $(LI $(D_INLINECODE @contained) (on component) - annotates component with information about container that manages it in a composite/joint container)
117  )
118 
119 Though annotations provide information about a component for framework, it does not
120 automatically register them into container. To add annotated components to a container
121 use $(D_INLINECODE scan ) family of functions. Example below shows how it is
122 possible to register an entire module using just one line of code:
123 
124 -------------------
125 container.scan!(app); // load all annotated components from module "app"
126 -------------------
127 
128 Other forms of scan exists. Check api documentation to see alternatives of module
129 based component registration if needed.
130 
131 The result of running example, will yield into following output:
132 -------------------
133 Uuh, what a nice car, Electric car with following specs:
134 Size:   Size(200, 150, 500)
135 Color:  Color(0, 255, 0)
136 Engine: app.ElectricEngine
137 Tire front left:        Tire(17 inch, 3 atm, divine tire)        located at memory 7FCC35376100
138 Tire front right:       Tire(17 inch, 3 atm, divine tire)        located at memory 7FCC35376140
139 Tire back left:         Tire(17 inch, 3 atm, divine tire)        located at memory 7FCC35376180
140 Tire back right:        Tire(17 inch, 3 atm, divine tire)        located at memory 7FCC353761C0
141 -------------------
142 
143 Check example below. Modify it, run it to understand annotation based configuration.
144 
145 License:
146 	Boost Software License - Version 1.0 - August 17th, 2003
147 
148 	Permission is hereby granted, free of charge, to any person or organization
149 	obtaining a copy of the software and accompanying documentation covered by
150 	this license (the "Software") to use, reproduce, display, distribute,
151 	execute, and transmit the Software, and to prepare derivative works of the
152 	Software, and to permit third-parties to whom the Software is furnished to
153 	do so, all subject to the following:
154 
155 	The copyright notices in the Software and this entire statement, including
156 	the above license grant, this restriction and the following disclaimer,
157 	must be included in all copies of the Software, in whole or in part, and
158 	all derivative works of the Software, unless such copies or derivative
159 	works are solely in the form of machine-executable object code generated by
160 	a source language processor.
161 
162 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
163 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
164 	FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
165 	SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
166 	FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
167 	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
168 	DEALINGS IN THE SOFTWARE.
169 
170 Authors:
171 	aermicioi
172 **/
173 
174 module annotation_configuration;
175 
176 import aermicioi.aedi;
177 import std.stdio;
178 
179 /**
180 A struct that should be managed by container.
181 **/
182 @component // Mark component to be registered in container
183 struct Color {
184     ubyte r;
185     ubyte g;
186     ubyte b;
187 }
188 
189 /**
190 Size of a car.
191 **/
192 @component // Mark component to be registered in container
193 struct Size {
194 
195     @setter(200UL) // Set the property to arguments passed in setter
196     ulong width;
197 
198     @setter(150UL) // Set the property to arguments passed in setter
199     ulong height;
200 
201     @setter(500UL) // Set the property to arguments passed in setter
202     ulong length;
203 }
204 
205 /**
206 Interface for engines.
207 
208 An engine should implement it, in order to be installable in a car.
209 **/
210 interface Engine {
211 
212     public {
213 
214         void turnOn();
215         void run();
216         void turnOff();
217     }
218 }
219 
220 /**
221 A concrete implementation of Engine that uses gasoline for propelling.
222 **/
223 @component // Mark component to be registered in container
224 @qualifier("gasoline")
225 class GasolineEngine : Engine {
226 
227     public {
228 
229         void turnOn() {
230             writeln("pururukVrooomVrrr");
231 
232         }
233 
234         void run() {
235             writeln("vrooom");
236         }
237 
238         void turnOff() {
239             writeln("vrrrPrrft");
240         }
241     }
242 }
243 
244 /**
245 A concrete implementation of Engine that uses diesel for propelling.
246 **/
247 @component // Mark component to be registered in container
248 @qualifier("diesel")
249 class DieselEngine : Engine {
250 
251     public {
252 
253         void turnOn() {
254             writeln("pururukVruumVrrr");
255 
256         }
257 
258         void run() {
259             writeln("vruum");
260         }
261 
262         void turnOff() {
263             writeln("vrrrPft");
264         }
265     }
266 }
267 
268 /**
269 A concrete implementation of Engine that uses electricity for propelling.
270 **/
271 @component // Mark component to be registered in container
272 @qualifier!Engine()
273 class ElectricEngine : Engine {
274     public {
275 
276         void turnOn() {
277             writeln("pzt");
278 
279         }
280 
281         void run() {
282             writeln("vvvvvvvvv");
283         }
284 
285         void turnOff() {
286             writeln("tzp");
287         }
288     }
289 }
290 
291 /**
292 Tire, what it can represent else?
293 **/
294 @component // Mark component to be registered in container
295 @contained("prototype")
296 class Tire {
297     private {
298         int size_;
299         float pressure_;
300         string vendor_;
301     }
302 
303     public @property {
304         @setter(17) // Set the property to arguments passed in setter
305         Tire size(int size) @safe nothrow {
306         	this.size_ = size;
307 
308         	return this;
309         }
310 
311         int size() @safe nothrow {
312         	return this.size_;
313         }
314 
315         @setter(3.0) // Set the property to arguments passed in setter
316         Tire pressure(float pressure) @safe nothrow {
317         	this.pressure_ = pressure;
318 
319         	return this;
320         }
321 
322         float pressure() @safe nothrow {
323         	return this.pressure_;
324         }
325 
326         @setter("tire.vendor".lref) // Set the property to arguments passed in setter
327         Tire vendor(string vendor) @safe nothrow {
328         	this.vendor_ = vendor;
329 
330         	return this;
331         }
332 
333         string vendor() @safe nothrow {
334         	return this.vendor_;
335         }
336     }
337 
338     public override string toString() {
339         import std.algorithm;
340         import std.range;
341         import std.conv;
342         import std.utf;
343 
344         return only("Tire(", this.size.to!string, " inch, ", this.pressure.to!string, " atm, ", this.vendor, ")")
345             .joiner
346             .byChar
347             .array;
348     }
349 }
350 
351 /**
352 A class representing a car.
353 **/
354 @component // Mark component to be registered in container
355 @qualifier("custom.identity")
356 class Car {
357 
358     private {
359         Color color_; // Car color
360         Size size_; // Car size
361         Engine engine_; // Car engine
362 
363         Tire frontLeft_;
364         Tire frontRight_;
365         Tire backLeft_;
366         Tire backRight_;
367     }
368 
369     public {
370 
371         /**
372         Constructor of car.
373 
374         Constructs a car with a set of sizes. A car cannot of course have
375         undefined sizes, so we should provide it during construction.
376 
377         Params:
378             size = size of a car.
379         **/
380         @constructor(lref!Size, lref!Engine) // Construct this component using arguments passed to constructor annotation
381         this(Size size, Engine engine) {
382             this.size_ = size;
383             this.engine = engine;
384         }
385 
386         @property {
387 
388             /**
389             Set color of car
390 
391             Set color of car. A car can live with undefined color (physics allow it).
392 
393             Params:
394             	color = color of a car.
395 
396             Returns:
397             	Car
398             **/
399             @setter("color.green".lref) // Set the property to arguments passed in setter
400             Car color(Color color) @safe nothrow {
401             	this.color_ = color;
402 
403             	return this;
404             }
405 
406             Color color() @safe nothrow {
407             	return this.color_;
408             }
409 
410             Size size() @safe nothrow {
411             	return this.size_;
412             }
413 
414             /**
415             Set engine used in car.
416 
417             Params:
418             	engine = engine used in car to propel it.
419 
420             Returns:
421             	Car
422             **/
423             @setter(lref!Engine) // Set the property to arguments passed in setter
424             Car engine(Engine engine) @safe nothrow {
425             	this.engine_ = engine;
426 
427             	return this;
428             }
429 
430             Engine engine() @safe nothrow {
431             	return this.engine_;
432             }
433 
434             @autowired // Automatically wire property using components from container identified by their types
435             Car frontLeft(Tire frontLeft) @safe nothrow {
436             	this.frontLeft_ = frontLeft;
437 
438             	return this;
439             }
440 
441             Tire frontLeft() @safe nothrow {
442             	return this.frontLeft_;
443             }
444 
445             @callback(
446                 function (Locator!() locator, Car configured) {
447                     configured.frontRight = locator.locate!Tire;
448                 }
449             ) // Use a callback to configure the property, or entire object. Can be attached anywhere on component
450             Car frontRight(Tire frontRight) @safe nothrow {
451             	this.frontRight_ = frontRight;
452 
453             	return this;
454             }
455 
456             Tire frontRight() @safe nothrow {
457             	return this.frontRight_;
458             }
459 
460             @autowired // Automatically wire property using components from container identified by their types
461             Car backLeft(Tire backLeft) @safe nothrow {
462             	this.backLeft_ = backLeft;
463 
464             	return this;
465             }
466 
467             Tire backLeft() @safe nothrow {
468             	return this.backLeft_;
469             }
470 
471             @autowired // Automatically wire property using components from container identified by their types
472             Car backRight(Tire backRight) @safe nothrow {
473             	this.backRight_ = backRight;
474 
475             	return this;
476             }
477 
478             Tire backRight() @safe nothrow {
479             	return this.backRight_;
480             }
481 
482         }
483 
484         void start() {
485             engine.turnOn();
486         }
487 
488         void run() {
489             engine.run();
490         }
491 
492         void stop() {
493             engine.turnOff();
494         }
495     }
496 }
497 
498 class ManufacturedCar : Car {
499         /**
500         Constructor of car.
501 
502         Constructs a car with a set of sizes. A car cannot of course have
503         undefined sizes, so we should provide it during construction.
504 
505         Params:
506             size = size of a car.
507         **/
508         @constructor(lref!Size, lref!Engine) // Construct this component using arguments passed to constructor annotation
509         this(Size size, Engine engine) {
510             super(size, engine);
511         }
512 }
513 
514 /**
515 A manufacturer of cars.
516 **/
517 @component
518 class CarManufacturer {
519 
520     public {
521         @component
522         ManufacturedCar manufacture(Size size) {
523             return new ManufacturedCar(size, new DieselEngine()); // Manufacture a car.
524         }
525     }
526 }
527 
528 void drive(Car car, string name) {
529     writeln("Uuh, what a nice car, ", name," with following specs:");
530     writeln("Size:\t", car.size());
531     writeln("Color:\t", car.color());
532     writeln("Engine:\t", car.engine());
533     writeln("Tire front left:\t", car.frontLeft(), "\t located at memory ", cast(void*) car.frontLeft());
534     writeln("Tire front right:\t", car.frontRight(), "\t located at memory ", cast(void*) car.frontRight());
535     writeln("Tire back left: \t", car.backLeft(), "\t located at memory ", cast(void*) car.backLeft());
536     writeln("Tire back right:\t", car.backRight(), "\t located at memory ", cast(void*) car.backRight());
537 }
538 
539 void main() {
540     auto container = aggregate( // Create a joint container hosting other two containers
541         singleton(), name!(Storage!(ObjectFactory, string)), // Create singleton container, and store it in joint container by "singleton" identity
542         singleton(), "singleton",
543         prototype(), "prototype", // Create prototype container, and store it in joint container by "prototype" identity
544         values(), "parameters"  // Create value container, and store it in joint container by "prototype" identity
545     );
546     scope(exit) container.terminate();
547 
548     container.scan!(annotation_configuration); // load all annotated components from module "annotation_configuration"
549 
550     with (container.configure("singleton")) { // add components that have custom identity
551 
552         register(Color(0, 255, 0), "color.green");
553         register("divine tire", "tire.vendor");
554     }
555 
556     with (container.locate!ValueContainer("parameters").configure) { // add instantiated components
557 
558         register(Size(200, 150, 300), "size.smarty");
559     }
560 
561     container.instantiate(); // Boot containers.
562 
563     container.locate!Car("custom.identity").drive("Electric car"); // drive the car
564 }