Merge pull request #240 from anatoliychakkaev/master
Docs and all pieces matters
This commit is contained in:
commit
121b13511d
2
Makefile
2
Makefile
|
@ -28,7 +28,7 @@ docs/man/%.3: docs/%.md scripts/doc.sh
|
||||||
docs/html/%.3.html: docs/%.md scripts/doc.sh docs/footer.html
|
docs/html/%.3.html: docs/%.md scripts/doc.sh docs/footer.html
|
||||||
scripts/doc.sh $< $@
|
scripts/doc.sh $< $@
|
||||||
|
|
||||||
docs/html/index.html: docs/jugglingdb.md scripts/doc.sh
|
docs/html/index.html: docs/jugglingdb.md scripts/doc.sh docs/footer.html
|
||||||
scripts/doc.sh $< $@
|
scripts/doc.sh $< $@
|
||||||
|
|
||||||
man: $(MAN_DOCS)
|
man: $(MAN_DOCS)
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
<script>
|
<script>
|
||||||
var filename = location.href.match(/([^\/]+)?\.3.html(#.*?)?$/)[1];
|
var filename = location.href.match(/([^\/]+)?\.3.html(#.*?)?$/);
|
||||||
|
if (!filename) {
|
||||||
|
filename = [null, 'jugglingdb'];
|
||||||
|
}
|
||||||
var div = document.createElement('div');
|
var div = document.createElement('div');
|
||||||
div.innerHTML = 'Found a typo? ' +
|
div.innerHTML = 'Found a typo? ' +
|
||||||
linkTo('View', 'blob') + ' and ' +
|
linkTo('View', 'blob') +
|
||||||
linkTo('edit', 'edit') + ' this file online at GitHub.';
|
' and ' +
|
||||||
|
'edit' +
|
||||||
|
//linkTo('edit', 'edit') +
|
||||||
|
' this file online at GitHub.';
|
||||||
|
|
||||||
document.getElementById('man').appendChild(div);
|
document.getElementById('man').appendChild(div);
|
||||||
|
|
||||||
function linkTo(text, dir) {
|
function linkTo(text, dir) {
|
||||||
return '<a href="https://github.com/1602/jugglingdb/' +
|
return '<a href="https://github.com/1602/jugglingdb/' +
|
||||||
dir + '/master/docs/' + filename + '.md">' + text + '</a>';
|
dir + '/master/docs/' + filename[1] + '.md">' + text + '</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
addLink('h3', '¶');
|
addLink('h3', '¶');
|
||||||
|
|
135
docs/hooks.md
135
docs/hooks.md
|
@ -1,2 +1,135 @@
|
||||||
jugglingdb-hooks(3) - Hooks and object lifecycle.
|
jugglingdb-hooks(3) - Hooks and object lifecycle.
|
||||||
==================
|
===================
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
|
||||||
|
Hook is a class method called on object when some event happens. List of events:
|
||||||
|
|
||||||
|
* `initialize`:
|
||||||
|
Called after `new Model` called.
|
||||||
|
|
||||||
|
* `create`:
|
||||||
|
Called before and after create.
|
||||||
|
|
||||||
|
* `update`:
|
||||||
|
Called before and after save (except create).
|
||||||
|
|
||||||
|
* `save`:
|
||||||
|
Called before and after save (including both create and update).
|
||||||
|
|
||||||
|
* `validate`:
|
||||||
|
Called before and after validations.
|
||||||
|
|
||||||
|
* `destroy`:
|
||||||
|
Called before and after destroy on instance.
|
||||||
|
|
||||||
|
|
||||||
|
Each hook except `initialize` accepts callback as first argument. This callback
|
||||||
|
should be called when hook done. All hooks called on object instance, but it's
|
||||||
|
not recommended to use `this` for updating in all hooks where data argument
|
||||||
|
available (second argument for all data-related before-hooks: save, update,
|
||||||
|
create).
|
||||||
|
|
||||||
|
## INITIALIZE
|
||||||
|
|
||||||
|
Initialize hook called when new object created after all setters and default
|
||||||
|
being applied.
|
||||||
|
|
||||||
|
Model.afterInitialize = function() {
|
||||||
|
this.property = 'some value;
|
||||||
|
console.log('afterInitialize called');
|
||||||
|
};
|
||||||
|
new Model; // afterInitialize called
|
||||||
|
|
||||||
|
## CREATE
|
||||||
|
|
||||||
|
Create hooks called when object created.
|
||||||
|
The `beforeCreate` hook accepts `data` as a second argument.
|
||||||
|
|
||||||
|
Model.beforeCreate = function(next, data) {
|
||||||
|
// use data argument to update object
|
||||||
|
data.createdAt = new Date();
|
||||||
|
console.log('before');
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
Model.afterCreate = function(next) {
|
||||||
|
this.notifySocialNetworks();
|
||||||
|
this.sendEmailNotifications();
|
||||||
|
console.log('after');
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
Model.create({foo: 'bar'}, function(err, model) {
|
||||||
|
console.log('callback');
|
||||||
|
});
|
||||||
|
|
||||||
|
Example output will be:
|
||||||
|
|
||||||
|
before
|
||||||
|
after
|
||||||
|
callback
|
||||||
|
|
||||||
|
## UPDATE
|
||||||
|
|
||||||
|
Update hooks called on each save except create.
|
||||||
|
The `beforeUpdate` hook accepts data as second argument.
|
||||||
|
Data argument only containing actual data for update, not full object data.
|
||||||
|
|
||||||
|
Model.beforeUpdate = function(next, data) {
|
||||||
|
// use data argument to update object
|
||||||
|
// in update hook data argumen only contains data for update (not
|
||||||
|
// full object)
|
||||||
|
data.updatedAt = new Date();
|
||||||
|
console.log('before');
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
Model.afterUpdate = function(next) {
|
||||||
|
this.scheduleFulltextIndexUpdate();
|
||||||
|
console.log('after');
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
model.updateAttributes({foo: 'bar'}, function(err, model) {
|
||||||
|
console.log('callback');
|
||||||
|
});
|
||||||
|
|
||||||
|
Example output will be:
|
||||||
|
|
||||||
|
before
|
||||||
|
after
|
||||||
|
callback
|
||||||
|
|
||||||
|
## SAVE
|
||||||
|
|
||||||
|
Save hooks called on each save, both update and create.
|
||||||
|
The `beforeSave` hook accepts `data` as a second argument.
|
||||||
|
For `beforeSave` hook `data` argument is the same as `this`.
|
||||||
|
|
||||||
|
Model.beforeSave = function(next, data) {
|
||||||
|
if ('string' !== typeof data.tags) {
|
||||||
|
data.tags = JSON.stringify(data.tags);
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
Model.afterSave = function(next) {
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
## DESTROY
|
||||||
|
|
||||||
|
Destroy hooks called when `model.destroy()` called. Please note that
|
||||||
|
`destroyAll` method doesn't call destroy hooks.
|
||||||
|
|
||||||
|
## VALIDATE
|
||||||
|
|
||||||
|
Validate hooks callen before and after validation and should be used for data
|
||||||
|
modification and not for validation. Use custom validation described in
|
||||||
|
jugglingdb-validations(3) man section.
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
|
||||||
|
jugglingdb-model(3)
|
||||||
|
jugglingdb-validations(3)
|
||||||
|
|
|
@ -109,9 +109,70 @@ count filtered set of records. Callback called with error and count arguments.
|
||||||
console.log(count); // count of approved users stored in database
|
console.log(count); // count of approved users stored in database
|
||||||
});
|
});
|
||||||
|
|
||||||
## ESSENTIALS
|
## RELATIONS
|
||||||
|
|
||||||
### Default values
|
### hasMany
|
||||||
|
|
||||||
|
Define all necessary stuff for "one to many" relation:
|
||||||
|
|
||||||
|
* foreign key in "many" model
|
||||||
|
* named scope in "one" model
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
var Book = db.define('Book');
|
||||||
|
var Chapter = db.define('Chapters');
|
||||||
|
|
||||||
|
// syntax 1 (old):
|
||||||
|
Book.hasMany(Chapter);
|
||||||
|
// syntax 2 (new):
|
||||||
|
Book.hasMany('chapters');
|
||||||
|
|
||||||
|
Syntax 1 and 2 does same things in different ways: adds `chapters` method to
|
||||||
|
`Book.prototype` and add `bookId` property to `Chapter` model. Foreign key name
|
||||||
|
(`bookId`) could be specified manually using second param:
|
||||||
|
|
||||||
|
Book.hasMany('chapters', {foreignKey: `chapter_id`});
|
||||||
|
|
||||||
|
When using syntax 2 jugglingdb looking for model with singularized name:
|
||||||
|
|
||||||
|
'chapters' => 'chapter' => 'Chapter'
|
||||||
|
|
||||||
|
But it's possible to specify model manually using second param:
|
||||||
|
|
||||||
|
Book.hasMany('stories', {model: Chapter});
|
||||||
|
|
||||||
|
Syntax 1 allows to override scope name using `as` property of second param:
|
||||||
|
|
||||||
|
Book.hasMany(Chapter, {as: 'stories'});
|
||||||
|
|
||||||
|
**Scope methods** created on BaseClass by hasMany allows to build, create and
|
||||||
|
query instances of other class. For example:
|
||||||
|
|
||||||
|
Book.create(function(err, book) {
|
||||||
|
// using 'chapters' scope for build:
|
||||||
|
var c = book.chapters.build({name: 'Chapter 1'});
|
||||||
|
// same as:
|
||||||
|
c = new Chapter({name: 'Chapter 1', bookId: book.id});
|
||||||
|
// using 'chapters' scope for create:
|
||||||
|
book.chapters.create();
|
||||||
|
// same as:
|
||||||
|
Chapter.create({bookId: book.id});
|
||||||
|
|
||||||
|
// using scope for querying:
|
||||||
|
book.chapters(function() {/* all chapters with bookId = book.id */ });
|
||||||
|
book.chapters({where: {name: 'test'}, function(err, chapters) {
|
||||||
|
// all chapters with bookId = book.id and name = 'test'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
### belongsTo
|
||||||
|
|
||||||
|
TODO: document
|
||||||
|
|
||||||
|
### hasAndBelongsToMany
|
||||||
|
|
||||||
|
TODO: implement and document
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
|
|
||||||
|
|
|
@ -70,11 +70,6 @@ Memory.prototype.all = function all(model, filter, callback) {
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
|
|
||||||
// do we need some filtration?
|
|
||||||
if (filter.where) {
|
|
||||||
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// do we need some sorting?
|
// do we need some sorting?
|
||||||
if (filter.order) {
|
if (filter.order) {
|
||||||
var props = this._models[model].properties;
|
var props = this._models[model].properties;
|
||||||
|
@ -87,12 +82,18 @@ Memory.prototype.all = function all(model, filter, callback) {
|
||||||
var m = key.match(/\s+(A|DE)SC$/i);
|
var m = key.match(/\s+(A|DE)SC$/i);
|
||||||
if (m) {
|
if (m) {
|
||||||
key = key.replace(/\s+(A|DE)SC/i, '');
|
key = key.replace(/\s+(A|DE)SC/i, '');
|
||||||
if (m[1] === 'DE') reverse = -1;
|
if (m[1].toLowerCase() === 'de') reverse = -1;
|
||||||
}
|
}
|
||||||
orders[i] = {"key": key, "reverse": reverse};
|
orders[i] = {"key": key, "reverse": reverse};
|
||||||
});
|
});
|
||||||
nodes = nodes.sort(sorting.bind(orders));
|
nodes = nodes.sort(sorting.bind(orders));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do we need some filtration?
|
||||||
|
if (filter.where) {
|
||||||
|
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
|
|
26
lib/model.js
26
lib/model.js
|
@ -512,6 +512,12 @@ AbstractClass.include = function (objects, include, cb) {
|
||||||
}
|
}
|
||||||
var relation = relations[relationName];
|
var relation = relations[relationName];
|
||||||
|
|
||||||
|
if (!relation) {
|
||||||
|
return function() {
|
||||||
|
cb(new Error('Relation "' + relationName + '" is not defined for ' + self.modelName + ' model'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var req = {'where': {}};
|
var req = {'where': {}};
|
||||||
|
|
||||||
if (!keyVals[relation.keyFrom]) {
|
if (!keyVals[relation.keyFrom]) {
|
||||||
|
@ -954,12 +960,26 @@ AbstractClass.hasMany = function hasMany(anotherClass, params) {
|
||||||
* This optional parameter default value is false, so the related object will be loaded from cache if available.
|
* This optional parameter default value is false, so the related object will be loaded from cache if available.
|
||||||
*/
|
*/
|
||||||
AbstractClass.belongsTo = function (anotherClass, params) {
|
AbstractClass.belongsTo = function (anotherClass, params) {
|
||||||
var methodName = params.as;
|
params = params || {};
|
||||||
var fk = params.foreignKey;
|
if ('string' === typeof anotherClass) {
|
||||||
|
params.as = anotherClass;
|
||||||
|
if (params.model) {
|
||||||
|
anotherClass = params.model;
|
||||||
|
} else {
|
||||||
|
var anotherClassName = anotherClass.toLowerCase();
|
||||||
|
for(var name in this.schema.models) {
|
||||||
|
if (name.toLowerCase() === anotherClassName) {
|
||||||
|
anotherClass = this.schema.models[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var methodName = params.as || i8n.camelize(anotherClass.modelName, true);
|
||||||
|
var fk = params.foreignKey || methodName + 'Id';
|
||||||
|
|
||||||
this.relations[params['as']] = {
|
this.relations[params['as']] = {
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
keyFrom: params['foreignKey'],
|
keyFrom: fk,
|
||||||
keyTo: 'id',
|
keyTo: 'id',
|
||||||
modelTo: anotherClass,
|
modelTo: anotherClass,
|
||||||
multiple: false
|
multiple: false
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
var db, User, should = require('should');
|
||||||
|
|
||||||
|
describe('basic-querying', function() {
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
db = getSchema();
|
||||||
|
|
||||||
|
User = db.define('User', {
|
||||||
|
name: String,
|
||||||
|
email: {type: String, index: true},
|
||||||
|
role: {type: String, index: true},
|
||||||
|
order: {type: Number, index: true}
|
||||||
|
});
|
||||||
|
|
||||||
|
db.automigrate(done);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('find', function() {
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
User.destroyAll(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query by id: not found', function(done) {
|
||||||
|
User.find(1, function(err, u) {
|
||||||
|
should.not.exist(u);
|
||||||
|
should.not.exist(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query by id: found', function(done) {
|
||||||
|
User.create(function(err, u) {
|
||||||
|
should.exist(u.id);
|
||||||
|
User.find(u.id, function(err, u) {
|
||||||
|
should.exist(u);
|
||||||
|
should.not.exist(err);
|
||||||
|
u.should.be.an.instanceOf(User);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('all', function() {
|
||||||
|
|
||||||
|
before(seed);
|
||||||
|
|
||||||
|
it('should query collection', function(done) {
|
||||||
|
User.all(function(err, users) {
|
||||||
|
should.exists(users);
|
||||||
|
should.not.exists(err);
|
||||||
|
users.should.have.lengthOf(6);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query filtered collection', function(done) {
|
||||||
|
User.all({where: {role: 'lead'}}, function(err, users) {
|
||||||
|
should.exists(users);
|
||||||
|
should.not.exists(err);
|
||||||
|
users.should.have.lengthOf(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query collection sorted by numeric field', function(done) {
|
||||||
|
User.all({order: 'order'}, function(err, users) {
|
||||||
|
should.exists(users);
|
||||||
|
should.not.exists(err);
|
||||||
|
users.forEach(function(u, i) {
|
||||||
|
u.order.should.eql(i + 1);
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query collection desc sorted by numeric field', function(done) {
|
||||||
|
User.all({order: 'order DESC'}, function(err, users) {
|
||||||
|
should.exists(users);
|
||||||
|
should.not.exists(err);
|
||||||
|
users.forEach(function(u, i) {
|
||||||
|
u.order.should.eql(users.length - i);
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query collection sorted by string field', function(done) {
|
||||||
|
User.all({order: 'name'}, function(err, users) {
|
||||||
|
should.exists(users);
|
||||||
|
should.not.exists(err);
|
||||||
|
users.shift().name.should.equal('George Harrison');
|
||||||
|
users.shift().name.should.equal('John Lennon');
|
||||||
|
users.pop().name.should.equal('Stuart Sutcliffe');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query collection desc sorted by string field', function(done) {
|
||||||
|
User.all({order: 'name DESC'}, function(err, users) {
|
||||||
|
should.exists(users);
|
||||||
|
should.not.exists(err);
|
||||||
|
users.pop().name.should.equal('George Harrison');
|
||||||
|
users.pop().name.should.equal('John Lennon');
|
||||||
|
users.shift().name.should.equal('Stuart Sutcliffe');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('count', function() {
|
||||||
|
|
||||||
|
before(seed);
|
||||||
|
|
||||||
|
it('should query total count', function(done) {
|
||||||
|
User.count(function(err, n) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(n);
|
||||||
|
n.should.equal(6);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query filtered count', function(done) {
|
||||||
|
User.count({role: 'lead'}, function(err, n) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(n);
|
||||||
|
n.should.equal(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findOne', function() {
|
||||||
|
|
||||||
|
before(seed);
|
||||||
|
|
||||||
|
it('should find first record (default sort by id)', function(done) {
|
||||||
|
User.all({sort: 'id'}, function(err, users) {
|
||||||
|
User.findOne(function(e, u) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(u);
|
||||||
|
u.id.should.equal(users[0].id);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find first record', function(done) {
|
||||||
|
User.findOne({order: 'order'}, function(e, u) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(u);
|
||||||
|
u.order.should.equal(1);
|
||||||
|
u.name.should.equal('Paul McCartney');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find last record', function(done) {
|
||||||
|
User.findOne({order: 'order DESC'}, function(e, u) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(u);
|
||||||
|
u.order.should.equal(6);
|
||||||
|
u.name.should.equal('Ringo Starr');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should find last record in filtered set', function(done) {
|
||||||
|
User.findOne({
|
||||||
|
where: {role: 'lead'},
|
||||||
|
order: 'order DESC'
|
||||||
|
}, function(e, u) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(u);
|
||||||
|
u.order.should.equal(2);
|
||||||
|
u.name.should.equal('John Lennon');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('exists', function() {
|
||||||
|
|
||||||
|
before(seed);
|
||||||
|
|
||||||
|
it('should check whether record exist', function(done) {
|
||||||
|
User.findOne(function(e, u) {
|
||||||
|
User.exists(u.id, function(err, exists) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(exists);
|
||||||
|
exists.should.be.ok;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check whether record not exist', function(done) {
|
||||||
|
User.destroyAll(function() {
|
||||||
|
User.exists(42, function(err, exists) {
|
||||||
|
should.not.exist(err);
|
||||||
|
exists.should.not.be.ok;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function seed(done) {
|
||||||
|
var count = 0;
|
||||||
|
var beatles = [
|
||||||
|
{
|
||||||
|
name: 'John Lennon',
|
||||||
|
mail: 'john@b3atl3s.co.uk',
|
||||||
|
role: 'lead',
|
||||||
|
order: 2
|
||||||
|
}, {
|
||||||
|
name: 'Paul McCartney',
|
||||||
|
mail: 'paul@b3atl3s.co.uk',
|
||||||
|
role: 'lead',
|
||||||
|
order: 1
|
||||||
|
},
|
||||||
|
{name: 'George Harrison', order: 5},
|
||||||
|
{name: 'Ringo Starr', order: 6},
|
||||||
|
{name: 'Pete Best', order: 4},
|
||||||
|
{name: 'Stuart Sutcliffe', order: 3}
|
||||||
|
];
|
||||||
|
User.destroyAll(function() {
|
||||||
|
beatles.forEach(function(beatle) {
|
||||||
|
User.create(beatle, ok);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function ok() {
|
||||||
|
if (++count === beatles.length) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
require('./basic-querying.test.js');
|
||||||
|
require('./hooks.test.js');
|
|
@ -209,42 +209,6 @@ function testOrm(schema) {
|
||||||
test.done();
|
test.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be exported to JSON', function (test) {
|
|
||||||
var outString = '{"title":"hello, json","date":1,"published":false,"likes":[],"related":[],"id":1}'
|
|
||||||
if (schema.name === 'nano')
|
|
||||||
outString = '{"title":"hello, json","subject":null,"content":null,"date":1,"published":false,"likes":[],"related":[],"_rev":null,"id":1,"userId":null}'
|
|
||||||
|
|
||||||
test.equal(JSON.stringify(new Post({id: 1, title: 'hello, json', date: 1})),outString);
|
|
||||||
test.done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create object', function (test) {
|
|
||||||
Post.create(function (err, post) {
|
|
||||||
if (err) throw err;
|
|
||||||
test.ok(post.id, 'Id present');
|
|
||||||
test.ok(!post.title, 'Title is blank');
|
|
||||||
Post.exists(post.id, function (err, exists) {
|
|
||||||
if (err) throw err;
|
|
||||||
test.ok(exists);
|
|
||||||
test.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create object without callback', function (test) {
|
|
||||||
var uniqueTitle = 'Unique title ' + Date.now();
|
|
||||||
Post.create({title: uniqueTitle});
|
|
||||||
|
|
||||||
setTimeout(delayedCallback, 100);
|
|
||||||
|
|
||||||
function delayedCallback() {
|
|
||||||
Post.all({where: {title: uniqueTitle}}, function (err, posts) {
|
|
||||||
test.equal(posts.length, 1);
|
|
||||||
test.done();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should save object', function (test) {
|
it('should save object', function (test) {
|
||||||
var title = 'Initial title', title2 = 'Hello world',
|
var title = 'Initial title', title2 = 'Hello world',
|
||||||
date = new Date;
|
date = new Date;
|
||||||
|
@ -420,53 +384,6 @@ function testOrm(schema) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch count of records in collection', function (test) {
|
|
||||||
Post.count(function (err, count) {
|
|
||||||
console.log(countOfposts, count);
|
|
||||||
test.equal(countOfposts, count, 'unfiltered count');
|
|
||||||
Post.count({title: 'title'}, function (err, count) {
|
|
||||||
console.log(countOfpostsFiltered, count, 'filtered count');
|
|
||||||
test.equal(countOfpostsFiltered, count, 'filtered count');
|
|
||||||
test.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should find filtered set of records', function (test) {
|
|
||||||
var wait = 1;
|
|
||||||
|
|
||||||
// exact match with string
|
|
||||||
Post.all({where: {title: 'New title'}}, function (err, res) {
|
|
||||||
var pass = true;
|
|
||||||
res.forEach(function (r) {
|
|
||||||
if (r.title != 'New title') pass = false;
|
|
||||||
});
|
|
||||||
test.ok(res.length > 0, 'Exact match with string returns dataset');
|
|
||||||
test.ok(pass, 'Exact match with string');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
// matching null
|
|
||||||
// Post.all({where: {title: null}}, function (err, res) {
|
|
||||||
|
|
||||||
// var pass = true;
|
|
||||||
// res.forEach(function (r) {
|
|
||||||
// if (r.title != null) pass = false;
|
|
||||||
// });
|
|
||||||
// test.ok(res.length > 0, 'Matching null returns dataset');
|
|
||||||
// test.ok(pass, 'Matching null');
|
|
||||||
// done();
|
|
||||||
// });
|
|
||||||
|
|
||||||
function done() {
|
|
||||||
if (--wait === 0) {
|
|
||||||
test.done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should find records filtered with multiple attributes', function (test) {
|
it('should find records filtered with multiple attributes', function (test) {
|
||||||
var d = new Date;
|
var d = new Date;
|
||||||
Post.create({title: 'title', content: 'content', published: true, date: d}, function (err, post) {
|
Post.create({title: 'title', content: 'content', published: true, date: d}, function (err, post) {
|
||||||
|
@ -661,252 +578,6 @@ function testOrm(schema) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
|
||||||
schema.name === 'mysql' ||
|
|
||||||
schema.name === 'sqlite3' ||
|
|
||||||
schema.name === 'postgres'
|
|
||||||
)
|
|
||||||
it('should handle include function', function (test) {
|
|
||||||
var createdUsers = [];
|
|
||||||
var createdPassports = [];
|
|
||||||
var createdPosts = [];
|
|
||||||
var context = null;
|
|
||||||
|
|
||||||
createUsers();
|
|
||||||
function createUsers() {
|
|
||||||
clearAndCreate(
|
|
||||||
User,
|
|
||||||
[
|
|
||||||
{name: 'User A', age: 21},
|
|
||||||
{name: 'User B', age: 22},
|
|
||||||
{name: 'User C', age: 23},
|
|
||||||
{name: 'User D', age: 24},
|
|
||||||
{name: 'User E', age: 25}
|
|
||||||
],
|
|
||||||
function(items) {
|
|
||||||
createdUsers = items;
|
|
||||||
createPassports();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createPassports() {
|
|
||||||
clearAndCreate(
|
|
||||||
Passport,
|
|
||||||
[
|
|
||||||
{number: '1', ownerId: createdUsers[0].id},
|
|
||||||
{number: '2', ownerId: createdUsers[1].id},
|
|
||||||
{number: '3'}
|
|
||||||
],
|
|
||||||
function(items) {
|
|
||||||
createdPassports = items;
|
|
||||||
createPosts();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createPosts() {
|
|
||||||
clearAndCreate(
|
|
||||||
Post,
|
|
||||||
[
|
|
||||||
{title: 'Post A', userId: createdUsers[0].id},
|
|
||||||
{title: 'Post B', userId: createdUsers[0].id},
|
|
||||||
{title: 'Post C', userId: createdUsers[0].id},
|
|
||||||
{title: 'Post D', userId: createdUsers[1].id},
|
|
||||||
{title: 'Post E'}
|
|
||||||
],
|
|
||||||
function(items) {
|
|
||||||
createdPosts = items;
|
|
||||||
makeTests();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeTests() {
|
|
||||||
var unitTests = [
|
|
||||||
function() {
|
|
||||||
context = ' (belongsTo simple string from passports to users)';
|
|
||||||
Passport.all({include: 'owner'}, testPassportsUser);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
context = ' (belongsTo simple string from posts to users)';
|
|
||||||
Post.all({include: 'author'}, testPostsUser);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
context = ' (belongsTo simple array)';
|
|
||||||
Passport.all({include: ['owner']}, testPassportsUser);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
context = ' (hasMany simple string from users to posts)';
|
|
||||||
User.all({include: 'posts'}, testUsersPosts);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
context = ' (hasMany simple string from users to passports)';
|
|
||||||
User.all({include: 'passports'}, testUsersPassports);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
context = ' (hasMany simple array)';
|
|
||||||
User.all({include: ['posts']}, testUsersPosts);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
context = ' (Passports - User - Posts in object)';
|
|
||||||
Passport.all({include: {'owner': 'posts'}}, testPassportsUserPosts);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
context = ' (Passports - User - Posts in array)';
|
|
||||||
Passport.all({include: [{'owner': 'posts'}]}, testPassportsUserPosts);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
context = ' (Passports - User - Posts - User)';
|
|
||||||
Passport.all({include: {'owner': {'posts': 'author'}}}, testPassportsUserPosts);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
context = ' (User - Posts AND Passports)';
|
|
||||||
User.all({include: ['posts', 'passports']}, testUsersPostsAndPassports);
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
function testPassportsUser(err, passports, callback) {
|
|
||||||
testBelongsTo(passports, 'owner', callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testPostsUser(err, posts, callback) {
|
|
||||||
testBelongsTo(posts, 'author', callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testBelongsTo(items, relationName, callback) {
|
|
||||||
if (typeof callback === 'undefined') {
|
|
||||||
callback = nextUnitTest;
|
|
||||||
}
|
|
||||||
var nbInitialRequests = nbSchemaRequests;
|
|
||||||
var nbItemsRemaining = items.length;
|
|
||||||
|
|
||||||
for (var i = 0; i < items.length; i++) {
|
|
||||||
testItem(items[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testItem(item) {
|
|
||||||
var relation = item.constructor.relations[relationName];
|
|
||||||
var modelNameFrom = item.constructor.modelName;
|
|
||||||
var modelNameTo = relation.modelTo.modelName;
|
|
||||||
item[relationName](function(err, relatedItem) {
|
|
||||||
if (relatedItem !== null) {
|
|
||||||
test.equal(relatedItem[relation.keyTo], item[relation.keyFrom], modelNameTo + '\'s instance match ' + modelNameFrom + '\'s instance' + context);
|
|
||||||
} else {
|
|
||||||
test.ok(item[relation.keyFrom] == null, 'User match passport even when user is null.' + context);
|
|
||||||
}
|
|
||||||
nbItemsRemaining--;
|
|
||||||
if (nbItemsRemaining == 0) {
|
|
||||||
requestsAreCounted && test.equal(nbSchemaRequests, nbInitialRequests, 'No more request have been executed for loading ' + relationName + ' relation' + context)
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testUsersPosts(err, users, expectedUserNumber, callback) {
|
|
||||||
if (typeof expectedUserNumber === 'undefined') {
|
|
||||||
expectedUserNumber = 5;
|
|
||||||
}
|
|
||||||
test.equal(users.length, expectedUserNumber, 'Exactly ' + expectedUserNumber + ' users returned by query' + context);
|
|
||||||
testHasMany(users, 'posts', callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testUsersPassports(err, users, callback) {
|
|
||||||
testHasMany(users, 'passports', callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testHasMany(items, relationName, callback) {
|
|
||||||
if (typeof callback === 'undefined') {
|
|
||||||
callback = nextUnitTest;
|
|
||||||
}
|
|
||||||
var nbInitialRequests = nbSchemaRequests;
|
|
||||||
var nbItemRemaining = items.length;
|
|
||||||
for (var i = 0; i < items.length; i++) {
|
|
||||||
testItem(items[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testItem(item) {
|
|
||||||
var relation = item.constructor.relations[relationName];
|
|
||||||
var modelNameFrom = item.constructor.modelName;
|
|
||||||
var modelNameTo = relation.modelTo.modelName;
|
|
||||||
item[relationName](function(err, relatedItems) {
|
|
||||||
for (var j = 0; j < relatedItems.length; j++) {
|
|
||||||
test.equal(relatedItems[j][relation.keyTo], item[relation.keyFrom], modelNameTo + '\'s instances match ' + modelNameFrom + '\'s instance' + context);
|
|
||||||
}
|
|
||||||
nbItemRemaining--;
|
|
||||||
if (nbItemRemaining == 0) {
|
|
||||||
requestsAreCounted && test.equal(nbSchemaRequests, nbInitialRequests, 'No more request have been executed for loading ' + relationName + ' relation' + context)
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testPassportsUserPosts(err, passports) {
|
|
||||||
testPassportsUser(err, passports, function() {
|
|
||||||
var nbPassportsRemaining = passports.length;
|
|
||||||
for (var i = 0; i < passports.length; i++) {
|
|
||||||
if (passports[i].ownerId !== null) {
|
|
||||||
passports[i].owner(function(err, user) {
|
|
||||||
testUsersPosts(null, [user], 1, function() {
|
|
||||||
nextPassport();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
nextPassport();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function nextPassport() {
|
|
||||||
nbPassportsRemaining--
|
|
||||||
if (nbPassportsRemaining == 0) {
|
|
||||||
nextUnitTest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testUsersPostsAndPassports(err, users) {
|
|
||||||
testUsersPosts(err, users, 5, function() {
|
|
||||||
testUsersPassports(err, users, function() {
|
|
||||||
nextUnitTest();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var testNum = 0;
|
|
||||||
function nextUnitTest() {
|
|
||||||
if (testNum >= unitTests.length) {
|
|
||||||
test.done();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unitTests[testNum]();
|
|
||||||
testNum++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
nextUnitTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should destroy all records', function (test) {
|
|
||||||
Post.destroyAll(function (err) {
|
|
||||||
if (err) {
|
|
||||||
console.log('Error in destroyAll');
|
|
||||||
console.log(err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
Post.all(function (err, posts) {
|
|
||||||
test.equal(posts.length, 0);
|
|
||||||
Post.count(function (err, count) {
|
|
||||||
test.equal(count, 0);
|
|
||||||
test.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return type of property', function (test) {
|
it('should return type of property', function (test) {
|
||||||
test.equal(Post.whatTypeName('title'), 'String');
|
test.equal(Post.whatTypeName('title'), 'String');
|
||||||
test.equal(Post.whatTypeName('content'), 'Text');
|
test.equal(Post.whatTypeName('content'), 'Text');
|
||||||
|
|
|
@ -8,15 +8,17 @@ var j = require('../'),
|
||||||
|
|
||||||
describe('hooks', function() {
|
describe('hooks', function() {
|
||||||
|
|
||||||
before(function() {
|
before(function(done) {
|
||||||
db = getSchema();
|
db = getSchema();
|
||||||
|
|
||||||
User = db.define('User', {
|
User = db.define('User', {
|
||||||
email: String,
|
email: {type: String, index: true},
|
||||||
name: String,
|
name: String,
|
||||||
password: String,
|
password: String,
|
||||||
state: String
|
state: String
|
||||||
});
|
});
|
||||||
|
|
||||||
|
db.automigrate(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('initialize', function() {
|
describe('initialize', function() {
|
||||||
|
@ -110,6 +112,26 @@ describe('hooks', function() {
|
||||||
user.save();
|
user.save();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should save actual modifications to database', function(done) {
|
||||||
|
User.beforeSave = function(next, data) {
|
||||||
|
data.password = 'hash';
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
User.destroyAll(function() {
|
||||||
|
User.create({
|
||||||
|
email: 'james.bond@example.com',
|
||||||
|
password: 'secret'
|
||||||
|
}, function() {
|
||||||
|
User.findOne({
|
||||||
|
where: {email: 'james.bond@example.com'}
|
||||||
|
}, function(err, jb) {
|
||||||
|
jb.password.should.equal('hash');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('update', function() {
|
describe('update', function() {
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
var db, User, Post, Passport, City, Street, Building, should = require('should');
|
||||||
|
var nbSchemaRequests = 0;
|
||||||
|
|
||||||
|
describe('include', function() {
|
||||||
|
|
||||||
|
before(setup);
|
||||||
|
|
||||||
|
it('should fetch belongsTo relation', function(done) {
|
||||||
|
Passport.all({include: 'owner'}, function (err, passports) {
|
||||||
|
passports.length.should.be.ok;
|
||||||
|
passports.forEach(function(p) {
|
||||||
|
p.__cachedRelations.should.have.property('owner');
|
||||||
|
if (p.ownerId === null) {
|
||||||
|
should.not.exist(p.__cachedRelations.owner);
|
||||||
|
} else {
|
||||||
|
p.__cachedRelations.owner.id.should.equal(p.ownerId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch hasMany relation', function(done) {
|
||||||
|
User.all({include: 'posts'}, function (err, users) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(users);
|
||||||
|
users.length.should.be.ok;
|
||||||
|
users.forEach(function(u) {
|
||||||
|
u.__cachedRelations.should.have.property('posts');
|
||||||
|
u.__cachedRelations.posts.forEach(function(p) {
|
||||||
|
p.userId.should.equal(u.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch Passport - Owner - Posts', function(done) {
|
||||||
|
Passport.all({include: {owner: 'posts'}}, function(err, passports) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(passports);
|
||||||
|
passports.length.should.be.ok;
|
||||||
|
passports.forEach(function(p) {
|
||||||
|
p.__cachedRelations.should.have.property('owner');
|
||||||
|
var user = p.__cachedRelations.owner;
|
||||||
|
if (p.ownerId === null) {
|
||||||
|
should.not.exist(user);
|
||||||
|
} else {
|
||||||
|
user.id.should.equal(p.ownerId);
|
||||||
|
user.__cachedRelations.should.have.property('posts');
|
||||||
|
user.__cachedRelations.posts.forEach(function(pp) {
|
||||||
|
pp.userId.should.equal(user.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch Passports - User - Posts - User', function(done) {
|
||||||
|
Passport.all({
|
||||||
|
include: {owner: {posts: 'author'}}
|
||||||
|
}, function(err, passports) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(passports);
|
||||||
|
passports.length.should.be.ok;
|
||||||
|
passports.forEach(function(p) {
|
||||||
|
p.__cachedRelations.should.have.property('owner');
|
||||||
|
var user = p.__cachedRelations.owner;
|
||||||
|
if (p.ownerId === null) {
|
||||||
|
should.not.exist(user);
|
||||||
|
} else {
|
||||||
|
user.id.should.equal(p.ownerId);
|
||||||
|
user.__cachedRelations.should.have.property('posts');
|
||||||
|
user.__cachedRelations.posts.forEach(function(pp) {
|
||||||
|
pp.userId.should.equal(user.id);
|
||||||
|
pp.__cachedRelations.should.have.property('author');
|
||||||
|
var author = pp.__cachedRelations.author;
|
||||||
|
author.id.should.equal(user.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch User - Posts AND Passports', function(done) {
|
||||||
|
User.all({include: ['posts', 'passports']}, function(err, users) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(users);
|
||||||
|
users.length.should.be.ok;
|
||||||
|
users.forEach(function(user) {
|
||||||
|
user.__cachedRelations.should.have.property('posts');
|
||||||
|
user.__cachedRelations.should.have.property('passports');
|
||||||
|
user.__cachedRelations.posts.forEach(function(p) {
|
||||||
|
p.userId.should.equal(user.id);
|
||||||
|
});
|
||||||
|
user.__cachedRelations.passports.forEach(function(pp) {
|
||||||
|
pp.ownerId.should.equal(user.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup(done) {
|
||||||
|
db = getSchema();
|
||||||
|
City = db.define('City');
|
||||||
|
Street = db.define('Street');
|
||||||
|
Building = db.define('Building');
|
||||||
|
User = db.define('User', {
|
||||||
|
name: String,
|
||||||
|
age: Number
|
||||||
|
});
|
||||||
|
Passport = db.define('Passport', {
|
||||||
|
number: String
|
||||||
|
});
|
||||||
|
Post = db.define('Post', {
|
||||||
|
title: String
|
||||||
|
});
|
||||||
|
|
||||||
|
Passport.belongsTo('owner', {model: User});
|
||||||
|
User.hasMany('passports', {foreignKey: 'ownerId'});
|
||||||
|
User.hasMany('posts', {foreignKey: 'userId'});
|
||||||
|
Post.belongsTo('author', {model: User, foreignKey: 'userId'});
|
||||||
|
|
||||||
|
db.automigrate(function() {
|
||||||
|
var createdUsers = [];
|
||||||
|
var createdPassports = [];
|
||||||
|
var createdPosts = [];
|
||||||
|
createUsers();
|
||||||
|
function createUsers() {
|
||||||
|
clearAndCreate(
|
||||||
|
User,
|
||||||
|
[
|
||||||
|
{name: 'User A', age: 21},
|
||||||
|
{name: 'User B', age: 22},
|
||||||
|
{name: 'User C', age: 23},
|
||||||
|
{name: 'User D', age: 24},
|
||||||
|
{name: 'User E', age: 25}
|
||||||
|
],
|
||||||
|
function(items) {
|
||||||
|
createdUsers = items;
|
||||||
|
createPassports();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPassports() {
|
||||||
|
clearAndCreate(
|
||||||
|
Passport,
|
||||||
|
[
|
||||||
|
{number: '1', ownerId: createdUsers[0].id},
|
||||||
|
{number: '2', ownerId: createdUsers[1].id},
|
||||||
|
{number: '3'}
|
||||||
|
],
|
||||||
|
function(items) {
|
||||||
|
createdPassports = items;
|
||||||
|
createPosts();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPosts() {
|
||||||
|
clearAndCreate(
|
||||||
|
Post,
|
||||||
|
[
|
||||||
|
{title: 'Post A', userId: createdUsers[0].id},
|
||||||
|
{title: 'Post B', userId: createdUsers[0].id},
|
||||||
|
{title: 'Post C', userId: createdUsers[0].id},
|
||||||
|
{title: 'Post D', userId: createdUsers[1].id},
|
||||||
|
{title: 'Post E'}
|
||||||
|
],
|
||||||
|
function(items) {
|
||||||
|
createdPosts = items;
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAndCreate(model, data, callback) {
|
||||||
|
var createdItems = [];
|
||||||
|
model.destroyAll(function () {
|
||||||
|
nextItem(null, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
var itemIndex = 0;
|
||||||
|
function nextItem(err, lastItem) {
|
||||||
|
if (lastItem !== null) {
|
||||||
|
createdItems.push(lastItem);
|
||||||
|
}
|
||||||
|
if (itemIndex >= data.length) {
|
||||||
|
callback(createdItems);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
model.create(data[itemIndex], nextItem);
|
||||||
|
itemIndex++;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
var db, Person, should = require('should');
|
||||||
|
|
||||||
|
describe('manipulation', function() {
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
db = getSchema();
|
||||||
|
|
||||||
|
Person = db.define('Person', {
|
||||||
|
name: String,
|
||||||
|
gender: String,
|
||||||
|
married: Boolean,
|
||||||
|
age: {type: Number, index: true},
|
||||||
|
dob: Date,
|
||||||
|
createdAt: {type: Number, default: Date.now}
|
||||||
|
});
|
||||||
|
|
||||||
|
db.automigrate(done);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('create', function() {
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
Person.destroyAll(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create instance', function(done) {
|
||||||
|
Person.create({name: 'Anatoliy'}, function(err, p) {
|
||||||
|
p.name.should.equal('Anatoliy');
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(p);
|
||||||
|
Person.find(p.id, function(err, person) {
|
||||||
|
person.id.should.equal(p.id);
|
||||||
|
person.name.should.equal('Anatoliy');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work when called without callback', function(done) {
|
||||||
|
Person.afterCreate = function(next) {
|
||||||
|
this.should.be.an.instanceOf(Person);
|
||||||
|
this.name.should.equal('Nickolay');
|
||||||
|
should.exist(this.id);
|
||||||
|
Person.afterCreate = null;
|
||||||
|
next();
|
||||||
|
setTimeout(done, 10);
|
||||||
|
};
|
||||||
|
Person.create({name: 'Nickolay'});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create instance with blank data', function(done) {
|
||||||
|
Person.create(function(err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(p);
|
||||||
|
should.not.exists(p.name);
|
||||||
|
Person.find(p.id, function(err, person) {
|
||||||
|
person.id.should.equal(p.id);
|
||||||
|
should.not.exists(person.name);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work when called with no data and callback', function(done) {
|
||||||
|
Person.afterCreate = function(next) {
|
||||||
|
this.should.be.an.instanceOf(Person);
|
||||||
|
should.not.exist(this.name);
|
||||||
|
should.exist(this.id);
|
||||||
|
Person.afterCreate = null;
|
||||||
|
next();
|
||||||
|
setTimeout(done, 10);
|
||||||
|
};
|
||||||
|
Person.create();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('save', function() {
|
||||||
|
it('should save new object');
|
||||||
|
it('should save existing object');
|
||||||
|
it('should save invalid object (skipping validation)');
|
||||||
|
it('should save throw error on validation');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('destroy', function() {
|
||||||
|
|
||||||
|
it('should destroy record', function(done) {
|
||||||
|
Person.create(function(err, p){
|
||||||
|
p.destroy(function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Person.exists(p.id, function(err, ex) {
|
||||||
|
ex.should.not.be.ok;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should destroy all records', function (done) {
|
||||||
|
Person.destroyAll(function (err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Person.all(function (err, posts) {
|
||||||
|
posts.should.have.lengthOf(0);
|
||||||
|
Person.count(function (err, count) {
|
||||||
|
count.should.eql(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: implement destroy with filtered set
|
||||||
|
it('should destroy filtered set of records');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('initialize', function() {
|
||||||
|
it('should initialize object properly', function() {
|
||||||
|
var hw = 'Hello word',
|
||||||
|
now = Date.now(),
|
||||||
|
person = new Person({name: hw});
|
||||||
|
|
||||||
|
person.name.should.equal(hw);
|
||||||
|
person.propertyChanged('name').should.be.false;
|
||||||
|
person.name = 'Goodbye, Lenin';
|
||||||
|
person.name_was.should.equal(hw);
|
||||||
|
person.propertyChanged('name').should.be.true;
|
||||||
|
(person.createdAt >= now).should.be.true;
|
||||||
|
person.isNewRecord().should.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work when constructor called as function', function() {
|
||||||
|
var p = Person({name: 'John Resig'});
|
||||||
|
p.should.be.an.instanceOf(Person);
|
||||||
|
p.name.should.equal('John Resig');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,12 +1,22 @@
|
||||||
var db, Book, Chapter, Author, Reader;
|
var db, Book, Chapter, Author, Reader, should = require('should');
|
||||||
|
|
||||||
describe('relations', function() {
|
describe('relations', function() {
|
||||||
before(function() {
|
before(function(done) {
|
||||||
db = getSchema();
|
db = getSchema();
|
||||||
Book = db.define('Book', {name: String});
|
Book = db.define('Book', {name: String});
|
||||||
Chapter = db.define('Chapter', {name: String});
|
Chapter = db.define('Chapter', {name: {type: String, index: true}});
|
||||||
Author = db.define('Author', {name: String});
|
Author = db.define('Author', {name: String});
|
||||||
Reader = db.define('Reader', {name: String});
|
Reader = db.define('Reader', {name: String});
|
||||||
|
|
||||||
|
db.automigrate(function() {
|
||||||
|
Book.destroyAll(function() {
|
||||||
|
Chapter.destroyAll(function() {
|
||||||
|
Author.destroyAll(function() {
|
||||||
|
Reader.destroyAll(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function() {
|
after(function() {
|
||||||
|
@ -14,7 +24,7 @@ describe('relations', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasMany', function() {
|
describe('hasMany', function() {
|
||||||
it('can be declared in different ways', function() {
|
it('can be declared in different ways', function(done) {
|
||||||
Book.hasMany(Chapter);
|
Book.hasMany(Chapter);
|
||||||
Book.hasMany(Reader, {as: 'users'});
|
Book.hasMany(Reader, {as: 'users'});
|
||||||
Book.hasMany(Author, {foreignKey: 'projectId'});
|
Book.hasMany(Author, {foreignKey: 'projectId'});
|
||||||
|
@ -24,17 +34,87 @@ describe('relations', function() {
|
||||||
b.authors.should.be.an.instanceOf(Function);
|
b.authors.should.be.an.instanceOf(Function);
|
||||||
Object.keys((new Chapter).toObject()).should.include('bookId');
|
Object.keys((new Chapter).toObject()).should.include('bookId');
|
||||||
Object.keys((new Author).toObject()).should.include('projectId');
|
Object.keys((new Author).toObject()).should.include('projectId');
|
||||||
|
|
||||||
|
db.automigrate(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can be declared in short form', function() {
|
it('can be declared in short form', function(done) {
|
||||||
Author.hasMany('readers');
|
Author.hasMany('readers');
|
||||||
(new Author).readers.should.be.an.instanceOf(Function);
|
(new Author).readers.should.be.an.instanceOf(Function);
|
||||||
Object.keys((new Reader).toObject()).should.include('authorId');
|
Object.keys((new Reader).toObject()).should.include('authorId');
|
||||||
|
|
||||||
|
db.automigrate(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should build record on scope', function(done) {
|
||||||
|
Book.create(function(err, book) {
|
||||||
|
var c = book.chapters.build();
|
||||||
|
c.bookId.should.equal(book.id);
|
||||||
|
c.save(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create record on scope', function(done) {
|
||||||
|
Book.create(function(err, book) {
|
||||||
|
book.chapters.create(function(err, c) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(c);
|
||||||
|
c.bookId.should.equal(book.id);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch all scoped instances', function(done) {
|
||||||
|
Book.create(function(err, book) {
|
||||||
|
book.chapters.create({name: 'a'}, function() {
|
||||||
|
book.chapters.create({name: 'z'}, function() {
|
||||||
|
book.chapters.create({name: 'c'}, function() {
|
||||||
|
fetch(book);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function fetch(book) {
|
||||||
|
book.chapters(function(err, ch) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(ch);
|
||||||
|
ch.should.have.lengthOf(3);
|
||||||
|
|
||||||
|
book.chapters({order: 'name DESC'}, function(e, c) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(c);
|
||||||
|
c.shift().name.should.equal('z');
|
||||||
|
c.pop().name.should.equal('a');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('belongsTo', function() {
|
describe('belongsTo', function() {
|
||||||
it('can be declared in different ways');
|
var List, Item, Fear, Mind;
|
||||||
|
|
||||||
|
it('can be declared in different ways', function() {
|
||||||
|
List = db.define('List', {name: String});
|
||||||
|
Item = db.define('Item', {name: String});
|
||||||
|
Fear = db.define('Fear');
|
||||||
|
Mind = db.define('Mind');
|
||||||
|
|
||||||
|
// syntax 1 (old)
|
||||||
|
Item.belongsTo(List);
|
||||||
|
Object.keys((new Item).toObject()).should.include('listId');
|
||||||
|
(new Item).list.should.be.an.instanceOf(Function);
|
||||||
|
|
||||||
|
// syntax 2 (new)
|
||||||
|
Fear.belongsTo('mind');
|
||||||
|
Object.keys((new Fear).toObject()).should.include('mindId');
|
||||||
|
(new Fear).mind.should.be.an.instanceOf(Function);
|
||||||
|
// (new Fear).mind.build().should.be.an.instanceOf(Mind);
|
||||||
|
});
|
||||||
|
|
||||||
it('can be declared in short form');
|
it('can be declared in short form');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue