The following code should solve these issues:
- Handle namespaces;
- Be a class factory from Pojos
- Be a object factory
- Create a class with inheritance capabilities from a POJO using john resig inheritance implementation;
- Handle singletons
Here is an example of it's usage:
var OliveOil=require('OliveOil')();
var handler=new OliveOil(MY_PATH_FOR_DEFAULT_CLASSES);
//This should load a class that is on MY_PATH_FOR_DEFAULT_CLASSES and create a object form it
var SimpleClass=handler.createObject('SimpleClass',param1,param2,param3);
handler.setNamespaceDir('business',PATH_TO_BUSINESS_MODULE);
//This should load a class on the path to business module with the name of controller
var businessController=handler.createObject('business.Controller',param1);
var childOfSimpleClassPojo={
'parent':'SimpleClass',
_param4:null,
'init':function(param1,param2,param3,param4){
this._super(param1,param2,param3);//Invoking parent constructor
this._param4=param4;
}
}
handler.setClassFromPojo('ChildOfSimpleClass',childOfSimpleClassPojo);
//This should create a class based on the pojo, and also inherits from the previously laoded simpleClass
var simpleObject=handler.createObject('ChildOfSimpleClass',param1,param2,param3,param4);
Here is the full code for the application:
var Class = require('class.extend');
var util=require('util');
var fs=require('fs');
module.exports=function(){
/**
* This class handles the initialization of several components, as well as path mapping, inheritances and namespace colisions
* It's serve as a common library for object,classes and paths lookup
* @type {Object}
*/
var oliveOil={
//A map of classes in which the namespace class name is the index and the class object the item
classMap:{
},
//A map of classes to it's class path
classFileMap:{
},
//A map of class names to objects, it is handled in our singletons objects
objectMap:{
},
//A map of directories based on the namespace
namespaceDirMap:{
},
noNamespaceDir:null,
/**
* Get a class object from the given object
* @param {[type]} namespace [description]
* @return {[type]} [description]
*/
getClass:function(name){
if(this.isClassSet(name)){
return this.classMap[name];
}
if(this.loadClass(name)){
return this.classMap[name];
}
else{
throw new Error('Was not able to load the class '+name);
}
},
loadClass:function(name){
var classPojo=this.getClassFileContents(name);
if(this.setClassFromPojo(name,classPojo)){
return true;
}
throw new Error('Was not able to load the class '+name+' on the path '+this.classFileMap[name]);
},
isObjectSet:function(name){
return !!this.objectMap[name];
},
isClassSet:function(name){
return !!this.classMap[name];
},
createObject:function(name, var_args){
//Getting the var_args
var params = Array.prototype.slice.call(arguments, 1);
if(!this.isClassSet(name)){
if(!this.loadClass(name)){
throw new Error('The class "'+name+'" is not set');
}
}
var DesiredClass=this.getClass(name);
//Needed for us to apply the constructor
var object = Object.create(DesiredClass.prototype);
result= DesiredClass.apply(object, params);
if (typeof result === 'object') {
return result;
}
return object;
},
getClassFile:function(name){
return this.classFileMap[name];
},
_getFile:function(path){
if(path.indexOf('.js')!=path.length-3){
path+='.js'
}
if(!fs.existsSync(path)){
throw new Error('the path '+path+' does not exists');
}
return require(path);
},
getClassFileContents:function(name){
if(!this.isClassFileSet(name)){
var namespaceAndName=this.getNamespaceAndNameFromPath(name);
var namespace=namespaceAndName['namespace'];
var className=namespaceAndName['className'];
if(namespace && !this.isNamespaceSet(namespace)){
throw new Error('The namespace '+namespace+' is not set');
}
if(namespace){
this.setClassFileByNamespace(namespace,className);
}
else{
this.setClassFile(name,this.noNamespaceDir+name);
}
}
return this._getFile(this.classFileMap[name]);
},
setClassFileByNamespace:function(namespace,className){
var fullName=namespace+'.'+className;
if(this.isClassFileSet(fullName)){
throw new Error('The class '+className+' on the namespace '+namespace+' is already mapped');
}
var path=this.getNamespaceDir(namespace)+'/'+className;
this.classFileMap[fullName]=path;
return true;
},
getNamespaceAndNameFromPath:function(name){
var pos=name.lastIndexOf('.');
if(pos==-1){
return {
'namespace':false,
'className':name
}
}
return {
'namespace':name.substr(0,pos),
'className':name.substr(pos+1)
};
},
getNamespaceFromPath:function(name){
var pos=name.lastIndexOf('.');
if(pos==-1){
throw new Error(name+' is not a valid namespace');
}
return name.substr(0,pos);
},
getClassNameFromPath:function(name){
var pos=name.lastIndexOf('.');
if(pos==-1){
throw new Error(name+ ' is not a namespace');
}
return name.substr(pos+1);
},
getSingletonObject:function(name,var_args){
if(this.isObjectSet(name)){
return this.objectMap[name];
}
this.objectMap[name]=this.createObject.apply(this,arguments);
return this.objectMap[name];
},
/**
* Sets a class on the given namespace to be the class object
* @type {[type]}
*/
setClassFromPojo:function(name,pojoData){
var ParentClass;
if(pojoData.parent){
ParentClass=this.getClass(pojoData.parent);
}
else{
ParentClass=Class;
}
return this.setClass(name,ParentClass.extend(pojoData));
},
/**
* Sets a class on the given namespace to be the class object
* @type {[type]}
*/
setClass:function(name,classObject){
if(this.isClassSet(name)){
throw new Error('The class '+name+' is already set');
}
this.classMap[name]=classObject;
return true;
},
setMultipleNamespacesDir:function(namespaceMap){
for(var name in namespaceDirMap){
this.setNamespaceDir(name,namespaceDirMap[name]);
}
return true;
},
setNamespaceDir:function(name,dir){
if(this.isNamespaceSet(name)){
throw new Error('The namespace '+name+' is already set');
}
this.namespaceDirMap[name]=dir;
return true;
},
getNamespaceDir:function(name){
return this.namespaceDirMap[name];
},
isNamespaceSet:function(name){
return !!this.namespaceDirMap[name];
},
isClassFileSet:function(name){
return !!this.classFileMap[name];
},
setClassFile:function(name,file){
if(this.isClassFileSet(name)){
throw new Error('The class '+name+' is already set with a path');
}
this.classFileMap[name]=file;
return true;
},
setObject:function(name,object){
if(this.isObjectSet(name)){
throw new Error('The object '+name+' is already set');
}
this.objectMap[name]=object;
},
init:function(noNamespaceDir){
this.noNamespaceDir=noNamespaceDir;
}
};
return Class.extend(oliveOil);
}
What improvements could I make?