diff --git a/lib/connectors/cradle.js b/lib/connectors/cradle.js deleted file mode 100644 index 89d9d8ed..00000000 --- a/lib/connectors/cradle.js +++ /dev/null @@ -1,343 +0,0 @@ -var safeRequire = require('../utils').safeRequire; - -/** - * Module dependencies - */ -var cradle = safeRequire('cradle'); - -/** - * Private functions for internal use - */ -function CradleAdapter(client) { - this._models = {}; - this.client = client; -} - -function createdbif(client, callback) { - client.exists(function (err, exists) { - if (err) callback(err); - if (!exists) { - client.create(function () { - callback(); - }); - } - else { - callback(); - } - }); -} - -function naturalize(data, model) { - data.nature = model; - //TODO: maybe this is not a really good idea - if (data.date) data.date = data.date.toString(); - return data; -} -function idealize(data) { - data.id = data._id; - return data; -} -function stringify(data) { - return data ? data.toString() : data -} - -function errorHandler(callback, func) { - return function (err, res) { - if (err) { - console.log('cradle', err); - callback(err); - } else { - if (func) { - func(res, function (res) { - callback(null, res); - }); - } else { - callback(null, res); - } - } - } -}; - -function synchronize(functions, args, callback) { - if (functions.length === 0) callback(); - if (functions.length > 0 && args.length === functions.length) { - functions[0](args[0][0], args[0][1], function (err, res) { - if (err) callback(err); - functions.splice(0, 1); - args.splice(0, 1); - synchronize(functions, args, callback); - }); - } -}; - -function applyFilter(filter) { - if (typeof filter.where === 'function') { - return filter.where; - } - var keys = Object.keys(filter.where); - return function (obj) { - var pass = true; - keys.forEach(function (key) { - if (!test(filter.where[key], obj[key])) { - pass = false; - } - }); - return pass; - } - - function test(example, value) { - if (typeof value === 'string' && example && example.constructor.name === 'RegExp') { - return value.match(example); - } - // not strict equality - return example == value; - } -} - -function numerically(a, b) { - return a[this[0]] - b[this[0]]; -} - -function literally(a, b) { - return a[this[0]] > b[this[0]]; -} - -function filtering(res, model, filter, instance) { - - if (model) { - if (filter == null) filter = {}; - if (filter.where == null) filter.where = {}; - filter.where.nature = model; - } - // do we need some filtration? - if (filter.where) { - res = res ? res.filter(applyFilter(filter)) : res; - } - - // do we need some sorting? - if (filter.order) { - var props = instance[model].properties; - var allNumeric = true; - var orders = filter.order; - var reverse = false; - if (typeof filter.order === "string") { - orders = [filter.order]; - } - - orders.forEach(function (key, i) { - var m = key.match(/\s+(A|DE)SC$/i); - if (m) { - key = key.replace(/\s+(A|DE)SC/i, ''); - if (m[1] === 'DE') reverse = true; - } - orders[i] = key; - if (props[key].type.name !== 'Number') { - allNumeric = false; - } - }); - if (allNumeric) { - res = res.sort(numerically.bind(orders)); - } else { - res = res.sort(literally.bind(orders)); - } - if (reverse) res = res.reverse(); - } - return res; -} - -/** - * Connection/Disconnection - */ -exports.initialize = function (dataSource, callback) { - if (!cradle) return; - - // when using cradle if we dont wait for the dataSource to be connected, the models fails to load correctly. - dataSource.waitForConnect = true; - if (!dataSource.settings.url) { - var host = dataSource.settings.host || 'localhost'; - var port = dataSource.settings.port || '5984'; - var options = dataSource.settings.options || { - cache: true, - raw: false - }; - if (dataSource.settings.username) { - options.auth = {}; - options.auth.username = dataSource.settings.username; - if (dataSource.settings.password) { - options.auth.password = dataSource.settings.password; - } - } - var database = dataSource.settings.database || 'loopback-datasource-juggler'; - - dataSource.settings.host = host; - dataSource.settings.port = port; - dataSource.settings.database = database; - dataSource.settings.options = options; - } - dataSource.client = new (cradle.Connection)(dataSource.settings.host, dataSource.settings.port, dataSource.settings.options).database(dataSource.settings.database); - - createdbif( - dataSource.client, - errorHandler(callback, function () { - dataSource.connector = new CradleAdapter(dataSource.client); - process.nextTick(callback); - })); -}; - -CradleAdapter.prototype.disconnect = function () { -}; - -/** - * Write methods - */ -CradleAdapter.prototype.define = function (descr) { - this._models[descr.model.modelName] = descr; -}; - -CradleAdapter.prototype.create = function (model, data, callback) { - this.client.save( - stringify(data.id), - naturalize(data, model), - errorHandler(callback, function (res, cb) { - cb(res.id); - }) - ); -}; - -CradleAdapter.prototype.save = function (model, data, callback) { - this.client.save( - stringify(data.id), - naturalize(data, model), - errorHandler(callback) - ) -}; - -CradleAdapter.prototype.updateAttributes = function (model, id, data, callback) { - this.client.merge( - stringify(id), - data, - errorHandler(callback, function (doc, cb) { - cb(idealize(doc)); - }) - ); -}; - -CradleAdapter.prototype.updateOrCreate = function (model, data, callback) { - this.client.get( - stringify(data.id), - function (err, doc) { - if (err) { - this.create(model, data, callback); - } else { - this.updateAttributes(model, data.id, data, callback); - } - }.bind(this) - ) -}; - -/** - * Read methods - */ -CradleAdapter.prototype.exists = function (model, id, callback) { - this.client.get( - stringify(id), - errorHandler(callback, function (doc, cb) { - cb(!!doc); - }) - ); -}; - -CradleAdapter.prototype.find = function (model, id, callback) { - this.client.get( - stringify(id), - errorHandler(callback, function (doc, cb) { - cb(idealize(doc)); - }) - ); -}; - -CradleAdapter.prototype.count = function (model, callback, where) { - this.models( - model, - {where: where}, - callback, - function (docs, cb) { - cb(docs.length); - } - ); -}; - -CradleAdapter.prototype.models = function (model, filter, callback, func) { - var limit = 200; - var skip = 0; - if (filter != null) { - limit = filter.limit || limit; - skip = filter.skip || skip; - } - - var self = this; - - self.client.save('_design/' + model, { - views: { - all: { - map: 'function(doc) { if (doc.nature == "' + model + '") { emit(doc._id, doc); } }' - } - } - }, function () { - self.client.view(model + '/all', {include_docs: true, limit: limit, skip: skip}, - errorHandler(callback, function (res, cb) { - var docs = res.map(function (doc) { - return idealize(doc); - }); - var filtered = filtering(docs, model, filter, this._models) - - func ? func(filtered, cb) : cb(filtered); - }.bind(self))); - }); -}; - -CradleAdapter.prototype.all = function (model, filter, callback) { - this.models( - model, - filter, - callback - ); -}; - -/** - * Detroy methods - */ -CradleAdapter.prototype.destroy = function (model, id, callback) { - this.client.remove( - stringify(id), - function (err, doc) { - callback(err); - } - ); -}; - -CradleAdapter.prototype.destroyAll = function (model, callback) { - this.models( - model, - null, - callback, - function (docs, cb) { - var docIds = docs.map(function (doc) { - return doc.id; - }); - this.client.get(docIds, function (err, res) { - if (err) cb(err); - - var funcs = res.map(function (doc) { - return this.client.remove.bind(this.client); - }.bind(this)); - - var args = res.map(function (doc) { - return [doc._id, doc._rev]; - }); - - synchronize(funcs, args, cb); - }.bind(this)); - }.bind(this) - ); -}; diff --git a/lib/connectors/http.js b/lib/connectors/http.js deleted file mode 100644 index 22f9f314..00000000 --- a/lib/connectors/http.js +++ /dev/null @@ -1,190 +0,0 @@ -exports.initialize = function initializeSchema(dataSource, callback) { - dataSource.connector = new WebService(); - process.nextTick(callback); -}; - -function WebService() { - this._models = {}; - this.cache = {}; - this.ids = {}; -} - -WebService.prototype.installPostProcessor = function installPostProcessor(descr) { - var dates = []; - Object.keys(descr.properties).forEach(function (column) { - if (descr.properties[column].type.name === 'Date') { - dates.push(column); - } - }); - - var postProcessor = function (model) { - var max = dates.length; - for (var i = 0; i < max; i++) { - var column = dates[i]; - if (model[column]) { - model[column] = new Date(model[column]); - } - } - ; - }; - - descr.postProcessor = postProcessor; -}; - -WebService.prototype.preProcess = function preProcess(data) { - var result = {}; - Object.keys(data).forEach(function (key) { - if (data[key] != null) { - result[key] = data[key]; - } - }) - return result; -}; - -WebService.prototype.postProcess = function postProcess(model, data) { - var postProcessor = this._models[model].postProcessor; - if (postProcessor && data) { - postProcessor(data); - } -}; - -WebService.prototype.postProcessMultiple = function postProcessMultiple(model, data) { - var postProcessor = this._models[model].postProcessor; - if (postProcessor) { - var max = data.length; - for (var i = 0; i < max; i++) { - if (data[i]) { - postProcessor(data[i]); - } - } - ; - } -}; - -WebService.prototype.define = function defineModel(descr) { - var m = descr.model.modelName; - this.installPostProcessor(descr); - this._models[m] = descr; -}; - -WebService.prototype.getResourceUrl = function getResourceUrl(model) { - var url = this._models[model].settings.restPath; - if (!url) throw new Error('Resource url (restPath) for ' + model + ' is not defined'); - return url; -}; - -WebService.prototype.getBlankReq = function () { - if (!this.csrfToken) { - this.csrfToken = $('meta[name=csrf-token]').attr('content'); - this.csrfParam = $('meta[name=csrf-param]').attr('content'); - } - var req = {}; - req[this.csrfParam] = this.csrfToken; - return req; -} - -WebService.prototype.create = function create(model, data, callback) { - var req = this.getBlankReq(); - req[model] = this.preProcess(data); - $.post(this.getResourceUrl(model) + '.json', req, function (res) { - if (res.code === 200) { - callback(null, res.data.id); - } else { - callback(res.error); - } - }, 'json'); - // this.cache[model][id] = data; -}; - -WebService.prototype.updateOrCreate = function (model, data, callback) { - var mem = this; - this.exists(model, data.id, function (err, exists) { - if (exists) { - mem.save(model, data, callback); - } else { - mem.create(model, data, function (err, id) { - data.id = id; - callback(err, data); - }); - } - }); -}; - -WebService.prototype.save = function save(model, data, callback) { - var _this = this; - var req = this.getBlankReq(); - req._method = 'PUT'; - req[model] = this.preProcess(data); - $.post(this.getResourceUrl(model) + '/' + data.id + '.json', req, function (res) { - if (res.code === 200) { - _this.postProcess(model, res.data); - callback(null, res.data); - } else { - callback(res.error); - } - }, 'json'); -}; - -WebService.prototype.exists = function exists(model, id, callback) { - $.getJSON(this.getResourceUrl(model) + '/' + id + '.json', function (res) { - if (res.code === 200) { - callback(null, true); - } else if (res.code === 404) { - callback(null, false); - } else { - callback(res.error); - } - }); -}; - -WebService.prototype.find = function find(model, id, callback) { - var _this = this; - $.getJSON(this.getResourceUrl(model) + '/' + id + '.json', function (res) { - if (res.code === 200) { - _this.postProcess(model, res.data); - callback(null, res.data); - } else { - callback(res.error); - } - }); -}; - -WebService.prototype.destroy = function destroy(model, id, callback) { - var _this = this; - var req = this.getBlankReq(); - req._method = 'DELETE'; - $.post(this.getResourceUrl(model) + '/' + id + '.json', req, function (res) { - if (res.code === 200) { - //delete _this.cache[model][id]; - callback(null, res.data); - } else { - callback(res.error); - } - }, 'json'); -}; - -WebService.prototype.all = function all(model, filter, callback) { - var _this = this; - $.getJSON(this.getResourceUrl(model) + '.json?query=' + encodeURIComponent(JSON.stringify(filter)), function (res) { - if (res.code === 200) { - _this.postProcessMultiple(model, res.data); - callback(null, res.data); - } else { - callback(res.error); - } - }); -}; - -WebService.prototype.destroyAll = function destroyAll(model, callback) { - throw new Error('Not supported'); -}; - -WebService.prototype.count = function count(model, callback, where) { - throw new Error('Not supported'); -}; - -WebService.prototype.updateAttributes = function (model, id, data, callback) { - data.id = id; - this.save(model, data, callback); -}; - diff --git a/lib/connectors/neo4j.js b/lib/connectors/neo4j.js deleted file mode 100644 index d4a9c121..00000000 --- a/lib/connectors/neo4j.js +++ /dev/null @@ -1,373 +0,0 @@ -var safeRequire = require('../utils').safeRequire; - -/** - * Module dependencies - */ -var neo4j = safeRequire('neo4j'); - -exports.initialize = function initializeSchema(dataSource, callback) { - dataSource.client = new neo4j.GraphDatabase(dataSource.settings.url); - dataSource.connector = new Neo4j(dataSource.client); - process.nextTick(callback); -}; - -function Neo4j(client) { - this._models = {}; - this.client = client; - this.cache = {}; -} - -Neo4j.prototype.define = function defineModel(descr) { - this.mixClassMethods(descr.model, descr.properties); - this.mixInstanceMethods(descr.model.prototype, descr.properties); - this._models[descr.model.modelName] = descr; -}; - -Neo4j.prototype.createIndexHelper = function (cls, indexName) { - var db = this.client; - var method = 'findBy' + indexName[0].toUpperCase() + indexName.substr(1); - cls[method] = function (value, cb) { - db.getIndexedNode(cls.modelName, indexName, value, function (err, node) { - if (err) return cb(err); - if (node) { - node.data.id = node.id; - cb(null, new cls(node.data)); - } else { - cb(null, null); - } - }); - }; -}; - -Neo4j.prototype.mixClassMethods = function mixClassMethods(cls, properties) { - var neo = this; - - Object.keys(properties).forEach(function (name) { - if (properties[name].index) { - neo.createIndexHelper(cls, name); - } - }); - - cls.setupCypherQuery = function (name, queryStr, rowHandler) { - cls[name] = function cypherQuery(params, cb) { - if (typeof params === 'function') { - cb = params; - params = []; - } else if (params.constructor.name !== 'Array') { - params = [params]; - } - - var i = 0; - var q = queryStr.replace(/\?/g, function () { - return params[i++]; - }); - - neo.client.query(function (err, result) { - if (err) return cb(err, []); - cb(null, result.map(rowHandler)); - }, q); - }; - }; - - /** - * @param from - id of object to check relation from - * @param to - id of object to check relation to - * @param type - type of relation - * @param direction - all | incoming | outgoing - * @param cb - callback (err, rel || false) - */ - cls.relationshipExists = function relationshipExists(from, to, type, direction, cb) { - neo.node(from, function (err, node) { - if (err) return cb(err); - node._getRelationships(direction, type, function (err, rels) { - if (err && cb) return cb(err); - if (err && !cb) throw err; - var found = false; - if (rels && rels.forEach) { - rels.forEach(function (r) { - if (r.start.id === from && r.end.id === to) { - found = true; - } - }); - } - cb && cb(err, found); - }); - }); - }; - - cls.createRelationshipTo = function createRelationshipTo(id1, id2, type, data, cb) { - var fromNode, toNode; - neo.node(id1, function (err, node) { - if (err && cb) return cb(err); - if (err && !cb) throw err; - fromNode = node; - ok(); - }); - neo.node(id2, function (err, node) { - if (err && cb) return cb(err); - if (err && !cb) throw err; - toNode = node; - ok(); - }); - function ok() { - if (fromNode && toNode) { - fromNode.createRelationshipTo(toNode, type, cleanup(data), cb); - } - } - }; - - cls.createRelationshipFrom = function createRelationshipFrom(id1, id2, type, data, cb) { - cls.createRelationshipTo(id2, id1, type, data, cb); - } - - // only create relationship if it is not exists - cls.ensureRelationshipTo = function (id1, id2, type, data, cb) { - cls.relationshipExists(id1, id2, type, 'outgoing', function (err, exists) { - if (err && cb) return cb(err); - if (err && !cb) throw err; - if (exists) return cb && cb(null); - cls.createRelationshipTo(id1, id2, type, data, cb); - }); - } -}; - -Neo4j.prototype.mixInstanceMethods = function mixInstanceMethods(proto) { - var neo = this; - - /** - * @param obj - Object or id of object to check relation with - * @param type - type of relation - * @param cb - callback (err, rel || false) - */ - proto.isInRelationWith = function isInRelationWith(obj, type, direction, cb) { - this.constructor.relationshipExists(this.id, obj.id || obj, type, 'all', cb); - }; -}; - -Neo4j.prototype.node = function find(id, callback) { - if (this.cache[id]) { - callback(null, this.cache[id]); - } else { - this.client.getNodeById(id, function (err, node) { - if (node) { - this.cache[id] = node; - } - callback(err, node); - }.bind(this)); - } -}; - -Neo4j.prototype.create = function create(model, data, callback) { - data.nodeType = model; - var node = this.client.createNode(); - node.data = cleanup(data); - node.data.nodeType = model; - node.save(function (err) { - if (err) { - return callback(err); - } - this.cache[node.id] = node; - node.index(model, 'id', node.id, function (err) { - if (err) return callback(err); - this.updateIndexes(model, node, function (err) { - if (err) return callback(err); - callback(null, node.id); - }); - }.bind(this)); - }.bind(this)); -}; - -Neo4j.prototype.updateIndexes = function updateIndexes(model, node, cb) { - var props = this._models[model].properties; - var wait = 1; - Object.keys(props).forEach(function (key) { - if (props[key].index && node.data[key]) { - wait += 1; - node.index(model, key, node.data[key], done); - } - }); - - done(); - - var error = false; - - function done(err) { - error = error || err; - if (--wait === 0) { - cb(error); - } - } -}; - -Neo4j.prototype.save = function save(model, data, callback) { - var self = this; - - this.node(data.id, function (err, node) { - //delete id property since that's redundant and we use the node.id - delete data.id; - if (err) return callback(err); - node.data = cleanup(data); - node.save(function (err) { - if (err) return callback(err); - self.updateIndexes(model, node, function (err) { - if (err) return console.log(err); - //map node id to the id property being sent back - node.data.id = node.id; - callback(null, node.data); - }); - }); - }); -}; - -Neo4j.prototype.exists = function exists(model, id, callback) { - delete this.cache[id]; - this.node(id, callback); -}; - -Neo4j.prototype.find = function find(model, id, callback) { - delete this.cache[id]; - this.node(id, function (err, node) { - if (node && node.data) { - node.data.id = id; - } - callback(err, this.readFromDb(model, node && node.data)); - }.bind(this)); -}; - -Neo4j.prototype.readFromDb = function readFromDb(model, data) { - if (!data) return data; - var res = {}; - var props = this._models[model].properties; - Object.keys(data).forEach(function (key) { - if (props[key] && props[key].type.name === 'Date') { - res[key] = new Date(data[key]); - } else { - res[key] = data[key]; - } - }); - return res; -}; - -Neo4j.prototype.destroy = function destroy(model, id, callback) { - var force = true; - this.node(id, function (err, node) { - if (err) return callback(err); - node.delete(function (err) { - if (err) return callback(err); - delete this.cache[id]; - }.bind(this), force); - }); -}; - -Neo4j.prototype.all = function all(model, filter, callback) { - this.client.queryNodeIndex(model, 'id:*', function (err, nodes) { - if (nodes) { - nodes = nodes.map(function (obj) { - obj.data.id = obj.id; - return this.readFromDb(model, obj.data); - }.bind(this)); - } - if (filter) { - nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes; - if (filter.order) { - var key = filter.order.split(' ')[0]; - var dir = filter.order.split(' ')[1]; - nodes = nodes.sort(function (a, b) { - return a[key] > b[key]; - }); - if (dir === 'DESC') nodes = nodes.reverse(); - } - } - callback(err, nodes); - }.bind(this)); -}; - -Neo4j.prototype.allNodes = function all(model, callback) { - this.client.queryNodeIndex(model, 'id:*', function (err, nodes) { - callback(err, nodes); - }); -}; - -function applyFilter(filter) { - if (typeof filter.where === 'function') { - return filter.where; - } - var keys = Object.keys(filter.where || {}); - return function (obj) { - var pass = true; - keys.forEach(function (key) { - if (!test(filter.where[key], obj[key])) { - pass = false; - } - }); - return pass; - } - - function test(example, value) { - if (typeof value === 'string' && example && example.constructor.name === 'RegExp') { - return value.match(example); - } - if (typeof value === 'object' && value.constructor.name === 'Date' && typeof example === 'object' && example.constructor.name === 'Date') { - return example.toString() === value.toString(); - } - // not strict equality - return example == value; - } -} - -Neo4j.prototype.destroyAll = function destroyAll(model, callback) { - var wait, error = null; - this.allNodes(model, function (err, collection) { - if (err) return callback(err); - wait = collection.length; - collection && collection.forEach && collection.forEach(function (node) { - node.delete(done, true); - }); - }); - - function done(err) { - error = error || err; - if (--wait === 0) { - callback(error); - } - } -}; - -Neo4j.prototype.count = function count(model, callback, conds) { - this.all(model, {where: conds}, function (err, collection) { - callback(err, collection ? collection.length : 0); - }); -}; - -Neo4j.prototype.updateAttributes = function updateAttributes(model, id, data, cb) { - data.id = id; - this.node(id, function (err, node) { - this.save(model, merge(node.data, data), cb); - }.bind(this)); -}; - -function cleanup(data) { - if (!data) return null; - - var res = {}; - Object.keys(data).forEach(function (key) { - var v = data[key]; - if (v === null) { - // skip - // console.log('skip null', key); - } else if (v && v.constructor.name === 'Array' && v.length === 0) { - // skip - // console.log('skip blank array', key); - } else if (typeof v !== 'undefined') { - res[key] = v; - } - }); - return res; -} - -function merge(base, update) { - Object.keys(update).forEach(function (key) { - base[key] = update[key]; - }); - return base; -} diff --git a/lib/connectors/riak.js b/lib/connectors/riak.js deleted file mode 100644 index 4730fc8b..00000000 --- a/lib/connectors/riak.js +++ /dev/null @@ -1,110 +0,0 @@ -var safeRequire = require('../utils').safeRequire; - -/** - * Module dependencies - */ -var uuid = require('node-uuid'); -var riak = safeRequire('riak-js'); - -exports.initialize = function initializeSchema(dataSource, callback) { - dataSource.client = riak.getClient({ - host: dataSource.settings.host || '127.0.0.1', - port: dataSource.settings.port || 8091 - }); - dataSource.connector = new Riak(dataSource.client); -}; - -function Riak(client) { - this._models = {}; - this.client = client; -} - -Riak.prototype.define = function (descr) { - this._models[descr.model.modelName] = descr; -}; - -Riak.prototype.save = function (model, data, callback) { - this.client.save(model, data.id, data, callback); -}; - -Riak.prototype.create = function (model, data, callback) { - data.id = uuid(); - this.save(model, data, function (err) { - if (callback) { - callback(err, data.id); - } - }); -}; - -Riak.prototype.exists = function (model, id, callback) { - this.client.exists(model, id, function (err, exists, meta) { - if (callback) { - callback(err, exists); - } - }); -}; - -Riak.prototype.find = function find(model, id, callback) { - this.client.get(model, id, function (err, data, meta) { - if (data && data.id) { - data.id = id; - } else { - data = null; - } - if (typeof callback === 'function') callback(err, data); - }); -}; - -Riak.prototype.destroy = function destroy(model, id, callback) { - this.client.remove(model, id, function (err) { - callback(err); - }); -}; - -Riak.prototype.all = function all(model, filter, callback) { - var opts = {}; - if (filter && filter.where) opts.where = filter.where; - this.client.getAll(model, function (err, result, meta) { - if (err) return callback(err, []); - /// return callback(err, result.map(function (x) { return {id: x}; })); - result = (result || []).map(function (row) { - var record = row.data; - record.id = row.meta.key; - console.log(record); - return record; - }); - - return callback(err, result); - }.bind(this)); -}; - -Riak.prototype.destroyAll = function destroyAll(model, callback) { - var self = this; - this.all(model, {}, function (err, recs) { - if (err) callback(err); - - removeOne(); - - function removeOne(error) { - err = err || error; - var rec = recs.pop(); - if (!rec) return callback(err && err.statusCode != '404' ? err : null); - console.log(rec.id); - self.client.remove(model, rec.id, removeOne); - } - - }); - -}; - -Riak.prototype.count = function count(model, callback) { - this.client.keys(model + ':*', function (err, keys) { - callback(err, err ? null : keys.length); - }); -}; - -Riak.prototype.updateAttributes = function updateAttrs(model, id, data, cb) { - data.id = id; - this.save(model, data, cb); -}; -