Reformat the code
This commit is contained in:
parent
58a06272c3
commit
2b8c1ebaee
|
@ -8,56 +8,56 @@ var ds = new DataSource('memory');
|
||||||
var Application = ds.createModel('Schemaless', {}, {strict: false});
|
var Application = ds.createModel('Schemaless', {}, {strict: false});
|
||||||
|
|
||||||
var application = {
|
var application = {
|
||||||
owner: 'rfeng',
|
owner: 'rfeng',
|
||||||
name: 'MyApp1',
|
name: 'MyApp1',
|
||||||
description: 'My first app',
|
description: 'My first app',
|
||||||
pushSettings: [
|
pushSettings: [
|
||||||
{ "platform": "apns",
|
{ "platform": "apns",
|
||||||
"apns": {
|
"apns": {
|
||||||
"pushOptions": {
|
"pushOptions": {
|
||||||
"gateway": "gateway.sandbox.push.apple.com",
|
"gateway": "gateway.sandbox.push.apple.com",
|
||||||
"cert": "credentials/apns_cert_dev.pem",
|
"cert": "credentials/apns_cert_dev.pem",
|
||||||
"key": "credentials/apns_key_dev.pem"
|
"key": "credentials/apns_key_dev.pem"
|
||||||
},
|
},
|
||||||
|
|
||||||
"feedbackOptions": {
|
"feedbackOptions": {
|
||||||
"gateway": "feedback.sandbox.push.apple.com",
|
"gateway": "feedback.sandbox.push.apple.com",
|
||||||
"cert": "credentials/apns_cert_dev.pem",
|
"cert": "credentials/apns_cert_dev.pem",
|
||||||
"key": "credentials/apns_key_dev.pem",
|
"key": "credentials/apns_key_dev.pem",
|
||||||
"batchFeedback": true,
|
"batchFeedback": true,
|
||||||
"interval": 300
|
"interval": 300
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
]}
|
]}
|
||||||
|
|
||||||
console.log(new Application(application).toObject());
|
console.log(new Application(application).toObject());
|
||||||
|
|
||||||
Application.create(application, function (err, app1) {
|
Application.create(application, function (err, app1) {
|
||||||
console.log('Created: ', app1.toObject());
|
console.log('Created: ', app1.toObject());
|
||||||
Application.findById(app1.id, function (err, app2) {
|
Application.findById(app1.id, function (err, app2) {
|
||||||
console.log('Found: ', app2.toObject());
|
console.log('Found: ', app2.toObject());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Instance JSON document
|
// Instance JSON document
|
||||||
var user = {
|
var user = {
|
||||||
name: 'Joe',
|
name: 'Joe',
|
||||||
age: 30,
|
age: 30,
|
||||||
birthday: new Date(),
|
birthday: new Date(),
|
||||||
vip: true,
|
vip: true,
|
||||||
address: {
|
address: {
|
||||||
street: '1 Main St',
|
street: '1 Main St',
|
||||||
city: 'San Jose',
|
city: 'San Jose',
|
||||||
state: 'CA',
|
state: 'CA',
|
||||||
zipcode: '95131',
|
zipcode: '95131',
|
||||||
country: 'US'
|
country: 'US'
|
||||||
},
|
},
|
||||||
friends: ['John', 'Mary'],
|
friends: ['John', 'Mary'],
|
||||||
emails: [
|
emails: [
|
||||||
{label: 'work', id: 'x@sample.com'},
|
{label: 'work', id: 'x@sample.com'},
|
||||||
{label: 'home', id: 'x@home.com'}
|
{label: 'home', id: 'x@home.com'}
|
||||||
],
|
],
|
||||||
tags: []
|
tags: []
|
||||||
};
|
};
|
||||||
|
|
||||||
// Introspect the JSON document to generate a schema
|
// Introspect the JSON document to generate a schema
|
||||||
|
@ -72,10 +72,10 @@ var obj = new User(user);
|
||||||
console.log(obj.toObject());
|
console.log(obj.toObject());
|
||||||
|
|
||||||
User.create(user, function (err, u1) {
|
User.create(user, function (err, u1) {
|
||||||
console.log('Created: ', u1.toObject());
|
console.log('Created: ', u1.toObject());
|
||||||
User.findById(u1.id, function (err, u2) {
|
User.findById(u1.id, function (err, u2) {
|
||||||
console.log('Found: ', u2.toObject());
|
console.log('Found: ', u2.toObject());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,27 +2,29 @@ var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
||||||
var modelBuilder = new ModelBuilder();
|
var modelBuilder = new ModelBuilder();
|
||||||
// define models
|
// define models
|
||||||
var Post = modelBuilder.define('Post', {
|
var Post = modelBuilder.define('Post', {
|
||||||
title: { type: String, length: 255 },
|
title: { type: String, length: 255 },
|
||||||
content: { type: ModelBuilder.Text },
|
content: { type: ModelBuilder.Text },
|
||||||
date: { type: Date, default: function () { return new Date;} },
|
date: { type: Date, default: function () {
|
||||||
timestamp: { type: Number, default: Date.now },
|
return new Date();
|
||||||
published: { type: Boolean, default: false, index: true }
|
} },
|
||||||
|
timestamp: { type: Number, default: Date.now },
|
||||||
|
published: { type: Boolean, default: false, index: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
// simplier way to describe model
|
// simpler way to describe model
|
||||||
var User = modelBuilder.define('User', {
|
var User = modelBuilder.define('User', {
|
||||||
name: String,
|
name: String,
|
||||||
bio: ModelBuilder.Text,
|
bio: ModelBuilder.Text,
|
||||||
approved: Boolean,
|
approved: Boolean,
|
||||||
joinedAt: Date,
|
joinedAt: Date,
|
||||||
age: Number
|
age: Number
|
||||||
});
|
});
|
||||||
|
|
||||||
var Group = modelBuilder.define('Group', {group: String});
|
var Group = modelBuilder.define('Group', {group: String});
|
||||||
|
|
||||||
// define any custom method
|
// define any custom method
|
||||||
User.prototype.getNameAndAge = function () {
|
User.prototype.getNameAndAge = function () {
|
||||||
return this.name + ', ' + this.age;
|
return this.name + ', ' + this.age;
|
||||||
};
|
};
|
||||||
|
|
||||||
var user = new User({name: 'Joe'});
|
var user = new User({name: 'Joe'});
|
||||||
|
|
|
@ -4,29 +4,29 @@ var ds = new DataSource('memory');
|
||||||
|
|
||||||
// define models
|
// define models
|
||||||
var Post = ds.define('Post', {
|
var Post = ds.define('Post', {
|
||||||
title: { type: String, length: 255 },
|
title: { type: String, length: 255 },
|
||||||
content: { type: DataSource.Text },
|
content: { type: DataSource.Text },
|
||||||
date: { type: Date, default: function () {
|
date: { type: Date, default: function () {
|
||||||
return new Date;
|
return new Date;
|
||||||
} },
|
} },
|
||||||
timestamp: { type: Number, default: Date.now },
|
timestamp: { type: Number, default: Date.now },
|
||||||
published: { type: Boolean, default: false, index: true }
|
published: { type: Boolean, default: false, index: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
// simplier way to describe model
|
// simplier way to describe model
|
||||||
var User = ds.define('User', {
|
var User = ds.define('User', {
|
||||||
name: String,
|
name: String,
|
||||||
bio: DataSource.Text,
|
bio: DataSource.Text,
|
||||||
approved: Boolean,
|
approved: Boolean,
|
||||||
joinedAt: Date,
|
joinedAt: Date,
|
||||||
age: Number
|
age: Number
|
||||||
});
|
});
|
||||||
|
|
||||||
var Group = ds.define('Group', {name: String});
|
var Group = ds.define('Group', {name: String});
|
||||||
|
|
||||||
// define any custom method
|
// define any custom method
|
||||||
User.prototype.getNameAndAge = function () {
|
User.prototype.getNameAndAge = function () {
|
||||||
return this.name + ', ' + this.age;
|
return this.name + ', ' + this.age;
|
||||||
};
|
};
|
||||||
|
|
||||||
var user = new User({name: 'Joe'});
|
var user = new User({name: 'Joe'});
|
||||||
|
@ -53,48 +53,48 @@ User.hasAndBelongsToMany('groups');
|
||||||
|
|
||||||
var user2 = new User({name: 'Smith'});
|
var user2 = new User({name: 'Smith'});
|
||||||
user2.save(function (err) {
|
user2.save(function (err) {
|
||||||
console.log(user2);
|
console.log(user2);
|
||||||
var post = user2.posts.build({title: 'Hello world'});
|
var post = user2.posts.build({title: 'Hello world'});
|
||||||
post.save(function(err, data) {
|
post.save(function (err, data) {
|
||||||
console.log(err ? err: data);
|
console.log(err ? err : data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Post.findOne({where: {published: false}, order: 'date DESC'}, function (err, data) {
|
Post.findOne({where: {published: false}, order: 'date DESC'}, function (err, data) {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
User.create({name: 'Jeff'}, function (err, data) {
|
User.create({name: 'Jeff'}, function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(data);
|
console.log(data);
|
||||||
var post = data.posts.build({title: 'My Post'});
|
var post = data.posts.build({title: 'My Post'});
|
||||||
console.log(post);
|
console.log(post);
|
||||||
});
|
});
|
||||||
|
|
||||||
User.create({name: 'Ray'}, function (err, data) {
|
User.create({name: 'Ray'}, function (err, data) {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
User.scope('minors', {age: {le: 16}});
|
User.scope('minors', {age: {le: 16}});
|
||||||
User.minors(function(err, kids) {
|
User.minors(function (err, kids) {
|
||||||
console.log('Kids: ', kids);
|
console.log('Kids: ', kids);
|
||||||
});
|
});
|
||||||
|
|
||||||
var Article = ds.define('Article', {title: String});
|
var Article = ds.define('Article', {title: String});
|
||||||
var Tag = ds.define('Tag', {name: String});
|
var Tag = ds.define('Tag', {name: String});
|
||||||
Article.hasAndBelongsToMany('tags');
|
Article.hasAndBelongsToMany('tags');
|
||||||
|
|
||||||
Article.create(function(e, article) {
|
Article.create(function (e, article) {
|
||||||
article.tags.create({name: 'popular'}, function (err, data) {
|
article.tags.create({name: 'popular'}, function (err, data) {
|
||||||
Article.findOne(function(e, article) {
|
Article.findOne(function (e, article) {
|
||||||
article.tags(function(e, tags) {
|
article.tags(function (e, tags) {
|
||||||
console.log(tags);
|
console.log(tags);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// should be able to attach a data source to an existing model
|
// should be able to attach a data source to an existing model
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
{
|
{
|
||||||
"title": {
|
"title": {
|
||||||
"type": "String"
|
"type": "String"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"type": "String",
|
"type": "String",
|
||||||
"default": "Raymond"
|
"default": "Raymond"
|
||||||
},
|
},
|
||||||
"body": "String",
|
"body": "String",
|
||||||
"date": {
|
"date": {
|
||||||
"type": "Date"
|
"type": "Date"
|
||||||
},
|
},
|
||||||
"hidden": "Boolean",
|
"hidden": "Boolean",
|
||||||
"comments": ["String"]
|
"comments": ["String"]
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
var path = require('path'),
|
var path = require('path'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
DataSource = require('../lib/datasource').DataSource;
|
DataSource = require('../lib/datasource').DataSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load LDL schemas from a json doc
|
* Load LDL schemas from a json doc
|
||||||
|
@ -8,27 +8,27 @@ var path = require('path'),
|
||||||
* @returns A map of schemas keyed by name
|
* @returns A map of schemas keyed by name
|
||||||
*/
|
*/
|
||||||
function loadSchemasSync(schemaFile, dataSource) {
|
function loadSchemasSync(schemaFile, dataSource) {
|
||||||
// Set up the data source
|
// Set up the data source
|
||||||
if(!dataSource) {
|
if (!dataSource) {
|
||||||
dataSource = new DataSource('memory');
|
dataSource = new DataSource('memory');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the dataSource JSON file
|
// Read the dataSource JSON file
|
||||||
var schemas = JSON.parse(fs.readFileSync(schemaFile));
|
var schemas = JSON.parse(fs.readFileSync(schemaFile));
|
||||||
|
|
||||||
return dataSource.buildModels(schemas);
|
return dataSource.buildModels(schemas);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var models = loadSchemasSync(path.join(__dirname, 'jdb-schemas.json'));
|
var models = loadSchemasSync(path.join(__dirname, 'jdb-schemas.json'));
|
||||||
|
|
||||||
for (var s in models) {
|
for (var s in models) {
|
||||||
var m = models[s];
|
var m = models[s];
|
||||||
console.log(m.modelName, new m());
|
console.log(m.modelName, new m());
|
||||||
}
|
}
|
||||||
|
|
||||||
models = loadSchemasSync(path.join(__dirname, 'schemas.json'));
|
models = loadSchemasSync(path.join(__dirname, 'schemas.json'));
|
||||||
for (var s in models) {
|
for (var s in models) {
|
||||||
var m = models[s];
|
var m = models[s];
|
||||||
console.log(m.modelName, new m());
|
console.log(m.modelName, new m());
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,26 +3,30 @@ var modelBuilder = new ModelBuilder();
|
||||||
|
|
||||||
// simplier way to describe model
|
// simplier way to describe model
|
||||||
var User = modelBuilder.define('User', {
|
var User = modelBuilder.define('User', {
|
||||||
name: String,
|
name: String,
|
||||||
bio: ModelBuilder.Text,
|
bio: ModelBuilder.Text,
|
||||||
approved: Boolean,
|
approved: Boolean,
|
||||||
joinedAt: Date,
|
joinedAt: Date,
|
||||||
age: Number,
|
age: Number,
|
||||||
address: {
|
address: {
|
||||||
street: String,
|
street: String,
|
||||||
city: String,
|
city: String,
|
||||||
state: String,
|
state: String,
|
||||||
zipCode: String,
|
zipCode: String,
|
||||||
country: String
|
country: String
|
||||||
},
|
},
|
||||||
emails: [{
|
emails: [
|
||||||
label: String,
|
{
|
||||||
email: String
|
label: String,
|
||||||
}],
|
email: String
|
||||||
friends: [String]
|
}
|
||||||
|
],
|
||||||
|
friends: [String]
|
||||||
});
|
});
|
||||||
|
|
||||||
var user = new User({name: 'Joe', age: 20, address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'},
|
var user = new User({name: 'Joe', age: 20, address: {street: '123 Main St', 'city': 'San Jose', state: 'CA'},
|
||||||
emails: [{label: 'work', email: 'xyz@sample.com'}],
|
emails: [
|
||||||
friends: ['John', 'Mary']});
|
{label: 'work', email: 'xyz@sample.com'}
|
||||||
|
],
|
||||||
|
friends: ['John', 'Mary']});
|
||||||
console.log(user.toObject());
|
console.log(user.toObject());
|
||||||
|
|
|
@ -2,58 +2,56 @@ var DataSource = require('../index').DataSource;
|
||||||
var ds = new DataSource('memory');
|
var ds = new DataSource('memory');
|
||||||
|
|
||||||
var Order = ds.createModel('Order', {
|
var Order = ds.createModel('Order', {
|
||||||
customerId: Number,
|
customerId: Number,
|
||||||
orderDate: Date
|
orderDate: Date
|
||||||
});
|
});
|
||||||
|
|
||||||
var Customer = ds.createModel('Customer', {
|
var Customer = ds.createModel('Customer', {
|
||||||
name: String
|
name: String
|
||||||
});
|
});
|
||||||
|
|
||||||
Order.belongsTo(Customer);
|
Order.belongsTo(Customer);
|
||||||
|
|
||||||
Customer.create({name: 'John'}, function (err, customer) {
|
Customer.create({name: 'John'}, function (err, customer) {
|
||||||
Order.create({customerId: customer.id, orderDate: new Date()}, function (err, order) {
|
Order.create({customerId: customer.id, orderDate: new Date()}, function (err, order) {
|
||||||
order.customer(console.log);
|
order.customer(console.log);
|
||||||
order.customer(true, console.log);
|
order.customer(true, console.log);
|
||||||
|
|
||||||
Customer.create({name: 'Mary'}, function (err, customer2) {
|
Customer.create({name: 'Mary'}, function (err, customer2) {
|
||||||
order.customer(customer2);
|
order.customer(customer2);
|
||||||
order.customer(console.log);
|
order.customer(console.log);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Customer.hasMany(Order, {as: 'orders', foreignKey: 'customerId'});
|
Customer.hasMany(Order, {as: 'orders', foreignKey: 'customerId'});
|
||||||
|
|
||||||
Customer.create({name: 'Ray'}, function (err, customer) {
|
Customer.create({name: 'Ray'}, function (err, customer) {
|
||||||
Order.create({customerId: customer.id, orderDate: new Date()}, function (err, order) {
|
Order.create({customerId: customer.id, orderDate: new Date()}, function (err, order) {
|
||||||
customer.orders(console.log);
|
customer.orders(console.log);
|
||||||
customer.orders.create({orderDate: new Date()}, function(err, order) {
|
customer.orders.create({orderDate: new Date()}, function (err, order) {
|
||||||
console.log(order);
|
console.log(order);
|
||||||
Customer.include([customer], 'orders', function(err, results) {
|
Customer.include([customer], 'orders', function (err, results) {
|
||||||
console.log('Results: ', results);
|
console.log('Results: ', results);
|
||||||
});
|
});
|
||||||
customer.orders.findById('2', console.log);
|
customer.orders.findById('2', console.log);
|
||||||
customer.orders.destroy('2', console.log);
|
customer.orders.destroy('2', console.log);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
var Physician = ds.createModel('Physician', {
|
var Physician = ds.createModel('Physician', {
|
||||||
name: String
|
name: String
|
||||||
});
|
});
|
||||||
|
|
||||||
var Patient = ds.createModel('Patient', {
|
var Patient = ds.createModel('Patient', {
|
||||||
name: String
|
name: String
|
||||||
});
|
});
|
||||||
|
|
||||||
var Appointment = ds.createModel('Appointment', {
|
var Appointment = ds.createModel('Appointment', {
|
||||||
physicianId: Number,
|
physicianId: Number,
|
||||||
patientId: Number,
|
patientId: Number,
|
||||||
appointmentDate: Date
|
appointmentDate: Date
|
||||||
});
|
});
|
||||||
|
|
||||||
Appointment.belongsTo(Patient);
|
Appointment.belongsTo(Patient);
|
||||||
|
@ -63,33 +61,32 @@ Physician.hasMany(Patient, {through: Appointment});
|
||||||
Patient.hasMany(Physician, {through: Appointment});
|
Patient.hasMany(Physician, {through: Appointment});
|
||||||
|
|
||||||
Physician.create({name: 'Smith'}, function (err, physician) {
|
Physician.create({name: 'Smith'}, function (err, physician) {
|
||||||
Patient.create({name: 'Mary'}, function (err, patient) {
|
Patient.create({name: 'Mary'}, function (err, patient) {
|
||||||
Appointment.create({appointmentDate: new Date(), physicianId: physician.id, patientId: patient.id},
|
Appointment.create({appointmentDate: new Date(), physicianId: physician.id, patientId: patient.id},
|
||||||
function (err, appt) {
|
function (err, appt) {
|
||||||
physician.patients(console.log);
|
physician.patients(console.log);
|
||||||
patient.physicians(console.log);
|
patient.physicians(console.log);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
var Assembly = ds.createModel('Assembly', {
|
var Assembly = ds.createModel('Assembly', {
|
||||||
name: String
|
name: String
|
||||||
});
|
});
|
||||||
|
|
||||||
var Part = ds.createModel('Part', {
|
var Part = ds.createModel('Part', {
|
||||||
partNumber: String
|
partNumber: String
|
||||||
});
|
});
|
||||||
|
|
||||||
Assembly.hasAndBelongsToMany(Part);
|
Assembly.hasAndBelongsToMany(Part);
|
||||||
Part.hasAndBelongsToMany(Assembly);
|
Part.hasAndBelongsToMany(Assembly);
|
||||||
|
|
||||||
Assembly.create({name: 'car'}, function (err, assembly) {
|
Assembly.create({name: 'car'}, function (err, assembly) {
|
||||||
Part.create({partNumber: 'engine'}, function (err, part) {
|
Part.create({partNumber: 'engine'}, function (err, part) {
|
||||||
assembly.parts.add(part, function(err) {
|
assembly.parts.add(part, function (err) {
|
||||||
assembly.parts(console.log);
|
assembly.parts(console.log);
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,83 +1,83 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "Address",
|
"name": "Address",
|
||||||
"properties": {
|
"properties": {
|
||||||
"label": "string",
|
"label": "string",
|
||||||
"street": "string",
|
"street": "string",
|
||||||
"city": "string",
|
"city": "string",
|
||||||
"zipCode": "string"
|
"zipCode": "string"
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Account",
|
|
||||||
"properties": {
|
|
||||||
"id": "string",
|
|
||||||
"customer": {
|
|
||||||
"type": "Customer",
|
|
||||||
"relation": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"as": "account"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"balance": "number"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Customer",
|
|
||||||
"options": {
|
|
||||||
"oracle": {
|
|
||||||
"owner": "STRONGLOOP",
|
|
||||||
"table": "CUSTOMER"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "number",
|
|
||||||
"id": true,
|
|
||||||
"doc": "Customer ID"
|
|
||||||
},
|
|
||||||
"firstName": {
|
|
||||||
"type": "string",
|
|
||||||
"trim": true,
|
|
||||||
"required": true,
|
|
||||||
"oracle": {
|
|
||||||
"column": "FNAME",
|
|
||||||
"type": "VARCHAR",
|
|
||||||
"length": 32
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lastName": {
|
|
||||||
"type": "string",
|
|
||||||
"trim": true,
|
|
||||||
"required": true,
|
|
||||||
"oracle": {
|
|
||||||
"column": "LNAME",
|
|
||||||
"type": "VARCHAR",
|
|
||||||
"length": 32
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"vip": {
|
|
||||||
"type": "boolean",
|
|
||||||
"doc": "indicate if the customer is a VIP",
|
|
||||||
"oracle": {
|
|
||||||
"column": "VIP",
|
|
||||||
"type": "CHAR",
|
|
||||||
"length": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"emails": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"trim": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"address": {
|
|
||||||
"type": "Address"
|
|
||||||
},
|
|
||||||
"account": "Account"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Account",
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"customer": {
|
||||||
|
"type": "Customer",
|
||||||
|
"relation": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"as": "account"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"balance": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Customer",
|
||||||
|
"options": {
|
||||||
|
"oracle": {
|
||||||
|
"owner": "STRONGLOOP",
|
||||||
|
"table": "CUSTOMER"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"doc": "Customer ID"
|
||||||
|
},
|
||||||
|
"firstName": {
|
||||||
|
"type": "string",
|
||||||
|
"trim": true,
|
||||||
|
"required": true,
|
||||||
|
"oracle": {
|
||||||
|
"column": "FNAME",
|
||||||
|
"type": "VARCHAR",
|
||||||
|
"length": 32
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lastName": {
|
||||||
|
"type": "string",
|
||||||
|
"trim": true,
|
||||||
|
"required": true,
|
||||||
|
"oracle": {
|
||||||
|
"column": "LNAME",
|
||||||
|
"type": "VARCHAR",
|
||||||
|
"length": 32
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vip": {
|
||||||
|
"type": "boolean",
|
||||||
|
"doc": "indicate if the customer is a VIP",
|
||||||
|
"oracle": {
|
||||||
|
"column": "VIP",
|
||||||
|
"type": "CHAR",
|
||||||
|
"length": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emails": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"trim": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"address": {
|
||||||
|
"type": "Address"
|
||||||
|
},
|
||||||
|
"account": "Account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
]
|
]
|
|
@ -6,9 +6,9 @@ module.exports = Connector;
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function Connector(name, settings) {
|
function Connector(name, settings) {
|
||||||
this._models = {};
|
this._models = {};
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.settings = settings || {};
|
this.settings = settings || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,21 +24,20 @@ Connector.prototype.relational = false;
|
||||||
* @param {Function} [callback] The callback function
|
* @param {Function} [callback] The callback function
|
||||||
*/
|
*/
|
||||||
Connector.prototype.execute = function (command, params, callback) {
|
Connector.prototype.execute = function (command, params, callback) {
|
||||||
throw new Error('query method should be declared in connector');
|
throw new Error('query method should be declared in connector');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look up the data source by model name
|
* Look up the data source by model name
|
||||||
* @param {String} model The model name
|
* @param {String} model The model name
|
||||||
* @returns {DataSource} The data source
|
* @returns {DataSource} The data source
|
||||||
*/
|
*/
|
||||||
Connector.prototype.getDataSource = function(model) {
|
Connector.prototype.getDataSource = function (model) {
|
||||||
var m = this._models[model];
|
var m = this._models[model];
|
||||||
if(!m) {
|
if (!m) {
|
||||||
console.trace('Model not found: ' + model);
|
console.trace('Model not found: ' + model);
|
||||||
}
|
}
|
||||||
return m && m.model.dataSource;
|
return m && m.model.dataSource;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +46,7 @@ Connector.prototype.getDataSource = function(model) {
|
||||||
* @returns {String} The id property name
|
* @returns {String} The id property name
|
||||||
*/
|
*/
|
||||||
Connector.prototype.idName = function (model) {
|
Connector.prototype.idName = function (model) {
|
||||||
return this.getDataSource(model).idName(model);
|
return this.getDataSource(model).idName(model);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,10 +55,9 @@ Connector.prototype.idName = function (model) {
|
||||||
* @returns {[String]} The id property names
|
* @returns {[String]} The id property names
|
||||||
*/
|
*/
|
||||||
Connector.prototype.idNames = function (model) {
|
Connector.prototype.idNames = function (model) {
|
||||||
return this.getDataSource(model).idNames(model);
|
return this.getDataSource(model).idNames(model);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the id index (sequence number, starting from 1)
|
* Get the id index (sequence number, starting from 1)
|
||||||
* @param {String} model The model name
|
* @param {String} model The model name
|
||||||
|
@ -67,11 +65,11 @@ Connector.prototype.idNames = function (model) {
|
||||||
* @returns {Number} The id index, undefined if the property is not part of the primary key
|
* @returns {Number} The id index, undefined if the property is not part of the primary key
|
||||||
*/
|
*/
|
||||||
Connector.prototype.id = function (model, prop) {
|
Connector.prototype.id = function (model, prop) {
|
||||||
var p = this._models[model].properties[prop];
|
var p = this._models[model].properties[prop];
|
||||||
if(!p) {
|
if (!p) {
|
||||||
console.trace('Property not found: ' + model +'.' + prop);
|
console.trace('Property not found: ' + model + '.' + prop);
|
||||||
}
|
}
|
||||||
return p.id;
|
return p.id;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,10 +77,10 @@ Connector.prototype.id = function (model, prop) {
|
||||||
* @param {Object} modelDefinition The model definition
|
* @param {Object} modelDefinition The model definition
|
||||||
*/
|
*/
|
||||||
Connector.prototype.define = function (modelDefinition) {
|
Connector.prototype.define = function (modelDefinition) {
|
||||||
if (!modelDefinition.settings) {
|
if (!modelDefinition.settings) {
|
||||||
modelDefinition.settings = {};
|
modelDefinition.settings = {};
|
||||||
}
|
}
|
||||||
this._models[modelDefinition.model.modelName] = modelDefinition;
|
this._models[modelDefinition.model.modelName] = modelDefinition;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,14 +90,14 @@ Connector.prototype.define = function (modelDefinition) {
|
||||||
* @param {Object} propertyDefinition The object for property metadata
|
* @param {Object} propertyDefinition The object for property metadata
|
||||||
*/
|
*/
|
||||||
Connector.prototype.defineProperty = function (model, propertyName, propertyDefinition) {
|
Connector.prototype.defineProperty = function (model, propertyName, propertyDefinition) {
|
||||||
this._models[model].properties[propertyName] = propertyDefinition;
|
this._models[model].properties[propertyName] = propertyDefinition;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnect from the connector
|
* Disconnect from the connector
|
||||||
*/
|
*/
|
||||||
Connector.prototype.disconnect = function disconnect(cb) {
|
Connector.prototype.disconnect = function disconnect(cb) {
|
||||||
// NO-OP
|
// NO-OP
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -109,8 +107,8 @@ Connector.prototype.disconnect = function disconnect(cb) {
|
||||||
* @returns {*} The id value
|
* @returns {*} The id value
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Connector.prototype.getIdValue = function(model, data) {
|
Connector.prototype.getIdValue = function (model, data) {
|
||||||
return data && data[this.idName(model)];
|
return data && data[this.idName(model)];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,10 +118,14 @@ Connector.prototype.getIdValue = function(model, data) {
|
||||||
* @param {*} value The id value
|
* @param {*} value The id value
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Connector.prototype.setIdValue = function(model, data, value) {
|
Connector.prototype.setIdValue = function (model, data, value) {
|
||||||
if(data) {
|
if (data) {
|
||||||
data[this.idName(model)] = value;
|
data[this.idName(model)] = value;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Connector.prototype.getType = function () {
|
||||||
|
return this.type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,328 +9,335 @@ var cradle = safeRequire('cradle');
|
||||||
* Private functions for internal use
|
* Private functions for internal use
|
||||||
*/
|
*/
|
||||||
function CradleAdapter(client) {
|
function CradleAdapter(client) {
|
||||||
this._models = {};
|
this._models = {};
|
||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createdbif(client, callback) {
|
function createdbif(client, callback) {
|
||||||
client.exists(function (err, exists) {
|
client.exists(function (err, exists) {
|
||||||
if(err) callback(err);
|
if (err) callback(err);
|
||||||
if (!exists) { client.create(function() { callback(); }); }
|
if (!exists) {
|
||||||
else { callback(); }
|
client.create(function () {
|
||||||
});
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function naturalize(data, model) {
|
function naturalize(data, model) {
|
||||||
data.nature = model;
|
data.nature = model;
|
||||||
//TODO: maybe this is not a really good idea
|
//TODO: maybe this is not a really good idea
|
||||||
if(data.date) data.date = data.date.toString();
|
if (data.date) data.date = data.date.toString();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
function idealize(data) {
|
function idealize(data) {
|
||||||
data.id = data._id;
|
data.id = data._id;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
function stringify(data) {
|
function stringify(data) {
|
||||||
return data ? data.toString() : data
|
return data ? data.toString() : data
|
||||||
}
|
}
|
||||||
|
|
||||||
function errorHandler(callback, func) {
|
function errorHandler(callback, func) {
|
||||||
return function(err, res) {
|
return function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log('cradle', err);
|
console.log('cradle', err);
|
||||||
callback(err);
|
callback(err);
|
||||||
} else {
|
} else {
|
||||||
if(func) {
|
if (func) {
|
||||||
func(res, function(res) {
|
func(res, function (res) {
|
||||||
callback(null, res);
|
callback(null, res);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
callback(null, res);
|
callback(null, res);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function synchronize(functions, args, callback) {
|
function synchronize(functions, args, callback) {
|
||||||
if(functions.length === 0) callback();
|
if (functions.length === 0) callback();
|
||||||
if(functions.length > 0 && args.length === functions.length) {
|
if (functions.length > 0 && args.length === functions.length) {
|
||||||
functions[0](args[0][0], args[0][1], function(err, res) {
|
functions[0](args[0][0], args[0][1], function (err, res) {
|
||||||
if(err) callback(err);
|
if (err) callback(err);
|
||||||
functions.splice(0, 1);
|
functions.splice(0, 1);
|
||||||
args.splice(0, 1);
|
args.splice(0, 1);
|
||||||
synchronize(functions, args, callback);
|
synchronize(functions, args, callback);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function applyFilter(filter) {
|
function applyFilter(filter) {
|
||||||
if (typeof filter.where === 'function') {
|
if (typeof filter.where === 'function') {
|
||||||
return filter.where;
|
return filter.where;
|
||||||
}
|
}
|
||||||
var keys = Object.keys(filter.where);
|
var keys = Object.keys(filter.where);
|
||||||
return function (obj) {
|
return function (obj) {
|
||||||
var pass = true;
|
var pass = true;
|
||||||
keys.forEach(function (key) {
|
keys.forEach(function (key) {
|
||||||
if (!test(filter.where[key], obj[key])) {
|
if (!test(filter.where[key], obj[key])) {
|
||||||
pass = false;
|
pass = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
function test(example, value) {
|
function test(example, value) {
|
||||||
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
||||||
return value.match(example);
|
return value.match(example);
|
||||||
}
|
|
||||||
// not strict equality
|
|
||||||
return example == value;
|
|
||||||
}
|
}
|
||||||
|
// not strict equality
|
||||||
|
return example == value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function numerically(a, b) {
|
function numerically(a, b) {
|
||||||
return a[this[0]] - b[this[0]];
|
return a[this[0]] - b[this[0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
function literally(a, b) {
|
function literally(a, b) {
|
||||||
return a[this[0]] > b[this[0]];
|
return a[this[0]] > b[this[0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
function filtering(res, model, filter, instance) {
|
function filtering(res, model, filter, instance) {
|
||||||
|
|
||||||
if(model) {
|
if (model) {
|
||||||
if(filter == null) filter = {};
|
if (filter == null) filter = {};
|
||||||
if(filter.where == null) filter.where = {};
|
if (filter.where == null) filter.where = {};
|
||||||
filter.where.nature = model;
|
filter.where.nature = model;
|
||||||
}
|
}
|
||||||
// do we need some filtration?
|
// do we need some filtration?
|
||||||
if (filter.where) {
|
if (filter.where) {
|
||||||
res = res ? res.filter(applyFilter(filter)) : res;
|
res = res ? res.filter(applyFilter(filter)) : res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do we need some sorting?
|
// do we need some sorting?
|
||||||
if (filter.order) {
|
if (filter.order) {
|
||||||
var props = instance[model].properties;
|
var props = instance[model].properties;
|
||||||
var allNumeric = true;
|
var allNumeric = true;
|
||||||
var orders = filter.order;
|
var orders = filter.order;
|
||||||
var reverse = false;
|
var reverse = false;
|
||||||
if (typeof filter.order === "string") {
|
if (typeof filter.order === "string") {
|
||||||
orders = [filter.order];
|
orders = [filter.order];
|
||||||
}
|
}
|
||||||
|
|
||||||
orders.forEach(function (key, i) {
|
orders.forEach(function (key, i) {
|
||||||
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 = true;
|
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();
|
orders[i] = key;
|
||||||
}
|
if (props[key].type.name !== 'Number') {
|
||||||
return res;
|
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
|
* Connection/Disconnection
|
||||||
*/
|
*/
|
||||||
exports.initialize = function(dataSource, callback) {
|
exports.initialize = function (dataSource, callback) {
|
||||||
if (!cradle) return;
|
if (!cradle) return;
|
||||||
|
|
||||||
// when using cradle if we dont wait for the dataSource to be connected, the models fails to load correctly.
|
// when using cradle if we dont wait for the dataSource to be connected, the models fails to load correctly.
|
||||||
dataSource.waitForConnect = true;
|
dataSource.waitForConnect = true;
|
||||||
if (!dataSource.settings.url) {
|
if (!dataSource.settings.url) {
|
||||||
var host = dataSource.settings.host || 'localhost';
|
var host = dataSource.settings.host || 'localhost';
|
||||||
var port = dataSource.settings.port || '5984';
|
var port = dataSource.settings.port || '5984';
|
||||||
var options = dataSource.settings.options || {
|
var options = dataSource.settings.options || {
|
||||||
cache: true,
|
cache: true,
|
||||||
raw: false
|
raw: false
|
||||||
};
|
};
|
||||||
if (dataSource.settings.username) {
|
if (dataSource.settings.username) {
|
||||||
options.auth = {};
|
options.auth = {};
|
||||||
options.auth.username = dataSource.settings.username;
|
options.auth.username = dataSource.settings.username;
|
||||||
if (dataSource.settings.password) {
|
if (dataSource.settings.password) {
|
||||||
options.auth.password = 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);
|
var database = dataSource.settings.database || 'loopback-datasource-juggler';
|
||||||
|
|
||||||
createdbif(
|
dataSource.settings.host = host;
|
||||||
dataSource.client,
|
dataSource.settings.port = port;
|
||||||
errorHandler(callback, function() {
|
dataSource.settings.database = database;
|
||||||
dataSource.connector = new CradleAdapter(dataSource.client);
|
dataSource.settings.options = options;
|
||||||
process.nextTick(callback);
|
}
|
||||||
}));
|
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() {
|
CradleAdapter.prototype.disconnect = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write methods
|
* Write methods
|
||||||
*/
|
*/
|
||||||
CradleAdapter.prototype.define = function(descr) {
|
CradleAdapter.prototype.define = function (descr) {
|
||||||
this._models[descr.model.modelName] = descr;
|
this._models[descr.model.modelName] = descr;
|
||||||
};
|
};
|
||||||
|
|
||||||
CradleAdapter.prototype.create = function(model, data, callback) {
|
CradleAdapter.prototype.create = function (model, data, callback) {
|
||||||
this.client.save(
|
this.client.save(
|
||||||
stringify(data.id),
|
stringify(data.id),
|
||||||
naturalize(data, model),
|
naturalize(data, model),
|
||||||
errorHandler(callback, function(res, cb) {
|
errorHandler(callback, function (res, cb) {
|
||||||
cb(res.id);
|
cb(res.id);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
CradleAdapter.prototype.save = function(model, data, callback) {
|
CradleAdapter.prototype.save = function (model, data, callback) {
|
||||||
this.client.save(
|
this.client.save(
|
||||||
stringify(data.id),
|
stringify(data.id),
|
||||||
naturalize(data, model),
|
naturalize(data, model),
|
||||||
errorHandler(callback)
|
errorHandler(callback)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
CradleAdapter.prototype.updateAttributes = function(model, id, data, callback) {
|
CradleAdapter.prototype.updateAttributes = function (model, id, data, callback) {
|
||||||
this.client.merge(
|
this.client.merge(
|
||||||
stringify(id),
|
stringify(id),
|
||||||
data,
|
data,
|
||||||
errorHandler(callback, function(doc, cb) {
|
errorHandler(callback, function (doc, cb) {
|
||||||
cb(idealize(doc));
|
cb(idealize(doc));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
CradleAdapter.prototype.updateOrCreate = function(model, data, callback) {
|
CradleAdapter.prototype.updateOrCreate = function (model, data, callback) {
|
||||||
this.client.get(
|
this.client.get(
|
||||||
stringify(data.id),
|
stringify(data.id),
|
||||||
function (err, doc) {
|
function (err, doc) {
|
||||||
if(err) {
|
if (err) {
|
||||||
this.create(model, data, callback);
|
this.create(model, data, callback);
|
||||||
} else {
|
} else {
|
||||||
this.updateAttributes(model, data.id, data, callback);
|
this.updateAttributes(model, data.id, data, callback);
|
||||||
}
|
}
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read methods
|
* Read methods
|
||||||
*/
|
*/
|
||||||
CradleAdapter.prototype.exists = function(model, id, callback) {
|
CradleAdapter.prototype.exists = function (model, id, callback) {
|
||||||
this.client.get(
|
this.client.get(
|
||||||
stringify(id),
|
stringify(id),
|
||||||
errorHandler(callback, function(doc, cb) {
|
errorHandler(callback, function (doc, cb) {
|
||||||
cb(!!doc);
|
cb(!!doc);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
CradleAdapter.prototype.find = function(model, id, callback) {
|
CradleAdapter.prototype.find = function (model, id, callback) {
|
||||||
this.client.get(
|
this.client.get(
|
||||||
stringify(id),
|
stringify(id),
|
||||||
errorHandler(callback, function(doc, cb) {
|
errorHandler(callback, function (doc, cb) {
|
||||||
cb(idealize(doc));
|
cb(idealize(doc));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
CradleAdapter.prototype.count = function(model, callback, where) {
|
CradleAdapter.prototype.count = function (model, callback, where) {
|
||||||
this.models(
|
this.models(
|
||||||
model,
|
model,
|
||||||
{where: where},
|
{where: where},
|
||||||
callback,
|
callback,
|
||||||
function(docs, cb) {
|
function (docs, cb) {
|
||||||
cb(docs.length);
|
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) {
|
CradleAdapter.prototype.models = function (model, filter, callback, func) {
|
||||||
this.models(
|
var limit = 200;
|
||||||
model,
|
var skip = 0;
|
||||||
filter,
|
if (filter != null) {
|
||||||
callback
|
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
|
* Detroy methods
|
||||||
*/
|
*/
|
||||||
CradleAdapter.prototype.destroy = function(model, id, callback) {
|
CradleAdapter.prototype.destroy = function (model, id, callback) {
|
||||||
this.client.remove(
|
this.client.remove(
|
||||||
stringify(id),
|
stringify(id),
|
||||||
function (err, doc) {
|
function (err, doc) {
|
||||||
callback(err);
|
callback(err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
CradleAdapter.prototype.destroyAll = function(model, callback) {
|
CradleAdapter.prototype.destroyAll = function (model, callback) {
|
||||||
this.models(
|
this.models(
|
||||||
model,
|
model,
|
||||||
null,
|
null,
|
||||||
callback,
|
callback,
|
||||||
function(docs, cb) {
|
function (docs, cb) {
|
||||||
var docIds = docs.map(function(doc) {
|
var docIds = docs.map(function (doc) {
|
||||||
return doc.id;
|
return doc.id;
|
||||||
});
|
});
|
||||||
this.client.get(docIds, function(err, res) {
|
this.client.get(docIds, function (err, res) {
|
||||||
if(err) cb(err);
|
if (err) cb(err);
|
||||||
|
|
||||||
var funcs = res.map(function(doc) {
|
var funcs = res.map(function (doc) {
|
||||||
return this.client.remove.bind(this.client);
|
return this.client.remove.bind(this.client);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
var args = res.map(function(doc) {
|
var args = res.map(function (doc) {
|
||||||
return [doc._id, doc._rev];
|
return [doc._id, doc._rev];
|
||||||
});
|
});
|
||||||
|
|
||||||
synchronize(funcs, args, cb);
|
synchronize(funcs, args, cb);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,188 +1,190 @@
|
||||||
exports.initialize = function initializeSchema(dataSource, callback) {
|
exports.initialize = function initializeSchema(dataSource, callback) {
|
||||||
dataSource.connector = new WebService();
|
dataSource.connector = new WebService();
|
||||||
process.nextTick(callback);
|
process.nextTick(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
function WebService() {
|
function WebService() {
|
||||||
this._models = {};
|
this._models = {};
|
||||||
this.cache = {};
|
this.cache = {};
|
||||||
this.ids = {};
|
this.ids = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
WebService.prototype.installPostProcessor = function installPostProcessor(descr) {
|
WebService.prototype.installPostProcessor = function installPostProcessor(descr) {
|
||||||
var dates = [];
|
var dates = [];
|
||||||
Object.keys(descr.properties).forEach(function(column) {
|
Object.keys(descr.properties).forEach(function (column) {
|
||||||
if (descr.properties[column].type.name === 'Date') {
|
if (descr.properties[column].type.name === 'Date') {
|
||||||
dates.push(column);
|
dates.push(column);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var postProcessor = function(model) {
|
var postProcessor = function (model) {
|
||||||
var max = dates.length;
|
var max = dates.length;
|
||||||
for (var i = 0; i < max; i++) {
|
for (var i = 0; i < max; i++) {
|
||||||
var column = dates[i];
|
var column = dates[i];
|
||||||
if (model[column]) {
|
if (model[column]) {
|
||||||
model[column] = new Date(model[column]);
|
model[column] = new Date(model[column]);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
;
|
||||||
|
};
|
||||||
|
|
||||||
descr.postProcessor = postProcessor;
|
descr.postProcessor = postProcessor;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.preProcess = function preProcess(data) {
|
WebService.prototype.preProcess = function preProcess(data) {
|
||||||
var result = {};
|
var result = {};
|
||||||
Object.keys(data).forEach(function(key) {
|
Object.keys(data).forEach(function (key) {
|
||||||
if (data[key] != null) {
|
if (data[key] != null) {
|
||||||
result[key] = data[key];
|
result[key] = data[key];
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.postProcess = function postProcess(model, data) {
|
WebService.prototype.postProcess = function postProcess(model, data) {
|
||||||
var postProcessor = this._models[model].postProcessor;
|
var postProcessor = this._models[model].postProcessor;
|
||||||
if (postProcessor && data) {
|
if (postProcessor && data) {
|
||||||
postProcessor(data);
|
postProcessor(data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.postProcessMultiple = function postProcessMultiple(model, data) {
|
WebService.prototype.postProcessMultiple = function postProcessMultiple(model, data) {
|
||||||
var postProcessor = this._models[model].postProcessor;
|
var postProcessor = this._models[model].postProcessor;
|
||||||
if (postProcessor) {
|
if (postProcessor) {
|
||||||
var max = data.length;
|
var max = data.length;
|
||||||
for (var i = 0; i < max; i++) {
|
for (var i = 0; i < max; i++) {
|
||||||
if (data[i]) {
|
if (data[i]) {
|
||||||
postProcessor(data[i]);
|
postProcessor(data[i]);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.define = function defineModel(descr) {
|
WebService.prototype.define = function defineModel(descr) {
|
||||||
var m = descr.model.modelName;
|
var m = descr.model.modelName;
|
||||||
this.installPostProcessor(descr);
|
this.installPostProcessor(descr);
|
||||||
this._models[m] = descr;
|
this._models[m] = descr;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.getResourceUrl = function getResourceUrl(model) {
|
WebService.prototype.getResourceUrl = function getResourceUrl(model) {
|
||||||
var url = this._models[model].settings.restPath;
|
var url = this._models[model].settings.restPath;
|
||||||
if (!url) throw new Error('Resource url (restPath) for ' + model + ' is not defined');
|
if (!url) throw new Error('Resource url (restPath) for ' + model + ' is not defined');
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.getBlankReq = function () {
|
WebService.prototype.getBlankReq = function () {
|
||||||
if (!this.csrfToken) {
|
if (!this.csrfToken) {
|
||||||
this.csrfToken = $('meta[name=csrf-token]').attr('content');
|
this.csrfToken = $('meta[name=csrf-token]').attr('content');
|
||||||
this.csrfParam = $('meta[name=csrf-param]').attr('content');
|
this.csrfParam = $('meta[name=csrf-param]').attr('content');
|
||||||
}
|
}
|
||||||
var req = {};
|
var req = {};
|
||||||
req[this.csrfParam] = this.csrfToken;
|
req[this.csrfParam] = this.csrfToken;
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebService.prototype.create = function create(model, data, callback) {
|
WebService.prototype.create = function create(model, data, callback) {
|
||||||
var req = this.getBlankReq();
|
var req = this.getBlankReq();
|
||||||
req[model] = this.preProcess(data);
|
req[model] = this.preProcess(data);
|
||||||
$.post(this.getResourceUrl(model) + '.json', req, function (res) {
|
$.post(this.getResourceUrl(model) + '.json', req, function (res) {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
callback(null, res.data.id);
|
callback(null, res.data.id);
|
||||||
} else {
|
} else {
|
||||||
callback(res.error);
|
callback(res.error);
|
||||||
}
|
}
|
||||||
}, 'json');
|
}, 'json');
|
||||||
// this.cache[model][id] = data;
|
// this.cache[model][id] = data;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.updateOrCreate = function (model, data, callback) {
|
WebService.prototype.updateOrCreate = function (model, data, callback) {
|
||||||
var mem = this;
|
var mem = this;
|
||||||
this.exists(model, data.id, function (err, exists) {
|
this.exists(model, data.id, function (err, exists) {
|
||||||
if (exists) {
|
if (exists) {
|
||||||
mem.save(model, data, callback);
|
mem.save(model, data, callback);
|
||||||
} else {
|
} else {
|
||||||
mem.create(model, data, function (err, id) {
|
mem.create(model, data, function (err, id) {
|
||||||
data.id = id;
|
data.id = id;
|
||||||
callback(err, data);
|
callback(err, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.save = function save(model, data, callback) {
|
WebService.prototype.save = function save(model, data, callback) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var req = this.getBlankReq();
|
var req = this.getBlankReq();
|
||||||
req._method = 'PUT';
|
req._method = 'PUT';
|
||||||
req[model] = this.preProcess(data);
|
req[model] = this.preProcess(data);
|
||||||
$.post(this.getResourceUrl(model) + '/' + data.id + '.json', req, function (res) {
|
$.post(this.getResourceUrl(model) + '/' + data.id + '.json', req, function (res) {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
_this.postProcess(model, res.data);
|
_this.postProcess(model, res.data);
|
||||||
callback(null, res.data);
|
callback(null, res.data);
|
||||||
} else {
|
} else {
|
||||||
callback(res.error);
|
callback(res.error);
|
||||||
}
|
}
|
||||||
}, 'json');
|
}, 'json');
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.exists = function exists(model, id, callback) {
|
WebService.prototype.exists = function exists(model, id, callback) {
|
||||||
$.getJSON(this.getResourceUrl(model) + '/' + id + '.json', function (res) {
|
$.getJSON(this.getResourceUrl(model) + '/' + id + '.json', function (res) {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
callback(null, true);
|
callback(null, true);
|
||||||
} else if (res.code === 404) {
|
} else if (res.code === 404) {
|
||||||
callback(null, false);
|
callback(null, false);
|
||||||
} else {
|
} else {
|
||||||
callback(res.error);
|
callback(res.error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.find = function find(model, id, callback) {
|
WebService.prototype.find = function find(model, id, callback) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
$.getJSON(this.getResourceUrl(model) + '/' + id + '.json', function (res) {
|
$.getJSON(this.getResourceUrl(model) + '/' + id + '.json', function (res) {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
_this.postProcess(model, res.data);
|
_this.postProcess(model, res.data);
|
||||||
callback(null, res.data);
|
callback(null, res.data);
|
||||||
} else {
|
} else {
|
||||||
callback(res.error);
|
callback(res.error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.destroy = function destroy(model, id, callback) {
|
WebService.prototype.destroy = function destroy(model, id, callback) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var req = this.getBlankReq();
|
var req = this.getBlankReq();
|
||||||
req._method = 'DELETE';
|
req._method = 'DELETE';
|
||||||
$.post(this.getResourceUrl(model) + '/' + id + '.json', req, function (res) {
|
$.post(this.getResourceUrl(model) + '/' + id + '.json', req, function (res) {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
//delete _this.cache[model][id];
|
//delete _this.cache[model][id];
|
||||||
callback(null, res.data);
|
callback(null, res.data);
|
||||||
} else {
|
} else {
|
||||||
callback(res.error);
|
callback(res.error);
|
||||||
}
|
}
|
||||||
}, 'json');
|
}, 'json');
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.all = function all(model, filter, callback) {
|
WebService.prototype.all = function all(model, filter, callback) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
$.getJSON(this.getResourceUrl(model) + '.json?query=' + encodeURIComponent(JSON.stringify(filter)), function (res) {
|
$.getJSON(this.getResourceUrl(model) + '.json?query=' + encodeURIComponent(JSON.stringify(filter)), function (res) {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
_this.postProcessMultiple(model, res.data);
|
_this.postProcessMultiple(model, res.data);
|
||||||
callback(null, res.data);
|
callback(null, res.data);
|
||||||
} else {
|
} else {
|
||||||
callback(res.error);
|
callback(res.error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.destroyAll = function destroyAll(model, callback) {
|
WebService.prototype.destroyAll = function destroyAll(model, callback) {
|
||||||
throw new Error('Not supported');
|
throw new Error('Not supported');
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.count = function count(model, callback, where) {
|
WebService.prototype.count = function count(model, callback, where) {
|
||||||
throw new Error('Not supported');
|
throw new Error('Not supported');
|
||||||
};
|
};
|
||||||
|
|
||||||
WebService.prototype.updateAttributes = function (model, id, data, callback) {
|
WebService.prototype.updateAttributes = function (model, id, data, callback) {
|
||||||
data.id = id;
|
data.id = id;
|
||||||
this.save(model, data, callback);
|
this.save(model, data, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,317 +10,317 @@ var utils = require('../utils');
|
||||||
* @param {Function} [callback] The callback function
|
* @param {Function} [callback] The callback function
|
||||||
*/
|
*/
|
||||||
exports.initialize = function initializeDataSource(dataSource, callback) {
|
exports.initialize = function initializeDataSource(dataSource, callback) {
|
||||||
dataSource.connector = new Memory();
|
dataSource.connector = new Memory();
|
||||||
dataSource.connector.connect(callback);
|
dataSource.connector.connect(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.Memory = Memory;
|
exports.Memory = Memory;
|
||||||
|
|
||||||
function Memory(m) {
|
function Memory(m) {
|
||||||
if (m) {
|
if (m) {
|
||||||
this.isTransaction = true;
|
this.isTransaction = true;
|
||||||
this.cache = m.cache;
|
this.cache = m.cache;
|
||||||
this.ids = m.ids;
|
this.ids = m.ids;
|
||||||
this.constructor.super_.call(this, 'memory');
|
this.constructor.super_.call(this, 'memory');
|
||||||
this._models = m._models;
|
this._models = m._models;
|
||||||
} else {
|
} else {
|
||||||
this.isTransaction = false;
|
this.isTransaction = false;
|
||||||
this.cache = {};
|
this.cache = {};
|
||||||
this.ids = {};
|
this.ids = {};
|
||||||
this.constructor.super_.call(this, 'memory');
|
this.constructor.super_.call(this, 'memory');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(Memory, Connector);
|
util.inherits(Memory, Connector);
|
||||||
|
|
||||||
Memory.prototype.connect = function(callback) {
|
Memory.prototype.connect = function (callback) {
|
||||||
if (this.isTransaction) {
|
if (this.isTransaction) {
|
||||||
this.onTransactionExec = callback;
|
this.onTransactionExec = callback;
|
||||||
} else {
|
} else {
|
||||||
process.nextTick(callback);
|
process.nextTick(callback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.define = function defineModel(definition) {
|
Memory.prototype.define = function defineModel(definition) {
|
||||||
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
|
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
|
||||||
var m = definition.model.modelName;
|
var m = definition.model.modelName;
|
||||||
this.cache[m] = {};
|
this.cache[m] = {};
|
||||||
this.ids[m] = 1;
|
this.ids[m] = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.create = function create(model, data, callback) {
|
Memory.prototype.create = function create(model, data, callback) {
|
||||||
// FIXME: [rfeng] We need to generate unique ids based on the id type
|
// FIXME: [rfeng] We need to generate unique ids based on the id type
|
||||||
// FIXME: [rfeng] We don't support composite ids yet
|
// FIXME: [rfeng] We don't support composite ids yet
|
||||||
var currentId = this.ids[model];
|
var currentId = this.ids[model];
|
||||||
if(currentId === undefined) {
|
if (currentId === undefined) {
|
||||||
// First time
|
// First time
|
||||||
this.ids[model] = 1;
|
this.ids[model] = 1;
|
||||||
currentId = 1;
|
currentId = 1;
|
||||||
}
|
}
|
||||||
var id = this.getIdValue(model, data) || currentId;
|
var id = this.getIdValue(model, data) || currentId;
|
||||||
if(id > currentId) {
|
if (id > currentId) {
|
||||||
// If the id is passed in and the value is greater than the current id
|
// If the id is passed in and the value is greater than the current id
|
||||||
currentId = id;
|
currentId = id;
|
||||||
}
|
}
|
||||||
this.ids[model] = Number(currentId) + 1;
|
this.ids[model] = Number(currentId) + 1;
|
||||||
|
|
||||||
var props = this._models[model].properties;
|
var props = this._models[model].properties;
|
||||||
var idName = this.idName(model);
|
var idName = this.idName(model);
|
||||||
id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
|
id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
|
||||||
this.setIdValue(model, data, id);
|
this.setIdValue(model, data, 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);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.updateOrCreate = function (model, data, callback) {
|
Memory.prototype.updateOrCreate = function (model, data, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.exists(model, self.getIdValue(model, data), function (err, exists) {
|
this.exists(model, self.getIdValue(model, data), function (err, exists) {
|
||||||
if (exists) {
|
if (exists) {
|
||||||
self.save(model, data, callback);
|
self.save(model, data, callback);
|
||||||
} else {
|
} else {
|
||||||
self.create(model, data, function (err, id) {
|
self.create(model, data, function (err, id) {
|
||||||
self.setIdValue(model, data, id);
|
self.setIdValue(model, data, id);
|
||||||
callback(err, data);
|
callback(err, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.save = function save(model, data, callback) {
|
Memory.prototype.save = function save(model, data, callback) {
|
||||||
this.cache[model][this.getIdValue(model, data)] = JSON.stringify(data);
|
this.cache[model][this.getIdValue(model, data)] = JSON.stringify(data);
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
callback(null, data);
|
callback(null, data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.exists = function exists(model, id, callback) {
|
Memory.prototype.exists = function exists(model, id, callback) {
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
callback(null, this.cache[model] && this.cache[model].hasOwnProperty(id));
|
callback(null, this.cache[model] && this.cache[model].hasOwnProperty(id));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.find = function find(model, id, callback) {
|
Memory.prototype.find = function find(model, id, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
|
callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.destroy = function destroy(model, id, callback) {
|
Memory.prototype.destroy = function destroy(model, id, callback) {
|
||||||
delete this.cache[model][id];
|
delete this.cache[model][id];
|
||||||
process.nextTick(callback);
|
process.nextTick(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.fromDb = function(model, data) {
|
Memory.prototype.fromDb = function (model, data) {
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
var props = this._models[model].properties;
|
var props = this._models[model].properties;
|
||||||
for(var key in data) {
|
for (var key in data) {
|
||||||
var val = data[key];
|
var val = data[key];
|
||||||
if (val === undefined || val === null) {
|
if (val === undefined || val === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
if (props[key]) {
|
|
||||||
switch(props[key].type.name) {
|
|
||||||
case 'Date':
|
|
||||||
val = new Date(val.toString().replace(/GMT.*$/, 'GMT'));
|
|
||||||
break;
|
|
||||||
case 'Boolean':
|
|
||||||
val = Boolean(val);
|
|
||||||
break;
|
|
||||||
case 'Number':
|
|
||||||
val = Number(val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data[key] = val;
|
|
||||||
}
|
}
|
||||||
return data;
|
if (props[key]) {
|
||||||
|
switch (props[key].type.name) {
|
||||||
|
case 'Date':
|
||||||
|
val = new Date(val.toString().replace(/GMT.*$/, 'GMT'));
|
||||||
|
break;
|
||||||
|
case 'Boolean':
|
||||||
|
val = Boolean(val);
|
||||||
|
break;
|
||||||
|
case 'Number':
|
||||||
|
val = Number(val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data[key] = val;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.all = function all(model, filter, callback) {
|
Memory.prototype.all = function all(model, filter, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var nodes = Object.keys(this.cache[model]).map(function (key) {
|
var nodes = Object.keys(this.cache[model]).map(function (key) {
|
||||||
return this.fromDb(model, this.cache[model][key]);
|
return this.fromDb(model, this.cache[model][key]);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
// do we need some sorting?
|
// do we need some sorting?
|
||||||
if (filter.order) {
|
if (filter.order) {
|
||||||
var orders = filter.order;
|
var orders = filter.order;
|
||||||
if (typeof filter.order === "string") {
|
if (typeof filter.order === "string") {
|
||||||
orders = [filter.order];
|
orders = [filter.order];
|
||||||
}
|
}
|
||||||
orders.forEach(function (key, i) {
|
orders.forEach(function (key, i) {
|
||||||
var reverse = 1;
|
var reverse = 1;
|
||||||
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].toLowerCase() === 'de') reverse = -1;
|
if (m[1].toLowerCase() === 'de') reverse = -1;
|
||||||
}
|
|
||||||
orders[i] = {"key": key, "reverse": reverse};
|
|
||||||
});
|
|
||||||
nodes = nodes.sort(sorting.bind(orders));
|
|
||||||
}
|
}
|
||||||
|
orders[i] = {"key": key, "reverse": reverse};
|
||||||
var nearFilter = geo.nearFilter(filter.where);
|
});
|
||||||
|
nodes = nodes.sort(sorting.bind(orders));
|
||||||
// geo sorting
|
|
||||||
if(nearFilter) {
|
|
||||||
nodes = geo.filter(nodes, nearFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// do we need some filtration?
|
|
||||||
if (filter.where) {
|
|
||||||
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// field selection
|
|
||||||
if(filter.fields) {
|
|
||||||
nodes = nodes.map(utils.selectFields(filter.fields));
|
|
||||||
}
|
|
||||||
|
|
||||||
// limit/skip
|
|
||||||
filter.skip = filter.skip || 0;
|
|
||||||
filter.limit = filter.limit || nodes.length;
|
|
||||||
nodes = nodes.slice(filter.skip, filter.skip + filter.limit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process.nextTick(function () {
|
var nearFilter = geo.nearFilter(filter.where);
|
||||||
if (filter && filter.include) {
|
|
||||||
self._models[model].model.include(nodes, filter.include, callback);
|
|
||||||
} else {
|
|
||||||
callback(null, nodes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function sorting(a, b) {
|
// geo sorting
|
||||||
for (var i=0, l=this.length; i<l; i++) {
|
if (nearFilter) {
|
||||||
if (a[this[i].key] > b[this[i].key]) {
|
nodes = geo.filter(nodes, nearFilter);
|
||||||
return 1*this[i].reverse;
|
|
||||||
} else if (a[this[i].key] < b[this[i].key]) {
|
|
||||||
return -1*this[i].reverse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do we need some filtration?
|
||||||
|
if (filter.where) {
|
||||||
|
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// field selection
|
||||||
|
if (filter.fields) {
|
||||||
|
nodes = nodes.map(utils.selectFields(filter.fields));
|
||||||
|
}
|
||||||
|
|
||||||
|
// limit/skip
|
||||||
|
filter.skip = filter.skip || 0;
|
||||||
|
filter.limit = filter.limit || nodes.length;
|
||||||
|
nodes = nodes.slice(filter.skip, filter.skip + filter.limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.nextTick(function () {
|
||||||
|
if (filter && filter.include) {
|
||||||
|
self._models[model].model.include(nodes, filter.include, callback);
|
||||||
|
} else {
|
||||||
|
callback(null, nodes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function sorting(a, b) {
|
||||||
|
for (var i = 0, l = this.length; i < l; i++) {
|
||||||
|
if (a[this[i].key] > b[this[i].key]) {
|
||||||
|
return 1 * this[i].reverse;
|
||||||
|
} else if (a[this[i].key] < b[this[i].key]) {
|
||||||
|
return -1 * this[i].reverse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function applyFilter(filter) {
|
function applyFilter(filter) {
|
||||||
if (typeof filter.where === 'function') {
|
if (typeof filter.where === 'function') {
|
||||||
return filter.where;
|
return filter.where;
|
||||||
}
|
}
|
||||||
var keys = Object.keys(filter.where);
|
var keys = Object.keys(filter.where);
|
||||||
return function (obj) {
|
return function (obj) {
|
||||||
var pass = true;
|
var pass = true;
|
||||||
keys.forEach(function (key) {
|
keys.forEach(function (key) {
|
||||||
if (!test(filter.where[key], obj && obj[key])) {
|
if (!test(filter.where[key], obj && obj[key])) {
|
||||||
pass = false;
|
pass = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
function test(example, value) {
|
function test(example, value) {
|
||||||
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
||||||
return value.match(example);
|
return value.match(example);
|
||||||
|
}
|
||||||
|
if (typeof example === 'undefined') return undefined;
|
||||||
|
if (typeof value === 'undefined') return undefined;
|
||||||
|
if (typeof example === 'object') {
|
||||||
|
// ignore geo near filter
|
||||||
|
if (example.near) return true;
|
||||||
|
|
||||||
|
if (example.inq) {
|
||||||
|
if (!value) return false;
|
||||||
|
for (var i = 0; i < example.inq.length; i += 1) {
|
||||||
|
if (example.inq[i] == value) return true;
|
||||||
}
|
}
|
||||||
if (typeof example === 'undefined') return undefined;
|
return false;
|
||||||
if (typeof value === 'undefined') return undefined;
|
}
|
||||||
if (typeof example === 'object') {
|
|
||||||
// ignore geo near filter
|
|
||||||
if(example.near) return true;
|
|
||||||
|
|
||||||
if (example.inq) {
|
if (isNum(example.gt) && example.gt < value) return true;
|
||||||
if (!value) return false;
|
if (isNum(example.gte) && example.gte <= value) return true;
|
||||||
for (var i = 0; i < example.inq.length; i += 1) {
|
if (isNum(example.lt) && example.lt > value) return true;
|
||||||
if (example.inq[i] == value) return true;
|
if (isNum(example.lte) && example.lte >= value) return true;
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isNum(example.gt) && example.gt < value) return true;
|
|
||||||
if(isNum(example.gte) && example.gte <= value) return true;
|
|
||||||
if(isNum(example.lt) && example.lt > value) return true;
|
|
||||||
if(isNum(example.lte) && example.lte >= value) return true;
|
|
||||||
}
|
|
||||||
// not strict equality
|
|
||||||
return (example !== null ? example.toString() : example) == (value !== null ? value.toString() : value);
|
|
||||||
}
|
}
|
||||||
|
// not strict equality
|
||||||
|
return (example !== null ? example.toString() : example) == (value !== null ? value.toString() : value);
|
||||||
|
}
|
||||||
|
|
||||||
function isNum(n) {
|
function isNum(n) {
|
||||||
return typeof n === 'number';
|
return typeof n === 'number';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Memory.prototype.destroyAll = function destroyAll(model, where, callback) {
|
Memory.prototype.destroyAll = function destroyAll(model, where, callback) {
|
||||||
if(!callback && 'function' === typeof where) {
|
if (!callback && 'function' === typeof where) {
|
||||||
callback = where;
|
callback = where;
|
||||||
where = undefined;
|
where = undefined;
|
||||||
|
}
|
||||||
|
var cache = this.cache[model];
|
||||||
|
var filter = null;
|
||||||
|
if (where) {
|
||||||
|
filter = applyFilter({where: where});
|
||||||
|
}
|
||||||
|
Object.keys(cache).forEach(function (id) {
|
||||||
|
if (!filter || filter(this.fromDb(model, cache[id]))) {
|
||||||
|
delete cache[id];
|
||||||
}
|
}
|
||||||
var cache = this.cache[model];
|
}.bind(this));
|
||||||
var filter = null;
|
if (!where) {
|
||||||
if (where) {
|
this.cache[model] = {};
|
||||||
filter = applyFilter({where: where});
|
}
|
||||||
}
|
process.nextTick(callback);
|
||||||
Object.keys(cache).forEach(function (id) {
|
|
||||||
if(!filter || filter(this.fromDb(model, cache[id]))) {
|
|
||||||
delete cache[id];
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
if(!where) {
|
|
||||||
this.cache[model] = {};
|
|
||||||
}
|
|
||||||
process.nextTick(callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.count = function count(model, callback, where) {
|
Memory.prototype.count = function count(model, callback, where) {
|
||||||
var cache = this.cache[model];
|
var cache = this.cache[model];
|
||||||
var data = Object.keys(cache);
|
var data = Object.keys(cache);
|
||||||
if (where) {
|
if (where) {
|
||||||
var filter = {where: where};
|
var filter = {where: where};
|
||||||
data = data.map(function (id) {
|
data = data.map(function (id) {
|
||||||
return this.fromDb(model, cache[id]);
|
return this.fromDb(model, cache[id]);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
data = data.filter(applyFilter(filter));
|
data = data.filter(applyFilter(filter));
|
||||||
}
|
}
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
callback(null, data.length);
|
callback(null, data.length);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
|
Memory.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
|
||||||
if(!id) {
|
if (!id) {
|
||||||
var err = new Error('You must provide an id when updating attributes!');
|
var err = new Error('You must provide an id when updating attributes!');
|
||||||
if(cb) {
|
if (cb) {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setIdValue(model, data, id);
|
|
||||||
|
|
||||||
var cachedModels = this.cache[model];
|
|
||||||
var modelAsString = cachedModels && this.cache[model][id];
|
|
||||||
var modelData = modelAsString && JSON.parse(modelAsString);
|
|
||||||
|
|
||||||
if(modelData) {
|
|
||||||
this.save(model, merge(modelData, data), cb);
|
|
||||||
} else {
|
} else {
|
||||||
cb(new Error('Could not update attributes. Object with id ' + id + ' does not exist!'));
|
throw err;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setIdValue(model, data, id);
|
||||||
|
|
||||||
|
var cachedModels = this.cache[model];
|
||||||
|
var modelAsString = cachedModels && this.cache[model][id];
|
||||||
|
var modelData = modelAsString && JSON.parse(modelAsString);
|
||||||
|
|
||||||
|
if (modelData) {
|
||||||
|
this.save(model, merge(modelData, data), cb);
|
||||||
|
} else {
|
||||||
|
cb(new Error('Could not update attributes. Object with id ' + id + ' does not exist!'));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.transaction = function () {
|
Memory.prototype.transaction = function () {
|
||||||
return new Memory(this);
|
return new Memory(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.exec = function(callback) {
|
Memory.prototype.exec = function (callback) {
|
||||||
this.onTransactionExec();
|
this.onTransactionExec();
|
||||||
setTimeout(callback, 50);
|
setTimeout(callback, 50);
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.buildNearFilter = function (filter) {
|
Memory.prototype.buildNearFilter = function (filter) {
|
||||||
|
@ -328,9 +328,9 @@ Memory.prototype.buildNearFilter = function (filter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
base[key] = update[key];
|
base[key] = update[key];
|
||||||
});
|
});
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
|
@ -6,367 +6,368 @@ var safeRequire = require('../utils').safeRequire;
|
||||||
var neo4j = safeRequire('neo4j');
|
var neo4j = safeRequire('neo4j');
|
||||||
|
|
||||||
exports.initialize = function initializeSchema(dataSource, callback) {
|
exports.initialize = function initializeSchema(dataSource, callback) {
|
||||||
dataSource.client = new neo4j.GraphDatabase(dataSource.settings.url);
|
dataSource.client = new neo4j.GraphDatabase(dataSource.settings.url);
|
||||||
dataSource.connector = new Neo4j(dataSource.client);
|
dataSource.connector = new Neo4j(dataSource.client);
|
||||||
process.nextTick(callback);
|
process.nextTick(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
function Neo4j(client) {
|
function Neo4j(client) {
|
||||||
this._models = {};
|
this._models = {};
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.cache = {};
|
this.cache = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Neo4j.prototype.define = function defineModel(descr) {
|
Neo4j.prototype.define = function defineModel(descr) {
|
||||||
this.mixClassMethods(descr.model, descr.properties);
|
this.mixClassMethods(descr.model, descr.properties);
|
||||||
this.mixInstanceMethods(descr.model.prototype, descr.properties);
|
this.mixInstanceMethods(descr.model.prototype, descr.properties);
|
||||||
this._models[descr.model.modelName] = descr;
|
this._models[descr.model.modelName] = descr;
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.createIndexHelper = function (cls, indexName) {
|
Neo4j.prototype.createIndexHelper = function (cls, indexName) {
|
||||||
var db = this.client;
|
var db = this.client;
|
||||||
var method = 'findBy' + indexName[0].toUpperCase() + indexName.substr(1);
|
var method = 'findBy' + indexName[0].toUpperCase() + indexName.substr(1);
|
||||||
cls[method] = function (value, cb) {
|
cls[method] = function (value, cb) {
|
||||||
db.getIndexedNode(cls.modelName, indexName, value, function (err, node) {
|
db.getIndexedNode(cls.modelName, indexName, value, function (err, node) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
if (node) {
|
if (node) {
|
||||||
node.data.id = node.id;
|
node.data.id = node.id;
|
||||||
cb(null, new cls(node.data));
|
cb(null, new cls(node.data));
|
||||||
} else {
|
} else {
|
||||||
cb(null, null);
|
cb(null, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.mixClassMethods = function mixClassMethods(cls, properties) {
|
Neo4j.prototype.mixClassMethods = function mixClassMethods(cls, properties) {
|
||||||
var neo = this;
|
var neo = this;
|
||||||
|
|
||||||
Object.keys(properties).forEach(function (name) {
|
Object.keys(properties).forEach(function (name) {
|
||||||
if (properties[name].index) {
|
if (properties[name].index) {
|
||||||
neo.createIndexHelper(cls, name);
|
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.setupCypherQuery = function (name, queryStr, rowHandler) {
|
cls.createRelationshipTo = function createRelationshipTo(id1, id2, type, data, cb) {
|
||||||
cls[name] = function cypherQuery(params, cb) {
|
var fromNode, toNode;
|
||||||
if (typeof params === 'function') {
|
neo.node(id1, function (err, node) {
|
||||||
cb = params;
|
if (err && cb) return cb(err);
|
||||||
params = [];
|
if (err && !cb) throw err;
|
||||||
} else if (params.constructor.name !== 'Array') {
|
fromNode = node;
|
||||||
params = [params];
|
ok();
|
||||||
}
|
});
|
||||||
|
neo.node(id2, function (err, node) {
|
||||||
var i = 0;
|
if (err && cb) return cb(err);
|
||||||
var q = queryStr.replace(/\?/g, function () {
|
if (err && !cb) throw err;
|
||||||
return params[i++];
|
toNode = node;
|
||||||
});
|
ok();
|
||||||
|
});
|
||||||
neo.client.query(function (err, result) {
|
function ok() {
|
||||||
if (err) return cb(err, []);
|
if (fromNode && toNode) {
|
||||||
cb(null, result.map(rowHandler));
|
fromNode.createRelationshipTo(toNode, type, cleanup(data), cb);
|
||||||
}, 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.createRelationshipFrom = function createRelationshipFrom(id1, id2, type, data, cb) {
|
||||||
cls.ensureRelationshipTo = function (id1, id2, type, data, cb) {
|
cls.createRelationshipTo(id2, id1, type, data, cb);
|
||||||
cls.relationshipExists(id1, id2, type, 'outgoing', function (err, exists) {
|
}
|
||||||
if (err && cb) return cb(err);
|
|
||||||
if (err && !cb) throw err;
|
// only create relationship if it is not exists
|
||||||
if (exists) return cb && cb(null);
|
cls.ensureRelationshipTo = function (id1, id2, type, data, cb) {
|
||||||
cls.createRelationshipTo(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) {
|
Neo4j.prototype.mixInstanceMethods = function mixInstanceMethods(proto) {
|
||||||
var neo = this;
|
var neo = this;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param obj - Object or id of object to check relation with
|
* @param obj - Object or id of object to check relation with
|
||||||
* @param type - type of relation
|
* @param type - type of relation
|
||||||
* @param cb - callback (err, rel || false)
|
* @param cb - callback (err, rel || false)
|
||||||
*/
|
*/
|
||||||
proto.isInRelationWith = function isInRelationWith(obj, type, direction, cb) {
|
proto.isInRelationWith = function isInRelationWith(obj, type, direction, cb) {
|
||||||
this.constructor.relationshipExists(this.id, obj.id || obj, type, 'all', cb);
|
this.constructor.relationshipExists(this.id, obj.id || obj, type, 'all', cb);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.node = function find(id, callback) {
|
Neo4j.prototype.node = function find(id, callback) {
|
||||||
if (this.cache[id]) {
|
if (this.cache[id]) {
|
||||||
callback(null, this.cache[id]);
|
callback(null, this.cache[id]);
|
||||||
} else {
|
} else {
|
||||||
this.client.getNodeById(id, function (err, node) {
|
this.client.getNodeById(id, function (err, node) {
|
||||||
if (node) {
|
if (node) {
|
||||||
this.cache[id] = node;
|
this.cache[id] = node;
|
||||||
}
|
}
|
||||||
callback(err, node);
|
callback(err, node);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.create = function create(model, data, callback) {
|
Neo4j.prototype.create = function create(model, data, callback) {
|
||||||
data.nodeType = model;
|
data.nodeType = model;
|
||||||
var node = this.client.createNode();
|
var node = this.client.createNode();
|
||||||
node.data = cleanup(data);
|
node.data = cleanup(data);
|
||||||
node.data.nodeType = model;
|
node.data.nodeType = model;
|
||||||
node.save(function (err) {
|
node.save(function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
this.cache[node.id] = node;
|
this.cache[node.id] = node;
|
||||||
node.index(model, 'id', node.id, function (err) {
|
node.index(model, 'id', node.id, function (err) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
this.updateIndexes(model, node, function (err) {
|
this.updateIndexes(model, node, function (err) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
callback(null, node.id);
|
callback(null, node.id);
|
||||||
});
|
});
|
||||||
}.bind(this));
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.updateIndexes = function updateIndexes(model, node, cb) {
|
Neo4j.prototype.updateIndexes = function updateIndexes(model, node, cb) {
|
||||||
var props = this._models[model].properties;
|
var props = this._models[model].properties;
|
||||||
var wait = 1;
|
var wait = 1;
|
||||||
Object.keys(props).forEach(function (key) {
|
Object.keys(props).forEach(function (key) {
|
||||||
if (props[key].index && node.data[key]) {
|
if (props[key].index && node.data[key]) {
|
||||||
wait += 1;
|
wait += 1;
|
||||||
node.index(model, key, node.data[key], done);
|
node.index(model, key, node.data[key], done);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
done();
|
|
||||||
|
|
||||||
var error = false;
|
|
||||||
function done(err) {
|
|
||||||
error = error || err;
|
|
||||||
if (--wait === 0) {
|
|
||||||
cb(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
done();
|
||||||
|
|
||||||
|
var error = false;
|
||||||
|
|
||||||
|
function done(err) {
|
||||||
|
error = error || err;
|
||||||
|
if (--wait === 0) {
|
||||||
|
cb(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.save = function save(model, data, callback) {
|
Neo4j.prototype.save = function save(model, data, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.node(data.id, function (err, node) {
|
this.node(data.id, function (err, node) {
|
||||||
//delete id property since that's redundant and we use the node.id
|
//delete id property since that's redundant and we use the node.id
|
||||||
delete data.id;
|
delete data.id;
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
node.data = cleanup(data);
|
node.data = cleanup(data);
|
||||||
node.save(function (err) {
|
node.save(function (err) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
self.updateIndexes(model, node, function (err) {
|
self.updateIndexes(model, node, function (err) {
|
||||||
if (err) return console.log(err);
|
if (err) return console.log(err);
|
||||||
//map node id to the id property being sent back
|
//map node id to the id property being sent back
|
||||||
node.data.id = node.id;
|
node.data.id = node.id;
|
||||||
callback(null, node.data);
|
callback(null, node.data);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.exists = function exists(model, id, callback) {
|
Neo4j.prototype.exists = function exists(model, id, callback) {
|
||||||
delete this.cache[id];
|
delete this.cache[id];
|
||||||
this.node(id, callback);
|
this.node(id, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.find = function find(model, id, callback) {
|
Neo4j.prototype.find = function find(model, id, callback) {
|
||||||
delete this.cache[id];
|
delete this.cache[id];
|
||||||
this.node(id, function (err, node) {
|
this.node(id, function (err, node) {
|
||||||
if (node && node.data) {
|
if (node && node.data) {
|
||||||
node.data.id = id;
|
node.data.id = id;
|
||||||
}
|
}
|
||||||
callback(err, this.readFromDb(model, node && node.data));
|
callback(err, this.readFromDb(model, node && node.data));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.readFromDb = function readFromDb(model, data) {
|
Neo4j.prototype.readFromDb = function readFromDb(model, data) {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
var res = {};
|
var res = {};
|
||||||
var props = this._models[model].properties;
|
var props = this._models[model].properties;
|
||||||
Object.keys(data).forEach(function (key) {
|
Object.keys(data).forEach(function (key) {
|
||||||
if (props[key] && props[key].type.name === 'Date') {
|
if (props[key] && props[key].type.name === 'Date') {
|
||||||
res[key] = new Date(data[key]);
|
res[key] = new Date(data[key]);
|
||||||
} else {
|
} else {
|
||||||
res[key] = data[key];
|
res[key] = data[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.destroy = function destroy(model, id, callback) {
|
Neo4j.prototype.destroy = function destroy(model, id, callback) {
|
||||||
var force = true;
|
var force = true;
|
||||||
this.node(id, function (err, node) {
|
this.node(id, function (err, node) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
node.delete(function (err) {
|
node.delete(function (err) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
delete this.cache[id];
|
delete this.cache[id];
|
||||||
}.bind(this), force);
|
}.bind(this), force);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.all = function all(model, filter, callback) {
|
Neo4j.prototype.all = function all(model, filter, callback) {
|
||||||
this.client.queryNodeIndex(model, 'id:*', function (err, nodes) {
|
this.client.queryNodeIndex(model, 'id:*', function (err, nodes) {
|
||||||
if (nodes) {
|
if (nodes) {
|
||||||
nodes = nodes.map(function (obj) {
|
nodes = nodes.map(function (obj) {
|
||||||
obj.data.id = obj.id;
|
obj.data.id = obj.id;
|
||||||
return this.readFromDb(model, obj.data);
|
return this.readFromDb(model, obj.data);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
if (filter) {
|
if (filter) {
|
||||||
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
|
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
|
||||||
if (filter.order) {
|
if (filter.order) {
|
||||||
var key = filter.order.split(' ')[0];
|
var key = filter.order.split(' ')[0];
|
||||||
var dir = filter.order.split(' ')[1];
|
var dir = filter.order.split(' ')[1];
|
||||||
nodes = nodes.sort(function (a, b) {
|
nodes = nodes.sort(function (a, b) {
|
||||||
return a[key] > b[key];
|
return a[key] > b[key];
|
||||||
});
|
});
|
||||||
if (dir === 'DESC') nodes = nodes.reverse();
|
if (dir === 'DESC') nodes = nodes.reverse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callback(err, nodes);
|
callback(err, nodes);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.allNodes = function all(model, callback) {
|
Neo4j.prototype.allNodes = function all(model, callback) {
|
||||||
this.client.queryNodeIndex(model, 'id:*', function (err, nodes) {
|
this.client.queryNodeIndex(model, 'id:*', function (err, nodes) {
|
||||||
callback(err, nodes);
|
callback(err, nodes);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function applyFilter(filter) {
|
function applyFilter(filter) {
|
||||||
if (typeof filter.where === 'function') {
|
if (typeof filter.where === 'function') {
|
||||||
return filter.where;
|
return filter.where;
|
||||||
}
|
}
|
||||||
var keys = Object.keys(filter.where || {});
|
var keys = Object.keys(filter.where || {});
|
||||||
return function (obj) {
|
return function (obj) {
|
||||||
var pass = true;
|
var pass = true;
|
||||||
keys.forEach(function (key) {
|
keys.forEach(function (key) {
|
||||||
if (!test(filter.where[key], obj[key])) {
|
if (!test(filter.where[key], obj[key])) {
|
||||||
pass = false;
|
pass = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
function test(example, value) {
|
function test(example, value) {
|
||||||
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
||||||
return value.match(example);
|
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;
|
|
||||||
}
|
}
|
||||||
|
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) {
|
Neo4j.prototype.destroyAll = function destroyAll(model, callback) {
|
||||||
var wait, error = null;
|
var wait, error = null;
|
||||||
this.allNodes(model, function (err, collection) {
|
this.allNodes(model, function (err, collection) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
wait = collection.length;
|
wait = collection.length;
|
||||||
collection && collection.forEach && collection.forEach(function (node) {
|
collection && collection.forEach && collection.forEach(function (node) {
|
||||||
node.delete(done, true);
|
node.delete(done, true);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function done(err) {
|
function done(err) {
|
||||||
error = error || err;
|
error = error || err;
|
||||||
if (--wait === 0) {
|
if (--wait === 0) {
|
||||||
callback(error);
|
callback(error);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.count = function count(model, callback, conds) {
|
Neo4j.prototype.count = function count(model, callback, conds) {
|
||||||
this.all(model, {where: conds}, function (err, collection) {
|
this.all(model, {where: conds}, function (err, collection) {
|
||||||
callback(err, collection ? collection.length : 0);
|
callback(err, collection ? collection.length : 0);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Neo4j.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
|
Neo4j.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
|
||||||
data.id = id;
|
data.id = id;
|
||||||
this.node(id, function (err, node) {
|
this.node(id, function (err, node) {
|
||||||
this.save(model, merge(node.data, data), cb);
|
this.save(model, merge(node.data, data), cb);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
function cleanup(data) {
|
function cleanup(data) {
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
|
|
||||||
var res = {};
|
var res = {};
|
||||||
Object.keys(data).forEach(function (key) {
|
Object.keys(data).forEach(function (key) {
|
||||||
var v = data[key];
|
var v = data[key];
|
||||||
if (v === null) {
|
if (v === null) {
|
||||||
// skip
|
// skip
|
||||||
// console.log('skip null', key);
|
// console.log('skip null', key);
|
||||||
} else if (v && v.constructor.name === 'Array' && v.length === 0) {
|
} else if (v && v.constructor.name === 'Array' && v.length === 0) {
|
||||||
// skip
|
// skip
|
||||||
// console.log('skip blank array', key);
|
// console.log('skip blank array', key);
|
||||||
} else if (typeof v !== 'undefined') {
|
} else if (typeof v !== 'undefined') {
|
||||||
res[key] = v;
|
res[key] = v;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function merge(base, update) {
|
function merge(base, update) {
|
||||||
Object.keys(update).forEach(function (key) {
|
Object.keys(update).forEach(function (key) {
|
||||||
base[key] = update[key];
|
base[key] = update[key];
|
||||||
});
|
});
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,104 +7,104 @@ var uuid = require('node-uuid');
|
||||||
var riak = safeRequire('riak-js');
|
var riak = safeRequire('riak-js');
|
||||||
|
|
||||||
exports.initialize = function initializeSchema(dataSource, callback) {
|
exports.initialize = function initializeSchema(dataSource, callback) {
|
||||||
dataSource.client = riak.getClient({
|
dataSource.client = riak.getClient({
|
||||||
host: dataSource.settings.host || '127.0.0.1',
|
host: dataSource.settings.host || '127.0.0.1',
|
||||||
port: dataSource.settings.port || 8091
|
port: dataSource.settings.port || 8091
|
||||||
});
|
});
|
||||||
dataSource.connector = new Riak(dataSource.client);
|
dataSource.connector = new Riak(dataSource.client);
|
||||||
};
|
};
|
||||||
|
|
||||||
function Riak(client) {
|
function Riak(client) {
|
||||||
this._models = {};
|
this._models = {};
|
||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
Riak.prototype.define = function (descr) {
|
Riak.prototype.define = function (descr) {
|
||||||
this._models[descr.model.modelName] = descr;
|
this._models[descr.model.modelName] = descr;
|
||||||
};
|
};
|
||||||
|
|
||||||
Riak.prototype.save = function (model, data, callback) {
|
Riak.prototype.save = function (model, data, callback) {
|
||||||
this.client.save(model, data.id, data, callback);
|
this.client.save(model, data.id, data, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Riak.prototype.create = function (model, data, callback) {
|
Riak.prototype.create = function (model, data, callback) {
|
||||||
data.id = uuid();
|
data.id = uuid();
|
||||||
this.save(model, data, function (err) {
|
this.save(model, data, function (err) {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(err, data.id);
|
callback(err, data.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Riak.prototype.exists = function (model, id, callback) {
|
Riak.prototype.exists = function (model, id, callback) {
|
||||||
this.client.exists(model, id, function (err, exists, meta) {
|
this.client.exists(model, id, function (err, exists, meta) {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(err, exists);
|
callback(err, exists);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Riak.prototype.find = function find(model, id, callback) {
|
Riak.prototype.find = function find(model, id, callback) {
|
||||||
this.client.get(model, id, function (err, data, meta) {
|
this.client.get(model, id, function (err, data, meta) {
|
||||||
if (data && data.id) {
|
if (data && data.id) {
|
||||||
data.id = id;
|
data.id = id;
|
||||||
} else {
|
} else {
|
||||||
data = null;
|
data = null;
|
||||||
}
|
}
|
||||||
if (typeof callback === 'function') callback(err, data);
|
if (typeof callback === 'function') callback(err, data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Riak.prototype.destroy = function destroy(model, id, callback) {
|
Riak.prototype.destroy = function destroy(model, id, callback) {
|
||||||
this.client.remove(model, id, function (err) {
|
this.client.remove(model, id, function (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Riak.prototype.all = function all(model, filter, callback) {
|
Riak.prototype.all = function all(model, filter, callback) {
|
||||||
var opts = {};
|
var opts = {};
|
||||||
if (filter && filter.where) opts.where = filter.where;
|
if (filter && filter.where) opts.where = filter.where;
|
||||||
this.client.getAll(model, function (err, result, meta) {
|
this.client.getAll(model, function (err, result, meta) {
|
||||||
if (err) return callback(err, []);
|
if (err) return callback(err, []);
|
||||||
/// return callback(err, result.map(function (x) { return {id: x}; }));
|
/// return callback(err, result.map(function (x) { return {id: x}; }));
|
||||||
result = (result || []).map(function (row) {
|
result = (result || []).map(function (row) {
|
||||||
var record = row.data;
|
var record = row.data;
|
||||||
record.id = row.meta.key;
|
record.id = row.meta.key;
|
||||||
console.log(record);
|
console.log(record);
|
||||||
return record;
|
return record;
|
||||||
});
|
});
|
||||||
|
|
||||||
return callback(err, result);
|
return callback(err, result);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
Riak.prototype.destroyAll = function destroyAll(model, callback) {
|
Riak.prototype.destroyAll = function destroyAll(model, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.all(model, {}, function (err, recs) {
|
this.all(model, {}, function (err, recs) {
|
||||||
if (err) callback(err);
|
if (err) callback(err);
|
||||||
|
|
||||||
removeOne();
|
removeOne();
|
||||||
|
|
||||||
function removeOne(error) {
|
function removeOne(error) {
|
||||||
err = err || error;
|
err = err || error;
|
||||||
var rec = recs.pop();
|
var rec = recs.pop();
|
||||||
if (!rec) return callback(err && err.statusCode != '404' ? err : null);
|
if (!rec) return callback(err && err.statusCode != '404' ? err : null);
|
||||||
console.log(rec.id);
|
console.log(rec.id);
|
||||||
self.client.remove(model, rec.id, removeOne);
|
self.client.remove(model, rec.id, removeOne);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Riak.prototype.count = function count(model, callback) {
|
Riak.prototype.count = function count(model, callback) {
|
||||||
this.client.keys(model + ':*', function (err, keys) {
|
this.client.keys(model + ':*', function (err, keys) {
|
||||||
callback(err, err ? null : keys.length);
|
callback(err, err ? null : keys.length);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Riak.prototype.updateAttributes = function updateAttrs(model, id, data, cb) {
|
Riak.prototype.updateAttributes = function updateAttrs(model, id, data, cb) {
|
||||||
data.id = id;
|
data.id = id;
|
||||||
this.save(model, data, cb);
|
this.save(model, data, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
1120
lib/dao.js
1120
lib/dao.js
File diff suppressed because it is too large
Load Diff
1776
lib/datasource.js
1776
lib/datasource.js
File diff suppressed because it is too large
Load Diff
38
lib/geo.js
38
lib/geo.js
|
@ -11,11 +11,11 @@ var assert = require('assert');
|
||||||
exports.nearFilter = function nearFilter(where) {
|
exports.nearFilter = function nearFilter(where) {
|
||||||
var result = false;
|
var result = false;
|
||||||
|
|
||||||
if(where && typeof where === 'object') {
|
if (where && typeof where === 'object') {
|
||||||
Object.keys(where).forEach(function (key) {
|
Object.keys(where).forEach(function (key) {
|
||||||
var ex = where[key];
|
var ex = where[key];
|
||||||
|
|
||||||
if(ex && ex.near) {
|
if (ex && ex.near) {
|
||||||
result = {
|
result = {
|
||||||
near: ex.near,
|
near: ex.near,
|
||||||
maxDistance: ex.maxDistance,
|
maxDistance: ex.maxDistance,
|
||||||
|
@ -45,18 +45,18 @@ exports.filter = function (arr, filter) {
|
||||||
var loc = obj[key];
|
var loc = obj[key];
|
||||||
|
|
||||||
// filter out objects without locations
|
// filter out objects without locations
|
||||||
if(!loc) return;
|
if (!loc) return;
|
||||||
|
|
||||||
if(!(loc instanceof GeoPoint)) {
|
if (!(loc instanceof GeoPoint)) {
|
||||||
loc = GeoPoint(loc);
|
loc = GeoPoint(loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof loc.lat !== 'number') return;
|
if (typeof loc.lat !== 'number') return;
|
||||||
if(typeof loc.lng !== 'number') return;
|
if (typeof loc.lng !== 'number') return;
|
||||||
|
|
||||||
var d = GeoPoint.distanceBetween(origin, loc);
|
var d = GeoPoint.distanceBetween(origin, loc);
|
||||||
|
|
||||||
if(max && d > max) {
|
if (max && d > max) {
|
||||||
// dont add
|
// dont add
|
||||||
} else {
|
} else {
|
||||||
distances[obj.id] = d;
|
distances[obj.id] = d;
|
||||||
|
@ -68,11 +68,11 @@ exports.filter = function (arr, filter) {
|
||||||
var a = objB[key];
|
var a = objB[key];
|
||||||
var b = objB[key];
|
var b = objB[key];
|
||||||
|
|
||||||
if(a && b) {
|
if (a && b) {
|
||||||
var da = distances[objA.id];
|
var da = distances[objA.id];
|
||||||
var db = distances[objB.id];
|
var db = distances[objB.id];
|
||||||
|
|
||||||
if(db === da) return 0;
|
if (db === da) return 0;
|
||||||
return da > db ? 1 : -1;
|
return da > db ? 1 : -1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -87,15 +87,15 @@ exports.filter = function (arr, filter) {
|
||||||
exports.GeoPoint = GeoPoint;
|
exports.GeoPoint = GeoPoint;
|
||||||
|
|
||||||
function GeoPoint(data) {
|
function GeoPoint(data) {
|
||||||
if(!(this instanceof GeoPoint)) {
|
if (!(this instanceof GeoPoint)) {
|
||||||
return new GeoPoint(data);
|
return new GeoPoint(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof data === 'string') {
|
if (typeof data === 'string') {
|
||||||
data = data.split(/,\s*/);
|
data = data.split(/,\s*/);
|
||||||
assert(data.length === 2, 'must provide a string "lng,lat" creating a GeoPoint with a string');
|
assert(data.length === 2, 'must provide a string "lng,lat" creating a GeoPoint with a string');
|
||||||
}
|
}
|
||||||
if(Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
data = {
|
data = {
|
||||||
lng: Number(data[0]),
|
lng: Number(data[0]),
|
||||||
lat: Number(data[1])
|
lat: Number(data[1])
|
||||||
|
@ -122,10 +122,10 @@ function GeoPoint(data) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
GeoPoint.distanceBetween = function distanceBetween(a, b, options) {
|
GeoPoint.distanceBetween = function distanceBetween(a, b, options) {
|
||||||
if(!(a instanceof GeoPoint)) {
|
if (!(a instanceof GeoPoint)) {
|
||||||
a = GeoPoint(a);
|
a = GeoPoint(a);
|
||||||
}
|
}
|
||||||
if(!(b instanceof GeoPoint)) {
|
if (!(b instanceof GeoPoint)) {
|
||||||
b = GeoPoint(b);
|
b = GeoPoint(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ GeoPoint.prototype.toString = function () {
|
||||||
var PI = 3.1415926535897932384626433832795;
|
var PI = 3.1415926535897932384626433832795;
|
||||||
|
|
||||||
// factor to convert decimal degrees to radians
|
// factor to convert decimal degrees to radians
|
||||||
var DEG2RAD = 0.01745329252;
|
var DEG2RAD = 0.01745329252;
|
||||||
|
|
||||||
// factor to convert decimal degrees to radians
|
// factor to convert decimal degrees to radians
|
||||||
var RAD2DEG = 57.29577951308;
|
var RAD2DEG = 57.29577951308;
|
||||||
|
@ -184,12 +184,12 @@ function geoDistance(x1, y1, x2, y2, options) {
|
||||||
x2 = x2 * DEG2RAD;
|
x2 = x2 * DEG2RAD;
|
||||||
y2 = y2 * DEG2RAD;
|
y2 = y2 * DEG2RAD;
|
||||||
|
|
||||||
var a = Math.pow(Math.sin(( y2-y1 ) / 2.0 ), 2);
|
var a = Math.pow(Math.sin(( y2 - y1 ) / 2.0), 2);
|
||||||
var b = Math.pow(Math.sin(( x2-x1 ) / 2.0 ), 2);
|
var b = Math.pow(Math.sin(( x2 - x1 ) / 2.0), 2);
|
||||||
var c = Math.sqrt( a + Math.cos( y2 ) * Math.cos( y1 ) * b );
|
var c = Math.sqrt(a + Math.cos(y2) * Math.cos(y1) * b);
|
||||||
|
|
||||||
var type = (options && options.type) || 'miles';
|
var type = (options && options.type) || 'miles';
|
||||||
|
|
||||||
return 2 * Math.asin( c ) * EARTH_RADIUS[type];
|
return 2 * Math.asin(c) * EARTH_RADIUS[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
56
lib/hooks.js
56
lib/hooks.js
|
@ -27,39 +27,41 @@ Hookable.afterDestroy = null;
|
||||||
|
|
||||||
// TODO: Evaluate https://github.com/bnoguchi/hooks-js/
|
// TODO: Evaluate https://github.com/bnoguchi/hooks-js/
|
||||||
Hookable.prototype.trigger = function trigger(actionName, work, data) {
|
Hookable.prototype.trigger = function trigger(actionName, work, data) {
|
||||||
var capitalizedName = capitalize(actionName);
|
var capitalizedName = capitalize(actionName);
|
||||||
var beforeHook = this.constructor["before" + capitalizedName] || this.constructor["pre" + capitalizedName];
|
var beforeHook = this.constructor["before" + capitalizedName]
|
||||||
var afterHook = this.constructor["after" + capitalizedName] || this.constructor["post" + capitalizedName];
|
|| this.constructor["pre" + capitalizedName];
|
||||||
if (actionName === 'validate') {
|
var afterHook = this.constructor["after" + capitalizedName]
|
||||||
beforeHook = beforeHook || this.constructor.beforeValidation;
|
|| this.constructor["post" + capitalizedName];
|
||||||
afterHook = afterHook || this.constructor.afterValidation;
|
if (actionName === 'validate') {
|
||||||
}
|
beforeHook = beforeHook || this.constructor.beforeValidation;
|
||||||
var inst = this;
|
afterHook = afterHook || this.constructor.afterValidation;
|
||||||
|
}
|
||||||
|
var inst = this;
|
||||||
|
|
||||||
// we only call "before" hook when we have actual action (work) to perform
|
// we only call "before" hook when we have actual action (work) to perform
|
||||||
if (work) {
|
if (work) {
|
||||||
if (beforeHook) {
|
if (beforeHook) {
|
||||||
// before hook should be called on instance with one param: callback
|
// before hook should be called on instance with one param: callback
|
||||||
beforeHook.call(inst, function () {
|
beforeHook.call(inst, function () {
|
||||||
// actual action also have one param: callback
|
// actual action also have one param: callback
|
||||||
work.call(inst, next);
|
work.call(inst, next);
|
||||||
}, data);
|
}, data);
|
||||||
} else {
|
|
||||||
work.call(inst, next);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
next();
|
work.call(inst, next);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
function next(done) {
|
function next(done) {
|
||||||
if (afterHook) {
|
if (afterHook) {
|
||||||
afterHook.call(inst, done);
|
afterHook.call(inst, done);
|
||||||
} else if (done) {
|
} else if (done) {
|
||||||
done.call(this);
|
done.call(this);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function capitalize(string) {
|
function capitalize(string) {
|
||||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
}
|
}
|
||||||
|
|
246
lib/include.js
246
lib/include.js
|
@ -26,135 +26,137 @@ function Inclusion() {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Inclusion.include = function (objects, include, cb) {
|
Inclusion.include = function (objects, include, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(include.constructor.name == 'Array' && include.length == 0) ||
|
(include.constructor.name == 'Array' && include.length == 0) ||
|
||||||
(include.constructor.name == 'Object' && Object.keys(include).length == 0)
|
(include.constructor.name == 'Object' && Object.keys(include).length == 0)
|
||||||
) {
|
) {
|
||||||
cb(null, objects);
|
cb(null, objects);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
include = processIncludeJoin(include);
|
||||||
|
|
||||||
|
var keyVals = {};
|
||||||
|
var objsByKeys = {};
|
||||||
|
|
||||||
|
var nbCallbacks = 0;
|
||||||
|
for (var i = 0; i < include.length; i++) {
|
||||||
|
var callback = processIncludeItem(objects, include[i], keyVals, objsByKeys);
|
||||||
|
if (callback !== null) {
|
||||||
|
nbCallbacks++;
|
||||||
|
callback(function () {
|
||||||
|
nbCallbacks--;
|
||||||
|
if (nbCallbacks == 0) {
|
||||||
|
cb(null, objects);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cb(null, objects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function processIncludeJoin(ij) {
|
||||||
|
if (typeof ij === 'string') {
|
||||||
|
ij = [ij];
|
||||||
|
}
|
||||||
|
if (ij.constructor.name === 'Object') {
|
||||||
|
var newIj = [];
|
||||||
|
for (var key in ij) {
|
||||||
|
var obj = {};
|
||||||
|
obj[key] = ij[key];
|
||||||
|
newIj.push(obj);
|
||||||
|
}
|
||||||
|
return newIj;
|
||||||
|
}
|
||||||
|
return ij;
|
||||||
|
}
|
||||||
|
|
||||||
|
function processIncludeItem(objs, include, keyVals, objsByKeys) {
|
||||||
|
var relations = self.relations;
|
||||||
|
|
||||||
|
if (include.constructor.name === 'Object') {
|
||||||
|
var relationName = Object.keys(include)[0];
|
||||||
|
var subInclude = include[relationName];
|
||||||
|
} else {
|
||||||
|
var relationName = include;
|
||||||
|
var subInclude = [];
|
||||||
|
}
|
||||||
|
var relation = relations[relationName];
|
||||||
|
|
||||||
|
if (!relation) {
|
||||||
|
return function () {
|
||||||
|
cb(new Error('Relation "' + relationName + '" is not defined for '
|
||||||
|
+ self.modelName + ' model'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
include = processIncludeJoin(include);
|
var req = {'where': {}};
|
||||||
|
|
||||||
var keyVals = {};
|
if (!keyVals[relation.keyFrom]) {
|
||||||
var objsByKeys = {};
|
objsByKeys[relation.keyFrom] = {};
|
||||||
|
objs.filter(Boolean).forEach(function (obj) {
|
||||||
|
if (!objsByKeys[relation.keyFrom][obj[relation.keyFrom]]) {
|
||||||
|
objsByKeys[relation.keyFrom][obj[relation.keyFrom]] = [];
|
||||||
|
}
|
||||||
|
objsByKeys[relation.keyFrom][obj[relation.keyFrom]].push(obj);
|
||||||
|
});
|
||||||
|
keyVals[relation.keyFrom] = Object.keys(objsByKeys[relation.keyFrom]);
|
||||||
|
}
|
||||||
|
|
||||||
var nbCallbacks = 0;
|
if (keyVals[relation.keyFrom].length > 0) {
|
||||||
for (var i = 0; i < include.length; i++) {
|
// deep clone is necessary since inq seems to change the processed array
|
||||||
var callback = processIncludeItem(objects, include[i], keyVals, objsByKeys);
|
var keysToBeProcessed = {};
|
||||||
if (callback !== null) {
|
var inValues = [];
|
||||||
nbCallbacks++;
|
for (var j = 0; j < keyVals[relation.keyFrom].length; j++) {
|
||||||
callback(function() {
|
keysToBeProcessed[keyVals[relation.keyFrom][j]] = true;
|
||||||
nbCallbacks--;
|
if (keyVals[relation.keyFrom][j] !== 'null'
|
||||||
if (nbCallbacks == 0) {
|
&& keyVals[relation.keyFrom][j] !== 'undefined') {
|
||||||
cb(null, objects);
|
inValues.push(keyVals[relation.keyFrom][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req['where'][relation.keyTo] = {inq: inValues};
|
||||||
|
req['include'] = subInclude;
|
||||||
|
|
||||||
|
return function (cb) {
|
||||||
|
relation.modelTo.find(req, function (err, objsIncluded) {
|
||||||
|
for (var i = 0; i < objsIncluded.length; i++) {
|
||||||
|
delete keysToBeProcessed[objsIncluded[i][relation.keyTo]];
|
||||||
|
var objectsFrom = objsByKeys[relation.keyFrom][objsIncluded[i][relation.keyTo]];
|
||||||
|
for (var j = 0; j < objectsFrom.length; j++) {
|
||||||
|
if (!objectsFrom[j].__cachedRelations) {
|
||||||
|
objectsFrom[j].__cachedRelations = {};
|
||||||
|
}
|
||||||
|
if (relation.multiple) {
|
||||||
|
if (!objectsFrom[j].__cachedRelations[relationName]) {
|
||||||
|
objectsFrom[j].__cachedRelations[relationName] = [];
|
||||||
}
|
}
|
||||||
});
|
objectsFrom[j].__cachedRelations[relationName].push(objsIncluded[i]);
|
||||||
} else {
|
} else {
|
||||||
cb(null, objects);
|
objectsFrom[j].__cachedRelations[relationName] = objsIncluded[i];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No relation have been found for these keys
|
||||||
|
for (var key in keysToBeProcessed) {
|
||||||
|
var objectsFrom = objsByKeys[relation.keyFrom][key];
|
||||||
|
for (var j = 0; j < objectsFrom.length; j++) {
|
||||||
|
if (!objectsFrom[j].__cachedRelations) {
|
||||||
|
objectsFrom[j].__cachedRelations = {};
|
||||||
|
}
|
||||||
|
objectsFrom[j].__cachedRelations[relationName] =
|
||||||
|
relation.multiple ? [] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(err, objsIncluded);
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function processIncludeJoin(ij) {
|
return null;
|
||||||
if (typeof ij === 'string') {
|
}
|
||||||
ij = [ij];
|
|
||||||
}
|
|
||||||
if (ij.constructor.name === 'Object') {
|
|
||||||
var newIj = [];
|
|
||||||
for (var key in ij) {
|
|
||||||
var obj = {};
|
|
||||||
obj[key] = ij[key];
|
|
||||||
newIj.push(obj);
|
|
||||||
}
|
|
||||||
return newIj;
|
|
||||||
}
|
|
||||||
return ij;
|
|
||||||
}
|
|
||||||
|
|
||||||
function processIncludeItem(objs, include, keyVals, objsByKeys) {
|
|
||||||
var relations = self.relations;
|
|
||||||
|
|
||||||
if (include.constructor.name === 'Object') {
|
|
||||||
var relationName = Object.keys(include)[0];
|
|
||||||
var subInclude = include[relationName];
|
|
||||||
} else {
|
|
||||||
var relationName = include;
|
|
||||||
var subInclude = [];
|
|
||||||
}
|
|
||||||
var relation = relations[relationName];
|
|
||||||
|
|
||||||
if (!relation) {
|
|
||||||
return function() {
|
|
||||||
cb(new Error('Relation "' + relationName + '" is not defined for ' + self.modelName + ' model'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var req = {'where': {}};
|
|
||||||
|
|
||||||
if (!keyVals[relation.keyFrom]) {
|
|
||||||
objsByKeys[relation.keyFrom] = {};
|
|
||||||
objs.filter(Boolean).forEach(function(obj) {
|
|
||||||
if (!objsByKeys[relation.keyFrom][obj[relation.keyFrom]]) {
|
|
||||||
objsByKeys[relation.keyFrom][obj[relation.keyFrom]] = [];
|
|
||||||
}
|
|
||||||
objsByKeys[relation.keyFrom][obj[relation.keyFrom]].push(obj);
|
|
||||||
});
|
|
||||||
keyVals[relation.keyFrom] = Object.keys(objsByKeys[relation.keyFrom]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyVals[relation.keyFrom].length > 0) {
|
|
||||||
// deep clone is necessary since inq seems to change the processed array
|
|
||||||
var keysToBeProcessed = {};
|
|
||||||
var inValues = [];
|
|
||||||
for (var j = 0; j < keyVals[relation.keyFrom].length; j++) {
|
|
||||||
keysToBeProcessed[keyVals[relation.keyFrom][j]] = true;
|
|
||||||
if (keyVals[relation.keyFrom][j] !== 'null' && keyVals[relation.keyFrom][j] !== 'undefined') {
|
|
||||||
inValues.push(keyVals[relation.keyFrom][j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
req['where'][relation.keyTo] = {inq: inValues};
|
|
||||||
req['include'] = subInclude;
|
|
||||||
|
|
||||||
return function(cb) {
|
|
||||||
relation.modelTo.find(req, function(err, objsIncluded) {
|
|
||||||
for (var i = 0; i < objsIncluded.length; i++) {
|
|
||||||
delete keysToBeProcessed[objsIncluded[i][relation.keyTo]];
|
|
||||||
var objectsFrom = objsByKeys[relation.keyFrom][objsIncluded[i][relation.keyTo]];
|
|
||||||
for (var j = 0; j < objectsFrom.length; j++) {
|
|
||||||
if (!objectsFrom[j].__cachedRelations) {
|
|
||||||
objectsFrom[j].__cachedRelations = {};
|
|
||||||
}
|
|
||||||
if (relation.multiple) {
|
|
||||||
if (!objectsFrom[j].__cachedRelations[relationName]) {
|
|
||||||
objectsFrom[j].__cachedRelations[relationName] = [];
|
|
||||||
}
|
|
||||||
objectsFrom[j].__cachedRelations[relationName].push(objsIncluded[i]);
|
|
||||||
} else {
|
|
||||||
objectsFrom[j].__cachedRelations[relationName] = objsIncluded[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No relation have been found for these keys
|
|
||||||
for (var key in keysToBeProcessed) {
|
|
||||||
var objectsFrom = objsByKeys[relation.keyFrom][key];
|
|
||||||
for (var j = 0; j < objectsFrom.length; j++) {
|
|
||||||
if (!objectsFrom[j].__cachedRelations) {
|
|
||||||
objectsFrom[j].__cachedRelations = {};
|
|
||||||
}
|
|
||||||
objectsFrom[j].__cachedRelations[relationName] = relation.multiple ? [] : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cb(err, objsIncluded);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,60 +1,60 @@
|
||||||
module.exports = function getIntrospector(ModelBuilder) {
|
module.exports = function getIntrospector(ModelBuilder) {
|
||||||
|
|
||||||
function introspectType(value) {
|
function introspectType(value) {
|
||||||
|
|
||||||
// Unknown type, using Any
|
// Unknown type, using Any
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined) {
|
||||||
return ModelBuilder.Any;
|
return ModelBuilder.Any;
|
||||||
}
|
|
||||||
|
|
||||||
// Check registered schemaTypes
|
|
||||||
for (var t in ModelBuilder.schemaTypes) {
|
|
||||||
var st = ModelBuilder.schemaTypes[t];
|
|
||||||
if (st !== Object && st !== Array && (value instanceof st)) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = typeof value;
|
|
||||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value instanceof Date) {
|
|
||||||
return 'date';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
for (var i = 0; i < value.length; i++) {
|
|
||||||
if (value[i] === null || value[i] === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var itemType = introspectType(value[i]);
|
|
||||||
if (itemType) {
|
|
||||||
return [itemType];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'array';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'function') {
|
|
||||||
return value.constructor.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
var properties = {};
|
|
||||||
for (var p in value) {
|
|
||||||
var itemType = introspectType(value[p]);
|
|
||||||
if (itemType) {
|
|
||||||
properties[p] = itemType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Object.keys(properties).length === 0) {
|
|
||||||
return 'object';
|
|
||||||
}
|
|
||||||
return properties;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return introspectType;
|
// Check registered schemaTypes
|
||||||
|
for (var t in ModelBuilder.schemaTypes) {
|
||||||
|
var st = ModelBuilder.schemaTypes[t];
|
||||||
|
if (st !== Object && st !== Array && (value instanceof st)) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = typeof value;
|
||||||
|
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Date) {
|
||||||
|
return 'date';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
for (var i = 0; i < value.length; i++) {
|
||||||
|
if (value[i] === null || value[i] === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var itemType = introspectType(value[i]);
|
||||||
|
if (itemType) {
|
||||||
|
return [itemType];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'array';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'function') {
|
||||||
|
return value.constructor.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
var properties = {};
|
||||||
|
for (var p in value) {
|
||||||
|
var itemType = introspectType(value[p]);
|
||||||
|
if (itemType) {
|
||||||
|
properties[p] = itemType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(properties).length === 0) {
|
||||||
|
return 'object';
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
return introspectType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
136
lib/jutil.js
136
lib/jutil.js
|
@ -5,24 +5,24 @@ var util = require('util');
|
||||||
* @param baseClass
|
* @param baseClass
|
||||||
*/
|
*/
|
||||||
exports.inherits = function (newClass, baseClass, options) {
|
exports.inherits = function (newClass, baseClass, options) {
|
||||||
util.inherits(newClass, baseClass);
|
util.inherits(newClass, baseClass);
|
||||||
|
|
||||||
options = options || {
|
options = options || {
|
||||||
staticProperties: true,
|
staticProperties: true,
|
||||||
override: false
|
override: false
|
||||||
};
|
};
|
||||||
|
|
||||||
if (options.staticProperties) {
|
if (options.staticProperties) {
|
||||||
Object.keys(baseClass).forEach(function (classProp) {
|
Object.keys(baseClass).forEach(function (classProp) {
|
||||||
if (classProp !== 'super_' && (!newClass.hasOwnProperty(classProp) || options.override)) {
|
if (classProp !== 'super_' && (!newClass.hasOwnProperty(classProp)
|
||||||
var pd = Object.getOwnPropertyDescriptor(baseClass, classProp);
|
|| options.override)) {
|
||||||
Object.defineProperty(newClass, classProp, pd);
|
var pd = Object.getOwnPropertyDescriptor(baseClass, classProp);
|
||||||
}
|
Object.defineProperty(newClass, classProp, pd);
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mix in the a class into the new class
|
* Mix in the a class into the new class
|
||||||
* @param newClass The target class to receive the mixin
|
* @param newClass The target class to receive the mixin
|
||||||
|
@ -30,76 +30,78 @@ exports.inherits = function (newClass, baseClass, options) {
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
exports.mixin = function (newClass, mixinClass, options) {
|
exports.mixin = function (newClass, mixinClass, options) {
|
||||||
if (Array.isArray(newClass._mixins)) {
|
if (Array.isArray(newClass._mixins)) {
|
||||||
if(newClass._mixins.indexOf(mixinClass) !== -1) {
|
if (newClass._mixins.indexOf(mixinClass) !== -1) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
newClass._mixins.push(mixinClass);
|
||||||
|
} else {
|
||||||
|
newClass._mixins = [mixinClass];
|
||||||
|
}
|
||||||
|
|
||||||
|
options = options || {
|
||||||
|
staticProperties: true,
|
||||||
|
instanceProperties: true,
|
||||||
|
override: false,
|
||||||
|
proxyFunctions: false
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options.staticProperties === undefined) {
|
||||||
|
options.staticProperties = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.instanceProperties === undefined) {
|
||||||
|
options.instanceProperties = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.staticProperties) {
|
||||||
|
var staticProxies = [];
|
||||||
|
Object.keys(mixinClass).forEach(function (classProp) {
|
||||||
|
if (classProp !== 'super_' && classProp !== '_mixins'
|
||||||
|
&& (!newClass.hasOwnProperty(classProp) || options.override)) {
|
||||||
|
var pd = Object.getOwnPropertyDescriptor(mixinClass, classProp);
|
||||||
|
if (options.proxyFunctions && pd.writable
|
||||||
|
&& typeof pd.value === 'function') {
|
||||||
|
pd.value = exports.proxy(pd.value, staticProxies);
|
||||||
}
|
}
|
||||||
newClass._mixins.push(mixinClass);
|
Object.defineProperty(newClass, classProp, pd);
|
||||||
} else {
|
}
|
||||||
newClass._mixins = [mixinClass];
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
options = options || {
|
if (options.instanceProperties) {
|
||||||
staticProperties: true,
|
if (mixinClass.prototype) {
|
||||||
instanceProperties: true,
|
var instanceProxies = [];
|
||||||
override: false,
|
Object.keys(mixinClass.prototype).forEach(function (instanceProp) {
|
||||||
proxyFunctions: false
|
if (!newClass.prototype.hasOwnProperty(instanceProp) || options.override) {
|
||||||
};
|
var pd = Object.getOwnPropertyDescriptor(mixinClass.prototype, instanceProp);
|
||||||
|
if (options.proxyFunctions && pd.writable && typeof pd.value === 'function') {
|
||||||
if(options.staticProperties === undefined) {
|
pd.value = exports.proxy(pd.value, instanceProxies);
|
||||||
options.staticProperties = true;
|
}
|
||||||
}
|
Object.defineProperty(newClass.prototype, instanceProp, pd);
|
||||||
|
|
||||||
if(options.instanceProperties === undefined) {
|
|
||||||
options.instanceProperties = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.staticProperties) {
|
|
||||||
var staticProxies = [];
|
|
||||||
Object.keys(mixinClass).forEach(function (classProp) {
|
|
||||||
if (classProp !== 'super_' && classProp !== '_mixins' && (!newClass.hasOwnProperty(classProp) || options.override)) {
|
|
||||||
var pd = Object.getOwnPropertyDescriptor(mixinClass, classProp);
|
|
||||||
if(options.proxyFunctions && pd.writable && typeof pd.value === 'function') {
|
|
||||||
pd.value = exports.proxy(pd.value, staticProxies);
|
|
||||||
}
|
|
||||||
Object.defineProperty(newClass, classProp, pd);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.instanceProperties) {
|
|
||||||
if (mixinClass.prototype) {
|
|
||||||
var instanceProxies = [];
|
|
||||||
Object.keys(mixinClass.prototype).forEach(function (instanceProp) {
|
|
||||||
if (!newClass.prototype.hasOwnProperty(instanceProp) || options.override) {
|
|
||||||
var pd = Object.getOwnPropertyDescriptor(mixinClass.prototype, instanceProp);
|
|
||||||
if(options.proxyFunctions && pd.writable && typeof pd.value === 'function') {
|
|
||||||
pd.value = exports.proxy(pd.value, instanceProxies);
|
|
||||||
}
|
|
||||||
Object.defineProperty(newClass.prototype, instanceProp, pd);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return newClass;
|
return newClass;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.proxy = function(fn, proxies) {
|
exports.proxy = function (fn, proxies) {
|
||||||
// Make sure same methods referenced by different properties have the same proxy
|
// Make sure same methods referenced by different properties have the same proxy
|
||||||
// For example, deleteById is an alias of removeById
|
// For example, deleteById is an alias of removeById
|
||||||
proxies = proxies || [];
|
proxies = proxies || [];
|
||||||
for(var i = 0; i<proxies.length; i++) {
|
for (var i = 0; i < proxies.length; i++) {
|
||||||
if(proxies[i]._delegate === fn) {
|
if (proxies[i]._delegate === fn) {
|
||||||
return proxies[i];
|
return proxies[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var f = function() {
|
var f = function () {
|
||||||
return fn.apply(this, arguments);
|
return fn.apply(this, arguments);
|
||||||
};
|
};
|
||||||
f._delegate = fn;
|
f._delegate = fn;
|
||||||
proxies.push(f);
|
proxies.push(f);
|
||||||
Object.keys(fn).forEach(function(x) {
|
Object.keys(fn).forEach(function (x) {
|
||||||
f[x] = fn[x];
|
f[x] = fn[x];
|
||||||
});
|
});
|
||||||
return f;
|
return f;
|
||||||
|
|
363
lib/list.js
363
lib/list.js
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
module.exports = List;
|
module.exports = List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,229 +9,229 @@ module.exports = List;
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function List(data, type, parent) {
|
function List(data, type, parent) {
|
||||||
var list = this;
|
var list = this;
|
||||||
if (!(list instanceof List)) {
|
if (!(list instanceof List)) {
|
||||||
return new List(data, type, parent);
|
return new List(data, type, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
try {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('could not create List from JSON string: ', data);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(typeof data === 'string') {
|
if (data && data instanceof List) data = data.items;
|
||||||
try {
|
|
||||||
data = JSON.parse(data);
|
Object.defineProperty(list, 'parent', {
|
||||||
} catch(e) {
|
writable: false,
|
||||||
throw new Error('could not create List from JSON string: ', data);
|
enumerable: false,
|
||||||
}
|
configurable: false,
|
||||||
|
value: parent
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(list, 'nextid', {
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
value: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
data = list.items = data || [];
|
||||||
|
var Item = list.ItemType = ListItem;
|
||||||
|
|
||||||
|
if (typeof type === 'object' && type.constructor.name === 'Array') {
|
||||||
|
Item = list.ItemType = type[0] || ListItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.forEach(function (item, i) {
|
||||||
|
data[i] = Item(item, list);
|
||||||
|
Object.defineProperty(list, data[i].id, {
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true,
|
||||||
|
value: data[i]
|
||||||
|
});
|
||||||
|
if (list.nextid <= data[i].id) {
|
||||||
|
list.nextid = data[i].id + 1;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (data && data instanceof List) data = data.items;
|
Object.defineProperty(list, 'length', {
|
||||||
|
enumerable: false,
|
||||||
Object.defineProperty(list, 'parent', {
|
configurable: true,
|
||||||
writable: false,
|
get: function () {
|
||||||
enumerable: false,
|
return list.items.length;
|
||||||
configurable: false,
|
|
||||||
value: parent
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(list, 'nextid', {
|
|
||||||
writable: true,
|
|
||||||
enumerable: false,
|
|
||||||
value: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
data = list.items = data || [];
|
|
||||||
var Item = list.ItemType = ListItem;
|
|
||||||
|
|
||||||
if (typeof type === 'object' && type.constructor.name === 'Array') {
|
|
||||||
Item = list.ItemType = type[0] || ListItem;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
data.forEach(function(item, i) {
|
return list;
|
||||||
data[i] = Item(item, list);
|
|
||||||
Object.defineProperty(list, data[i].id, {
|
|
||||||
writable: true,
|
|
||||||
enumerable: false,
|
|
||||||
configurable: true,
|
|
||||||
value: data[i]
|
|
||||||
});
|
|
||||||
if (list.nextid <= data[i].id) {
|
|
||||||
list.nextid = data[i].id + 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(list, 'length', {
|
|
||||||
enumerable: false,
|
|
||||||
configurable: true,
|
|
||||||
get: function() {
|
|
||||||
return list.items.length;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return list;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _;
|
var _;
|
||||||
try {
|
try {
|
||||||
var underscore = 'underscore';
|
var underscore = 'underscore';
|
||||||
_ = require(underscore);
|
_ = require(underscore);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_ = false;
|
_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_) {
|
if (_) {
|
||||||
var _import = [
|
var _import = [
|
||||||
// collection methods
|
// collection methods
|
||||||
'each',
|
'each',
|
||||||
'map',
|
'map',
|
||||||
'reduce',
|
'reduce',
|
||||||
'reduceRight',
|
'reduceRight',
|
||||||
'find',
|
'find',
|
||||||
'filter',
|
'filter',
|
||||||
'reject',
|
'reject',
|
||||||
'all',
|
'all',
|
||||||
'any',
|
'any',
|
||||||
'include',
|
'include',
|
||||||
'invoke',
|
'invoke',
|
||||||
'pluck',
|
'pluck',
|
||||||
'max',
|
'max',
|
||||||
'min',
|
'min',
|
||||||
'sortBy',
|
'sortBy',
|
||||||
'groupBy',
|
'groupBy',
|
||||||
'sortedIndex',
|
'sortedIndex',
|
||||||
'shuffle',
|
'shuffle',
|
||||||
'toArray',
|
'toArray',
|
||||||
'size',
|
'size',
|
||||||
// array methods
|
// array methods
|
||||||
'first',
|
'first',
|
||||||
'initial',
|
'initial',
|
||||||
'last',
|
'last',
|
||||||
'rest',
|
'rest',
|
||||||
'compact',
|
'compact',
|
||||||
'flatten',
|
'flatten',
|
||||||
'without',
|
'without',
|
||||||
'union',
|
'union',
|
||||||
'intersection',
|
'intersection',
|
||||||
'difference',
|
'difference',
|
||||||
'uniq',
|
'uniq',
|
||||||
'zip',
|
'zip',
|
||||||
'indexOf',
|
'indexOf',
|
||||||
'lastIndexOf',
|
'lastIndexOf',
|
||||||
'range'
|
'range'
|
||||||
];
|
];
|
||||||
|
|
||||||
_import.forEach(function(name) {
|
_import.forEach(function (name) {
|
||||||
List.prototype[name] = function() {
|
List.prototype[name] = function () {
|
||||||
var args = [].slice.call(arguments);
|
var args = [].slice.call(arguments);
|
||||||
args.unshift(this.items);
|
args.unshift(this.items);
|
||||||
return _[name].apply(_, args);
|
return _[name].apply(_, args);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
['slice', 'forEach', 'filter', 'reduce', 'map'].forEach(function (method) {
|
['slice', 'forEach', 'filter', 'reduce', 'map'].forEach(function (method) {
|
||||||
var slice = [].slice;
|
var slice = [].slice;
|
||||||
List.prototype[method] = function () {
|
List.prototype[method] = function () {
|
||||||
return Array.prototype[method].apply(this.items, slice.call(arguments));
|
return Array.prototype[method].apply(this.items, slice.call(arguments));
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
List.prototype.find = function(pattern, field) {
|
List.prototype.find = function (pattern, field) {
|
||||||
if (field) {
|
if (field) {
|
||||||
var res;
|
var res;
|
||||||
this.items.forEach(function(o) {
|
this.items.forEach(function (o) {
|
||||||
if (o[field] == pattern) res = o;
|
if (o[field] == pattern) res = o;
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
|
} else {
|
||||||
|
return this.items[this.items.indexOf(pattern)];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
List.prototype.toObject = function (onlySchema) {
|
||||||
|
var items = [];
|
||||||
|
this.items.forEach(function (item) {
|
||||||
|
if (item.toObject) {
|
||||||
|
items.push(item.toObject(onlySchema));
|
||||||
} else {
|
} else {
|
||||||
return this.items[this.items.indexOf(pattern)];
|
items.push(item);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
return items;
|
||||||
};
|
};
|
||||||
|
|
||||||
List.prototype.toObject = function(onlySchema) {
|
List.prototype.toJSON = function () {
|
||||||
var items = [];
|
return this.toObject(true);
|
||||||
this.items.forEach(function(item) {
|
|
||||||
if(item.toObject) {
|
|
||||||
items.push(item.toObject(onlySchema));
|
|
||||||
} else {
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return items;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
List.prototype.toJSON = function() {
|
List.prototype.toString = function () {
|
||||||
return this.toObject(true);
|
return JSON.stringify(this.items);
|
||||||
};
|
};
|
||||||
|
|
||||||
List.prototype.toString = function() {
|
List.prototype.autoincrement = function () {
|
||||||
return JSON.stringify(this.items);
|
return this.nextid++;
|
||||||
};
|
};
|
||||||
|
|
||||||
List.prototype.autoincrement = function() {
|
List.prototype.push = function (obj) {
|
||||||
return this.nextid++;
|
var item = new ListItem(obj, this);
|
||||||
|
this.items.push(item);
|
||||||
|
return item;
|
||||||
};
|
};
|
||||||
|
|
||||||
List.prototype.push = function(obj) {
|
List.prototype.remove = function (obj) {
|
||||||
var item = new ListItem(obj, this);
|
var id = obj.id ? obj.id : obj;
|
||||||
this.items.push(item);
|
var found = false;
|
||||||
return item;
|
this.items.forEach(function (o, i) {
|
||||||
};
|
if (id && o.id == id) {
|
||||||
|
found = i;
|
||||||
List.prototype.remove = function(obj) {
|
if (o.id !== id) {
|
||||||
var id = obj.id ? obj.id : obj;
|
console.log('WARNING! Type of id not matched');
|
||||||
var found = false;
|
}
|
||||||
this.items.forEach(function(o, i) {
|
|
||||||
if (id && o.id == id) {
|
|
||||||
found = i;
|
|
||||||
if (o.id !== id) {
|
|
||||||
console.log('WARNING! Type of id not matched');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (found !== false) {
|
|
||||||
delete this[id];
|
|
||||||
this.items.splice(found, 1);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
if (found !== false) {
|
||||||
|
delete this[id];
|
||||||
|
this.items.splice(found, 1);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
List.prototype.sort = function(cb) {
|
List.prototype.sort = function (cb) {
|
||||||
return this.items.sort(cb);
|
return this.items.sort(cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
List.prototype.map = function(cb) {
|
List.prototype.map = function (cb) {
|
||||||
if (typeof cb === 'function') return this.items.map(cb);
|
if (typeof cb === 'function') return this.items.map(cb);
|
||||||
if (typeof cb === 'string') return this.items.map(function(el) {
|
if (typeof cb === 'string') return this.items.map(function (el) {
|
||||||
if (typeof el[cb] === 'function') return el[cb]();
|
if (typeof el[cb] === 'function') return el[cb]();
|
||||||
if (el.hasOwnProperty(cb)) return el[cb];
|
if (el.hasOwnProperty(cb)) return el[cb];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function ListItem(data, parent) {
|
function ListItem(data, parent) {
|
||||||
if(!(this instanceof ListItem)) {
|
if (!(this instanceof ListItem)) {
|
||||||
return new ListItem(data, parent);
|
return new ListItem(data, parent);
|
||||||
}
|
}
|
||||||
if (typeof data === 'object') {
|
if (typeof data === 'object') {
|
||||||
for (var i in data) this[i] = data[i];
|
for (var i in data) this[i] = data[i];
|
||||||
} else {
|
} else {
|
||||||
this.id = data;
|
this.id = data;
|
||||||
}
|
}
|
||||||
Object.defineProperty(this, 'parent', {
|
Object.defineProperty(this, 'parent', {
|
||||||
writable: false,
|
writable: false,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
value: parent
|
value: parent
|
||||||
});
|
});
|
||||||
if (!this.id) {
|
if (!this.id) {
|
||||||
this.id = parent.autoincrement();
|
this.id = parent.autoincrement();
|
||||||
}
|
}
|
||||||
if (parent.ItemType) {
|
if (parent.ItemType) {
|
||||||
this.__proto__ = parent.ItemType.prototype;
|
this.__proto__ = parent.ItemType.prototype;
|
||||||
if (parent.ItemType !== ListItem) {
|
if (parent.ItemType !== ListItem) {
|
||||||
parent.ItemType.apply(this);
|
parent.ItemType.apply(this);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.save = function(c) {
|
this.save = function (c) {
|
||||||
parent.parent.save(c);
|
parent.parent.save(c);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,15 +32,15 @@ var slice = Array.prototype.slice;
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function ModelBuilder() {
|
function ModelBuilder() {
|
||||||
// create blank models pool
|
// create blank models pool
|
||||||
/**
|
/**
|
||||||
* @property {Object} models Model constructors
|
* @property {Object} models Model constructors
|
||||||
*/
|
*/
|
||||||
this.models = {};
|
this.models = {};
|
||||||
/**
|
/**
|
||||||
* @property {Object} definitions Definitions of the models
|
* @property {Object} definitions Definitions of the models
|
||||||
*/
|
*/
|
||||||
this.definitions = {};
|
this.definitions = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inherit from EventEmitter
|
// Inherit from EventEmitter
|
||||||
|
@ -50,10 +50,10 @@ util.inherits(ModelBuilder, EventEmitter);
|
||||||
ModelBuilder.defaultInstance = new ModelBuilder();
|
ModelBuilder.defaultInstance = new ModelBuilder();
|
||||||
|
|
||||||
function isModelClass(cls) {
|
function isModelClass(cls) {
|
||||||
if(!cls) {
|
if (!cls) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return cls.prototype instanceof DefaultModelBaseClass;
|
return cls.prototype instanceof DefaultModelBaseClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,15 +63,15 @@ function isModelClass(cls) {
|
||||||
* given name if a model doesn't exist
|
* given name if a model doesn't exist
|
||||||
* @returns {*} The model class
|
* @returns {*} The model class
|
||||||
*/
|
*/
|
||||||
ModelBuilder.prototype.getModel = function(name, forceCreate) {
|
ModelBuilder.prototype.getModel = function (name, forceCreate) {
|
||||||
var model = this.models[name];
|
var model = this.models[name];
|
||||||
if(!model && forceCreate) {
|
if (!model && forceCreate) {
|
||||||
model = this.define(name, {}, {unresolved: true});
|
model = this.define(name, {}, {unresolved: true});
|
||||||
}
|
}
|
||||||
return model;
|
return model;
|
||||||
};
|
};
|
||||||
|
|
||||||
ModelBuilder.prototype.getModelDefinition = function(name) {
|
ModelBuilder.prototype.getModelDefinition = function (name) {
|
||||||
return this.definitions[name];
|
return this.definitions[name];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -107,316 +107,316 @@ ModelBuilder.prototype.getModelDefinition = function(name) {
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
ModelBuilder.prototype.define = function defineClass(className, properties, settings, parent) {
|
ModelBuilder.prototype.define = function defineClass(className, properties, settings, parent) {
|
||||||
var modelBuilder = this;
|
var modelBuilder = this;
|
||||||
var args = slice.call(arguments);
|
var args = slice.call(arguments);
|
||||||
var pluralName = (settings && settings.plural) ||
|
var pluralName = (settings && settings.plural) ||
|
||||||
inflection.pluralize(className);
|
inflection.pluralize(className);
|
||||||
|
|
||||||
if (!className) {
|
if (!className) {
|
||||||
throw new Error('Class name required');
|
throw new Error('Class name required');
|
||||||
|
}
|
||||||
|
if (args.length === 1) {
|
||||||
|
properties = {};
|
||||||
|
args.push(properties);
|
||||||
|
}
|
||||||
|
if (args.length === 2) {
|
||||||
|
settings = {};
|
||||||
|
args.push(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
properties = properties || {};
|
||||||
|
settings = settings || {};
|
||||||
|
|
||||||
|
// Set the strict mode to be false by default
|
||||||
|
if (settings.strict === undefined || settings.strict === null) {
|
||||||
|
settings.strict = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the base model class
|
||||||
|
var ModelBaseClass = parent || DefaultModelBaseClass;
|
||||||
|
var baseClass = settings.base || settings['super'];
|
||||||
|
if (baseClass) {
|
||||||
|
if (isModelClass(baseClass)) {
|
||||||
|
ModelBaseClass = baseClass;
|
||||||
|
} else {
|
||||||
|
ModelBaseClass = this.models[baseClass];
|
||||||
|
assert(ModelBaseClass, 'Base model is not found: ' + baseClass);
|
||||||
}
|
}
|
||||||
if (args.length === 1) {
|
}
|
||||||
properties = {};
|
|
||||||
args.push(properties);
|
// Check if there is a unresolved model with the same name
|
||||||
|
var ModelClass = this.models[className];
|
||||||
|
|
||||||
|
// Create the ModelClass if it doesn't exist or it's resolved (override)
|
||||||
|
// TODO: [rfeng] We need to decide what names to use for built-in models such as User.
|
||||||
|
if (!ModelClass || !ModelClass.settings.unresolved) {
|
||||||
|
// every class can receive hash of data as optional param
|
||||||
|
ModelClass = function ModelConstructor(data, dataSource) {
|
||||||
|
if (!(this instanceof ModelConstructor)) {
|
||||||
|
return new ModelConstructor(data, dataSource);
|
||||||
|
}
|
||||||
|
if (ModelClass.settings.unresolved) {
|
||||||
|
throw new Error('Model ' + ModelClass.modelName + ' is not defined.');
|
||||||
|
}
|
||||||
|
ModelBaseClass.apply(this, arguments);
|
||||||
|
if (dataSource) {
|
||||||
|
hiddenProperty(this, '__dataSource', dataSource);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// mix in EventEmitter (don't inherit from)
|
||||||
|
var events = new EventEmitter();
|
||||||
|
for (var f in EventEmitter.prototype) {
|
||||||
|
if (typeof EventEmitter.prototype[f] === 'function') {
|
||||||
|
ModelClass[f] = EventEmitter.prototype[f].bind(events);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (args.length === 2) {
|
hiddenProperty(ModelClass, 'modelName', className);
|
||||||
settings = {};
|
}
|
||||||
args.push(settings);
|
|
||||||
|
util.inherits(ModelClass, ModelBaseClass);
|
||||||
|
|
||||||
|
// store class in model pool
|
||||||
|
this.models[className] = ModelClass;
|
||||||
|
|
||||||
|
// Return the unresolved model
|
||||||
|
if (settings.unresolved) {
|
||||||
|
ModelClass.settings = {unresolved: true};
|
||||||
|
return ModelClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add metadata to the ModelClass
|
||||||
|
hiddenProperty(ModelClass, 'modelBuilder', modelBuilder);
|
||||||
|
hiddenProperty(ModelClass, 'dataSource', modelBuilder); // Keep for back-compatibility
|
||||||
|
hiddenProperty(ModelClass, 'pluralModelName', pluralName);
|
||||||
|
hiddenProperty(ModelClass, 'relations', {});
|
||||||
|
hiddenProperty(ModelClass, 'http', { path: '/' + pluralName });
|
||||||
|
|
||||||
|
// inherit ModelBaseClass static methods
|
||||||
|
for (var i in ModelBaseClass) {
|
||||||
|
// We need to skip properties that are already in the subclass, for example, the event emitter methods
|
||||||
|
if (i !== '_mixins' && !(i in ModelClass)) {
|
||||||
|
ModelClass[i] = ModelBaseClass[i];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
properties = properties || {};
|
// Load and inject the model classes
|
||||||
settings = settings || {};
|
if (settings.models) {
|
||||||
|
Object.keys(settings.models).forEach(function (m) {
|
||||||
|
var model = settings.models[m];
|
||||||
|
ModelClass[m] = typeof model === 'string' ? modelBuilder.getModel(model, true) : model;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Set the strict mode to be false by default
|
ModelClass.getter = {};
|
||||||
if(settings.strict === undefined || settings.strict === null) {
|
ModelClass.setter = {};
|
||||||
settings.strict = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the base model class
|
var modelDefinition = new ModelDefinition(this, className, properties, settings);
|
||||||
var ModelBaseClass = parent || DefaultModelBaseClass;
|
|
||||||
var baseClass = settings.base || settings['super'];
|
|
||||||
if(baseClass) {
|
|
||||||
if(isModelClass(baseClass)) {
|
|
||||||
ModelBaseClass = baseClass;
|
|
||||||
} else {
|
|
||||||
ModelBaseClass = this.models[baseClass];
|
|
||||||
assert(ModelBaseClass, 'Base model is not found: ' + baseClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there is a unresolved model with the same name
|
this.definitions[className] = modelDefinition;
|
||||||
var ModelClass = this.models[className];
|
|
||||||
|
|
||||||
// Create the ModelClass if it doesn't exist or it's resolved (override)
|
// expose properties on the ModelClass
|
||||||
// TODO: [rfeng] We need to decide what names to use for built-in models such as User.
|
ModelClass.definition = modelDefinition;
|
||||||
if(!ModelClass || !ModelClass.settings.unresolved) {
|
// keep a pointer to settings as models can use it for configuration
|
||||||
// every class can receive hash of data as optional param
|
ModelClass.settings = modelDefinition.settings;
|
||||||
ModelClass = function ModelConstructor(data, dataSource) {
|
|
||||||
if(!(this instanceof ModelConstructor)) {
|
|
||||||
return new ModelConstructor(data, dataSource);
|
|
||||||
}
|
|
||||||
if(ModelClass.settings.unresolved) {
|
|
||||||
throw new Error('Model ' + ModelClass.modelName + ' is not defined.');
|
|
||||||
}
|
|
||||||
ModelBaseClass.apply(this, arguments);
|
|
||||||
if(dataSource) {
|
|
||||||
hiddenProperty(this, '__dataSource', dataSource);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// mix in EventEmitter (don't inherit from)
|
|
||||||
var events = new EventEmitter();
|
|
||||||
for (var f in EventEmitter.prototype) {
|
|
||||||
if (typeof EventEmitter.prototype[f] === 'function') {
|
|
||||||
ModelClass[f] = EventEmitter.prototype[f].bind(events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hiddenProperty(ModelClass, 'modelName', className);
|
|
||||||
}
|
|
||||||
|
|
||||||
util.inherits(ModelClass, ModelBaseClass);
|
var idInjection = settings.idInjection;
|
||||||
|
if (idInjection !== false) {
|
||||||
|
// Default to true if undefined
|
||||||
|
idInjection = true;
|
||||||
|
}
|
||||||
|
|
||||||
// store class in model pool
|
var idNames = modelDefinition.idNames();
|
||||||
this.models[className] = ModelClass;
|
if (idNames.length > 0) {
|
||||||
|
// id already exists
|
||||||
|
idInjection = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Return the unresolved model
|
// Add the id property
|
||||||
if(settings.unresolved) {
|
if (idInjection) {
|
||||||
ModelClass.settings = {unresolved: true};
|
// Set up the id property
|
||||||
return ModelClass;
|
ModelClass.definition.defineProperty('id', { type: Number, id: 1, generated: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add metadata to the ModelClass
|
idNames = modelDefinition.idNames(); // Reload it after rebuild
|
||||||
hiddenProperty(ModelClass, 'modelBuilder', modelBuilder);
|
// Create a virtual property 'id'
|
||||||
hiddenProperty(ModelClass, 'dataSource', modelBuilder); // Keep for back-compatibility
|
if (idNames.length === 1) {
|
||||||
hiddenProperty(ModelClass, 'pluralModelName', pluralName);
|
var idProp = idNames[0];
|
||||||
hiddenProperty(ModelClass, 'relations', {});
|
if (idProp !== 'id') {
|
||||||
hiddenProperty(ModelClass, 'http', { path: '/' + pluralName });
|
Object.defineProperty(ModelClass.prototype, 'id', {
|
||||||
|
get: function () {
|
||||||
// inherit ModelBaseClass static methods
|
var idProp = ModelClass.definition.idNames[0];
|
||||||
for (var i in ModelBaseClass) {
|
return this.__data[idProp];
|
||||||
// We need to skip properties that are already in the subclass, for example, the event emitter methods
|
},
|
||||||
if(i !== '_mixins' && !(i in ModelClass)) {
|
configurable: true,
|
||||||
ModelClass[i] = ModelBaseClass[i];
|
enumerable: false
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load and inject the model classes
|
|
||||||
if(settings.models) {
|
|
||||||
Object.keys(settings.models).forEach(function(m) {
|
|
||||||
var model = settings.models[m];
|
|
||||||
ModelClass[m] = typeof model === 'string' ? modelBuilder.getModel(model, true) : model;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
ModelClass.getter = {};
|
// Now the id property is an object that consists of multiple keys
|
||||||
ModelClass.setter = {};
|
Object.defineProperty(ModelClass.prototype, 'id', {
|
||||||
|
get: function () {
|
||||||
var modelDefinition = new ModelDefinition(this, className, properties, settings);
|
var compositeId = {};
|
||||||
|
var idNames = ModelClass.definition.idNames();
|
||||||
this.definitions[className] = modelDefinition;
|
for (var p in idNames) {
|
||||||
|
compositeId[p] = this.__data[p];
|
||||||
// expose properties on the ModelClass
|
|
||||||
ModelClass.definition = modelDefinition;
|
|
||||||
// keep a pointer to settings as models can use it for configuration
|
|
||||||
ModelClass.settings = modelDefinition.settings;
|
|
||||||
|
|
||||||
var idInjection = settings.idInjection;
|
|
||||||
if(idInjection !== false) {
|
|
||||||
// Default to true if undefined
|
|
||||||
idInjection = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var idNames = modelDefinition.idNames();
|
|
||||||
if(idNames.length > 0) {
|
|
||||||
// id already exists
|
|
||||||
idInjection = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the id property
|
|
||||||
if (idInjection) {
|
|
||||||
// Set up the id property
|
|
||||||
ModelClass.definition.defineProperty('id', { type: Number, id: 1, generated: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
idNames = modelDefinition.idNames(); // Reload it after rebuild
|
|
||||||
// Create a virtual property 'id'
|
|
||||||
if (idNames.length === 1) {
|
|
||||||
var idProp = idNames[0];
|
|
||||||
if (idProp !== 'id') {
|
|
||||||
Object.defineProperty(ModelClass.prototype, 'id', {
|
|
||||||
get: function () {
|
|
||||||
var idProp = ModelClass.definition.idNames[0];
|
|
||||||
return this.__data[idProp];
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: false
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
return compositeId;
|
||||||
// Now the id property is an object that consists of multiple keys
|
},
|
||||||
Object.defineProperty(ModelClass.prototype, 'id', {
|
configurable: true,
|
||||||
get: function () {
|
enumerable: false
|
||||||
var compositeId = {};
|
});
|
||||||
var idNames = ModelClass.definition.idNames();
|
}
|
||||||
for (var p in idNames) {
|
|
||||||
compositeId[p] = this.__data[p];
|
// A function to loop through the properties
|
||||||
}
|
ModelClass.forEachProperty = function (cb) {
|
||||||
return compositeId;
|
Object.keys(ModelClass.definition.properties).forEach(cb);
|
||||||
},
|
};
|
||||||
configurable: true,
|
|
||||||
enumerable: false
|
// A function to attach the model class to a data source
|
||||||
});
|
ModelClass.attachTo = function (dataSource) {
|
||||||
|
dataSource.attach(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
// A function to extend the model
|
||||||
|
ModelClass.extend = function (className, subclassProperties, subclassSettings) {
|
||||||
|
var properties = ModelClass.definition.properties;
|
||||||
|
var settings = ModelClass.definition.settings;
|
||||||
|
|
||||||
|
subclassProperties = subclassProperties || {};
|
||||||
|
subclassSettings = subclassSettings || {};
|
||||||
|
|
||||||
|
// Check if subclass redefines the ids
|
||||||
|
var idFound = false;
|
||||||
|
for (var k in subclassProperties) {
|
||||||
|
if (subclassProperties[k].id) {
|
||||||
|
idFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A function to loop through the properties
|
// Merging the properties
|
||||||
ModelClass.forEachProperty = function (cb) {
|
Object.keys(properties).forEach(function (key) {
|
||||||
Object.keys(ModelClass.definition.properties).forEach(cb);
|
if (idFound && properties[key].id) {
|
||||||
};
|
// don't inherit id properties
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (subclassProperties[key] === undefined) {
|
||||||
|
subclassProperties[key] = properties[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// A function to attach the model class to a data source
|
// Merge the settings
|
||||||
ModelClass.attachTo = function (dataSource) {
|
subclassSettings = mergeSettings(settings, subclassSettings);
|
||||||
dataSource.attach(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
// A function to extend the model
|
/*
|
||||||
ModelClass.extend = function (className, subclassProperties, subclassSettings) {
|
Object.keys(settings).forEach(function (key) {
|
||||||
var properties = ModelClass.definition.properties;
|
if(subclassSettings[key] === undefined) {
|
||||||
var settings = ModelClass.definition.settings;
|
subclassSettings[key] = settings[key];
|
||||||
|
}
|
||||||
subclassProperties = subclassProperties || {};
|
});
|
||||||
subclassSettings = subclassSettings || {};
|
|
||||||
|
|
||||||
// Check if subclass redefines the ids
|
|
||||||
var idFound = false;
|
|
||||||
for(var k in subclassProperties) {
|
|
||||||
if(subclassProperties[k].id) {
|
|
||||||
idFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merging the properties
|
|
||||||
Object.keys(properties).forEach(function (key) {
|
|
||||||
if(idFound && properties[key].id) {
|
|
||||||
// don't inherit id properties
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(subclassProperties[key] === undefined) {
|
|
||||||
subclassProperties[key] = properties[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Merge the settings
|
|
||||||
subclassSettings = mergeSettings(settings, subclassSettings);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Object.keys(settings).forEach(function (key) {
|
|
||||||
if(subclassSettings[key] === undefined) {
|
|
||||||
subclassSettings[key] = settings[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Define the subclass
|
|
||||||
var subClass = modelBuilder.define(className, subclassProperties, subclassSettings, ModelClass);
|
|
||||||
|
|
||||||
// Calling the setup function
|
|
||||||
if(typeof subClass.setup === 'function') {
|
|
||||||
subClass.setup.call(subClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
return subClass;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a property for the model class
|
|
||||||
* @param propertyName
|
|
||||||
*/
|
*/
|
||||||
ModelClass.registerProperty = function (propertyName) {
|
|
||||||
var properties = modelDefinition.build();
|
// Define the subclass
|
||||||
var prop = properties[propertyName];
|
var subClass = modelBuilder.define(className, subclassProperties, subclassSettings, ModelClass);
|
||||||
var DataType = prop.type;
|
|
||||||
if(!DataType) {
|
// Calling the setup function
|
||||||
throw new Error('Invalid type for property ' + propertyName);
|
if (typeof subClass.setup === 'function') {
|
||||||
|
subClass.setup.call(subClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subClass;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a property for the model class
|
||||||
|
* @param propertyName
|
||||||
|
*/
|
||||||
|
ModelClass.registerProperty = function (propertyName) {
|
||||||
|
var properties = modelDefinition.build();
|
||||||
|
var prop = properties[propertyName];
|
||||||
|
var DataType = prop.type;
|
||||||
|
if (!DataType) {
|
||||||
|
throw new Error('Invalid type for property ' + propertyName);
|
||||||
|
}
|
||||||
|
if (Array.isArray(DataType) || DataType === Array) {
|
||||||
|
DataType = List;
|
||||||
|
} else if (DataType.name === 'Date') {
|
||||||
|
var OrigDate = Date;
|
||||||
|
DataType = function Date(arg) {
|
||||||
|
return new OrigDate(arg);
|
||||||
|
};
|
||||||
|
} else if (typeof DataType === 'string') {
|
||||||
|
DataType = modelBuilder.resolveType(DataType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop.required) {
|
||||||
|
var requiredOptions = typeof prop.required === 'object' ? prop.required : undefined;
|
||||||
|
ModelClass.validatesPresenceOf(propertyName, requiredOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(ModelClass.prototype, propertyName, {
|
||||||
|
get: function () {
|
||||||
|
if (ModelClass.getter[propertyName]) {
|
||||||
|
return ModelClass.getter[propertyName].call(this); // Try getter first
|
||||||
|
} else {
|
||||||
|
return this.__data && this.__data[propertyName]; // Try __data
|
||||||
}
|
}
|
||||||
if (Array.isArray(DataType) || DataType === Array) {
|
},
|
||||||
DataType = List;
|
set: function (value) {
|
||||||
} else if (DataType.name === 'Date') {
|
if (ModelClass.setter[propertyName]) {
|
||||||
var OrigDate = Date;
|
ModelClass.setter[propertyName].call(this, value); // Try setter first
|
||||||
DataType = function Date(arg) {
|
} else {
|
||||||
return new OrigDate(arg);
|
if (!this.__data) {
|
||||||
};
|
this.__data = {};
|
||||||
} else if(typeof DataType === 'string') {
|
}
|
||||||
DataType = modelBuilder.resolveType(DataType);
|
if (value === null || value === undefined) {
|
||||||
|
this.__data[propertyName] = value;
|
||||||
|
} else {
|
||||||
|
if (DataType === List) {
|
||||||
|
this.__data[propertyName] = DataType(value, properties[propertyName].type, this.__data);
|
||||||
|
} else {
|
||||||
|
// Assume the type constructor handles Constructor() call
|
||||||
|
// If not, we should call new DataType(value).valueOf();
|
||||||
|
this.__data[propertyName] = DataType(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
|
||||||
if(prop.required) {
|
// <propertyName>$was --> __dataWas.<propertyName>
|
||||||
var requiredOptions = typeof prop.required === 'object' ? prop.required : undefined;
|
Object.defineProperty(ModelClass.prototype, propertyName + '$was', {
|
||||||
ModelClass.validatesPresenceOf(propertyName, requiredOptions);
|
get: function () {
|
||||||
|
return this.__dataWas && this.__dataWas[propertyName];
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// FIXME: [rfeng] Do we need to keep the raw data?
|
||||||
|
// Use $ as the prefix to avoid conflicts with properties such as _id
|
||||||
|
Object.defineProperty(ModelClass.prototype, '$' + propertyName, {
|
||||||
|
get: function () {
|
||||||
|
return this.__data && this.__data[propertyName];
|
||||||
|
},
|
||||||
|
set: function (value) {
|
||||||
|
if (!this.__data) {
|
||||||
|
this.__data = {};
|
||||||
}
|
}
|
||||||
|
this.__data[propertyName] = value;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Object.defineProperty(ModelClass.prototype, propertyName, {
|
ModelClass.forEachProperty(ModelClass.registerProperty);
|
||||||
get: function () {
|
|
||||||
if (ModelClass.getter[propertyName]) {
|
|
||||||
return ModelClass.getter[propertyName].call(this); // Try getter first
|
|
||||||
} else {
|
|
||||||
return this.__data && this.__data[propertyName]; // Try __data
|
|
||||||
}
|
|
||||||
},
|
|
||||||
set: function (value) {
|
|
||||||
if (ModelClass.setter[propertyName]) {
|
|
||||||
ModelClass.setter[propertyName].call(this, value); // Try setter first
|
|
||||||
} else {
|
|
||||||
if (!this.__data) {
|
|
||||||
this.__data = {};
|
|
||||||
}
|
|
||||||
if (value === null || value === undefined) {
|
|
||||||
this.__data[propertyName] = value;
|
|
||||||
} else {
|
|
||||||
if(DataType === List) {
|
|
||||||
this.__data[propertyName] = DataType(value, properties[propertyName].type, this.__data);
|
|
||||||
} else {
|
|
||||||
// Assume the type constructor handles Constructor() call
|
|
||||||
// If not, we should call new DataType(value).valueOf();
|
|
||||||
this.__data[propertyName] = DataType(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// <propertyName>$was --> __dataWas.<propertyName>
|
ModelClass.emit('defined', ModelClass);
|
||||||
Object.defineProperty(ModelClass.prototype, propertyName + '$was', {
|
|
||||||
get: function () {
|
|
||||||
return this.__dataWas && this.__dataWas[propertyName];
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// FIXME: [rfeng] Do we need to keep the raw data?
|
return ModelClass;
|
||||||
// Use $ as the prefix to avoid conflicts with properties such as _id
|
|
||||||
Object.defineProperty(ModelClass.prototype, '$' + propertyName, {
|
|
||||||
get: function () {
|
|
||||||
return this.__data && this.__data[propertyName];
|
|
||||||
},
|
|
||||||
set: function (value) {
|
|
||||||
if (!this.__data) {
|
|
||||||
this.__data = {};
|
|
||||||
}
|
|
||||||
this.__data[propertyName] = value;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
ModelClass.forEachProperty(ModelClass.registerProperty);
|
|
||||||
|
|
||||||
ModelClass.emit('defined', ModelClass);
|
|
||||||
|
|
||||||
return ModelClass;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -428,8 +428,8 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
||||||
* @param {Object} propertyDefinition - property settings
|
* @param {Object} propertyDefinition - property settings
|
||||||
*/
|
*/
|
||||||
ModelBuilder.prototype.defineProperty = function (model, propertyName, propertyDefinition) {
|
ModelBuilder.prototype.defineProperty = function (model, propertyName, propertyDefinition) {
|
||||||
this.definitions[model].defineProperty(propertyName, propertyDefinition);
|
this.definitions[model].defineProperty(propertyName, propertyDefinition);
|
||||||
this.models[model].registerProperty(propertyName);
|
this.models[model].registerProperty(propertyName);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -456,69 +456,67 @@ ModelBuilder.prototype.defineProperty = function (model, propertyName, propertyD
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
ModelBuilder.prototype.extendModel = function (model, props) {
|
ModelBuilder.prototype.extendModel = function (model, props) {
|
||||||
var t = this;
|
var t = this;
|
||||||
Object.keys(props).forEach(function (propName) {
|
Object.keys(props).forEach(function (propName) {
|
||||||
var definition = props[propName];
|
var definition = props[propName];
|
||||||
t.defineProperty(model, propName, definition);
|
t.defineProperty(model, propName, definition);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
ModelBuilder.prototype.copyModel = function copyModel(Master) {
|
ModelBuilder.prototype.copyModel = function copyModel(Master) {
|
||||||
var modelBuilder = this;
|
var modelBuilder = this;
|
||||||
var className = Master.modelName;
|
var className = Master.modelName;
|
||||||
var md = Master.modelBuilder.definitions[className];
|
var md = Master.modelBuilder.definitions[className];
|
||||||
var Slave = function SlaveModel() {
|
var Slave = function SlaveModel() {
|
||||||
Master.apply(this, [].slice.call(arguments));
|
Master.apply(this, [].slice.call(arguments));
|
||||||
|
};
|
||||||
|
|
||||||
|
util.inherits(Slave, Master);
|
||||||
|
|
||||||
|
Slave.__proto__ = Master;
|
||||||
|
|
||||||
|
hiddenProperty(Slave, 'modelBuilder', modelBuilder);
|
||||||
|
hiddenProperty(Slave, 'modelName', className);
|
||||||
|
hiddenProperty(Slave, 'relations', Master.relations);
|
||||||
|
|
||||||
|
if (!(className in modelBuilder.models)) {
|
||||||
|
|
||||||
|
// store class in model pool
|
||||||
|
modelBuilder.models[className] = Slave;
|
||||||
|
modelBuilder.definitions[className] = {
|
||||||
|
properties: md.properties,
|
||||||
|
settings: md.settings
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
util.inherits(Slave, Master);
|
return Slave;
|
||||||
|
|
||||||
Slave.__proto__ = Master;
|
|
||||||
|
|
||||||
hiddenProperty(Slave, 'modelBuilder', modelBuilder);
|
|
||||||
hiddenProperty(Slave, 'modelName', className);
|
|
||||||
hiddenProperty(Slave, 'relations', Master.relations);
|
|
||||||
|
|
||||||
if (!(className in modelBuilder.models)) {
|
|
||||||
|
|
||||||
// store class in model pool
|
|
||||||
modelBuilder.models[className] = Slave;
|
|
||||||
modelBuilder.definitions[className] = {
|
|
||||||
properties: md.properties,
|
|
||||||
settings: md.settings
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return Slave;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Define hidden property
|
* Define hidden property
|
||||||
*/
|
*/
|
||||||
function hiddenProperty(where, property, value) {
|
function hiddenProperty(where, property, value) {
|
||||||
Object.defineProperty(where, property, {
|
Object.defineProperty(where, property, {
|
||||||
writable: true,
|
writable: true,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
value: value
|
value: value
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the schema name
|
* Get the schema name
|
||||||
*/
|
*/
|
||||||
ModelBuilder.prototype.getSchemaName = function (name) {
|
ModelBuilder.prototype.getSchemaName = function (name) {
|
||||||
if (name) {
|
if (name) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
if (typeof this._nameCount !== 'number') {
|
if (typeof this._nameCount !== 'number') {
|
||||||
this._nameCount = 0;
|
this._nameCount = 0;
|
||||||
} else {
|
} else {
|
||||||
this._nameCount++;
|
this._nameCount++;
|
||||||
}
|
}
|
||||||
return 'AnonymousModel_' + this._nameCount;
|
return 'AnonymousModel_' + this._nameCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -526,41 +524,41 @@ ModelBuilder.prototype.getSchemaName = function (name) {
|
||||||
* @param {String} type The type string, such as 'number', 'Number', 'boolean', or 'String'. It's case insensitive
|
* @param {String} type The type string, such as 'number', 'Number', 'boolean', or 'String'. It's case insensitive
|
||||||
* @returns {Function} if the type is resolved
|
* @returns {Function} if the type is resolved
|
||||||
*/
|
*/
|
||||||
ModelBuilder.prototype.resolveType = function(type) {
|
ModelBuilder.prototype.resolveType = function (type) {
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return type;
|
|
||||||
}
|
|
||||||
if (Array.isArray(type) && type.length > 0) {
|
|
||||||
// For array types, the first item should be the type string
|
|
||||||
var itemType = this.resolveType(type[0]);
|
|
||||||
if (typeof itemType === 'function') {
|
|
||||||
return [itemType];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return itemType; // Not resolved, return the type string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof type === 'string') {
|
|
||||||
var schemaType = ModelBuilder.schemaTypes[type.toLowerCase()] || this.models[type];
|
|
||||||
if (schemaType) {
|
|
||||||
return schemaType;
|
|
||||||
} else {
|
|
||||||
// The type cannot be resolved, let's create a place holder
|
|
||||||
type = this.define(type, {}, {unresolved: true});
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
} else if (type.constructor.name === 'Object') {
|
|
||||||
// We also support the syntax {type: 'string', ...}
|
|
||||||
if (type.type) {
|
|
||||||
return this.resolveType(type.type);
|
|
||||||
} else {
|
|
||||||
return this.define(this.getSchemaName(null),
|
|
||||||
type, {anonymous: true, idInjection: false});
|
|
||||||
}
|
|
||||||
} else if('function' === typeof type ) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
return type;
|
return type;
|
||||||
|
}
|
||||||
|
if (Array.isArray(type) && type.length > 0) {
|
||||||
|
// For array types, the first item should be the type string
|
||||||
|
var itemType = this.resolveType(type[0]);
|
||||||
|
if (typeof itemType === 'function') {
|
||||||
|
return [itemType];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return itemType; // Not resolved, return the type string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof type === 'string') {
|
||||||
|
var schemaType = ModelBuilder.schemaTypes[type.toLowerCase()] || this.models[type];
|
||||||
|
if (schemaType) {
|
||||||
|
return schemaType;
|
||||||
|
} else {
|
||||||
|
// The type cannot be resolved, let's create a place holder
|
||||||
|
type = this.define(type, {}, {unresolved: true});
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
} else if (type.constructor.name === 'Object') {
|
||||||
|
// We also support the syntax {type: 'string', ...}
|
||||||
|
if (type.type) {
|
||||||
|
return this.resolveType(type.type);
|
||||||
|
} else {
|
||||||
|
return this.define(this.getSchemaName(null),
|
||||||
|
type, {anonymous: true, idInjection: false});
|
||||||
|
}
|
||||||
|
} else if ('function' === typeof type) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -576,46 +574,46 @@ ModelBuilder.prototype.resolveType = function(type) {
|
||||||
* @returns {Object} A map of model constructors keyed by model name
|
* @returns {Object} A map of model constructors keyed by model name
|
||||||
*/
|
*/
|
||||||
ModelBuilder.prototype.buildModels = function (schemas) {
|
ModelBuilder.prototype.buildModels = function (schemas) {
|
||||||
var models = {};
|
var models = {};
|
||||||
|
|
||||||
// Normalize the schemas to be an array of the schema objects {name: <name>, properties: {}, options: {}}
|
// Normalize the schemas to be an array of the schema objects {name: <name>, properties: {}, options: {}}
|
||||||
if (!Array.isArray(schemas)) {
|
if (!Array.isArray(schemas)) {
|
||||||
if (schemas.properties && schemas.name) {
|
if (schemas.properties && schemas.name) {
|
||||||
// Only one item
|
// Only one item
|
||||||
schemas = [schemas];
|
schemas = [schemas];
|
||||||
} else {
|
} else {
|
||||||
// Anonymous schema
|
// Anonymous schema
|
||||||
schemas = [
|
schemas = [
|
||||||
{
|
{
|
||||||
name: this.getSchemaName(),
|
name: this.getSchemaName(),
|
||||||
properties: schemas,
|
properties: schemas,
|
||||||
options: {anonymous: true}
|
options: {anonymous: true}
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var relations = [];
|
var relations = [];
|
||||||
for (var s in schemas) {
|
for (var s in schemas) {
|
||||||
var name = this.getSchemaName(schemas[s].name);
|
var name = this.getSchemaName(schemas[s].name);
|
||||||
schemas[s].name = name;
|
schemas[s].name = name;
|
||||||
var model = this.define(schemas[s].name, schemas[s].properties, schemas[s].options);
|
var model = this.define(schemas[s].name, schemas[s].properties, schemas[s].options);
|
||||||
models[name] = model;
|
models[name] = model;
|
||||||
relations = relations.concat(model.definition.relations);
|
relations = relations.concat(model.definition.relations);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect the models based on the relations
|
// Connect the models based on the relations
|
||||||
for (var i = 0; i < relations.length; i++) {
|
for (var i = 0; i < relations.length; i++) {
|
||||||
var relation = relations[i];
|
var relation = relations[i];
|
||||||
var sourceModel = models[relation.source];
|
var sourceModel = models[relation.source];
|
||||||
var targetModel = models[relation.target];
|
var targetModel = models[relation.target];
|
||||||
if (sourceModel && targetModel) {
|
if (sourceModel && targetModel) {
|
||||||
if(typeof sourceModel[relation.type] === 'function') {
|
if (typeof sourceModel[relation.type] === 'function') {
|
||||||
sourceModel[relation.type](targetModel, {as: relation.as});
|
sourceModel[relation.type](targetModel, {as: relation.as});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return models;
|
}
|
||||||
|
return models;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -625,13 +623,13 @@ ModelBuilder.prototype.buildModels = function (schemas) {
|
||||||
* @param [Object} options The options
|
* @param [Object} options The options
|
||||||
* @returns {}
|
* @returns {}
|
||||||
*/
|
*/
|
||||||
ModelBuilder.prototype.buildModelFromInstance = function(name, json, options) {
|
ModelBuilder.prototype.buildModelFromInstance = function (name, json, options) {
|
||||||
|
|
||||||
// Introspect the JSON document to generate a schema
|
// Introspect the JSON document to generate a schema
|
||||||
var schema = introspect(json);
|
var schema = introspect(json);
|
||||||
|
|
||||||
// Create a model for the generated schema
|
// Create a model for the generated schema
|
||||||
return this.define(name, schema, options);
|
return this.define(name, schema, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,27 +21,27 @@ module.exports = ModelDefinition;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function ModelDefinition(modelBuilder, name, properties, settings) {
|
function ModelDefinition(modelBuilder, name, properties, settings) {
|
||||||
if (!(this instanceof ModelDefinition)) {
|
if (!(this instanceof ModelDefinition)) {
|
||||||
// Allow to call ModelDefinition without new
|
// Allow to call ModelDefinition without new
|
||||||
return new ModelDefinition(modelBuilder, name, properties, settings);
|
return new ModelDefinition(modelBuilder, name, properties, settings);
|
||||||
}
|
}
|
||||||
this.modelBuilder = modelBuilder || ModelBuilder.defaultInstance;
|
this.modelBuilder = modelBuilder || ModelBuilder.defaultInstance;
|
||||||
assert(name, 'name is missing');
|
assert(name, 'name is missing');
|
||||||
|
|
||||||
if (arguments.length === 2 && typeof name === 'object') {
|
if (arguments.length === 2 && typeof name === 'object') {
|
||||||
var schema = name;
|
var schema = name;
|
||||||
this.name = schema.name;
|
this.name = schema.name;
|
||||||
this.rawProperties = schema.properties || {}; // Keep the raw property definitions
|
this.rawProperties = schema.properties || {}; // Keep the raw property definitions
|
||||||
this.settings = schema.settings || {};
|
this.settings = schema.settings || {};
|
||||||
} else {
|
} else {
|
||||||
assert(typeof name === 'string', 'name must be a string');
|
assert(typeof name === 'string', 'name must be a string');
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.rawProperties = properties || {}; // Keep the raw property definitions
|
this.rawProperties = properties || {}; // Keep the raw property definitions
|
||||||
this.settings = settings || {};
|
this.settings = settings || {};
|
||||||
}
|
}
|
||||||
this.relations = [];
|
this.relations = [];
|
||||||
this.properties = null;
|
this.properties = null;
|
||||||
this.build();
|
this.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(ModelDefinition, EventEmitter);
|
util.inherits(ModelDefinition, EventEmitter);
|
||||||
|
@ -49,18 +49,17 @@ util.inherits(ModelDefinition, EventEmitter);
|
||||||
// Set up types
|
// Set up types
|
||||||
require('./types')(ModelDefinition);
|
require('./types')(ModelDefinition);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return table name for specified `modelName`
|
* Return table name for specified `modelName`
|
||||||
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
|
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.tableName = function (connectorType) {
|
ModelDefinition.prototype.tableName = function (connectorType) {
|
||||||
var settings = this.settings;
|
var settings = this.settings;
|
||||||
if(settings[connectorType]) {
|
if (settings[connectorType]) {
|
||||||
return settings[connectorType].table || settings[connectorType].tableName || this.name;
|
return settings[connectorType].table || settings[connectorType].tableName || this.name;
|
||||||
} else {
|
} else {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,16 +69,16 @@ ModelDefinition.prototype.tableName = function (connectorType) {
|
||||||
* @returns {String} columnName
|
* @returns {String} columnName
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.columnName = function (connectorType, propertyName) {
|
ModelDefinition.prototype.columnName = function (connectorType, propertyName) {
|
||||||
if(!propertyName) {
|
if (!propertyName) {
|
||||||
return propertyName;
|
return propertyName;
|
||||||
}
|
}
|
||||||
this.build();
|
this.build();
|
||||||
var property = this.properties[propertyName];
|
var property = this.properties[propertyName];
|
||||||
if(property && property[connectorType]) {
|
if (property && property[connectorType]) {
|
||||||
return property[connectorType].column || property[connectorType].columnName || propertyName;
|
return property[connectorType].column || property[connectorType].columnName || propertyName;
|
||||||
} else {
|
} else {
|
||||||
return propertyName;
|
return propertyName;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,16 +88,16 @@ ModelDefinition.prototype.columnName = function (connectorType, propertyName) {
|
||||||
* @returns {Object} column metadata
|
* @returns {Object} column metadata
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.columnMetadata = function (connectorType, propertyName) {
|
ModelDefinition.prototype.columnMetadata = function (connectorType, propertyName) {
|
||||||
if(!propertyName) {
|
if (!propertyName) {
|
||||||
return propertyName;
|
return propertyName;
|
||||||
}
|
}
|
||||||
this.build();
|
this.build();
|
||||||
var property = this.properties[propertyName];
|
var property = this.properties[propertyName];
|
||||||
if(property && property[connectorType]) {
|
if (property && property[connectorType]) {
|
||||||
return property[connectorType];
|
return property[connectorType];
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,17 +106,17 @@ ModelDefinition.prototype.columnMetadata = function (connectorType, propertyName
|
||||||
* @returns {String[]} column names
|
* @returns {String[]} column names
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.columnNames = function (connectorType) {
|
ModelDefinition.prototype.columnNames = function (connectorType) {
|
||||||
this.build();
|
this.build();
|
||||||
var props = this.properties;
|
var props = this.properties;
|
||||||
var cols = [];
|
var cols = [];
|
||||||
for(var p in props) {
|
for (var p in props) {
|
||||||
if(props[p][connectorType]) {
|
if (props[p][connectorType]) {
|
||||||
cols.push(property[connectorType].column || props[p][connectorType].columnName || p);
|
cols.push(property[connectorType].column || props[p][connectorType].columnName || p);
|
||||||
} else {
|
} else {
|
||||||
cols.push(p);
|
cols.push(p);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return cols;
|
}
|
||||||
|
return cols;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,27 +124,27 @@ ModelDefinition.prototype.columnNames = function (connectorType) {
|
||||||
* @returns {Object[]} property name/index for IDs
|
* @returns {Object[]} property name/index for IDs
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.ids = function () {
|
ModelDefinition.prototype.ids = function () {
|
||||||
if(this._ids) {
|
if (this._ids) {
|
||||||
return this._ids;
|
return this._ids;
|
||||||
|
}
|
||||||
|
var ids = [];
|
||||||
|
this.build();
|
||||||
|
var props = this.properties;
|
||||||
|
for (var key in props) {
|
||||||
|
var id = props[key].id;
|
||||||
|
if (!id) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
var ids = [];
|
if (typeof id !== 'number') {
|
||||||
this.build();
|
id = 1;
|
||||||
var props = this.properties;
|
|
||||||
for (var key in props) {
|
|
||||||
var id = props[key].id;
|
|
||||||
if(!id) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(typeof id !== 'number') {
|
|
||||||
id = 1;
|
|
||||||
}
|
|
||||||
ids.push({name: key, id: id});
|
|
||||||
}
|
}
|
||||||
ids.sort(function (a, b) {
|
ids.push({name: key, id: id});
|
||||||
return a.key - b.key;
|
}
|
||||||
});
|
ids.sort(function (a, b) {
|
||||||
this._ids = ids;
|
return a.key - b.key;
|
||||||
return ids;
|
});
|
||||||
|
this._ids = ids;
|
||||||
|
return ids;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -153,20 +152,21 @@ ModelDefinition.prototype.ids = function () {
|
||||||
* @param {String} modelName The model name
|
* @param {String} modelName The model name
|
||||||
* @returns {String} columnName for ID
|
* @returns {String} columnName for ID
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.idColumnName = function(connectorType) {
|
ModelDefinition.prototype.idColumnName = function (connectorType) {
|
||||||
return this.columnName(connectorType, this.idName());
|
return this.columnName(connectorType, this.idName());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the ID property name
|
* Find the ID property name
|
||||||
* @returns {String} property name for ID
|
* @returns {String} property name for ID
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.idName = function() {
|
ModelDefinition.prototype.idName = function () {
|
||||||
var id = this.ids()[0];
|
var id = this.ids()[0];
|
||||||
if(this.properties.id && this.properties.id.id) {
|
if (this.properties.id && this.properties.id.id) {
|
||||||
return 'id';
|
return 'id';
|
||||||
} else {}
|
} else {
|
||||||
return id && id.name;
|
}
|
||||||
|
return id && id.name;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,11 +174,11 @@ ModelDefinition.prototype.idName = function() {
|
||||||
* @returns {String[]} property names for IDs
|
* @returns {String[]} property names for IDs
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.idNames = function () {
|
ModelDefinition.prototype.idNames = function () {
|
||||||
var ids = this.ids();
|
var ids = this.ids();
|
||||||
var names = ids.map(function (id) {
|
var names = ids.map(function (id) {
|
||||||
return id.name;
|
return id.name;
|
||||||
});
|
});
|
||||||
return names;
|
return names;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -186,19 +186,19 @@ ModelDefinition.prototype.idNames = function () {
|
||||||
* @returns {{}}
|
* @returns {{}}
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.indexes = function () {
|
ModelDefinition.prototype.indexes = function () {
|
||||||
this.build();
|
this.build();
|
||||||
var indexes = {};
|
var indexes = {};
|
||||||
if (this.settings.indexes) {
|
if (this.settings.indexes) {
|
||||||
for (var i in this.settings.indexes) {
|
for (var i in this.settings.indexes) {
|
||||||
indexes[i] = this.settings.indexes[i];
|
indexes[i] = this.settings.indexes[i];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (var p in this.properties) {
|
}
|
||||||
if (this.properties[p].index) {
|
for (var p in this.properties) {
|
||||||
indexes[p + '_index'] = this.properties[p].index;
|
if (this.properties[p].index) {
|
||||||
}
|
indexes[p + '_index'] = this.properties[p].index;
|
||||||
}
|
}
|
||||||
return indexes;
|
}
|
||||||
|
return indexes;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -206,41 +206,41 @@ ModelDefinition.prototype.indexes = function () {
|
||||||
* @param {Boolean} force Forcing rebuild
|
* @param {Boolean} force Forcing rebuild
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.build = function (forceRebuild) {
|
ModelDefinition.prototype.build = function (forceRebuild) {
|
||||||
if(forceRebuild) {
|
if (forceRebuild) {
|
||||||
this.properties = null;
|
this.properties = null;
|
||||||
this.relations = [];
|
this.relations = [];
|
||||||
this._ids = null;
|
this._ids = null;
|
||||||
}
|
}
|
||||||
if (this.properties) {
|
if (this.properties) {
|
||||||
return this.properties;
|
|
||||||
}
|
|
||||||
this.properties = {};
|
|
||||||
for (var p in this.rawProperties) {
|
|
||||||
var prop = this.rawProperties[p];
|
|
||||||
var type = this.modelBuilder.resolveType(prop);
|
|
||||||
if (typeof type === 'string') {
|
|
||||||
this.relations.push({
|
|
||||||
source: this.name,
|
|
||||||
target: type,
|
|
||||||
type: Array.isArray(prop) ? 'hasMany' : 'belongsTo',
|
|
||||||
as: p
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
var typeDef = {
|
|
||||||
type: type
|
|
||||||
};
|
|
||||||
if (typeof prop === 'object' && prop !== null) {
|
|
||||||
for (var a in prop) {
|
|
||||||
// Skip the type property but don't delete it Model.extend() shares same instances of the properties from the base class
|
|
||||||
if (a !== 'type') {
|
|
||||||
typeDef[a] = prop[a];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.properties[p] = typeDef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.properties;
|
return this.properties;
|
||||||
|
}
|
||||||
|
this.properties = {};
|
||||||
|
for (var p in this.rawProperties) {
|
||||||
|
var prop = this.rawProperties[p];
|
||||||
|
var type = this.modelBuilder.resolveType(prop);
|
||||||
|
if (typeof type === 'string') {
|
||||||
|
this.relations.push({
|
||||||
|
source: this.name,
|
||||||
|
target: type,
|
||||||
|
type: Array.isArray(prop) ? 'hasMany' : 'belongsTo',
|
||||||
|
as: p
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var typeDef = {
|
||||||
|
type: type
|
||||||
|
};
|
||||||
|
if (typeof prop === 'object' && prop !== null) {
|
||||||
|
for (var a in prop) {
|
||||||
|
// Skip the type property but don't delete it Model.extend() shares same instances of the properties from the base class
|
||||||
|
if (a !== 'type') {
|
||||||
|
typeDef[a] = prop[a];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.properties[p] = typeDef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -249,56 +249,55 @@ ModelDefinition.prototype.build = function (forceRebuild) {
|
||||||
* @param {Object} propertyDefinition The property definition
|
* @param {Object} propertyDefinition The property definition
|
||||||
*/
|
*/
|
||||||
ModelDefinition.prototype.defineProperty = function (propertyName, propertyDefinition) {
|
ModelDefinition.prototype.defineProperty = function (propertyName, propertyDefinition) {
|
||||||
this.rawProperties[propertyName] = propertyDefinition;
|
this.rawProperties[propertyName] = propertyDefinition;
|
||||||
this.build(true);
|
this.build(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function isModelClass(cls) {
|
function isModelClass(cls) {
|
||||||
if(!cls) {
|
if (!cls) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return cls.prototype instanceof ModelBaseClass;
|
return cls.prototype instanceof ModelBaseClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelDefinition.prototype.toJSON = function(forceRebuild) {
|
ModelDefinition.prototype.toJSON = function (forceRebuild) {
|
||||||
if(forceRebuild) {
|
if (forceRebuild) {
|
||||||
this.json = null;
|
this.json = null;
|
||||||
}
|
}
|
||||||
if(this.json) {
|
if (this.json) {
|
||||||
return json;
|
|
||||||
}
|
|
||||||
var json = {
|
|
||||||
name: this.name,
|
|
||||||
properties: {},
|
|
||||||
settings: this.settings
|
|
||||||
};
|
|
||||||
this.build(forceRebuild);
|
|
||||||
|
|
||||||
var mapper = function(val) {
|
|
||||||
if(val === undefined || val === null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
if('function' === typeof val.toJSON) {
|
|
||||||
// The value has its own toJSON() object
|
|
||||||
return val.toJSON();
|
|
||||||
}
|
|
||||||
if('function' === typeof val) {
|
|
||||||
if(isModelClass(val)) {
|
|
||||||
if(val.settings && val.settings.anonymous) {
|
|
||||||
return val.definition && val.definition.toJSON().properties;
|
|
||||||
} else {
|
|
||||||
return val.modelName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return val.name;
|
|
||||||
} else {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for(var p in this.properties) {
|
|
||||||
json.properties[p] = traverse(this.properties[p]).map(mapper);
|
|
||||||
}
|
|
||||||
this.json = json;
|
|
||||||
return json;
|
return json;
|
||||||
|
}
|
||||||
|
var json = {
|
||||||
|
name: this.name,
|
||||||
|
properties: {},
|
||||||
|
settings: this.settings
|
||||||
|
};
|
||||||
|
this.build(forceRebuild);
|
||||||
|
|
||||||
|
var mapper = function (val) {
|
||||||
|
if (val === undefined || val === null) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
if ('function' === typeof val.toJSON) {
|
||||||
|
// The value has its own toJSON() object
|
||||||
|
return val.toJSON();
|
||||||
|
}
|
||||||
|
if ('function' === typeof val) {
|
||||||
|
if (isModelClass(val)) {
|
||||||
|
if (val.settings && val.settings.anonymous) {
|
||||||
|
return val.definition && val.definition.toJSON().properties;
|
||||||
|
} else {
|
||||||
|
return val.modelName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val.name;
|
||||||
|
} else {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (var p in this.properties) {
|
||||||
|
json.properties[p] = traverse(this.properties[p]).map(mapper);
|
||||||
|
}
|
||||||
|
this.json = json;
|
||||||
|
return json;
|
||||||
};
|
};
|
||||||
|
|
330
lib/model.js
330
lib/model.js
|
@ -28,19 +28,19 @@ var BASE_TYPES = ['String', 'Boolean', 'Number', 'Date', 'Text'];
|
||||||
* @param {Object} data - initial object data
|
* @param {Object} data - initial object data
|
||||||
*/
|
*/
|
||||||
function ModelBaseClass(data) {
|
function ModelBaseClass(data) {
|
||||||
this._initProperties(data, true);
|
this._initProperties(data, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: [rfeng] We need to make sure the input data should not be mutated. Disabled cloning for now to get tests passing
|
// FIXME: [rfeng] We need to make sure the input data should not be mutated. Disabled cloning for now to get tests passing
|
||||||
function clone(data) {
|
function clone(data) {
|
||||||
/*
|
/*
|
||||||
if(!(data instanceof ModelBaseClass)) {
|
if(!(data instanceof ModelBaseClass)) {
|
||||||
if(data && (Array.isArray(data) || 'object' === typeof data)) {
|
if(data && (Array.isArray(data) || 'object' === typeof data)) {
|
||||||
return traverse(data).clone();
|
return traverse(data).clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Initialize properties
|
* Initialize properties
|
||||||
|
@ -49,117 +49,117 @@ function clone(data) {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
ModelBaseClass.prototype._initProperties = function (data, applySetters) {
|
ModelBaseClass.prototype._initProperties = function (data, applySetters) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var ctor = this.constructor;
|
var ctor = this.constructor;
|
||||||
|
|
||||||
var properties = ctor.definition.build();
|
var properties = ctor.definition.build();
|
||||||
data = data || {};
|
data = data || {};
|
||||||
|
|
||||||
Object.defineProperty(this, '__cachedRelations', {
|
Object.defineProperty(this, '__cachedRelations', {
|
||||||
writable: true,
|
writable: true,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
value: {}
|
value: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(this, '__data', {
|
Object.defineProperty(this, '__data', {
|
||||||
writable: true,
|
writable: true,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
value: {}
|
value: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(this, '__dataWas', {
|
Object.defineProperty(this, '__dataWas', {
|
||||||
writable: true,
|
writable: true,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
value: {}
|
value: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data['__cachedRelations']) {
|
if (data['__cachedRelations']) {
|
||||||
this.__cachedRelations = data['__cachedRelations'];
|
this.__cachedRelations = data['__cachedRelations'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the strict option is set to false for the model
|
||||||
|
var strict = ctor.definition.settings.strict;
|
||||||
|
|
||||||
|
for (var i in data) {
|
||||||
|
if (i in properties) {
|
||||||
|
this.__data[i] = this.__dataWas[i] = clone(data[i]);
|
||||||
|
} else if (i in ctor.relations) {
|
||||||
|
this.__data[ctor.relations[i].keyFrom] = this.__dataWas[i] = data[i][ctor.relations[i].keyTo];
|
||||||
|
this.__cachedRelations[i] = data[i];
|
||||||
|
} else {
|
||||||
|
if (strict === false) {
|
||||||
|
this.__data[i] = this.__dataWas[i] = clone(data[i]);
|
||||||
|
} else if (strict === 'throw') {
|
||||||
|
throw new Error('Unknown property: ' + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applySetters === true) {
|
||||||
|
for (var propertyName in data) {
|
||||||
|
if ((propertyName in properties) || (propertyName in ctor.relations)) {
|
||||||
|
self[propertyName] = self.__data[propertyName] || data[propertyName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the unknown properties as properties to the object
|
||||||
|
if (strict === false) {
|
||||||
|
for (var propertyName in data) {
|
||||||
|
if (!(propertyName in properties)) {
|
||||||
|
self[propertyName] = self.__data[propertyName] || data[propertyName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctor.forEachProperty(function (propertyName) {
|
||||||
|
|
||||||
|
if ('undefined' === typeof self.__data[propertyName]) {
|
||||||
|
self.__data[propertyName] = self.__dataWas[propertyName] = getDefault(propertyName);
|
||||||
|
} else {
|
||||||
|
self.__dataWas[propertyName] = self.__data[propertyName];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the strict option is set to false for the model
|
});
|
||||||
var strict = ctor.definition.settings.strict;
|
|
||||||
|
|
||||||
for (var i in data) {
|
ctor.forEachProperty(function (propertyName) {
|
||||||
if (i in properties) {
|
|
||||||
this.__data[i] = this.__dataWas[i] = clone(data[i]);
|
var type = properties[propertyName].type;
|
||||||
} else if (i in ctor.relations) {
|
|
||||||
this.__data[ctor.relations[i].keyFrom] = this.__dataWas[i] = data[i][ctor.relations[i].keyTo];
|
if (BASE_TYPES.indexOf(type.name) === -1) {
|
||||||
this.__cachedRelations[i] = data[i];
|
if (typeof self.__data[propertyName] !== 'object' && self.__data[propertyName]) {
|
||||||
} else {
|
try {
|
||||||
if(strict === false) {
|
self.__data[propertyName] = JSON.parse(self.__data[propertyName] + '');
|
||||||
this.__data[i] = this.__dataWas[i] = clone(data[i]);
|
} catch (e) {
|
||||||
} else if(strict === 'throw') {
|
self.__data[propertyName] = String(self.__data[propertyName]);
|
||||||
throw new Error('Unknown property: ' + i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (type.name === 'Array' || Array.isArray(type)) {
|
||||||
|
if (!(self.__data[propertyName] instanceof List)) {
|
||||||
|
self.__data[propertyName] = new List(self.__data[propertyName], type, self);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (applySetters === true) {
|
});
|
||||||
for(var propertyName in data) {
|
|
||||||
if((propertyName in properties) || (propertyName in ctor.relations)) {
|
function getDefault(propertyName) {
|
||||||
self[propertyName] = self.__data[propertyName] || data[propertyName];
|
var def = properties[propertyName]['default'];
|
||||||
}
|
if (def !== undefined) {
|
||||||
}
|
if (typeof def === 'function') {
|
||||||
|
return def();
|
||||||
|
} else {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set the unknown properties as properties to the object
|
this.trigger('initialize');
|
||||||
if(strict === false) {
|
|
||||||
for(var propertyName in data) {
|
|
||||||
if(!(propertyName in properties)) {
|
|
||||||
self[propertyName] = self.__data[propertyName] || data[propertyName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctor.forEachProperty(function (propertyName) {
|
|
||||||
|
|
||||||
if ('undefined' === typeof self.__data[propertyName]) {
|
|
||||||
self.__data[propertyName] = self.__dataWas[propertyName] = getDefault(propertyName);
|
|
||||||
} else {
|
|
||||||
self.__dataWas[propertyName] = self.__data[propertyName];
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
ctor.forEachProperty(function (propertyName) {
|
|
||||||
|
|
||||||
var type = properties[propertyName].type;
|
|
||||||
|
|
||||||
if (BASE_TYPES.indexOf(type.name) === -1) {
|
|
||||||
if (typeof self.__data[propertyName] !== 'object' && self.__data[propertyName]) {
|
|
||||||
try {
|
|
||||||
self.__data[propertyName] = JSON.parse(self.__data[propertyName] + '');
|
|
||||||
} catch (e) {
|
|
||||||
self.__data[propertyName] = String(self.__data[propertyName]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (type.name === 'Array' || Array.isArray(type)) {
|
|
||||||
if(!(self.__data[propertyName] instanceof List)) {
|
|
||||||
self.__data[propertyName] = new List(self.__data[propertyName], type, self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
function getDefault(propertyName) {
|
|
||||||
var def = properties[propertyName]['default'];
|
|
||||||
if (def !== undefined) {
|
|
||||||
if (typeof def === 'function') {
|
|
||||||
return def();
|
|
||||||
} else {
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.trigger('initialize');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -167,24 +167,24 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
|
||||||
* @param {Object} params - various property configuration
|
* @param {Object} params - various property configuration
|
||||||
*/
|
*/
|
||||||
ModelBaseClass.defineProperty = function (prop, params) {
|
ModelBaseClass.defineProperty = function (prop, params) {
|
||||||
this.dataSource.defineProperty(this.modelName, prop, params);
|
this.dataSource.defineProperty(this.modelName, prop, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
ModelBaseClass.getPropertyType = function (propName) {
|
ModelBaseClass.getPropertyType = function (propName) {
|
||||||
var prop = this.definition.properties[propName];
|
var prop = this.definition.properties[propName];
|
||||||
if(!prop) {
|
if (!prop) {
|
||||||
// The property is not part of the definition
|
// The property is not part of the definition
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!prop.type) {
|
if (!prop.type) {
|
||||||
throw new Error('Type not defined for property ' + this.modelName + '.' + propName);
|
throw new Error('Type not defined for property ' + this.modelName + '.' + propName);
|
||||||
// return null;
|
// return null;
|
||||||
}
|
}
|
||||||
return prop.type.name;
|
return prop.type.name;
|
||||||
};
|
};
|
||||||
|
|
||||||
ModelBaseClass.prototype.getPropertyType = function (propName) {
|
ModelBaseClass.prototype.getPropertyType = function (propName) {
|
||||||
return this.constructor.getPropertyType(propName);
|
return this.constructor.getPropertyType(propName);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,7 +193,7 @@ ModelBaseClass.prototype.getPropertyType = function (propName) {
|
||||||
* @override default toString method
|
* @override default toString method
|
||||||
*/
|
*/
|
||||||
ModelBaseClass.toString = function () {
|
ModelBaseClass.toString = function () {
|
||||||
return '[Model ' + this.modelName + ']';
|
return '[Model ' + this.modelName + ']';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -205,37 +205,37 @@ ModelBaseClass.toString = function () {
|
||||||
* @returns {Object} - canonical object representation (no getters and setters)
|
* @returns {Object} - canonical object representation (no getters and setters)
|
||||||
*/
|
*/
|
||||||
ModelBaseClass.prototype.toObject = function (onlySchema) {
|
ModelBaseClass.prototype.toObject = function (onlySchema) {
|
||||||
var data = {};
|
var data = {};
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var schemaLess = this.constructor.definition.settings.strict === false || !onlySchema;
|
var schemaLess = this.constructor.definition.settings.strict === false || !onlySchema;
|
||||||
this.constructor.forEachProperty(function (propertyName) {
|
this.constructor.forEachProperty(function (propertyName) {
|
||||||
if (self[propertyName] instanceof List) {
|
if (self[propertyName] instanceof List) {
|
||||||
data[propertyName] = self[propertyName].toObject(!schemaLess);
|
data[propertyName] = self[propertyName].toObject(!schemaLess);
|
||||||
} else if (self.__data.hasOwnProperty(propertyName)) {
|
} else if (self.__data.hasOwnProperty(propertyName)) {
|
||||||
if(self[propertyName] !== undefined && self[propertyName]!== null && self[propertyName].toObject) {
|
if (self[propertyName] !== undefined && self[propertyName] !== null && self[propertyName].toObject) {
|
||||||
data[propertyName] = self[propertyName].toObject(!schemaLess);
|
data[propertyName] = self[propertyName].toObject(!schemaLess);
|
||||||
} else {
|
} else {
|
||||||
data[propertyName] = self[propertyName];
|
data[propertyName] = self[propertyName];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
data[propertyName] = null;
|
data[propertyName] = null;
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (schemaLess) {
|
|
||||||
for(var propertyName in self.__data) {
|
|
||||||
if (!data.hasOwnProperty(propertyName)) {
|
|
||||||
var val = self.hasOwnProperty(propertyName) ? self[propertyName] : self.__data[propertyName];
|
|
||||||
if(val !== undefined && val!== null && val.toObject) {
|
|
||||||
data[propertyName] = val.toObject(!schemaLess);
|
|
||||||
} else {
|
|
||||||
data[propertyName] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return data;
|
});
|
||||||
|
|
||||||
|
if (schemaLess) {
|
||||||
|
for (var propertyName in self.__data) {
|
||||||
|
if (!data.hasOwnProperty(propertyName)) {
|
||||||
|
var val = self.hasOwnProperty(propertyName) ? self[propertyName] : self.__data[propertyName];
|
||||||
|
if (val !== undefined && val !== null && val.toObject) {
|
||||||
|
data[propertyName] = val.toObject(!schemaLess);
|
||||||
|
} else {
|
||||||
|
data[propertyName] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ModelBaseClass.prototype.hasOwnProperty = function (prop) {
|
// ModelBaseClass.prototype.hasOwnProperty = function (prop) {
|
||||||
|
@ -244,13 +244,13 @@ ModelBaseClass.prototype.toObject = function (onlySchema) {
|
||||||
// };
|
// };
|
||||||
|
|
||||||
ModelBaseClass.prototype.toJSON = function () {
|
ModelBaseClass.prototype.toJSON = function () {
|
||||||
return this.toObject();
|
return this.toObject();
|
||||||
};
|
};
|
||||||
|
|
||||||
ModelBaseClass.prototype.fromObject = function (obj) {
|
ModelBaseClass.prototype.fromObject = function (obj) {
|
||||||
for(var key in obj) {
|
for (var key in obj) {
|
||||||
this[key] = obj[key];
|
this[key] = obj[key];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -260,7 +260,7 @@ ModelBaseClass.prototype.fromObject = function (obj) {
|
||||||
* @return Boolean
|
* @return Boolean
|
||||||
*/
|
*/
|
||||||
ModelBaseClass.prototype.propertyChanged = function propertyChanged(propertyName) {
|
ModelBaseClass.prototype.propertyChanged = function propertyChanged(propertyName) {
|
||||||
return this.__data[propertyName] !== this.__dataWas[propertyName];
|
return this.__data[propertyName] !== this.__dataWas[propertyName];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -270,23 +270,23 @@ ModelBaseClass.prototype.propertyChanged = function propertyChanged(propertyName
|
||||||
* initial state
|
* initial state
|
||||||
*/
|
*/
|
||||||
ModelBaseClass.prototype.reset = function () {
|
ModelBaseClass.prototype.reset = function () {
|
||||||
var obj = this;
|
var obj = this;
|
||||||
for(var k in obj) {
|
for (var k in obj) {
|
||||||
if (k !== 'id' && !obj.constructor.dataSource.definitions[obj.constructor.modelName].properties[k]) {
|
if (k !== 'id' && !obj.constructor.dataSource.definitions[obj.constructor.modelName].properties[k]) {
|
||||||
delete obj[k];
|
delete obj[k];
|
||||||
}
|
|
||||||
if (obj.propertyChanged(k)) {
|
|
||||||
obj[k] = obj[k + '$was'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (obj.propertyChanged(k)) {
|
||||||
|
obj[k] = obj[k + '$was'];
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ModelBaseClass.prototype.inspect = function () {
|
ModelBaseClass.prototype.inspect = function () {
|
||||||
return util.inspect(this.__data, false, 4, true);
|
return util.inspect(this.__data, false, 4, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
ModelBaseClass.mixin = function(anotherClass, options) {
|
ModelBaseClass.mixin = function (anotherClass, options) {
|
||||||
return jutil.mixin(this, anotherClass, options);
|
return jutil.mixin(this, anotherClass, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
ModelBaseClass.prototype.getDataSource = function () {
|
ModelBaseClass.prototype.getDataSource = function () {
|
||||||
|
|
451
lib/relations.js
451
lib/relations.js
|
@ -11,11 +11,11 @@ function Relation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Relation.relationNameFor = function relationNameFor(foreignKey) {
|
Relation.relationNameFor = function relationNameFor(foreignKey) {
|
||||||
for (var rel in this.relations) {
|
for (var rel in this.relations) {
|
||||||
if (this.relations[rel].type === 'belongsTo' && this.relations[rel].keyFrom === foreignKey) {
|
if (this.relations[rel].type === 'belongsTo' && this.relations[rel].keyFrom === foreignKey) {
|
||||||
return rel;
|
return rel;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,131 +26,132 @@ Relation.relationNameFor = function relationNameFor(foreignKey) {
|
||||||
* @example `User.hasMany(Post, {as: 'posts', foreignKey: 'authorId'});`
|
* @example `User.hasMany(Post, {as: 'posts', foreignKey: 'authorId'});`
|
||||||
*/
|
*/
|
||||||
Relation.hasMany = function hasMany(anotherClass, params) {
|
Relation.hasMany = function hasMany(anotherClass, params) {
|
||||||
var thisClassName = this.modelName;
|
var thisClassName = this.modelName;
|
||||||
params = params || {};
|
params = params || {};
|
||||||
if (typeof anotherClass === 'string') {
|
if (typeof anotherClass === 'string') {
|
||||||
params.as = anotherClass;
|
params.as = anotherClass;
|
||||||
if (params.model) {
|
if (params.model) {
|
||||||
anotherClass = params.model;
|
anotherClass = params.model;
|
||||||
} else {
|
} else {
|
||||||
var anotherClassName = i8n.singularize(anotherClass).toLowerCase();
|
var anotherClassName = i8n.singularize(anotherClass).toLowerCase();
|
||||||
for(var name in this.dataSource.modelBuilder.models) {
|
for (var name in this.dataSource.modelBuilder.models) {
|
||||||
if (name.toLowerCase() === anotherClassName) {
|
if (name.toLowerCase() === anotherClassName) {
|
||||||
anotherClass = this.dataSource.modelBuilder.models[name];
|
anotherClass = this.dataSource.modelBuilder.models[name];
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var methodName = params.as || i8n.camelize(anotherClass.pluralModelName, true);
|
}
|
||||||
var fk = params.foreignKey || i8n.camelize(thisClassName + '_id', true);
|
var methodName = params.as || i8n.camelize(anotherClass.pluralModelName, true);
|
||||||
|
var fk = params.foreignKey || i8n.camelize(thisClassName + '_id', true);
|
||||||
|
|
||||||
var idName = this.dataSource.idName(this.modelName) || 'id';
|
var idName = this.dataSource.idName(this.modelName) || 'id';
|
||||||
|
|
||||||
this.relations[methodName] = {
|
this.relations[methodName] = {
|
||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
keyFrom: idName,
|
keyFrom: idName,
|
||||||
keyTo: fk,
|
keyTo: fk,
|
||||||
modelTo: anotherClass,
|
modelTo: anotherClass,
|
||||||
multiple: true
|
multiple: true
|
||||||
};
|
};
|
||||||
// each instance of this class should have method named
|
// each instance of this class should have method named
|
||||||
// pluralize(anotherClass.modelName)
|
// pluralize(anotherClass.modelName)
|
||||||
// which is actually just anotherClass.find({where: {thisModelNameId: this[idName]}}, cb);
|
// which is actually just anotherClass.find({where: {thisModelNameId: this[idName]}}, cb);
|
||||||
var scopeMethods = {
|
var scopeMethods = {
|
||||||
findById: find,
|
findById: find,
|
||||||
destroy: destroy
|
destroy: destroy
|
||||||
};
|
};
|
||||||
if (params.through) {
|
if (params.through) {
|
||||||
var fk2 = i8n.camelize(anotherClass.modelName + '_id', true);
|
var fk2 = i8n.camelize(anotherClass.modelName + '_id', true);
|
||||||
scopeMethods.create = function create(data, done) {
|
scopeMethods.create = function create(data, done) {
|
||||||
if (typeof data !== 'object') {
|
if (typeof data !== 'object') {
|
||||||
done = data;
|
done = data;
|
||||||
data = {};
|
data = {};
|
||||||
}
|
}
|
||||||
if ('function' !== typeof done) {
|
if ('function' !== typeof done) {
|
||||||
done = function() {};
|
done = function () {
|
||||||
}
|
};
|
||||||
var self = this;
|
}
|
||||||
anotherClass.create(data, function(err, ac) {
|
var self = this;
|
||||||
if (err) return done(err, ac);
|
anotherClass.create(data, function (err, ac) {
|
||||||
var d = {};
|
if (err) return done(err, ac);
|
||||||
d[params.through.relationNameFor(fk)] = self;
|
var d = {};
|
||||||
d[params.through.relationNameFor(fk2)] = ac;
|
d[params.through.relationNameFor(fk)] = self;
|
||||||
params.through.create(d, function(e) {
|
d[params.through.relationNameFor(fk2)] = ac;
|
||||||
if (e) {
|
params.through.create(d, function (e) {
|
||||||
ac.destroy(function() {
|
if (e) {
|
||||||
done(e);
|
ac.destroy(function () {
|
||||||
});
|
done(e);
|
||||||
} else {
|
|
||||||
done(err, ac);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
} else {
|
||||||
scopeMethods.add = function(acInst, done) {
|
done(err, ac);
|
||||||
var data = {};
|
}
|
||||||
var query = {};
|
|
||||||
query[fk] = this[idName];
|
|
||||||
data[params.through.relationNameFor(fk)] = this;
|
|
||||||
query[fk2] = acInst[idName] || acInst;
|
|
||||||
data[params.through.relationNameFor(fk2)] = acInst;
|
|
||||||
params.through.findOrCreate({where: query}, data, done);
|
|
||||||
};
|
|
||||||
scopeMethods.remove = function(acInst, done) {
|
|
||||||
var q = {};
|
|
||||||
q[fk2] = acInst[idName] || acInst;
|
|
||||||
params.through.findOne({where: q}, function(err, d) {
|
|
||||||
if (err) {
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
if (!d) {
|
|
||||||
return done();
|
|
||||||
}
|
|
||||||
d.destroy(done);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
delete scopeMethods.destroy;
|
|
||||||
}
|
|
||||||
defineScope(this.prototype, params.through || anotherClass, methodName, function () {
|
|
||||||
var filter = {};
|
|
||||||
filter.where = {};
|
|
||||||
filter.where[fk] = this[idName];
|
|
||||||
if (params.through) {
|
|
||||||
filter.collect = i8n.camelize(anotherClass.modelName, true);
|
|
||||||
filter.include = filter.collect;
|
|
||||||
}
|
|
||||||
return filter;
|
|
||||||
}, scopeMethods);
|
|
||||||
|
|
||||||
if (!params.through) {
|
|
||||||
// obviously, anotherClass should have attribute called `fk`
|
|
||||||
anotherClass.dataSource.defineForeignKey(anotherClass.modelName, fk, this.modelName);
|
|
||||||
}
|
|
||||||
|
|
||||||
function find(id, cb) {
|
|
||||||
anotherClass.findById(id, function (err, inst) {
|
|
||||||
if (err) return cb(err);
|
|
||||||
if (!inst) return cb(new Error('Not found'));
|
|
||||||
if (inst[fk] && inst[fk].toString() == this[idName].toString()) {
|
|
||||||
cb(null, inst);
|
|
||||||
} else {
|
|
||||||
cb(new Error('Permission denied'));
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
function destroy(id, cb) {
|
|
||||||
var self = this;
|
|
||||||
anotherClass.findById(id, function (err, inst) {
|
|
||||||
if (err) return cb(err);
|
|
||||||
if (!inst) return cb(new Error('Not found'));
|
|
||||||
if (inst[fk] && inst[fk].toString() == self[idName].toString()) {
|
|
||||||
inst.destroy(cb);
|
|
||||||
} else {
|
|
||||||
cb(new Error('Permission denied'));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
scopeMethods.add = function (acInst, done) {
|
||||||
|
var data = {};
|
||||||
|
var query = {};
|
||||||
|
query[fk] = this[idName];
|
||||||
|
data[params.through.relationNameFor(fk)] = this;
|
||||||
|
query[fk2] = acInst[idName] || acInst;
|
||||||
|
data[params.through.relationNameFor(fk2)] = acInst;
|
||||||
|
params.through.findOrCreate({where: query}, data, done);
|
||||||
|
};
|
||||||
|
scopeMethods.remove = function (acInst, done) {
|
||||||
|
var q = {};
|
||||||
|
q[fk2] = acInst[idName] || acInst;
|
||||||
|
params.through.findOne({where: q}, function (err, d) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
if (!d) {
|
||||||
|
return done();
|
||||||
|
}
|
||||||
|
d.destroy(done);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
delete scopeMethods.destroy;
|
||||||
|
}
|
||||||
|
defineScope(this.prototype, params.through || anotherClass, methodName, function () {
|
||||||
|
var filter = {};
|
||||||
|
filter.where = {};
|
||||||
|
filter.where[fk] = this[idName];
|
||||||
|
if (params.through) {
|
||||||
|
filter.collect = i8n.camelize(anotherClass.modelName, true);
|
||||||
|
filter.include = filter.collect;
|
||||||
}
|
}
|
||||||
|
return filter;
|
||||||
|
}, scopeMethods);
|
||||||
|
|
||||||
|
if (!params.through) {
|
||||||
|
// obviously, anotherClass should have attribute called `fk`
|
||||||
|
anotherClass.dataSource.defineForeignKey(anotherClass.modelName, fk, this.modelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function find(id, cb) {
|
||||||
|
anotherClass.findById(id, function (err, inst) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
if (!inst) return cb(new Error('Not found'));
|
||||||
|
if (inst[fk] && inst[fk].toString() == this[idName].toString()) {
|
||||||
|
cb(null, inst);
|
||||||
|
} else {
|
||||||
|
cb(new Error('Permission denied'));
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroy(id, cb) {
|
||||||
|
var self = this;
|
||||||
|
anotherClass.findById(id, function (err, inst) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
if (!inst) return cb(new Error('Not found'));
|
||||||
|
if (inst[fk] && inst[fk].toString() == self[idName].toString()) {
|
||||||
|
inst.destroy(cb);
|
||||||
|
} else {
|
||||||
|
cb(new Error('Permission denied'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -178,87 +179,87 @@ Relation.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.
|
||||||
*/
|
*/
|
||||||
Relation.belongsTo = function (anotherClass, params) {
|
Relation.belongsTo = function (anotherClass, params) {
|
||||||
params = params || {};
|
params = params || {};
|
||||||
if ('string' === typeof anotherClass) {
|
if ('string' === typeof anotherClass) {
|
||||||
params.as = anotherClass;
|
params.as = anotherClass;
|
||||||
if (params.model) {
|
if (params.model) {
|
||||||
anotherClass = params.model;
|
anotherClass = params.model;
|
||||||
} else {
|
} else {
|
||||||
var anotherClassName = anotherClass.toLowerCase();
|
var anotherClassName = anotherClass.toLowerCase();
|
||||||
for(var name in this.dataSource.modelBuilder.models) {
|
for (var name in this.dataSource.modelBuilder.models) {
|
||||||
if (name.toLowerCase() === anotherClassName) {
|
if (name.toLowerCase() === anotherClassName) {
|
||||||
anotherClass = this.dataSource.modelBuilder.models[name];
|
anotherClass = this.dataSource.modelBuilder.models[name];
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var idName = this.dataSource.idName(anotherClass.modelName) || 'id';
|
var idName = this.dataSource.idName(anotherClass.modelName) || 'id';
|
||||||
var methodName = params.as || i8n.camelize(anotherClass.modelName, true);
|
var methodName = params.as || i8n.camelize(anotherClass.modelName, true);
|
||||||
var fk = params.foreignKey || methodName + 'Id';
|
var fk = params.foreignKey || methodName + 'Id';
|
||||||
|
|
||||||
this.relations[methodName] = {
|
this.relations[methodName] = {
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
keyFrom: fk,
|
keyFrom: fk,
|
||||||
keyTo: idName,
|
keyTo: idName,
|
||||||
modelTo: anotherClass,
|
modelTo: anotherClass,
|
||||||
multiple: false
|
multiple: false
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dataSource.defineForeignKey(this.modelName, fk, anotherClass.modelName);
|
this.dataSource.defineForeignKey(this.modelName, fk, anotherClass.modelName);
|
||||||
this.prototype['__finders__'] = this.prototype['__finders__'] || {};
|
this.prototype['__finders__'] = this.prototype['__finders__'] || {};
|
||||||
|
|
||||||
this.prototype['__finders__'][methodName] = function (id, cb) {
|
this.prototype['__finders__'][methodName] = function (id, cb) {
|
||||||
if (id === null) {
|
if (id === null) {
|
||||||
cb(null, null);
|
cb(null, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
anotherClass.findById(id, function (err,inst) {
|
anotherClass.findById(id, function (err, inst) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
if (!inst) return cb(null, null);
|
if (!inst) return cb(null, null);
|
||||||
if (inst[idName] === this[fk]) {
|
if (inst[idName] === this[fk]) {
|
||||||
cb(null, inst);
|
cb(null, inst);
|
||||||
} else {
|
} else {
|
||||||
cb(new Error('Permission denied'));
|
cb(new Error('Permission denied'));
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
this.prototype[methodName] = function (refresh, p) {
|
this.prototype[methodName] = function (refresh, p) {
|
||||||
if (arguments.length === 1) {
|
if (arguments.length === 1) {
|
||||||
p = refresh;
|
p = refresh;
|
||||||
refresh = false;
|
refresh = false;
|
||||||
} else if (arguments.length > 2) {
|
} else if (arguments.length > 2) {
|
||||||
throw new Error('Method can\'t be called with more than two arguments');
|
throw new Error('Method can\'t be called with more than two arguments');
|
||||||
}
|
}
|
||||||
var self = this;
|
var self = this;
|
||||||
var cachedValue;
|
var cachedValue;
|
||||||
if (!refresh && this.__cachedRelations && (typeof this.__cachedRelations[methodName] !== 'undefined')) {
|
if (!refresh && this.__cachedRelations && (typeof this.__cachedRelations[methodName] !== 'undefined')) {
|
||||||
cachedValue = this.__cachedRelations[methodName];
|
cachedValue = this.__cachedRelations[methodName];
|
||||||
}
|
}
|
||||||
if (p instanceof ModelBaseClass) { // acts as setter
|
if (p instanceof ModelBaseClass) { // acts as setter
|
||||||
this[fk] = p[idName];
|
this[fk] = p[idName];
|
||||||
this.__cachedRelations[methodName] = p;
|
this.__cachedRelations[methodName] = p;
|
||||||
} else if (typeof p === 'function') { // acts as async getter
|
} else if (typeof p === 'function') { // acts as async getter
|
||||||
if (typeof cachedValue === 'undefined') {
|
if (typeof cachedValue === 'undefined') {
|
||||||
this.__finders__[methodName].apply(self, [this[fk], function(err, inst) {
|
this.__finders__[methodName].apply(self, [this[fk], function (err, inst) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
self.__cachedRelations[methodName] = inst;
|
self.__cachedRelations[methodName] = inst;
|
||||||
}
|
}
|
||||||
p(err, inst);
|
p(err, inst);
|
||||||
}]);
|
}]);
|
||||||
return this[fk];
|
return this[fk];
|
||||||
} else {
|
} else {
|
||||||
p(null, cachedValue);
|
p(null, cachedValue);
|
||||||
return cachedValue;
|
return cachedValue;
|
||||||
}
|
}
|
||||||
} else if (typeof p === 'undefined') { // acts as sync getter
|
} else if (typeof p === 'undefined') { // acts as sync getter
|
||||||
return this[fk];
|
return this[fk];
|
||||||
} else { // setter
|
} else { // setter
|
||||||
this[fk] = p;
|
this[fk] = p;
|
||||||
delete this.__cachedRelations[methodName];
|
delete this.__cachedRelations[methodName];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -268,40 +269,40 @@ Relation.belongsTo = function (anotherClass, params) {
|
||||||
* Post.hasAndBelongsToMany('tags'); creates connection model 'PostTag'
|
* Post.hasAndBelongsToMany('tags'); creates connection model 'PostTag'
|
||||||
*/
|
*/
|
||||||
Relation.hasAndBelongsToMany = function hasAndBelongsToMany(anotherClass, params) {
|
Relation.hasAndBelongsToMany = function hasAndBelongsToMany(anotherClass, params) {
|
||||||
params = params || {};
|
params = params || {};
|
||||||
var models = this.dataSource.modelBuilder.models;
|
var models = this.dataSource.modelBuilder.models;
|
||||||
|
|
||||||
if ('string' === typeof anotherClass) {
|
if ('string' === typeof anotherClass) {
|
||||||
params.as = anotherClass;
|
params.as = anotherClass;
|
||||||
if (params.model) {
|
if (params.model) {
|
||||||
anotherClass = params.model;
|
anotherClass = params.model;
|
||||||
} else {
|
} else {
|
||||||
anotherClass = lookupModel(i8n.singularize(anotherClass)) ||
|
anotherClass = lookupModel(i8n.singularize(anotherClass)) ||
|
||||||
anotherClass;
|
anotherClass;
|
||||||
}
|
|
||||||
if (typeof anotherClass === 'string') {
|
|
||||||
throw new Error('Could not find "' + anotherClass + '" relation for ' + this.modelName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (typeof anotherClass === 'string') {
|
||||||
if (!params.through) {
|
throw new Error('Could not find "' + anotherClass + '" relation for ' + this.modelName);
|
||||||
var name1 = this.modelName + anotherClass.modelName;
|
|
||||||
var name2 = anotherClass.modelName + this.modelName;
|
|
||||||
params.through = lookupModel(name1) || lookupModel(name2) ||
|
|
||||||
this.dataSource.define(name1);
|
|
||||||
}
|
}
|
||||||
params.through.belongsTo(this);
|
}
|
||||||
params.through.belongsTo(anotherClass);
|
|
||||||
|
|
||||||
this.hasMany(anotherClass, {as: params.as, through: params.through});
|
if (!params.through) {
|
||||||
|
var name1 = this.modelName + anotherClass.modelName;
|
||||||
|
var name2 = anotherClass.modelName + this.modelName;
|
||||||
|
params.through = lookupModel(name1) || lookupModel(name2) ||
|
||||||
|
this.dataSource.define(name1);
|
||||||
|
}
|
||||||
|
params.through.belongsTo(this);
|
||||||
|
params.through.belongsTo(anotherClass);
|
||||||
|
|
||||||
function lookupModel(modelName) {
|
this.hasMany(anotherClass, {as: params.as, through: params.through});
|
||||||
var lookupClassName = modelName.toLowerCase();
|
|
||||||
for (var name in models) {
|
function lookupModel(modelName) {
|
||||||
if (name.toLowerCase() === lookupClassName) {
|
var lookupClassName = modelName.toLowerCase();
|
||||||
return models[name];
|
for (var name in models) {
|
||||||
}
|
if (name.toLowerCase() === lookupClassName) {
|
||||||
}
|
return models[name];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
366
lib/scope.js
366
lib/scope.js
|
@ -5,192 +5,192 @@ exports.defineScope = defineScope;
|
||||||
|
|
||||||
function defineScope(cls, targetClass, name, params, methods) {
|
function defineScope(cls, targetClass, name, params, methods) {
|
||||||
|
|
||||||
// collect meta info about scope
|
// collect meta info about scope
|
||||||
if (!cls._scopeMeta) {
|
if (!cls._scopeMeta) {
|
||||||
cls._scopeMeta = {};
|
cls._scopeMeta = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// only makes sence to add scope in meta if base and target classes
|
// only makes sence to add scope in meta if base and target classes
|
||||||
// are same
|
// are same
|
||||||
if (cls === targetClass) {
|
if (cls === targetClass) {
|
||||||
cls._scopeMeta[name] = params;
|
cls._scopeMeta[name] = params;
|
||||||
} else {
|
} else {
|
||||||
if (!targetClass._scopeMeta) {
|
if (!targetClass._scopeMeta) {
|
||||||
targetClass._scopeMeta = {};
|
targetClass._scopeMeta = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a property for the scope
|
||||||
|
Object.defineProperty(cls, name, {
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true,
|
||||||
|
/**
|
||||||
|
* This defines a property for the scope. For example, user.accounts or
|
||||||
|
* User.vips. Please note the cls can be the model class or prototype of
|
||||||
|
* the model class.
|
||||||
|
*
|
||||||
|
* The property value is function. It can be used to query the scope,
|
||||||
|
* such as user.accounts(condOrRefresh, cb) or User.vips(cb). The value
|
||||||
|
* can also have child properties for create/build/delete. For example,
|
||||||
|
* user.accounts.create(act, cb).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
get: function () {
|
||||||
|
var f = function caller(condOrRefresh, cb) {
|
||||||
|
var actualCond = {};
|
||||||
|
var actualRefresh = false;
|
||||||
|
var saveOnCache = true;
|
||||||
|
if (arguments.length === 1) {
|
||||||
|
cb = condOrRefresh;
|
||||||
|
} else if (arguments.length === 2) {
|
||||||
|
if (typeof condOrRefresh === 'boolean') {
|
||||||
|
actualRefresh = condOrRefresh;
|
||||||
|
} else {
|
||||||
|
actualCond = condOrRefresh;
|
||||||
|
actualRefresh = true;
|
||||||
|
saveOnCache = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Method can be only called with one or two arguments');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Define a property for the scope
|
if (!this.__cachedRelations || (typeof this.__cachedRelations[name] == 'undefined') || actualRefresh) {
|
||||||
Object.defineProperty(cls, name, {
|
var self = this;
|
||||||
enumerable: false,
|
var params = mergeParams(actualCond, caller._scope);
|
||||||
configurable: true,
|
return targetClass.find(params, function (err, data) {
|
||||||
/**
|
if (!err && saveOnCache) {
|
||||||
* This defines a property for the scope. For example, user.accounts or
|
if (!self.__cachedRelations) {
|
||||||
* User.vips. Please note the cls can be the model class or prototype of
|
self.__cachedRelations = {};
|
||||||
* the model class.
|
}
|
||||||
*
|
self.__cachedRelations[name] = data;
|
||||||
* The property value is function. It can be used to query the scope,
|
|
||||||
* such as user.accounts(condOrRefresh, cb) or User.vips(cb). The value
|
|
||||||
* can also have child properties for create/build/delete. For example,
|
|
||||||
* user.accounts.create(act, cb).
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
get: function () {
|
|
||||||
var f = function caller(condOrRefresh, cb) {
|
|
||||||
var actualCond = {};
|
|
||||||
var actualRefresh = false;
|
|
||||||
var saveOnCache = true;
|
|
||||||
if (arguments.length === 1) {
|
|
||||||
cb = condOrRefresh;
|
|
||||||
} else if (arguments.length === 2) {
|
|
||||||
if (typeof condOrRefresh === 'boolean') {
|
|
||||||
actualRefresh = condOrRefresh;
|
|
||||||
} else {
|
|
||||||
actualCond = condOrRefresh;
|
|
||||||
actualRefresh = true;
|
|
||||||
saveOnCache = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error('Method can be only called with one or two arguments');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.__cachedRelations || (typeof this.__cachedRelations[name] == 'undefined') || actualRefresh) {
|
|
||||||
var self = this;
|
|
||||||
var params = mergeParams(actualCond, caller._scope);
|
|
||||||
return targetClass.find(params, function(err, data) {
|
|
||||||
if (!err && saveOnCache) {
|
|
||||||
if (!self.__cachedRelations) {
|
|
||||||
self.__cachedRelations = {};
|
|
||||||
}
|
|
||||||
self.__cachedRelations[name] = data;
|
|
||||||
}
|
|
||||||
cb(err, data);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
cb(null, this.__cachedRelations[name]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
f._scope = typeof params === 'function' ? params.call(this) : params;
|
|
||||||
|
|
||||||
f.build = build;
|
|
||||||
f.create = create;
|
|
||||||
f.destroyAll = destroyAll;
|
|
||||||
for (var i in methods) {
|
|
||||||
f[i] = methods[i].bind(this);
|
|
||||||
}
|
}
|
||||||
|
cb(err, data);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cb(null, this.__cachedRelations[name]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
f._scope = typeof params === 'function' ? params.call(this) : params;
|
||||||
|
|
||||||
// define sub-scopes
|
f.build = build;
|
||||||
Object.keys(targetClass._scopeMeta).forEach(function (name) {
|
f.create = create;
|
||||||
Object.defineProperty(f, name, {
|
f.destroyAll = destroyAll;
|
||||||
enumerable: false,
|
for (var i in methods) {
|
||||||
get: function () {
|
f[i] = methods[i].bind(this);
|
||||||
mergeParams(f._scope, targetClass._scopeMeta[name]);
|
}
|
||||||
return f;
|
|
||||||
}
|
// define sub-scopes
|
||||||
});
|
Object.keys(targetClass._scopeMeta).forEach(function (name) {
|
||||||
}.bind(this));
|
Object.defineProperty(f, name, {
|
||||||
|
enumerable: false,
|
||||||
|
get: function () {
|
||||||
|
mergeParams(f._scope, targetClass._scopeMeta[name]);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Wrap the property into a function for remoting
|
|
||||||
var fn = function() {
|
|
||||||
// primaryObject.scopeName, such as user.accounts
|
|
||||||
var f = this[name];
|
|
||||||
// set receiver to be the scope property whose value is a function
|
|
||||||
f.apply(this[name], arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
fn.shared = true;
|
|
||||||
fn.http = {verb: 'get', path: '/' + name};
|
|
||||||
fn.accepts = {arg: 'where', type: 'object'};
|
|
||||||
fn.description = 'Fetches ' + name;
|
|
||||||
fn.returns = {arg: name, type: 'array', root: true};
|
|
||||||
|
|
||||||
cls['__get__' + name] = fn;
|
|
||||||
|
|
||||||
var fn_create = function() {
|
|
||||||
var f = this[name].create;
|
|
||||||
f.apply(this[name], arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
fn_create.shared = true;
|
|
||||||
fn_create.http = {verb: 'post', path: '/' + name};
|
|
||||||
fn_create.accepts = {arg: 'data', type: 'object', http: {source: 'body'}};
|
|
||||||
fn_create.description = 'Creates ' + name;
|
|
||||||
fn_create.returns = {arg: 'data', type: 'object', root: true};
|
|
||||||
|
|
||||||
cls['__create__' + name] = fn_create;
|
|
||||||
|
|
||||||
var fn_delete = function() {
|
|
||||||
var f = this[name].destroyAll;
|
|
||||||
f.apply(this[name], arguments);
|
|
||||||
};
|
|
||||||
fn_delete.shared = true;
|
|
||||||
fn_delete.http = {verb: 'delete', path: '/' + name};
|
|
||||||
fn_delete.description = 'Deletes ' + name;
|
|
||||||
fn_delete.returns = {arg: 'data', type: 'object', root: true};
|
|
||||||
|
|
||||||
cls['__delete__' + name] = fn_delete;
|
|
||||||
|
|
||||||
// and it should have create/build methods with binded thisModelNameId param
|
|
||||||
function build(data) {
|
|
||||||
return new targetClass(mergeParams(this._scope, {where:data || {}}).where);
|
|
||||||
}
|
|
||||||
|
|
||||||
function create(data, cb) {
|
|
||||||
if (typeof data === 'function') {
|
|
||||||
cb = data;
|
|
||||||
data = {};
|
|
||||||
}
|
|
||||||
this.build(data).save(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Callback
|
|
||||||
- The callback will be called after all elements are destroyed
|
|
||||||
- For every destroy call which results in an error
|
|
||||||
- If fetching the Elements on which destroyAll is called results in an error
|
|
||||||
*/
|
|
||||||
function destroyAll(cb) {
|
|
||||||
targetClass.find(this._scope, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
cb(err);
|
|
||||||
} else {
|
|
||||||
(function loopOfDestruction (data) {
|
|
||||||
if(data.length > 0) {
|
|
||||||
data.shift().destroy(function(err) {
|
|
||||||
if(err && cb) cb(err);
|
|
||||||
loopOfDestruction(data);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if(cb) cb();
|
|
||||||
}
|
|
||||||
}(data));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}.bind(this));
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wrap the property into a function for remoting
|
||||||
|
var fn = function () {
|
||||||
|
// primaryObject.scopeName, such as user.accounts
|
||||||
|
var f = this[name];
|
||||||
|
// set receiver to be the scope property whose value is a function
|
||||||
|
f.apply(this[name], arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
fn.shared = true;
|
||||||
|
fn.http = {verb: 'get', path: '/' + name};
|
||||||
|
fn.accepts = {arg: 'where', type: 'object'};
|
||||||
|
fn.description = 'Fetches ' + name;
|
||||||
|
fn.returns = {arg: name, type: 'array', root: true};
|
||||||
|
|
||||||
|
cls['__get__' + name] = fn;
|
||||||
|
|
||||||
|
var fn_create = function () {
|
||||||
|
var f = this[name].create;
|
||||||
|
f.apply(this[name], arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
fn_create.shared = true;
|
||||||
|
fn_create.http = {verb: 'post', path: '/' + name};
|
||||||
|
fn_create.accepts = {arg: 'data', type: 'object', http: {source: 'body'}};
|
||||||
|
fn_create.description = 'Creates ' + name;
|
||||||
|
fn_create.returns = {arg: 'data', type: 'object', root: true};
|
||||||
|
|
||||||
|
cls['__create__' + name] = fn_create;
|
||||||
|
|
||||||
|
var fn_delete = function () {
|
||||||
|
var f = this[name].destroyAll;
|
||||||
|
f.apply(this[name], arguments);
|
||||||
|
};
|
||||||
|
fn_delete.shared = true;
|
||||||
|
fn_delete.http = {verb: 'delete', path: '/' + name};
|
||||||
|
fn_delete.description = 'Deletes ' + name;
|
||||||
|
fn_delete.returns = {arg: 'data', type: 'object', root: true};
|
||||||
|
|
||||||
|
cls['__delete__' + name] = fn_delete;
|
||||||
|
|
||||||
|
// and it should have create/build methods with binded thisModelNameId param
|
||||||
|
function build(data) {
|
||||||
|
return new targetClass(mergeParams(this._scope, {where: data || {}}).where);
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(data, cb) {
|
||||||
|
if (typeof data === 'function') {
|
||||||
|
cb = data;
|
||||||
|
data = {};
|
||||||
|
}
|
||||||
|
this.build(data).save(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Callback
|
||||||
|
- The callback will be called after all elements are destroyed
|
||||||
|
- For every destroy call which results in an error
|
||||||
|
- If fetching the Elements on which destroyAll is called results in an error
|
||||||
|
*/
|
||||||
|
function destroyAll(cb) {
|
||||||
|
targetClass.find(this._scope, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
cb(err);
|
||||||
|
} else {
|
||||||
|
(function loopOfDestruction(data) {
|
||||||
|
if (data.length > 0) {
|
||||||
|
data.shift().destroy(function (err) {
|
||||||
|
if (err && cb) cb(err);
|
||||||
|
loopOfDestruction(data);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (cb) cb();
|
||||||
|
}
|
||||||
|
}(data));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeParams(base, update) {
|
||||||
|
base = base || {};
|
||||||
|
if (update.where) {
|
||||||
|
base.where = merge(base.where, update.where);
|
||||||
|
}
|
||||||
|
if (update.include) {
|
||||||
|
base.include = update.include;
|
||||||
|
}
|
||||||
|
if (update.collect) {
|
||||||
|
base.collect = update.collect;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeParams(base, update) {
|
// overwrite order
|
||||||
base = base || {};
|
if (update.order) {
|
||||||
if (update.where) {
|
base.order = update.order;
|
||||||
base.where = merge(base.where, update.where);
|
|
||||||
}
|
|
||||||
if (update.include) {
|
|
||||||
base.include = update.include;
|
|
||||||
}
|
|
||||||
if (update.collect) {
|
|
||||||
base.collect = update.collect;
|
|
||||||
}
|
|
||||||
|
|
||||||
// overwrite order
|
|
||||||
if (update.order) {
|
|
||||||
base.order = update.order;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return base;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -200,12 +200,12 @@ function defineScope(cls, targetClass, name, params, methods) {
|
||||||
* @returns {Object} `base`
|
* @returns {Object} `base`
|
||||||
*/
|
*/
|
||||||
function merge(base, update) {
|
function merge(base, update) {
|
||||||
base = base || {};
|
base = base || {};
|
||||||
if (update) {
|
if (update) {
|
||||||
Object.keys(update).forEach(function (key) {
|
Object.keys(update).forEach(function (key) {
|
||||||
base[key] = update[key];
|
base[key] = update[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
249
lib/sql.js
249
lib/sql.js
|
@ -8,7 +8,7 @@ module.exports = BaseSQL;
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function BaseSQL() {
|
function BaseSQL() {
|
||||||
Connector.apply(this, [].slice.call(arguments));
|
Connector.apply(this, [].slice.call(arguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(BaseSQL, Connector);
|
util.inherits(BaseSQL, Connector);
|
||||||
|
@ -20,33 +20,32 @@ util.inherits(BaseSQL, Connector);
|
||||||
BaseSQL.prototype.relational = true;
|
BaseSQL.prototype.relational = true;
|
||||||
|
|
||||||
BaseSQL.prototype.query = function () {
|
BaseSQL.prototype.query = function () {
|
||||||
throw new Error('query method should be declared in connector');
|
throw new Error('query method should be declared in connector');
|
||||||
};
|
};
|
||||||
|
|
||||||
BaseSQL.prototype.command = function (sql, params, callback) {
|
BaseSQL.prototype.command = function (sql, params, callback) {
|
||||||
return this.query(sql, params, callback);
|
return this.query(sql, params, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
BaseSQL.prototype.queryOne = function (sql, callback) {
|
BaseSQL.prototype.queryOne = function (sql, callback) {
|
||||||
return this.query(sql, function (err, data) {
|
return this.query(sql, function (err, data) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
callback(err, data[0]);
|
callback(err, data[0]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the table name for a given model
|
* Get the table name for a given model
|
||||||
* @param {String} model The model name
|
* @param {String} model The model name
|
||||||
* @returns {String} The table name
|
* @returns {String} The table name
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.table = function (model) {
|
BaseSQL.prototype.table = function (model) {
|
||||||
var name = this.getDataSource(model).tableName(model);
|
var name = this.getDataSource(model).tableName(model);
|
||||||
var dbName = this.dbName;
|
var dbName = this.dbName;
|
||||||
if(typeof dbName === 'function') {
|
if (typeof dbName === 'function') {
|
||||||
name = dbName(name);
|
name = dbName(name);
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,12 +55,12 @@ BaseSQL.prototype.table = function (model) {
|
||||||
* @returns {String} The column name
|
* @returns {String} The column name
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.column = function (model, property) {
|
BaseSQL.prototype.column = function (model, property) {
|
||||||
var name = this.getDataSource(model).columnName(model, property);
|
var name = this.getDataSource(model).columnName(model, property);
|
||||||
var dbName = this.dbName;
|
var dbName = this.dbName;
|
||||||
if(typeof dbName === 'function') {
|
if (typeof dbName === 'function') {
|
||||||
name = dbName(name);
|
name = dbName(name);
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,7 +70,7 @@ BaseSQL.prototype.column = function (model, property) {
|
||||||
* @returns {Object} The column metadata
|
* @returns {Object} The column metadata
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.columnMetadata = function (model, property) {
|
BaseSQL.prototype.columnMetadata = function (model, property) {
|
||||||
return this.getDataSource(model).columnMetadata(model, property);
|
return this.getDataSource(model).columnMetadata(model, property);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,13 +80,13 @@ BaseSQL.prototype.columnMetadata = function (model, property) {
|
||||||
* @returns {String} The property name for a given column
|
* @returns {String} The property name for a given column
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.propertyName = function (model, column) {
|
BaseSQL.prototype.propertyName = function (model, column) {
|
||||||
var props = this._models[model].properties;
|
var props = this._models[model].properties;
|
||||||
for(var p in props) {
|
for (var p in props) {
|
||||||
if(this.column(model, p) === column) {
|
if (this.column(model, p) === column) {
|
||||||
return p;
|
return p;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,12 +95,13 @@ BaseSQL.prototype.propertyName = function (model, column) {
|
||||||
* @returns {String} The column name
|
* @returns {String} The column name
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.idColumn = function (model) {
|
BaseSQL.prototype.idColumn = function (model) {
|
||||||
var name = this.getDataSource(model).idColumnName(model);;
|
var name = this.getDataSource(model).idColumnName(model);
|
||||||
var dbName = this.dbName;
|
;
|
||||||
if(typeof dbName === 'function') {
|
var dbName = this.dbName;
|
||||||
name = dbName(name);
|
if (typeof dbName === 'function') {
|
||||||
}
|
name = dbName(name);
|
||||||
return name;
|
}
|
||||||
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,7 +110,7 @@ BaseSQL.prototype.idColumn = function (model) {
|
||||||
* @returns {String} the escaped id column name
|
* @returns {String} the escaped id column name
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.idColumnEscaped = function (model) {
|
BaseSQL.prototype.idColumnEscaped = function (model) {
|
||||||
return this.escapeName(this.getDataSource(model).idColumnName(model));
|
return this.escapeName(this.getDataSource(model).idColumnName(model));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,7 +118,7 @@ BaseSQL.prototype.idColumnEscaped = function (model) {
|
||||||
* @param {String} name The name
|
* @param {String} name The name
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.escapeName = function (name) {
|
BaseSQL.prototype.escapeName = function (name) {
|
||||||
throw new Error('escapeName method should be declared in connector');
|
throw new Error('escapeName method should be declared in connector');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,7 +127,7 @@ BaseSQL.prototype.escapeName = function (name) {
|
||||||
* @returns {String} the escaped table name
|
* @returns {String} the escaped table name
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.tableEscaped = function (model) {
|
BaseSQL.prototype.tableEscaped = function (model) {
|
||||||
return this.escapeName(this.table(model));
|
return this.escapeName(this.table(model));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,7 +137,7 @@ BaseSQL.prototype.tableEscaped = function (model) {
|
||||||
* @returns {String} The escaped column name
|
* @returns {String} The escaped column name
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.columnEscaped = function (model, property) {
|
BaseSQL.prototype.columnEscaped = function (model, property) {
|
||||||
return this.escapeName(this.column(model, property));
|
return this.escapeName(this.column(model, property));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -147,15 +147,14 @@ BaseSQL.prototype.columnEscaped = function (model, property) {
|
||||||
* @param {Function} callback The callback function
|
* @param {Function} callback The callback function
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.save = function (model, data, callback) {
|
BaseSQL.prototype.save = function (model, data, callback) {
|
||||||
var sql = 'UPDATE ' + this.tableEscaped(model) + ' SET '
|
var sql = 'UPDATE ' + this.tableEscaped(model) + ' SET '
|
||||||
+ this.toFields(model, data) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + Number(data.id);
|
+ this.toFields(model, data) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + Number(data.id);
|
||||||
|
|
||||||
this.query(sql, function (err) {
|
this.query(sql, function (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a model instance exists for the given id value
|
* Check if a model instance exists for the given id value
|
||||||
* @param {String} model The model name
|
* @param {String} model The model name
|
||||||
|
@ -163,13 +162,13 @@ BaseSQL.prototype.save = function (model, data, callback) {
|
||||||
* @param {Function} callback The callback function
|
* @param {Function} callback The callback function
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.exists = function (model, id, callback) {
|
BaseSQL.prototype.exists = function (model, id, callback) {
|
||||||
var sql = 'SELECT 1 FROM ' +
|
var sql = 'SELECT 1 FROM ' +
|
||||||
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + Number(id) + ' LIMIT 1';
|
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + Number(id) + ' LIMIT 1';
|
||||||
|
|
||||||
this.query(sql, function (err, data) {
|
this.query(sql, function (err, data) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
callback(null, data.length === 1);
|
callback(null, data.length === 1);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,17 +178,17 @@ BaseSQL.prototype.exists = function (model, id, callback) {
|
||||||
* @param {Function} callback The callback function
|
* @param {Function} callback The callback function
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.find = function find(model, id, callback) {
|
BaseSQL.prototype.find = function find(model, id, callback) {
|
||||||
var sql = 'SELECT * FROM ' +
|
var sql = 'SELECT * FROM ' +
|
||||||
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + id + ' LIMIT 1';
|
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + id + ' LIMIT 1';
|
||||||
|
|
||||||
this.query(sql, function (err, data) {
|
this.query(sql, function (err, data) {
|
||||||
if (data && data.length === 1) {
|
if (data && data.length === 1) {
|
||||||
data[0].id = id;
|
data[0].id = id;
|
||||||
} else {
|
} else {
|
||||||
data = [null];
|
data = [null];
|
||||||
}
|
}
|
||||||
callback(err, this.fromDatabase(model, data[0]));
|
callback(err, this.fromDatabase(model, data[0]));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -199,12 +198,12 @@ BaseSQL.prototype.find = function find(model, id, callback) {
|
||||||
* @param {Function} callback The callback function
|
* @param {Function} callback The callback function
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.delete = BaseSQL.prototype.destroy = function destroy(model, id, callback) {
|
BaseSQL.prototype.delete = BaseSQL.prototype.destroy = function destroy(model, id, callback) {
|
||||||
var sql = 'DELETE FROM ' +
|
var sql = 'DELETE FROM ' +
|
||||||
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + id;
|
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + id;
|
||||||
|
|
||||||
this.command(sql, function (err) {
|
this.command(sql, function (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -214,12 +213,12 @@ BaseSQL.prototype.delete = BaseSQL.prototype.destroy = function destroy(model, i
|
||||||
* @param {Function} callback The callback function
|
* @param {Function} callback The callback function
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.deleteAll = BaseSQL.prototype.destroyAll = function destroyAll(model, callback) {
|
BaseSQL.prototype.deleteAll = BaseSQL.prototype.destroyAll = function destroyAll(model, callback) {
|
||||||
this.command('DELETE FROM ' + this.tableEscaped(model), function (err) {
|
this.command('DELETE FROM ' + this.tableEscaped(model), function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err, []);
|
return callback(err, []);
|
||||||
}
|
}
|
||||||
callback(err);
|
callback(err);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -230,27 +229,27 @@ BaseSQL.prototype.deleteAll = BaseSQL.prototype.destroyAll = function destroyAll
|
||||||
* @param {Object} where The where clause
|
* @param {Object} where The where clause
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.count = function count(model, callback, where) {
|
BaseSQL.prototype.count = function count(model, callback, where) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var props = this._models[model].properties;
|
var props = this._models[model].properties;
|
||||||
|
|
||||||
this.queryOne('SELECT count(*) as cnt FROM ' +
|
this.queryOne('SELECT count(*) as cnt FROM ' +
|
||||||
this.tableEscaped(model) + ' ' + buildWhere(where), function (err, res) {
|
this.tableEscaped(model) + ' ' + buildWhere(where), function (err, res) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
callback(err, res && res.cnt);
|
callback(err, res && res.cnt);
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildWhere(conds) {
|
||||||
|
var cs = [];
|
||||||
|
Object.keys(conds || {}).forEach(function (key) {
|
||||||
|
var keyEscaped = self.columnEscaped(model, key);
|
||||||
|
if (conds[key] === null) {
|
||||||
|
cs.push(keyEscaped + ' IS NULL');
|
||||||
|
} else {
|
||||||
|
cs.push(keyEscaped + ' = ' + self.toDatabase(props[key], conds[key]));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
return cs.length ? ' WHERE ' + cs.join(' AND ') : '';
|
||||||
function buildWhere(conds) {
|
}
|
||||||
var cs = [];
|
|
||||||
Object.keys(conds || {}).forEach(function (key) {
|
|
||||||
var keyEscaped = self.columnEscaped(model, key);
|
|
||||||
if (conds[key] === null) {
|
|
||||||
cs.push(keyEscaped + ' IS NULL');
|
|
||||||
} else {
|
|
||||||
cs.push(keyEscaped + ' = ' + self.toDatabase(props[key], conds[key]));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return cs.length ? ' WHERE ' + cs.join(' AND ') : '';
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -261,15 +260,15 @@ BaseSQL.prototype.count = function count(model, callback, where) {
|
||||||
* @param {Function} cb The callback function
|
* @param {Function} cb The callback function
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.updateAttributes = function updateAttrs(model, id, data, cb) {
|
BaseSQL.prototype.updateAttributes = function updateAttrs(model, id, data, cb) {
|
||||||
data.id = id;
|
data.id = id;
|
||||||
this.save(model, data, cb);
|
this.save(model, data, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnect from the connector
|
* Disconnect from the connector
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.disconnect = function disconnect() {
|
BaseSQL.prototype.disconnect = function disconnect() {
|
||||||
this.client.end();
|
this.client.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -278,38 +277,38 @@ BaseSQL.prototype.disconnect = function disconnect() {
|
||||||
* @param {Function} [cb] The callback function
|
* @param {Function} [cb] The callback function
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.automigrate = function (models, cb) {
|
BaseSQL.prototype.automigrate = function (models, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var wait = 0;
|
var wait = 0;
|
||||||
if ((!cb) && ('function' === typeof models)) {
|
if ((!cb) && ('function' === typeof models)) {
|
||||||
cb = models;
|
cb = models;
|
||||||
models = undefined;
|
models = undefined;
|
||||||
}
|
}
|
||||||
// First argument is a model name
|
// First argument is a model name
|
||||||
if ('string' === typeof models) {
|
if ('string' === typeof models) {
|
||||||
models = [models];
|
models = [models];
|
||||||
}
|
}
|
||||||
|
|
||||||
models = models || Object.keys(this._models);
|
models = models || Object.keys(this._models);
|
||||||
models.forEach(function (model) {
|
models.forEach(function (model) {
|
||||||
if (model in self._models) {
|
if (model in self._models) {
|
||||||
wait++;
|
wait++;
|
||||||
self.dropTable(model, function () {
|
self.dropTable(model, function () {
|
||||||
// console.log('drop', model);
|
// console.log('drop', model);
|
||||||
self.createTable(model, function (err) {
|
self.createTable(model, function (err) {
|
||||||
// console.log('create', model);
|
// console.log('create', model);
|
||||||
if (err) console.log(err);
|
if (err) console.log(err);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
if (wait === 0) cb();
|
|
||||||
|
|
||||||
function done() {
|
|
||||||
if (--wait === 0 && cb) {
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
if (wait === 0) cb();
|
||||||
|
|
||||||
|
function done() {
|
||||||
|
if (--wait === 0 && cb) {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -318,7 +317,7 @@ BaseSQL.prototype.automigrate = function (models, cb) {
|
||||||
* @param {Function} [cb] The callback function
|
* @param {Function} [cb] The callback function
|
||||||
*/
|
*/
|
||||||
BaseSQL.prototype.dropTable = function (model, cb) {
|
BaseSQL.prototype.dropTable = function (model, cb) {
|
||||||
this.command('DROP TABLE IF EXISTS ' + this.tableEscaped(model), cb);
|
this.command('DROP TABLE IF EXISTS ' + this.tableEscaped(model), cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -328,7 +327,7 @@ BaseSQL.prototype.dropTable = function (model, cb) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
BaseSQL.prototype.createTable = function (model, cb) {
|
BaseSQL.prototype.createTable = function (model, cb) {
|
||||||
this.command('CREATE TABLE ' + this.tableEscaped(model) +
|
this.command('CREATE TABLE ' + this.tableEscaped(model) +
|
||||||
' (\n ' + this.propertiesSQL(model) + '\n)', cb);
|
' (\n ' + this.propertiesSQL(model) + '\n)', cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
104
lib/types.js
104
lib/types.js
|
@ -1,61 +1,61 @@
|
||||||
module.exports = function (Types) {
|
module.exports = function (Types) {
|
||||||
|
|
||||||
var List = require('./list.js');
|
var List = require('./list.js');
|
||||||
var GeoPoint = require('./geo').GeoPoint;
|
var GeoPoint = require('./geo').GeoPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schema types
|
* Schema types
|
||||||
*/
|
*/
|
||||||
Types.Text = function Text(value) {
|
Types.Text = function Text(value) {
|
||||||
if (!(this instanceof Text)) {
|
if (!(this instanceof Text)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}; // Text type
|
}; // Text type
|
||||||
|
|
||||||
Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function () {
|
Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function () {
|
||||||
return this.value;
|
return this.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
Types.JSON = function JSON(value) {
|
Types.JSON = function JSON(value) {
|
||||||
if (!(this instanceof JSON)) {
|
if (!(this instanceof JSON)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}; // JSON Object
|
}; // JSON Object
|
||||||
Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function () {
|
Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function () {
|
||||||
return this.value;
|
return this.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
Types.Any = function Any(value) {
|
Types.Any = function Any(value) {
|
||||||
if (!(this instanceof Any)) {
|
if (!(this instanceof Any)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}; // Any Type
|
}; // Any Type
|
||||||
Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function () {
|
Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function () {
|
||||||
return this.value;
|
return this.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
Types.schemaTypes = {};
|
Types.schemaTypes = {};
|
||||||
Types.registerType = function (type, names) {
|
Types.registerType = function (type, names) {
|
||||||
names = names || [];
|
names = names || [];
|
||||||
names = names.concat([type.name]);
|
names = names.concat([type.name]);
|
||||||
for (var n = 0; n < names.length; n++) {
|
for (var n = 0; n < names.length; n++) {
|
||||||
this.schemaTypes[names[n].toLowerCase()] = type;
|
this.schemaTypes[names[n].toLowerCase()] = type;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Types.registerType(Types.Text);
|
Types.registerType(Types.Text);
|
||||||
Types.registerType(Types.JSON);
|
Types.registerType(Types.JSON);
|
||||||
Types.registerType(Types.Any);
|
Types.registerType(Types.Any);
|
||||||
|
|
||||||
Types.registerType(String);
|
Types.registerType(String);
|
||||||
Types.registerType(Number);
|
Types.registerType(Number);
|
||||||
Types.registerType(Boolean);
|
Types.registerType(Boolean);
|
||||||
Types.registerType(Date);
|
Types.registerType(Date);
|
||||||
Types.registerType(Buffer, ['Binary']);
|
Types.registerType(Buffer, ['Binary']);
|
||||||
Types.registerType(Array);
|
Types.registerType(Array);
|
||||||
Types.registerType(GeoPoint);
|
Types.registerType(GeoPoint);
|
||||||
Types.registerType(Object);
|
Types.registerType(Object);
|
||||||
}
|
};
|
151
lib/utils.js
151
lib/utils.js
|
@ -8,54 +8,56 @@ exports.mergeSettings = mergeSettings;
|
||||||
var traverse = require('traverse');
|
var traverse = require('traverse');
|
||||||
|
|
||||||
function safeRequire(module) {
|
function safeRequire(module) {
|
||||||
try {
|
try {
|
||||||
return require(module);
|
return require(module);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Run "npm install loopback-datasource-juggler ' + module + '" command to use loopback-datasource-juggler using ' + module + ' database engine');
|
console.log('Run "npm install loopback-datasource-juggler ' + module
|
||||||
process.exit(1);
|
+ '" command to use loopback-datasource-juggler using ' + module
|
||||||
}
|
+ ' database engine');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fieldsToArray(fields, properties) {
|
function fieldsToArray(fields, properties) {
|
||||||
if(!fields) return;
|
if (!fields) return;
|
||||||
|
|
||||||
// include all properties by default
|
// include all properties by default
|
||||||
var result = properties;
|
var result = properties;
|
||||||
|
|
||||||
if(typeof fields === 'string') {
|
if (typeof fields === 'string') {
|
||||||
return [fields];
|
return [fields];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(fields) && fields.length > 0) {
|
||||||
|
// No empty array, including all the fields
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('object' === typeof fields) {
|
||||||
|
// { field1: boolean, field2: boolean ... }
|
||||||
|
var included = [];
|
||||||
|
var excluded = [];
|
||||||
|
var keys = Object.keys(fields);
|
||||||
|
if (!keys.length) return;
|
||||||
|
|
||||||
|
keys.forEach(function (k) {
|
||||||
|
if (fields[k]) {
|
||||||
|
included.push(k);
|
||||||
|
} else if ((k in fields) && !fields[k]) {
|
||||||
|
excluded.push(k);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (included.length > 0) {
|
||||||
|
result = included;
|
||||||
|
} else if (excluded.length > 0) {
|
||||||
|
excluded.forEach(function (e) {
|
||||||
|
var index = result.indexOf(e);
|
||||||
|
result.splice(index, 1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(fields) && fields.length > 0) {
|
return result;
|
||||||
// No empty array, including all the fields
|
|
||||||
return fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('object' === typeof fields) {
|
|
||||||
// { field1: boolean, field2: boolean ... }
|
|
||||||
var included = [];
|
|
||||||
var excluded = [];
|
|
||||||
var keys = Object.keys(fields);
|
|
||||||
if(!keys.length) return;
|
|
||||||
|
|
||||||
keys.forEach(function (k) {
|
|
||||||
if (fields[k]) {
|
|
||||||
included.push(k);
|
|
||||||
} else if ((k in fields) && !fields[k]) {
|
|
||||||
excluded.push(k);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (included.length > 0) {
|
|
||||||
result = included;
|
|
||||||
} else if (excluded.length > 0) {
|
|
||||||
excluded.forEach(function (e) {
|
|
||||||
var index = result.indexOf(e);
|
|
||||||
result.splice(index, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectFields(fields) {
|
function selectFields(fields) {
|
||||||
|
@ -79,24 +81,25 @@ function selectFields(fields) {
|
||||||
* @returns {exports.map|*}
|
* @returns {exports.map|*}
|
||||||
*/
|
*/
|
||||||
function removeUndefined(query) {
|
function removeUndefined(query) {
|
||||||
if (typeof query !== 'object' || query === null) {
|
if (typeof query !== 'object' || query === null) {
|
||||||
return query;
|
return query;
|
||||||
|
}
|
||||||
|
// WARNING: [rfeng] Use map() will cause mongodb to produce invalid BSON
|
||||||
|
// as traverse doesn't transform the ObjectId correctly
|
||||||
|
return traverse(query).forEach(function (x) {
|
||||||
|
if (x === undefined) {
|
||||||
|
this.remove();
|
||||||
}
|
}
|
||||||
// WARNING: [rfeng] Use map() will cause mongodb to produce invalid BSON
|
|
||||||
// as traverse doesn't transform the ObjectId correctly
|
|
||||||
return traverse(query).forEach(function (x) {
|
|
||||||
if (x === undefined) {
|
|
||||||
this.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Array.isArray(x) && (typeof x === 'object' && x !== null && x.constructor !== Object)) {
|
if (!Array.isArray(x) && (typeof x === 'object' && x !== null
|
||||||
// This object is not a plain object
|
&& x.constructor !== Object)) {
|
||||||
this.update(x, true); // Stop navigating into this object
|
// This object is not a plain object
|
||||||
return x;
|
this.update(x, true); // Stop navigating into this object
|
||||||
}
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = require('url');
|
var url = require('url');
|
||||||
|
@ -108,25 +111,25 @@ var qs = require('qs');
|
||||||
* @returns {Object} The settings object
|
* @returns {Object} The settings object
|
||||||
*/
|
*/
|
||||||
function parseSettings(urlStr) {
|
function parseSettings(urlStr) {
|
||||||
if(!urlStr) {
|
if (!urlStr) {
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
var uri = url.parse(urlStr, false);
|
||||||
|
var settings = {};
|
||||||
|
settings.connector = uri.protocol && uri.protocol.split(':')[0]; // Remove the trailing :
|
||||||
|
settings.host = settings.hostname = uri.hostname;
|
||||||
|
settings.port = uri.port && Number(uri.port); // port is a string
|
||||||
|
settings.user = settings.username = uri.auth && uri.auth.split(':')[0]; // <username>:<password>
|
||||||
|
settings.password = uri.auth && uri.auth.split(':')[1];
|
||||||
|
settings.database = uri.pathname && uri.pathname.split('/')[1]; // remove the leading /
|
||||||
|
settings.url = urlStr;
|
||||||
|
if (uri.query) {
|
||||||
|
var params = qs.parse(uri.query);
|
||||||
|
for (var p in params) {
|
||||||
|
settings[p] = params[p];
|
||||||
}
|
}
|
||||||
var uri = url.parse(urlStr, false);
|
}
|
||||||
var settings = {};
|
return settings;
|
||||||
settings.connector = uri.protocol && uri.protocol.split(':')[0]; // Remove the trailing :
|
|
||||||
settings.host = settings.hostname = uri.hostname;
|
|
||||||
settings.port = uri.port && Number(uri.port); // port is a string
|
|
||||||
settings.user = settings.username = uri.auth && uri.auth.split(':')[0]; // <username>:<password>
|
|
||||||
settings.password = uri.auth && uri.auth.split(':')[1];
|
|
||||||
settings.database = uri.pathname && uri.pathname.split('/')[1]; // remove the leading /
|
|
||||||
settings.url = urlStr;
|
|
||||||
if(uri.query) {
|
|
||||||
var params = qs.parse(uri.query);
|
|
||||||
for(var p in params) {
|
|
||||||
settings[p] = params[p];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return settings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -203,121 +203,121 @@ Validatable.validatesUniquenessOf = getConfigurator('uniqueness', {async: true})
|
||||||
* Presence validator
|
* Presence validator
|
||||||
*/
|
*/
|
||||||
function validatePresence(attr, conf, err) {
|
function validatePresence(attr, conf, err) {
|
||||||
if (blank(this[attr])) {
|
if (blank(this[attr])) {
|
||||||
err();
|
err();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Length validator
|
* Length validator
|
||||||
*/
|
*/
|
||||||
function validateLength(attr, conf, err) {
|
function validateLength(attr, conf, err) {
|
||||||
if (nullCheck.call(this, attr, conf, err)) return;
|
if (nullCheck.call(this, attr, conf, err)) return;
|
||||||
|
|
||||||
var len = this[attr].length;
|
var len = this[attr].length;
|
||||||
if (conf.min && len < conf.min) {
|
if (conf.min && len < conf.min) {
|
||||||
err('min');
|
err('min');
|
||||||
}
|
}
|
||||||
if (conf.max && len > conf.max) {
|
if (conf.max && len > conf.max) {
|
||||||
err('max');
|
err('max');
|
||||||
}
|
}
|
||||||
if (conf.is && len !== conf.is) {
|
if (conf.is && len !== conf.is) {
|
||||||
err('is');
|
err('is');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Numericality validator
|
* Numericality validator
|
||||||
*/
|
*/
|
||||||
function validateNumericality(attr, conf, err) {
|
function validateNumericality(attr, conf, err) {
|
||||||
if (nullCheck.call(this, attr, conf, err)) return;
|
if (nullCheck.call(this, attr, conf, err)) return;
|
||||||
|
|
||||||
if (typeof this[attr] !== 'number') {
|
if (typeof this[attr] !== 'number') {
|
||||||
return err('number');
|
return err('number');
|
||||||
}
|
}
|
||||||
if (conf.int && this[attr] !== Math.round(this[attr])) {
|
if (conf.int && this[attr] !== Math.round(this[attr])) {
|
||||||
return err('int');
|
return err('int');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inclusion validator
|
* Inclusion validator
|
||||||
*/
|
*/
|
||||||
function validateInclusion(attr, conf, err) {
|
function validateInclusion(attr, conf, err) {
|
||||||
if (nullCheck.call(this, attr, conf, err)) return;
|
if (nullCheck.call(this, attr, conf, err)) return;
|
||||||
|
|
||||||
if (!~conf.in.indexOf(this[attr])) {
|
if (!~conf.in.indexOf(this[attr])) {
|
||||||
err()
|
err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exclusion validator
|
* Exclusion validator
|
||||||
*/
|
*/
|
||||||
function validateExclusion(attr, conf, err) {
|
function validateExclusion(attr, conf, err) {
|
||||||
if (nullCheck.call(this, attr, conf, err)) return;
|
if (nullCheck.call(this, attr, conf, err)) return;
|
||||||
|
|
||||||
if (~conf.in.indexOf(this[attr])) {
|
if (~conf.in.indexOf(this[attr])) {
|
||||||
err()
|
err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format validator
|
* Format validator
|
||||||
*/
|
*/
|
||||||
function validateFormat(attr, conf, err) {
|
function validateFormat(attr, conf, err) {
|
||||||
if (nullCheck.call(this, attr, conf, err)) return;
|
if (nullCheck.call(this, attr, conf, err)) return;
|
||||||
|
|
||||||
if (typeof this[attr] === 'string') {
|
if (typeof this[attr] === 'string') {
|
||||||
if (!this[attr].match(conf['with'])) {
|
if (!this[attr].match(conf['with'])) {
|
||||||
err();
|
err();
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
err();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom validator
|
* Custom validator
|
||||||
*/
|
*/
|
||||||
function validateCustom(attr, conf, err, done) {
|
function validateCustom(attr, conf, err, done) {
|
||||||
conf.customValidator.call(this, err, done);
|
conf.customValidator.call(this, err, done);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uniqueness validator
|
* Uniqueness validator
|
||||||
*/
|
*/
|
||||||
function validateUniqueness(attr, conf, err, done) {
|
function validateUniqueness(attr, conf, err, done) {
|
||||||
var cond = {where: {}};
|
var cond = {where: {}};
|
||||||
cond.where[attr] = this[attr];
|
cond.where[attr] = this[attr];
|
||||||
this.constructor.find(cond, function (error, found) {
|
this.constructor.find(cond, function (error, found) {
|
||||||
if (error) {
|
if (error) {
|
||||||
return err();
|
return err();
|
||||||
}
|
}
|
||||||
if (found.length > 1) {
|
if (found.length > 1) {
|
||||||
err();
|
err();
|
||||||
} else if (found.length === 1 && (!this.id || !found[0].id || found[0].id.toString() != this.id.toString())) {
|
} else if (found.length === 1 && (!this.id || !found[0].id || found[0].id.toString() != this.id.toString())) {
|
||||||
err();
|
err();
|
||||||
}
|
}
|
||||||
done();
|
done();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
var validators = {
|
var validators = {
|
||||||
presence: validatePresence,
|
presence: validatePresence,
|
||||||
length: validateLength,
|
length: validateLength,
|
||||||
numericality: validateNumericality,
|
numericality: validateNumericality,
|
||||||
inclusion: validateInclusion,
|
inclusion: validateInclusion,
|
||||||
exclusion: validateExclusion,
|
exclusion: validateExclusion,
|
||||||
format: validateFormat,
|
format: validateFormat,
|
||||||
custom: validateCustom,
|
custom: validateCustom,
|
||||||
uniqueness: validateUniqueness
|
uniqueness: validateUniqueness
|
||||||
};
|
};
|
||||||
|
|
||||||
function getConfigurator(name, opts) {
|
function getConfigurator(name, opts) {
|
||||||
return function () {
|
return function () {
|
||||||
configure(this, name, arguments, opts);
|
configure(this, name, arguments, opts);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -341,193 +341,193 @@ function getConfigurator(name, opts) {
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
Validatable.prototype.isValid = function (callback, data) {
|
Validatable.prototype.isValid = function (callback, data) {
|
||||||
var valid = true, inst = this, wait = 0, async = false;
|
var valid = true, inst = this, wait = 0, async = false;
|
||||||
|
|
||||||
// exit with success when no errors
|
// exit with success when no errors
|
||||||
if (!this.constructor._validations) {
|
if (!this.constructor._validations) {
|
||||||
cleanErrors(this);
|
cleanErrors(this);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
this.trigger('validate', function (validationsDone) {
|
this.trigger('validate', function (validationsDone) {
|
||||||
validationsDone.call(inst, function() {
|
validationsDone.call(inst, function () {
|
||||||
callback(valid);
|
callback(valid);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
return valid;
|
|
||||||
}
|
}
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'errors', {
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true,
|
||||||
|
value: new Errors
|
||||||
|
});
|
||||||
|
|
||||||
|
this.trigger('validate', function (validationsDone) {
|
||||||
|
var inst = this,
|
||||||
|
asyncFail = false;
|
||||||
|
|
||||||
|
this.constructor._validations.forEach(function (v) {
|
||||||
|
if (v[2] && v[2].async) {
|
||||||
|
async = true;
|
||||||
|
wait += 1;
|
||||||
|
process.nextTick(function () {
|
||||||
|
validationFailed(inst, v, done);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (validationFailed(inst, v)) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Object.defineProperty(this, 'errors', {
|
|
||||||
enumerable: false,
|
|
||||||
configurable: true,
|
|
||||||
value: new Errors
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.trigger('validate', function (validationsDone) {
|
if (!async) {
|
||||||
var inst = this,
|
validationsDone.call(inst, function () {
|
||||||
asyncFail = false;
|
if (valid) cleanErrors(inst);
|
||||||
|
if (callback) {
|
||||||
this.constructor._validations.forEach(function (v) {
|
callback(valid);
|
||||||
if (v[2] && v[2].async) {
|
|
||||||
async = true;
|
|
||||||
wait += 1;
|
|
||||||
process.nextTick(function () {
|
|
||||||
validationFailed(inst, v, done);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (validationFailed(inst, v)) {
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!async) {
|
|
||||||
validationsDone.call(inst, function() {
|
|
||||||
if (valid) cleanErrors(inst);
|
|
||||||
if (callback) {
|
|
||||||
callback(valid);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
function done(fail) {
|
|
||||||
asyncFail = asyncFail || fail;
|
|
||||||
if (--wait === 0) {
|
|
||||||
validationsDone.call(inst, function () {
|
|
||||||
if (valid && !asyncFail) cleanErrors(inst);
|
|
||||||
if (callback) {
|
|
||||||
callback(valid && !asyncFail);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}, data);
|
|
||||||
|
|
||||||
if (async) {
|
|
||||||
// in case of async validation we should return undefined here,
|
|
||||||
// because not all validations are finished yet
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
return valid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function done(fail) {
|
||||||
|
asyncFail = asyncFail || fail;
|
||||||
|
if (--wait === 0) {
|
||||||
|
validationsDone.call(inst, function () {
|
||||||
|
if (valid && !asyncFail) cleanErrors(inst);
|
||||||
|
if (callback) {
|
||||||
|
callback(valid && !asyncFail);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, data);
|
||||||
|
|
||||||
|
if (async) {
|
||||||
|
// in case of async validation we should return undefined here,
|
||||||
|
// because not all validations are finished yet
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function cleanErrors(inst) {
|
function cleanErrors(inst) {
|
||||||
Object.defineProperty(inst, 'errors', {
|
Object.defineProperty(inst, 'errors', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
value: false
|
value: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function validationFailed(inst, v, cb) {
|
function validationFailed(inst, v, cb) {
|
||||||
var attr = v[0];
|
var attr = v[0];
|
||||||
var conf = v[1];
|
var conf = v[1];
|
||||||
var opts = v[2] || {};
|
var opts = v[2] || {};
|
||||||
|
|
||||||
if (typeof attr !== 'string') return false;
|
if (typeof attr !== 'string') return false;
|
||||||
|
|
||||||
// here we should check skip validation conditions (if, unless)
|
// here we should check skip validation conditions (if, unless)
|
||||||
// that can be specified in conf
|
// that can be specified in conf
|
||||||
if (skipValidation(inst, conf, 'if')) return false;
|
if (skipValidation(inst, conf, 'if')) return false;
|
||||||
if (skipValidation(inst, conf, 'unless')) return false;
|
if (skipValidation(inst, conf, 'unless')) return false;
|
||||||
|
|
||||||
var fail = false;
|
var fail = false;
|
||||||
var validator = validators[conf.validation];
|
var validator = validators[conf.validation];
|
||||||
var validatorArguments = [];
|
var validatorArguments = [];
|
||||||
validatorArguments.push(attr);
|
validatorArguments.push(attr);
|
||||||
validatorArguments.push(conf);
|
validatorArguments.push(conf);
|
||||||
validatorArguments.push(function onerror(kind) {
|
validatorArguments.push(function onerror(kind) {
|
||||||
var message, code = conf.validation;
|
var message, code = conf.validation;
|
||||||
if (conf.message) {
|
if (conf.message) {
|
||||||
message = conf.message;
|
message = conf.message;
|
||||||
}
|
|
||||||
if (!message && defaultMessages[conf.validation]) {
|
|
||||||
message = defaultMessages[conf.validation];
|
|
||||||
}
|
|
||||||
if (!message) {
|
|
||||||
message = 'is invalid';
|
|
||||||
}
|
|
||||||
if (kind) {
|
|
||||||
code += '.' + kind;
|
|
||||||
if (message[kind]) {
|
|
||||||
// get deeper
|
|
||||||
message = message[kind];
|
|
||||||
} else if (defaultMessages.common[kind]) {
|
|
||||||
message = defaultMessages.common[kind];
|
|
||||||
} else {
|
|
||||||
message = 'is invalid';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inst.errors.add(attr, message, code);
|
|
||||||
fail = true;
|
|
||||||
});
|
|
||||||
if (cb) {
|
|
||||||
validatorArguments.push(function () {
|
|
||||||
cb(fail);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
validator.apply(inst, validatorArguments);
|
if (!message && defaultMessages[conf.validation]) {
|
||||||
return fail;
|
message = defaultMessages[conf.validation];
|
||||||
|
}
|
||||||
|
if (!message) {
|
||||||
|
message = 'is invalid';
|
||||||
|
}
|
||||||
|
if (kind) {
|
||||||
|
code += '.' + kind;
|
||||||
|
if (message[kind]) {
|
||||||
|
// get deeper
|
||||||
|
message = message[kind];
|
||||||
|
} else if (defaultMessages.common[kind]) {
|
||||||
|
message = defaultMessages.common[kind];
|
||||||
|
} else {
|
||||||
|
message = 'is invalid';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inst.errors.add(attr, message, code);
|
||||||
|
fail = true;
|
||||||
|
});
|
||||||
|
if (cb) {
|
||||||
|
validatorArguments.push(function () {
|
||||||
|
cb(fail);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
validator.apply(inst, validatorArguments);
|
||||||
|
return fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
function skipValidation(inst, conf, kind) {
|
function skipValidation(inst, conf, kind) {
|
||||||
var doValidate = true;
|
var doValidate = true;
|
||||||
if (typeof conf[kind] === 'function') {
|
if (typeof conf[kind] === 'function') {
|
||||||
doValidate = conf[kind].call(inst);
|
doValidate = conf[kind].call(inst);
|
||||||
if (kind === 'unless') doValidate = !doValidate;
|
if (kind === 'unless') doValidate = !doValidate;
|
||||||
} else if (typeof conf[kind] === 'string') {
|
} else if (typeof conf[kind] === 'string') {
|
||||||
if (typeof inst[conf[kind]] === 'function') {
|
if (typeof inst[conf[kind]] === 'function') {
|
||||||
doValidate = inst[conf[kind]].call(inst);
|
doValidate = inst[conf[kind]].call(inst);
|
||||||
if (kind === 'unless') doValidate = !doValidate;
|
if (kind === 'unless') doValidate = !doValidate;
|
||||||
} else if (inst.__data.hasOwnProperty(conf[kind])) {
|
} else if (inst.__data.hasOwnProperty(conf[kind])) {
|
||||||
doValidate = inst[conf[kind]];
|
doValidate = inst[conf[kind]];
|
||||||
if (kind === 'unless') doValidate = !doValidate;
|
if (kind === 'unless') doValidate = !doValidate;
|
||||||
} else {
|
} else {
|
||||||
doValidate = kind === 'if';
|
doValidate = kind === 'if';
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return !doValidate;
|
}
|
||||||
|
return !doValidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultMessages = {
|
var defaultMessages = {
|
||||||
presence: 'can\'t be blank',
|
presence: 'can\'t be blank',
|
||||||
length: {
|
length: {
|
||||||
min: 'too short',
|
min: 'too short',
|
||||||
max: 'too long',
|
max: 'too long',
|
||||||
is: 'length is wrong'
|
is: 'length is wrong'
|
||||||
},
|
},
|
||||||
common: {
|
common: {
|
||||||
blank: 'is blank',
|
blank: 'is blank',
|
||||||
'null': 'is null'
|
'null': 'is null'
|
||||||
},
|
},
|
||||||
numericality: {
|
numericality: {
|
||||||
'int': 'is not an integer',
|
'int': 'is not an integer',
|
||||||
'number': 'is not a number'
|
'number': 'is not a number'
|
||||||
},
|
},
|
||||||
inclusion: 'is not included in the list',
|
inclusion: 'is not included in the list',
|
||||||
exclusion: 'is reserved',
|
exclusion: 'is reserved',
|
||||||
uniqueness: 'is not unique'
|
uniqueness: 'is not unique'
|
||||||
};
|
};
|
||||||
|
|
||||||
function nullCheck(attr, conf, err) {
|
function nullCheck(attr, conf, err) {
|
||||||
var isNull = this[attr] === null || !(attr in this);
|
var isNull = this[attr] === null || !(attr in this);
|
||||||
if (isNull) {
|
if (isNull) {
|
||||||
if (!conf.allowNull) {
|
if (!conf.allowNull) {
|
||||||
err('null');
|
err('null');
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (blank(this[attr])) {
|
|
||||||
if (!conf.allowBlank) {
|
|
||||||
err('blank');
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
|
} else {
|
||||||
|
if (blank(this[attr])) {
|
||||||
|
if (!conf.allowBlank) {
|
||||||
|
err('blank');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -538,78 +538,78 @@ function nullCheck(attr, conf, err) {
|
||||||
* @returns {Boolean} whether `v` blank or not
|
* @returns {Boolean} whether `v` blank or not
|
||||||
*/
|
*/
|
||||||
function blank(v) {
|
function blank(v) {
|
||||||
if (typeof v === 'undefined') return true;
|
if (typeof v === 'undefined') return true;
|
||||||
if (v instanceof Array && v.length === 0) return true;
|
if (v instanceof Array && v.length === 0) return true;
|
||||||
if (v === null) return true;
|
if (v === null) return true;
|
||||||
if (typeof v == 'string' && v === '') return true;
|
if (typeof v == 'string' && v === '') return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function configure(cls, validation, args, opts) {
|
function configure(cls, validation, args, opts) {
|
||||||
if (!cls._validations) {
|
if (!cls._validations) {
|
||||||
Object.defineProperty(cls, '_validations', {
|
Object.defineProperty(cls, '_validations', {
|
||||||
writable: true,
|
writable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
value: []
|
value: []
|
||||||
});
|
|
||||||
}
|
|
||||||
args = [].slice.call(args);
|
|
||||||
var conf;
|
|
||||||
if (typeof args[args.length - 1] === 'object') {
|
|
||||||
conf = args.pop();
|
|
||||||
} else {
|
|
||||||
conf = {};
|
|
||||||
}
|
|
||||||
if (validation === 'custom' && typeof args[args.length - 1] === 'function') {
|
|
||||||
conf.customValidator = args.pop();
|
|
||||||
}
|
|
||||||
conf.validation = validation;
|
|
||||||
args.forEach(function (attr) {
|
|
||||||
cls._validations.push([attr, conf, opts]);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
args = [].slice.call(args);
|
||||||
|
var conf;
|
||||||
|
if (typeof args[args.length - 1] === 'object') {
|
||||||
|
conf = args.pop();
|
||||||
|
} else {
|
||||||
|
conf = {};
|
||||||
|
}
|
||||||
|
if (validation === 'custom' && typeof args[args.length - 1] === 'function') {
|
||||||
|
conf.customValidator = args.pop();
|
||||||
|
}
|
||||||
|
conf.validation = validation;
|
||||||
|
args.forEach(function (attr) {
|
||||||
|
cls._validations.push([attr, conf, opts]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function Errors() {
|
function Errors() {
|
||||||
Object.defineProperty(this, 'codes', {
|
Object.defineProperty(this, 'codes', {
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
value: {}
|
value: {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Errors.prototype.add = function (field, message, code) {
|
Errors.prototype.add = function (field, message, code) {
|
||||||
code = code || 'invalid';
|
code = code || 'invalid';
|
||||||
if (!this[field]) {
|
if (!this[field]) {
|
||||||
this[field] = [];
|
this[field] = [];
|
||||||
this.codes[field] = [];
|
this.codes[field] = [];
|
||||||
}
|
}
|
||||||
this[field].push(message);
|
this[field].push(message);
|
||||||
this.codes[field].push(code);
|
this.codes[field].push(code);
|
||||||
};
|
};
|
||||||
|
|
||||||
function ErrorCodes(messages) {
|
function ErrorCodes(messages) {
|
||||||
var c = this;
|
var c = this;
|
||||||
Object.keys(messages).forEach(function(field) {
|
Object.keys(messages).forEach(function (field) {
|
||||||
c[field] = messages[field].codes;
|
c[field] = messages[field].codes;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function ValidationError(obj) {
|
function ValidationError(obj) {
|
||||||
if (!(this instanceof ValidationError)) return new ValidationError(obj);
|
if (!(this instanceof ValidationError)) return new ValidationError(obj);
|
||||||
|
|
||||||
this.name = 'ValidationError';
|
this.name = 'ValidationError';
|
||||||
this.message = 'The Model instance is not valid. ' +
|
this.message = 'The Model instance is not valid. ' +
|
||||||
'See `details` property of the error object for more info.';
|
'See `details` property of the error object for more info.';
|
||||||
this.statusCode = 422;
|
this.statusCode = 422;
|
||||||
|
|
||||||
this.details = {
|
this.details = {
|
||||||
context: obj && obj.constructor && obj.constructor.modelName,
|
context: obj && obj.constructor && obj.constructor.modelName,
|
||||||
codes: obj.errors && obj.errors.codes,
|
codes: obj.errors && obj.errors.codes,
|
||||||
messages: obj.errors
|
messages: obj.errors
|
||||||
};
|
};
|
||||||
|
|
||||||
Error.captureStackTrace(this, this.constructor);
|
Error.captureStackTrace(this, this.constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(ValidationError, Error);
|
util.inherits(ValidationError, Error);
|
||||||
|
|
|
@ -2,347 +2,345 @@
|
||||||
var should = require('./init.js');
|
var should = require('./init.js');
|
||||||
var db, User;
|
var db, User;
|
||||||
|
|
||||||
describe('basic-querying', function() {
|
describe('basic-querying', function () {
|
||||||
|
|
||||||
before(function(done) {
|
before(function (done) {
|
||||||
db = getSchema();
|
db = getSchema();
|
||||||
|
|
||||||
User = db.define('User', {
|
|
||||||
name: {type: String, index: true, sort: true},
|
|
||||||
email: {type: String, index: true},
|
|
||||||
role: {type: String, index: true},
|
|
||||||
order: {type: Number, index: true, sort: true}
|
|
||||||
});
|
|
||||||
|
|
||||||
db.automigrate(done);
|
|
||||||
|
|
||||||
|
User = db.define('User', {
|
||||||
|
name: {type: String, index: true, sort: true},
|
||||||
|
email: {type: String, index: true},
|
||||||
|
role: {type: String, index: true},
|
||||||
|
order: {type: Number, index: true, sort: true}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
db.automigrate(done);
|
||||||
|
|
||||||
describe('findById', function() {
|
});
|
||||||
|
|
||||||
before(function(done) {
|
describe('findById', function () {
|
||||||
User.destroyAll(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should query by id: not found', function(done) {
|
|
||||||
User.findById(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.not.exist(err);
|
|
||||||
should.exist(u.id);
|
|
||||||
User.findById(u.id, function(err, u) {
|
|
||||||
should.exist(u);
|
|
||||||
should.not.exist(err);
|
|
||||||
u.should.be.an.instanceOf(User);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
User.destroyAll(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('find', function() {
|
it('should query by id: not found', function (done) {
|
||||||
|
User.findById(1, function (err, u) {
|
||||||
|
should.not.exist(u);
|
||||||
|
should.not.exist(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
before(seed);
|
it('should query by id: found', function (done) {
|
||||||
|
User.create(function (err, u) {
|
||||||
it('should query collection', function(done) {
|
should.not.exist(err);
|
||||||
User.find(function(err, users) {
|
should.exist(u.id);
|
||||||
should.exists(users);
|
User.findById(u.id, function (err, u) {
|
||||||
should.not.exists(err);
|
should.exist(u);
|
||||||
users.should.have.lengthOf(6);
|
should.not.exist(err);
|
||||||
done();
|
u.should.be.an.instanceOf(User);
|
||||||
});
|
done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should query limited collection', function(done) {
|
});
|
||||||
User.find({limit: 3}, function(err, users) {
|
|
||||||
should.exists(users);
|
describe('find', function () {
|
||||||
should.not.exists(err);
|
|
||||||
users.should.have.lengthOf(3);
|
before(seed);
|
||||||
done();
|
|
||||||
});
|
it('should query collection', function (done) {
|
||||||
|
User.find(function (err, users) {
|
||||||
|
should.exists(users);
|
||||||
|
should.not.exists(err);
|
||||||
|
users.should.have.lengthOf(6);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query limited collection', function (done) {
|
||||||
|
User.find({limit: 3}, function (err, users) {
|
||||||
|
should.exists(users);
|
||||||
|
should.not.exists(err);
|
||||||
|
users.should.have.lengthOf(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query offset collection with limit', function (done) {
|
||||||
|
User.find({skip: 1, limit: 4}, function (err, users) {
|
||||||
|
should.exists(users);
|
||||||
|
should.not.exists(err);
|
||||||
|
users.should.have.lengthOf(4);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query filtered collection', function (done) {
|
||||||
|
User.find({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.find({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 offset collection with limit', function(done) {
|
it('should query collection desc sorted by numeric field', function (done) {
|
||||||
User.find({skip: 1, limit: 4}, function(err, users) {
|
User.find({order: 'order DESC'}, function (err, users) {
|
||||||
should.exists(users);
|
should.exists(users);
|
||||||
should.not.exists(err);
|
should.not.exists(err);
|
||||||
users.should.have.lengthOf(4);
|
users.forEach(function (u, i) {
|
||||||
done();
|
u.order.should.eql(users.length - i);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should query filtered collection', function(done) {
|
it('should query collection sorted by string field', function (done) {
|
||||||
User.find({where: {role: 'lead'}}, function(err, users) {
|
User.find({order: 'name'}, function (err, users) {
|
||||||
should.exists(users);
|
should.exists(users);
|
||||||
should.not.exists(err);
|
should.not.exists(err);
|
||||||
users.should.have.lengthOf(2);
|
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.find({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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should only include fields as specified', function (done) {
|
||||||
|
var remaining = 0;
|
||||||
|
|
||||||
|
function sample(fields) {
|
||||||
|
|
||||||
|
return {
|
||||||
|
expect: function (arr) {
|
||||||
|
remaining++;
|
||||||
|
User.find({fields: fields}, function (err, users) {
|
||||||
|
|
||||||
|
remaining--;
|
||||||
|
if (err) return done(err);
|
||||||
|
|
||||||
|
should.exists(users);
|
||||||
|
|
||||||
|
if (remaining === 0) {
|
||||||
done();
|
done();
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should query collection sorted by numeric field', function(done) {
|
|
||||||
User.find({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.find({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.find({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.find({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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should only include fields as specified', function(done) {
|
|
||||||
var remaining = 0;
|
|
||||||
|
|
||||||
function sample(fields) {
|
|
||||||
|
|
||||||
return {
|
|
||||||
expect: function (arr) {
|
|
||||||
remaining++;
|
|
||||||
User.find({fields: fields}, function(err, users) {
|
|
||||||
|
|
||||||
remaining--;
|
|
||||||
if(err) return done(err);
|
|
||||||
|
|
||||||
should.exists(users);
|
|
||||||
|
|
||||||
if(remaining === 0) {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
|
|
||||||
users.forEach(function (user) {
|
|
||||||
var obj = user.toObject();
|
|
||||||
|
|
||||||
Object.keys(obj)
|
|
||||||
.forEach(function (key) {
|
|
||||||
// if the obj has an unexpected value
|
|
||||||
if(obj[key] !== undefined && arr.indexOf(key) === -1) {
|
|
||||||
console.log('Given fields:', fields);
|
|
||||||
console.log('Got:', key, obj[key]);
|
|
||||||
console.log('Expected:', arr);
|
|
||||||
throw new Error('should not include data for key: '+ key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
sample({name: true}).expect(['name']);
|
users.forEach(function (user) {
|
||||||
sample({name: false}).expect(['id', 'email', 'role', 'order']);
|
var obj = user.toObject();
|
||||||
sample({name: false, id: true}).expect(['id']);
|
|
||||||
sample({id: true}).expect(['id']);
|
|
||||||
sample('id').expect(['id']);
|
|
||||||
sample(['id']).expect(['id']);
|
|
||||||
sample(['email']).expect(['email']);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
Object.keys(obj)
|
||||||
|
.forEach(function (key) {
|
||||||
|
// if the obj has an unexpected value
|
||||||
|
if (obj[key] !== undefined && arr.indexOf(key) === -1) {
|
||||||
|
console.log('Given fields:', fields);
|
||||||
|
console.log('Got:', key, obj[key]);
|
||||||
|
console.log('Expected:', arr);
|
||||||
|
throw new Error('should not include data for key: ' + key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sample({name: true}).expect(['name']);
|
||||||
|
sample({name: false}).expect(['id', 'email', 'role', 'order']);
|
||||||
|
sample({name: false, id: true}).expect(['id']);
|
||||||
|
sample({id: true}).expect(['id']);
|
||||||
|
sample('id').expect(['id']);
|
||||||
|
sample(['id']).expect(['id']);
|
||||||
|
sample(['email']).expect(['email']);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('count', function() {
|
});
|
||||||
|
|
||||||
before(seed);
|
describe('count', function () {
|
||||||
|
|
||||||
it('should query total count', function(done) {
|
before(seed);
|
||||||
User.count(function(err, n) {
|
|
||||||
should.not.exist(err);
|
|
||||||
should.exist(n);
|
|
||||||
n.should.equal(6);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should query filtered count', function(done) {
|
it('should query total count', function (done) {
|
||||||
User.count({role: 'lead'}, function(err, n) {
|
User.count(function (err, n) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(n);
|
should.exist(n);
|
||||||
n.should.equal(2);
|
n.should.equal(6);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('findOne', function() {
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
before(seed);
|
describe('findOne', function () {
|
||||||
|
|
||||||
it('should find first record (default sort by id)', function(done) {
|
before(seed);
|
||||||
User.all({order: 'id'}, function(err, users) {
|
|
||||||
User.findOne(function(e, u) {
|
it('should find first record (default sort by id)', function (done) {
|
||||||
should.not.exist(e);
|
User.all({order: 'id'}, function (err, users) {
|
||||||
should.exist(u);
|
User.findOne(function (e, u) {
|
||||||
u.id.toString().should.equal(users[0].id.toString());
|
should.not.exist(e);
|
||||||
done();
|
should.exist(u);
|
||||||
});
|
u.id.toString().should.equal(users[0].id.toString());
|
||||||
});
|
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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work even when find by id', function(done) {
|
|
||||||
User.findOne(function(e, u) {
|
|
||||||
User.findOne({where: {id: u.id}}, function(err, user) {
|
|
||||||
should.not.exist(err);
|
|
||||||
should.exist(user);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('exists', function() {
|
it('should find first record', function (done) {
|
||||||
|
User.findOne({order: 'order'}, function (e, u) {
|
||||||
before(seed);
|
should.not.exist(e);
|
||||||
|
should.exist(u);
|
||||||
it('should check whether record exist', function(done) {
|
u.order.should.equal(1);
|
||||||
User.findOne(function(e, u) {
|
u.name.should.equal('Paul McCartney');
|
||||||
User.exists(u.id, function(err, exists) {
|
done();
|
||||||
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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('destroyAll with where option', function() {
|
it('should find last record', function (done) {
|
||||||
|
User.findOne({order: 'order DESC'}, function (e, u) {
|
||||||
before(seed);
|
should.not.exist(e);
|
||||||
|
should.exist(u);
|
||||||
it('should only delete instances that satisfy the where condition', function(done) {
|
u.order.should.equal(6);
|
||||||
User.destroyAll({name: 'John Lennon'}, function() {
|
u.name.should.equal('Ringo Starr');
|
||||||
User.find({where: {name: 'John Lennon'}}, function(err, data) {
|
done();
|
||||||
should.not.exist(err);
|
});
|
||||||
data.length.should.equal(0);
|
|
||||||
User.find({where: {name: 'Paul McCartney'}}, function(err, data) {
|
|
||||||
should.not.exist(err);
|
|
||||||
data.length.should.equal(1);
|
|
||||||
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work even when find by id', function (done) {
|
||||||
|
User.findOne(function (e, u) {
|
||||||
|
User.findOne({where: {id: u.id}}, function (err, user) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(user);
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('destroyAll with where option', function () {
|
||||||
|
|
||||||
|
before(seed);
|
||||||
|
|
||||||
|
it('should only delete instances that satisfy the where condition', function (done) {
|
||||||
|
User.destroyAll({name: 'John Lennon'}, function () {
|
||||||
|
User.find({where: {name: 'John Lennon'}}, function (err, data) {
|
||||||
|
should.not.exist(err);
|
||||||
|
data.length.should.equal(0);
|
||||||
|
User.find({where: {name: 'Paul McCartney'}}, function (err, data) {
|
||||||
|
should.not.exist(err);
|
||||||
|
data.length.should.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function seed(done) {
|
function seed(done) {
|
||||||
var count = 0;
|
var count = 0;
|
||||||
var beatles = [
|
var beatles = [
|
||||||
{
|
{
|
||||||
name: 'John Lennon',
|
name: 'John Lennon',
|
||||||
email: 'john@b3atl3s.co.uk',
|
email: 'john@b3atl3s.co.uk',
|
||||||
role: 'lead',
|
role: 'lead',
|
||||||
order: 2
|
order: 2
|
||||||
}, {
|
},
|
||||||
name: 'Paul McCartney',
|
{
|
||||||
email: 'paul@b3atl3s.co.uk',
|
name: 'Paul McCartney',
|
||||||
role: 'lead',
|
email: 'paul@b3atl3s.co.uk',
|
||||||
order: 1
|
role: 'lead',
|
||||||
},
|
order: 1
|
||||||
{name: 'George Harrison', order: 5},
|
},
|
||||||
{name: 'Ringo Starr', order: 6},
|
{name: 'George Harrison', order: 5},
|
||||||
{name: 'Pete Best', order: 4},
|
{name: 'Ringo Starr', order: 6},
|
||||||
{name: 'Stuart Sutcliffe', order: 3}
|
{name: 'Pete Best', order: 4},
|
||||||
];
|
{name: 'Stuart Sutcliffe', order: 3}
|
||||||
User.destroyAll(function() {
|
];
|
||||||
beatles.forEach(function(beatle) {
|
User.destroyAll(function () {
|
||||||
User.create(beatle, ok);
|
beatles.forEach(function (beatle) {
|
||||||
});
|
User.create(beatle, ok);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function ok() {
|
function ok() {
|
||||||
if (++count === beatles.length) {
|
if (++count === beatles.length) {
|
||||||
done();
|
done();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
2065
test/common_test.js
2065
test/common_test.js
File diff suppressed because it is too large
Load Diff
|
@ -3,64 +3,64 @@ var should = require('./init.js');
|
||||||
|
|
||||||
var db, Model;
|
var db, Model;
|
||||||
|
|
||||||
describe('datatypes', function() {
|
describe('datatypes', function () {
|
||||||
|
|
||||||
before(function(done){
|
before(function (done) {
|
||||||
db = getSchema();
|
db = getSchema();
|
||||||
Model = db.define('Model', {
|
Model = db.define('Model', {
|
||||||
str: String,
|
str: String,
|
||||||
date: Date,
|
date: Date,
|
||||||
num: Number,
|
num: Number,
|
||||||
bool: Boolean,
|
bool: Boolean,
|
||||||
list: {type: []},
|
list: {type: []},
|
||||||
});
|
});
|
||||||
db.automigrate(function() {
|
db.automigrate(function () {
|
||||||
Model.destroyAll(done);
|
Model.destroyAll(done);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep types when get read data from db', function (done) {
|
||||||
|
var d = new Date, id;
|
||||||
|
|
||||||
|
Model.create({
|
||||||
|
str: 'hello', date: d, num: '3', bool: 1, list: ['test']
|
||||||
|
}, function (err, m) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(m && m.id);
|
||||||
|
m.str.should.be.a('string');
|
||||||
|
m.num.should.be.a('number');
|
||||||
|
m.bool.should.be.a('boolean');
|
||||||
|
id = m.id;
|
||||||
|
testFind(testAll);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should keep types when get read data from db', function(done) {
|
function testFind(next) {
|
||||||
var d = new Date, id;
|
debugger;
|
||||||
|
Model.findById(id, function (err, m) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(m);
|
||||||
|
m.str.should.be.a('string');
|
||||||
|
m.num.should.be.a('number');
|
||||||
|
m.bool.should.be.a('boolean');
|
||||||
|
m.date.should.be.an.instanceOf(Date);
|
||||||
|
m.date.toString().should.equal(d.toString(), 'Time must match');
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Model.create({
|
function testAll() {
|
||||||
str: 'hello', date: d, num: '3', bool: 1, list: ['test']
|
Model.findOne(function (err, m) {
|
||||||
}, function(err, m) {
|
should.not.exist(err);
|
||||||
should.not.exist(err);
|
should.exist(m);
|
||||||
should.exist(m && m.id);
|
m.str.should.be.a('string');
|
||||||
m.str.should.be.a('string');
|
m.num.should.be.a('number');
|
||||||
m.num.should.be.a('number');
|
m.bool.should.be.a('boolean');
|
||||||
m.bool.should.be.a('boolean');
|
m.date.should.be.an.instanceOf(Date);
|
||||||
id = m.id;
|
m.date.toString().should.equal(d.toString(), 'Time must match');
|
||||||
testFind(testAll);
|
done();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function testFind(next) {
|
});
|
||||||
debugger;
|
|
||||||
Model.findById(id, function(err, m) {
|
|
||||||
should.not.exist(err);
|
|
||||||
should.exist(m);
|
|
||||||
m.str.should.be.a('string');
|
|
||||||
m.num.should.be.a('number');
|
|
||||||
m.bool.should.be.a('boolean');
|
|
||||||
m.date.should.be.an.instanceOf(Date);
|
|
||||||
m.date.toString().should.equal(d.toString(), 'Time must match');
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testAll() {
|
|
||||||
Model.findOne(function(err, m) {
|
|
||||||
should.not.exist(err);
|
|
||||||
should.exist(m);
|
|
||||||
m.str.should.be.a('string');
|
|
||||||
m.num.should.be.a('number');
|
|
||||||
m.bool.should.be.a('boolean');
|
|
||||||
m.date.should.be.an.instanceOf(Date);
|
|
||||||
m.date.toString().should.equal(d.toString(), 'Time must match');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,36 +3,36 @@ var should = require('./init.js');
|
||||||
|
|
||||||
var db = getSchema();
|
var db = getSchema();
|
||||||
|
|
||||||
describe('defaults', function() {
|
describe('defaults', function () {
|
||||||
var Server;
|
var Server;
|
||||||
|
|
||||||
before(function() {
|
before(function () {
|
||||||
Server = db.define('Server', {
|
Server = db.define('Server', {
|
||||||
host: String,
|
host: String,
|
||||||
port: {type: Number, default: 80}
|
port: {type: Number, default: 80}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should apply defaults on new', function() {
|
it('should apply defaults on new', function () {
|
||||||
var s = new Server;
|
var s = new Server;
|
||||||
s.port.should.equal(80);
|
s.port.should.equal(80);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply defaults on create', function(done) {
|
it('should apply defaults on create', function (done) {
|
||||||
Server.create(function(err, s) {
|
Server.create(function (err, s) {
|
||||||
s.port.should.equal(80);
|
s.port.should.equal(80);
|
||||||
done();
|
done();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should apply defaults on read', function(done) {
|
it('should apply defaults on read', function (done) {
|
||||||
db.defineProperty('Server', 'host', {
|
db.defineProperty('Server', 'host', {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'localhost'
|
default: 'localhost'
|
||||||
});
|
|
||||||
Server.all(function (err, servers) {
|
|
||||||
(new String('localhost')).should.equal(servers[0].host);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
Server.all(function (err, servers) {
|
||||||
|
(new String('localhost')).should.equal(servers[0].host);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,391 +2,423 @@
|
||||||
var should = require('./init.js');
|
var should = require('./init.js');
|
||||||
|
|
||||||
var j = require('../'),
|
var j = require('../'),
|
||||||
Schema = j.Schema,
|
Schema = j.Schema,
|
||||||
AbstractClass = j.AbstractClass,
|
AbstractClass = j.AbstractClass,
|
||||||
Hookable = j.Hookable,
|
Hookable = j.Hookable,
|
||||||
|
|
||||||
db, User;
|
db, User;
|
||||||
|
|
||||||
describe('hooks', function() {
|
describe('hooks', function () {
|
||||||
|
|
||||||
before(function(done) {
|
before(function (done) {
|
||||||
db = getSchema();
|
db = getSchema();
|
||||||
|
|
||||||
User = db.define('User', {
|
User = db.define('User', {
|
||||||
email: {type: String, index: true},
|
email: {type: String, index: true},
|
||||||
name: String,
|
name: String,
|
||||||
password: String,
|
password: String,
|
||||||
state: String
|
state: String
|
||||||
});
|
|
||||||
|
|
||||||
db.automigrate(done);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('initialize', function() {
|
db.automigrate(done);
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
describe('initialize', function () {
|
||||||
User.afterInitialize = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be triggered on new', function(done) {
|
|
||||||
User.afterInitialize = function() {
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
new User;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be triggered on create', function(done) {
|
|
||||||
var user;
|
|
||||||
User.afterInitialize = function() {
|
|
||||||
if (this.name === 'Nickolay') {
|
|
||||||
this.name += ' Rozental';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
User.create({name: 'Nickolay'}, function(err, u) {
|
|
||||||
u.id.should.be.ok;
|
|
||||||
u.name.should.equal('Nickolay Rozental');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
User.afterInitialize = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('create', function() {
|
it('should be triggered on new', function (done) {
|
||||||
|
User.afterInitialize = function () {
|
||||||
afterEach(removeHooks('Create'));
|
done();
|
||||||
|
};
|
||||||
it('should be triggered on create', function(done) {
|
new User;
|
||||||
addHooks('Create', done);
|
|
||||||
User.create();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be triggered on new', function() {
|
|
||||||
User.beforeCreate = function(next) {
|
|
||||||
should.fail('This should not be called');
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
var u = new User;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be triggered on new+save', function(done) {
|
|
||||||
addHooks('Create', done);
|
|
||||||
(new User).save();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('afterCreate should not be triggered on failed create', function(done) {
|
|
||||||
var old = User.dataSource.connector.create;
|
|
||||||
User.dataSource.connector.create = function(modelName, id, cb) {
|
|
||||||
cb(new Error('error'));
|
|
||||||
}
|
|
||||||
|
|
||||||
User.afterCreate = function() {
|
|
||||||
throw new Error('shouldn\'t be called')
|
|
||||||
};
|
|
||||||
User.create(function (err, user) {
|
|
||||||
User.dataSource.connector.create = old;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('save', function() {
|
it('should be triggered on create', function (done) {
|
||||||
afterEach(removeHooks('Save'));
|
var user;
|
||||||
|
User.afterInitialize = function () {
|
||||||
it('should be triggered on create', function(done) {
|
if (this.name === 'Nickolay') {
|
||||||
addHooks('Save', done);
|
this.name += ' Rozental';
|
||||||
User.create();
|
}
|
||||||
});
|
};
|
||||||
|
User.create({name: 'Nickolay'}, function (err, u) {
|
||||||
it('should be triggered on new+save', function(done) {
|
u.id.should.be.ok;
|
||||||
addHooks('Save', done);
|
u.name.should.equal('Nickolay Rozental');
|
||||||
(new User).save();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be triggered on updateAttributes', function(done) {
|
|
||||||
User.create(function(err, user) {
|
|
||||||
addHooks('Save', done);
|
|
||||||
user.updateAttributes({name: 'Anatoliy'});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be triggered on save', function(done) {
|
|
||||||
User.create(function(err, user) {
|
|
||||||
addHooks('Save', done);
|
|
||||||
user.name = 'Hamburger';
|
|
||||||
user.save();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should save full object', function(done) {
|
|
||||||
User.create(function(err, user) {
|
|
||||||
User.beforeSave = function(next, data) {
|
|
||||||
data.should.have.keys('id', 'name', 'email',
|
|
||||||
'password', 'state')
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
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: '53cr3t'
|
|
||||||
}, function() {
|
|
||||||
User.findOne({
|
|
||||||
where: {email: 'james.bond@example.com'}
|
|
||||||
}, function(err, jb) {
|
|
||||||
jb.password.should.equal('hash');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should save actual modifications on updateAttributes', function(done) {
|
|
||||||
User.beforeSave = function(next, data) {
|
|
||||||
data.password = 'hash';
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
User.destroyAll(function() {
|
|
||||||
User.create({
|
|
||||||
email: 'james.bond@example.com'
|
|
||||||
}, function(err, u) {
|
|
||||||
u.updateAttribute('password', 'new password', function(e, u) {
|
|
||||||
should.not.exist(e);
|
|
||||||
should.exist(u);
|
|
||||||
u.password.should.equal('hash');
|
|
||||||
User.findOne({
|
|
||||||
where: {email: 'james.bond@example.com'}
|
|
||||||
}, function(err, jb) {
|
|
||||||
jb.password.should.equal('hash');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('update', function() {
|
});
|
||||||
afterEach(removeHooks('Update'));
|
|
||||||
|
|
||||||
it('should not be triggered on create', function() {
|
describe('create', function () {
|
||||||
User.beforeUpdate = function(next) {
|
|
||||||
should.fail('This should not be called');
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
User.create();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be triggered on new+save', function() {
|
afterEach(removeHooks('Create'));
|
||||||
User.beforeUpdate = function(next) {
|
|
||||||
should.fail('This should not be called');
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
(new User).save();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be triggered on updateAttributes', function(done) {
|
it('should be triggered on create', function (done) {
|
||||||
User.create(function (err, user) {
|
addHooks('Create', done);
|
||||||
addHooks('Update', done);
|
User.create();
|
||||||
user.updateAttributes({name: 'Anatoliy'});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be triggered on save', function(done) {
|
|
||||||
User.create(function (err, user) {
|
|
||||||
addHooks('Update', done);
|
|
||||||
user.name = 'Hamburger';
|
|
||||||
user.save();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update limited set of fields', function(done) {
|
|
||||||
User.create(function (err, user) {
|
|
||||||
User.beforeUpdate = function(next, data) {
|
|
||||||
data.should.have.keys('name', 'email');
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
user.updateAttributes({name: 1, email: 2});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not trigger after-hook on failed save', function(done) {
|
|
||||||
User.afterUpdate = function() {
|
|
||||||
should.fail('afterUpdate shouldn\'t be called')
|
|
||||||
};
|
|
||||||
User.create(function (err, user) {
|
|
||||||
var save = User.dataSource.connector.save;
|
|
||||||
User.dataSource.connector.save = function(modelName, id, cb) {
|
|
||||||
User.dataSource.connector.save = save;
|
|
||||||
cb(new Error('Error'));
|
|
||||||
}
|
|
||||||
|
|
||||||
user.save(function(err) {
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('destroy', function() {
|
it('should not be triggered on new', function () {
|
||||||
|
User.beforeCreate = function (next) {
|
||||||
afterEach(removeHooks('Destroy'));
|
should.fail('This should not be called');
|
||||||
|
next();
|
||||||
it('should be triggered on destroy', function(done) {
|
};
|
||||||
var hook = 'not called';
|
var u = new User;
|
||||||
User.beforeDestroy = function(next) {
|
|
||||||
hook = 'called';
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
User.afterDestroy = function(next) {
|
|
||||||
hook.should.eql('called');
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
User.create(function (err, user) {
|
|
||||||
user.destroy(done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not trigger after-hook on failed destroy', function(done) {
|
|
||||||
var destroy = User.dataSource.connector.destroy;
|
|
||||||
User.dataSource.connector.destroy = function(modelName, id, cb) {
|
|
||||||
cb(new Error('error'));
|
|
||||||
}
|
|
||||||
User.afterDestroy = function() {
|
|
||||||
should.fail('afterDestroy shouldn\'t be called')
|
|
||||||
};
|
|
||||||
User.create(function (err, user) {
|
|
||||||
user.destroy(function(err) {
|
|
||||||
User.dataSource.connector.destroy = destroy;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('lifecycle', function() {
|
it('should be triggered on new+save', function (done) {
|
||||||
var life = [], user;
|
addHooks('Create', done);
|
||||||
before(function(done) {
|
(new User).save();
|
||||||
User.beforeSave = function(d){life.push('beforeSave'); d();};
|
|
||||||
User.beforeCreate = function(d){life.push('beforeCreate'); d();};
|
|
||||||
User.beforeUpdate = function(d){life.push('beforeUpdate'); d();};
|
|
||||||
User.beforeDestroy = function(d){life.push('beforeDestroy');d();};
|
|
||||||
User.beforeValidate = function(d){life.push('beforeValidate');d();};
|
|
||||||
User.afterInitialize= function( ){life.push('afterInitialize'); };
|
|
||||||
User.afterSave = function(d){life.push('afterSave'); d();};
|
|
||||||
User.afterCreate = function(d){life.push('afterCreate'); d();};
|
|
||||||
User.afterUpdate = function(d){life.push('afterUpdate'); d();};
|
|
||||||
User.afterDestroy = function(d){life.push('afterDestroy'); d();};
|
|
||||||
User.afterValidate = function(d){life.push('afterValidate');d();};
|
|
||||||
User.create(function(e, u) {
|
|
||||||
user = u;
|
|
||||||
life = [];
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
beforeEach(function() {
|
|
||||||
life = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should describe create sequence', function(done) {
|
|
||||||
User.create(function() {
|
|
||||||
life.should.eql([
|
|
||||||
'afterInitialize',
|
|
||||||
'beforeValidate',
|
|
||||||
'afterValidate',
|
|
||||||
'beforeCreate',
|
|
||||||
'beforeSave',
|
|
||||||
'afterSave',
|
|
||||||
'afterCreate'
|
|
||||||
]);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should describe new+save sequence', function(done) {
|
|
||||||
var u = new User;
|
|
||||||
u.save(function() {
|
|
||||||
life.should.eql([
|
|
||||||
'afterInitialize',
|
|
||||||
'beforeValidate',
|
|
||||||
'afterValidate',
|
|
||||||
'beforeCreate',
|
|
||||||
'beforeSave',
|
|
||||||
'afterSave',
|
|
||||||
'afterCreate'
|
|
||||||
]);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should describe updateAttributes sequence', function(done) {
|
|
||||||
user.updateAttributes({name: 'Antony'}, function() {
|
|
||||||
life.should.eql([
|
|
||||||
'beforeValidate',
|
|
||||||
'afterValidate',
|
|
||||||
'beforeSave',
|
|
||||||
'beforeUpdate',
|
|
||||||
'afterUpdate',
|
|
||||||
'afterSave',
|
|
||||||
]);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should describe isValid sequence', function(done) {
|
|
||||||
should.not.exist(
|
|
||||||
user.constructor._validations,
|
|
||||||
'Expected user to have no validations, but she have');
|
|
||||||
user.isValid(function(valid) {
|
|
||||||
valid.should.be.true;
|
|
||||||
life.should.eql([
|
|
||||||
'beforeValidate',
|
|
||||||
'afterValidate'
|
|
||||||
]);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should describe destroy sequence', function(done) {
|
|
||||||
user.destroy(function() {
|
|
||||||
life.should.eql([
|
|
||||||
'beforeDestroy',
|
|
||||||
'afterDestroy'
|
|
||||||
]);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('afterCreate should not be triggered on failed create', function (done) {
|
||||||
|
var old = User.dataSource.connector.create;
|
||||||
|
User.dataSource.connector.create = function (modelName, id, cb) {
|
||||||
|
cb(new Error('error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
User.afterCreate = function () {
|
||||||
|
throw new Error('shouldn\'t be called')
|
||||||
|
};
|
||||||
|
User.create(function (err, user) {
|
||||||
|
User.dataSource.connector.create = old;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('save', function () {
|
||||||
|
afterEach(removeHooks('Save'));
|
||||||
|
|
||||||
|
it('should be triggered on create', function (done) {
|
||||||
|
addHooks('Save', done);
|
||||||
|
User.create();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be triggered on new+save', function (done) {
|
||||||
|
addHooks('Save', done);
|
||||||
|
(new User).save();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be triggered on updateAttributes', function (done) {
|
||||||
|
User.create(function (err, user) {
|
||||||
|
addHooks('Save', done);
|
||||||
|
user.updateAttributes({name: 'Anatoliy'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be triggered on save', function (done) {
|
||||||
|
User.create(function (err, user) {
|
||||||
|
addHooks('Save', done);
|
||||||
|
user.name = 'Hamburger';
|
||||||
|
user.save();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should save full object', function (done) {
|
||||||
|
User.create(function (err, user) {
|
||||||
|
User.beforeSave = function (next, data) {
|
||||||
|
data.should.have.keys('id', 'name', 'email',
|
||||||
|
'password', 'state')
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
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: '53cr3t'
|
||||||
|
}, function () {
|
||||||
|
User.findOne({
|
||||||
|
where: {email: 'james.bond@example.com'}
|
||||||
|
}, function (err, jb) {
|
||||||
|
jb.password.should.equal('hash');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should save actual modifications on updateAttributes', function (done) {
|
||||||
|
User.beforeSave = function (next, data) {
|
||||||
|
data.password = 'hash';
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
User.destroyAll(function () {
|
||||||
|
User.create({
|
||||||
|
email: 'james.bond@example.com'
|
||||||
|
}, function (err, u) {
|
||||||
|
u.updateAttribute('password', 'new password', function (e, u) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(u);
|
||||||
|
u.password.should.equal('hash');
|
||||||
|
User.findOne({
|
||||||
|
where: {email: 'james.bond@example.com'}
|
||||||
|
}, function (err, jb) {
|
||||||
|
jb.password.should.equal('hash');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('update', function () {
|
||||||
|
afterEach(removeHooks('Update'));
|
||||||
|
|
||||||
|
it('should not be triggered on create', function () {
|
||||||
|
User.beforeUpdate = function (next) {
|
||||||
|
should.fail('This should not be called');
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
User.create();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be triggered on new+save', function () {
|
||||||
|
User.beforeUpdate = function (next) {
|
||||||
|
should.fail('This should not be called');
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
(new User).save();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be triggered on updateAttributes', function (done) {
|
||||||
|
User.create(function (err, user) {
|
||||||
|
addHooks('Update', done);
|
||||||
|
user.updateAttributes({name: 'Anatoliy'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be triggered on save', function (done) {
|
||||||
|
User.create(function (err, user) {
|
||||||
|
addHooks('Update', done);
|
||||||
|
user.name = 'Hamburger';
|
||||||
|
user.save();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update limited set of fields', function (done) {
|
||||||
|
User.create(function (err, user) {
|
||||||
|
User.beforeUpdate = function (next, data) {
|
||||||
|
data.should.have.keys('name', 'email');
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
user.updateAttributes({name: 1, email: 2});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not trigger after-hook on failed save', function (done) {
|
||||||
|
User.afterUpdate = function () {
|
||||||
|
should.fail('afterUpdate shouldn\'t be called')
|
||||||
|
};
|
||||||
|
User.create(function (err, user) {
|
||||||
|
var save = User.dataSource.connector.save;
|
||||||
|
User.dataSource.connector.save = function (modelName, id, cb) {
|
||||||
|
User.dataSource.connector.save = save;
|
||||||
|
cb(new Error('Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
user.save(function (err) {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('destroy', function () {
|
||||||
|
|
||||||
|
afterEach(removeHooks('Destroy'));
|
||||||
|
|
||||||
|
it('should be triggered on destroy', function (done) {
|
||||||
|
var hook = 'not called';
|
||||||
|
User.beforeDestroy = function (next) {
|
||||||
|
hook = 'called';
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
User.afterDestroy = function (next) {
|
||||||
|
hook.should.eql('called');
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
User.create(function (err, user) {
|
||||||
|
user.destroy(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not trigger after-hook on failed destroy', function (done) {
|
||||||
|
var destroy = User.dataSource.connector.destroy;
|
||||||
|
User.dataSource.connector.destroy = function (modelName, id, cb) {
|
||||||
|
cb(new Error('error'));
|
||||||
|
}
|
||||||
|
User.afterDestroy = function () {
|
||||||
|
should.fail('afterDestroy shouldn\'t be called')
|
||||||
|
};
|
||||||
|
User.create(function (err, user) {
|
||||||
|
user.destroy(function (err) {
|
||||||
|
User.dataSource.connector.destroy = destroy;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('lifecycle', function () {
|
||||||
|
var life = [], user;
|
||||||
|
before(function (done) {
|
||||||
|
User.beforeSave = function (d) {
|
||||||
|
life.push('beforeSave');
|
||||||
|
d();
|
||||||
|
};
|
||||||
|
User.beforeCreate = function (d) {
|
||||||
|
life.push('beforeCreate');
|
||||||
|
d();
|
||||||
|
};
|
||||||
|
User.beforeUpdate = function (d) {
|
||||||
|
life.push('beforeUpdate');
|
||||||
|
d();
|
||||||
|
};
|
||||||
|
User.beforeDestroy = function (d) {
|
||||||
|
life.push('beforeDestroy');
|
||||||
|
d();
|
||||||
|
};
|
||||||
|
User.beforeValidate = function (d) {
|
||||||
|
life.push('beforeValidate');
|
||||||
|
d();
|
||||||
|
};
|
||||||
|
User.afterInitialize = function () {
|
||||||
|
life.push('afterInitialize');
|
||||||
|
};
|
||||||
|
User.afterSave = function (d) {
|
||||||
|
life.push('afterSave');
|
||||||
|
d();
|
||||||
|
};
|
||||||
|
User.afterCreate = function (d) {
|
||||||
|
life.push('afterCreate');
|
||||||
|
d();
|
||||||
|
};
|
||||||
|
User.afterUpdate = function (d) {
|
||||||
|
life.push('afterUpdate');
|
||||||
|
d();
|
||||||
|
};
|
||||||
|
User.afterDestroy = function (d) {
|
||||||
|
life.push('afterDestroy');
|
||||||
|
d();
|
||||||
|
};
|
||||||
|
User.afterValidate = function (d) {
|
||||||
|
life.push('afterValidate');
|
||||||
|
d();
|
||||||
|
};
|
||||||
|
User.create(function (e, u) {
|
||||||
|
user = u;
|
||||||
|
life = [];
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
beforeEach(function () {
|
||||||
|
life = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should describe create sequence', function (done) {
|
||||||
|
User.create(function () {
|
||||||
|
life.should.eql([
|
||||||
|
'afterInitialize',
|
||||||
|
'beforeValidate',
|
||||||
|
'afterValidate',
|
||||||
|
'beforeCreate',
|
||||||
|
'beforeSave',
|
||||||
|
'afterSave',
|
||||||
|
'afterCreate'
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should describe new+save sequence', function (done) {
|
||||||
|
var u = new User;
|
||||||
|
u.save(function () {
|
||||||
|
life.should.eql([
|
||||||
|
'afterInitialize',
|
||||||
|
'beforeValidate',
|
||||||
|
'afterValidate',
|
||||||
|
'beforeCreate',
|
||||||
|
'beforeSave',
|
||||||
|
'afterSave',
|
||||||
|
'afterCreate'
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should describe updateAttributes sequence', function (done) {
|
||||||
|
user.updateAttributes({name: 'Antony'}, function () {
|
||||||
|
life.should.eql([
|
||||||
|
'beforeValidate',
|
||||||
|
'afterValidate',
|
||||||
|
'beforeSave',
|
||||||
|
'beforeUpdate',
|
||||||
|
'afterUpdate',
|
||||||
|
'afterSave',
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should describe isValid sequence', function (done) {
|
||||||
|
should.not.exist(
|
||||||
|
user.constructor._validations,
|
||||||
|
'Expected user to have no validations, but she have');
|
||||||
|
user.isValid(function (valid) {
|
||||||
|
valid.should.be.true;
|
||||||
|
life.should.eql([
|
||||||
|
'beforeValidate',
|
||||||
|
'afterValidate'
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should describe destroy sequence', function (done) {
|
||||||
|
user.destroy(function () {
|
||||||
|
life.should.eql([
|
||||||
|
'beforeDestroy',
|
||||||
|
'afterDestroy'
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function addHooks(name, done) {
|
function addHooks(name, done) {
|
||||||
var called = false, random = String(Math.floor(Math.random() * 1000));
|
var called = false, random = String(Math.floor(Math.random() * 1000));
|
||||||
User['before' + name] = function(next, data) {
|
User['before' + name] = function (next, data) {
|
||||||
called = true;
|
called = true;
|
||||||
data.email = random;
|
data.email = random;
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
User['after' + name] = function(next) {
|
User['after' + name] = function (next) {
|
||||||
(new Boolean(called)).should.equal(true);
|
(new Boolean(called)).should.equal(true);
|
||||||
this.email.should.equal(random);
|
this.email.should.equal(random);
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeHooks(name) {
|
function removeHooks(name) {
|
||||||
return function() {
|
return function () {
|
||||||
User['after' + name] = null;
|
User['after' + name] = null;
|
||||||
User['before' + name] = null;
|
User['before' + name] = null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,208 +4,209 @@ var should = require('./init.js');
|
||||||
var db, User, Post, Passport, City, Street, Building;
|
var db, User, Post, Passport, City, Street, Building;
|
||||||
var nbSchemaRequests = 0;
|
var nbSchemaRequests = 0;
|
||||||
|
|
||||||
describe('include', function() {
|
describe('include', function () {
|
||||||
|
|
||||||
before(setup);
|
before(setup);
|
||||||
|
|
||||||
it('should fetch belongsTo relation', function(done) {
|
it('should fetch belongsTo relation', function (done) {
|
||||||
Passport.all({include: 'owner'}, function (err, passports) {
|
Passport.all({include: 'owner'}, function (err, passports) {
|
||||||
passports.length.should.be.ok;
|
passports.length.should.be.ok;
|
||||||
passports.forEach(function(p) {
|
passports.forEach(function (p) {
|
||||||
p.__cachedRelations.should.have.property('owner');
|
p.__cachedRelations.should.have.property('owner');
|
||||||
var owner = p.__cachedRelations.owner;
|
var owner = p.__cachedRelations.owner;
|
||||||
if (!p.ownerId) {
|
if (!p.ownerId) {
|
||||||
should.not.exist(owner);
|
should.not.exist(owner);
|
||||||
} else {
|
} else {
|
||||||
should.exist(owner);
|
should.exist(owner);
|
||||||
owner.id.should.equal(p.ownerId);
|
owner.id.should.equal(p.ownerId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should fetch hasMany relation', function(done) {
|
it('should fetch hasMany relation', function (done) {
|
||||||
User.all({include: 'posts'}, function (err, users) {
|
User.all({include: 'posts'}, function (err, users) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(users);
|
should.exist(users);
|
||||||
users.length.should.be.ok;
|
users.length.should.be.ok;
|
||||||
users.forEach(function(u) {
|
users.forEach(function (u) {
|
||||||
u.__cachedRelations.should.have.property('posts');
|
u.__cachedRelations.should.have.property('posts');
|
||||||
u.__cachedRelations.posts.forEach(function(p) {
|
u.__cachedRelations.posts.forEach(function (p) {
|
||||||
p.userId.should.equal(u.id);
|
p.userId.should.equal(u.id);
|
||||||
});
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should fetch Passport - Owner - Posts', function(done) {
|
it('should fetch Passport - Owner - Posts', function (done) {
|
||||||
Passport.all({include: {owner: 'posts'}}, function(err, passports) {
|
Passport.all({include: {owner: 'posts'}}, function (err, passports) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(passports);
|
should.exist(passports);
|
||||||
passports.length.should.be.ok;
|
passports.length.should.be.ok;
|
||||||
passports.forEach(function(p) {
|
passports.forEach(function (p) {
|
||||||
p.__cachedRelations.should.have.property('owner');
|
p.__cachedRelations.should.have.property('owner');
|
||||||
var user = p.__cachedRelations.owner;
|
var user = p.__cachedRelations.owner;
|
||||||
if (!p.ownerId) {
|
if (!p.ownerId) {
|
||||||
should.not.exist(user);
|
should.not.exist(user);
|
||||||
} else {
|
} else {
|
||||||
should.exist(user);
|
should.exist(user);
|
||||||
user.id.should.equal(p.ownerId);
|
user.id.should.equal(p.ownerId);
|
||||||
user.__cachedRelations.should.have.property('posts');
|
user.__cachedRelations.should.have.property('posts');
|
||||||
user.__cachedRelations.posts.forEach(function(pp) {
|
user.__cachedRelations.posts.forEach(function (pp) {
|
||||||
pp.userId.should.equal(user.id);
|
pp.userId.should.equal(user.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should fetch Passports - User - Posts - User', function(done) {
|
it('should fetch Passports - User - Posts - User', function (done) {
|
||||||
Passport.all({
|
Passport.all({
|
||||||
include: {owner: {posts: 'author'}}
|
include: {owner: {posts: 'author'}}
|
||||||
}, function(err, passports) {
|
}, function (err, passports) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(passports);
|
should.exist(passports);
|
||||||
passports.length.should.be.ok;
|
passports.length.should.be.ok;
|
||||||
passports.forEach(function(p) {
|
passports.forEach(function (p) {
|
||||||
p.__cachedRelations.should.have.property('owner');
|
p.__cachedRelations.should.have.property('owner');
|
||||||
var user = p.__cachedRelations.owner;
|
var user = p.__cachedRelations.owner;
|
||||||
if (!p.ownerId) {
|
if (!p.ownerId) {
|
||||||
should.not.exist(user);
|
should.not.exist(user);
|
||||||
} else {
|
} else {
|
||||||
should.exist(user);
|
should.exist(user);
|
||||||
user.id.should.equal(p.ownerId);
|
user.id.should.equal(p.ownerId);
|
||||||
user.__cachedRelations.should.have.property('posts');
|
user.__cachedRelations.should.have.property('posts');
|
||||||
user.__cachedRelations.posts.forEach(function(pp) {
|
user.__cachedRelations.posts.forEach(function (pp) {
|
||||||
pp.userId.should.equal(user.id);
|
pp.userId.should.equal(user.id);
|
||||||
pp.__cachedRelations.should.have.property('author');
|
pp.__cachedRelations.should.have.property('author');
|
||||||
var author = pp.__cachedRelations.author;
|
var author = pp.__cachedRelations.author;
|
||||||
author.id.should.equal(user.id);
|
author.id.should.equal(user.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should fetch User - Posts AND Passports', function(done) {
|
it('should fetch User - Posts AND Passports', function (done) {
|
||||||
User.all({include: ['posts', 'passports']}, function(err, users) {
|
User.all({include: ['posts', 'passports']}, function (err, users) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(users);
|
should.exist(users);
|
||||||
users.length.should.be.ok;
|
users.length.should.be.ok;
|
||||||
users.forEach(function(user) {
|
users.forEach(function (user) {
|
||||||
user.__cachedRelations.should.have.property('posts');
|
user.__cachedRelations.should.have.property('posts');
|
||||||
user.__cachedRelations.should.have.property('passports');
|
user.__cachedRelations.should.have.property('passports');
|
||||||
user.__cachedRelations.posts.forEach(function(p) {
|
user.__cachedRelations.posts.forEach(function (p) {
|
||||||
p.userId.should.equal(user.id);
|
p.userId.should.equal(user.id);
|
||||||
});
|
|
||||||
user.__cachedRelations.passports.forEach(function(pp) {
|
|
||||||
pp.ownerId.should.equal(user.id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
user.__cachedRelations.passports.forEach(function (pp) {
|
||||||
|
pp.ownerId.should.equal(user.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function setup(done) {
|
function setup(done) {
|
||||||
db = getSchema();
|
db = getSchema();
|
||||||
City = db.define('City');
|
City = db.define('City');
|
||||||
Street = db.define('Street');
|
Street = db.define('Street');
|
||||||
Building = db.define('Building');
|
Building = db.define('Building');
|
||||||
User = db.define('User', {
|
User = db.define('User', {
|
||||||
name: String,
|
name: String,
|
||||||
age: Number
|
age: Number
|
||||||
});
|
});
|
||||||
Passport = db.define('Passport', {
|
Passport = db.define('Passport', {
|
||||||
number: String
|
number: String
|
||||||
});
|
});
|
||||||
Post = db.define('Post', {
|
Post = db.define('Post', {
|
||||||
title: String
|
title: String
|
||||||
});
|
});
|
||||||
|
|
||||||
Passport.belongsTo('owner', {model: User});
|
Passport.belongsTo('owner', {model: User});
|
||||||
User.hasMany('passports', {foreignKey: 'ownerId'});
|
User.hasMany('passports', {foreignKey: 'ownerId'});
|
||||||
User.hasMany('posts', {foreignKey: 'userId'});
|
User.hasMany('posts', {foreignKey: 'userId'});
|
||||||
Post.belongsTo('author', {model: User, foreignKey: 'userId'});
|
Post.belongsTo('author', {model: User, foreignKey: 'userId'});
|
||||||
|
|
||||||
db.automigrate(function() {
|
db.automigrate(function () {
|
||||||
var createdUsers = [];
|
var createdUsers = [];
|
||||||
var createdPassports = [];
|
var createdPassports = [];
|
||||||
var createdPosts = [];
|
var createdPosts = [];
|
||||||
createUsers();
|
createUsers();
|
||||||
function createUsers() {
|
function createUsers() {
|
||||||
clearAndCreate(
|
clearAndCreate(
|
||||||
User,
|
User,
|
||||||
[
|
[
|
||||||
{name: 'User A', age: 21},
|
{name: 'User A', age: 21},
|
||||||
{name: 'User B', age: 22},
|
{name: 'User B', age: 22},
|
||||||
{name: 'User C', age: 23},
|
{name: 'User C', age: 23},
|
||||||
{name: 'User D', age: 24},
|
{name: 'User D', age: 24},
|
||||||
{name: 'User E', age: 25}
|
{name: 'User E', age: 25}
|
||||||
],
|
],
|
||||||
function(items) {
|
function (items) {
|
||||||
createdUsers = items;
|
createdUsers = items;
|
||||||
createPassports();
|
createPassports();
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function createPassports() {
|
function createPassports() {
|
||||||
clearAndCreate(
|
clearAndCreate(
|
||||||
Passport,
|
Passport,
|
||||||
[
|
[
|
||||||
{number: '1', ownerId: createdUsers[0].id},
|
{number: '1', ownerId: createdUsers[0].id},
|
||||||
{number: '2', ownerId: createdUsers[1].id},
|
{number: '2', ownerId: createdUsers[1].id},
|
||||||
{number: '3'}
|
{number: '3'}
|
||||||
],
|
],
|
||||||
function(items) {
|
function (items) {
|
||||||
createdPassports = items;
|
createdPassports = items;
|
||||||
createPosts();
|
createPosts();
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function createPosts() {
|
function createPosts() {
|
||||||
clearAndCreate(
|
clearAndCreate(
|
||||||
Post,
|
Post,
|
||||||
[
|
[
|
||||||
{title: 'Post A', userId: createdUsers[0].id},
|
{title: 'Post A', userId: createdUsers[0].id},
|
||||||
{title: 'Post B', userId: createdUsers[0].id},
|
{title: 'Post B', userId: createdUsers[0].id},
|
||||||
{title: 'Post C', userId: createdUsers[0].id},
|
{title: 'Post C', userId: createdUsers[0].id},
|
||||||
{title: 'Post D', userId: createdUsers[1].id},
|
{title: 'Post D', userId: createdUsers[1].id},
|
||||||
{title: 'Post E'}
|
{title: 'Post E'}
|
||||||
],
|
],
|
||||||
function(items) {
|
function (items) {
|
||||||
createdPosts = items;
|
createdPosts = items;
|
||||||
done();
|
done();
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearAndCreate(model, data, callback) {
|
function clearAndCreate(model, data, callback) {
|
||||||
var createdItems = [];
|
var createdItems = [];
|
||||||
model.destroyAll(function () {
|
model.destroyAll(function () {
|
||||||
nextItem(null, null);
|
nextItem(null, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
var itemIndex = 0;
|
var itemIndex = 0;
|
||||||
function nextItem(err, lastItem) {
|
|
||||||
if (lastItem !== null) {
|
function nextItem(err, lastItem) {
|
||||||
createdItems.push(lastItem);
|
if (lastItem !== null) {
|
||||||
}
|
createdItems.push(lastItem);
|
||||||
if (itemIndex >= data.length) {
|
|
||||||
callback(createdItems);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
model.create(data[itemIndex], nextItem);
|
|
||||||
itemIndex++;
|
|
||||||
}
|
}
|
||||||
|
if (itemIndex >= data.length) {
|
||||||
|
callback(createdItems);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
model.create(data[itemIndex], nextItem);
|
||||||
|
itemIndex++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
24
test/init.js
24
test/init.js
|
@ -1,21 +1,21 @@
|
||||||
module.exports = require('should');
|
module.exports = require('should');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (!process.env.TRAVIS) {
|
if (!process.env.TRAVIS) {
|
||||||
if (typeof __cov === 'undefined') {
|
if (typeof __cov === 'undefined') {
|
||||||
process.on('exit', function () {
|
process.on('exit', function () {
|
||||||
require('semicov').report();
|
require('semicov').report();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
require('semicov').init('lib');
|
require('semicov').init('lib');
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Schema = require('../').Schema;
|
var Schema = require('../').Schema;
|
||||||
|
|
||||||
if (!('getSchema' in global)) {
|
if (!('getSchema' in global)) {
|
||||||
global.getSchema = function() {
|
global.getSchema = function () {
|
||||||
return new Schema('memory');
|
return new Schema('memory');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,99 +3,99 @@ var ModelBuilder = require('../lib/model-builder').ModelBuilder;
|
||||||
var introspectType = require('../lib/introspection')(ModelBuilder);
|
var introspectType = require('../lib/introspection')(ModelBuilder);
|
||||||
var traverse = require('traverse');
|
var traverse = require('traverse');
|
||||||
|
|
||||||
describe('Introspection of model definitions from JSON', function() {
|
describe('Introspection of model definitions from JSON', function () {
|
||||||
|
|
||||||
it('should handle simple types', function() {
|
it('should handle simple types', function () {
|
||||||
assert.equal(introspectType('123'), 'string');
|
assert.equal(introspectType('123'), 'string');
|
||||||
assert.equal(introspectType(true), 'boolean');
|
assert.equal(introspectType(true), 'boolean');
|
||||||
assert.equal(introspectType(false), 'boolean');
|
assert.equal(introspectType(false), 'boolean');
|
||||||
assert.equal(introspectType(12), 'number');
|
assert.equal(introspectType(12), 'number');
|
||||||
assert.equal(introspectType(new Date()), 'date');
|
assert.equal(introspectType(new Date()), 'date');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle array types', function() {
|
it('should handle array types', function () {
|
||||||
var type = introspectType(['123']);
|
var type = introspectType(['123']);
|
||||||
assert.deepEqual(type, ['string'], 'type should be ["string"]');
|
assert.deepEqual(type, ['string'], 'type should be ["string"]');
|
||||||
type = introspectType([1]);
|
type = introspectType([1]);
|
||||||
assert.deepEqual(type, ['number'], 'type should be ["number"]');
|
assert.deepEqual(type, ['number'], 'type should be ["number"]');
|
||||||
// Stop at first known type
|
// Stop at first known type
|
||||||
type = introspectType([1, '123']);
|
type = introspectType([1, '123']);
|
||||||
assert.deepEqual(type, ['number'], 'type should be ["number"]');
|
assert.deepEqual(type, ['number'], 'type should be ["number"]');
|
||||||
type = introspectType([null, '123']);
|
type = introspectType([null, '123']);
|
||||||
assert.deepEqual(type, ['string'], 'type should be ["string"]');
|
assert.deepEqual(type, ['string'], 'type should be ["string"]');
|
||||||
|
|
||||||
type = introspectType([]);
|
type = introspectType([]);
|
||||||
assert.equal(type, 'array');
|
assert.equal(type, 'array');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return Any for null or undefined', function() {
|
it('should return Any for null or undefined', function () {
|
||||||
assert.equal(introspectType(null), ModelBuilder.Any);
|
assert.equal(introspectType(null), ModelBuilder.Any);
|
||||||
assert.equal(introspectType(undefined), ModelBuilder.Any);
|
assert.equal(introspectType(undefined), ModelBuilder.Any);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a schema for object', function() {
|
it('should return a schema for object', function () {
|
||||||
var json = {a: 'str', b: 0, c: true};
|
var json = {a: 'str', b: 0, c: true};
|
||||||
var type = introspectType(json);
|
var type = introspectType(json);
|
||||||
assert.equal(type.a, 'string');
|
assert.equal(type.a, 'string');
|
||||||
assert.equal(type.b, 'number');
|
assert.equal(type.b, 'number');
|
||||||
assert.equal(type.c, 'boolean');
|
assert.equal(type.c, 'boolean');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle nesting objects', function() {
|
it('should handle nesting objects', function () {
|
||||||
var json = {a: 'str', b: 0, c: true, d: {x: 10, y: 5}};
|
var json = {a: 'str', b: 0, c: true, d: {x: 10, y: 5}};
|
||||||
var type = introspectType(json);
|
var type = introspectType(json);
|
||||||
assert.equal(type.a, 'string');
|
assert.equal(type.a, 'string');
|
||||||
assert.equal(type.b, 'number');
|
assert.equal(type.b, 'number');
|
||||||
assert.equal(type.c, 'boolean');
|
assert.equal(type.c, 'boolean');
|
||||||
assert.equal(type.d.x, 'number');
|
assert.equal(type.d.x, 'number');
|
||||||
assert.equal(type.d.y, 'number');
|
assert.equal(type.d.y, 'number');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle nesting arrays', function() {
|
it('should handle nesting arrays', function () {
|
||||||
var json = {a: 'str', b: 0, c: true, d: [1, 2]};
|
var json = {a: 'str', b: 0, c: true, d: [1, 2]};
|
||||||
var type = introspectType(json);
|
var type = introspectType(json);
|
||||||
assert.equal(type.a, 'string');
|
assert.equal(type.a, 'string');
|
||||||
assert.equal(type.b, 'number');
|
assert.equal(type.b, 'number');
|
||||||
assert.equal(type.c, 'boolean');
|
assert.equal(type.c, 'boolean');
|
||||||
assert.deepEqual(type.d, ['number']);
|
assert.deepEqual(type.d, ['number']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should build a model from the introspected schema', function(done) {
|
it('should build a model from the introspected schema', function (done) {
|
||||||
|
|
||||||
var json = {
|
var json = {
|
||||||
name: 'Joe',
|
name: 'Joe',
|
||||||
age: 30,
|
age: 30,
|
||||||
birthday: new Date(),
|
birthday: new Date(),
|
||||||
vip: true,
|
vip: true,
|
||||||
address: {
|
address: {
|
||||||
street: '1 Main St',
|
street: '1 Main St',
|
||||||
city: 'San Jose',
|
city: 'San Jose',
|
||||||
state: 'CA',
|
state: 'CA',
|
||||||
zipcode: '95131',
|
zipcode: '95131',
|
||||||
country: 'US'
|
country: 'US'
|
||||||
},
|
},
|
||||||
friends: ['John', 'Mary'],
|
friends: ['John', 'Mary'],
|
||||||
emails: [
|
emails: [
|
||||||
{label: 'work', id: 'x@sample.com'},
|
{label: 'work', id: 'x@sample.com'},
|
||||||
{label: 'home', id: 'x@home.com'}
|
{label: 'home', id: 'x@home.com'}
|
||||||
],
|
],
|
||||||
tags: []
|
tags: []
|
||||||
};
|
};
|
||||||
|
|
||||||
var copy = traverse(json).clone();
|
var copy = traverse(json).clone();
|
||||||
|
|
||||||
var schema = introspectType(json);
|
var schema = introspectType(json);
|
||||||
|
|
||||||
var builder = new ModelBuilder();
|
var builder = new ModelBuilder();
|
||||||
var Model = builder.define('MyModel', schema, {idInjection: false});
|
var Model = builder.define('MyModel', schema, {idInjection: false});
|
||||||
|
|
||||||
// FIXME: [rfeng] The constructor mutates the arguments
|
// FIXME: [rfeng] The constructor mutates the arguments
|
||||||
var obj = new Model(json);
|
var obj = new Model(json);
|
||||||
|
|
||||||
obj = obj.toObject();
|
obj = obj.toObject();
|
||||||
|
|
||||||
assert.deepEqual(obj, copy);
|
assert.deepEqual(obj, copy);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,36 +4,36 @@ var should = require('./init.js');
|
||||||
var Schema = require('../').Schema;
|
var Schema = require('../').Schema;
|
||||||
var ModelBuilder = require('../').ModelBuilder;
|
var ModelBuilder = require('../').ModelBuilder;
|
||||||
|
|
||||||
describe('JSON property', function() {
|
describe('JSON property', function () {
|
||||||
var dataSource, Model;
|
var dataSource, Model;
|
||||||
|
|
||||||
it('should be defined', function() {
|
it('should be defined', function () {
|
||||||
dataSource = getSchema();
|
dataSource = getSchema();
|
||||||
Model = dataSource.define('Model', {propertyName: ModelBuilder.JSON});
|
Model = dataSource.define('Model', {propertyName: ModelBuilder.JSON});
|
||||||
var m = new Model;
|
var m = new Model;
|
||||||
(new Boolean('propertyName' in m)).should.eql(true);
|
(new Boolean('propertyName' in m)).should.eql(true);
|
||||||
should.not.exist(m.propertyName);
|
should.not.exist(m.propertyName);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should accept JSON in constructor and return object', function() {
|
it('should accept JSON in constructor and return object', function () {
|
||||||
var m = new Model({
|
var m = new Model({
|
||||||
propertyName: '{"foo": "bar"}'
|
propertyName: '{"foo": "bar"}'
|
||||||
});
|
|
||||||
m.propertyName.should.be.an.Object;
|
|
||||||
m.propertyName.foo.should.equal('bar');
|
|
||||||
});
|
});
|
||||||
|
m.propertyName.should.be.an.Object;
|
||||||
|
m.propertyName.foo.should.equal('bar');
|
||||||
|
});
|
||||||
|
|
||||||
it('should accept object in setter and return object', function() {
|
it('should accept object in setter and return object', function () {
|
||||||
var m = new Model;
|
var m = new Model;
|
||||||
m.propertyName = {"foo": "bar"};
|
m.propertyName = {"foo": "bar"};
|
||||||
m.propertyName.should.be.an.Object;
|
m.propertyName.should.be.an.Object;
|
||||||
m.propertyName.foo.should.equal('bar');
|
m.propertyName.foo.should.equal('bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should accept string in setter and return string', function() {
|
it('should accept string in setter and return string', function () {
|
||||||
var m = new Model;
|
var m = new Model;
|
||||||
m.propertyName = '{"foo": "bar"}';
|
m.propertyName = '{"foo": "bar"}';
|
||||||
m.propertyName.should.be.a.String;
|
m.propertyName.should.be.a.String;
|
||||||
m.propertyName.should.equal('{"foo": "bar"}');
|
m.propertyName.should.equal('{"foo": "bar"}');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,8 +3,8 @@ var should = require('./init.js');
|
||||||
|
|
||||||
var loopbackData = require('../');
|
var loopbackData = require('../');
|
||||||
|
|
||||||
describe('loopback-datasource-juggler', function() {
|
describe('loopback-datasource-juggler', function () {
|
||||||
it('should expose version', function () {
|
it('should expose version', function () {
|
||||||
loopbackData.version.should.equal(require('../package.json').version);
|
loopbackData.version.should.equal(require('../package.json').version);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,254 +4,258 @@ var should = require('./init.js');
|
||||||
var db, Person;
|
var db, Person;
|
||||||
var ValidationError = require('..').ValidationError;
|
var ValidationError = require('..').ValidationError;
|
||||||
|
|
||||||
describe('manipulation', function() {
|
describe('manipulation', function () {
|
||||||
|
|
||||||
before(function(done) {
|
before(function (done) {
|
||||||
db = getSchema();
|
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);
|
|
||||||
|
|
||||||
|
Person = db.define('Person', {
|
||||||
|
name: String,
|
||||||
|
gender: String,
|
||||||
|
married: Boolean,
|
||||||
|
age: {type: Number, index: true},
|
||||||
|
dob: Date,
|
||||||
|
createdAt: {type: Number, default: Date.now}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('create', function() {
|
db.automigrate(done);
|
||||||
|
|
||||||
before(function(done) {
|
});
|
||||||
Person.destroyAll(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create instance', function(done) {
|
describe('create', function () {
|
||||||
Person.create({name: 'Anatoliy'}, function(err, p) {
|
|
||||||
p.name.should.equal('Anatoliy');
|
|
||||||
should.not.exist(err);
|
|
||||||
should.exist(p);
|
|
||||||
Person.findById(p.id, function(err, person) {
|
|
||||||
person.id.should.equal(p.id);
|
|
||||||
person.name.should.equal('Anatoliy');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return instance of object', function(done) {
|
before(function (done) {
|
||||||
var person = Person.create(function(err, p) {
|
Person.destroyAll(done);
|
||||||
p.id.should.eql(person.id);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
should.exist(person);
|
|
||||||
person.should.be.an.instanceOf(Person);
|
|
||||||
should.not.exist(person.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
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.findById(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, 30);
|
|
||||||
};
|
|
||||||
Person.create();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create batch of objects', function(done) {
|
|
||||||
var batch = [{name: 'Shaltay'}, {name: 'Boltay'}, {}];
|
|
||||||
Person.create(batch, function(e, ps) {
|
|
||||||
should.not.exist(e);
|
|
||||||
should.exist(ps);
|
|
||||||
ps.should.be.instanceOf(Array);
|
|
||||||
ps.should.have.lengthOf(batch.length);
|
|
||||||
|
|
||||||
Person.validatesPresenceOf('name');
|
|
||||||
Person.create(batch, function(errors, persons) {
|
|
||||||
delete Person._validations;
|
|
||||||
should.exist(errors);
|
|
||||||
errors.should.have.lengthOf(batch.length);
|
|
||||||
should.not.exist(errors[0]);
|
|
||||||
should.not.exist(errors[1]);
|
|
||||||
should.exist(errors[2]);
|
|
||||||
|
|
||||||
should.exist(persons);
|
|
||||||
persons.should.have.lengthOf(batch.length);
|
|
||||||
persons[0].errors.should.be.false;
|
|
||||||
done();
|
|
||||||
}).should.be.instanceOf(Array);
|
|
||||||
}).should.have.lengthOf(3);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('save', function() {
|
it('should create instance', function (done) {
|
||||||
|
Person.create({name: 'Anatoliy'}, function (err, p) {
|
||||||
it('should save new object', function(done) {
|
p.name.should.equal('Anatoliy');
|
||||||
var p = new Person;
|
should.not.exist(err);
|
||||||
p.save(function(err) {
|
should.exist(p);
|
||||||
should.not.exist(err);
|
Person.findById(p.id, function (err, person) {
|
||||||
should.exist(p.id);
|
person.id.should.equal(p.id);
|
||||||
done();
|
person.name.should.equal('Anatoliy');
|
||||||
});
|
done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
it('should save existing object', function(done) {
|
|
||||||
Person.findOne(function(err, p) {
|
|
||||||
should.not.exist(err);
|
|
||||||
p.name = 'Hans';
|
|
||||||
p.propertyChanged('name').should.be.true;
|
|
||||||
p.save(function(err) {
|
|
||||||
should.not.exist(err);
|
|
||||||
p.propertyChanged('name').should.be.false;
|
|
||||||
Person.findOne(function(err, p) {
|
|
||||||
should.not.exist(err);
|
|
||||||
p.name.should.equal('Hans');
|
|
||||||
p.propertyChanged('name').should.be.false;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should save invalid object (skipping validation)', function(done) {
|
|
||||||
Person.findOne(function(err, p) {
|
|
||||||
should.not.exist(err);
|
|
||||||
p.isValid = function(done) {
|
|
||||||
process.nextTick(done);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
p.name = 'Nana';
|
|
||||||
p.save(function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
p.propertyChanged('name').should.be.true;
|
|
||||||
p.save({validate: false}, function(err) {
|
|
||||||
should.not.exist(err);
|
|
||||||
p.propertyChanged('name').should.be.false;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should save throw error on validation', function() {
|
|
||||||
Person.findOne(function(err, p) {
|
|
||||||
should.not.exist(err);
|
|
||||||
p.isValid = function(cb) {
|
|
||||||
cb(false);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
(function() {
|
|
||||||
p.save({
|
|
||||||
'throws': true
|
|
||||||
});
|
|
||||||
}).should.throw(ValidationError);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateAttributes', function() {
|
it('should return instance of object', function (done) {
|
||||||
var person;
|
var person = Person.create(function (err, p) {
|
||||||
|
p.id.should.eql(person.id);
|
||||||
before(function(done) {
|
done();
|
||||||
Person.destroyAll(function() {
|
});
|
||||||
person = Person.create(done);
|
should.exist(person);
|
||||||
});
|
person.should.be.an.instanceOf(Person);
|
||||||
});
|
should.not.exist(person.id);
|
||||||
|
|
||||||
it('should update one attribute', function(done) {
|
|
||||||
person.updateAttribute('name', 'Paul Graham', function(err, p) {
|
|
||||||
should.not.exist(err);
|
|
||||||
Person.all(function(e, ps) {
|
|
||||||
should.not.exist(err);
|
|
||||||
ps.should.have.lengthOf(1);
|
|
||||||
ps.pop().name.should.equal('Paul Graham');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('destroy', function() {
|
it('should work when called without callback', function (done) {
|
||||||
|
Person.afterCreate = function (next) {
|
||||||
it('should destroy record', function(done) {
|
this.should.be.an.instanceOf(Person);
|
||||||
Person.create(function(err, p){
|
this.name.should.equal('Nickolay');
|
||||||
p.destroy(function(err) {
|
should.exist(this.id);
|
||||||
should.not.exist(err);
|
Person.afterCreate = null;
|
||||||
Person.exists(p.id, function(err, ex) {
|
next();
|
||||||
ex.should.not.be.ok;
|
setTimeout(done, 10);
|
||||||
done();
|
};
|
||||||
});
|
Person.create({name: 'Nickolay'});
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
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 create instance with blank data', function (done) {
|
||||||
it('should initialize object properly', function() {
|
Person.create(function (err, p) {
|
||||||
var hw = 'Hello word',
|
should.not.exist(err);
|
||||||
now = Date.now(),
|
should.exist(p);
|
||||||
person = new Person({name: hw});
|
should.not.exists(p.name);
|
||||||
|
Person.findById(p.id, function (err, person) {
|
||||||
person.name.should.equal(hw);
|
person.id.should.equal(p.id);
|
||||||
person.propertyChanged('name').should.be.false;
|
should.not.exists(person.name);
|
||||||
person.name = 'Goodbye, Lenin';
|
done();
|
||||||
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');
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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, 30);
|
||||||
|
};
|
||||||
|
Person.create();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create batch of objects', function (done) {
|
||||||
|
var batch = [
|
||||||
|
{name: 'Shaltay'},
|
||||||
|
{name: 'Boltay'},
|
||||||
|
{}
|
||||||
|
];
|
||||||
|
Person.create(batch,function (e, ps) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(ps);
|
||||||
|
ps.should.be.instanceOf(Array);
|
||||||
|
ps.should.have.lengthOf(batch.length);
|
||||||
|
|
||||||
|
Person.validatesPresenceOf('name');
|
||||||
|
Person.create(batch,function (errors, persons) {
|
||||||
|
delete Person._validations;
|
||||||
|
should.exist(errors);
|
||||||
|
errors.should.have.lengthOf(batch.length);
|
||||||
|
should.not.exist(errors[0]);
|
||||||
|
should.not.exist(errors[1]);
|
||||||
|
should.exist(errors[2]);
|
||||||
|
|
||||||
|
should.exist(persons);
|
||||||
|
persons.should.have.lengthOf(batch.length);
|
||||||
|
persons[0].errors.should.be.false;
|
||||||
|
done();
|
||||||
|
}).should.be.instanceOf(Array);
|
||||||
|
}).should.have.lengthOf(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('save', function () {
|
||||||
|
|
||||||
|
it('should save new object', function (done) {
|
||||||
|
var p = new Person;
|
||||||
|
p.save(function (err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(p.id);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should save existing object', function (done) {
|
||||||
|
Person.findOne(function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.name = 'Hans';
|
||||||
|
p.propertyChanged('name').should.be.true;
|
||||||
|
p.save(function (err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.propertyChanged('name').should.be.false;
|
||||||
|
Person.findOne(function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.name.should.equal('Hans');
|
||||||
|
p.propertyChanged('name').should.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should save invalid object (skipping validation)', function (done) {
|
||||||
|
Person.findOne(function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.isValid = function (done) {
|
||||||
|
process.nextTick(done);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
p.name = 'Nana';
|
||||||
|
p.save(function (err) {
|
||||||
|
should.exist(err);
|
||||||
|
p.propertyChanged('name').should.be.true;
|
||||||
|
p.save({validate: false}, function (err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.propertyChanged('name').should.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should save throw error on validation', function () {
|
||||||
|
Person.findOne(function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.isValid = function (cb) {
|
||||||
|
cb(false);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
(function () {
|
||||||
|
p.save({
|
||||||
|
'throws': true
|
||||||
|
});
|
||||||
|
}).should.throw(ValidationError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateAttributes', function () {
|
||||||
|
var person;
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
Person.destroyAll(function () {
|
||||||
|
person = Person.create(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update one attribute', function (done) {
|
||||||
|
person.updateAttribute('name', 'Paul Graham', function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
Person.all(function (e, ps) {
|
||||||
|
should.not.exist(err);
|
||||||
|
ps.should.have.lengthOf(1);
|
||||||
|
ps.pop().name.should.equal('Paul Graham');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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');
|
||||||
|
// });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,243 +11,236 @@ var ModelDefinition = require('../lib/model-definition');
|
||||||
|
|
||||||
describe('ModelDefinition class', function () {
|
describe('ModelDefinition class', function () {
|
||||||
|
|
||||||
it('should be able to define plain models', function (done) {
|
it('should be able to define plain models', function (done) {
|
||||||
var modelBuilder = new ModelBuilder();
|
var modelBuilder = new ModelBuilder();
|
||||||
|
|
||||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||||
name: "string",
|
name: "string",
|
||||||
bio: ModelBuilder.Text,
|
bio: ModelBuilder.Text,
|
||||||
approved: Boolean,
|
approved: Boolean,
|
||||||
joinedAt: Date,
|
joinedAt: Date,
|
||||||
age: "number"
|
age: "number"
|
||||||
});
|
});
|
||||||
|
|
||||||
User.build();
|
User.build();
|
||||||
assert.equal(User.properties.name.type, String);
|
assert.equal(User.properties.name.type, String);
|
||||||
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
||||||
assert.equal(User.properties.approved.type, Boolean);
|
assert.equal(User.properties.approved.type, Boolean);
|
||||||
assert.equal(User.properties.joinedAt.type, Date);
|
assert.equal(User.properties.joinedAt.type, Date);
|
||||||
assert.equal(User.properties.age.type, Number);
|
assert.equal(User.properties.age.type, Number);
|
||||||
|
|
||||||
|
var json = User.toJSON();
|
||||||
|
assert.equal(json.name, "User");
|
||||||
|
assert.equal(json.properties.name.type, "String");
|
||||||
|
assert.equal(json.properties.bio.type, "Text");
|
||||||
|
assert.equal(json.properties.approved.type, "Boolean");
|
||||||
|
assert.equal(json.properties.joinedAt.type, "Date");
|
||||||
|
assert.equal(json.properties.age.type, "Number");
|
||||||
|
|
||||||
var json = User.toJSON();
|
done();
|
||||||
assert.equal(json.name, "User");
|
|
||||||
assert.equal(json.properties.name.type, "String");
|
|
||||||
assert.equal(json.properties.bio.type, "Text");
|
|
||||||
assert.equal(json.properties.approved.type, "Boolean");
|
|
||||||
assert.equal(json.properties.joinedAt.type, "Date");
|
|
||||||
assert.equal(json.properties.age.type, "Number");
|
|
||||||
|
|
||||||
done();
|
});
|
||||||
|
|
||||||
|
it('should be able to define additional properties', function (done) {
|
||||||
|
var modelBuilder = new ModelBuilder();
|
||||||
|
|
||||||
|
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||||
|
name: "string",
|
||||||
|
bio: ModelBuilder.Text,
|
||||||
|
approved: Boolean,
|
||||||
|
joinedAt: Date,
|
||||||
|
age: "number"
|
||||||
|
});
|
||||||
|
|
||||||
|
User.build();
|
||||||
|
|
||||||
|
User.defineProperty("id", {type: "number", id: true});
|
||||||
|
assert.equal(User.properties.name.type, String);
|
||||||
|
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
||||||
|
assert.equal(User.properties.approved.type, Boolean);
|
||||||
|
assert.equal(User.properties.joinedAt.type, Date);
|
||||||
|
assert.equal(User.properties.age.type, Number);
|
||||||
|
|
||||||
|
assert.equal(User.properties.id.type, Number);
|
||||||
|
done();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to define nesting models', function (done) {
|
||||||
|
var modelBuilder = new ModelBuilder();
|
||||||
|
|
||||||
|
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||||
|
name: String,
|
||||||
|
bio: ModelBuilder.Text,
|
||||||
|
approved: Boolean,
|
||||||
|
joinedAt: Date,
|
||||||
|
age: Number,
|
||||||
|
address: {
|
||||||
|
street: String,
|
||||||
|
city: String,
|
||||||
|
zipCode: String,
|
||||||
|
state: String
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
User.build();
|
||||||
|
assert.equal(User.properties.name.type, String);
|
||||||
|
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
||||||
|
assert.equal(User.properties.approved.type, Boolean);
|
||||||
|
assert.equal(User.properties.joinedAt.type, Date);
|
||||||
|
assert.equal(User.properties.age.type, Number);
|
||||||
|
assert.equal(typeof User.properties.address.type, 'function');
|
||||||
|
|
||||||
|
var json = User.toJSON();
|
||||||
|
assert.equal(json.name, "User");
|
||||||
|
assert.equal(json.properties.name.type, "String");
|
||||||
|
assert.equal(json.properties.bio.type, "Text");
|
||||||
|
assert.equal(json.properties.approved.type, "Boolean");
|
||||||
|
assert.equal(json.properties.joinedAt.type, "Date");
|
||||||
|
assert.equal(json.properties.age.type, "Number");
|
||||||
|
|
||||||
|
assert.deepEqual(json.properties.address.type, { street: { type: 'String' },
|
||||||
|
city: { type: 'String' },
|
||||||
|
zipCode: { type: 'String' },
|
||||||
|
state: { type: 'String' } });
|
||||||
|
|
||||||
|
done();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to define referencing models', function (done) {
|
||||||
|
var modelBuilder = new ModelBuilder();
|
||||||
|
|
||||||
|
var Address = modelBuilder.define('Address', {
|
||||||
|
street: String,
|
||||||
|
city: String,
|
||||||
|
zipCode: String,
|
||||||
|
state: String
|
||||||
|
});
|
||||||
|
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||||
|
name: String,
|
||||||
|
bio: ModelBuilder.Text,
|
||||||
|
approved: Boolean,
|
||||||
|
joinedAt: Date,
|
||||||
|
age: Number,
|
||||||
|
address: Address
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to define additional properties', function (done) {
|
User.build();
|
||||||
var modelBuilder = new ModelBuilder();
|
assert.equal(User.properties.name.type, String);
|
||||||
|
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
||||||
|
assert.equal(User.properties.approved.type, Boolean);
|
||||||
|
assert.equal(User.properties.joinedAt.type, Date);
|
||||||
|
assert.equal(User.properties.age.type, Number);
|
||||||
|
assert.equal(User.properties.address.type, Address);
|
||||||
|
|
||||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
var json = User.toJSON();
|
||||||
name: "string",
|
assert.equal(json.name, "User");
|
||||||
bio: ModelBuilder.Text,
|
assert.equal(json.properties.name.type, "String");
|
||||||
approved: Boolean,
|
assert.equal(json.properties.bio.type, "Text");
|
||||||
joinedAt: Date,
|
assert.equal(json.properties.approved.type, "Boolean");
|
||||||
age: "number"
|
assert.equal(json.properties.joinedAt.type, "Date");
|
||||||
});
|
assert.equal(json.properties.age.type, "Number");
|
||||||
|
|
||||||
User.build();
|
assert.equal(json.properties.address.type, 'Address');
|
||||||
|
|
||||||
User.defineProperty("id", {type: "number", id: true});
|
done();
|
||||||
assert.equal(User.properties.name.type, String);
|
|
||||||
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
|
||||||
assert.equal(User.properties.approved.type, Boolean);
|
|
||||||
assert.equal(User.properties.joinedAt.type, Date);
|
|
||||||
assert.equal(User.properties.age.type, Number);
|
|
||||||
|
|
||||||
assert.equal(User.properties.id.type, Number);
|
});
|
||||||
done();
|
|
||||||
|
|
||||||
|
it('should be able to define referencing models by name', function (done) {
|
||||||
|
var modelBuilder = new ModelBuilder();
|
||||||
|
|
||||||
|
var Address = modelBuilder.define('Address', {
|
||||||
|
street: String,
|
||||||
|
city: String,
|
||||||
|
zipCode: String,
|
||||||
|
state: String
|
||||||
|
});
|
||||||
|
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||||
|
name: String,
|
||||||
|
bio: ModelBuilder.Text,
|
||||||
|
approved: Boolean,
|
||||||
|
joinedAt: Date,
|
||||||
|
age: Number,
|
||||||
|
address: 'Address'
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
User.build();
|
||||||
|
assert.equal(User.properties.name.type, String);
|
||||||
|
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
||||||
|
assert.equal(User.properties.approved.type, Boolean);
|
||||||
|
assert.equal(User.properties.joinedAt.type, Date);
|
||||||
|
assert.equal(User.properties.age.type, Number);
|
||||||
|
assert.equal(User.properties.address.type, Address);
|
||||||
|
|
||||||
it('should be able to define nesting models', function (done) {
|
var json = User.toJSON();
|
||||||
var modelBuilder = new ModelBuilder();
|
assert.equal(json.name, "User");
|
||||||
|
assert.equal(json.properties.name.type, "String");
|
||||||
|
assert.equal(json.properties.bio.type, "Text");
|
||||||
|
assert.equal(json.properties.approved.type, "Boolean");
|
||||||
|
assert.equal(json.properties.joinedAt.type, "Date");
|
||||||
|
assert.equal(json.properties.age.type, "Number");
|
||||||
|
|
||||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
assert.equal(json.properties.address.type, 'Address');
|
||||||
name: String,
|
|
||||||
bio: ModelBuilder.Text,
|
|
||||||
approved: Boolean,
|
|
||||||
joinedAt: Date,
|
|
||||||
age: Number,
|
|
||||||
address: {
|
|
||||||
street: String,
|
|
||||||
city: String,
|
|
||||||
zipCode: String,
|
|
||||||
state: String
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
User.build();
|
done();
|
||||||
assert.equal(User.properties.name.type, String);
|
|
||||||
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
|
||||||
assert.equal(User.properties.approved.type, Boolean);
|
|
||||||
assert.equal(User.properties.joinedAt.type, Date);
|
|
||||||
assert.equal(User.properties.age.type, Number);
|
|
||||||
assert.equal(typeof User.properties.address.type, 'function');
|
|
||||||
|
|
||||||
var json = User.toJSON();
|
});
|
||||||
assert.equal(json.name, "User");
|
|
||||||
assert.equal(json.properties.name.type, "String");
|
|
||||||
assert.equal(json.properties.bio.type, "Text");
|
|
||||||
assert.equal(json.properties.approved.type, "Boolean");
|
|
||||||
assert.equal(json.properties.joinedAt.type, "Date");
|
|
||||||
assert.equal(json.properties.age.type, "Number");
|
|
||||||
|
|
||||||
assert.deepEqual(json.properties.address.type, { street: { type: 'String' },
|
it('should report correct id names', function (done) {
|
||||||
city: { type: 'String' },
|
var modelBuilder = new ModelBuilder();
|
||||||
zipCode: { type: 'String' },
|
|
||||||
state: { type: 'String' } });
|
|
||||||
|
|
||||||
done();
|
|
||||||
|
|
||||||
|
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||||
|
userId: {type: String, id: true},
|
||||||
|
name: "string",
|
||||||
|
bio: ModelBuilder.Text,
|
||||||
|
approved: Boolean,
|
||||||
|
joinedAt: Date,
|
||||||
|
age: "number"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
assert.equal(User.idName(), 'userId');
|
||||||
|
assert.deepEqual(User.idNames(), ['userId']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
it('should be able to define referencing models', function (done) {
|
it('should report correct table/column names', function (done) {
|
||||||
var modelBuilder = new ModelBuilder();
|
var modelBuilder = new ModelBuilder();
|
||||||
|
|
||||||
var Address = modelBuilder.define('Address', {
|
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||||
street: String,
|
userId: {type: String, id: true, oracle: {column: 'ID'}},
|
||||||
city: String,
|
name: "string"
|
||||||
zipCode: String,
|
}, {oracle: {table: 'USER'}});
|
||||||
state: String
|
|
||||||
});
|
|
||||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
|
||||||
name: String,
|
|
||||||
bio: ModelBuilder.Text,
|
|
||||||
approved: Boolean,
|
|
||||||
joinedAt: Date,
|
|
||||||
age: Number,
|
|
||||||
address: Address
|
|
||||||
|
|
||||||
});
|
assert.equal(User.tableName('oracle'), 'USER');
|
||||||
|
assert.equal(User.tableName('mysql'), 'User');
|
||||||
User.build();
|
assert.equal(User.columnName('oracle', 'userId'), 'ID');
|
||||||
assert.equal(User.properties.name.type, String);
|
assert.equal(User.columnName('mysql', 'userId'), 'userId');
|
||||||
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
done();
|
||||||
assert.equal(User.properties.approved.type, Boolean);
|
});
|
||||||
assert.equal(User.properties.joinedAt.type, Date);
|
|
||||||
assert.equal(User.properties.age.type, Number);
|
|
||||||
assert.equal(User.properties.address.type, Address);
|
|
||||||
|
|
||||||
|
|
||||||
var json = User.toJSON();
|
|
||||||
assert.equal(json.name, "User");
|
|
||||||
assert.equal(json.properties.name.type, "String");
|
|
||||||
assert.equal(json.properties.bio.type, "Text");
|
|
||||||
assert.equal(json.properties.approved.type, "Boolean");
|
|
||||||
assert.equal(json.properties.joinedAt.type, "Date");
|
|
||||||
assert.equal(json.properties.age.type, "Number");
|
|
||||||
|
|
||||||
assert.equal(json.properties.address.type, 'Address');
|
|
||||||
|
|
||||||
done();
|
|
||||||
|
|
||||||
|
it('should inherit prototype using option.base', function () {
|
||||||
|
var memory = new DataSource({connector: Memory});
|
||||||
|
var modelBuilder = memory.modelBuilder;
|
||||||
|
var parent = memory.createModel('parent', {}, {
|
||||||
|
relations: {
|
||||||
|
children: {
|
||||||
|
type: 'hasMany',
|
||||||
|
model: 'anotherChild'
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
var baseChild = modelBuilder.define('baseChild');
|
||||||
|
baseChild.attachTo(memory);
|
||||||
|
// the name of this must begin with a letter < b
|
||||||
|
// for this test to fail
|
||||||
|
var anotherChild = baseChild.extend('anotherChild');
|
||||||
|
|
||||||
it('should be able to define referencing models by name', function (done) {
|
assert(anotherChild.prototype instanceof baseChild);
|
||||||
var modelBuilder = new ModelBuilder();
|
});
|
||||||
|
|
||||||
var Address = modelBuilder.define('Address', {
|
|
||||||
street: String,
|
|
||||||
city: String,
|
|
||||||
zipCode: String,
|
|
||||||
state: String
|
|
||||||
});
|
|
||||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
|
||||||
name: String,
|
|
||||||
bio: ModelBuilder.Text,
|
|
||||||
approved: Boolean,
|
|
||||||
joinedAt: Date,
|
|
||||||
age: Number,
|
|
||||||
address: 'Address'
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
User.build();
|
|
||||||
assert.equal(User.properties.name.type, String);
|
|
||||||
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
|
||||||
assert.equal(User.properties.approved.type, Boolean);
|
|
||||||
assert.equal(User.properties.joinedAt.type, Date);
|
|
||||||
assert.equal(User.properties.age.type, Number);
|
|
||||||
assert.equal(User.properties.address.type, Address);
|
|
||||||
|
|
||||||
|
|
||||||
var json = User.toJSON();
|
|
||||||
assert.equal(json.name, "User");
|
|
||||||
assert.equal(json.properties.name.type, "String");
|
|
||||||
assert.equal(json.properties.bio.type, "Text");
|
|
||||||
assert.equal(json.properties.approved.type, "Boolean");
|
|
||||||
assert.equal(json.properties.joinedAt.type, "Date");
|
|
||||||
assert.equal(json.properties.age.type, "Number");
|
|
||||||
|
|
||||||
assert.equal(json.properties.address.type, 'Address');
|
|
||||||
|
|
||||||
done();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should report correct id names', function (done) {
|
|
||||||
var modelBuilder = new ModelBuilder();
|
|
||||||
|
|
||||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
|
||||||
userId: {type: String, id: true},
|
|
||||||
name: "string",
|
|
||||||
bio: ModelBuilder.Text,
|
|
||||||
approved: Boolean,
|
|
||||||
joinedAt: Date,
|
|
||||||
age: "number"
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(User.idName(), 'userId');
|
|
||||||
assert.deepEqual(User.idNames(), ['userId']);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should report correct table/column names', function (done) {
|
|
||||||
var modelBuilder = new ModelBuilder();
|
|
||||||
|
|
||||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
|
||||||
userId: {type: String, id: true, oracle: {column: 'ID'}},
|
|
||||||
name: "string"
|
|
||||||
}, {oracle: {table: 'USER'}});
|
|
||||||
|
|
||||||
assert.equal(User.tableName('oracle'), 'USER');
|
|
||||||
assert.equal(User.tableName('mysql'), 'User');
|
|
||||||
assert.equal(User.columnName('oracle', 'userId'), 'ID');
|
|
||||||
assert.equal(User.columnName('mysql', 'userId'), 'userId');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should inherit prototype using option.base', function () {
|
|
||||||
var memory = new DataSource({connector: Memory});
|
|
||||||
var modelBuilder = memory.modelBuilder;
|
|
||||||
var parent = memory.createModel('parent', {}, {
|
|
||||||
relations: {
|
|
||||||
children: {
|
|
||||||
type: 'hasMany',
|
|
||||||
model: 'anotherChild'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var baseChild = modelBuilder.define('baseChild');
|
|
||||||
baseChild.attachTo(memory);
|
|
||||||
// the name of this must begin with a letter < b
|
|
||||||
// for this test to fail
|
|
||||||
var anotherChild = baseChild.extend('anotherChild');
|
|
||||||
|
|
||||||
assert(anotherChild.prototype instanceof baseChild);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,80 +4,81 @@ Text = Schema.Text
|
||||||
require('./spec_helper').init exports
|
require('./spec_helper').init exports
|
||||||
|
|
||||||
schemas =
|
schemas =
|
||||||
neo4j:
|
neo4j:
|
||||||
url: 'http://localhost:7474/'
|
url: 'http://localhost:7474/'
|
||||||
mongoose:
|
mongoose:
|
||||||
url: 'mongodb://localhost/test'
|
url: 'mongodb://localhost/test'
|
||||||
redis: {}
|
redis: {}
|
||||||
memory: {}
|
memory: {}
|
||||||
cradle: {}
|
cradle: {}
|
||||||
nano:
|
nano:
|
||||||
url: 'http://localhost:5984/nano-test'
|
url: 'http://localhost:5984/nano-test'
|
||||||
|
|
||||||
testOrm = (dataSource) ->
|
testOrm = (dataSource) ->
|
||||||
|
User = Post = 'unknown'
|
||||||
|
maxUsers = 100
|
||||||
|
maxPosts = 50000
|
||||||
|
users = []
|
||||||
|
|
||||||
User = Post = 'unknown'
|
it 'should define simple', (test) ->
|
||||||
maxUsers = 100
|
User = dataSource.define 'User', {
|
||||||
maxPosts = 50000
|
name: String,
|
||||||
users = []
|
bio: Text,
|
||||||
|
approved: Boolean,
|
||||||
|
joinedAt: Date,
|
||||||
|
age: Number
|
||||||
|
}
|
||||||
|
|
||||||
it 'should define simple', (test) ->
|
Post = dataSource.define 'Post',
|
||||||
|
title: { type: String, length: 255, index: true }
|
||||||
|
content: { type: Text }
|
||||||
|
date: { type: Date, detault: Date.now }
|
||||||
|
published: { type: Boolean, default: false }
|
||||||
|
|
||||||
User = dataSource.define 'User', {
|
User.hasMany(Post, {as: 'posts', foreignKey: 'userId'})
|
||||||
name: String,
|
Post.belongsTo(User, {as: 'author', foreignKey: 'userId'})
|
||||||
bio: Text,
|
|
||||||
approved: Boolean,
|
|
||||||
joinedAt: Date,
|
|
||||||
age: Number
|
|
||||||
}
|
|
||||||
|
|
||||||
Post = dataSource.define 'Post',
|
test.done()
|
||||||
title: { type: String, length: 255, index: true }
|
|
||||||
content: { type: Text }
|
|
||||||
date: { type: Date, detault: Date.now }
|
|
||||||
published: { type: Boolean, default: false }
|
|
||||||
|
|
||||||
User.hasMany(Post, {as: 'posts', foreignKey: 'userId'})
|
it 'should create users', (test) ->
|
||||||
Post.belongsTo(User, {as: 'author', foreignKey: 'userId'})
|
wait = maxUsers
|
||||||
|
done = (e, u) ->
|
||||||
|
users.push(u)
|
||||||
|
test.done() if --wait == 0
|
||||||
|
User.create(done) for i in [1..maxUsers]
|
||||||
|
|
||||||
test.done()
|
it 'should create bunch of data', (test) ->
|
||||||
|
wait = maxPosts
|
||||||
|
done = ->
|
||||||
|
test.done() if --wait == 0
|
||||||
|
rnd = (title) ->
|
||||||
|
{
|
||||||
|
userId: users[Math.floor(Math.random() * maxUsers)].id
|
||||||
|
title: 'Post number ' + (title % 5)
|
||||||
|
}
|
||||||
|
Post.create(rnd(num), done) for num in [1..maxPosts]
|
||||||
|
|
||||||
it 'should create users', (test) ->
|
it 'do some queries using foreign keys', (test) ->
|
||||||
wait = maxUsers
|
wait = 4
|
||||||
done = (e, u) ->
|
done = ->
|
||||||
users.push(u)
|
test.done() if --wait == 0
|
||||||
test.done() if --wait == 0
|
ts = Date.now()
|
||||||
User.create(done) for i in [1..maxUsers]
|
query = (num) ->
|
||||||
|
users[num].posts { title: 'Post number 3' }, (err, collection) ->
|
||||||
|
console.log('User ' + num + ':', collection.length, 'posts in',
|
||||||
|
Date.now() - ts, 'ms')
|
||||||
|
done()
|
||||||
|
query num for num in [0..4]
|
||||||
|
|
||||||
it 'should create bunch of data', (test) ->
|
return
|
||||||
wait = maxPosts
|
|
||||||
done = -> test.done() if --wait == 0
|
|
||||||
rnd = (title) ->
|
|
||||||
{
|
|
||||||
userId: users[Math.floor(Math.random() * maxUsers)].id
|
|
||||||
title: 'Post number ' + (title % 5)
|
|
||||||
}
|
|
||||||
Post.create(rnd(num), done) for num in [1..maxPosts]
|
|
||||||
|
|
||||||
it 'do some queries using foreign keys', (test) ->
|
it 'should destroy all data', (test) ->
|
||||||
wait = 4
|
Post.destroyAll ->
|
||||||
done = -> test.done() if --wait == 0
|
User.destroyAll(test.done)
|
||||||
ts = Date.now()
|
|
||||||
query = (num) ->
|
|
||||||
users[num].posts { title: 'Post number 3' }, (err, collection) ->
|
|
||||||
console.log('User ' + num + ':', collection.length, 'posts in', Date.now() - ts,'ms')
|
|
||||||
done()
|
|
||||||
query num for num in [0..4]
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
it 'should destroy all data', (test) ->
|
|
||||||
Post.destroyAll ->
|
|
||||||
User.destroyAll(test.done)
|
|
||||||
|
|
||||||
Object.keys(schemas).forEach (schemaName) ->
|
Object.keys(schemas).forEach (schemaName) ->
|
||||||
return if process.env.ONLY && process.env.ONLY != schemaName
|
return if process.env.ONLY && process.env.ONLY != schemaName
|
||||||
context schemaName, ->
|
context schemaName, ->
|
||||||
dataSource = new Schema schemaName, schemas[schemaName]
|
dataSource = new Schema schemaName, schemas[schemaName]
|
||||||
testOrm(dataSource)
|
testOrm(dataSource)
|
||||||
|
|
||||||
|
|
|
@ -3,251 +3,251 @@ var should = require('./init.js');
|
||||||
|
|
||||||
var db, Book, Chapter, Author, Reader;
|
var db, Book, Chapter, Author, Reader;
|
||||||
|
|
||||||
describe('relations', function() {
|
describe('relations', function () {
|
||||||
before(function(done) {
|
before(function (done) {
|
||||||
db = getSchema();
|
db = getSchema();
|
||||||
Book = db.define('Book', {name: String});
|
Book = db.define('Book', {name: String});
|
||||||
Chapter = db.define('Chapter', {name: {type: String, index: true}});
|
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() {
|
db.automigrate(function () {
|
||||||
Book.destroyAll(function() {
|
Book.destroyAll(function () {
|
||||||
Chapter.destroyAll(function() {
|
Chapter.destroyAll(function () {
|
||||||
Author.destroyAll(function() {
|
Author.destroyAll(function () {
|
||||||
Reader.destroyAll(done);
|
Reader.destroyAll(done);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function () {
|
||||||
|
db.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasMany', function () {
|
||||||
|
it('can be declared in different ways', function (done) {
|
||||||
|
Book.hasMany(Chapter);
|
||||||
|
Book.hasMany(Reader, {as: 'users'});
|
||||||
|
Book.hasMany(Author, {foreignKey: 'projectId'});
|
||||||
|
var b = new Book;
|
||||||
|
b.chapters.should.be.an.instanceOf(Function);
|
||||||
|
b.users.should.be.an.instanceOf(Function);
|
||||||
|
b.authors.should.be.an.instanceOf(Function);
|
||||||
|
Object.keys((new Chapter).toObject()).should.include('bookId');
|
||||||
|
Object.keys((new Author).toObject()).should.include('projectId');
|
||||||
|
|
||||||
|
db.automigrate(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function() {
|
it('can be declared in short form', function (done) {
|
||||||
db.disconnect();
|
Author.hasMany('readers');
|
||||||
|
(new Author).readers.should.be.an.instanceOf(Function);
|
||||||
|
Object.keys((new Reader).toObject()).should.include('authorId');
|
||||||
|
|
||||||
|
db.autoupdate(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasMany', function() {
|
it('should build record on scope', function (done) {
|
||||||
it('can be declared in different ways', function(done) {
|
Book.create(function (err, book) {
|
||||||
Book.hasMany(Chapter);
|
var c = book.chapters.build();
|
||||||
Book.hasMany(Reader, {as: 'users'});
|
c.bookId.should.equal(book.id);
|
||||||
Book.hasMany(Author, {foreignKey: 'projectId'});
|
c.save(done);
|
||||||
var b = new Book;
|
});
|
||||||
b.chapters.should.be.an.instanceOf(Function);
|
|
||||||
b.users.should.be.an.instanceOf(Function);
|
|
||||||
b.authors.should.be.an.instanceOf(Function);
|
|
||||||
Object.keys((new Chapter).toObject()).should.include('bookId');
|
|
||||||
Object.keys((new Author).toObject()).should.include('projectId');
|
|
||||||
|
|
||||||
db.automigrate(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can be declared in short form', function(done) {
|
|
||||||
Author.hasMany('readers');
|
|
||||||
(new Author).readers.should.be.an.instanceOf(Function);
|
|
||||||
Object.keys((new Reader).toObject()).should.include('authorId');
|
|
||||||
|
|
||||||
db.autoupdate(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.skip('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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should find scoped record', function(done) {
|
|
||||||
var id;
|
|
||||||
Book.create(function(err, book) {
|
|
||||||
book.chapters.create({name: 'a'}, function(err, ch) {
|
|
||||||
id = ch.id;
|
|
||||||
book.chapters.create({name: 'z'}, function() {
|
|
||||||
book.chapters.create({name: 'c'}, function() {
|
|
||||||
fetch(book);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function fetch(book) {
|
|
||||||
book.chapters.findById(id, function(err, ch) {
|
|
||||||
should.not.exist(err);
|
|
||||||
should.exist(ch);
|
|
||||||
ch.id.should.equal(id);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('belongsTo', function() {
|
it('should create record on scope', function (done) {
|
||||||
var List, Item, Fear, Mind;
|
Book.create(function (err, book) {
|
||||||
|
book.chapters.create(function (err, c) {
|
||||||
it('can be declared in different ways', function() {
|
should.not.exist(err);
|
||||||
List = db.define('List', {name: String});
|
should.exist(c);
|
||||||
Item = db.define('Item', {name: String});
|
c.bookId.should.equal(book.id);
|
||||||
Fear = db.define('Fear');
|
done();
|
||||||
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 used to query data', function(done) {
|
|
||||||
List.hasMany('todos', {model: Item});
|
|
||||||
db.automigrate(function() {
|
|
||||||
List.create(function(e, list) {
|
|
||||||
should.not.exist(e);
|
|
||||||
should.exist(list);
|
|
||||||
list.todos.create(function(err, todo) {
|
|
||||||
todo.list(function(e, l) {
|
|
||||||
should.not.exist(e);
|
|
||||||
should.exist(l);
|
|
||||||
l.should.be.an.instanceOf(List);
|
|
||||||
todo.list().should.equal(l.id);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('could accept objects when creating on scope', function(done) {
|
|
||||||
List.create(function(e, list) {
|
|
||||||
should.not.exist(e);
|
|
||||||
should.exist(list);
|
|
||||||
Item.create({list: list}, function(err, item) {
|
|
||||||
should.not.exist(err);
|
|
||||||
should.exist(item);
|
|
||||||
should.exist(item.listId);
|
|
||||||
item.listId.should.equal(list.id);
|
|
||||||
item.__cachedRelations.list.should.equal(list);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasAndBelongsToMany', function() {
|
it.skip('should fetch all scoped instances', function (done) {
|
||||||
var Article, Tag, ArticleTag;
|
Book.create(function (err, book) {
|
||||||
it('can be declared', function(done) {
|
book.chapters.create({name: 'a'}, function () {
|
||||||
Article = db.define('Article', {title: String});
|
book.chapters.create({name: 'z'}, function () {
|
||||||
Tag = db.define('Tag', {name: String});
|
book.chapters.create({name: 'c'}, function () {
|
||||||
Article.hasAndBelongsToMany('tags');
|
fetch(book);
|
||||||
ArticleTag = db.models.ArticleTag;
|
|
||||||
db.automigrate(function() {
|
|
||||||
Article.destroyAll(function() {
|
|
||||||
Tag.destroyAll(function() {
|
|
||||||
ArticleTag.destroyAll(done)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
function fetch(book) {
|
||||||
|
book.chapters(function (err, ch) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(ch);
|
||||||
|
ch.should.have.lengthOf(3);
|
||||||
|
|
||||||
it('should allow to create instances on scope', function(done) {
|
book.chapters({order: 'name DESC'}, function (e, c) {
|
||||||
Article.create(function(e, article) {
|
should.not.exist(e);
|
||||||
article.tags.create({name: 'popular'}, function(e, t) {
|
should.exist(c);
|
||||||
t.should.be.an.instanceOf(Tag);
|
c.shift().name.should.equal('z');
|
||||||
// console.log(t);
|
c.pop().name.should.equal('a');
|
||||||
ArticleTag.findOne(function(e, at) {
|
done();
|
||||||
should.exist(at);
|
});
|
||||||
at.tagId.toString().should.equal(t.id.toString());
|
|
||||||
at.articleId.toString().should.equal(article.id.toString());
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
it('should allow to fetch scoped instances', function(done) {
|
|
||||||
Article.findOne(function(e, article) {
|
|
||||||
article.tags(function(e, tags) {
|
|
||||||
should.not.exist(e);
|
|
||||||
should.exist(tags);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow to add connection with instance', function(done) {
|
|
||||||
Article.findOne(function(e, article) {
|
|
||||||
Tag.create({name: 'awesome'}, function(e, tag) {
|
|
||||||
article.tags.add(tag, function(e, at) {
|
|
||||||
should.not.exist(e);
|
|
||||||
should.exist(at);
|
|
||||||
at.should.be.an.instanceOf(ArticleTag);
|
|
||||||
at.tagId.should.equal(tag.id);
|
|
||||||
at.articleId.should.equal(article.id);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow to remove connection with instance', function(done) {
|
|
||||||
Article.findOne(function(e, article) {
|
|
||||||
article.tags(function(e, tags) {
|
|
||||||
var len = tags.length;
|
|
||||||
tags.should.not.be.empty;
|
|
||||||
article.tags.remove(tags[0], function(e) {
|
|
||||||
should.not.exist(e);
|
|
||||||
article.tags(true, function(e, tags) {
|
|
||||||
tags.should.have.lengthOf(len - 1);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should find scoped record', function (done) {
|
||||||
|
var id;
|
||||||
|
Book.create(function (err, book) {
|
||||||
|
book.chapters.create({name: 'a'}, function (err, ch) {
|
||||||
|
id = ch.id;
|
||||||
|
book.chapters.create({name: 'z'}, function () {
|
||||||
|
book.chapters.create({name: 'c'}, function () {
|
||||||
|
fetch(book);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function fetch(book) {
|
||||||
|
book.chapters.findById(id, function (err, ch) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(ch);
|
||||||
|
ch.id.should.equal(id);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('belongsTo', function () {
|
||||||
|
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 used to query data', function (done) {
|
||||||
|
List.hasMany('todos', {model: Item});
|
||||||
|
db.automigrate(function () {
|
||||||
|
List.create(function (e, list) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(list);
|
||||||
|
list.todos.create(function (err, todo) {
|
||||||
|
todo.list(function (e, l) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(l);
|
||||||
|
l.should.be.an.instanceOf(List);
|
||||||
|
todo.list().should.equal(l.id);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('could accept objects when creating on scope', function (done) {
|
||||||
|
List.create(function (e, list) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(list);
|
||||||
|
Item.create({list: list}, function (err, item) {
|
||||||
|
should.not.exist(err);
|
||||||
|
should.exist(item);
|
||||||
|
should.exist(item.listId);
|
||||||
|
item.listId.should.equal(list.id);
|
||||||
|
item.__cachedRelations.list.should.equal(list);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasAndBelongsToMany', function () {
|
||||||
|
var Article, Tag, ArticleTag;
|
||||||
|
it('can be declared', function (done) {
|
||||||
|
Article = db.define('Article', {title: String});
|
||||||
|
Tag = db.define('Tag', {name: String});
|
||||||
|
Article.hasAndBelongsToMany('tags');
|
||||||
|
ArticleTag = db.models.ArticleTag;
|
||||||
|
db.automigrate(function () {
|
||||||
|
Article.destroyAll(function () {
|
||||||
|
Tag.destroyAll(function () {
|
||||||
|
ArticleTag.destroyAll(done)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow to create instances on scope', function (done) {
|
||||||
|
Article.create(function (e, article) {
|
||||||
|
article.tags.create({name: 'popular'}, function (e, t) {
|
||||||
|
t.should.be.an.instanceOf(Tag);
|
||||||
|
// console.log(t);
|
||||||
|
ArticleTag.findOne(function (e, at) {
|
||||||
|
should.exist(at);
|
||||||
|
at.tagId.toString().should.equal(t.id.toString());
|
||||||
|
at.articleId.toString().should.equal(article.id.toString());
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow to fetch scoped instances', function (done) {
|
||||||
|
Article.findOne(function (e, article) {
|
||||||
|
article.tags(function (e, tags) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(tags);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow to add connection with instance', function (done) {
|
||||||
|
Article.findOne(function (e, article) {
|
||||||
|
Tag.create({name: 'awesome'}, function (e, tag) {
|
||||||
|
article.tags.add(tag, function (e, at) {
|
||||||
|
should.not.exist(e);
|
||||||
|
should.exist(at);
|
||||||
|
at.should.be.an.instanceOf(ArticleTag);
|
||||||
|
at.tagId.should.equal(tag.id);
|
||||||
|
at.articleId.should.equal(article.id);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow to remove connection with instance', function (done) {
|
||||||
|
Article.findOne(function (e, article) {
|
||||||
|
article.tags(function (e, tags) {
|
||||||
|
var len = tags.length;
|
||||||
|
tags.should.not.be.empty;
|
||||||
|
article.tags.remove(tags[0], function (e) {
|
||||||
|
should.not.exist(e);
|
||||||
|
article.tags(true, function (e, tags) {
|
||||||
|
tags.should.have.lengthOf(len - 1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,55 +3,55 @@ var should = require('./init.js');
|
||||||
|
|
||||||
var db = getSchema(), slave = getSchema(), Model, SlaveModel;
|
var db = getSchema(), slave = getSchema(), Model, SlaveModel;
|
||||||
|
|
||||||
describe('dataSource', function() {
|
describe('dataSource', function () {
|
||||||
|
|
||||||
it('should define Model', function() {
|
it('should define Model', function () {
|
||||||
Model = db.define('Model');
|
Model = db.define('Model');
|
||||||
Model.dataSource.should.eql(db);
|
Model.dataSource.should.eql(db);
|
||||||
var m = new Model;
|
var m = new Model;
|
||||||
m.getDataSource().should.eql(db);
|
m.getDataSource().should.eql(db);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clone existing model', function () {
|
||||||
|
SlaveModel = slave.copyModel(Model);
|
||||||
|
SlaveModel.dataSource.should.eql(slave);
|
||||||
|
slave.should.not.eql(db);
|
||||||
|
var sm = new SlaveModel;
|
||||||
|
sm.should.be.instanceOf(Model);
|
||||||
|
sm.getDataSource().should.not.eql(db);
|
||||||
|
sm.getDataSource().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;
|
||||||
|
|
||||||
it('should clone existing model', function() {
|
db.models.Model.count(function (err, c) {
|
||||||
SlaveModel = slave.copyModel(Model);
|
should.not.exist(err);
|
||||||
SlaveModel.dataSource.should.eql(slave);
|
should.exist(c);
|
||||||
slave.should.not.eql(db);
|
c.should.equal(0);
|
||||||
var sm = new SlaveModel;
|
called.should.be.false;
|
||||||
sm.should.be.instanceOf(Model);
|
tr.exec(function () {
|
||||||
sm.getDataSource().should.not.eql(db);
|
setTimeout(function () {
|
||||||
sm.getDataSource().should.eql(slave);
|
called.should.be.true;
|
||||||
});
|
db.models.Model.count(function (err, c) {
|
||||||
|
c.should.equal(3);
|
||||||
it('should automigrate', function(done) {
|
done();
|
||||||
db.automigrate(done);
|
});
|
||||||
});
|
}, 100);
|
||||||
|
});
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,62 +3,62 @@ var should = require('./init.js');
|
||||||
|
|
||||||
var db, Railway, Station;
|
var db, Railway, Station;
|
||||||
|
|
||||||
describe('sc0pe', function() {
|
describe('sc0pe', function () {
|
||||||
|
|
||||||
before(function() {
|
before(function () {
|
||||||
db = getSchema();
|
db = getSchema();
|
||||||
Railway = db.define('Railway', {
|
Railway = db.define('Railway', {
|
||||||
URID: {type: String, index: true}
|
URID: {type: String, index: true}
|
||||||
});
|
|
||||||
Station = db.define('Station', {
|
|
||||||
USID: {type: String, index: true},
|
|
||||||
capacity: {type: Number, index: true},
|
|
||||||
thoughput: {type: Number, index: true},
|
|
||||||
isActive: {type: Boolean, index: true},
|
|
||||||
isUndeground: {type: Boolean, index: true}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
Station = db.define('Station', {
|
||||||
|
USID: {type: String, index: true},
|
||||||
|
capacity: {type: Number, index: true},
|
||||||
|
thoughput: {type: Number, index: true},
|
||||||
|
isActive: {type: Boolean, index: true},
|
||||||
|
isUndeground: {type: Boolean, index: true}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach(function (done) {
|
||||||
Railway.destroyAll(function() {
|
Railway.destroyAll(function () {
|
||||||
Station.destroyAll(done);
|
Station.destroyAll(done);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should define scope with query', function(done) {
|
it('should define scope with query', function (done) {
|
||||||
Station.scope('active', {where: {isActive: true}});
|
Station.scope('active', {where: {isActive: true}});
|
||||||
Station.active.create(function(err, station) {
|
Station.active.create(function (err, station) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(station);
|
should.exist(station);
|
||||||
should.exist(station.isActive);
|
should.exist(station.isActive);
|
||||||
station.isActive.should.be.true;
|
station.isActive.should.be.true;
|
||||||
done();
|
done();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should allow scope chaining', function(done) {
|
it('should allow scope chaining', function (done) {
|
||||||
Station.scope('active', {where: {isActive: true}});
|
Station.scope('active', {where: {isActive: true}});
|
||||||
Station.scope('subway', {where: {isUndeground: true}});
|
Station.scope('subway', {where: {isUndeground: true}});
|
||||||
Station.active.subway.create(function(err, station) {
|
Station.active.subway.create(function (err, station) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
should.exist(station);
|
should.exist(station);
|
||||||
station.isActive.should.be.true;
|
station.isActive.should.be.true;
|
||||||
station.isUndeground.should.be.true;
|
station.isUndeground.should.be.true;
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should query all', function(done) {
|
it('should query all', function (done) {
|
||||||
Station.scope('active', {where: {isActive: true}});
|
Station.scope('active', {where: {isActive: true}});
|
||||||
Station.scope('inactive', {where: {isActive: false}});
|
Station.scope('inactive', {where: {isActive: false}});
|
||||||
Station.scope('ground', {where: {isUndeground: true}});
|
Station.scope('ground', {where: {isUndeground: true}});
|
||||||
Station.active.ground.create(function() {
|
Station.active.ground.create(function () {
|
||||||
Station.inactive.ground.create(function() {
|
Station.inactive.ground.create(function () {
|
||||||
Station.ground.inactive(function(err, ss) {
|
Station.ground.inactive(function (err, ss) {
|
||||||
ss.should.have.lengthOf(1);
|
ss.should.have.lengthOf(1);
|
||||||
done();
|
done();
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,56 +1,56 @@
|
||||||
/*
|
/*
|
||||||
if (!process.env.TRAVIS) {
|
if (!process.env.TRAVIS) {
|
||||||
var semicov = require('semicov');
|
var semicov = require('semicov');
|
||||||
semicov.init('lib', 'LoopbackData');
|
semicov.init('lib', 'LoopbackData');
|
||||||
process.on('exit', semicov.report);
|
process.on('exit', semicov.report);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
try {
|
try {
|
||||||
global.sinon = require('sinon');
|
global.sinon = require('sinon');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
var group_name = false, EXT_EXP;
|
var group_name = false, EXT_EXP;
|
||||||
function it(should, test_case) {
|
function it(should, test_case) {
|
||||||
check_external_exports();
|
check_external_exports();
|
||||||
if (group_name) {
|
if (group_name) {
|
||||||
EXT_EXP[group_name][should] = test_case;
|
EXT_EXP[group_name][should] = test_case;
|
||||||
} else {
|
} else {
|
||||||
EXT_EXP[should] = test_case;
|
EXT_EXP[should] = test_case;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global.it = it;
|
global.it = it;
|
||||||
|
|
||||||
function context(name, tests) {
|
function context(name, tests) {
|
||||||
check_external_exports();
|
check_external_exports();
|
||||||
EXT_EXP[name] = {};
|
EXT_EXP[name] = {};
|
||||||
group_name = name;
|
group_name = name;
|
||||||
tests({
|
tests({
|
||||||
before: function (f) {
|
before: function (f) {
|
||||||
it('setUp', f);
|
it('setUp', f);
|
||||||
},
|
},
|
||||||
after: function (f) {
|
after: function (f) {
|
||||||
it('tearDown', f);
|
it('tearDown', f);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
group_name = false;
|
group_name = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
global.context = context;
|
global.context = context;
|
||||||
|
|
||||||
exports.init = function init(external_exports) {
|
exports.init = function init(external_exports) {
|
||||||
EXT_EXP = external_exports;
|
EXT_EXP = external_exports;
|
||||||
if (external_exports.done) {
|
if (external_exports.done) {
|
||||||
external_exports.done();
|
external_exports.done();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function check_external_exports() {
|
function check_external_exports() {
|
||||||
if (!EXT_EXP) throw new Error(
|
if (!EXT_EXP) throw new Error(
|
||||||
'Before run this, please ensure that ' +
|
'Before run this, please ensure that ' +
|
||||||
'require("spec_helper").init(exports); called');
|
'require("spec_helper").init(exports); called');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
{
|
{
|
||||||
"title": {
|
"title": {
|
||||||
"type": "String"
|
"type": "String"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"type": "String",
|
"type": "String",
|
||||||
"default": "Raymond"
|
"default": "Raymond"
|
||||||
},
|
},
|
||||||
"body": "String",
|
"body": "String",
|
||||||
"date": {
|
"date": {
|
||||||
"type": "Date"
|
"type": "Date"
|
||||||
},
|
},
|
||||||
"hidden": "Boolean",
|
"hidden": "Boolean",
|
||||||
"comments": ["String"]
|
"comments": ["String"]
|
||||||
}
|
}
|
|
@ -1,83 +1,83 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "Address",
|
"name": "Address",
|
||||||
"properties": {
|
"properties": {
|
||||||
"label": "string",
|
"label": "string",
|
||||||
"street": "string",
|
"street": "string",
|
||||||
"city": "string",
|
"city": "string",
|
||||||
"zipCode": "string"
|
"zipCode": "string"
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Account",
|
|
||||||
"properties": {
|
|
||||||
"id": "string",
|
|
||||||
"customer": {
|
|
||||||
"type": "Customer",
|
|
||||||
"relation": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"as": "account"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"balance": "number"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Customer",
|
|
||||||
"options": {
|
|
||||||
"oracle": {
|
|
||||||
"owner": "STRONGLOOP",
|
|
||||||
"table": "CUSTOMER"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "number",
|
|
||||||
"id": true,
|
|
||||||
"doc": "Customer ID"
|
|
||||||
},
|
|
||||||
"firstName": {
|
|
||||||
"type": "string",
|
|
||||||
"trim": true,
|
|
||||||
"required": true,
|
|
||||||
"oracle": {
|
|
||||||
"column": "FNAME",
|
|
||||||
"type": "VARCHAR",
|
|
||||||
"length": 32
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lastName": {
|
|
||||||
"type": "string",
|
|
||||||
"trim": true,
|
|
||||||
"required": true,
|
|
||||||
"oracle": {
|
|
||||||
"column": "LNAME",
|
|
||||||
"type": "VARCHAR",
|
|
||||||
"length": 32
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"vip": {
|
|
||||||
"type": "boolean",
|
|
||||||
"doc": "indicate if the customer is a VIP",
|
|
||||||
"oracle": {
|
|
||||||
"column": "VIP",
|
|
||||||
"type": "CHAR",
|
|
||||||
"length": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"emails": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"trim": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"address": {
|
|
||||||
"type": "Address"
|
|
||||||
},
|
|
||||||
"account": "Account"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Account",
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"customer": {
|
||||||
|
"type": "Customer",
|
||||||
|
"relation": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"as": "account"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"balance": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Customer",
|
||||||
|
"options": {
|
||||||
|
"oracle": {
|
||||||
|
"owner": "STRONGLOOP",
|
||||||
|
"table": "CUSTOMER"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"doc": "Customer ID"
|
||||||
|
},
|
||||||
|
"firstName": {
|
||||||
|
"type": "string",
|
||||||
|
"trim": true,
|
||||||
|
"required": true,
|
||||||
|
"oracle": {
|
||||||
|
"column": "FNAME",
|
||||||
|
"type": "VARCHAR",
|
||||||
|
"length": 32
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lastName": {
|
||||||
|
"type": "string",
|
||||||
|
"trim": true,
|
||||||
|
"required": true,
|
||||||
|
"oracle": {
|
||||||
|
"column": "LNAME",
|
||||||
|
"type": "VARCHAR",
|
||||||
|
"length": 32
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vip": {
|
||||||
|
"type": "boolean",
|
||||||
|
"doc": "indicate if the customer is a VIP",
|
||||||
|
"oracle": {
|
||||||
|
"column": "VIP",
|
||||||
|
"type": "CHAR",
|
||||||
|
"length": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"emails": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"trim": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"address": {
|
||||||
|
"type": "Address"
|
||||||
|
},
|
||||||
|
"account": "Account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
]
|
]
|
|
@ -4,10 +4,8 @@ var fieldsToArray = utils.fieldsToArray;
|
||||||
var removeUndefined = utils.removeUndefined;
|
var removeUndefined = utils.removeUndefined;
|
||||||
var mergeSettings = utils.mergeSettings;
|
var mergeSettings = utils.mergeSettings;
|
||||||
|
|
||||||
|
describe('util.fieldsToArray', function () {
|
||||||
describe('util.fieldsToArray', function(){
|
it('Turn objects and strings into an array of fields to include when finding models', function () {
|
||||||
it('Turn objects and strings into an array of fields to include when finding models', function() {
|
|
||||||
|
|
||||||
|
|
||||||
function sample(fields) {
|
function sample(fields) {
|
||||||
var properties = ['foo', 'bar', 'bat', 'baz'];
|
var properties = ['foo', 'bar', 'bat', 'baz'];
|
||||||
|
@ -30,90 +28,90 @@ describe('util.fieldsToArray', function(){
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('util.removeUndefined', function(){
|
describe('util.removeUndefined', function () {
|
||||||
it('Remove undefined values from the query object', function() {
|
it('Remove undefined values from the query object', function () {
|
||||||
var q1 = {where: {x: 1, y: undefined}};
|
var q1 = {where: {x: 1, y: undefined}};
|
||||||
should.deepEqual(removeUndefined(q1), {where: {x: 1}});
|
should.deepEqual(removeUndefined(q1), {where: {x: 1}});
|
||||||
|
|
||||||
var q2 = {where: {x: 1, y: 2}};
|
var q2 = {where: {x: 1, y: 2}};
|
||||||
should.deepEqual(removeUndefined(q2), {where: {x: 1, y: 2}});
|
should.deepEqual(removeUndefined(q2), {where: {x: 1, y: 2}});
|
||||||
|
|
||||||
var q3 = {where: {x: 1, y: {in: [2, undefined]}}};
|
var q3 = {where: {x: 1, y: {in: [2, undefined]}}};
|
||||||
should.deepEqual(removeUndefined(q3), {where: {x: 1, y: {in: [2]}}});
|
should.deepEqual(removeUndefined(q3), {where: {x: 1, y: {in: [2]}}});
|
||||||
|
|
||||||
should.equal(removeUndefined(null), null);
|
should.equal(removeUndefined(null), null);
|
||||||
|
|
||||||
should.equal(removeUndefined(undefined), undefined);
|
should.equal(removeUndefined(undefined), undefined);
|
||||||
|
|
||||||
should.equal(removeUndefined('x'), 'x');
|
should.equal(removeUndefined('x'), 'x');
|
||||||
|
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
var q4 = {where: {x: 1, y: date}};
|
var q4 = {where: {x: 1, y: date}};
|
||||||
should.deepEqual(removeUndefined(q4), {where: {x: 1, y: date}});
|
should.deepEqual(removeUndefined(q4), {where: {x: 1, y: date}});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('util.parseSettings', function(){
|
describe('util.parseSettings', function () {
|
||||||
it('Parse a full url into a settings object', function() {
|
it('Parse a full url into a settings object', function () {
|
||||||
var url = 'mongodb://x:y@localhost:27017/mydb?w=2';
|
var url = 'mongodb://x:y@localhost:27017/mydb?w=2';
|
||||||
var settings = utils.parseSettings(url);
|
var settings = utils.parseSettings(url);
|
||||||
should.equal(settings.hostname, 'localhost');
|
should.equal(settings.hostname, 'localhost');
|
||||||
should.equal(settings.port, 27017);
|
should.equal(settings.port, 27017);
|
||||||
should.equal(settings.host, 'localhost');
|
should.equal(settings.host, 'localhost');
|
||||||
should.equal(settings.user, 'x');
|
should.equal(settings.user, 'x');
|
||||||
should.equal(settings.password, 'y');
|
should.equal(settings.password, 'y');
|
||||||
should.equal(settings.database, 'mydb');
|
should.equal(settings.database, 'mydb');
|
||||||
should.equal(settings.connector, 'mongodb');
|
should.equal(settings.connector, 'mongodb');
|
||||||
should.equal(settings.w, '2');
|
should.equal(settings.w, '2');
|
||||||
should.equal(settings.url, 'mongodb://x:y@localhost:27017/mydb?w=2');
|
should.equal(settings.url, 'mongodb://x:y@localhost:27017/mydb?w=2');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Parse a url without auth into a settings object', function() {
|
it('Parse a url without auth into a settings object', function () {
|
||||||
var url = 'mongodb://localhost:27017/mydb/abc?w=2';
|
var url = 'mongodb://localhost:27017/mydb/abc?w=2';
|
||||||
var settings = utils.parseSettings(url);
|
var settings = utils.parseSettings(url);
|
||||||
should.equal(settings.hostname, 'localhost');
|
should.equal(settings.hostname, 'localhost');
|
||||||
should.equal(settings.port, 27017);
|
should.equal(settings.port, 27017);
|
||||||
should.equal(settings.host, 'localhost');
|
should.equal(settings.host, 'localhost');
|
||||||
should.equal(settings.user, undefined);
|
should.equal(settings.user, undefined);
|
||||||
should.equal(settings.password, undefined);
|
should.equal(settings.password, undefined);
|
||||||
should.equal(settings.database, 'mydb');
|
should.equal(settings.database, 'mydb');
|
||||||
should.equal(settings.connector, 'mongodb');
|
should.equal(settings.connector, 'mongodb');
|
||||||
should.equal(settings.w, '2');
|
should.equal(settings.w, '2');
|
||||||
should.equal(settings.url, 'mongodb://localhost:27017/mydb/abc?w=2');
|
should.equal(settings.url, 'mongodb://localhost:27017/mydb/abc?w=2');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Parse a url with complex query into a settings object', function() {
|
it('Parse a url with complex query into a settings object', function () {
|
||||||
var url = 'mysql://127.0.0.1:3306/mydb?x[a]=1&x[b]=2&engine=InnoDB';
|
var url = 'mysql://127.0.0.1:3306/mydb?x[a]=1&x[b]=2&engine=InnoDB';
|
||||||
var settings = utils.parseSettings(url);
|
var settings = utils.parseSettings(url);
|
||||||
should.equal(settings.hostname, '127.0.0.1');
|
should.equal(settings.hostname, '127.0.0.1');
|
||||||
should.equal(settings.port, 3306);
|
should.equal(settings.port, 3306);
|
||||||
should.equal(settings.host, '127.0.0.1');
|
should.equal(settings.host, '127.0.0.1');
|
||||||
should.equal(settings.user, undefined);
|
should.equal(settings.user, undefined);
|
||||||
should.equal(settings.password, undefined);
|
should.equal(settings.password, undefined);
|
||||||
should.equal(settings.database, 'mydb');
|
should.equal(settings.database, 'mydb');
|
||||||
should.equal(settings.connector, 'mysql');
|
should.equal(settings.connector, 'mysql');
|
||||||
should.equal(settings.x.a, '1');
|
should.equal(settings.x.a, '1');
|
||||||
should.equal(settings.x.b, '2');
|
should.equal(settings.x.b, '2');
|
||||||
should.equal(settings.engine, 'InnoDB');
|
should.equal(settings.engine, 'InnoDB');
|
||||||
should.equal(settings.url, 'mysql://127.0.0.1:3306/mydb?x[a]=1&x[b]=2&engine=InnoDB');
|
should.equal(settings.url, 'mysql://127.0.0.1:3306/mydb?x[a]=1&x[b]=2&engine=InnoDB');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Parse a url without auth into a settings object', function() {
|
it('Parse a url without auth into a settings object', function () {
|
||||||
var url = 'memory://?x=1';
|
var url = 'memory://?x=1';
|
||||||
var settings = utils.parseSettings(url);
|
var settings = utils.parseSettings(url);
|
||||||
should.equal(settings.hostname, '');
|
should.equal(settings.hostname, '');
|
||||||
should.equal(settings.user, undefined);
|
should.equal(settings.user, undefined);
|
||||||
should.equal(settings.password, undefined);
|
should.equal(settings.password, undefined);
|
||||||
should.equal(settings.database, undefined);
|
should.equal(settings.database, undefined);
|
||||||
should.equal(settings.connector, 'memory');
|
should.equal(settings.connector, 'memory');
|
||||||
should.equal(settings.x, '1');
|
should.equal(settings.x, '1');
|
||||||
should.equal(settings.url, 'memory://?x=1');
|
should.equal(settings.url, 'memory://?x=1');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,198 +5,198 @@ var j = require('../'), db, User;
|
||||||
var ValidationError = j.ValidationError;
|
var ValidationError = j.ValidationError;
|
||||||
|
|
||||||
function getValidAttributes() {
|
function getValidAttributes() {
|
||||||
return {
|
return {
|
||||||
name: 'Anatoliy',
|
name: 'Anatoliy',
|
||||||
email: 'email@example.com',
|
email: 'email@example.com',
|
||||||
state: '',
|
state: '',
|
||||||
age: 26,
|
age: 26,
|
||||||
gender: 'male',
|
gender: 'male',
|
||||||
createdByAdmin: false,
|
createdByAdmin: false,
|
||||||
createdByScript: true
|
createdByScript: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('validations', function() {
|
describe('validations', function () {
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
db = getSchema();
|
||||||
|
User = db.define('User', {
|
||||||
|
email: String,
|
||||||
|
name: String,
|
||||||
|
password: String,
|
||||||
|
state: String,
|
||||||
|
age: Number,
|
||||||
|
gender: String,
|
||||||
|
domain: String,
|
||||||
|
pendingPeriod: Number,
|
||||||
|
createdByAdmin: Boolean,
|
||||||
|
createdByScript: Boolean,
|
||||||
|
updatedAt: Date
|
||||||
|
});
|
||||||
|
db.automigrate(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(function (done) {
|
||||||
|
User.destroyAll(function () {
|
||||||
|
delete User._validations;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function () {
|
||||||
|
db.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('commons', function () {
|
||||||
|
|
||||||
|
describe('skipping', function () {
|
||||||
|
|
||||||
|
it('should allow to skip using if: attribute', function () {
|
||||||
|
User.validatesPresenceOf('pendingPeriod', {if: 'createdByAdmin'});
|
||||||
|
var user = new User;
|
||||||
|
user.createdByAdmin = true;
|
||||||
|
user.isValid().should.be.false;
|
||||||
|
user.errors.pendingPeriod.should.eql(['can\'t be blank']);
|
||||||
|
user.pendingPeriod = 1
|
||||||
|
user.isValid().should.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
before(function(done) {
|
|
||||||
db = getSchema();
|
|
||||||
User = db.define('User', {
|
|
||||||
email: String,
|
|
||||||
name: String,
|
|
||||||
password: String,
|
|
||||||
state: String,
|
|
||||||
age: Number,
|
|
||||||
gender: String,
|
|
||||||
domain: String,
|
|
||||||
pendingPeriod: Number,
|
|
||||||
createdByAdmin: Boolean,
|
|
||||||
createdByScript: Boolean,
|
|
||||||
updatedAt: Date
|
|
||||||
});
|
|
||||||
db.automigrate(done);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function(done) {
|
describe('lifecycle', function () {
|
||||||
User.destroyAll(function() {
|
|
||||||
delete User._validations;
|
it('should work on create', function (done) {
|
||||||
|
delete User._validations;
|
||||||
|
User.validatesPresenceOf('name');
|
||||||
|
User.create(function (e, u) {
|
||||||
|
should.exist(e);
|
||||||
|
User.create({name: 'Valid'}, function (e, d) {
|
||||||
|
should.not.exist(e);
|
||||||
done();
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function() {
|
it('should work on update', function (done) {
|
||||||
db.disconnect();
|
delete User._validations;
|
||||||
});
|
User.validatesPresenceOf('name');
|
||||||
|
User.create({name: 'Valid'}, function (e, d) {
|
||||||
describe('commons', function() {
|
d.updateAttribute('name', null, function (e) {
|
||||||
|
should.exist(e);
|
||||||
describe('skipping', function() {
|
e.should.be.instanceOf(Error);
|
||||||
|
e.should.be.instanceOf(ValidationError);
|
||||||
it('should allow to skip using if: attribute', function() {
|
d.updateAttribute('name', 'Vasiliy', function (e) {
|
||||||
User.validatesPresenceOf('pendingPeriod', {if: 'createdByAdmin'});
|
should.not.exist(e);
|
||||||
var user = new User;
|
done();
|
||||||
user.createdByAdmin = true;
|
|
||||||
user.isValid().should.be.false;
|
|
||||||
user.errors.pendingPeriod.should.eql(['can\'t be blank']);
|
|
||||||
user.pendingPeriod = 1
|
|
||||||
user.isValid().should.be.true;
|
|
||||||
});
|
});
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('lifecycle', function() {
|
it('should return error code', function (done) {
|
||||||
|
delete User._validations;
|
||||||
it('should work on create', function(done) {
|
User.validatesPresenceOf('name');
|
||||||
delete User._validations;
|
User.create(function (e, u) {
|
||||||
User.validatesPresenceOf('name');
|
should.exist(e);
|
||||||
User.create(function(e, u) {
|
e.details.codes.name.should.eql(['presence']);
|
||||||
should.exist(e);
|
done();
|
||||||
User.create({name: 'Valid'}, function(e, d) {
|
|
||||||
should.not.exist(e);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work on update', function(done) {
|
|
||||||
delete User._validations;
|
|
||||||
User.validatesPresenceOf('name');
|
|
||||||
User.create({name: 'Valid'}, function(e, d) {
|
|
||||||
d.updateAttribute('name', null, function(e) {
|
|
||||||
should.exist(e);
|
|
||||||
e.should.be.instanceOf(Error);
|
|
||||||
e.should.be.instanceOf(ValidationError);
|
|
||||||
d.updateAttribute('name', 'Vasiliy', function(e) {
|
|
||||||
should.not.exist(e);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return error code', function(done) {
|
|
||||||
delete User._validations;
|
|
||||||
User.validatesPresenceOf('name');
|
|
||||||
User.create(function(e, u) {
|
|
||||||
should.exist(e);
|
|
||||||
e.details.codes.name.should.eql(['presence']);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow to modify error after validation', function(done) {
|
|
||||||
User.afterValidate = function(next) {
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow to modify error after validation', function (done) {
|
||||||
|
User.afterValidate = function (next) {
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('presence', function () {
|
||||||
|
|
||||||
|
it('should validate presence', function () {
|
||||||
|
User.validatesPresenceOf('name', 'email');
|
||||||
|
var u = new User;
|
||||||
|
u.isValid().should.not.be.true;
|
||||||
|
u.name = 1;
|
||||||
|
u.email = 2;
|
||||||
|
u.isValid().should.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('presence', function() {
|
it('should skip validation by property (if/unless)', function () {
|
||||||
|
User.validatesPresenceOf('domain', {unless: 'createdByScript'});
|
||||||
|
|
||||||
it('should validate presence', function() {
|
var user = new User(getValidAttributes())
|
||||||
User.validatesPresenceOf('name', 'email');
|
user.isValid().should.be.true;
|
||||||
var u = new User;
|
|
||||||
u.isValid().should.not.be.true;
|
user.createdByScript = false;
|
||||||
u.name = 1;
|
user.isValid().should.be.false;
|
||||||
u.email = 2;
|
user.errors.domain.should.eql(['can\'t be blank']);
|
||||||
u.isValid().should.be.true;
|
|
||||||
|
user.domain = 'domain';
|
||||||
|
user.isValid().should.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('uniqueness', function () {
|
||||||
|
it('should validate uniqueness', function (done) {
|
||||||
|
User.validatesUniquenessOf('email');
|
||||||
|
var u = new User({email: 'hey'});
|
||||||
|
Boolean(u.isValid(function (valid) {
|
||||||
|
valid.should.be.true;
|
||||||
|
u.save(function () {
|
||||||
|
var u2 = new User({email: 'hey'});
|
||||||
|
u2.isValid(function (valid) {
|
||||||
|
valid.should.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
})).should.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
it('should skip validation by property (if/unless)', function() {
|
it('should handle same object modification', function (done) {
|
||||||
User.validatesPresenceOf('domain', {unless: 'createdByScript'});
|
User.validatesUniquenessOf('email');
|
||||||
|
var u = new User({email: 'hey'});
|
||||||
var user = new User(getValidAttributes())
|
Boolean(u.isValid(function (valid) {
|
||||||
user.isValid().should.be.true;
|
valid.should.be.true;
|
||||||
|
u.save(function () {
|
||||||
user.createdByScript = false;
|
u.name = 'Goghi';
|
||||||
user.isValid().should.be.false;
|
u.isValid(function (valid) {
|
||||||
user.errors.domain.should.eql(['can\'t be blank']);
|
valid.should.be.true;
|
||||||
|
u.save(done);
|
||||||
user.domain = 'domain';
|
});
|
||||||
user.isValid().should.be.true;
|
|
||||||
});
|
});
|
||||||
|
// async validations always falsy when called as sync
|
||||||
|
})).should.not.be.ok;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('uniqueness', function() {
|
});
|
||||||
it('should validate uniqueness', function(done) {
|
|
||||||
User.validatesUniquenessOf('email');
|
|
||||||
var u = new User({email: 'hey'});
|
|
||||||
Boolean(u.isValid(function(valid) {
|
|
||||||
valid.should.be.true;
|
|
||||||
u.save(function() {
|
|
||||||
var u2 = new User({email: 'hey'});
|
|
||||||
u2.isValid(function(valid) {
|
|
||||||
valid.should.be.false;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})).should.be.false;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle same object modification', function(done) {
|
describe('format', function () {
|
||||||
User.validatesUniquenessOf('email');
|
it('should validate format');
|
||||||
var u = new User({email: 'hey'});
|
it('should overwrite default blank message with custom format message');
|
||||||
Boolean(u.isValid(function(valid) {
|
});
|
||||||
valid.should.be.true;
|
|
||||||
u.save(function() {
|
|
||||||
u.name = 'Goghi';
|
|
||||||
u.isValid(function(valid) {
|
|
||||||
valid.should.be.true;
|
|
||||||
u.save(done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// async validations always falsy when called as sync
|
|
||||||
})).should.not.be.ok;
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
describe('numericality', function () {
|
||||||
|
it('should validate numericality');
|
||||||
|
});
|
||||||
|
|
||||||
describe('format', function() {
|
describe('inclusion', function () {
|
||||||
it('should validate format');
|
it('should validate inclusion');
|
||||||
it('should overwrite default blank message with custom format message');
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('numericality', function() {
|
describe('exclusion', function () {
|
||||||
it('should validate numericality');
|
it('should validate exclusion');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('inclusion', function() {
|
describe('length', function () {
|
||||||
it('should validate inclusion');
|
it('should validate length');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('exclusion', function() {
|
describe('custom', function () {
|
||||||
it('should validate exclusion');
|
it('should validate using custom sync validation');
|
||||||
});
|
it('should validate using custom async validation');
|
||||||
|
});
|
||||||
describe('length', function() {
|
|
||||||
it('should validate length');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('custom', function() {
|
|
||||||
it('should validate using custom sync validation');
|
|
||||||
it('should validate using custom async validation');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue