Merge transactions

This commit is contained in:
Anatoliy Chakkaev 2013-04-05 02:13:14 +04:00
commit 4943feb230
6 changed files with 154 additions and 29 deletions

View File

@ -56,20 +56,22 @@ about-docs:
GITBRANCH = $(shell git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/') GITBRANCH = $(shell git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
REPO = marcusgreenwood/hatchjs REPO = marcusgreenwood/hatchjs
TARGET = origin
FROM = $(GITBRANCH) FROM = $(GITBRANCH)
TO = $(GITBRANCH) TO = $(GITBRANCH)
pull: pull:
git pull origin $(FROM) git pull $(TARGET) $(FROM)
safe-pull: safe-pull:
git pull origin $(FROM) --no-commit git pull $(TARGET) $(FROM) --no-commit
push: test push: test
git push origin $(TO) git push $(TARGET) $(TO)
feature: feature:
git checkout -b feature-$(filter-out $@,$(MAKECMDGOALS)) git checkout -b feature-$(filter-out $@,$(MAKECMDGOALS))
git push -u $(TARGET) feature-$(filter-out $@,$(MAKECMDGOALS))
%: %:
@: @:

View File

@ -19,6 +19,8 @@ jugglingdb-roadmap - The Future of JugglingDB
## MODEL CORE ## MODEL CORE
* schema switching
* common transaction support
* virtual attributes * virtual attributes
* object presentation modes * object presentation modes
* mass-assignment protection * mass-assignment protection

View File

@ -1,14 +1,30 @@
exports.initialize = function initializeSchema(schema, callback) { exports.initialize = function initializeSchema(schema, callback) {
schema.adapter = new Memory(); schema.adapter = new Memory();
process.nextTick(callback); schema.adapter.connect(callback);
}; };
function Memory() { function Memory(m) {
this._models = {}; if (m) {
this.isTransaction = true;
this.cache = m.cache;
this.ids = m.ids;
this._models = m._models;
} else {
this.isTransaction = false;
this.cache = {}; this.cache = {};
this.ids = {}; this.ids = {};
this._models = {};
}
} }
Memory.prototype.connect = function(callback) {
if (this.isTransaction) {
this.onTransactionExec = callback;
} else {
process.nextTick(callback);
}
};
Memory.prototype.define = function defineModel(descr) { Memory.prototype.define = function defineModel(descr) {
var m = descr.model.modelName; var m = descr.model.modelName;
this._models[m] = descr; this._models[m] = descr;
@ -20,7 +36,7 @@ Memory.prototype.create = function create(model, data, callback) {
var id = data.id || this.ids[model]++; var id = data.id || this.ids[model]++;
data.id = id; data.id = id;
this.cache[model][id] = JSON.stringify(data); this.cache[model][id] = JSON.stringify(data);
process.nextTick(function () { process.nextTick(function() {
callback(null, id); callback(null, id);
}); });
}; };
@ -185,6 +201,15 @@ Memory.prototype.updateAttributes = function updateAttributes(model, id, data, c
this.save(model, merge(base, data), cb); this.save(model, merge(base, data), cb);
}; };
Memory.prototype.transaction = function () {
return new Memory(this);
};
Memory.prototype.exec = function(callback) {
this.onTransactionExec();
setTimeout(callback, 50);
};
function merge(base, update) { function merge(base, update) {
if (!base) return update; if (!base) return update;
Object.keys(update).forEach(function (key) { Object.keys(update).forEach(function (key) {

View File

@ -174,23 +174,29 @@ AbstractClass.create = function (data, callback) {
callback = function () {}; callback = function () {};
} }
if (!data) {
data = {};
}
if (data instanceof Array) { if (data instanceof Array) {
var instances = []; var instances = [];
var errors = new Array(data.length); var errors = Array(data.length);
var gotError = false; var gotError = false;
var wait = data.length; var wait = data.length;
if (wait === 0) callback(null, []); if (wait === 0) callback(null, []);
var instances = data.map(function(d, i) { var instances = [];
return Model.create(d, function(err, inst) { for (var i = 0; i < data.length; i += 1) {
// console.log('got', i, err, inst, inst.errors); (function(d, i) {
instances.push(Model.create(d, function(err, inst) {
if (err) { if (err) {
errors[i] = err; errors[i] = err;
gotError = true; gotError = true;
} }
modelCreated(); modelCreated();
}); }));
}); })(data[i], i);
}
return instances; return instances;
@ -252,6 +258,9 @@ AbstractClass.create = function (data, callback) {
function stillConnecting(schema, obj, args) { function stillConnecting(schema, obj, args) {
if (schema.connected) return false; if (schema.connected) return false;
if (!schema.connecting) {
schema.connect();
}
var method = args.callee; var method = args.callee;
schema.on('connected', function () { schema.on('connected', function () {
method.apply(obj, [].slice.call(args)); method.apply(obj, [].slice.call(args));
@ -697,7 +706,7 @@ AbstractClass.prototype.isNewRecord = function () {
* @private * @private
*/ */
AbstractClass.prototype._adapter = function () { AbstractClass.prototype._adapter = function () {
return this.constructor.schema.adapter; return this.schema.adapter;
}; };
/** /**

View File

@ -2,6 +2,7 @@
* Module dependencies * Module dependencies
*/ */
var AbstractClass = require('./model.js').AbstractClass; var AbstractClass = require('./model.js').AbstractClass;
var EventEmitter = require('events').EventEmitter;
var util = require('util'); var util = require('util');
var path = require('path'); var path = require('path');
var fs = require('fs'); var fs = require('fs');
@ -65,6 +66,7 @@ function Schema(name, settings) {
// Disconnected by default // Disconnected by default
this.connected = false; this.connected = false;
this.connecting = true;
// create blank models pool // create blank models pool
this.models = {}; this.models = {};
@ -115,9 +117,24 @@ function Schema(name, settings) {
this.emit('connected'); this.emit('connected');
}.bind(this)); }.bind(this));
schema.connect = function(cb) {
var schema = this;
schema.connecting = true;
schema.adapter.connect(function(err) {
if (!err) {
schema.connected = true;
schema.connecting = false;
schema.emit('connected');
}
if (cb) {
cb(err);
}
});
};
}; };
util.inherits(Schema, require('events').EventEmitter); util.inherits(Schema, EventEmitter);
/** /**
* Define class * Define class
@ -160,20 +177,17 @@ Schema.prototype.define = function defineClass(className, properties, settings)
settings = settings || {}; settings = settings || {};
standartize(properties, settings);
// every class can receive hash of data as optional param // every class can receive hash of data as optional param
var NewClass = function ModelConstructor(data) { var NewClass = function ModelConstructor(data, schema) {
if (!(this instanceof ModelConstructor)) { if (!(this instanceof ModelConstructor)) {
return new ModelConstructor(data); return new ModelConstructor(data);
} }
AbstractClass.call(this, data); AbstractClass.call(this, data);
this.schema = schema || this.constructor.schema;
}; };
hiddenProperty(NewClass, 'schema', schema); hiddenProperty(NewClass, 'schema', schema);
hiddenProperty(NewClass, 'modelName', className); hiddenProperty(NewClass, 'modelName', className);
hiddenProperty(NewClass, 'cache', {});
hiddenProperty(NewClass, 'mru', []);
hiddenProperty(NewClass, 'relations', {}); hiddenProperty(NewClass, 'relations', {});
// inherit AbstractClass methods // inherit AbstractClass methods
@ -187,6 +201,8 @@ Schema.prototype.define = function defineClass(className, properties, settings)
NewClass.getter = {}; NewClass.getter = {};
NewClass.setter = {}; NewClass.setter = {};
standartize(properties, settings);
// store class in model pool // store class in model pool
this.models[className] = NewClass; this.models[className] = NewClass;
this.definitions[className] = { this.definitions[className] = {
@ -194,7 +210,7 @@ Schema.prototype.define = function defineClass(className, properties, settings)
settings: settings settings: settings
}; };
// pass controll to adapter // pass control to adapter
this.adapter.define({ this.adapter.define({
model: NewClass, model: NewClass,
properties: properties, properties: properties,
@ -251,7 +267,6 @@ Schema.prototype.define = function defineClass(className, properties, settings)
return NewClass; return NewClass;
}; };
function standartize(properties, settings) { function standartize(properties, settings) {
@ -269,6 +284,7 @@ Schema.prototype.define = function defineClass(className, properties, settings)
// or {timestamps: {created: 'created_at', updated: false}} // or {timestamps: {created: 'created_at', updated: false}}
// by default property names: createdAt, updatedAt // by default property names: createdAt, updatedAt
} }
/** /**
* Define single property named `prop` on `model` * Define single property named `prop` on `model`
* *
@ -416,6 +432,73 @@ Schema.prototype.disconnect = function disconnect(cb) {
} }
}; };
Schema.prototype.copyModel = function copyModel(Master) {
var schema = this;
var className = Master.modelName;
var md = Master.schema.definitions[className];
var Slave = function SlaveModel() {
Master.apply(this, [].slice.call(arguments));
this.schema = schema;
};
util.inherits(Slave, Master);
Slave.__proto__ = Master;
hiddenProperty(Slave, 'schema', schema);
hiddenProperty(Slave, 'modelName', className);
hiddenProperty(Slave, 'relations', Master.relations);
if (!(className in schema.models)) {
// store class in model pool
schema.models[className] = Slave;
schema.definitions[className] = {
properties: md.properties,
settings: md.settings
};
if (!schema.isTransaction) {
schema.adapter.define({
model: Slave,
properties: md.properties,
settings: md.settings
});
}
}
return Slave;
};
Schema.prototype.transaction = function() {
var schema = this;
var transaction = new EventEmitter;
transaction.isTransaction = true;
transaction.origin = schema;
transaction.name = schema.name;
transaction.settings = schema.settings;
transaction.connected = false;
transaction.connecting = false;
transaction.adapter = schema.adapter.transaction();
// create blank models pool
transaction.models = {};
transaction.definitions = {};
for (var i in schema.models) {
schema.copyModel.call(transaction, schema.models[i]);
}
transaction.connect = schema.connect;
transaction.exec = function(cb) {
transaction.adapter.exec(cb);
};
return transaction;
};
/** /**
* Define hidden property * Define hidden property
*/ */

View File

@ -1,7 +1,11 @@
var db = getSchema(), slave = getSchema(), Model, SlaveModel; var db = getSchema(), slave = getSchema(), Model, SlaveModel;
var should = require('should'); var should = require('should');
<<<<<<< HEAD
describe.skip('schema', function() { describe.skip('schema', function() {
=======
describe.only('schema', function() {
>>>>>>> feature-transactions
it('should define Model', function() { it('should define Model', function() {
Model = db.define('Model'); Model = db.define('Model');