1 var comb = require("comb"), 2 hitch = comb.hitch, 3 logging = comb.logging, 4 Logger = logging.Logger, 5 util = require('util'), 6 Promise = comb.Promise, 7 PromiseList = comb.PromiseList; 8 9 var moose, adapter; 10 11 /** 12 * @class Wrapper for {@link SQL} adpaters to allow execution functions such as: 13 * <ul> 14 * <li>forEach</li> 15 * <li>one</li> 16 * <li>all</li> 17 * <li>first</li> 18 * <li>last</li> 19 * <li>all</li> 20 * <li>save</li> 21 * </ul> 22 * 23 * This class should be used insead of SQL directly, becuase: 24 * <ul> 25 * <li>Allows for Model creation if needed</li> 26 * <li>Handles the massaging of data to make the use of results easier.</li> 27 * <li>Closing of database connections</li> 28 * </ul> 29 * @name Dataset 30 * @augments SQL 31 * 32 * 33 */ 34 35 var LOGGER = Logger.getLogger("moose.Dataset"); 36 37 var Dataset = comb.define(null, { 38 instance : { 39 /**@lends Dataset.prototype*/ 40 41 constructor: function(table, db, type, model) { 42 if (!table) throw new Error("table is required by dataset"); 43 if (!db) throw new Error("db is required by dataset"); 44 //if(!model) throw new Error("model is required by dataset"); 45 this.super(arguments); 46 this.model = model; 47 this.type = type; 48 }, 49 50 _load : function(results) { 51 var promises = [], retPromise; 52 promises = results.map(function(o) { 53 var p = new Promise(); 54 if (this.model) { 55 var m = this.model.load(o).then(function(m) { 56 m.__isNew = false; 57 p.callback(m); 58 }); 59 } else { 60 p.callback(o); 61 } 62 return p; 63 }, this); 64 65 retPromise = new PromiseList(promises); 66 return retPromise; 67 }, 68 69 /** 70 * Provide Array style looping a query results. 71 * 72 * @example 73 * dataset.forEach(function(r, i){ 74 * console.log("Row %d", i); 75 * }); 76 * 77 * 78 * @param {Function} [callback] executed for each row returned. 79 * @param {Function} [errback] executed if an error occurs. 80 * @param {Object} [scope] scope to execute the callback and errback in. 81 * 82 * @return {comb.Promise} called back with results or the error if one occurs. 83 */ 84 forEach : function(callback, errback, scope) { 85 var retPromise = new Promise(); 86 if (callback) { 87 this.all().addCallback(hitch(this, function(results) { 88 if (results && results.length) { 89 results.forEach(callback, scope); 90 } else { 91 results = null; 92 callback.call(scope || this, null); 93 } 94 retPromise.callback(results); 95 })).addErrback(hitch(retPromise, "errback")); 96 retPromise.addErrback(errback); 97 } else { 98 throw new Error("callback required"); 99 } 100 return retPromise; 101 }, 102 103 /** 104 * Retrieve one row result from the query. 105 * 106 * @example 107 * 108 * dataset.one(function(r){ 109 * Do something.... 110 * }, function(err){ 111 * Do something... 112 * }); 113 * 114 * //OR 115 * 116 * dataset.one().then(function(r){ 117 * Do something.... 118 * }, function(err){ 119 * Do something... 120 * }); 121 * 122 * @param {Function} [callback] executed with the row 123 * @param {Function} [errback] executed if an error occurs. 124 * 125 * @return {comb.Promise} called back with result or the error if one occurs. 126 */ 127 one : function(callback, errback) { 128 var retPromise = new Promise(); 129 this.limit(1); 130 this.exec().addCallback(hitch(this, function(results, fields) { 131 if (results && results.length) { 132 results = this._load(results).then(hitch(this, function(results) { 133 results = results[0][1]; 134 callback && callback(results); 135 retPromise.callback(results); 136 })); 137 } else { 138 results = null; 139 callback && callback(results); 140 retPromise.callback(results); 141 } 142 143 })).addErrback(hitch(retPromise, "errback")); 144 retPromise.addErrback(errback); 145 return retPromise; 146 }, 147 148 /** 149 * Retrieve the first result from an ordered query. 150 * 151 * @example 152 * dataset.first(function(r){ 153 * Do something.... 154 * }, function(err){ 155 * Do something... 156 * }); 157 * 158 * //OR 159 * 160 * dataset.first().then(function(r){ 161 * Do something.... 162 * }, function(err){ 163 * Do something... 164 * }); 165 * 166 * @param {Function} [callback] executed with the row 167 * @param {Function} [errback] executed if an error occurs. 168 * 169 * @return {comb.Promise} called back with result or the error if one occurs. 170 */ 171 first : function(callback, errback) { 172 var retPromise = new Promise(); 173 this.exec().addCallback(hitch(this, function(results, fields) { 174 if (results && results.length) { 175 results = this._load(results).then(hitch(this, function(results) { 176 results = results[0][1]; 177 callback && callback(results); 178 retPromise.callback(results); 179 })); 180 } else { 181 results = null; 182 callback && callback(results); 183 retPromise.callback(results); 184 } 185 })).addErrback(hitch(retPromise, "errback")); 186 retPromise.addErrback(errback); 187 return retPromise; 188 }, 189 190 /** 191 * Retrieve the last result from an ordered query. If the query is not ordered then the result is ambiguous. 192 * 193 * @example 194 * 195 * dataset.last(function(r){ 196 * Do something.... 197 * }, function(err){ 198 * Do something... 199 * }); 200 * 201 * //OR 202 * 203 * dataset.last().then(function(r){ 204 * Do something.... 205 * }, function(err){ 206 * Do something... 207 * }); 208 * 209 * @param {Function} [callback] executed with the row 210 * @param {Function} [errback] executed if an error occurs. 211 * 212 * @return {comb.Promise} called back with result or the error if one occurs. 213 */ 214 last : function(callback, errback) { 215 var retPromise = new Promise(); 216 this.exec().addCallback(hitch(this, function(results, fields) { 217 if (results && results.length) { 218 results = this._load(results).then(hitch(this, function(results) { 219 results = results[results.length - 1][1]; 220 callback && callback(results); 221 retPromise.callback(results); 222 })); 223 } else { 224 results = null; 225 callback && callback(results); 226 retPromise.callback(results); 227 } 228 })).addErrback(hitch(retPromise, "errback")); 229 retPromise.addErrback(errback); 230 return retPromise; 231 232 }, 233 234 /** 235 * Retrieve all rows from the query. 236 * 237 * @example 238 * 239 * dataset.all(function(r){ 240 * Do something.... 241 * }, function(err){ 242 * Do something... 243 * }); 244 * 245 * //OR 246 * 247 * dataset.all().then(function(r){ 248 * Do something.... 249 * }, function(err){ 250 * Do something... 251 * }); 252 * 253 * @param {Function} [callback] executed with the results. 254 * @param {Function} [errback] executed if an error occurs. 255 * 256 * @return {comb.Promise} called back with results or the error if one occurs. 257 */ 258 all : function(callback, errback) { 259 var retPromise = new Promise(); 260 this.exec().addCallback(hitch(this, function(results, fields) { 261 if (results && results.length) { 262 results = this._load(results).then(hitch(this, function(results) { 263 results = results.map(function(r) { 264 return r[1]; 265 }); 266 callback && callback(results); 267 retPromise.callback(results); 268 })); 269 } else { 270 callback && callback(results); 271 retPromise.callback(results); 272 } 273 })).addErrback(hitch(retPromise, "errback")); 274 retPromise.addErrback(errback); 275 return retPromise; 276 }, 277 278 /** 279 * Retrieve the last inserted id from the database. 280 * 281 * @example 282 * 283 * dataset.getLastInsertId(function(r){ 284 * Do something.... 285 * }, function(err){ 286 * Do something... 287 * }); 288 * 289 * //OR 290 * 291 * dataset.getLastInsertId().then(function(r){ 292 * Do something.... 293 * }, function(err){ 294 * Do something... 295 * }); 296 * 297 * @param {Function} [callback] executed with the id 298 * @param {Function} [errback] executed if an error occurs. 299 * 300 * @return {comb.Promise} called back with id or the error if one occurs. 301 */ 302 getLastInsertId : function(callback, errback) { 303 var retPromise = new Promise(); 304 adapter.getLastInsertId(this.db).addCallback(hitch(this, function(results) { 305 if (results) { 306 retPromise.callback(results[0].id); 307 } else { 308 retPromise.callback(null); 309 } 310 })).addErrback(hitch(retPromise, "errback")); 311 retPromise.then(callback, errback); 312 return retPromise; 313 }, 314 315 /** 316 * Save values to a table. 317 * 318 * </br> 319 * <b>This should not be used directly</b> 320 * 321 * @param {Function} [callback] executed with the row 322 * @param {Function} [errback] executed if an error occurs. 323 * 324 * @return {comb.Promise} called back with results or the error if one occurs. 325 */ 326 save : function(vals, loadId, callback, errback) { 327 var retPromise = new Promise(); 328 adapter.save(this.table, vals, this.db).addCallback(hitch(this, function(results) { 329 if (loadId) { 330 retPromise.callback(results.insertId); 331 } else { 332 retPromise.callback(results); 333 } 334 })).addErrback(hitch(retPromise, "errback")); 335 retPromise.addErrback(errback); 336 return retPromise; 337 }, 338 339 /** 340 * Alias for {@link Dataset#all} 341 */ 342 run : function(callback, errback) { 343 return this.all(callback, errback); 344 } 345 } 346 }); 347 348 349 //returns a dataset for a particular type 350 exports.getDataSet = function(table, db, type, model) { 351 if(!moose){ 352 moose = require("./index"), adapter = moose.adapter; 353 } 354 var dataset = comb.define([adapter, Dataset], {}); 355 return new dataset(table, db, type, model); 356 };