UNPKG

chinood

Version:

Object Data Mapper for Riak built on nodiak (https://npmjs.org/package/nodiak).

389 lines (346 loc) 14.5 kB
// (The MIT License) // Copyright (c) 2012 Coradine Aviation Systems // Copyright (c) 2012 Nathan Aschbacher // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // 'Software'), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. var RObject = require(require.resolve('nodiak')+"/../lib/robject.js"); var inherits = require('trustfund').inherits; var async = require('async'); var BaseModel = function BaseModel(key, init_data, check_constraints) { this.bucket = BaseModel.caller.client ? BaseModel.caller.client.bucket(BaseModel.caller.type.toLowerCase()) : undefined; BaseModel.super_.call(this, this.bucket, key, this.data, this.metadata); if(init_data && init_data.constructor == Object) { this.init(init_data, check_constraints); } }; inherits(BaseModel, RObject); BaseModel.type = 'BaseModel'; BaseModel.prototype.attrs = {}; BaseModel.prototype.hasChanged = true; // true by default, sets to false on find and fetch, until some other change happens. BaseModel.prototype.init = function(data, check_constraints) { if(data && (data).constructor.name == Object.name) { if(check_constraints) { for(var key in data) { this[key] = data[key]; } } else { this.data = data; } return this; } else if((data).constructor.name == String.name) { return this.init(JSON.parse(data)); } }; BaseModel.find = function find(/* criteria, _return */) { var criteria = typeof(arguments[0]) === 'object' ? arguments[0] : {}; //var depth = arguments[1] instanceof Function ? true : false; var _return = arguments[arguments.length-1] instanceof Function ? arguments[arguments.length-1] : function(){}; var caller_inst = new this(); var compiled_results = []; var errors = []; var ops = []; if(criteria.keys instanceof Array) { ops.push(getKeys); } if(criteria.twoi instanceof Object) { ops.push(getTwoi); } if(criteria.solr instanceof Object) { ops.push(getSolr); } if(ops[0]) { async.parallel(ops, function() { errors = errors.length > 0 ? errors : null; _return(errors, compiled_results); }); } else { _return(new TypeError("Invalid Criteria, please make sure that you have keys: [], twoi: [], and/or solr: {} set."), null); } function getKeys(_complete) { caller_inst.bucket.objects.get(criteria.keys).stream(function(response) { response.on('data', function(obj) { obj.__proto__ = caller_inst; obj.hasChanged = false; compiled_results.push(obj); }); response.on('error', function(err) { errors.push(err); }); response.on('end', function() { _complete(); }); }); } function getTwoi(_complete) { caller_inst.bucket.search.twoi(criteria.twoi.range, criteria.twoi.index).stream(function(response) { response.on('data', function(obj) { obj.__proto__ = caller_inst; obj.hasChanged = false; compiled_results.push(obj); }); response.on('error', function(err) { errors.push(err); }); response.on('end', function() { _complete(); }); }); } function getSolr(_complete) { caller_inst.bucket.search.solr(criteria.search).stream(function(response) { response.on('data', function(obj) { obj.__proto__ = caller_inst; obj.hasChanged = false; compiled_results.push(obj); }); response.on('error', function(err) { errors.push(err); }); response.on('end', function() { _complete(); }); }); } }; BaseModel.prototype.setMeta = function(name, value) { BaseModel.super_.prototype.setMeta.apply(this, arguments); this.hasChanged = true; return this; }; BaseModel.prototype.removeMeta = function(name) { BaseModel.super_.prototype.removeMeta.apply(this, arguments); this.hasChanged = true; return this; }; BaseModel.prototype.addToIndex = function(name, value) { BaseModel.super_.prototype.addToIndex.apply(this, arguments); this.hasChanged = true; return this; }; BaseModel.prototype.removeFromIndex = function(name, value) { BaseModel.super_.prototype.removeFromIndex.apply(this, arguments); this.hasChanged = true; return this; }; BaseModel.prototype.clearIndex = function() { BaseModel.super_.prototype.clearIndex.apply(this, arguments); this.hasChanged = true; return this; }; BaseModel.prototype.fetch = function(depth, _return) { _return = _return instanceof Function ? _return : depth; depth = depth instanceof Function ? Infinity : depth; var _this = this; _this.doOp('fetch', depth, function(err, results) { if(err) { _return(err, results); } else { _this.hasChanged = false; _return(err, _this); } }); }; BaseModel.prototype.delete = function(depth, _return) { _return = _return instanceof Function ? _return : depth; depth = depth instanceof Function ? 0 : depth; var _this = this; this.doOp('delete', depth, function(err, results) { if(err) _return(err, undefined); else _return(err, undefined); }); }; BaseModel.prototype.save = function(depth, _return) { _return = _return instanceof Function ? _return : depth; depth = depth instanceof Function ? Infinity : depth; var _this = this; this.doOp('save', depth, function(err, results) { if(err) _return(err, undefined); else _return(err, _this); }); }; BaseModel.prototype.doOp = function(op, depth, _return) { var _this = this; var errors = []; var start_case = function(_complete) { if((op == 'fetch') || (op == 'save' && _this.hasChanged) || op == 'delete') { BaseModel.super_.prototype[op].call(_this, function(err, result) { if(err) { console.log(err); err.data = _this; errors.push(err); } _complete(); }); } else { _complete(); } }; var deep_fetch = function(_complete) { var working_set = []; for(var attr in _this.attrs) { if(_this.attrs[attr] && _this.attrs[attr].is && (_this.attrs[attr].is.super_ == BaseModel || (_this.attrs[attr].of && _this.attrs[attr].of.super_ == BaseModel))) { working_set.push(attr); } } async.forEach(working_set, function(attr, _sub_complete) { //console.log("%s is a %s", attr, _this.data[attr].constructor); if(_this.data[attr] !== undefined && _this.data[attr] !== null) if(_this.data[attr].constructor != Array && _this.data[attr].constructor != Object) { var temp = new _this.attrs[attr].is(_this.data[attr]); temp.doOp(op, depth-1, function(err, result) { if(err) { console.log(err); err.data = item; errors.push(err); } else { _this.data[attr] = temp; } _sub_complete(); }); } else if(_this.data[attr].constructor == Object) { async.forEach(Object.keys(_this.data[attr]), function(sub_key, _sub_sub_complete) { //console.log("in Object, %s is a %s", sub_key, _this.data[attr][sub_key].constructor); var temp = new _this.attrs[attr].of(_this.data[attr][sub_key]); temp.doOp(op, depth-1, function(err, result) { if(err) { console.log(err); err.data = item; errors.push(err); } else { _this.data[attr][sub_key] = temp; } _sub_sub_complete(); }); }, function(err) { _sub_complete(); } ); } else if(_this.data[attr].constructor == Array) { async.map(_this.data[attr], function(sub_key, _sub_sub_complete) { //console.log("in Array, %s is a %s", sub_key, "???"); var temp = new _this.attrs[attr].of(sub_key); //console.log(temp); temp.doOp(op, depth-1, function(err, result) { if(err) { console.log(err); err.data = item; errors.push(err); } //else { sub_key = temp; } _sub_sub_complete(null, temp); }); }, function(err, results) { _this.data[attr] = results; _sub_complete(); } ); } else { _sub_complete(); } else { _sub_complete(); } }, function(err) { _complete(); } ); }; var deep_save_delete = function(_complete) { var working_set = []; for(var attr in _this.attrs) { if(_this.attrs[attr] && _this.attrs[attr].is && (_this.attrs[attr].is.super_ == BaseModel || (_this.attrs[attr].of && _this.attrs[attr].of.super_ == BaseModel))) { working_set.push(_this.data[attr]); } } async.forEach(working_set, function(item, _sub_complete) { if(item !== undefined && item !== null) { if(item.constructor != Array && item.constructor != Object) { if(item.doOp) { item.doOp(op, depth-1, function(err, result) { if(err) { err.data = item; errors.push(err); } _sub_complete(); }); } else { _sub_complete(); } } else if(item.constructor == Object) { var keys = Object.keys(item); async.forEach(keys, function(key, _sub_sub_complete) { if(item[key].doOp) { item[key].doOp(op, depth-1, function(err, result) { if(err) { err.data = item; errors.push(err); } _sub_sub_complete(); }); } else { _sub_sub_complete(); } }, function(err) { _sub_complete(); } ); } else if(item.constructor == Array) { async.forEach(item, function(sub_item, _sub_sub_complete) { if(sub_item.doOp) { sub_item.doOp(op, depth-1, function(err, result) { if(err) { err.data = item; errors.push(err); } _sub_sub_complete(); }); } else { _sub_sub_complete(); } }, function(err) { _sub_complete(); } ); } else { _sub_complete(); } } else { _sub_complete(); } }, function(err) { _complete(); } ); }; if(op == 'fetch') { async.series(depth > 0 ? [start_case, deep_fetch] : [start_case], function() { if(errors.length === 0) { errors = null; } else if(errors.length === 1) { errors = errors[0]; } _return(errors, _this); }); } else { async.parallel(depth > 0 ? [start_case, deep_save_delete] : [start_case], function() { if(errors.length === 0) { errors = null; } else if(errors.length === 1) { errors = errors[0]; } _return(errors, _this); }); } }; module.exports = BaseModel;