1 /**
2  * Модуль содержит методы для работы со совйствами приложения
3  *
4  * Copyright: (c) 2015-2017, Milofon Project.
5  * License: Subject to the terms of the BSD license, as written in the included LICENSE.txt file.
6  * Authors: Maksim Galanin
7  */
8 module dango.system.properties;
9 
10 public
11 {
12     import poodinis : Registration;
13 }
14 
15 private
16 {
17     import poodinis;
18     import proped;
19 
20     import dango.system.container : registerByName;
21     import dango.system.exception : configEnforce;
22 }
23 
24 
25 /**
26  * Получение настроек или генерация исключения
27  * Params:
28  * config = Объект содержащий необходимы ключ конфигурации
29  * key = Ключ конфигруации
30  * msg = Сообщение об ошибке
31  */
32 T getOrEnforce(T)(Properties config, string key, lazy string msg)
33 {
34     static if (is(T == Properties))
35         auto ret = config.sub(key);
36     else
37         auto ret = config.get!T(key);
38 
39     configEnforce(!ret.isNull, msg);
40     return ret.get;
41 }
42 
43 
44 /**
45  * Извление имени из объекта конфигурации
46  * Params:
47  * config = Объект содержащий необходимы ключ конфигурации
48  * msg = Сообщение об ошибке
49  */
50 string getNameOrEnforce(Properties config, string msg)
51 {
52     if (config.isObject)
53         return config.getOrEnforce!string("name", msg);
54     else
55     {
56         auto val = config.get!string;
57         configEnforce(!val.isNull, msg);
58         return val.get;
59     }
60 }
61 
62 /**
63  * Контекст для регистрации системных компоненетов
64  */
65 class PropertiesContext : ApplicationContext
66 {
67     public override void registerDependencies(shared(DependencyContainer) container)
68 	{
69         container.registerByName!(PropertiesLoader, SDLPropertiesLoader)("SDL");
70         container.registerByName!(PropertiesLoader, YAMLPropertiesLoader)("YAML");
71         container.registerByName!(PropertiesLoader, JSONPropertiesLoader)("JSON");
72 	}
73 }
74 
75 
76 /**
77  * Класс обертка на объектом настроек для распространения в DI контейнере
78  */
79 class PropertiesProxy
80 {
81     Properties _properties;
82     alias _properties this;
83 
84     this(Properties properties)
85     {
86         _properties = properties;
87     }
88 }
89 
90 
91 /**
92   * Создает функцию загрузчик
93   * Params:
94   *
95   * container = Контейнер зависимостей
96   */
97 Loader createLoaderFromContainer(shared(DependencyContainer) container)
98 {
99     PropertiesLoader[] loaders = container.resolveAll!PropertiesLoader;
100 
101     return (string fileName)
102     {
103         return loaders.loadProperties(fileName);
104     };
105 }
106 
107 
108 /**
109  * Функция позволяет назначить фабрику передающую параметры в конструктор
110  * для зарегистрированной в контейнере класса
111  * Params:
112  *
113  * registration = Объект регистрации в контейнере
114  * config       = Конфигурация
115  */
116 Registration propertiesInstance(T)(Registration registration, Properties config) {
117 
118     Object propertiesInstanceMethod()
119     {
120         return new T(config);
121     }
122 
123     registration.instanceFactory = new InstanceFactory(registration.instanceType,
124             CreatesSingleton.yes, null, &propertiesInstanceMethod);
125     return registration;
126 }
127 
128 
129 /**
130  * Шаблон для регистрации зависимостей массово
131  * Используется для более простой регистрации зависимостей в контейнер DI
132  *
133  * Params:
134  *
135  * pairs = Массив из пар (наименование, класс)
136  *
137  * Example:
138  * --------------------
139  * auto callback = (Registration reg, string name, Properties cfg) {};
140  * registerControllerDependencies!("api", ControllerApi)(container, config, callback);
141  * --------------------
142  */
143 mixin template registerPropertiesDependencies(C, pairs...)
144 {
145     template GenerateSwitch()
146     {
147         template GenerateSwitchBody(tpairs...)
148         {
149             static if (tpairs.length > 0)
150             {
151                 enum GenerateSwitchBody = `
152                     case ("` ~ tpairs[0]  ~ `"):
153                         auto reg = container.register!(C, ` ~ tpairs[1].stringof ~ `)
154                             .propertiesInstance!(` ~ tpairs[1].stringof ~ `)(cfg);
155                         if (callback !is null)
156                             callback(reg, name, cfg);
157                         break;` ~ GenerateSwitchBody!(tpairs[2..$]);
158             }
159             else
160                 enum GenerateSwitchBody = "";
161         }
162         enum GenerateSwitch = "switch(name)\n{" ~ GenerateSwitchBody!(pairs) ~ "\ndefault: break;\n}";
163     }
164 
165     void registerPropertiesDependencies(shared(DependencyContainer) container, Properties config,
166             void function(Registration, string, Properties) callback = null)
167     {
168         pragma(msg, __MODULE__);
169         // pragma(msg, GenerateSwitch!());
170         foreach (Properties cfg; config.getArray())
171         {
172             auto pName = cfg.get!string("name");
173             if (pName.isNull)
174                 continue;
175             string name = pName.get;
176             mixin(GenerateSwitch!());
177         }
178     }
179 }
180 
181 
182 /**
183  * Функция для регистрации существующего контекста
184  * Params:
185  *
186  * container = Контейнер зависимостей
187  * ctx       = Объект контекста
188  */
189 void registerExistsContext(Context : ApplicationContext)(shared(DependencyContainer) container, Context ctx)
190 {
191     ctx.registerDependencies(container);
192     ctx.registerContextComponents(container);
193     container.register!(ApplicationContext, Context)().existingInstance(ctx);
194     autowire(container, ctx);
195 }
196 
197 
198 void registerExtContext(Context : ApplicationContext)(shared(DependencyContainer) container, Properties config)
199 {
200     auto context = new Context();
201     context.registerDependencies(container, config);
202     context.registerContextComponents(container);
203     container.register!(ApplicationContext, Context)().existingInstance(context);
204     autowire(container, context);
205 }
206