UNPKG

9.78 kBJavaScriptView Raw
1/**
2 * @license
3 * MOST Web Framework 2.0 Codename Blueshift
4 * Copyright (c) 2017, THEMOST LP All rights reserved
5 *
6 * Use of this source code is governed by an BSD-3-Clause license that can be
7 * found in the LICENSE file at https://themost.io/license
8 */
9var _ = require('lodash');
10var Symbol = require('symbol');
11var LangUtils = require("./utils").LangUtils;
12var Args = require('./utils').Args;
13var TraceUtils = require('./utils').TraceUtils;
14var PathUtils = require('./utils').PathUtils;
15var AbstractClassError = require('./errors').AbstractClassError;
16
17var configProperty = Symbol('config');
18var currentConfiguration = Symbol('current');
19var configPathProperty = Symbol('configurationPath');
20var executionPathProperty = Symbol('executionPath');
21var strategiesProperty = Symbol('strategies');
22
23
24/**
25 * @class Represents an application configuration
26 * @param {string=} configPath
27 * @property {*} settings
28 * @constructor
29 */
30function ConfigurationBase(configPath) {
31 //init strategies
32 this[strategiesProperty] = { };
33
34 this[configPathProperty] = configPath || PathUtils.join(process.cwd(),'config');
35 TraceUtils.debug('Initializing configuration under %s.', this[configPathProperty]);
36
37 this[executionPathProperty] = PathUtils.join(this[configPathProperty],'..');
38 TraceUtils.debug('Setting execution path under %s.', this[executionPathProperty]);
39
40 //load default module loader strategy
41 this.useStrategy(ModuleLoaderStrategy, DefaultModuleLoaderStrategy);
42
43 //get configuration source
44 var configSourcePath;
45 try {
46 var env = 'production';
47 //node.js mode
48 if (process && process.env) {
49 env = process.env['NODE_ENV'] || 'production';
50 }
51 //browser mode
52 else if (window && window.env) {
53 env = window.env['BROWSER_ENV'] || 'production';
54 }
55 configSourcePath = PathUtils.join(this[configPathProperty], 'app.' + env + '.json');
56 TraceUtils.debug('Validating environment configuration source on %s.', configSourcePath);
57 this[configProperty] = require(configSourcePath);
58 }
59 catch (err) {
60 if (err.code === 'MODULE_NOT_FOUND') {
61 TraceUtils.log('The environment specific configuration cannot be found or is inaccesible.');
62 try {
63 configSourcePath = PathUtils.join(this[configPathProperty], 'app.json');
64 TraceUtils.debug('Validating application configuration source on %s.', configSourcePath);
65 this[configProperty] = require(configSourcePath);
66 }
67 catch(err) {
68 if (err.code === 'MODULE_NOT_FOUND') {
69 TraceUtils.log('The default application configuration cannot be found or is inaccesible.');
70 }
71 else {
72 TraceUtils.error('An error occured while trying to open default application configuration.');
73 TraceUtils.error(err);
74 }
75 TraceUtils.debug('Initializing empty configuration');
76 this[configProperty] = { };
77 }
78 }
79 else {
80 TraceUtils.error('An error occured while trying to open application configuration.');
81 TraceUtils.error(err);
82 //load default configuration
83 this[configProperty] = { };
84 }
85 }
86 //initialize settings object
87 this[configProperty]['settings'] = this[configProperty]['settings'] || { };
88
89 /**
90 * @property
91 * @name ConfigurationBase#settings
92 * @type {*}
93 */
94
95 Object.defineProperty(this, 'settings',{
96 get: function() {
97 return this[configProperty]['settings'];
98 },
99 enumerable:true,
100 configurable:false});
101
102}
103//noinspection JSUnusedGlobalSymbols
104/**
105 * Returns the configuration source object
106 * @returns {*}
107 */
108ConfigurationBase.prototype.getSource = function() {
109 return this[configProperty];
110};
111//noinspection JSUnusedGlobalSymbols
112/**
113 * Returns the source configuration object based on the given path (e.g. settings.auth.cookieName or settings/auth/cookieName)
114 * @param {string} p - A string which represents an object path
115 * @returns {Object|Array}
116 */
117ConfigurationBase.prototype.getSourceAt = function(p) {
118 return _.at(this[configProperty],p.replace(/\//g,'.'))[0];
119};
120//noinspection JSUnusedGlobalSymbols
121/**
122 * Returns a boolean which indicates whether the specified object path exists or not (e.g. settings.auth.cookieName or settings/auth/cookieName)
123 * @param {string} p - A string which represents an object path
124 * @returns {boolean}
125 */
126ConfigurationBase.prototype.hasSourceAt = function(p) {
127 return _.isObject(_.at(this[configProperty],p.replace(/\//g,'.'))[0]);
128};
129//noinspection JSUnusedGlobalSymbols
130/**
131 * Sets the config value to the specified object path (e.g. settings.auth.cookieName or settings/auth/cookieName)
132 * @param {string} p - A string which represents an object path
133 * @param {*} value
134 * @returns {Object}
135 */
136ConfigurationBase.prototype.setSourceAt = function(p, value) {
137 return _.set(this[configProperty], p.replace(/\//g,'.'), value);
138};
139//noinspection JSUnusedGlobalSymbols
140/**
141 * Sets the current execution path
142 * @param {string} p
143 * @returns ConfigurationBase
144 */
145ConfigurationBase.prototype.setExecutionPath = function(p) {
146 this[executionPathProperty] = p;
147 return this;
148};
149
150/**
151 * Gets the current execution path
152 * @returns {string}
153 */
154ConfigurationBase.prototype.getExecutionPath = function() {
155 return this[executionPathProperty];
156};
157
158/**
159 * Gets the current configuration path
160 * @returns {string}
161 */
162ConfigurationBase.prototype.getConfigurationPath = function() {
163 return this[configPathProperty];
164};
165
166/**
167 * Register a configuration strategy
168 * @param {Function} configStrategyCtor
169 * @param {Function} strategyCtor
170 * @returns ConfigurationBase
171 */
172ConfigurationBase.prototype.useStrategy = function(configStrategyCtor, strategyCtor) {
173 Args.notFunction(configStrategyCtor,"Configuration strategy constructor");
174 Args.notFunction(strategyCtor,"Strategy constructor");
175 this[strategiesProperty]["$".concat(configStrategyCtor.name)] = new strategyCtor(this);
176 return this;
177};
178//noinspection JSUnusedGlobalSymbols
179/**
180 * Gets a configuration strategy
181 * @param {Function} configStrategyCtor
182 */
183ConfigurationBase.prototype.getStrategy = function(configStrategyCtor) {
184 Args.notFunction(configStrategyCtor,"Configuration strategy constructor");
185 return this[strategiesProperty]["$".concat(configStrategyCtor.name)];
186};
187
188/**
189 * Gets a configuration strategy
190 * @param {Function} configStrategyCtor
191 */
192ConfigurationBase.prototype.hasStrategy = function(configStrategyCtor) {
193 Args.notFunction(configStrategyCtor,"Configuration strategy constructor");
194 return typeof this[strategiesProperty]["$".concat(configStrategyCtor.name)] !== 'undefined';
195};
196
197/**
198 * Gets the current configuration
199 * @returns ConfigurationBase - An instance of DataConfiguration class which represents the current data configuration
200 */
201ConfigurationBase.getCurrent = function() {
202 if (_.isNil(ConfigurationBase[currentConfiguration])) {
203 ConfigurationBase[currentConfiguration] = new ConfigurationBase();
204 }
205 return ConfigurationBase[currentConfiguration];
206};
207/**
208 * Sets the current configuration
209 * @param {ConfigurationBase} configuration
210 * @returns ConfigurationBase - An instance of ApplicationConfiguration class which represents the current configuration
211 */
212ConfigurationBase.setCurrent = function(configuration) {
213 if (configuration instanceof ConfigurationBase) {
214 if (!configuration.hasStrategy(ModuleLoaderStrategy)) {
215 configuration.useStrategy(ModuleLoaderStrategy, DefaultModuleLoaderStrategy);
216 }
217 ConfigurationBase[currentConfiguration] = configuration;
218 return ConfigurationBase[currentConfiguration];
219 }
220 throw new TypeError('Invalid argument. Expected an instance of DataConfiguration class.');
221};
222
223/**
224 * @class
225 * @param {ConfigurationBase} config
226 * @constructor
227 * @abstract
228 */
229function ConfigurationStrategy(config) {
230 Args.check(this.constructor.name !== ConfigurationStrategy, new AbstractClassError());
231 Args.notNull(config, 'Configuration');
232 this[configProperty] = config;
233}
234
235/**
236 * @returns {ConfigurationBase}
237 */
238ConfigurationStrategy.prototype.getConfiguration = function() {
239 return this[configProperty];
240};
241
242/**
243 * @class
244 * @constructor
245 * @param {ConfigurationBase} config
246 * @extends ConfigurationStrategy
247 */
248function ModuleLoaderStrategy(config) {
249 ModuleLoaderStrategy.super_.bind(this)(config);
250}
251LangUtils.inherits(ModuleLoaderStrategy, ConfigurationStrategy);
252
253ModuleLoaderStrategy.prototype.require = function(modulePath) {
254 Args.notEmpty(modulePath,'Module Path');
255 if (!/^.\//i.test(modulePath)) {
256 //load module which is not starting with ./
257 if (require.main && typeof require.main.require === 'function') {
258 return require.main.require(modulePath)
259 }
260 return require(modulePath);
261 }
262 return require(PathUtils.join(this.getConfiguration().getExecutionPath(),modulePath));
263};
264
265/**
266 * @class
267 * @constructor
268 * @param {ConfigurationBase} config
269 * @extends ModuleLoaderStrategy
270 */
271function DefaultModuleLoaderStrategy(config) {
272 DefaultModuleLoaderStrategy.super_.bind(this)(config);
273}
274LangUtils.inherits(DefaultModuleLoaderStrategy, ModuleLoaderStrategy);
275
276
277if (typeof exports !== 'undefined') {
278 module.exports.ConfigurationBase = ConfigurationBase;
279 module.exports.ConfigurationStrategy = ConfigurationStrategy;
280 module.exports.ModuleLoaderStrategy = ModuleLoaderStrategy;
281 module.exports.DefaultModuleLoaderStrategy = DefaultModuleLoaderStrategy;
282}
283
284