Source: manager/modelManager.js

  1. define( [ 'mmirf/resources', 'mmirf/commonUtils', 'mmirf/logger', 'mmirf/util/deferred', 'module' ],
  2. /**
  3. *
  4. * A class for managing the models of the application (MVC-Component). <br>
  5. * It's purpose is to load the models automatically.
  6. *
  7. * This "class" is a singleton - so that only one instance is in
  8. * use.<br>
  9. *
  10. * TODO add example for usage (models as "class" / models as "singleton")
  11. *
  12. * @class
  13. * @name ModelManager
  14. * @memberOf mmir
  15. * @static
  16. * @hideconstructor
  17. *
  18. */
  19. function(
  20. resources, commonUtils, Logger, deferred, module
  21. ){
  22. //the next comment enables JSDoc2 to map all functions etc. to the correct class description
  23. /** @scope mmir.ModelManager.prototype */
  24. /**
  25. * @private
  26. * @type mmir.tools.Logger
  27. * @memberOf mmir.ModelManager#
  28. * @see mmir.Logging#create
  29. */
  30. var logger = Logger.create(module);
  31. // private members
  32. /**
  33. * Map of models
  34. *
  35. * @type Map
  36. * @private
  37. *
  38. * @memberOf mmir.ModelManager#
  39. */
  40. var models = new Map();
  41. /**
  42. * Name of the default namespace
  43. * (within the manager's space)
  44. * into which Models will be loaded
  45. *
  46. * @constant
  47. * @type String
  48. * @private
  49. *
  50. * @memberOf mmir.ModelManager#
  51. */
  52. var MODEL_DEFAULT_NAMESPACE_NAME = 'mmir';
  53. /**
  54. * The "global" namespace for the model manager
  55. *
  56. * @constant
  57. * @type Object
  58. * @private
  59. *
  60. * @memberOf mmir.ModelManager#
  61. */
  62. var MODEL_MANAGER_NAMESPACE = typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : typeof global !== 'undefined' ? global : this;
  63. /**
  64. *
  65. * This function returns the fully qualified model name (including namespace(s)).
  66. *
  67. * @function
  68. * @param {String}
  69. * modelClassName the model's class-name (i.e. without namespace)
  70. * @returns {String} fully qualified name for the model
  71. * @private
  72. *
  73. * @memberOf mmir.ModelManager#
  74. */
  75. function getFullModelName(modelClassName){
  76. if( ! MODEL_DEFAULT_NAMESPACE_NAME){
  77. return modelClassName;
  78. }
  79. else {
  80. return MODEL_DEFAULT_NAMESPACE_NAME + '.' + modelClassName;
  81. }
  82. }
  83. /**
  84. *
  85. * This function returns all loaded models.
  86. *
  87. * @function
  88. * @returns {Array<String>} all loaded model names
  89. * @public
  90. * @memberOf mmir.ModelManager#
  91. */
  92. function getModelNames(){//TODO export this function on _instance?
  93. return Array.from(models.keys());
  94. }
  95. /**
  96. * This function invokes the method
  97. * {@link mmir.ModelManager#foundModelsCallBack} to load all
  98. * models in the path specified by *modelPath*.
  99. *
  100. * @function
  101. * @param {Function} [initCallbackFunction] OPTIONAL
  102. * The callback function from the constructor
  103. * which shall be called after the initialization of the
  104. * {@link mmir.ModelManager}.
  105. *
  106. * @param {Object} [ctx] OPTIONAL
  107. * the context for the model implementations (DEFAULT: the global context, i.e. window)
  108. * @returns {Promise}
  109. * a deferred promise that gets fulfilled when models are loaded.
  110. * @private
  111. * @memberOf mmir.ModelManager#
  112. */
  113. function _init(initCallbackFunction, ctx) {
  114. /** @scope mmir.ModelManager.prototype */
  115. //shift arguments if necessary:
  116. //shift arguments if necessary:
  117. if(!ctx && typeof initCallbackFunction !== 'function'){
  118. ctx = initCallbackFunction;
  119. initCallbackFunction = void(0);
  120. }
  121. /**
  122. *
  123. * This function returns the fully qualified model name (incl. namespace(s)).
  124. *
  125. * @function
  126. * @name getModelByName
  127. * @param {String|Array<String>} fullModelName the fully qualified model name (i.e. with namespace(s))
  128. * Note, if {String} components/namespaces are separated by a <tt>.</tt> (dot)
  129. * If {Array<String>} the entries correspond to the namespace components (without dots),
  130. * where the last entry corresponds to the class/singleton name
  131. * @param {Object} [ctx] OPTIONAL
  132. * the (base) context for the model implementations (DEFAULT: the global context MODEL_MANAGER_NAMESPACE)
  133. * @returns {Object} the "raw" model object (may be a constructor or the main-singleton-namespace).
  134. * Or <tt>null</tt> if there is no Model with the name.
  135. * @private
  136. *
  137. * @requires mmir.CommonUtils#isArray
  138. *
  139. * @see mmir.ModelManager#getFullModelName
  140. * @see mmir.ModelManager#doGetModelInstance
  141. *
  142. * @memberOf mmir.ModelManager#
  143. */
  144. function getModelByName(fullModelName, ctx){
  145. ctx = ctx || MODEL_MANAGER_NAMESPACE;
  146. var components;
  147. if(commonUtils.isArray(fullModelName)){
  148. components = fullModelName;
  149. }
  150. else {
  151. components = fullModelName.split('.');
  152. }
  153. //try to resolve fully-qualified name (without triggering an error)
  154. var currentNameSpace = ctx;
  155. for(var i=0, size = components.length; i < size; ++i){
  156. currentNameSpace = currentNameSpace[components[i]];
  157. if(typeof currentNameSpace !== 'undefined' ){
  158. if(i === size-1){
  159. return currentNameSpace;
  160. }
  161. }
  162. else {
  163. break;
  164. }
  165. }
  166. if(size > 0 && ctx[components[size-1]]){
  167. //try simple model name
  168. return ctx[components[size-1]];
  169. }
  170. var isReTry = ctx !== MODEL_MANAGER_NAMESPACE;
  171. var logFunc = isReTry? 'info' : 'error';
  172. logger[logFunc]('ModelManager.getModelByName: could not find model'
  173. +(isReTry? ' in model context with path "<modelContext>.' : ' "')
  174. +(components.join('.'))
  175. +'": '
  176. +(isReTry? 'there is no' : 'invalid')
  177. +' namespace/class: "'
  178. +components[i]
  179. +'"'
  180. +(isReTry? ', trying to find model in global namespace ...' : '')
  181. );
  182. return null;
  183. }
  184. /**
  185. * Returns the instance for a model implementation:
  186. *
  187. * If the model-object is a constructor (i.e. a function),
  188. * a new instance is created and returned.
  189. *
  190. * Otherwise the model-object itself is returned (e.g. for
  191. * singleton pattern models).
  192. *
  193. * @function
  194. * @private
  195. *
  196. * @see mmir.ModelManager#getModelByName
  197. *
  198. * @memberOf mmir.ModelManager#
  199. */
  200. function doGetModelInstance(modelImplObject){
  201. if(typeof modelImplObject === 'function'){
  202. return new modelImplObject();
  203. }
  204. //TODO allow alternative initialization methods for models?:
  205. // else if(typeof modelImplObject.getInstance === 'function'){
  206. // return modelImplObject.getInstance();
  207. // }
  208. // else if(typeof modelImplObject.init === 'function'){
  209. // return modelImplObject.init();
  210. // }
  211. else{
  212. return modelImplObject;
  213. }
  214. //TODO export to requirejs?
  215. //define(modelImplObject.toString(), function(){ return THE_MODEL_INSTANCE;});
  216. }
  217. var _defer = deferred();
  218. if(initCallbackFunction){
  219. _defer.then(initCallbackFunction, initCallbackFunction);
  220. }
  221. commonUtils.loadImpl(
  222. resources.getModelPath(),
  223. false,
  224. function () {
  225. logger.debug('[loadModels] done');
  226. _defer.resolve(_instance);
  227. },
  228. function isAlreadyLoaded(_name) {
  229. return false; // ( _instance && _instance.getModel(name) ); TODO
  230. },
  231. function callbackStatus(status, fileName, msg) {
  232. if (status === 'info') {
  233. logger.info('[loadModel] "' + fileName);
  234. var modelName = fileName.charAt(0).toUpperCase() + fileName.slice(1).replace(/\.[^.]+$/g, "");
  235. var fullName = getFullModelName(modelName);
  236. var modelImpl = getModelByName(fullName, ctx);
  237. if(!modelImpl){
  238. modelImpl = getModelByName(fullName);
  239. }
  240. var modelInstance;
  241. if (modelImpl) {
  242. modelInstance = doGetModelInstance(modelImpl);
  243. } else {
  244. logger.error('ModelManager.load: Could not find implementation for Model "' + modelName + '" (' + fullName + ') for file ' + fileName);
  245. modelInstance = modelName;
  246. }
  247. models.set(fullName, modelInstance);
  248. }
  249. else if (status === 'warning') {
  250. logger.warn('[loadModel] "' + fileName + '": ' + msg);
  251. }
  252. else if (status === 'error') {
  253. logger.error('[loadModel] "' + fileName + '": ' + msg);
  254. }
  255. else {
  256. logger.error('[loadModel] ' + status + ' (UNKNOWN STATUS) -> "' + fileName + '": ' + msg);
  257. }
  258. }
  259. // , function callbackAfterLoading(jsfile) {
  260. // var modelName = jsfile.charAt(0).toUpperCase() + jsfile.slice(1).replace(/\.[^.]+$/g, "");
  261. // var fullName = getFullModelName(modelName);
  262. // var modelImpl = getModelByName(fullName);
  263. // var modelInstance;
  264. // if (modelImpl) {
  265. // modelInstance = doGetModelInstance(modelImpl);
  266. // } else {
  267. // logger.error('ModelManager.load: Could not find implementation for Model "' + modelName + '" (' + fullName + ') for file ' + jsfile);
  268. // modelInstance = modelName;
  269. // }
  270. // models.set(fullName, modelInstance);
  271. // }
  272. );
  273. return _defer;
  274. };
  275. /**
  276. * Object containing the instance of the class {@link mmir.ModelManager}
  277. *
  278. * @type Object
  279. * @private
  280. * @ignore
  281. */
  282. var _instance = {
  283. /** @scope mmir.ModelManager.prototype */
  284. // public members
  285. /**
  286. * This function gets the model by name.
  287. *
  288. * @function
  289. * @param {String}
  290. * modelName Name of the model which should be returned
  291. * @returns {Object} The model if found, null else
  292. * @public
  293. * @memberOf mmir.ModelManager.prototype
  294. */
  295. get: function(modelName) {
  296. var retModel = null;
  297. // TODO implement mechanism for multiple/configurable model namespaces
  298. // (add optional namespace argument to getModel)
  299. var fullModelName = getFullModelName(modelName);
  300. retModel = models.get(fullModelName);
  301. if (!retModel) {
  302. logger.error('Could not find Model "' + modelName + '" at ' + fullModelName);
  303. return null;
  304. }
  305. return retModel;
  306. },
  307. /**
  308. * This function returns all loaded model names.
  309. *
  310. * @function
  311. * @returns {Array<string>} All loaded model names
  312. * @public
  313. */
  314. getNames: getModelNames,
  315. /**
  316. * This function must be called before using the {@link mmir.ModelManager}. The Initialization process is asynchronous,
  317. * because javascript-files must be loaded (the models), so it forces a synchronous behavior by using
  318. * a callback function containing the instructions, which rely on the presence of the loaded models.<br>
  319. *
  320. * It loads the models and then calls the callback functions and returns the instance of this class.
  321. *
  322. * <div class="box important">
  323. * <b>Note:</b>
  324. * The callback function should contain all (!) instructions which require the prior loading of the models.<br>
  325. * The callback mechanism is necessary, because loading the models is asynchronous.<br><br>
  326. * If provided, the callback function is invoked with 1 argument, the ModelManager instance:<br>
  327. * <code> callbackFunction(modelManagerInstance) </code>
  328. * </div>
  329. *
  330. * NOTE: use EITHER callback-function OR returned Promise -- do not use both!
  331. *
  332. * @function
  333. * @async
  334. * @param {Function} [callbackFunction]
  335. * The function which should be called after loading all controllers
  336. * @param {Object} [ctx] OPTIONAL
  337. * the context for the model implementations (DEFAULT: the global context, i.e. window)
  338. * @returns {Promise}
  339. * a deferred promise that gets fulfilled when models are loaded.
  340. * @example
  341. * function afterLoadingModels(modelManagerInstance){
  342. * var userModel = modelManagerInstance.get('User');
  343. * //do something...
  344. * }
  345. * mmir.model.create(afterLoadingModels);
  346. * @public
  347. */
  348. init: _init
  349. };
  350. return _instance;
  351. });