1 var comb = require("comb"), 2 plugins = require("./plugins"), 3 AssociationPlugin = plugins.AssociationPlugin, 4 QueryPlugin = plugins.QueryPlugin, 5 Promise = comb.Promise, 6 PromiseList = comb.PromiseList; 7 8 9 var Model = comb.define([QueryPlugin, AssociationPlugin, comb.plugins.Middleware], { 10 instance : { 11 /** 12 * @lends Model.prototype 13 */ 14 /** 15 * The table this model represent 16 * @type moose.Table 17 * */ 18 table : null, 19 20 /** 21 * moose - read only 22 * 23 * @type moose 24 */ 25 moose : null, 26 27 /** 28 * The database type such as mysql 29 * 30 * @typre String 31 * 32 * */ 33 type : null, 34 35 /** 36 * Whether or not this model is new 37 * */ 38 __isNew : true, 39 40 /** 41 * Signifies if the model has changed 42 * */ 43 __isChanged : false, 44 45 /** 46 * Base class for all models. 47 * <p>This is used through {@link moose.addModel}, <b>NOT directly.</b></p> 48 * 49 * @constructs 50 * @augments moose.plugins.QueryPlugin 51 * @augments moose.plugins.AssociationPlugin 52 * @augments comb.plugins.Middleware 53 * 54 * @param {Object} columnValues values of each column to be used by this Model. 55 * 56 * @property {*} primaryKeyValue the value of this models primaryKey 57 * @property {Boolean} isNew true if this model is new and does not exist in the database. 58 * @property {Boolean} isChanged true if the model has been changed and not saved. 59 * */ 60 constructor : function(options) { 61 if (options) { 62 for (var i in options) { 63 this[i] = options[i]; 64 } 65 } 66 }, 67 68 /** 69 * 70 * Validate values against the model {@link moose.Table#validate} 71 * 72 * @returns {Boolean} true if the values are valid 73 * */ 74 isValid : function(options) { 75 try { 76 return this.table.validate.apply(this.table, arguments); 77 } catch(e) { 78 return false; 79 } 80 }, 81 82 /** 83 * Convert this model to an object, containing column, value pairs. 84 * 85 * @return {Object} the object version of this model. 86 **/ 87 toObject : function() { 88 var columns = this.table.columns, ret = {}; 89 for (var i in columns) { 90 ret[i] = this[i]; 91 } 92 return ret; 93 }, 94 95 /** 96 * Convert this model to JSON, containing column, value pairs. 97 * 98 * @return {JSON} the JSON version of this model. 99 **/ 100 toJson : function() { 101 return JSON.stringify(this.toObject(), null, 4); 102 }, 103 104 getters : { 105 /*Returns my actual primary key value*/ 106 primaryKeyValue : function() { 107 return this[this.primaryKey]; 108 }, 109 110 /*Return if Im a new object*/ 111 isNew : function() { 112 return this.__isNew; 113 }, 114 115 /*Return if Im changed*/ 116 isChanged : function() { 117 return this.__isChanged; 118 } 119 120 } 121 }, 122 123 static : { 124 125 /**@lends Model*/ 126 127 /** 128 * The table that this Model represents. 129 */ 130 table : null, 131 132 /** 133 * moose - read only 134 * 135 * @type moose 136 */ 137 moose : null, 138 139 /** 140 * 141 * Validate values against the Model {@link moose.Table#validate} 142 * 143 * @returns {Boolean} true if the values are valid 144 * */ 145 isValid : function(options) { 146 try { 147 return this.table.validate(options); 148 } catch(e) { 149 //we dont actually want to throw an error just 150 //return false 151 return false; 152 } 153 }, 154 155 /** 156 * Create a new model instance from sql values. 157 * 158 * @example 159 * 160 * var myModel = Model.load({ 161 * myDate : "1999-01-01", 162 * intValue : "1" 163 * }); 164 * 165 * //intValue is converted to a number. 166 * myModel.intValue => 1, 167 * //mydate is converted to a date 168 * myModel.myDate => new Date(1999,01,01); 169 * 170 * @param {Object} values object containing the values to initialize the model with. 171 * 172 * @returns {Model} instantiated model initialized with the values passed in. 173 * */ 174 load : function(values) { 175 //load an object from an object 176 var promise = new Promise(); 177 var m = new this(this.table.fromSql(values)); 178 m._hook("post", "load").then(comb.hitch(promise, "callback", m)); 179 return promise; 180 }, 181 182 /** 183 * Create a new model initialized with the specified values. 184 * 185 * @param {Object} values the values to initialize the model with. 186 * 187 * @returns {Model} instantiated model initialized with the values passed in. 188 */ 189 create : function(values) { 190 //load an object from an object 191 return new this(values); 192 } 193 } 194 195 }); 196 197 /*Adds a setter to an object*/ 198 var addSetter = function(name, col) { 199 return function(val) { 200 col.check(val); 201 if (!this.isNew) this.__isChanged = true; 202 this["_" + name] = val; 203 }; 204 }; 205 206 /*Adds a getter to an object*/ 207 var addGetter = function(name) { 208 return function() { 209 return this["_" + name]; 210 }; 211 }; 212 213 exports.create = function(table, moose, modelOptions) { 214 modelOptions = modelOptions || {}; 215 //Create the default proto 216 var proto = { 217 instance : { 218 table : table, 219 moose : moose, 220 _hooks : ["save", "update", "remove", "load"], 221 __hooks : {pre : {}, post : {}}, 222 getters : { 223 primaryKey : function() { 224 return this.table.pk; 225 } 226 }, 227 setters : {} 228 }, 229 static : { 230 getters : { 231 table : function() { 232 return table; 233 }, 234 235 tableName : function() { 236 return table.tableName; 237 }, 238 239 moose : function() { 240 return moose; 241 }, 242 243 type : function() { 244 return table.type; 245 } 246 }, 247 setters : {} 248 } 249 }; 250 var instance = proto.instance, 251 getters = instance.getters, 252 setters = instance.setters, 253 static = proto.static, 254 staticGetters = proto.getters, 255 staticSetters = proto.setters, 256 modelInstance = modelOptions.instance || {}, 257 modelGetters = modelInstance.getters || {}, 258 modelSetters = modelInstance.setters || {}, 259 modelStatic = modelOptions.static || {}, 260 modelStaticGetters = modelStatic.getters || {}, 261 modelStaticSetters = modelStatic.setters || {}; 262 //remove these so they dont override our proto 263 delete modelInstance.getters; 264 delete modelInstance.setters; 265 delete modelStatic.getters; 266 delete modelStatic.setters; 267 //Mixin the column setter/getters 268 var columns = table.columns; 269 for (var i in columns) { 270 var col = columns[i]; 271 getters[i] = addGetter(i); 272 setters[i] = addSetter(i, col); 273 } 274 275 //Define super and mixins 276 //By Default we include Query and Association plugins 277 var parents = [Model].concat(modelOptions.plugins || []); 278 279 //START MERGE OF PASSED PROTO 280 var merge = comb.merge; 281 282 merge(instance, modelInstance); 283 merge(getters, modelGetters); 284 merge(setters, modelSetters); 285 merge(static, modelStatic); 286 merge(staticGetters, modelStaticGetters); 287 merge(staticSetters, modelStaticSetters); 288 //END MERGE OF PASSED PROTO 289 //Create return model 290 var model = comb.define(parents, proto); 291 //mixin pre and post functions 292 ["pre","post"].forEach(function(op) { 293 var optionsOp = modelOptions[op]; 294 if (optionsOp) { 295 for (var i in optionsOp) { 296 model[op](i, optionsOp[i]); 297 } 298 } 299 }); 300 return model; 301 }; 302 303 304