Skip to content
Browse files

Transactions

  • Loading branch information...
1 parent 6d1e47c commit cf5ed5b8775637ae2454e74c2163197388dad4ef @1602 committed Apr 1, 2013
Showing with 195 additions and 24 deletions.
  1. +31 −6 lib/adapters/memory.js
  2. +20 −11 lib/model.js
  3. +89 −7 lib/schema.js
  4. +55 −0 test/schema.test.js
View
37 lib/adapters/memory.js
@@ -1,14 +1,30 @@
exports.initialize = function initializeSchema(schema, callback) {
schema.adapter = new Memory();
- process.nextTick(callback);
+ schema.adapter.connect(callback);
};
-function Memory() {
- this._models = {};
- this.cache = {};
- this.ids = {};
+function Memory(m) {
+ if (m) {
+ this.isTransaction = true;
+ this.cache = m.cache;
+ this.ids = m.ids;
+ this._models = m._models;
+ } else {
+ this.isTransaction = false;
+ this.cache = {};
+ 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) {
var m = descr.model.modelName;
this._models[m] = descr;
@@ -20,7 +36,7 @@ Memory.prototype.create = function create(model, data, callback) {
var id = data.id || this.ids[model]++;
data.id = id;
this.cache[model][id] = JSON.stringify(data);
- process.nextTick(function () {
+ process.nextTick(function() {
callback(null, id);
});
};
@@ -185,6 +201,15 @@ Memory.prototype.updateAttributes = function updateAttributes(model, id, data, c
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) {
if (!base) return update;
Object.keys(update).forEach(function (key) {
View
31 lib/model.js
@@ -172,23 +172,29 @@ AbstractClass.create = function (data, callback) {
callback = function () {};
}
+ if (!data) {
+ data = {};
+ }
+
if (data instanceof Array) {
var instances = [];
- var errors = new Array(data.length);
+ var errors = Array(data.length);
var gotError = false;
var wait = data.length;
if (wait === 0) callback(null, []);
- var instances = data.map(function(d, i) {
- return Model.create(d, function(err, inst) {
- // console.log('got', i, err, inst, inst.errors);
- if (err) {
- errors[i] = err;
- gotError = true;
- }
- modelCreated();
- });
- });
+ var instances = [];
+ for (var i = 0; i < data.length; i += 1) {
+ (function(d, i) {
+ instances.push(Model.create(d, function(err, inst) {
+ if (err) {
+ errors[i] = err;
+ gotError = true;
+ }
+ modelCreated();
+ }));
+ })(data[i], i);
+ }
return instances;
@@ -249,6 +255,9 @@ AbstractClass.create = function (data, callback) {
function stillConnecting(schema, obj, args) {
if (schema.connected) return false;
+ if (!schema.connecting) {
+ schema.connect();
+ }
var method = args.callee;
schema.on('connected', function () {
method.apply(obj, [].slice.call(args));
View
96 lib/schema.js
@@ -2,6 +2,7 @@
* Module dependencies
*/
var AbstractClass = require('./model.js').AbstractClass;
+var EventEmitter = require('events').EventEmitter;
var util = require('util');
var path = require('path');
var fs = require('fs');
@@ -65,6 +66,7 @@ function Schema(name, settings) {
// Disconnected by default
this.connected = false;
+ this.connecting = true;
// create blank models pool
this.models = {};
@@ -115,9 +117,24 @@ function Schema(name, settings) {
this.emit('connected');
}.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
@@ -160,8 +177,6 @@ Schema.prototype.define = function defineClass(className, properties, settings)
settings = settings || {};
- standartize(properties, settings);
-
// every class can receive hash of data as optional param
var NewClass = function ModelConstructor(data, schema) {
if (!(this instanceof ModelConstructor)) {
@@ -173,8 +188,6 @@ Schema.prototype.define = function defineClass(className, properties, settings)
hiddenProperty(NewClass, 'schema', schema);
hiddenProperty(NewClass, 'modelName', className);
- hiddenProperty(NewClass, 'cache', {});
- hiddenProperty(NewClass, 'mru', []);
hiddenProperty(NewClass, 'relations', {});
// inherit AbstractClass methods
@@ -188,14 +201,16 @@ Schema.prototype.define = function defineClass(className, properties, settings)
NewClass.getter = {};
NewClass.setter = {};
+ standartize(properties, settings);
+
// store class in model pool
this.models[className] = NewClass;
this.definitions[className] = {
properties: properties,
settings: settings
};
- // pass controll to adapter
+ // pass control to adapter
this.adapter.define({
model: NewClass,
properties: properties,
@@ -252,7 +267,6 @@ Schema.prototype.define = function defineClass(className, properties, settings)
return NewClass;
-
};
function standartize(properties, settings) {
@@ -270,6 +284,7 @@ Schema.prototype.define = function defineClass(className, properties, settings)
// or {timestamps: {created: 'created_at', updated: false}}
// by default property names: createdAt, updatedAt
}
+
/**
* Define single property named `prop` on `model`
*
@@ -417,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
*/
View
55 test/schema.test.js
@@ -0,0 +1,55 @@
+var db = getSchema(), slave = getSchema(), Model, SlaveModel;
+var should = require('should');
+
+describe.only('schema', function() {
+
+ it('should define Model', function() {
+ Model = db.define('Model');
+ Model.schema.should.eql(db);
+ var m = new Model;
+ m.schema.should.eql(db);
+ });
+
+ it('should clone existing model', function() {
+ SlaveModel = slave.copyModel(Model);
+ SlaveModel.schema.should.eql(slave);
+ slave.should.not.eql(db);
+ var sm = new SlaveModel;
+ sm.should.be.instanceOf(Model);
+ sm.schema.should.not.eql(db);
+ sm.schema.should.eql(slave);
+ });
+
+ it('should automigrate', function(done) {
+ db.automigrate(done);
+ });
+
+ it('should create transaction', function(done) {
+ var tr = db.transaction();
+ tr.connected.should.be.false;
+ tr.connecting.should.be.false;
+ var called = false;
+ tr.models.Model.create(Array(3), function () {
+ called = true;
+ });
+ tr.connected.should.be.false;
+ tr.connecting.should.be.true;
+
+ db.models.Model.count(function(err, c) {
+ should.not.exist(err);
+ should.exist(c);
+ c.should.equal(0);
+ called.should.be.false;
+ tr.exec(function () {
+ setTimeout(function() {
+ called.should.be.true;
+ db.models.Model.count(function(err, c) {
+ c.should.equal(3);
+ done();
+ });
+ }, 100);
+ });
+ });
+ });
+
+});

0 comments on commit cf5ed5b

Please sign in to comment.
Something went wrong with that request. Please try again.