Merge branch 'release/1.2.13' into production
This commit is contained in:
commit
758cea9f5c
|
@ -4,7 +4,9 @@ LoopBack DataSource Juggler is an ORM that provides a common set of interfaces
|
|||
for interacting with databases, REST APIs, and other data sources. It was
|
||||
initially forked from [JugglingDB](https://github.com/1602/jugglingdb).
|
||||
|
||||
For full documentation, see [StrongLoop Suite Documentation](http://docs.strongloop.com/display/DOC/Data+Source+Juggler).
|
||||
**For full documentation, see the official StrongLoop documentation**:
|
||||
* [Data sources and connectors](http://docs.strongloop.com/display/DOC/Data+sources+and+connectors)
|
||||
* [Data Source Juggler](http://docs.strongloop.com/display/DOC/Data+Source+Juggler).
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
|
@ -58,12 +58,15 @@ The `DataSource` constructor is available from `loopback-datasource-juggler` mod
|
|||
- connector: The name or instance of the connector module
|
||||
- settings: An object of properties to configure the connector
|
||||
|
||||
var dataSource = new DataSource({
|
||||
connector: require('loopback-connector-mongodb'),
|
||||
host: 'localhost',
|
||||
port: 27017,
|
||||
database: 'mydb'
|
||||
});
|
||||
|
||||
```
|
||||
var dataSource = new DataSource({
|
||||
connector: require('loopback-connector-mongodb'),
|
||||
host: 'localhost',
|
||||
port: 27017,
|
||||
database: 'mydb'
|
||||
});
|
||||
```
|
||||
|
||||
#### connector
|
||||
|
||||
|
@ -75,10 +78,11 @@ The `connector` argument passed the DataSource constructor can be one of the fol
|
|||
to 'loopback-connector-<shortName>'
|
||||
* A local module under ./connectors/<connectorName> folder
|
||||
|
||||
|
||||
var ds1 = new DataSource('memory');
|
||||
var ds2 = new DataSource('loopback-connector-mongodb'));
|
||||
var ds3 = new DataSource(require('loopback-connector-oracle'));
|
||||
```
|
||||
var ds1 = new DataSource('memory');
|
||||
var ds2 = new DataSource('loopback-connector-mongodb'));
|
||||
var ds3 = new DataSource(require('loopback-connector-oracle'));
|
||||
```
|
||||
|
||||
**Note**: LoopBack provides a built-in connector named as `memory` to use in-memory
|
||||
store for CRUD operations.
|
||||
|
|
|
@ -8,56 +8,56 @@ var ds = new DataSource('memory');
|
|||
var Application = ds.createModel('Schemaless', {}, {strict: false});
|
||||
|
||||
var application = {
|
||||
owner: 'rfeng',
|
||||
name: 'MyApp1',
|
||||
description: 'My first app',
|
||||
pushSettings: [
|
||||
{ "platform": "apns",
|
||||
"apns": {
|
||||
"pushOptions": {
|
||||
"gateway": "gateway.sandbox.push.apple.com",
|
||||
"cert": "credentials/apns_cert_dev.pem",
|
||||
"key": "credentials/apns_key_dev.pem"
|
||||
},
|
||||
owner: 'rfeng',
|
||||
name: 'MyApp1',
|
||||
description: 'My first app',
|
||||
pushSettings: [
|
||||
{ "platform": "apns",
|
||||
"apns": {
|
||||
"pushOptions": {
|
||||
"gateway": "gateway.sandbox.push.apple.com",
|
||||
"cert": "credentials/apns_cert_dev.pem",
|
||||
"key": "credentials/apns_key_dev.pem"
|
||||
},
|
||||
|
||||
"feedbackOptions": {
|
||||
"gateway": "feedback.sandbox.push.apple.com",
|
||||
"cert": "credentials/apns_cert_dev.pem",
|
||||
"key": "credentials/apns_key_dev.pem",
|
||||
"batchFeedback": true,
|
||||
"interval": 300
|
||||
}
|
||||
}}
|
||||
]}
|
||||
"feedbackOptions": {
|
||||
"gateway": "feedback.sandbox.push.apple.com",
|
||||
"cert": "credentials/apns_cert_dev.pem",
|
||||
"key": "credentials/apns_key_dev.pem",
|
||||
"batchFeedback": true,
|
||||
"interval": 300
|
||||
}
|
||||
}}
|
||||
]}
|
||||
|
||||
console.log(new Application(application).toObject());
|
||||
|
||||
Application.create(application, function (err, app1) {
|
||||
console.log('Created: ', app1.toObject());
|
||||
Application.findById(app1.id, function (err, app2) {
|
||||
console.log('Found: ', app2.toObject());
|
||||
});
|
||||
console.log('Created: ', app1.toObject());
|
||||
Application.findById(app1.id, function (err, app2) {
|
||||
console.log('Found: ', app2.toObject());
|
||||
});
|
||||
});
|
||||
|
||||
// Instance JSON document
|
||||
var user = {
|
||||
name: 'Joe',
|
||||
age: 30,
|
||||
birthday: new Date(),
|
||||
vip: true,
|
||||
address: {
|
||||
street: '1 Main St',
|
||||
city: 'San Jose',
|
||||
state: 'CA',
|
||||
zipcode: '95131',
|
||||
country: 'US'
|
||||
},
|
||||
friends: ['John', 'Mary'],
|
||||
emails: [
|
||||
{label: 'work', id: 'x@sample.com'},
|
||||
{label: 'home', id: 'x@home.com'}
|
||||
],
|
||||
tags: []
|
||||
name: 'Joe',
|
||||
age: 30,
|
||||
birthday: new Date(),
|
||||
vip: true,
|
||||
address: {
|
||||
street: '1 Main St',
|
||||
city: 'San Jose',
|
||||
state: 'CA',
|
||||
zipcode: '95131',
|
||||
country: 'US'
|
||||
},
|
||||
friends: ['John', 'Mary'],
|
||||
emails: [
|
||||
{label: 'work', id: 'x@sample.com'},
|
||||
{label: 'home', id: 'x@home.com'}
|
||||
],
|
||||
tags: []
|
||||
};
|
||||
|
||||
// Introspect the JSON document to generate a schema
|
||||
|
@ -72,10 +72,10 @@ var obj = new User(user);
|
|||
console.log(obj.toObject());
|
||||
|
||||
User.create(user, function (err, u1) {
|
||||
console.log('Created: ', u1.toObject());
|
||||
User.findById(u1.id, function (err, u2) {
|
||||
console.log('Found: ', u2.toObject());
|
||||
});
|
||||
console.log('Created: ', u1.toObject());
|
||||
User.findById(u1.id, function (err, u2) {
|
||||
console.log('Found: ', u2.toObject());
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -2,27 +2,29 @@ var ModelBuilder = require('../../loopback-datasource-juggler').ModelBuilder;
|
|||
var modelBuilder = new ModelBuilder();
|
||||
// define models
|
||||
var Post = modelBuilder.define('Post', {
|
||||
title: { type: String, length: 255 },
|
||||
content: { type: ModelBuilder.Text },
|
||||
date: { type: Date, default: function () { return new Date;} },
|
||||
timestamp: { type: Number, default: Date.now },
|
||||
published: { type: Boolean, default: false, index: true }
|
||||
title: { type: String, length: 255 },
|
||||
content: { type: ModelBuilder.Text },
|
||||
date: { type: Date, default: function () {
|
||||
return new Date();
|
||||
} },
|
||||
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', {
|
||||
name: String,
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
name: String,
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
});
|
||||
|
||||
var Group = modelBuilder.define('Group', {group: String});
|
||||
|
||||
// define any custom method
|
||||
User.prototype.getNameAndAge = function () {
|
||||
return this.name + ', ' + this.age;
|
||||
return this.name + ', ' + this.age;
|
||||
};
|
||||
|
||||
var user = new User({name: 'Joe'});
|
||||
|
|
|
@ -4,29 +4,29 @@ var ds = new DataSource('memory');
|
|||
|
||||
// define models
|
||||
var Post = ds.define('Post', {
|
||||
title: { type: String, length: 255 },
|
||||
content: { type: DataSource.Text },
|
||||
date: { type: Date, default: function () {
|
||||
return new Date;
|
||||
} },
|
||||
timestamp: { type: Number, default: Date.now },
|
||||
published: { type: Boolean, default: false, index: true }
|
||||
title: { type: String, length: 255 },
|
||||
content: { type: DataSource.Text },
|
||||
date: { type: Date, default: function () {
|
||||
return new Date;
|
||||
} },
|
||||
timestamp: { type: Number, default: Date.now },
|
||||
published: { type: Boolean, default: false, index: true }
|
||||
});
|
||||
|
||||
// simplier way to describe model
|
||||
var User = ds.define('User', {
|
||||
name: String,
|
||||
bio: DataSource.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
name: String,
|
||||
bio: DataSource.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
});
|
||||
|
||||
var Group = ds.define('Group', {name: String});
|
||||
|
||||
// define any custom method
|
||||
User.prototype.getNameAndAge = function () {
|
||||
return this.name + ', ' + this.age;
|
||||
return this.name + ', ' + this.age;
|
||||
};
|
||||
|
||||
var user = new User({name: 'Joe'});
|
||||
|
@ -53,48 +53,48 @@ User.hasAndBelongsToMany('groups');
|
|||
|
||||
var user2 = new User({name: 'Smith'});
|
||||
user2.save(function (err) {
|
||||
console.log(user2);
|
||||
var post = user2.posts.build({title: 'Hello world'});
|
||||
post.save(function(err, data) {
|
||||
console.log(err ? err: data);
|
||||
});
|
||||
console.log(user2);
|
||||
var post = user2.posts.build({title: 'Hello world'});
|
||||
post.save(function (err, data) {
|
||||
console.log(err ? 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) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
console.log(data);
|
||||
var post = data.posts.build({title: 'My Post'});
|
||||
console.log(post);
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
console.log(data);
|
||||
var post = data.posts.build({title: 'My Post'});
|
||||
console.log(post);
|
||||
});
|
||||
|
||||
User.create({name: 'Ray'}, function (err, data) {
|
||||
console.log(data);
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
User.scope('minors', {age: {le: 16}});
|
||||
User.minors(function(err, kids) {
|
||||
console.log('Kids: ', kids);
|
||||
User.minors(function (err, kids) {
|
||||
console.log('Kids: ', kids);
|
||||
});
|
||||
|
||||
var Article = ds.define('Article', {title: String});
|
||||
var Tag = ds.define('Tag', {name: String});
|
||||
Article.hasAndBelongsToMany('tags');
|
||||
|
||||
Article.create(function(e, article) {
|
||||
article.tags.create({name: 'popular'}, function (err, data) {
|
||||
Article.findOne(function(e, article) {
|
||||
article.tags(function(e, tags) {
|
||||
console.log(tags);
|
||||
});
|
||||
});
|
||||
Article.create(function (e, article) {
|
||||
article.tags.create({name: 'popular'}, function (err, data) {
|
||||
Article.findOne(function (e, article) {
|
||||
article.tags(function (e, tags) {
|
||||
console.log(tags);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// should be able to attach a data source to an existing model
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
{
|
||||
"title": {
|
||||
"type": "String"
|
||||
},
|
||||
"author": {
|
||||
"type": "String",
|
||||
"default": "Raymond"
|
||||
},
|
||||
"body": "String",
|
||||
"date": {
|
||||
"type": "Date"
|
||||
},
|
||||
"hidden": "Boolean",
|
||||
"comments": ["String"]
|
||||
"title": {
|
||||
"type": "String"
|
||||
},
|
||||
"author": {
|
||||
"type": "String",
|
||||
"default": "Raymond"
|
||||
},
|
||||
"body": "String",
|
||||
"date": {
|
||||
"type": "Date"
|
||||
},
|
||||
"hidden": "Boolean",
|
||||
"comments": ["String"]
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
var path = require('path'),
|
||||
fs = require('fs'),
|
||||
DataSource = require('../lib/datasource').DataSource;
|
||||
fs = require('fs'),
|
||||
DataSource = require('../lib/datasource').DataSource;
|
||||
|
||||
/**
|
||||
* Load LDL schemas from a json doc
|
||||
|
@ -8,27 +8,27 @@ var path = require('path'),
|
|||
* @returns A map of schemas keyed by name
|
||||
*/
|
||||
function loadSchemasSync(schemaFile, dataSource) {
|
||||
// Set up the data source
|
||||
if(!dataSource) {
|
||||
dataSource = new DataSource('memory');
|
||||
}
|
||||
// Set up the data source
|
||||
if (!dataSource) {
|
||||
dataSource = new DataSource('memory');
|
||||
}
|
||||
|
||||
// Read the dataSource JSON file
|
||||
var schemas = JSON.parse(fs.readFileSync(schemaFile));
|
||||
// Read the dataSource JSON file
|
||||
var schemas = JSON.parse(fs.readFileSync(schemaFile));
|
||||
|
||||
return dataSource.buildModels(schemas);
|
||||
return dataSource.buildModels(schemas);
|
||||
|
||||
}
|
||||
|
||||
var models = loadSchemasSync(path.join(__dirname, 'jdb-schemas.json'));
|
||||
|
||||
for (var s in models) {
|
||||
var m = models[s];
|
||||
console.log(m.modelName, new m());
|
||||
var m = models[s];
|
||||
console.log(m.modelName, new m());
|
||||
}
|
||||
|
||||
models = loadSchemasSync(path.join(__dirname, 'schemas.json'));
|
||||
for (var s in models) {
|
||||
var m = models[s];
|
||||
console.log(m.modelName, new m());
|
||||
var m = models[s];
|
||||
console.log(m.modelName, new m());
|
||||
}
|
||||
|
|
|
@ -3,26 +3,30 @@ var modelBuilder = new ModelBuilder();
|
|||
|
||||
// simplier way to describe model
|
||||
var User = modelBuilder.define('User', {
|
||||
name: String,
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number,
|
||||
address: {
|
||||
street: String,
|
||||
city: String,
|
||||
state: String,
|
||||
zipCode: String,
|
||||
country: String
|
||||
},
|
||||
emails: [{
|
||||
label: String,
|
||||
email: String
|
||||
}],
|
||||
friends: [String]
|
||||
name: String,
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number,
|
||||
address: {
|
||||
street: String,
|
||||
city: String,
|
||||
state: String,
|
||||
zipCode: String,
|
||||
country: String
|
||||
},
|
||||
emails: [
|
||||
{
|
||||
label: String,
|
||||
email: String
|
||||
}
|
||||
],
|
||||
friends: [String]
|
||||
});
|
||||
|
||||
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'}],
|
||||
friends: ['John', 'Mary']});
|
||||
emails: [
|
||||
{label: 'work', email: 'xyz@sample.com'}
|
||||
],
|
||||
friends: ['John', 'Mary']});
|
||||
console.log(user.toObject());
|
||||
|
|
|
@ -2,58 +2,56 @@ var DataSource = require('../index').DataSource;
|
|||
var ds = new DataSource('memory');
|
||||
|
||||
var Order = ds.createModel('Order', {
|
||||
customerId: Number,
|
||||
orderDate: Date
|
||||
customerId: Number,
|
||||
orderDate: Date
|
||||
});
|
||||
|
||||
var Customer = ds.createModel('Customer', {
|
||||
name: String
|
||||
name: String
|
||||
});
|
||||
|
||||
Order.belongsTo(Customer);
|
||||
|
||||
Customer.create({name: 'John'}, function (err, customer) {
|
||||
Order.create({customerId: customer.id, orderDate: new Date()}, function (err, order) {
|
||||
order.customer(console.log);
|
||||
order.customer(true, console.log);
|
||||
Order.create({customerId: customer.id, orderDate: new Date()}, function (err, order) {
|
||||
order.customer(console.log);
|
||||
order.customer(true, console.log);
|
||||
|
||||
Customer.create({name: 'Mary'}, function (err, customer2) {
|
||||
order.customer(customer2);
|
||||
order.customer(console.log);
|
||||
});
|
||||
Customer.create({name: 'Mary'}, function (err, customer2) {
|
||||
order.customer(customer2);
|
||||
order.customer(console.log);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Customer.hasMany(Order, {as: 'orders', foreignKey: 'customerId'});
|
||||
|
||||
Customer.create({name: 'Ray'}, function (err, customer) {
|
||||
Order.create({customerId: customer.id, orderDate: new Date()}, function (err, order) {
|
||||
customer.orders(console.log);
|
||||
customer.orders.create({orderDate: new Date()}, function(err, order) {
|
||||
console.log(order);
|
||||
Customer.include([customer], 'orders', function(err, results) {
|
||||
console.log('Results: ', results);
|
||||
});
|
||||
customer.orders.findById('2', console.log);
|
||||
customer.orders.destroy('2', console.log);
|
||||
});
|
||||
Order.create({customerId: customer.id, orderDate: new Date()}, function (err, order) {
|
||||
customer.orders(console.log);
|
||||
customer.orders.create({orderDate: new Date()}, function (err, order) {
|
||||
console.log(order);
|
||||
Customer.include([customer], 'orders', function (err, results) {
|
||||
console.log('Results: ', results);
|
||||
});
|
||||
customer.orders.findById('2', console.log);
|
||||
customer.orders.destroy('2', console.log);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
var Physician = ds.createModel('Physician', {
|
||||
name: String
|
||||
name: String
|
||||
});
|
||||
|
||||
var Patient = ds.createModel('Patient', {
|
||||
name: String
|
||||
name: String
|
||||
});
|
||||
|
||||
var Appointment = ds.createModel('Appointment', {
|
||||
physicianId: Number,
|
||||
patientId: Number,
|
||||
appointmentDate: Date
|
||||
physicianId: Number,
|
||||
patientId: Number,
|
||||
appointmentDate: Date
|
||||
});
|
||||
|
||||
Appointment.belongsTo(Patient);
|
||||
|
@ -63,33 +61,32 @@ Physician.hasMany(Patient, {through: Appointment});
|
|||
Patient.hasMany(Physician, {through: Appointment});
|
||||
|
||||
Physician.create({name: 'Smith'}, function (err, physician) {
|
||||
Patient.create({name: 'Mary'}, function (err, patient) {
|
||||
Appointment.create({appointmentDate: new Date(), physicianId: physician.id, patientId: patient.id},
|
||||
function (err, appt) {
|
||||
physician.patients(console.log);
|
||||
patient.physicians(console.log);
|
||||
});
|
||||
});
|
||||
Patient.create({name: 'Mary'}, function (err, patient) {
|
||||
Appointment.create({appointmentDate: new Date(), physicianId: physician.id, patientId: patient.id},
|
||||
function (err, appt) {
|
||||
physician.patients(console.log);
|
||||
patient.physicians(console.log);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
var Assembly = ds.createModel('Assembly', {
|
||||
name: String
|
||||
name: String
|
||||
});
|
||||
|
||||
var Part = ds.createModel('Part', {
|
||||
partNumber: String
|
||||
partNumber: String
|
||||
});
|
||||
|
||||
Assembly.hasAndBelongsToMany(Part);
|
||||
Part.hasAndBelongsToMany(Assembly);
|
||||
|
||||
Assembly.create({name: 'car'}, function (err, assembly) {
|
||||
Part.create({partNumber: 'engine'}, function (err, part) {
|
||||
assembly.parts.add(part, function(err) {
|
||||
assembly.parts(console.log);
|
||||
});
|
||||
|
||||
Part.create({partNumber: 'engine'}, function (err, part) {
|
||||
assembly.parts.add(part, function (err) {
|
||||
assembly.parts(console.log);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,83 +1,83 @@
|
|||
[
|
||||
{
|
||||
"name": "Address",
|
||||
"properties": {
|
||||
"label": "string",
|
||||
"street": "string",
|
||||
"city": "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": "Address",
|
||||
"properties": {
|
||||
"label": "string",
|
||||
"street": "string",
|
||||
"city": "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"
|
||||
}
|
||||
}
|
||||
|
||||
]
|
|
@ -6,9 +6,9 @@ module.exports = Connector;
|
|||
* @constructor
|
||||
*/
|
||||
function Connector(name, settings) {
|
||||
this._models = {};
|
||||
this.name = name;
|
||||
this.settings = settings || {};
|
||||
this._models = {};
|
||||
this.name = name;
|
||||
this.settings = settings || {};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,21 +24,20 @@ Connector.prototype.relational = false;
|
|||
* @param {Function} [callback] The callback function
|
||||
*/
|
||||
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
|
||||
* @param {String} model The model name
|
||||
* @returns {DataSource} The data source
|
||||
*/
|
||||
Connector.prototype.getDataSource = function(model) {
|
||||
var m = this._models[model];
|
||||
if(!m) {
|
||||
console.trace('Model not found: ' + model);
|
||||
}
|
||||
return m && m.model.dataSource;
|
||||
Connector.prototype.getDataSource = function (model) {
|
||||
var m = this._models[model];
|
||||
if (!m) {
|
||||
console.trace('Model not found: ' + model);
|
||||
}
|
||||
return m && m.model.dataSource;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -47,7 +46,7 @@ Connector.prototype.getDataSource = function(model) {
|
|||
* @returns {String} The id property name
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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)
|
||||
* @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
|
||||
*/
|
||||
Connector.prototype.id = function (model, prop) {
|
||||
var p = this._models[model].properties[prop];
|
||||
if(!p) {
|
||||
console.trace('Property not found: ' + model +'.' + prop);
|
||||
}
|
||||
return p.id;
|
||||
var p = this._models[model].properties[prop];
|
||||
if (!p) {
|
||||
console.trace('Property not found: ' + model + '.' + prop);
|
||||
}
|
||||
return p.id;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -79,10 +77,10 @@ Connector.prototype.id = function (model, prop) {
|
|||
* @param {Object} modelDefinition The model definition
|
||||
*/
|
||||
Connector.prototype.define = function (modelDefinition) {
|
||||
if (!modelDefinition.settings) {
|
||||
modelDefinition.settings = {};
|
||||
}
|
||||
this._models[modelDefinition.model.modelName] = modelDefinition;
|
||||
if (!modelDefinition.settings) {
|
||||
modelDefinition.settings = {};
|
||||
}
|
||||
this._models[modelDefinition.model.modelName] = modelDefinition;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -92,14 +90,14 @@ Connector.prototype.define = function (modelDefinition) {
|
|||
* @param {Object} propertyDefinition The object for property metadata
|
||||
*/
|
||||
Connector.prototype.defineProperty = function (model, propertyName, propertyDefinition) {
|
||||
this._models[model].properties[propertyName] = propertyDefinition;
|
||||
this._models[model].properties[propertyName] = propertyDefinition;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disconnect from the connector
|
||||
*/
|
||||
Connector.prototype.disconnect = function disconnect(cb) {
|
||||
// NO-OP
|
||||
// NO-OP
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -109,8 +107,8 @@ Connector.prototype.disconnect = function disconnect(cb) {
|
|||
* @returns {*} The id value
|
||||
*
|
||||
*/
|
||||
Connector.prototype.getIdValue = function(model, data) {
|
||||
return data && data[this.idName(model)];
|
||||
Connector.prototype.getIdValue = function (model, data) {
|
||||
return data && data[this.idName(model)];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -120,10 +118,14 @@ Connector.prototype.getIdValue = function(model, data) {
|
|||
* @param {*} value The id value
|
||||
*
|
||||
*/
|
||||
Connector.prototype.setIdValue = function(model, data, value) {
|
||||
if(data) {
|
||||
data[this.idName(model)] = value;
|
||||
}
|
||||
Connector.prototype.setIdValue = function (model, data, value) {
|
||||
if (data) {
|
||||
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
|
||||
*/
|
||||
function CradleAdapter(client) {
|
||||
this._models = {};
|
||||
this.client = client;
|
||||
this._models = {};
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
function createdbif(client, callback) {
|
||||
client.exists(function (err, exists) {
|
||||
if(err) callback(err);
|
||||
if (!exists) { client.create(function() { callback(); }); }
|
||||
else { callback(); }
|
||||
});
|
||||
client.exists(function (err, exists) {
|
||||
if (err) callback(err);
|
||||
if (!exists) {
|
||||
client.create(function () {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function naturalize(data, model) {
|
||||
data.nature = model;
|
||||
//TODO: maybe this is not a really good idea
|
||||
if(data.date) data.date = data.date.toString();
|
||||
return data;
|
||||
data.nature = model;
|
||||
//TODO: maybe this is not a really good idea
|
||||
if (data.date) data.date = data.date.toString();
|
||||
return data;
|
||||
}
|
||||
function idealize(data) {
|
||||
data.id = data._id;
|
||||
return data;
|
||||
data.id = data._id;
|
||||
return data;
|
||||
}
|
||||
function stringify(data) {
|
||||
return data ? data.toString() : data
|
||||
return data ? data.toString() : data
|
||||
}
|
||||
|
||||
function errorHandler(callback, func) {
|
||||
return function(err, res) {
|
||||
if (err) {
|
||||
console.log('cradle', err);
|
||||
callback(err);
|
||||
} else {
|
||||
if(func) {
|
||||
func(res, function(res) {
|
||||
callback(null, res);
|
||||
});
|
||||
} else {
|
||||
callback(null, res);
|
||||
}
|
||||
}
|
||||
return function (err, res) {
|
||||
if (err) {
|
||||
console.log('cradle', err);
|
||||
callback(err);
|
||||
} else {
|
||||
if (func) {
|
||||
func(res, function (res) {
|
||||
callback(null, res);
|
||||
});
|
||||
} else {
|
||||
callback(null, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function synchronize(functions, args, callback) {
|
||||
if(functions.length === 0) callback();
|
||||
if(functions.length > 0 && args.length === functions.length) {
|
||||
functions[0](args[0][0], args[0][1], function(err, res) {
|
||||
if(err) callback(err);
|
||||
functions.splice(0, 1);
|
||||
args.splice(0, 1);
|
||||
synchronize(functions, args, callback);
|
||||
});
|
||||
}
|
||||
if (functions.length === 0) callback();
|
||||
if (functions.length > 0 && args.length === functions.length) {
|
||||
functions[0](args[0][0], args[0][1], function (err, res) {
|
||||
if (err) callback(err);
|
||||
functions.splice(0, 1);
|
||||
args.splice(0, 1);
|
||||
synchronize(functions, args, callback);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function applyFilter(filter) {
|
||||
if (typeof filter.where === 'function') {
|
||||
return filter.where;
|
||||
}
|
||||
var keys = Object.keys(filter.where);
|
||||
return function (obj) {
|
||||
var pass = true;
|
||||
keys.forEach(function (key) {
|
||||
if (!test(filter.where[key], obj[key])) {
|
||||
pass = false;
|
||||
}
|
||||
});
|
||||
return pass;
|
||||
}
|
||||
if (typeof filter.where === 'function') {
|
||||
return filter.where;
|
||||
}
|
||||
var keys = Object.keys(filter.where);
|
||||
return function (obj) {
|
||||
var pass = true;
|
||||
keys.forEach(function (key) {
|
||||
if (!test(filter.where[key], obj[key])) {
|
||||
pass = false;
|
||||
}
|
||||
});
|
||||
return pass;
|
||||
}
|
||||
|
||||
function test(example, value) {
|
||||
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
||||
return value.match(example);
|
||||
}
|
||||
// not strict equality
|
||||
return example == value;
|
||||
function test(example, value) {
|
||||
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
||||
return value.match(example);
|
||||
}
|
||||
// not strict equality
|
||||
return example == value;
|
||||
}
|
||||
}
|
||||
|
||||
function numerically(a, b) {
|
||||
return a[this[0]] - b[this[0]];
|
||||
return a[this[0]] - b[this[0]];
|
||||
}
|
||||
|
||||
function literally(a, b) {
|
||||
return a[this[0]] > b[this[0]];
|
||||
return a[this[0]] > b[this[0]];
|
||||
}
|
||||
|
||||
function filtering(res, model, filter, instance) {
|
||||
|
||||
if(model) {
|
||||
if(filter == null) filter = {};
|
||||
if(filter.where == null) filter.where = {};
|
||||
filter.where.nature = model;
|
||||
}
|
||||
// do we need some filtration?
|
||||
if (filter.where) {
|
||||
res = res ? res.filter(applyFilter(filter)) : res;
|
||||
}
|
||||
if (model) {
|
||||
if (filter == null) filter = {};
|
||||
if (filter.where == null) filter.where = {};
|
||||
filter.where.nature = model;
|
||||
}
|
||||
// do we need some filtration?
|
||||
if (filter.where) {
|
||||
res = res ? res.filter(applyFilter(filter)) : res;
|
||||
}
|
||||
|
||||
// do we need some sorting?
|
||||
if (filter.order) {
|
||||
var props = instance[model].properties;
|
||||
var allNumeric = true;
|
||||
var orders = filter.order;
|
||||
var reverse = false;
|
||||
if (typeof filter.order === "string") {
|
||||
orders = [filter.order];
|
||||
}
|
||||
// do we need some sorting?
|
||||
if (filter.order) {
|
||||
var props = instance[model].properties;
|
||||
var allNumeric = true;
|
||||
var orders = filter.order;
|
||||
var reverse = false;
|
||||
if (typeof filter.order === "string") {
|
||||
orders = [filter.order];
|
||||
}
|
||||
|
||||
orders.forEach(function (key, i) {
|
||||
var m = key.match(/\s+(A|DE)SC$/i);
|
||||
if (m) {
|
||||
key = key.replace(/\s+(A|DE)SC/i, '');
|
||||
if (m[1] === 'DE') reverse = true;
|
||||
}
|
||||
orders[i] = key;
|
||||
if (props[key].type.name !== 'Number') {
|
||||
allNumeric = false;
|
||||
}
|
||||
});
|
||||
if (allNumeric) {
|
||||
res = res.sort(numerically.bind(orders));
|
||||
} else {
|
||||
res = res.sort(literally.bind(orders));
|
||||
orders.forEach(function (key, i) {
|
||||
var m = key.match(/\s+(A|DE)SC$/i);
|
||||
if (m) {
|
||||
key = key.replace(/\s+(A|DE)SC/i, '');
|
||||
if (m[1] === 'DE') reverse = true;
|
||||
}
|
||||
if (reverse) res = res.reverse();
|
||||
}
|
||||
return res;
|
||||
orders[i] = key;
|
||||
if (props[key].type.name !== 'Number') {
|
||||
allNumeric = false;
|
||||
}
|
||||
});
|
||||
if (allNumeric) {
|
||||
res = res.sort(numerically.bind(orders));
|
||||
} else {
|
||||
res = res.sort(literally.bind(orders));
|
||||
}
|
||||
if (reverse) res = res.reverse();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection/Disconnection
|
||||
*/
|
||||
exports.initialize = function(dataSource, callback) {
|
||||
if (!cradle) return;
|
||||
exports.initialize = function (dataSource, callback) {
|
||||
if (!cradle) return;
|
||||
|
||||
// when using cradle if we dont wait for the dataSource to be connected, the models fails to load correctly.
|
||||
dataSource.waitForConnect = true;
|
||||
if (!dataSource.settings.url) {
|
||||
var host = dataSource.settings.host || 'localhost';
|
||||
var port = dataSource.settings.port || '5984';
|
||||
var options = dataSource.settings.options || {
|
||||
cache: true,
|
||||
raw: false
|
||||
};
|
||||
if (dataSource.settings.username) {
|
||||
options.auth = {};
|
||||
options.auth.username = dataSource.settings.username;
|
||||
if (dataSource.settings.password) {
|
||||
options.auth.password = dataSource.settings.password;
|
||||
}
|
||||
}
|
||||
var database = dataSource.settings.database || 'loopback-datasource-juggler';
|
||||
|
||||
dataSource.settings.host = host;
|
||||
dataSource.settings.port = port;
|
||||
dataSource.settings.database = database;
|
||||
dataSource.settings.options = options;
|
||||
// when using cradle if we dont wait for the dataSource to be connected, the models fails to load correctly.
|
||||
dataSource.waitForConnect = true;
|
||||
if (!dataSource.settings.url) {
|
||||
var host = dataSource.settings.host || 'localhost';
|
||||
var port = dataSource.settings.port || '5984';
|
||||
var options = dataSource.settings.options || {
|
||||
cache: true,
|
||||
raw: false
|
||||
};
|
||||
if (dataSource.settings.username) {
|
||||
options.auth = {};
|
||||
options.auth.username = dataSource.settings.username;
|
||||
if (dataSource.settings.password) {
|
||||
options.auth.password = dataSource.settings.password;
|
||||
}
|
||||
}
|
||||
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.client,
|
||||
errorHandler(callback, function() {
|
||||
dataSource.connector = new CradleAdapter(dataSource.client);
|
||||
process.nextTick(callback);
|
||||
}));
|
||||
dataSource.settings.host = host;
|
||||
dataSource.settings.port = port;
|
||||
dataSource.settings.database = database;
|
||||
dataSource.settings.options = options;
|
||||
}
|
||||
dataSource.client = new (cradle.Connection)(dataSource.settings.host, dataSource.settings.port, dataSource.settings.options).database(dataSource.settings.database);
|
||||
|
||||
createdbif(
|
||||
dataSource.client,
|
||||
errorHandler(callback, function () {
|
||||
dataSource.connector = new CradleAdapter(dataSource.client);
|
||||
process.nextTick(callback);
|
||||
}));
|
||||
};
|
||||
|
||||
CradleAdapter.prototype.disconnect = function() {
|
||||
CradleAdapter.prototype.disconnect = function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* Write methods
|
||||
*/
|
||||
CradleAdapter.prototype.define = function(descr) {
|
||||
this._models[descr.model.modelName] = descr;
|
||||
CradleAdapter.prototype.define = function (descr) {
|
||||
this._models[descr.model.modelName] = descr;
|
||||
};
|
||||
|
||||
CradleAdapter.prototype.create = function(model, data, callback) {
|
||||
this.client.save(
|
||||
stringify(data.id),
|
||||
naturalize(data, model),
|
||||
errorHandler(callback, function(res, cb) {
|
||||
cb(res.id);
|
||||
})
|
||||
);
|
||||
CradleAdapter.prototype.create = function (model, data, callback) {
|
||||
this.client.save(
|
||||
stringify(data.id),
|
||||
naturalize(data, model),
|
||||
errorHandler(callback, function (res, cb) {
|
||||
cb(res.id);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
CradleAdapter.prototype.save = function(model, data, callback) {
|
||||
this.client.save(
|
||||
stringify(data.id),
|
||||
naturalize(data, model),
|
||||
errorHandler(callback)
|
||||
)
|
||||
CradleAdapter.prototype.save = function (model, data, callback) {
|
||||
this.client.save(
|
||||
stringify(data.id),
|
||||
naturalize(data, model),
|
||||
errorHandler(callback)
|
||||
)
|
||||
};
|
||||
|
||||
CradleAdapter.prototype.updateAttributes = function(model, id, data, callback) {
|
||||
this.client.merge(
|
||||
stringify(id),
|
||||
data,
|
||||
errorHandler(callback, function(doc, cb) {
|
||||
cb(idealize(doc));
|
||||
})
|
||||
);
|
||||
CradleAdapter.prototype.updateAttributes = function (model, id, data, callback) {
|
||||
this.client.merge(
|
||||
stringify(id),
|
||||
data,
|
||||
errorHandler(callback, function (doc, cb) {
|
||||
cb(idealize(doc));
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
CradleAdapter.prototype.updateOrCreate = function(model, data, callback) {
|
||||
this.client.get(
|
||||
stringify(data.id),
|
||||
function (err, doc) {
|
||||
if(err) {
|
||||
this.create(model, data, callback);
|
||||
} else {
|
||||
this.updateAttributes(model, data.id, data, callback);
|
||||
}
|
||||
}.bind(this)
|
||||
)
|
||||
CradleAdapter.prototype.updateOrCreate = function (model, data, callback) {
|
||||
this.client.get(
|
||||
stringify(data.id),
|
||||
function (err, doc) {
|
||||
if (err) {
|
||||
this.create(model, data, callback);
|
||||
} else {
|
||||
this.updateAttributes(model, data.id, data, callback);
|
||||
}
|
||||
}.bind(this)
|
||||
)
|
||||
};
|
||||
|
||||
/**
|
||||
* Read methods
|
||||
*/
|
||||
CradleAdapter.prototype.exists = function(model, id, callback) {
|
||||
this.client.get(
|
||||
stringify(id),
|
||||
errorHandler(callback, function(doc, cb) {
|
||||
cb(!!doc);
|
||||
})
|
||||
);
|
||||
CradleAdapter.prototype.exists = function (model, id, callback) {
|
||||
this.client.get(
|
||||
stringify(id),
|
||||
errorHandler(callback, function (doc, cb) {
|
||||
cb(!!doc);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
CradleAdapter.prototype.find = function(model, id, callback) {
|
||||
this.client.get(
|
||||
stringify(id),
|
||||
errorHandler(callback, function(doc, cb) {
|
||||
cb(idealize(doc));
|
||||
})
|
||||
);
|
||||
CradleAdapter.prototype.find = function (model, id, callback) {
|
||||
this.client.get(
|
||||
stringify(id),
|
||||
errorHandler(callback, function (doc, cb) {
|
||||
cb(idealize(doc));
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
CradleAdapter.prototype.count = function(model, callback, where) {
|
||||
this.models(
|
||||
model,
|
||||
{where: where},
|
||||
callback,
|
||||
function(docs, cb) {
|
||||
cb(docs.length);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
CradleAdapter.prototype.models = function(model, filter, callback, func) {
|
||||
var limit = 200;
|
||||
var skip = 0;
|
||||
if (filter != null) {
|
||||
limit = filter.limit || limit;
|
||||
skip = filter.skip ||skip;
|
||||
CradleAdapter.prototype.count = function (model, callback, where) {
|
||||
this.models(
|
||||
model,
|
||||
{where: where},
|
||||
callback,
|
||||
function (docs, cb) {
|
||||
cb(docs.length);
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
CradleAdapter.prototype.models = function (model, filter, callback, func) {
|
||||
var limit = 200;
|
||||
var skip = 0;
|
||||
if (filter != null) {
|
||||
limit = filter.limit || limit;
|
||||
skip = filter.skip || skip;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
self.client.save('_design/' + model, {
|
||||
views: {
|
||||
all: {
|
||||
map: 'function(doc) { if (doc.nature == "' + model + '") { emit(doc._id, doc); } }'
|
||||
}
|
||||
}
|
||||
}, function () {
|
||||
self.client.view(model + '/all', {include_docs: true, limit: limit, skip: skip},
|
||||
errorHandler(callback, function (res, cb) {
|
||||
var docs = res.map(function (doc) {
|
||||
return idealize(doc);
|
||||
});
|
||||
var filtered = filtering(docs, model, filter, this._models)
|
||||
|
||||
func ? func(filtered, cb) : cb(filtered);
|
||||
}.bind(self)));
|
||||
});
|
||||
};
|
||||
|
||||
CradleAdapter.prototype.all = function (model, filter, callback) {
|
||||
this.models(
|
||||
model,
|
||||
filter,
|
||||
callback
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detroy methods
|
||||
*/
|
||||
CradleAdapter.prototype.destroy = function(model, id, callback) {
|
||||
this.client.remove(
|
||||
stringify(id),
|
||||
function (err, doc) {
|
||||
callback(err);
|
||||
}
|
||||
);
|
||||
CradleAdapter.prototype.destroy = function (model, id, callback) {
|
||||
this.client.remove(
|
||||
stringify(id),
|
||||
function (err, doc) {
|
||||
callback(err);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
CradleAdapter.prototype.destroyAll = function(model, callback) {
|
||||
this.models(
|
||||
model,
|
||||
null,
|
||||
callback,
|
||||
function(docs, cb) {
|
||||
var docIds = docs.map(function(doc) {
|
||||
return doc.id;
|
||||
});
|
||||
this.client.get(docIds, function(err, res) {
|
||||
if(err) cb(err);
|
||||
CradleAdapter.prototype.destroyAll = function (model, callback) {
|
||||
this.models(
|
||||
model,
|
||||
null,
|
||||
callback,
|
||||
function (docs, cb) {
|
||||
var docIds = docs.map(function (doc) {
|
||||
return doc.id;
|
||||
});
|
||||
this.client.get(docIds, function (err, res) {
|
||||
if (err) cb(err);
|
||||
|
||||
var funcs = res.map(function(doc) {
|
||||
return this.client.remove.bind(this.client);
|
||||
}.bind(this));
|
||||
var funcs = res.map(function (doc) {
|
||||
return this.client.remove.bind(this.client);
|
||||
}.bind(this));
|
||||
|
||||
var args = res.map(function(doc) {
|
||||
return [doc._id, doc._rev];
|
||||
});
|
||||
var args = res.map(function (doc) {
|
||||
return [doc._id, doc._rev];
|
||||
});
|
||||
|
||||
synchronize(funcs, args, cb);
|
||||
}.bind(this));
|
||||
}.bind(this)
|
||||
);
|
||||
synchronize(funcs, args, cb);
|
||||
}.bind(this));
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,188 +1,190 @@
|
|||
exports.initialize = function initializeSchema(dataSource, callback) {
|
||||
dataSource.connector = new WebService();
|
||||
process.nextTick(callback);
|
||||
dataSource.connector = new WebService();
|
||||
process.nextTick(callback);
|
||||
};
|
||||
|
||||
function WebService() {
|
||||
this._models = {};
|
||||
this.cache = {};
|
||||
this.ids = {};
|
||||
this._models = {};
|
||||
this.cache = {};
|
||||
this.ids = {};
|
||||
}
|
||||
|
||||
WebService.prototype.installPostProcessor = function installPostProcessor(descr) {
|
||||
var dates = [];
|
||||
Object.keys(descr.properties).forEach(function(column) {
|
||||
if (descr.properties[column].type.name === 'Date') {
|
||||
dates.push(column);
|
||||
}
|
||||
});
|
||||
var dates = [];
|
||||
Object.keys(descr.properties).forEach(function (column) {
|
||||
if (descr.properties[column].type.name === 'Date') {
|
||||
dates.push(column);
|
||||
}
|
||||
});
|
||||
|
||||
var postProcessor = function(model) {
|
||||
var max = dates.length;
|
||||
for (var i = 0; i < max; i++) {
|
||||
var column = dates[i];
|
||||
if (model[column]) {
|
||||
model[column] = new Date(model[column]);
|
||||
}
|
||||
};
|
||||
};
|
||||
var postProcessor = function (model) {
|
||||
var max = dates.length;
|
||||
for (var i = 0; i < max; i++) {
|
||||
var column = dates[i];
|
||||
if (model[column]) {
|
||||
model[column] = new Date(model[column]);
|
||||
}
|
||||
}
|
||||
;
|
||||
};
|
||||
|
||||
descr.postProcessor = postProcessor;
|
||||
descr.postProcessor = postProcessor;
|
||||
};
|
||||
|
||||
WebService.prototype.preProcess = function preProcess(data) {
|
||||
var result = {};
|
||||
Object.keys(data).forEach(function(key) {
|
||||
if (data[key] != null) {
|
||||
result[key] = data[key];
|
||||
}
|
||||
})
|
||||
return result;
|
||||
var result = {};
|
||||
Object.keys(data).forEach(function (key) {
|
||||
if (data[key] != null) {
|
||||
result[key] = data[key];
|
||||
}
|
||||
})
|
||||
return result;
|
||||
};
|
||||
|
||||
WebService.prototype.postProcess = function postProcess(model, data) {
|
||||
var postProcessor = this._models[model].postProcessor;
|
||||
if (postProcessor && data) {
|
||||
postProcessor(data);
|
||||
}
|
||||
var postProcessor = this._models[model].postProcessor;
|
||||
if (postProcessor && data) {
|
||||
postProcessor(data);
|
||||
}
|
||||
};
|
||||
|
||||
WebService.prototype.postProcessMultiple = function postProcessMultiple(model, data) {
|
||||
var postProcessor = this._models[model].postProcessor;
|
||||
if (postProcessor) {
|
||||
var max = data.length;
|
||||
for (var i = 0; i < max; i++) {
|
||||
if (data[i]) {
|
||||
postProcessor(data[i]);
|
||||
}
|
||||
};
|
||||
var postProcessor = this._models[model].postProcessor;
|
||||
if (postProcessor) {
|
||||
var max = data.length;
|
||||
for (var i = 0; i < max; i++) {
|
||||
if (data[i]) {
|
||||
postProcessor(data[i]);
|
||||
}
|
||||
}
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
WebService.prototype.define = function defineModel(descr) {
|
||||
var m = descr.model.modelName;
|
||||
this.installPostProcessor(descr);
|
||||
this._models[m] = descr;
|
||||
var m = descr.model.modelName;
|
||||
this.installPostProcessor(descr);
|
||||
this._models[m] = descr;
|
||||
};
|
||||
|
||||
WebService.prototype.getResourceUrl = function getResourceUrl(model) {
|
||||
var url = this._models[model].settings.restPath;
|
||||
if (!url) throw new Error('Resource url (restPath) for ' + model + ' is not defined');
|
||||
return url;
|
||||
var url = this._models[model].settings.restPath;
|
||||
if (!url) throw new Error('Resource url (restPath) for ' + model + ' is not defined');
|
||||
return url;
|
||||
};
|
||||
|
||||
WebService.prototype.getBlankReq = function () {
|
||||
if (!this.csrfToken) {
|
||||
this.csrfToken = $('meta[name=csrf-token]').attr('content');
|
||||
this.csrfParam = $('meta[name=csrf-param]').attr('content');
|
||||
}
|
||||
var req = {};
|
||||
req[this.csrfParam] = this.csrfToken;
|
||||
return req;
|
||||
if (!this.csrfToken) {
|
||||
this.csrfToken = $('meta[name=csrf-token]').attr('content');
|
||||
this.csrfParam = $('meta[name=csrf-param]').attr('content');
|
||||
}
|
||||
var req = {};
|
||||
req[this.csrfParam] = this.csrfToken;
|
||||
return req;
|
||||
}
|
||||
|
||||
WebService.prototype.create = function create(model, data, callback) {
|
||||
var req = this.getBlankReq();
|
||||
req[model] = this.preProcess(data);
|
||||
$.post(this.getResourceUrl(model) + '.json', req, function (res) {
|
||||
if (res.code === 200) {
|
||||
callback(null, res.data.id);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
}, 'json');
|
||||
// this.cache[model][id] = data;
|
||||
var req = this.getBlankReq();
|
||||
req[model] = this.preProcess(data);
|
||||
$.post(this.getResourceUrl(model) + '.json', req, function (res) {
|
||||
if (res.code === 200) {
|
||||
callback(null, res.data.id);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
}, 'json');
|
||||
// this.cache[model][id] = data;
|
||||
};
|
||||
|
||||
WebService.prototype.updateOrCreate = function (model, data, callback) {
|
||||
var mem = this;
|
||||
this.exists(model, data.id, function (err, exists) {
|
||||
if (exists) {
|
||||
mem.save(model, data, callback);
|
||||
} else {
|
||||
mem.create(model, data, function (err, id) {
|
||||
data.id = id;
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
});
|
||||
var mem = this;
|
||||
this.exists(model, data.id, function (err, exists) {
|
||||
if (exists) {
|
||||
mem.save(model, data, callback);
|
||||
} else {
|
||||
mem.create(model, data, function (err, id) {
|
||||
data.id = id;
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
WebService.prototype.save = function save(model, data, callback) {
|
||||
var _this = this;
|
||||
var req = this.getBlankReq();
|
||||
req._method = 'PUT';
|
||||
req[model] = this.preProcess(data);
|
||||
$.post(this.getResourceUrl(model) + '/' + data.id + '.json', req, function (res) {
|
||||
if (res.code === 200) {
|
||||
_this.postProcess(model, res.data);
|
||||
callback(null, res.data);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
}, 'json');
|
||||
var _this = this;
|
||||
var req = this.getBlankReq();
|
||||
req._method = 'PUT';
|
||||
req[model] = this.preProcess(data);
|
||||
$.post(this.getResourceUrl(model) + '/' + data.id + '.json', req, function (res) {
|
||||
if (res.code === 200) {
|
||||
_this.postProcess(model, res.data);
|
||||
callback(null, res.data);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
}, 'json');
|
||||
};
|
||||
|
||||
WebService.prototype.exists = function exists(model, id, callback) {
|
||||
$.getJSON(this.getResourceUrl(model) + '/' + id + '.json', function (res) {
|
||||
if (res.code === 200) {
|
||||
callback(null, true);
|
||||
} else if (res.code === 404) {
|
||||
callback(null, false);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
});
|
||||
$.getJSON(this.getResourceUrl(model) + '/' + id + '.json', function (res) {
|
||||
if (res.code === 200) {
|
||||
callback(null, true);
|
||||
} else if (res.code === 404) {
|
||||
callback(null, false);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
WebService.prototype.find = function find(model, id, callback) {
|
||||
var _this = this;
|
||||
$.getJSON(this.getResourceUrl(model) + '/' + id + '.json', function (res) {
|
||||
if (res.code === 200) {
|
||||
_this.postProcess(model, res.data);
|
||||
callback(null, res.data);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
});
|
||||
var _this = this;
|
||||
$.getJSON(this.getResourceUrl(model) + '/' + id + '.json', function (res) {
|
||||
if (res.code === 200) {
|
||||
_this.postProcess(model, res.data);
|
||||
callback(null, res.data);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
WebService.prototype.destroy = function destroy(model, id, callback) {
|
||||
var _this = this;
|
||||
var req = this.getBlankReq();
|
||||
req._method = 'DELETE';
|
||||
$.post(this.getResourceUrl(model) + '/' + id + '.json', req, function (res) {
|
||||
if (res.code === 200) {
|
||||
//delete _this.cache[model][id];
|
||||
callback(null, res.data);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
}, 'json');
|
||||
var _this = this;
|
||||
var req = this.getBlankReq();
|
||||
req._method = 'DELETE';
|
||||
$.post(this.getResourceUrl(model) + '/' + id + '.json', req, function (res) {
|
||||
if (res.code === 200) {
|
||||
//delete _this.cache[model][id];
|
||||
callback(null, res.data);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
}, 'json');
|
||||
};
|
||||
|
||||
WebService.prototype.all = function all(model, filter, callback) {
|
||||
var _this = this;
|
||||
$.getJSON(this.getResourceUrl(model) + '.json?query=' + encodeURIComponent(JSON.stringify(filter)), function (res) {
|
||||
if (res.code === 200) {
|
||||
_this.postProcessMultiple(model, res.data);
|
||||
callback(null, res.data);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
});
|
||||
var _this = this;
|
||||
$.getJSON(this.getResourceUrl(model) + '.json?query=' + encodeURIComponent(JSON.stringify(filter)), function (res) {
|
||||
if (res.code === 200) {
|
||||
_this.postProcessMultiple(model, res.data);
|
||||
callback(null, res.data);
|
||||
} else {
|
||||
callback(res.error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
WebService.prototype.destroyAll = function destroyAll(model, callback) {
|
||||
throw new Error('Not supported');
|
||||
throw new Error('Not supported');
|
||||
};
|
||||
|
||||
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) {
|
||||
data.id = id;
|
||||
this.save(model, data, callback);
|
||||
data.id = id;
|
||||
this.save(model, data, callback);
|
||||
};
|
||||
|
||||
|
|
|
@ -10,317 +10,317 @@ var utils = require('../utils');
|
|||
* @param {Function} [callback] The callback function
|
||||
*/
|
||||
exports.initialize = function initializeDataSource(dataSource, callback) {
|
||||
dataSource.connector = new Memory();
|
||||
dataSource.connector.connect(callback);
|
||||
dataSource.connector = new Memory();
|
||||
dataSource.connector.connect(callback);
|
||||
};
|
||||
|
||||
exports.Memory = Memory;
|
||||
|
||||
function Memory(m) {
|
||||
if (m) {
|
||||
this.isTransaction = true;
|
||||
this.cache = m.cache;
|
||||
this.ids = m.ids;
|
||||
this.constructor.super_.call(this, 'memory');
|
||||
this._models = m._models;
|
||||
} else {
|
||||
this.isTransaction = false;
|
||||
this.cache = {};
|
||||
this.ids = {};
|
||||
this.constructor.super_.call(this, 'memory');
|
||||
}
|
||||
if (m) {
|
||||
this.isTransaction = true;
|
||||
this.cache = m.cache;
|
||||
this.ids = m.ids;
|
||||
this.constructor.super_.call(this, 'memory');
|
||||
this._models = m._models;
|
||||
} else {
|
||||
this.isTransaction = false;
|
||||
this.cache = {};
|
||||
this.ids = {};
|
||||
this.constructor.super_.call(this, 'memory');
|
||||
}
|
||||
}
|
||||
|
||||
util.inherits(Memory, Connector);
|
||||
|
||||
Memory.prototype.connect = function(callback) {
|
||||
if (this.isTransaction) {
|
||||
this.onTransactionExec = callback;
|
||||
} else {
|
||||
process.nextTick(callback);
|
||||
}
|
||||
Memory.prototype.connect = function (callback) {
|
||||
if (this.isTransaction) {
|
||||
this.onTransactionExec = callback;
|
||||
} else {
|
||||
process.nextTick(callback);
|
||||
}
|
||||
};
|
||||
|
||||
Memory.prototype.define = function defineModel(definition) {
|
||||
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
|
||||
var m = definition.model.modelName;
|
||||
this.cache[m] = {};
|
||||
this.ids[m] = 1;
|
||||
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
|
||||
var m = definition.model.modelName;
|
||||
this.cache[m] = {};
|
||||
this.ids[m] = 1;
|
||||
};
|
||||
|
||||
Memory.prototype.create = function create(model, data, callback) {
|
||||
// FIXME: [rfeng] We need to generate unique ids based on the id type
|
||||
// FIXME: [rfeng] We don't support composite ids yet
|
||||
var currentId = this.ids[model];
|
||||
if(currentId === undefined) {
|
||||
// First time
|
||||
this.ids[model] = 1;
|
||||
currentId = 1;
|
||||
}
|
||||
var id = this.getIdValue(model, data) || currentId;
|
||||
if(id > currentId) {
|
||||
// If the id is passed in and the value is greater than the current id
|
||||
currentId = id;
|
||||
}
|
||||
this.ids[model] = Number(currentId) + 1;
|
||||
// FIXME: [rfeng] We need to generate unique ids based on the id type
|
||||
// FIXME: [rfeng] We don't support composite ids yet
|
||||
var currentId = this.ids[model];
|
||||
if (currentId === undefined) {
|
||||
// First time
|
||||
this.ids[model] = 1;
|
||||
currentId = 1;
|
||||
}
|
||||
var id = this.getIdValue(model, data) || currentId;
|
||||
if (id > currentId) {
|
||||
// If the id is passed in and the value is greater than the current id
|
||||
currentId = id;
|
||||
}
|
||||
this.ids[model] = Number(currentId) + 1;
|
||||
|
||||
var props = this._models[model].properties;
|
||||
var idName = this.idName(model);
|
||||
id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
|
||||
this.setIdValue(model, data, id);
|
||||
this.cache[model][id] = JSON.stringify(data);
|
||||
process.nextTick(function() {
|
||||
callback(null, id);
|
||||
});
|
||||
var props = this._models[model].properties;
|
||||
var idName = this.idName(model);
|
||||
id = (props[idName] && props[idName].type && props[idName].type(id)) || id;
|
||||
this.setIdValue(model, data, id);
|
||||
this.cache[model][id] = JSON.stringify(data);
|
||||
process.nextTick(function () {
|
||||
callback(null, id);
|
||||
});
|
||||
};
|
||||
|
||||
Memory.prototype.updateOrCreate = function (model, data, callback) {
|
||||
var self = this;
|
||||
this.exists(model, self.getIdValue(model, data), function (err, exists) {
|
||||
if (exists) {
|
||||
self.save(model, data, callback);
|
||||
} else {
|
||||
self.create(model, data, function (err, id) {
|
||||
self.setIdValue(model, data, id);
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
});
|
||||
var self = this;
|
||||
this.exists(model, self.getIdValue(model, data), function (err, exists) {
|
||||
if (exists) {
|
||||
self.save(model, data, callback);
|
||||
} else {
|
||||
self.create(model, data, function (err, id) {
|
||||
self.setIdValue(model, data, id);
|
||||
callback(err, data);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Memory.prototype.save = function save(model, data, callback) {
|
||||
this.cache[model][this.getIdValue(model, data)] = JSON.stringify(data);
|
||||
process.nextTick(function () {
|
||||
callback(null, data);
|
||||
});
|
||||
this.cache[model][this.getIdValue(model, data)] = JSON.stringify(data);
|
||||
process.nextTick(function () {
|
||||
callback(null, data);
|
||||
});
|
||||
};
|
||||
|
||||
Memory.prototype.exists = function exists(model, id, callback) {
|
||||
process.nextTick(function () {
|
||||
callback(null, this.cache[model] && this.cache[model].hasOwnProperty(id));
|
||||
}.bind(this));
|
||||
process.nextTick(function () {
|
||||
callback(null, this.cache[model] && this.cache[model].hasOwnProperty(id));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Memory.prototype.find = function find(model, id, callback) {
|
||||
var self = this;
|
||||
process.nextTick(function () {
|
||||
callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
|
||||
}.bind(this));
|
||||
var self = this;
|
||||
process.nextTick(function () {
|
||||
callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Memory.prototype.destroy = function destroy(model, id, callback) {
|
||||
delete this.cache[model][id];
|
||||
process.nextTick(callback);
|
||||
delete this.cache[model][id];
|
||||
process.nextTick(callback);
|
||||
};
|
||||
|
||||
Memory.prototype.fromDb = function(model, data) {
|
||||
if (!data) return null;
|
||||
data = JSON.parse(data);
|
||||
var props = this._models[model].properties;
|
||||
for(var key in data) {
|
||||
var val = data[key];
|
||||
if (val === undefined || val === null) {
|
||||
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;
|
||||
Memory.prototype.fromDb = function (model, data) {
|
||||
if (!data) return null;
|
||||
data = JSON.parse(data);
|
||||
var props = this._models[model].properties;
|
||||
for (var key in data) {
|
||||
var val = data[key];
|
||||
if (val === undefined || val === null) {
|
||||
continue;
|
||||
}
|
||||
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) {
|
||||
var self = this;
|
||||
var nodes = Object.keys(this.cache[model]).map(function (key) {
|
||||
return this.fromDb(model, this.cache[model][key]);
|
||||
}.bind(this));
|
||||
var self = this;
|
||||
var nodes = Object.keys(this.cache[model]).map(function (key) {
|
||||
return this.fromDb(model, this.cache[model][key]);
|
||||
}.bind(this));
|
||||
|
||||
if (filter) {
|
||||
// do we need some sorting?
|
||||
if (filter.order) {
|
||||
var orders = filter.order;
|
||||
if (typeof filter.order === "string") {
|
||||
orders = [filter.order];
|
||||
}
|
||||
orders.forEach(function (key, i) {
|
||||
var reverse = 1;
|
||||
var m = key.match(/\s+(A|DE)SC$/i);
|
||||
if (m) {
|
||||
key = key.replace(/\s+(A|DE)SC/i, '');
|
||||
if (m[1].toLowerCase() === 'de') reverse = -1;
|
||||
}
|
||||
orders[i] = {"key": key, "reverse": reverse};
|
||||
});
|
||||
nodes = nodes.sort(sorting.bind(orders));
|
||||
if (filter) {
|
||||
// do we need some sorting?
|
||||
if (filter.order) {
|
||||
var orders = filter.order;
|
||||
if (typeof filter.order === "string") {
|
||||
orders = [filter.order];
|
||||
}
|
||||
orders.forEach(function (key, i) {
|
||||
var reverse = 1;
|
||||
var m = key.match(/\s+(A|DE)SC$/i);
|
||||
if (m) {
|
||||
key = key.replace(/\s+(A|DE)SC/i, '');
|
||||
if (m[1].toLowerCase() === 'de') reverse = -1;
|
||||
}
|
||||
|
||||
var nearFilter = geo.nearFilter(filter.where);
|
||||
|
||||
// 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);
|
||||
orders[i] = {"key": key, "reverse": reverse};
|
||||
});
|
||||
nodes = nodes.sort(sorting.bind(orders));
|
||||
}
|
||||
|
||||
process.nextTick(function () {
|
||||
if (filter && filter.include) {
|
||||
self._models[model].model.include(nodes, filter.include, callback);
|
||||
} else {
|
||||
callback(null, nodes);
|
||||
}
|
||||
});
|
||||
var nearFilter = geo.nearFilter(filter.where);
|
||||
|
||||
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;
|
||||
// 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 () {
|
||||
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) {
|
||||
if (typeof filter.where === 'function') {
|
||||
return filter.where;
|
||||
}
|
||||
var keys = Object.keys(filter.where);
|
||||
return function (obj) {
|
||||
var pass = true;
|
||||
keys.forEach(function (key) {
|
||||
if (!test(filter.where[key], obj && obj[key])) {
|
||||
pass = false;
|
||||
}
|
||||
});
|
||||
return pass;
|
||||
}
|
||||
if (typeof filter.where === 'function') {
|
||||
return filter.where;
|
||||
}
|
||||
var keys = Object.keys(filter.where);
|
||||
return function (obj) {
|
||||
var pass = true;
|
||||
keys.forEach(function (key) {
|
||||
if (!test(filter.where[key], obj && obj[key])) {
|
||||
pass = false;
|
||||
}
|
||||
});
|
||||
return pass;
|
||||
}
|
||||
|
||||
function test(example, value) {
|
||||
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
||||
return value.match(example);
|
||||
}
|
||||
if (typeof 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;
|
||||
}
|
||||
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);
|
||||
function test(example, value) {
|
||||
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
||||
return value.match(example);
|
||||
}
|
||||
|
||||
function isNum(n) {
|
||||
return typeof n === 'number';
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
function isNum(n) {
|
||||
return typeof n === 'number';
|
||||
}
|
||||
}
|
||||
|
||||
Memory.prototype.destroyAll = function destroyAll(model, where, callback) {
|
||||
if(!callback && 'function' === typeof where) {
|
||||
callback = where;
|
||||
where = undefined;
|
||||
if (!callback && 'function' === typeof where) {
|
||||
callback = where;
|
||||
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];
|
||||
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];
|
||||
}
|
||||
}.bind(this));
|
||||
if(!where) {
|
||||
this.cache[model] = {};
|
||||
}
|
||||
process.nextTick(callback);
|
||||
}.bind(this));
|
||||
if (!where) {
|
||||
this.cache[model] = {};
|
||||
}
|
||||
process.nextTick(callback);
|
||||
};
|
||||
|
||||
Memory.prototype.count = function count(model, callback, where) {
|
||||
var cache = this.cache[model];
|
||||
var data = Object.keys(cache);
|
||||
if (where) {
|
||||
var filter = {where: where};
|
||||
data = data.map(function (id) {
|
||||
return this.fromDb(model, cache[id]);
|
||||
}.bind(this));
|
||||
data = data.filter(applyFilter(filter));
|
||||
}
|
||||
process.nextTick(function () {
|
||||
callback(null, data.length);
|
||||
});
|
||||
var cache = this.cache[model];
|
||||
var data = Object.keys(cache);
|
||||
if (where) {
|
||||
var filter = {where: where};
|
||||
data = data.map(function (id) {
|
||||
return this.fromDb(model, cache[id]);
|
||||
}.bind(this));
|
||||
data = data.filter(applyFilter(filter));
|
||||
}
|
||||
process.nextTick(function () {
|
||||
callback(null, data.length);
|
||||
});
|
||||
};
|
||||
|
||||
Memory.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
|
||||
if(!id) {
|
||||
var err = new Error('You must provide an id when updating attributes!');
|
||||
if(cb) {
|
||||
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);
|
||||
if (!id) {
|
||||
var err = new Error('You must provide an id when updating attributes!');
|
||||
if (cb) {
|
||||
return cb(err);
|
||||
} 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 () {
|
||||
return new Memory(this);
|
||||
return new Memory(this);
|
||||
};
|
||||
|
||||
Memory.prototype.exec = function(callback) {
|
||||
this.onTransactionExec();
|
||||
setTimeout(callback, 50);
|
||||
Memory.prototype.exec = function (callback) {
|
||||
this.onTransactionExec();
|
||||
setTimeout(callback, 50);
|
||||
};
|
||||
|
||||
Memory.prototype.buildNearFilter = function (filter) {
|
||||
|
@ -328,9 +328,9 @@ Memory.prototype.buildNearFilter = function (filter) {
|
|||
}
|
||||
|
||||
function merge(base, update) {
|
||||
if (!base) return update;
|
||||
Object.keys(update).forEach(function (key) {
|
||||
base[key] = update[key];
|
||||
});
|
||||
return base;
|
||||
if (!base) return update;
|
||||
Object.keys(update).forEach(function (key) {
|
||||
base[key] = update[key];
|
||||
});
|
||||
return base;
|
||||
}
|
|
@ -6,367 +6,368 @@ var safeRequire = require('../utils').safeRequire;
|
|||
var neo4j = safeRequire('neo4j');
|
||||
|
||||
exports.initialize = function initializeSchema(dataSource, callback) {
|
||||
dataSource.client = new neo4j.GraphDatabase(dataSource.settings.url);
|
||||
dataSource.connector = new Neo4j(dataSource.client);
|
||||
process.nextTick(callback);
|
||||
dataSource.client = new neo4j.GraphDatabase(dataSource.settings.url);
|
||||
dataSource.connector = new Neo4j(dataSource.client);
|
||||
process.nextTick(callback);
|
||||
};
|
||||
|
||||
function Neo4j(client) {
|
||||
this._models = {};
|
||||
this.client = client;
|
||||
this.cache = {};
|
||||
this._models = {};
|
||||
this.client = client;
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
Neo4j.prototype.define = function defineModel(descr) {
|
||||
this.mixClassMethods(descr.model, descr.properties);
|
||||
this.mixInstanceMethods(descr.model.prototype, descr.properties);
|
||||
this._models[descr.model.modelName] = descr;
|
||||
this.mixClassMethods(descr.model, descr.properties);
|
||||
this.mixInstanceMethods(descr.model.prototype, descr.properties);
|
||||
this._models[descr.model.modelName] = descr;
|
||||
};
|
||||
|
||||
Neo4j.prototype.createIndexHelper = function (cls, indexName) {
|
||||
var db = this.client;
|
||||
var method = 'findBy' + indexName[0].toUpperCase() + indexName.substr(1);
|
||||
cls[method] = function (value, cb) {
|
||||
db.getIndexedNode(cls.modelName, indexName, value, function (err, node) {
|
||||
if (err) return cb(err);
|
||||
if (node) {
|
||||
node.data.id = node.id;
|
||||
cb(null, new cls(node.data));
|
||||
} else {
|
||||
cb(null, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
var db = this.client;
|
||||
var method = 'findBy' + indexName[0].toUpperCase() + indexName.substr(1);
|
||||
cls[method] = function (value, cb) {
|
||||
db.getIndexedNode(cls.modelName, indexName, value, function (err, node) {
|
||||
if (err) return cb(err);
|
||||
if (node) {
|
||||
node.data.id = node.id;
|
||||
cb(null, new cls(node.data));
|
||||
} else {
|
||||
cb(null, null);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
Neo4j.prototype.mixClassMethods = function mixClassMethods(cls, properties) {
|
||||
var neo = this;
|
||||
var neo = this;
|
||||
|
||||
Object.keys(properties).forEach(function (name) {
|
||||
if (properties[name].index) {
|
||||
neo.createIndexHelper(cls, name);
|
||||
Object.keys(properties).forEach(function (name) {
|
||||
if (properties[name].index) {
|
||||
neo.createIndexHelper(cls, name);
|
||||
}
|
||||
});
|
||||
|
||||
cls.setupCypherQuery = function (name, queryStr, rowHandler) {
|
||||
cls[name] = function cypherQuery(params, cb) {
|
||||
if (typeof params === 'function') {
|
||||
cb = params;
|
||||
params = [];
|
||||
} else if (params.constructor.name !== 'Array') {
|
||||
params = [params];
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var q = queryStr.replace(/\?/g, function () {
|
||||
return params[i++];
|
||||
});
|
||||
|
||||
neo.client.query(function (err, result) {
|
||||
if (err) return cb(err, []);
|
||||
cb(null, result.map(rowHandler));
|
||||
}, q);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param from - id of object to check relation from
|
||||
* @param to - id of object to check relation to
|
||||
* @param type - type of relation
|
||||
* @param direction - all | incoming | outgoing
|
||||
* @param cb - callback (err, rel || false)
|
||||
*/
|
||||
cls.relationshipExists = function relationshipExists(from, to, type, direction, cb) {
|
||||
neo.node(from, function (err, node) {
|
||||
if (err) return cb(err);
|
||||
node._getRelationships(direction, type, function (err, rels) {
|
||||
if (err && cb) return cb(err);
|
||||
if (err && !cb) throw err;
|
||||
var found = false;
|
||||
if (rels && rels.forEach) {
|
||||
rels.forEach(function (r) {
|
||||
if (r.start.id === from && r.end.id === to) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
cb && cb(err, found);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
cls.setupCypherQuery = function (name, queryStr, rowHandler) {
|
||||
cls[name] = function cypherQuery(params, cb) {
|
||||
if (typeof params === 'function') {
|
||||
cb = params;
|
||||
params = [];
|
||||
} else if (params.constructor.name !== 'Array') {
|
||||
params = [params];
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var q = queryStr.replace(/\?/g, function () {
|
||||
return params[i++];
|
||||
});
|
||||
|
||||
neo.client.query(function (err, result) {
|
||||
if (err) return cb(err, []);
|
||||
cb(null, result.map(rowHandler));
|
||||
}, q);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param from - id of object to check relation from
|
||||
* @param to - id of object to check relation to
|
||||
* @param type - type of relation
|
||||
* @param direction - all | incoming | outgoing
|
||||
* @param cb - callback (err, rel || false)
|
||||
*/
|
||||
cls.relationshipExists = function relationshipExists(from, to, type, direction, cb) {
|
||||
neo.node(from, function (err, node) {
|
||||
if (err) return cb(err);
|
||||
node._getRelationships(direction, type, function (err, rels) {
|
||||
if (err && cb) return cb(err);
|
||||
if (err && !cb) throw err;
|
||||
var found = false;
|
||||
if (rels && rels.forEach) {
|
||||
rels.forEach(function (r) {
|
||||
if (r.start.id === from && r.end.id === to) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
cb && cb(err, found);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
cls.createRelationshipTo = function createRelationshipTo(id1, id2, type, data, cb) {
|
||||
var fromNode, toNode;
|
||||
neo.node(id1, function (err, node) {
|
||||
if (err && cb) return cb(err);
|
||||
if (err && !cb) throw err;
|
||||
fromNode = node;
|
||||
ok();
|
||||
});
|
||||
neo.node(id2, function (err, node) {
|
||||
if (err && cb) return cb(err);
|
||||
if (err && !cb) throw err;
|
||||
toNode = node;
|
||||
ok();
|
||||
});
|
||||
function ok() {
|
||||
if (fromNode && toNode) {
|
||||
fromNode.createRelationshipTo(toNode, type, cleanup(data), cb);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cls.createRelationshipFrom = function createRelationshipFrom(id1, id2, type, data, cb) {
|
||||
cls.createRelationshipTo(id2, id1, type, data, cb);
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// only create relationship if it is not exists
|
||||
cls.ensureRelationshipTo = function (id1, id2, type, data, cb) {
|
||||
cls.relationshipExists(id1, id2, type, 'outgoing', function (err, exists) {
|
||||
if (err && cb) return cb(err);
|
||||
if (err && !cb) throw err;
|
||||
if (exists) return cb && cb(null);
|
||||
cls.createRelationshipTo(id1, id2, type, data, cb);
|
||||
});
|
||||
}
|
||||
cls.createRelationshipFrom = function createRelationshipFrom(id1, id2, type, data, cb) {
|
||||
cls.createRelationshipTo(id2, id1, type, data, cb);
|
||||
}
|
||||
|
||||
// only create relationship if it is not exists
|
||||
cls.ensureRelationshipTo = function (id1, id2, type, data, cb) {
|
||||
cls.relationshipExists(id1, id2, type, 'outgoing', function (err, exists) {
|
||||
if (err && cb) return cb(err);
|
||||
if (err && !cb) throw err;
|
||||
if (exists) return cb && cb(null);
|
||||
cls.createRelationshipTo(id1, id2, type, data, cb);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Neo4j.prototype.mixInstanceMethods = function mixInstanceMethods(proto) {
|
||||
var neo = this;
|
||||
var neo = this;
|
||||
|
||||
/**
|
||||
* @param obj - Object or id of object to check relation with
|
||||
* @param type - type of relation
|
||||
* @param cb - callback (err, rel || false)
|
||||
*/
|
||||
proto.isInRelationWith = function isInRelationWith(obj, type, direction, cb) {
|
||||
this.constructor.relationshipExists(this.id, obj.id || obj, type, 'all', cb);
|
||||
};
|
||||
/**
|
||||
* @param obj - Object or id of object to check relation with
|
||||
* @param type - type of relation
|
||||
* @param cb - callback (err, rel || false)
|
||||
*/
|
||||
proto.isInRelationWith = function isInRelationWith(obj, type, direction, cb) {
|
||||
this.constructor.relationshipExists(this.id, obj.id || obj, type, 'all', cb);
|
||||
};
|
||||
};
|
||||
|
||||
Neo4j.prototype.node = function find(id, callback) {
|
||||
if (this.cache[id]) {
|
||||
callback(null, this.cache[id]);
|
||||
} else {
|
||||
this.client.getNodeById(id, function (err, node) {
|
||||
if (node) {
|
||||
this.cache[id] = node;
|
||||
}
|
||||
callback(err, node);
|
||||
}.bind(this));
|
||||
}
|
||||
if (this.cache[id]) {
|
||||
callback(null, this.cache[id]);
|
||||
} else {
|
||||
this.client.getNodeById(id, function (err, node) {
|
||||
if (node) {
|
||||
this.cache[id] = node;
|
||||
}
|
||||
callback(err, node);
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
Neo4j.prototype.create = function create(model, data, callback) {
|
||||
data.nodeType = model;
|
||||
var node = this.client.createNode();
|
||||
node.data = cleanup(data);
|
||||
node.data.nodeType = model;
|
||||
node.save(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
this.cache[node.id] = node;
|
||||
node.index(model, 'id', node.id, function (err) {
|
||||
if (err) return callback(err);
|
||||
this.updateIndexes(model, node, function (err) {
|
||||
if (err) return callback(err);
|
||||
callback(null, node.id);
|
||||
});
|
||||
}.bind(this));
|
||||
data.nodeType = model;
|
||||
var node = this.client.createNode();
|
||||
node.data = cleanup(data);
|
||||
node.data.nodeType = model;
|
||||
node.save(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
this.cache[node.id] = node;
|
||||
node.index(model, 'id', node.id, function (err) {
|
||||
if (err) return callback(err);
|
||||
this.updateIndexes(model, node, function (err) {
|
||||
if (err) return callback(err);
|
||||
callback(null, node.id);
|
||||
});
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Neo4j.prototype.updateIndexes = function updateIndexes(model, node, cb) {
|
||||
var props = this._models[model].properties;
|
||||
var wait = 1;
|
||||
Object.keys(props).forEach(function (key) {
|
||||
if (props[key].index && node.data[key]) {
|
||||
wait += 1;
|
||||
node.index(model, key, node.data[key], done);
|
||||
}
|
||||
});
|
||||
|
||||
done();
|
||||
|
||||
var error = false;
|
||||
function done(err) {
|
||||
error = error || err;
|
||||
if (--wait === 0) {
|
||||
cb(error);
|
||||
}
|
||||
var props = this._models[model].properties;
|
||||
var wait = 1;
|
||||
Object.keys(props).forEach(function (key) {
|
||||
if (props[key].index && node.data[key]) {
|
||||
wait += 1;
|
||||
node.index(model, key, node.data[key], done);
|
||||
}
|
||||
});
|
||||
|
||||
done();
|
||||
|
||||
var error = false;
|
||||
|
||||
function done(err) {
|
||||
error = error || err;
|
||||
if (--wait === 0) {
|
||||
cb(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Neo4j.prototype.save = function save(model, data, callback) {
|
||||
var self = this;
|
||||
|
||||
this.node(data.id, function (err, node) {
|
||||
//delete id property since that's redundant and we use the node.id
|
||||
delete data.id;
|
||||
if (err) return callback(err);
|
||||
node.data = cleanup(data);
|
||||
node.save(function (err) {
|
||||
if (err) return callback(err);
|
||||
self.updateIndexes(model, node, function (err) {
|
||||
if (err) return console.log(err);
|
||||
//map node id to the id property being sent back
|
||||
node.data.id = node.id;
|
||||
callback(null, node.data);
|
||||
});
|
||||
});
|
||||
var self = this;
|
||||
|
||||
this.node(data.id, function (err, node) {
|
||||
//delete id property since that's redundant and we use the node.id
|
||||
delete data.id;
|
||||
if (err) return callback(err);
|
||||
node.data = cleanup(data);
|
||||
node.save(function (err) {
|
||||
if (err) return callback(err);
|
||||
self.updateIndexes(model, node, function (err) {
|
||||
if (err) return console.log(err);
|
||||
//map node id to the id property being sent back
|
||||
node.data.id = node.id;
|
||||
callback(null, node.data);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Neo4j.prototype.exists = function exists(model, id, callback) {
|
||||
delete this.cache[id];
|
||||
this.node(id, callback);
|
||||
delete this.cache[id];
|
||||
this.node(id, callback);
|
||||
};
|
||||
|
||||
Neo4j.prototype.find = function find(model, id, callback) {
|
||||
delete this.cache[id];
|
||||
this.node(id, function (err, node) {
|
||||
if (node && node.data) {
|
||||
node.data.id = id;
|
||||
}
|
||||
callback(err, this.readFromDb(model, node && node.data));
|
||||
}.bind(this));
|
||||
delete this.cache[id];
|
||||
this.node(id, function (err, node) {
|
||||
if (node && node.data) {
|
||||
node.data.id = id;
|
||||
}
|
||||
callback(err, this.readFromDb(model, node && node.data));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Neo4j.prototype.readFromDb = function readFromDb(model, data) {
|
||||
if (!data) return data;
|
||||
var res = {};
|
||||
var props = this._models[model].properties;
|
||||
Object.keys(data).forEach(function (key) {
|
||||
if (props[key] && props[key].type.name === 'Date') {
|
||||
res[key] = new Date(data[key]);
|
||||
} else {
|
||||
res[key] = data[key];
|
||||
}
|
||||
});
|
||||
return res;
|
||||
if (!data) return data;
|
||||
var res = {};
|
||||
var props = this._models[model].properties;
|
||||
Object.keys(data).forEach(function (key) {
|
||||
if (props[key] && props[key].type.name === 'Date') {
|
||||
res[key] = new Date(data[key]);
|
||||
} else {
|
||||
res[key] = data[key];
|
||||
}
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
Neo4j.prototype.destroy = function destroy(model, id, callback) {
|
||||
var force = true;
|
||||
this.node(id, function (err, node) {
|
||||
if (err) return callback(err);
|
||||
node.delete(function (err) {
|
||||
if (err) return callback(err);
|
||||
delete this.cache[id];
|
||||
}.bind(this), force);
|
||||
});
|
||||
var force = true;
|
||||
this.node(id, function (err, node) {
|
||||
if (err) return callback(err);
|
||||
node.delete(function (err) {
|
||||
if (err) return callback(err);
|
||||
delete this.cache[id];
|
||||
}.bind(this), force);
|
||||
});
|
||||
};
|
||||
|
||||
Neo4j.prototype.all = function all(model, filter, callback) {
|
||||
this.client.queryNodeIndex(model, 'id:*', function (err, nodes) {
|
||||
if (nodes) {
|
||||
nodes = nodes.map(function (obj) {
|
||||
obj.data.id = obj.id;
|
||||
return this.readFromDb(model, obj.data);
|
||||
}.bind(this));
|
||||
}
|
||||
if (filter) {
|
||||
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
|
||||
if (filter.order) {
|
||||
var key = filter.order.split(' ')[0];
|
||||
var dir = filter.order.split(' ')[1];
|
||||
nodes = nodes.sort(function (a, b) {
|
||||
return a[key] > b[key];
|
||||
});
|
||||
if (dir === 'DESC') nodes = nodes.reverse();
|
||||
}
|
||||
}
|
||||
callback(err, nodes);
|
||||
}.bind(this));
|
||||
this.client.queryNodeIndex(model, 'id:*', function (err, nodes) {
|
||||
if (nodes) {
|
||||
nodes = nodes.map(function (obj) {
|
||||
obj.data.id = obj.id;
|
||||
return this.readFromDb(model, obj.data);
|
||||
}.bind(this));
|
||||
}
|
||||
if (filter) {
|
||||
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
|
||||
if (filter.order) {
|
||||
var key = filter.order.split(' ')[0];
|
||||
var dir = filter.order.split(' ')[1];
|
||||
nodes = nodes.sort(function (a, b) {
|
||||
return a[key] > b[key];
|
||||
});
|
||||
if (dir === 'DESC') nodes = nodes.reverse();
|
||||
}
|
||||
}
|
||||
callback(err, nodes);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Neo4j.prototype.allNodes = function all(model, callback) {
|
||||
this.client.queryNodeIndex(model, 'id:*', function (err, nodes) {
|
||||
callback(err, nodes);
|
||||
});
|
||||
this.client.queryNodeIndex(model, 'id:*', function (err, nodes) {
|
||||
callback(err, nodes);
|
||||
});
|
||||
};
|
||||
|
||||
function applyFilter(filter) {
|
||||
if (typeof filter.where === 'function') {
|
||||
return filter.where;
|
||||
}
|
||||
var keys = Object.keys(filter.where || {});
|
||||
return function (obj) {
|
||||
var pass = true;
|
||||
keys.forEach(function (key) {
|
||||
if (!test(filter.where[key], obj[key])) {
|
||||
pass = false;
|
||||
}
|
||||
});
|
||||
return pass;
|
||||
}
|
||||
if (typeof filter.where === 'function') {
|
||||
return filter.where;
|
||||
}
|
||||
var keys = Object.keys(filter.where || {});
|
||||
return function (obj) {
|
||||
var pass = true;
|
||||
keys.forEach(function (key) {
|
||||
if (!test(filter.where[key], obj[key])) {
|
||||
pass = false;
|
||||
}
|
||||
});
|
||||
return pass;
|
||||
}
|
||||
|
||||
function test(example, value) {
|
||||
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
||||
return value.match(example);
|
||||
}
|
||||
if (typeof value === 'object' && value.constructor.name === 'Date' && typeof example === 'object' && example.constructor.name === 'Date') {
|
||||
return example.toString() === value.toString();
|
||||
}
|
||||
// not strict equality
|
||||
return example == value;
|
||||
function test(example, value) {
|
||||
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
||||
return value.match(example);
|
||||
}
|
||||
if (typeof value === 'object' && value.constructor.name === 'Date' && typeof example === 'object' && example.constructor.name === 'Date') {
|
||||
return example.toString() === value.toString();
|
||||
}
|
||||
// not strict equality
|
||||
return example == value;
|
||||
}
|
||||
}
|
||||
|
||||
Neo4j.prototype.destroyAll = function destroyAll(model, callback) {
|
||||
var wait, error = null;
|
||||
this.allNodes(model, function (err, collection) {
|
||||
if (err) return callback(err);
|
||||
wait = collection.length;
|
||||
collection && collection.forEach && collection.forEach(function (node) {
|
||||
node.delete(done, true);
|
||||
});
|
||||
var wait, error = null;
|
||||
this.allNodes(model, function (err, collection) {
|
||||
if (err) return callback(err);
|
||||
wait = collection.length;
|
||||
collection && collection.forEach && collection.forEach(function (node) {
|
||||
node.delete(done, true);
|
||||
});
|
||||
});
|
||||
|
||||
function done(err) {
|
||||
error = error || err;
|
||||
if (--wait === 0) {
|
||||
callback(error);
|
||||
}
|
||||
function done(err) {
|
||||
error = error || err;
|
||||
if (--wait === 0) {
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Neo4j.prototype.count = function count(model, callback, conds) {
|
||||
this.all(model, {where: conds}, function (err, collection) {
|
||||
callback(err, collection ? collection.length : 0);
|
||||
});
|
||||
this.all(model, {where: conds}, function (err, collection) {
|
||||
callback(err, collection ? collection.length : 0);
|
||||
});
|
||||
};
|
||||
|
||||
Neo4j.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
|
||||
data.id = id;
|
||||
this.node(id, function (err, node) {
|
||||
this.save(model, merge(node.data, data), cb);
|
||||
}.bind(this));
|
||||
data.id = id;
|
||||
this.node(id, function (err, node) {
|
||||
this.save(model, merge(node.data, data), cb);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
function cleanup(data) {
|
||||
if (!data) return null;
|
||||
|
||||
var res = {};
|
||||
Object.keys(data).forEach(function (key) {
|
||||
var v = data[key];
|
||||
if (v === null) {
|
||||
// skip
|
||||
// console.log('skip null', key);
|
||||
} else if (v && v.constructor.name === 'Array' && v.length === 0) {
|
||||
// skip
|
||||
// console.log('skip blank array', key);
|
||||
} else if (typeof v !== 'undefined') {
|
||||
res[key] = v;
|
||||
}
|
||||
});
|
||||
return res;
|
||||
if (!data) return null;
|
||||
|
||||
var res = {};
|
||||
Object.keys(data).forEach(function (key) {
|
||||
var v = data[key];
|
||||
if (v === null) {
|
||||
// skip
|
||||
// console.log('skip null', key);
|
||||
} else if (v && v.constructor.name === 'Array' && v.length === 0) {
|
||||
// skip
|
||||
// console.log('skip blank array', key);
|
||||
} else if (typeof v !== 'undefined') {
|
||||
res[key] = v;
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
function merge(base, update) {
|
||||
Object.keys(update).forEach(function (key) {
|
||||
base[key] = update[key];
|
||||
});
|
||||
return base;
|
||||
Object.keys(update).forEach(function (key) {
|
||||
base[key] = update[key];
|
||||
});
|
||||
return base;
|
||||
}
|
||||
|
|
|
@ -7,104 +7,104 @@ var uuid = require('node-uuid');
|
|||
var riak = safeRequire('riak-js');
|
||||
|
||||
exports.initialize = function initializeSchema(dataSource, callback) {
|
||||
dataSource.client = riak.getClient({
|
||||
host: dataSource.settings.host || '127.0.0.1',
|
||||
port: dataSource.settings.port || 8091
|
||||
});
|
||||
dataSource.connector = new Riak(dataSource.client);
|
||||
dataSource.client = riak.getClient({
|
||||
host: dataSource.settings.host || '127.0.0.1',
|
||||
port: dataSource.settings.port || 8091
|
||||
});
|
||||
dataSource.connector = new Riak(dataSource.client);
|
||||
};
|
||||
|
||||
function Riak(client) {
|
||||
this._models = {};
|
||||
this.client = client;
|
||||
this._models = {};
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
Riak.prototype.define = function (descr) {
|
||||
this._models[descr.model.modelName] = descr;
|
||||
this._models[descr.model.modelName] = descr;
|
||||
};
|
||||
|
||||
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) {
|
||||
data.id = uuid();
|
||||
this.save(model, data, function (err) {
|
||||
if (callback) {
|
||||
callback(err, data.id);
|
||||
}
|
||||
});
|
||||
data.id = uuid();
|
||||
this.save(model, data, function (err) {
|
||||
if (callback) {
|
||||
callback(err, data.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Riak.prototype.exists = function (model, id, callback) {
|
||||
this.client.exists(model, id, function (err, exists, meta) {
|
||||
if (callback) {
|
||||
callback(err, exists);
|
||||
}
|
||||
});
|
||||
this.client.exists(model, id, function (err, exists, meta) {
|
||||
if (callback) {
|
||||
callback(err, exists);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Riak.prototype.find = function find(model, id, callback) {
|
||||
this.client.get(model, id, function (err, data, meta) {
|
||||
if (data && data.id) {
|
||||
data.id = id;
|
||||
} else {
|
||||
data = null;
|
||||
}
|
||||
if (typeof callback === 'function') callback(err, data);
|
||||
});
|
||||
this.client.get(model, id, function (err, data, meta) {
|
||||
if (data && data.id) {
|
||||
data.id = id;
|
||||
} else {
|
||||
data = null;
|
||||
}
|
||||
if (typeof callback === 'function') callback(err, data);
|
||||
});
|
||||
};
|
||||
|
||||
Riak.prototype.destroy = function destroy(model, id, callback) {
|
||||
this.client.remove(model, id, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
this.client.remove(model, id, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
Riak.prototype.all = function all(model, filter, callback) {
|
||||
var opts = {};
|
||||
if (filter && filter.where) opts.where = filter.where;
|
||||
this.client.getAll(model, function (err, result, meta) {
|
||||
if (err) return callback(err, []);
|
||||
/// return callback(err, result.map(function (x) { return {id: x}; }));
|
||||
result = (result || []).map(function (row) {
|
||||
var record = row.data;
|
||||
record.id = row.meta.key;
|
||||
console.log(record);
|
||||
return record;
|
||||
});
|
||||
var opts = {};
|
||||
if (filter && filter.where) opts.where = filter.where;
|
||||
this.client.getAll(model, function (err, result, meta) {
|
||||
if (err) return callback(err, []);
|
||||
/// return callback(err, result.map(function (x) { return {id: x}; }));
|
||||
result = (result || []).map(function (row) {
|
||||
var record = row.data;
|
||||
record.id = row.meta.key;
|
||||
console.log(record);
|
||||
return record;
|
||||
});
|
||||
|
||||
return callback(err, result);
|
||||
}.bind(this));
|
||||
return callback(err, result);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Riak.prototype.destroyAll = function destroyAll(model, callback) {
|
||||
var self = this;
|
||||
this.all(model, {}, function (err, recs) {
|
||||
if (err) callback(err);
|
||||
var self = this;
|
||||
this.all(model, {}, function (err, recs) {
|
||||
if (err) callback(err);
|
||||
|
||||
removeOne();
|
||||
removeOne();
|
||||
|
||||
function removeOne(error) {
|
||||
err = err || error;
|
||||
var rec = recs.pop();
|
||||
if (!rec) return callback(err && err.statusCode != '404' ? err : null);
|
||||
console.log(rec.id);
|
||||
self.client.remove(model, rec.id, removeOne);
|
||||
}
|
||||
function removeOne(error) {
|
||||
err = err || error;
|
||||
var rec = recs.pop();
|
||||
if (!rec) return callback(err && err.statusCode != '404' ? err : null);
|
||||
console.log(rec.id);
|
||||
self.client.remove(model, rec.id, removeOne);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
Riak.prototype.count = function count(model, callback) {
|
||||
this.client.keys(model + ':*', function (err, keys) {
|
||||
callback(err, err ? null : keys.length);
|
||||
});
|
||||
this.client.keys(model + ':*', function (err, keys) {
|
||||
callback(err, err ? null : keys.length);
|
||||
});
|
||||
};
|
||||
|
||||
Riak.prototype.updateAttributes = function updateAttrs(model, id, data, cb) {
|
||||
data.id = id;
|
||||
this.save(model, data, cb);
|
||||
data.id = id;
|
||||
this.save(model, data, cb);
|
||||
};
|
||||
|
||||
|
|
1130
lib/dao.js
1130
lib/dao.js
File diff suppressed because it is too large
Load Diff
1780
lib/datasource.js
1780
lib/datasource.js
File diff suppressed because it is too large
Load Diff
76
lib/geo.js
76
lib/geo.js
|
@ -10,12 +10,12 @@ var assert = require('assert');
|
|||
|
||||
exports.nearFilter = function nearFilter(where) {
|
||||
var result = false;
|
||||
|
||||
if(where && typeof where === 'object') {
|
||||
|
||||
if (where && typeof where === 'object') {
|
||||
Object.keys(where).forEach(function (key) {
|
||||
var ex = where[key];
|
||||
|
||||
if(ex && ex.near) {
|
||||
|
||||
if (ex && ex.near) {
|
||||
result = {
|
||||
near: ex.near,
|
||||
maxDistance: ex.maxDistance,
|
||||
|
@ -24,7 +24,7 @@ exports.nearFilter = function nearFilter(where) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -36,43 +36,43 @@ exports.filter = function (arr, filter) {
|
|||
var origin = filter.near;
|
||||
var max = filter.maxDistance > 0 ? filter.maxDistance : false;
|
||||
var key = filter.key;
|
||||
|
||||
|
||||
// create distance index
|
||||
var distances = {};
|
||||
var result = [];
|
||||
|
||||
|
||||
arr.forEach(function (obj) {
|
||||
var loc = obj[key];
|
||||
|
||||
|
||||
// filter out objects without locations
|
||||
if(!loc) return;
|
||||
|
||||
if(!(loc instanceof GeoPoint)) {
|
||||
if (!loc) return;
|
||||
|
||||
if (!(loc instanceof GeoPoint)) {
|
||||
loc = GeoPoint(loc);
|
||||
}
|
||||
|
||||
if(typeof loc.lat !== 'number') return;
|
||||
if(typeof loc.lng !== 'number') return;
|
||||
|
||||
|
||||
if (typeof loc.lat !== 'number') return;
|
||||
if (typeof loc.lng !== 'number') return;
|
||||
|
||||
var d = GeoPoint.distanceBetween(origin, loc);
|
||||
|
||||
if(max && d > max) {
|
||||
|
||||
if (max && d > max) {
|
||||
// dont add
|
||||
} else {
|
||||
distances[obj.id] = d;
|
||||
result.push(obj);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return result.sort(function (objA, objB) {
|
||||
var a = objB[key];
|
||||
var b = objB[key];
|
||||
|
||||
if(a && b) {
|
||||
|
||||
if (a && b) {
|
||||
var da = distances[objA.id];
|
||||
var db = distances[objB.id];
|
||||
|
||||
if(db === da) return 0;
|
||||
|
||||
if (db === da) return 0;
|
||||
return da > db ? 1 : -1;
|
||||
} else {
|
||||
return 0;
|
||||
|
@ -87,15 +87,15 @@ exports.filter = function (arr, filter) {
|
|||
exports.GeoPoint = GeoPoint;
|
||||
|
||||
function GeoPoint(data) {
|
||||
if(!(this instanceof GeoPoint)) {
|
||||
if (!(this instanceof GeoPoint)) {
|
||||
return new GeoPoint(data);
|
||||
}
|
||||
|
||||
if(typeof data === 'string') {
|
||||
|
||||
if (typeof data === 'string') {
|
||||
data = data.split(/,\s*/);
|
||||
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 = {
|
||||
lng: Number(data[0]),
|
||||
lat: Number(data[1])
|
||||
|
@ -104,7 +104,7 @@ function GeoPoint(data) {
|
|||
data.lng = Number(data.lng);
|
||||
data.lat = Number(data.lat);
|
||||
}
|
||||
|
||||
|
||||
assert(typeof data === 'object', 'must provide a lat and lng object when creating a GeoPoint');
|
||||
assert(typeof data.lat === 'number' && !isNaN(data.lat), 'lat must be a number when creating a GeoPoint');
|
||||
assert(typeof data.lng === 'number' && !isNaN(data.lng), 'lng must be a number when creating a GeoPoint');
|
||||
|
@ -112,7 +112,7 @@ function GeoPoint(data) {
|
|||
assert(data.lng >= -180, 'lng must be >= -180');
|
||||
assert(data.lat <= 90, 'lat must be <= 90');
|
||||
assert(data.lat >= -90, 'lat must be >= -90');
|
||||
|
||||
|
||||
this.lat = data.lat;
|
||||
this.lng = data.lng;
|
||||
}
|
||||
|
@ -122,19 +122,19 @@ function GeoPoint(data) {
|
|||
*/
|
||||
|
||||
GeoPoint.distanceBetween = function distanceBetween(a, b, options) {
|
||||
if(!(a instanceof GeoPoint)) {
|
||||
if (!(a instanceof GeoPoint)) {
|
||||
a = GeoPoint(a);
|
||||
}
|
||||
if(!(b instanceof GeoPoint)) {
|
||||
if (!(b instanceof GeoPoint)) {
|
||||
b = GeoPoint(b);
|
||||
}
|
||||
|
||||
|
||||
var x1 = a.lat;
|
||||
var y1 = a.lng;
|
||||
|
||||
|
||||
var x2 = b.lat;
|
||||
var y2 = b.lng;
|
||||
|
||||
|
||||
return geoDistance(x1, y1, x2, y2, options);
|
||||
}
|
||||
|
||||
|
@ -162,7 +162,7 @@ GeoPoint.prototype.toString = function () {
|
|||
var PI = 3.1415926535897932384626433832795;
|
||||
|
||||
// factor to convert decimal degrees to radians
|
||||
var DEG2RAD = 0.01745329252;
|
||||
var DEG2RAD = 0.01745329252;
|
||||
|
||||
// factor to convert decimal degrees to radians
|
||||
var RAD2DEG = 57.29577951308;
|
||||
|
@ -184,12 +184,12 @@ function geoDistance(x1, y1, x2, y2, options) {
|
|||
x2 = x2 * DEG2RAD;
|
||||
y2 = y2 * DEG2RAD;
|
||||
|
||||
var a = Math.pow(Math.sin(( y2-y1 ) / 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 a = Math.pow(Math.sin(( y2 - y1 ) / 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 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/
|
||||
Hookable.prototype.trigger = function trigger(actionName, work, data) {
|
||||
var capitalizedName = capitalize(actionName);
|
||||
var beforeHook = this.constructor["before" + capitalizedName] || this.constructor["pre" + capitalizedName];
|
||||
var afterHook = this.constructor["after" + capitalizedName] || this.constructor["post" + capitalizedName];
|
||||
if (actionName === 'validate') {
|
||||
beforeHook = beforeHook || this.constructor.beforeValidation;
|
||||
afterHook = afterHook || this.constructor.afterValidation;
|
||||
}
|
||||
var inst = this;
|
||||
var capitalizedName = capitalize(actionName);
|
||||
var beforeHook = this.constructor["before" + capitalizedName]
|
||||
|| this.constructor["pre" + capitalizedName];
|
||||
var afterHook = this.constructor["after" + capitalizedName]
|
||||
|| this.constructor["post" + capitalizedName];
|
||||
if (actionName === 'validate') {
|
||||
beforeHook = beforeHook || this.constructor.beforeValidation;
|
||||
afterHook = afterHook || this.constructor.afterValidation;
|
||||
}
|
||||
var inst = this;
|
||||
|
||||
// we only call "before" hook when we have actual action (work) to perform
|
||||
if (work) {
|
||||
if (beforeHook) {
|
||||
// before hook should be called on instance with one param: callback
|
||||
beforeHook.call(inst, function () {
|
||||
// actual action also have one param: callback
|
||||
work.call(inst, next);
|
||||
}, data);
|
||||
} else {
|
||||
work.call(inst, next);
|
||||
}
|
||||
// we only call "before" hook when we have actual action (work) to perform
|
||||
if (work) {
|
||||
if (beforeHook) {
|
||||
// before hook should be called on instance with one param: callback
|
||||
beforeHook.call(inst, function () {
|
||||
// actual action also have one param: callback
|
||||
work.call(inst, next);
|
||||
}, data);
|
||||
} else {
|
||||
next();
|
||||
work.call(inst, next);
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
|
||||
function next(done) {
|
||||
if (afterHook) {
|
||||
afterHook.call(inst, done);
|
||||
} else if (done) {
|
||||
done.call(this);
|
||||
}
|
||||
function next(done) {
|
||||
if (afterHook) {
|
||||
afterHook.call(inst, done);
|
||||
} else if (done) {
|
||||
done.call(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
var self = this;
|
||||
var self = this;
|
||||
|
||||
if (
|
||||
(include.constructor.name == 'Array' && include.length == 0) ||
|
||||
(include.constructor.name == 'Object' && Object.keys(include).length == 0)
|
||||
) {
|
||||
cb(null, objects);
|
||||
return;
|
||||
if (
|
||||
(include.constructor.name == 'Array' && include.length == 0) ||
|
||||
(include.constructor.name == 'Object' && Object.keys(include).length == 0)
|
||||
) {
|
||||
cb(null, objects);
|
||||
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 = {};
|
||||
var objsByKeys = {};
|
||||
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]);
|
||||
}
|
||||
|
||||
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);
|
||||
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] = [];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cb(null, objects);
|
||||
}
|
||||
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);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,60 +1,60 @@
|
|||
module.exports = function getIntrospector(ModelBuilder) {
|
||||
|
||||
function introspectType(value) {
|
||||
function introspectType(value) {
|
||||
|
||||
// Unknown type, using Any
|
||||
if (value === null || value === undefined) {
|
||||
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;
|
||||
// Unknown type, using Any
|
||||
if (value === null || value === undefined) {
|
||||
return ModelBuilder.Any;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
exports.inherits = function (newClass, baseClass, options) {
|
||||
util.inherits(newClass, baseClass);
|
||||
util.inherits(newClass, baseClass);
|
||||
|
||||
options = options || {
|
||||
staticProperties: true,
|
||||
override: false
|
||||
};
|
||||
options = options || {
|
||||
staticProperties: true,
|
||||
override: false
|
||||
};
|
||||
|
||||
if (options.staticProperties) {
|
||||
Object.keys(baseClass).forEach(function (classProp) {
|
||||
if (classProp !== 'super_' && (!newClass.hasOwnProperty(classProp) || options.override)) {
|
||||
var pd = Object.getOwnPropertyDescriptor(baseClass, classProp);
|
||||
Object.defineProperty(newClass, classProp, pd);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (options.staticProperties) {
|
||||
Object.keys(baseClass).forEach(function (classProp) {
|
||||
if (classProp !== 'super_' && (!newClass.hasOwnProperty(classProp)
|
||||
|| options.override)) {
|
||||
var pd = Object.getOwnPropertyDescriptor(baseClass, classProp);
|
||||
Object.defineProperty(newClass, classProp, pd);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mix in the a class into the new class
|
||||
* @param newClass The target class to receive the mixin
|
||||
|
@ -30,76 +30,78 @@ exports.inherits = function (newClass, baseClass, options) {
|
|||
* @param options
|
||||
*/
|
||||
exports.mixin = function (newClass, mixinClass, options) {
|
||||
if (Array.isArray(newClass._mixins)) {
|
||||
if(newClass._mixins.indexOf(mixinClass) !== -1) {
|
||||
return;
|
||||
if (Array.isArray(newClass._mixins)) {
|
||||
if (newClass._mixins.indexOf(mixinClass) !== -1) {
|
||||
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);
|
||||
} else {
|
||||
newClass._mixins = [mixinClass];
|
||||
}
|
||||
Object.defineProperty(newClass, classProp, pd);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
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
|
||||
// For example, deleteById is an alias of removeById
|
||||
proxies = proxies || [];
|
||||
for(var i = 0; i<proxies.length; i++) {
|
||||
if(proxies[i]._delegate === fn) {
|
||||
for (var i = 0; i < proxies.length; i++) {
|
||||
if (proxies[i]._delegate === fn) {
|
||||
return proxies[i];
|
||||
}
|
||||
}
|
||||
var f = function() {
|
||||
var f = function () {
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
f._delegate = fn;
|
||||
proxies.push(f);
|
||||
Object.keys(fn).forEach(function(x) {
|
||||
Object.keys(fn).forEach(function (x) {
|
||||
f[x] = fn[x];
|
||||
});
|
||||
return f;
|
||||
|
|
365
lib/list.js
365
lib/list.js
|
@ -1,4 +1,3 @@
|
|||
|
||||
module.exports = List;
|
||||
|
||||
/**
|
||||
|
@ -10,229 +9,229 @@ module.exports = List;
|
|||
* @constructor
|
||||
*/
|
||||
function List(data, type, parent) {
|
||||
var list = this;
|
||||
if (!(list instanceof List)) {
|
||||
return new List(data, type, parent);
|
||||
var list = this;
|
||||
if (!(list instanceof List)) {
|
||||
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') {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
throw new Error('could not create List from JSON string: ', data);
|
||||
}
|
||||
}
|
||||
|
||||
if (data && data instanceof List) data = data.items;
|
||||
|
||||
Object.defineProperty(list, 'parent', {
|
||||
writable: false,
|
||||
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, 'parent', {
|
||||
writable: false,
|
||||
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;
|
||||
Object.defineProperty(list, 'length', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
get: function () {
|
||||
return list.items.length;
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(list, 'length', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
get: function() {
|
||||
return list.items.length;
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
var _;
|
||||
try {
|
||||
var underscore = 'underscore';
|
||||
_ = require(underscore);
|
||||
var underscore = 'underscore';
|
||||
_ = require(underscore);
|
||||
} catch (e) {
|
||||
_ = false;
|
||||
_ = false;
|
||||
}
|
||||
|
||||
if (_) {
|
||||
var _import = [
|
||||
// collection methods
|
||||
'each',
|
||||
'map',
|
||||
'reduce',
|
||||
'reduceRight',
|
||||
'find',
|
||||
'filter',
|
||||
'reject',
|
||||
'all',
|
||||
'any',
|
||||
'include',
|
||||
'invoke',
|
||||
'pluck',
|
||||
'max',
|
||||
'min',
|
||||
'sortBy',
|
||||
'groupBy',
|
||||
'sortedIndex',
|
||||
'shuffle',
|
||||
'toArray',
|
||||
'size',
|
||||
// array methods
|
||||
'first',
|
||||
'initial',
|
||||
'last',
|
||||
'rest',
|
||||
'compact',
|
||||
'flatten',
|
||||
'without',
|
||||
'union',
|
||||
'intersection',
|
||||
'difference',
|
||||
'uniq',
|
||||
'zip',
|
||||
'indexOf',
|
||||
'lastIndexOf',
|
||||
'range'
|
||||
];
|
||||
var _import = [
|
||||
// collection methods
|
||||
'each',
|
||||
'map',
|
||||
'reduce',
|
||||
'reduceRight',
|
||||
'find',
|
||||
'filter',
|
||||
'reject',
|
||||
'all',
|
||||
'any',
|
||||
'include',
|
||||
'invoke',
|
||||
'pluck',
|
||||
'max',
|
||||
'min',
|
||||
'sortBy',
|
||||
'groupBy',
|
||||
'sortedIndex',
|
||||
'shuffle',
|
||||
'toArray',
|
||||
'size',
|
||||
// array methods
|
||||
'first',
|
||||
'initial',
|
||||
'last',
|
||||
'rest',
|
||||
'compact',
|
||||
'flatten',
|
||||
'without',
|
||||
'union',
|
||||
'intersection',
|
||||
'difference',
|
||||
'uniq',
|
||||
'zip',
|
||||
'indexOf',
|
||||
'lastIndexOf',
|
||||
'range'
|
||||
];
|
||||
|
||||
_import.forEach(function(name) {
|
||||
List.prototype[name] = function() {
|
||||
var args = [].slice.call(arguments);
|
||||
args.unshift(this.items);
|
||||
return _[name].apply(_, args);
|
||||
};
|
||||
});
|
||||
_import.forEach(function (name) {
|
||||
List.prototype[name] = function () {
|
||||
var args = [].slice.call(arguments);
|
||||
args.unshift(this.items);
|
||||
return _[name].apply(_, args);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
['slice', 'forEach', 'filter', 'reduce', 'map'].forEach(function (method) {
|
||||
var slice = [].slice;
|
||||
List.prototype[method] = function () {
|
||||
return Array.prototype[method].apply(this.items, slice.call(arguments));
|
||||
};
|
||||
var slice = [].slice;
|
||||
List.prototype[method] = function () {
|
||||
return Array.prototype[method].apply(this.items, slice.call(arguments));
|
||||
};
|
||||
});
|
||||
|
||||
List.prototype.find = function(pattern, field) {
|
||||
if (field) {
|
||||
var res;
|
||||
this.items.forEach(function(o) {
|
||||
if (o[field] == pattern) res = o;
|
||||
});
|
||||
return res;
|
||||
List.prototype.find = function (pattern, field) {
|
||||
if (field) {
|
||||
var res;
|
||||
this.items.forEach(function (o) {
|
||||
if (o[field] == pattern) res = o;
|
||||
});
|
||||
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 {
|
||||
return this.items[this.items.indexOf(pattern)];
|
||||
items.push(item);
|
||||
}
|
||||
});
|
||||
return items;
|
||||
};
|
||||
|
||||
List.prototype.toObject = function(onlySchema) {
|
||||
var items = [];
|
||||
this.items.forEach(function(item) {
|
||||
if(item.toObject) {
|
||||
items.push(item.toObject(onlySchema));
|
||||
} else {
|
||||
items.push(item);
|
||||
}
|
||||
});
|
||||
return items;
|
||||
List.prototype.toJSON = function () {
|
||||
return this.toObject(true);
|
||||
};
|
||||
|
||||
List.prototype.toJSON = function() {
|
||||
return this.toObject(true);
|
||||
List.prototype.toString = function () {
|
||||
return JSON.stringify(this.items);
|
||||
};
|
||||
|
||||
List.prototype.toString = function() {
|
||||
return JSON.stringify(this.items);
|
||||
List.prototype.autoincrement = function () {
|
||||
return this.nextid++;
|
||||
};
|
||||
|
||||
List.prototype.autoincrement = function() {
|
||||
return this.nextid++;
|
||||
List.prototype.push = function (obj) {
|
||||
var item = new ListItem(obj, this);
|
||||
this.items.push(item);
|
||||
return item;
|
||||
};
|
||||
|
||||
List.prototype.push = function(obj) {
|
||||
var item = new ListItem(obj, this);
|
||||
this.items.push(item);
|
||||
return item;
|
||||
};
|
||||
|
||||
List.prototype.remove = function(obj) {
|
||||
var id = obj.id ? obj.id : obj;
|
||||
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);
|
||||
List.prototype.remove = function (obj) {
|
||||
var id = obj.id ? obj.id : obj;
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
List.prototype.sort = function(cb) {
|
||||
return this.items.sort(cb);
|
||||
List.prototype.sort = function (cb) {
|
||||
return this.items.sort(cb);
|
||||
};
|
||||
|
||||
List.prototype.map = function(cb) {
|
||||
if (typeof cb === 'function') return this.items.map(cb);
|
||||
if (typeof cb === 'string') return this.items.map(function(el) {
|
||||
if (typeof el[cb] === 'function') return el[cb]();
|
||||
if (el.hasOwnProperty(cb)) return el[cb];
|
||||
});
|
||||
List.prototype.map = function (cb) {
|
||||
if (typeof cb === 'function') return this.items.map(cb);
|
||||
if (typeof cb === 'string') return this.items.map(function (el) {
|
||||
if (typeof el[cb] === 'function') return el[cb]();
|
||||
if (el.hasOwnProperty(cb)) return el[cb];
|
||||
});
|
||||
};
|
||||
|
||||
function ListItem(data, parent) {
|
||||
if(!(this instanceof ListItem)) {
|
||||
return new ListItem(data, parent);
|
||||
}
|
||||
if (typeof data === 'object') {
|
||||
for (var i in data) this[i] = data[i];
|
||||
} else {
|
||||
this.id = data;
|
||||
}
|
||||
Object.defineProperty(this, 'parent', {
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: parent
|
||||
});
|
||||
if (!this.id) {
|
||||
this.id = parent.autoincrement();
|
||||
}
|
||||
if (parent.ItemType) {
|
||||
this.__proto__ = parent.ItemType.prototype;
|
||||
if (parent.ItemType !== ListItem) {
|
||||
parent.ItemType.apply(this);
|
||||
}
|
||||
if (!(this instanceof ListItem)) {
|
||||
return new ListItem(data, parent);
|
||||
}
|
||||
if (typeof data === 'object') {
|
||||
for (var i in data) this[i] = data[i];
|
||||
} else {
|
||||
this.id = data;
|
||||
}
|
||||
Object.defineProperty(this, 'parent', {
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: parent
|
||||
});
|
||||
if (!this.id) {
|
||||
this.id = parent.autoincrement();
|
||||
}
|
||||
if (parent.ItemType) {
|
||||
this.__proto__ = parent.ItemType.prototype;
|
||||
if (parent.ItemType !== ListItem) {
|
||||
parent.ItemType.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
this.save = function(c) {
|
||||
parent.parent.save(c);
|
||||
};
|
||||
this.save = function (c) {
|
||||
parent.parent.save(c);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -32,15 +32,15 @@ var slice = Array.prototype.slice;
|
|||
* @constructor
|
||||
*/
|
||||
function ModelBuilder() {
|
||||
// create blank models pool
|
||||
/**
|
||||
* @property {Object} models Model constructors
|
||||
*/
|
||||
this.models = {};
|
||||
/**
|
||||
* @property {Object} definitions Definitions of the models
|
||||
*/
|
||||
this.definitions = {};
|
||||
// create blank models pool
|
||||
/**
|
||||
* @property {Object} models Model constructors
|
||||
*/
|
||||
this.models = {};
|
||||
/**
|
||||
* @property {Object} definitions Definitions of the models
|
||||
*/
|
||||
this.definitions = {};
|
||||
}
|
||||
|
||||
// Inherit from EventEmitter
|
||||
|
@ -50,10 +50,10 @@ util.inherits(ModelBuilder, EventEmitter);
|
|||
ModelBuilder.defaultInstance = new ModelBuilder();
|
||||
|
||||
function isModelClass(cls) {
|
||||
if(!cls) {
|
||||
return false;
|
||||
}
|
||||
return cls.prototype instanceof DefaultModelBaseClass;
|
||||
if (!cls) {
|
||||
return false;
|
||||
}
|
||||
return cls.prototype instanceof DefaultModelBaseClass;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,15 +63,15 @@ function isModelClass(cls) {
|
|||
* given name if a model doesn't exist
|
||||
* @returns {*} The model class
|
||||
*/
|
||||
ModelBuilder.prototype.getModel = function(name, forceCreate) {
|
||||
ModelBuilder.prototype.getModel = function (name, forceCreate) {
|
||||
var model = this.models[name];
|
||||
if(!model && forceCreate) {
|
||||
model = this.define(name, {}, {unresolved: true});
|
||||
if (!model && forceCreate) {
|
||||
model = this.define(name, {}, {unresolved: true});
|
||||
}
|
||||
return model;
|
||||
};
|
||||
|
||||
ModelBuilder.prototype.getModelDefinition = function(name) {
|
||||
ModelBuilder.prototype.getModelDefinition = function (name) {
|
||||
return this.definitions[name];
|
||||
};
|
||||
|
||||
|
@ -107,314 +107,316 @@ ModelBuilder.prototype.getModelDefinition = function(name) {
|
|||
* ```
|
||||
*/
|
||||
ModelBuilder.prototype.define = function defineClass(className, properties, settings, parent) {
|
||||
var modelBuilder = this;
|
||||
var args = slice.call(arguments);
|
||||
var pluralName = settings && settings.plural;
|
||||
var modelBuilder = this;
|
||||
var args = slice.call(arguments);
|
||||
var pluralName = (settings && settings.plural) ||
|
||||
inflection.pluralize(className);
|
||||
|
||||
if (!className) {
|
||||
throw new Error('Class name required');
|
||||
if (!className) {
|
||||
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) {
|
||||
settings = {};
|
||||
args.push(settings);
|
||||
hiddenProperty(ModelClass, 'modelName', className);
|
||||
}
|
||||
|
||||
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 || {};
|
||||
settings = settings || {};
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
|
||||
// Set the strict mode to be false by default
|
||||
if(settings.strict === undefined || settings.strict === null) {
|
||||
settings.strict = false;
|
||||
}
|
||||
ModelClass.getter = {};
|
||||
ModelClass.setter = {};
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
var modelDefinition = new ModelDefinition(this, className, properties, settings);
|
||||
|
||||
// Check if there is a unresolved model with the same name
|
||||
var ModelClass = this.models[className];
|
||||
this.definitions[className] = modelDefinition;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
hiddenProperty(ModelClass, 'modelName', className);
|
||||
}
|
||||
// expose properties on the ModelClass
|
||||
ModelClass.definition = modelDefinition;
|
||||
// keep a pointer to settings as models can use it for configuration
|
||||
ModelClass.settings = modelDefinition.settings;
|
||||
|
||||
util.inherits(ModelClass, ModelBaseClass);
|
||||
var idInjection = settings.idInjection;
|
||||
if (idInjection !== false) {
|
||||
// Default to true if undefined
|
||||
idInjection = true;
|
||||
}
|
||||
|
||||
// store class in model pool
|
||||
this.models[className] = ModelClass;
|
||||
var idNames = modelDefinition.idNames();
|
||||
if (idNames.length > 0) {
|
||||
// id already exists
|
||||
idInjection = false;
|
||||
}
|
||||
|
||||
// Return the unresolved model
|
||||
if(settings.unresolved) {
|
||||
ModelClass.settings = {unresolved: true};
|
||||
return ModelClass;
|
||||
}
|
||||
// Add the id property
|
||||
if (idInjection) {
|
||||
// Set up the id property
|
||||
ModelClass.definition.defineProperty('id', { type: Number, id: 1, generated: true });
|
||||
}
|
||||
|
||||
// Add metadata to the ModelClass
|
||||
hiddenProperty(ModelClass, 'modelBuilder', modelBuilder);
|
||||
hiddenProperty(ModelClass, 'dataSource', modelBuilder); // Keep for back-compatibility
|
||||
hiddenProperty(ModelClass, 'pluralModelName', pluralName || inflection.pluralize(className));
|
||||
hiddenProperty(ModelClass, 'relations', {});
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
ModelClass.getter = {};
|
||||
ModelClass.setter = {};
|
||||
|
||||
var modelDefinition = new ModelDefinition(this, className, properties, settings);
|
||||
|
||||
this.definitions[className] = modelDefinition;
|
||||
|
||||
// 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 {
|
||||
// Now the id property is an object that consists of multiple keys
|
||||
Object.defineProperty(ModelClass.prototype, 'id', {
|
||||
get: function () {
|
||||
var compositeId = {};
|
||||
var idNames = ModelClass.definition.idNames();
|
||||
for (var p in idNames) {
|
||||
compositeId[p] = this.__data[p];
|
||||
}
|
||||
} else {
|
||||
// Now the id property is an object that consists of multiple keys
|
||||
Object.defineProperty(ModelClass.prototype, 'id', {
|
||||
get: function () {
|
||||
var compositeId = {};
|
||||
var idNames = ModelClass.definition.idNames();
|
||||
for (var p in idNames) {
|
||||
compositeId[p] = this.__data[p];
|
||||
}
|
||||
return compositeId;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
});
|
||||
return compositeId;
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
// A function to loop through the properties
|
||||
ModelClass.forEachProperty = function (cb) {
|
||||
Object.keys(ModelClass.definition.properties).forEach(cb);
|
||||
};
|
||||
|
||||
// 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
|
||||
ModelClass.forEachProperty = function (cb) {
|
||||
Object.keys(ModelClass.definition.properties).forEach(cb);
|
||||
};
|
||||
// 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];
|
||||
}
|
||||
});
|
||||
|
||||
// A function to attach the model class to a data source
|
||||
ModelClass.attachTo = function (dataSource) {
|
||||
dataSource.attach(this);
|
||||
};
|
||||
// Merge the settings
|
||||
subclassSettings = mergeSettings(settings, subclassSettings);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
/*
|
||||
Object.keys(settings).forEach(function (key) {
|
||||
if(subclassSettings[key] === undefined) {
|
||||
subclassSettings[key] = settings[key];
|
||||
}
|
||||
});
|
||||
*/
|
||||
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);
|
||||
|
||||
// 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();
|
||||
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;
|
||||
} 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);
|
||||
},
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(prop.required) {
|
||||
var requiredOptions = typeof prop.required === 'object' ? prop.required : undefined;
|
||||
ModelClass.validatesPresenceOf(propertyName, requiredOptions);
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
// <propertyName>$was --> __dataWas.<propertyName>
|
||||
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?
|
||||
// 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, {
|
||||
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
|
||||
});
|
||||
ModelClass.forEachProperty(ModelClass.registerProperty);
|
||||
|
||||
// <propertyName>$was --> __dataWas.<propertyName>
|
||||
Object.defineProperty(ModelClass.prototype, propertyName + '$was', {
|
||||
get: function () {
|
||||
return this.__dataWas && this.__dataWas[propertyName];
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
});
|
||||
ModelClass.emit('defined', ModelClass);
|
||||
|
||||
// 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
|
||||
});
|
||||
};
|
||||
|
||||
ModelClass.forEachProperty(ModelClass.registerProperty);
|
||||
|
||||
ModelClass.emit('defined', ModelClass);
|
||||
|
||||
return ModelClass;
|
||||
return ModelClass;
|
||||
|
||||
};
|
||||
|
||||
|
@ -426,8 +428,8 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
* @param {Object} propertyDefinition - property settings
|
||||
*/
|
||||
ModelBuilder.prototype.defineProperty = function (model, propertyName, propertyDefinition) {
|
||||
this.definitions[model].defineProperty(propertyName, propertyDefinition);
|
||||
this.models[model].registerProperty(propertyName);
|
||||
this.definitions[model].defineProperty(propertyName, propertyDefinition);
|
||||
this.models[model].registerProperty(propertyName);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -454,69 +456,67 @@ ModelBuilder.prototype.defineProperty = function (model, propertyName, propertyD
|
|||
* });
|
||||
*/
|
||||
ModelBuilder.prototype.extendModel = function (model, props) {
|
||||
var t = this;
|
||||
Object.keys(props).forEach(function (propName) {
|
||||
var definition = props[propName];
|
||||
t.defineProperty(model, propName, definition);
|
||||
});
|
||||
var t = this;
|
||||
Object.keys(props).forEach(function (propName) {
|
||||
var definition = props[propName];
|
||||
t.defineProperty(model, propName, definition);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
ModelBuilder.prototype.copyModel = function copyModel(Master) {
|
||||
var modelBuilder = this;
|
||||
var className = Master.modelName;
|
||||
var md = Master.modelBuilder.definitions[className];
|
||||
var Slave = function SlaveModel() {
|
||||
Master.apply(this, [].slice.call(arguments));
|
||||
var modelBuilder = this;
|
||||
var className = Master.modelName;
|
||||
var md = Master.modelBuilder.definitions[className];
|
||||
var Slave = function SlaveModel() {
|
||||
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);
|
||||
|
||||
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;
|
||||
return Slave;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* Define hidden property
|
||||
*/
|
||||
function hiddenProperty(where, property, value) {
|
||||
Object.defineProperty(where, property, {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: value
|
||||
});
|
||||
Object.defineProperty(where, property, {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the schema name
|
||||
*/
|
||||
ModelBuilder.prototype.getSchemaName = function (name) {
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
if (typeof this._nameCount !== 'number') {
|
||||
this._nameCount = 0;
|
||||
} else {
|
||||
this._nameCount++;
|
||||
}
|
||||
return 'AnonymousModel_' + this._nameCount;
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
if (typeof this._nameCount !== 'number') {
|
||||
this._nameCount = 0;
|
||||
} else {
|
||||
this._nameCount++;
|
||||
}
|
||||
return 'AnonymousModel_' + this._nameCount;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -524,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
|
||||
* @returns {Function} if the type is resolved
|
||||
*/
|
||||
ModelBuilder.prototype.resolveType = function(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;
|
||||
}
|
||||
ModelBuilder.prototype.resolveType = function (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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -574,46 +574,46 @@ ModelBuilder.prototype.resolveType = function(type) {
|
|||
* @returns {Object} A map of model constructors keyed by model name
|
||||
*/
|
||||
ModelBuilder.prototype.buildModels = function (schemas) {
|
||||
var models = {};
|
||||
var models = {};
|
||||
|
||||
// Normalize the schemas to be an array of the schema objects {name: <name>, properties: {}, options: {}}
|
||||
if (!Array.isArray(schemas)) {
|
||||
if (schemas.properties && schemas.name) {
|
||||
// Only one item
|
||||
schemas = [schemas];
|
||||
} else {
|
||||
// Anonymous schema
|
||||
schemas = [
|
||||
{
|
||||
name: this.getSchemaName(),
|
||||
properties: schemas,
|
||||
options: {anonymous: true}
|
||||
}
|
||||
];
|
||||
// Normalize the schemas to be an array of the schema objects {name: <name>, properties: {}, options: {}}
|
||||
if (!Array.isArray(schemas)) {
|
||||
if (schemas.properties && schemas.name) {
|
||||
// Only one item
|
||||
schemas = [schemas];
|
||||
} else {
|
||||
// Anonymous schema
|
||||
schemas = [
|
||||
{
|
||||
name: this.getSchemaName(),
|
||||
properties: schemas,
|
||||
options: {anonymous: true}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
var relations = [];
|
||||
for (var s in schemas) {
|
||||
var name = this.getSchemaName(schemas[s].name);
|
||||
schemas[s].name = name;
|
||||
var model = this.define(schemas[s].name, schemas[s].properties, schemas[s].options);
|
||||
models[name] = model;
|
||||
relations = relations.concat(model.definition.relations);
|
||||
}
|
||||
var relations = [];
|
||||
for (var s in schemas) {
|
||||
var name = this.getSchemaName(schemas[s].name);
|
||||
schemas[s].name = name;
|
||||
var model = this.define(schemas[s].name, schemas[s].properties, schemas[s].options);
|
||||
models[name] = model;
|
||||
relations = relations.concat(model.definition.relations);
|
||||
}
|
||||
|
||||
// Connect the models based on the relations
|
||||
for (var i = 0; i < relations.length; i++) {
|
||||
var relation = relations[i];
|
||||
var sourceModel = models[relation.source];
|
||||
var targetModel = models[relation.target];
|
||||
if (sourceModel && targetModel) {
|
||||
if(typeof sourceModel[relation.type] === 'function') {
|
||||
sourceModel[relation.type](targetModel, {as: relation.as});
|
||||
}
|
||||
}
|
||||
// Connect the models based on the relations
|
||||
for (var i = 0; i < relations.length; i++) {
|
||||
var relation = relations[i];
|
||||
var sourceModel = models[relation.source];
|
||||
var targetModel = models[relation.target];
|
||||
if (sourceModel && targetModel) {
|
||||
if (typeof sourceModel[relation.type] === 'function') {
|
||||
sourceModel[relation.type](targetModel, {as: relation.as});
|
||||
}
|
||||
}
|
||||
return models;
|
||||
}
|
||||
return models;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -623,13 +623,13 @@ ModelBuilder.prototype.buildModels = function (schemas) {
|
|||
* @param [Object} options The options
|
||||
* @returns {}
|
||||
*/
|
||||
ModelBuilder.prototype.buildModelFromInstance = function(name, json, options) {
|
||||
ModelBuilder.prototype.buildModelFromInstance = function (name, json, options) {
|
||||
|
||||
// Introspect the JSON document to generate a schema
|
||||
var schema = introspect(json);
|
||||
// Introspect the JSON document to generate a schema
|
||||
var schema = introspect(json);
|
||||
|
||||
// Create a model for the generated schema
|
||||
return this.define(name, schema, options);
|
||||
// Create a model for the generated schema
|
||||
return this.define(name, schema, options);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -21,27 +21,27 @@ module.exports = ModelDefinition;
|
|||
*
|
||||
*/
|
||||
function ModelDefinition(modelBuilder, name, properties, settings) {
|
||||
if (!(this instanceof ModelDefinition)) {
|
||||
// Allow to call ModelDefinition without new
|
||||
return new ModelDefinition(modelBuilder, name, properties, settings);
|
||||
}
|
||||
this.modelBuilder = modelBuilder || ModelBuilder.defaultInstance;
|
||||
assert(name, 'name is missing');
|
||||
if (!(this instanceof ModelDefinition)) {
|
||||
// Allow to call ModelDefinition without new
|
||||
return new ModelDefinition(modelBuilder, name, properties, settings);
|
||||
}
|
||||
this.modelBuilder = modelBuilder || ModelBuilder.defaultInstance;
|
||||
assert(name, 'name is missing');
|
||||
|
||||
if (arguments.length === 2 && typeof name === 'object') {
|
||||
var schema = name;
|
||||
this.name = schema.name;
|
||||
this.rawProperties = schema.properties || {}; // Keep the raw property definitions
|
||||
this.settings = schema.settings || {};
|
||||
} else {
|
||||
assert(typeof name === 'string', 'name must be a string');
|
||||
this.name = name;
|
||||
this.rawProperties = properties || {}; // Keep the raw property definitions
|
||||
this.settings = settings || {};
|
||||
}
|
||||
this.relations = [];
|
||||
this.properties = null;
|
||||
this.build();
|
||||
if (arguments.length === 2 && typeof name === 'object') {
|
||||
var schema = name;
|
||||
this.name = schema.name;
|
||||
this.rawProperties = schema.properties || {}; // Keep the raw property definitions
|
||||
this.settings = schema.settings || {};
|
||||
} else {
|
||||
assert(typeof name === 'string', 'name must be a string');
|
||||
this.name = name;
|
||||
this.rawProperties = properties || {}; // Keep the raw property definitions
|
||||
this.settings = settings || {};
|
||||
}
|
||||
this.relations = [];
|
||||
this.properties = null;
|
||||
this.build();
|
||||
}
|
||||
|
||||
util.inherits(ModelDefinition, EventEmitter);
|
||||
|
@ -49,18 +49,17 @@ util.inherits(ModelDefinition, EventEmitter);
|
|||
// Set up types
|
||||
require('./types')(ModelDefinition);
|
||||
|
||||
|
||||
/**
|
||||
* Return table name for specified `modelName`
|
||||
* @param {String} connectorType The connector type, such as 'oracle' or 'mongodb'
|
||||
*/
|
||||
ModelDefinition.prototype.tableName = function (connectorType) {
|
||||
var settings = this.settings;
|
||||
if(settings[connectorType]) {
|
||||
return settings[connectorType].table || settings[connectorType].tableName || this.name;
|
||||
} else {
|
||||
return this.name;
|
||||
}
|
||||
var settings = this.settings;
|
||||
if (settings[connectorType]) {
|
||||
return settings[connectorType].table || settings[connectorType].tableName || this.name;
|
||||
} else {
|
||||
return this.name;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -70,16 +69,16 @@ ModelDefinition.prototype.tableName = function (connectorType) {
|
|||
* @returns {String} columnName
|
||||
*/
|
||||
ModelDefinition.prototype.columnName = function (connectorType, propertyName) {
|
||||
if(!propertyName) {
|
||||
return propertyName;
|
||||
}
|
||||
this.build();
|
||||
var property = this.properties[propertyName];
|
||||
if(property && property[connectorType]) {
|
||||
return property[connectorType].column || property[connectorType].columnName || propertyName;
|
||||
} else {
|
||||
return propertyName;
|
||||
}
|
||||
if (!propertyName) {
|
||||
return propertyName;
|
||||
}
|
||||
this.build();
|
||||
var property = this.properties[propertyName];
|
||||
if (property && property[connectorType]) {
|
||||
return property[connectorType].column || property[connectorType].columnName || propertyName;
|
||||
} else {
|
||||
return propertyName;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -89,16 +88,16 @@ ModelDefinition.prototype.columnName = function (connectorType, propertyName) {
|
|||
* @returns {Object} column metadata
|
||||
*/
|
||||
ModelDefinition.prototype.columnMetadata = function (connectorType, propertyName) {
|
||||
if(!propertyName) {
|
||||
return propertyName;
|
||||
}
|
||||
this.build();
|
||||
var property = this.properties[propertyName];
|
||||
if(property && property[connectorType]) {
|
||||
return property[connectorType];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (!propertyName) {
|
||||
return propertyName;
|
||||
}
|
||||
this.build();
|
||||
var property = this.properties[propertyName];
|
||||
if (property && property[connectorType]) {
|
||||
return property[connectorType];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -107,17 +106,17 @@ ModelDefinition.prototype.columnMetadata = function (connectorType, propertyName
|
|||
* @returns {String[]} column names
|
||||
*/
|
||||
ModelDefinition.prototype.columnNames = function (connectorType) {
|
||||
this.build();
|
||||
var props = this.properties;
|
||||
var cols = [];
|
||||
for(var p in props) {
|
||||
if(props[p][connectorType]) {
|
||||
cols.push(property[connectorType].column || props[p][connectorType].columnName || p);
|
||||
} else {
|
||||
cols.push(p);
|
||||
}
|
||||
this.build();
|
||||
var props = this.properties;
|
||||
var cols = [];
|
||||
for (var p in props) {
|
||||
if (props[p][connectorType]) {
|
||||
cols.push(property[connectorType].column || props[p][connectorType].columnName || p);
|
||||
} else {
|
||||
cols.push(p);
|
||||
}
|
||||
return cols;
|
||||
}
|
||||
return cols;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -125,27 +124,27 @@ ModelDefinition.prototype.columnNames = function (connectorType) {
|
|||
* @returns {Object[]} property name/index for IDs
|
||||
*/
|
||||
ModelDefinition.prototype.ids = function () {
|
||||
if(this._ids) {
|
||||
return this._ids;
|
||||
if (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 = [];
|
||||
this.build();
|
||||
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});
|
||||
if (typeof id !== 'number') {
|
||||
id = 1;
|
||||
}
|
||||
ids.sort(function (a, b) {
|
||||
return a.key - b.key;
|
||||
});
|
||||
this._ids = ids;
|
||||
return ids;
|
||||
ids.push({name: key, id: id});
|
||||
}
|
||||
ids.sort(function (a, b) {
|
||||
return a.key - b.key;
|
||||
});
|
||||
this._ids = ids;
|
||||
return ids;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -153,20 +152,21 @@ ModelDefinition.prototype.ids = function () {
|
|||
* @param {String} modelName The model name
|
||||
* @returns {String} columnName for ID
|
||||
*/
|
||||
ModelDefinition.prototype.idColumnName = function(connectorType) {
|
||||
return this.columnName(connectorType, this.idName());
|
||||
ModelDefinition.prototype.idColumnName = function (connectorType) {
|
||||
return this.columnName(connectorType, this.idName());
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the ID property name
|
||||
* @returns {String} property name for ID
|
||||
*/
|
||||
ModelDefinition.prototype.idName = function() {
|
||||
var id = this.ids()[0];
|
||||
if(this.properties.id && this.properties.id.id) {
|
||||
return 'id';
|
||||
} else {}
|
||||
return id && id.name;
|
||||
ModelDefinition.prototype.idName = function () {
|
||||
var id = this.ids()[0];
|
||||
if (this.properties.id && this.properties.id.id) {
|
||||
return 'id';
|
||||
} else {
|
||||
}
|
||||
return id && id.name;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -174,11 +174,11 @@ ModelDefinition.prototype.idName = function() {
|
|||
* @returns {String[]} property names for IDs
|
||||
*/
|
||||
ModelDefinition.prototype.idNames = function () {
|
||||
var ids = this.ids();
|
||||
var names = ids.map(function (id) {
|
||||
return id.name;
|
||||
});
|
||||
return names;
|
||||
var ids = this.ids();
|
||||
var names = ids.map(function (id) {
|
||||
return id.name;
|
||||
});
|
||||
return names;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -186,19 +186,19 @@ ModelDefinition.prototype.idNames = function () {
|
|||
* @returns {{}}
|
||||
*/
|
||||
ModelDefinition.prototype.indexes = function () {
|
||||
this.build();
|
||||
var indexes = {};
|
||||
if (this.settings.indexes) {
|
||||
for (var i in this.settings.indexes) {
|
||||
indexes[i] = this.settings.indexes[i];
|
||||
}
|
||||
this.build();
|
||||
var indexes = {};
|
||||
if (this.settings.indexes) {
|
||||
for (var i in this.settings.indexes) {
|
||||
indexes[i] = this.settings.indexes[i];
|
||||
}
|
||||
for (var p in this.properties) {
|
||||
if (this.properties[p].index) {
|
||||
indexes[p + '_index'] = this.properties[p].index;
|
||||
}
|
||||
}
|
||||
for (var p in this.properties) {
|
||||
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
|
||||
*/
|
||||
ModelDefinition.prototype.build = function (forceRebuild) {
|
||||
if(forceRebuild) {
|
||||
this.properties = null;
|
||||
this.relations = [];
|
||||
this._ids = null;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (forceRebuild) {
|
||||
this.properties = null;
|
||||
this.relations = [];
|
||||
this._ids = null;
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -249,56 +249,55 @@ ModelDefinition.prototype.build = function (forceRebuild) {
|
|||
* @param {Object} propertyDefinition The property definition
|
||||
*/
|
||||
ModelDefinition.prototype.defineProperty = function (propertyName, propertyDefinition) {
|
||||
this.rawProperties[propertyName] = propertyDefinition;
|
||||
this.build(true);
|
||||
this.rawProperties[propertyName] = propertyDefinition;
|
||||
this.build(true);
|
||||
};
|
||||
|
||||
|
||||
function isModelClass(cls) {
|
||||
if(!cls) {
|
||||
return false;
|
||||
}
|
||||
return cls.prototype instanceof ModelBaseClass;
|
||||
if (!cls) {
|
||||
return false;
|
||||
}
|
||||
return cls.prototype instanceof ModelBaseClass;
|
||||
}
|
||||
|
||||
ModelDefinition.prototype.toJSON = function(forceRebuild) {
|
||||
if(forceRebuild) {
|
||||
this.json = null;
|
||||
}
|
||||
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;
|
||||
ModelDefinition.prototype.toJSON = function (forceRebuild) {
|
||||
if (forceRebuild) {
|
||||
this.json = null;
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
|
334
lib/model.js
334
lib/model.js
|
@ -6,7 +6,7 @@ module.exports = ModelBaseClass;
|
|||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
|
||||
var util = require('util');
|
||||
var traverse = require('traverse');
|
||||
var jutil = require('./jutil');
|
||||
|
@ -28,19 +28,19 @@ var BASE_TYPES = ['String', 'Boolean', 'Number', 'Date', 'Text'];
|
|||
* @param {Object} data - initial object 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
|
||||
function clone(data) {
|
||||
/*
|
||||
if(!(data instanceof ModelBaseClass)) {
|
||||
if(data && (Array.isArray(data) || 'object' === typeof data)) {
|
||||
return traverse(data).clone();
|
||||
}
|
||||
}
|
||||
*/
|
||||
return data;
|
||||
/*
|
||||
if(!(data instanceof ModelBaseClass)) {
|
||||
if(data && (Array.isArray(data) || 'object' === typeof data)) {
|
||||
return traverse(data).clone();
|
||||
}
|
||||
}
|
||||
*/
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Initialize properties
|
||||
|
@ -49,117 +49,117 @@ function clone(data) {
|
|||
* @private
|
||||
*/
|
||||
ModelBaseClass.prototype._initProperties = function (data, applySetters) {
|
||||
var self = this;
|
||||
var ctor = this.constructor;
|
||||
|
||||
var properties = ctor.definition.build();
|
||||
data = data || {};
|
||||
var self = this;
|
||||
var ctor = this.constructor;
|
||||
|
||||
Object.defineProperty(this, '__cachedRelations', {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
});
|
||||
var properties = ctor.definition.build();
|
||||
data = data || {};
|
||||
|
||||
Object.defineProperty(this, '__data', {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
});
|
||||
Object.defineProperty(this, '__cachedRelations', {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
});
|
||||
|
||||
Object.defineProperty(this, '__dataWas', {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
});
|
||||
Object.defineProperty(this, '__data', {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
});
|
||||
|
||||
if (data['__cachedRelations']) {
|
||||
this.__cachedRelations = data['__cachedRelations'];
|
||||
Object.defineProperty(this, '__dataWas', {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
});
|
||||
|
||||
if (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) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (applySetters === true) {
|
||||
for(var propertyName in data) {
|
||||
if((propertyName in properties) || (propertyName in ctor.relations)) {
|
||||
self[propertyName] = self.__data[propertyName] || data[propertyName];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function getDefault(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
|
||||
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');
|
||||
this.trigger('initialize');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -167,24 +167,24 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
|
|||
* @param {Object} params - various property configuration
|
||||
*/
|
||||
ModelBaseClass.defineProperty = function (prop, params) {
|
||||
this.dataSource.defineProperty(this.modelName, prop, params);
|
||||
this.dataSource.defineProperty(this.modelName, prop, params);
|
||||
};
|
||||
|
||||
ModelBaseClass.getPropertyType = function (propName) {
|
||||
var prop = this.definition.properties[propName];
|
||||
if(!prop) {
|
||||
// The property is not part of the definition
|
||||
return null;
|
||||
}
|
||||
if (!prop.type) {
|
||||
throw new Error('Type not defined for property ' + this.modelName + '.' + propName);
|
||||
// return null;
|
||||
}
|
||||
return prop.type.name;
|
||||
var prop = this.definition.properties[propName];
|
||||
if (!prop) {
|
||||
// The property is not part of the definition
|
||||
return null;
|
||||
}
|
||||
if (!prop.type) {
|
||||
throw new Error('Type not defined for property ' + this.modelName + '.' + propName);
|
||||
// return null;
|
||||
}
|
||||
return prop.type.name;
|
||||
};
|
||||
|
||||
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
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
ModelBaseClass.prototype.toObject = function (onlySchema) {
|
||||
var data = {};
|
||||
var self = this;
|
||||
var data = {};
|
||||
var self = this;
|
||||
|
||||
var schemaLess = this.constructor.definition.settings.strict === false || !onlySchema;
|
||||
this.constructor.forEachProperty(function (propertyName) {
|
||||
if (self[propertyName] instanceof List) {
|
||||
data[propertyName] = self[propertyName].toObject(!schemaLess);
|
||||
} else if (self.__data.hasOwnProperty(propertyName)) {
|
||||
if(self[propertyName] !== undefined && self[propertyName]!== null && self[propertyName].toObject) {
|
||||
data[propertyName] = self[propertyName].toObject(!schemaLess);
|
||||
} else {
|
||||
data[propertyName] = self[propertyName];
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
var schemaLess = this.constructor.definition.settings.strict === false || !onlySchema;
|
||||
this.constructor.forEachProperty(function (propertyName) {
|
||||
if (self[propertyName] instanceof List) {
|
||||
data[propertyName] = self[propertyName].toObject(!schemaLess);
|
||||
} else if (self.__data.hasOwnProperty(propertyName)) {
|
||||
if (self[propertyName] !== undefined && self[propertyName] !== null && self[propertyName].toObject) {
|
||||
data[propertyName] = self[propertyName].toObject(!schemaLess);
|
||||
} else {
|
||||
data[propertyName] = self[propertyName];
|
||||
}
|
||||
} else {
|
||||
data[propertyName] = null;
|
||||
}
|
||||
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) {
|
||||
|
@ -244,13 +244,13 @@ ModelBaseClass.prototype.toObject = function (onlySchema) {
|
|||
// };
|
||||
|
||||
ModelBaseClass.prototype.toJSON = function () {
|
||||
return this.toObject();
|
||||
return this.toObject();
|
||||
};
|
||||
|
||||
ModelBaseClass.prototype.fromObject = function (obj) {
|
||||
for(var key in obj) {
|
||||
this[key] = obj[key];
|
||||
}
|
||||
for (var key in obj) {
|
||||
this[key] = obj[key];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -260,7 +260,7 @@ ModelBaseClass.prototype.fromObject = function (obj) {
|
|||
* @return Boolean
|
||||
*/
|
||||
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
|
||||
*/
|
||||
ModelBaseClass.prototype.reset = function () {
|
||||
var obj = this;
|
||||
for(var k in obj) {
|
||||
if (k !== 'id' && !obj.constructor.dataSource.definitions[obj.constructor.modelName].properties[k]) {
|
||||
delete obj[k];
|
||||
}
|
||||
if (obj.propertyChanged(k)) {
|
||||
obj[k] = obj[k + '$was'];
|
||||
}
|
||||
var obj = this;
|
||||
for (var k in obj) {
|
||||
if (k !== 'id' && !obj.constructor.dataSource.definitions[obj.constructor.modelName].properties[k]) {
|
||||
delete obj[k];
|
||||
}
|
||||
if (obj.propertyChanged(k)) {
|
||||
obj[k] = obj[k + '$was'];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
return jutil.mixin(this, anotherClass, options);
|
||||
ModelBaseClass.mixin = function (anotherClass, options) {
|
||||
return jutil.mixin(this, anotherClass, options);
|
||||
};
|
||||
|
||||
ModelBaseClass.prototype.getDataSource = function () {
|
||||
|
|
451
lib/relations.js
451
lib/relations.js
|
@ -11,11 +11,11 @@ function Relation() {
|
|||
}
|
||||
|
||||
Relation.relationNameFor = function relationNameFor(foreignKey) {
|
||||
for (var rel in this.relations) {
|
||||
if (this.relations[rel].type === 'belongsTo' && this.relations[rel].keyFrom === foreignKey) {
|
||||
return rel;
|
||||
}
|
||||
for (var rel in this.relations) {
|
||||
if (this.relations[rel].type === 'belongsTo' && this.relations[rel].keyFrom === foreignKey) {
|
||||
return rel;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -26,131 +26,132 @@ Relation.relationNameFor = function relationNameFor(foreignKey) {
|
|||
* @example `User.hasMany(Post, {as: 'posts', foreignKey: 'authorId'});`
|
||||
*/
|
||||
Relation.hasMany = function hasMany(anotherClass, params) {
|
||||
var thisClassName = this.modelName;
|
||||
params = params || {};
|
||||
if (typeof anotherClass === 'string') {
|
||||
params.as = anotherClass;
|
||||
if (params.model) {
|
||||
anotherClass = params.model;
|
||||
} else {
|
||||
var anotherClassName = i8n.singularize(anotherClass).toLowerCase();
|
||||
for(var name in this.dataSource.modelBuilder.models) {
|
||||
if (name.toLowerCase() === anotherClassName) {
|
||||
anotherClass = this.dataSource.modelBuilder.models[name];
|
||||
}
|
||||
}
|
||||
var thisClassName = this.modelName;
|
||||
params = params || {};
|
||||
if (typeof anotherClass === 'string') {
|
||||
params.as = anotherClass;
|
||||
if (params.model) {
|
||||
anotherClass = params.model;
|
||||
} else {
|
||||
var anotherClassName = i8n.singularize(anotherClass).toLowerCase();
|
||||
for (var name in this.dataSource.modelBuilder.models) {
|
||||
if (name.toLowerCase() === anotherClassName) {
|
||||
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] = {
|
||||
type: 'hasMany',
|
||||
keyFrom: idName,
|
||||
keyTo: fk,
|
||||
modelTo: anotherClass,
|
||||
multiple: true
|
||||
};
|
||||
// each instance of this class should have method named
|
||||
// pluralize(anotherClass.modelName)
|
||||
// which is actually just anotherClass.find({where: {thisModelNameId: this[idName]}}, cb);
|
||||
var scopeMethods = {
|
||||
findById: find,
|
||||
destroy: destroy
|
||||
};
|
||||
if (params.through) {
|
||||
var fk2 = i8n.camelize(anotherClass.modelName + '_id', true);
|
||||
scopeMethods.create = function create(data, done) {
|
||||
if (typeof data !== 'object') {
|
||||
done = data;
|
||||
data = {};
|
||||
}
|
||||
if ('function' !== typeof done) {
|
||||
done = function() {};
|
||||
}
|
||||
var self = this;
|
||||
anotherClass.create(data, function(err, ac) {
|
||||
if (err) return done(err, ac);
|
||||
var d = {};
|
||||
d[params.through.relationNameFor(fk)] = self;
|
||||
d[params.through.relationNameFor(fk2)] = ac;
|
||||
params.through.create(d, function(e) {
|
||||
if (e) {
|
||||
ac.destroy(function() {
|
||||
done(e);
|
||||
});
|
||||
} else {
|
||||
done(err, ac);
|
||||
}
|
||||
});
|
||||
this.relations[methodName] = {
|
||||
type: 'hasMany',
|
||||
keyFrom: idName,
|
||||
keyTo: fk,
|
||||
modelTo: anotherClass,
|
||||
multiple: true
|
||||
};
|
||||
// each instance of this class should have method named
|
||||
// pluralize(anotherClass.modelName)
|
||||
// which is actually just anotherClass.find({where: {thisModelNameId: this[idName]}}, cb);
|
||||
var scopeMethods = {
|
||||
findById: find,
|
||||
destroy: destroy
|
||||
};
|
||||
if (params.through) {
|
||||
var fk2 = i8n.camelize(anotherClass.modelName + '_id', true);
|
||||
scopeMethods.create = function create(data, done) {
|
||||
if (typeof data !== 'object') {
|
||||
done = data;
|
||||
data = {};
|
||||
}
|
||||
if ('function' !== typeof done) {
|
||||
done = function () {
|
||||
};
|
||||
}
|
||||
var self = this;
|
||||
anotherClass.create(data, function (err, ac) {
|
||||
if (err) return done(err, ac);
|
||||
var d = {};
|
||||
d[params.through.relationNameFor(fk)] = self;
|
||||
d[params.through.relationNameFor(fk2)] = ac;
|
||||
params.through.create(d, function (e) {
|
||||
if (e) {
|
||||
ac.destroy(function () {
|
||||
done(e);
|
||||
});
|
||||
};
|
||||
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'));
|
||||
}
|
||||
} else {
|
||||
done(err, ac);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
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.
|
||||
*/
|
||||
Relation.belongsTo = function (anotherClass, params) {
|
||||
params = params || {};
|
||||
if ('string' === typeof anotherClass) {
|
||||
params.as = anotherClass;
|
||||
if (params.model) {
|
||||
anotherClass = params.model;
|
||||
} else {
|
||||
var anotherClassName = anotherClass.toLowerCase();
|
||||
for(var name in this.dataSource.modelBuilder.models) {
|
||||
if (name.toLowerCase() === anotherClassName) {
|
||||
anotherClass = this.dataSource.modelBuilder.models[name];
|
||||
}
|
||||
}
|
||||
params = params || {};
|
||||
if ('string' === typeof anotherClass) {
|
||||
params.as = anotherClass;
|
||||
if (params.model) {
|
||||
anotherClass = params.model;
|
||||
} else {
|
||||
var anotherClassName = anotherClass.toLowerCase();
|
||||
for (var name in this.dataSource.modelBuilder.models) {
|
||||
if (name.toLowerCase() === anotherClassName) {
|
||||
anotherClass = this.dataSource.modelBuilder.models[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var idName = this.dataSource.idName(anotherClass.modelName) || 'id';
|
||||
var methodName = params.as || i8n.camelize(anotherClass.modelName, true);
|
||||
var fk = params.foreignKey || methodName + 'Id';
|
||||
var idName = this.dataSource.idName(anotherClass.modelName) || 'id';
|
||||
var methodName = params.as || i8n.camelize(anotherClass.modelName, true);
|
||||
var fk = params.foreignKey || methodName + 'Id';
|
||||
|
||||
this.relations[methodName] = {
|
||||
type: 'belongsTo',
|
||||
keyFrom: fk,
|
||||
keyTo: idName,
|
||||
modelTo: anotherClass,
|
||||
multiple: false
|
||||
};
|
||||
this.relations[methodName] = {
|
||||
type: 'belongsTo',
|
||||
keyFrom: fk,
|
||||
keyTo: idName,
|
||||
modelTo: anotherClass,
|
||||
multiple: false
|
||||
};
|
||||
|
||||
this.dataSource.defineForeignKey(this.modelName, fk, anotherClass.modelName);
|
||||
this.prototype['__finders__'] = this.prototype['__finders__'] || {};
|
||||
this.dataSource.defineForeignKey(this.modelName, fk, anotherClass.modelName);
|
||||
this.prototype['__finders__'] = this.prototype['__finders__'] || {};
|
||||
|
||||
this.prototype['__finders__'][methodName] = function (id, cb) {
|
||||
if (id === null) {
|
||||
cb(null, null);
|
||||
return;
|
||||
}
|
||||
anotherClass.findById(id, function (err,inst) {
|
||||
if (err) return cb(err);
|
||||
if (!inst) return cb(null, null);
|
||||
if (inst[idName] === this[fk]) {
|
||||
cb(null, inst);
|
||||
} else {
|
||||
cb(new Error('Permission denied'));
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
this.prototype['__finders__'][methodName] = function (id, cb) {
|
||||
if (id === null) {
|
||||
cb(null, null);
|
||||
return;
|
||||
}
|
||||
anotherClass.findById(id, function (err, inst) {
|
||||
if (err) return cb(err);
|
||||
if (!inst) return cb(null, null);
|
||||
if (inst[idName] === this[fk]) {
|
||||
cb(null, inst);
|
||||
} else {
|
||||
cb(new Error('Permission denied'));
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
this.prototype[methodName] = function (refresh, p) {
|
||||
if (arguments.length === 1) {
|
||||
p = refresh;
|
||||
refresh = false;
|
||||
} else if (arguments.length > 2) {
|
||||
throw new Error('Method can\'t be called with more than two arguments');
|
||||
}
|
||||
var self = this;
|
||||
var cachedValue;
|
||||
if (!refresh && this.__cachedRelations && (typeof this.__cachedRelations[methodName] !== 'undefined')) {
|
||||
cachedValue = this.__cachedRelations[methodName];
|
||||
}
|
||||
if (p instanceof ModelBaseClass) { // acts as setter
|
||||
this[fk] = p[idName];
|
||||
this.__cachedRelations[methodName] = p;
|
||||
} else if (typeof p === 'function') { // acts as async getter
|
||||
if (typeof cachedValue === 'undefined') {
|
||||
this.__finders__[methodName].apply(self, [this[fk], function(err, inst) {
|
||||
if (!err) {
|
||||
self.__cachedRelations[methodName] = inst;
|
||||
}
|
||||
p(err, inst);
|
||||
}]);
|
||||
return this[fk];
|
||||
} else {
|
||||
p(null, cachedValue);
|
||||
return cachedValue;
|
||||
}
|
||||
} else if (typeof p === 'undefined') { // acts as sync getter
|
||||
return this[fk];
|
||||
} else { // setter
|
||||
this[fk] = p;
|
||||
delete this.__cachedRelations[methodName];
|
||||
}
|
||||
};
|
||||
this.prototype[methodName] = function (refresh, p) {
|
||||
if (arguments.length === 1) {
|
||||
p = refresh;
|
||||
refresh = false;
|
||||
} else if (arguments.length > 2) {
|
||||
throw new Error('Method can\'t be called with more than two arguments');
|
||||
}
|
||||
var self = this;
|
||||
var cachedValue;
|
||||
if (!refresh && this.__cachedRelations && (typeof this.__cachedRelations[methodName] !== 'undefined')) {
|
||||
cachedValue = this.__cachedRelations[methodName];
|
||||
}
|
||||
if (p instanceof ModelBaseClass) { // acts as setter
|
||||
this[fk] = p[idName];
|
||||
this.__cachedRelations[methodName] = p;
|
||||
} else if (typeof p === 'function') { // acts as async getter
|
||||
if (typeof cachedValue === 'undefined') {
|
||||
this.__finders__[methodName].apply(self, [this[fk], function (err, inst) {
|
||||
if (!err) {
|
||||
self.__cachedRelations[methodName] = inst;
|
||||
}
|
||||
p(err, inst);
|
||||
}]);
|
||||
return this[fk];
|
||||
} else {
|
||||
p(null, cachedValue);
|
||||
return cachedValue;
|
||||
}
|
||||
} else if (typeof p === 'undefined') { // acts as sync getter
|
||||
return this[fk];
|
||||
} else { // setter
|
||||
this[fk] = p;
|
||||
delete this.__cachedRelations[methodName];
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
@ -268,40 +269,40 @@ Relation.belongsTo = function (anotherClass, params) {
|
|||
* Post.hasAndBelongsToMany('tags'); creates connection model 'PostTag'
|
||||
*/
|
||||
Relation.hasAndBelongsToMany = function hasAndBelongsToMany(anotherClass, params) {
|
||||
params = params || {};
|
||||
var models = this.dataSource.modelBuilder.models;
|
||||
params = params || {};
|
||||
var models = this.dataSource.modelBuilder.models;
|
||||
|
||||
if ('string' === typeof anotherClass) {
|
||||
params.as = anotherClass;
|
||||
if (params.model) {
|
||||
anotherClass = params.model;
|
||||
} else {
|
||||
anotherClass = lookupModel(i8n.singularize(anotherClass)) ||
|
||||
anotherClass;
|
||||
}
|
||||
if (typeof anotherClass === 'string') {
|
||||
throw new Error('Could not find "' + anotherClass + '" relation for ' + this.modelName);
|
||||
}
|
||||
if ('string' === typeof anotherClass) {
|
||||
params.as = anotherClass;
|
||||
if (params.model) {
|
||||
anotherClass = params.model;
|
||||
} else {
|
||||
anotherClass = lookupModel(i8n.singularize(anotherClass)) ||
|
||||
anotherClass;
|
||||
}
|
||||
|
||||
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);
|
||||
if (typeof anotherClass === 'string') {
|
||||
throw new Error('Could not find "' + anotherClass + '" relation for ' + this.modelName);
|
||||
}
|
||||
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) {
|
||||
var lookupClassName = modelName.toLowerCase();
|
||||
for (var name in models) {
|
||||
if (name.toLowerCase() === lookupClassName) {
|
||||
return models[name];
|
||||
}
|
||||
}
|
||||
this.hasMany(anotherClass, {as: params.as, through: params.through});
|
||||
|
||||
function lookupModel(modelName) {
|
||||
var lookupClassName = modelName.toLowerCase();
|
||||
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) {
|
||||
|
||||
// collect meta info about scope
|
||||
if (!cls._scopeMeta) {
|
||||
cls._scopeMeta = {};
|
||||
}
|
||||
// collect meta info about scope
|
||||
if (!cls._scopeMeta) {
|
||||
cls._scopeMeta = {};
|
||||
}
|
||||
|
||||
// only makes sence to add scope in meta if base and target classes
|
||||
// are same
|
||||
if (cls === targetClass) {
|
||||
cls._scopeMeta[name] = params;
|
||||
} else {
|
||||
if (!targetClass._scopeMeta) {
|
||||
targetClass._scopeMeta = {};
|
||||
// only makes sence to add scope in meta if base and target classes
|
||||
// are same
|
||||
if (cls === targetClass) {
|
||||
cls._scopeMeta[name] = params;
|
||||
} else {
|
||||
if (!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
|
||||
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');
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
// define sub-scopes
|
||||
Object.keys(targetClass._scopeMeta).forEach(function (name) {
|
||||
Object.defineProperty(f, name, {
|
||||
enumerable: false,
|
||||
get: function () {
|
||||
mergeParams(f._scope, targetClass._scopeMeta[name]);
|
||||
return f;
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
f.build = build;
|
||||
f.create = create;
|
||||
f.destroyAll = destroyAll;
|
||||
for (var i in methods) {
|
||||
f[i] = methods[i].bind(this);
|
||||
}
|
||||
|
||||
// define sub-scopes
|
||||
Object.keys(targetClass._scopeMeta).forEach(function (name) {
|
||||
Object.defineProperty(f, name, {
|
||||
enumerable: false,
|
||||
get: function () {
|
||||
mergeParams(f._scope, targetClass._scopeMeta[name]);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
// overwrite order
|
||||
if (update.order) {
|
||||
base.order = update.order;
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
// overwrite order
|
||||
if (update.order) {
|
||||
base.order = update.order;
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -200,12 +200,12 @@ function defineScope(cls, targetClass, name, params, methods) {
|
|||
* @returns {Object} `base`
|
||||
*/
|
||||
function merge(base, update) {
|
||||
base = base || {};
|
||||
if (update) {
|
||||
Object.keys(update).forEach(function (key) {
|
||||
base[key] = update[key];
|
||||
});
|
||||
}
|
||||
return base;
|
||||
base = base || {};
|
||||
if (update) {
|
||||
Object.keys(update).forEach(function (key) {
|
||||
base[key] = update[key];
|
||||
});
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
|
|
249
lib/sql.js
249
lib/sql.js
|
@ -8,7 +8,7 @@ module.exports = BaseSQL;
|
|||
* @constructor
|
||||
*/
|
||||
function BaseSQL() {
|
||||
Connector.apply(this, [].slice.call(arguments));
|
||||
Connector.apply(this, [].slice.call(arguments));
|
||||
}
|
||||
|
||||
util.inherits(BaseSQL, Connector);
|
||||
|
@ -20,33 +20,32 @@ util.inherits(BaseSQL, Connector);
|
|||
BaseSQL.prototype.relational = true;
|
||||
|
||||
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) {
|
||||
return this.query(sql, params, callback);
|
||||
return this.query(sql, params, callback);
|
||||
};
|
||||
|
||||
BaseSQL.prototype.queryOne = function (sql, callback) {
|
||||
return this.query(sql, function (err, data) {
|
||||
if (err) return callback(err);
|
||||
callback(err, data[0]);
|
||||
});
|
||||
return this.query(sql, function (err, data) {
|
||||
if (err) return callback(err);
|
||||
callback(err, data[0]);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the table name for a given model
|
||||
* @param {String} model The model name
|
||||
* @returns {String} The table name
|
||||
*/
|
||||
BaseSQL.prototype.table = function (model) {
|
||||
var name = this.getDataSource(model).tableName(model);
|
||||
var dbName = this.dbName;
|
||||
if(typeof dbName === 'function') {
|
||||
name = dbName(name);
|
||||
}
|
||||
return name;
|
||||
var name = this.getDataSource(model).tableName(model);
|
||||
var dbName = this.dbName;
|
||||
if (typeof dbName === 'function') {
|
||||
name = dbName(name);
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -56,12 +55,12 @@ BaseSQL.prototype.table = function (model) {
|
|||
* @returns {String} The column name
|
||||
*/
|
||||
BaseSQL.prototype.column = function (model, property) {
|
||||
var name = this.getDataSource(model).columnName(model, property);
|
||||
var dbName = this.dbName;
|
||||
if(typeof dbName === 'function') {
|
||||
name = dbName(name);
|
||||
}
|
||||
return name;
|
||||
var name = this.getDataSource(model).columnName(model, property);
|
||||
var dbName = this.dbName;
|
||||
if (typeof dbName === 'function') {
|
||||
name = dbName(name);
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -71,7 +70,7 @@ BaseSQL.prototype.column = function (model, property) {
|
|||
* @returns {Object} The column metadata
|
||||
*/
|
||||
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
|
||||
*/
|
||||
BaseSQL.prototype.propertyName = function (model, column) {
|
||||
var props = this._models[model].properties;
|
||||
for(var p in props) {
|
||||
if(this.column(model, p) === column) {
|
||||
return p;
|
||||
}
|
||||
var props = this._models[model].properties;
|
||||
for (var p in props) {
|
||||
if (this.column(model, p) === column) {
|
||||
return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -96,12 +95,13 @@ BaseSQL.prototype.propertyName = function (model, column) {
|
|||
* @returns {String} The column name
|
||||
*/
|
||||
BaseSQL.prototype.idColumn = function (model) {
|
||||
var name = this.getDataSource(model).idColumnName(model);;
|
||||
var dbName = this.dbName;
|
||||
if(typeof dbName === 'function') {
|
||||
name = dbName(name);
|
||||
}
|
||||
return name;
|
||||
var name = this.getDataSource(model).idColumnName(model);
|
||||
;
|
||||
var dbName = this.dbName;
|
||||
if (typeof dbName === 'function') {
|
||||
name = dbName(name);
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -110,7 +110,7 @@ BaseSQL.prototype.idColumn = function (model) {
|
|||
* @returns {String} the escaped id column name
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
BaseSQL.prototype.save = function (model, data, callback) {
|
||||
var sql = 'UPDATE ' + this.tableEscaped(model) + ' SET '
|
||||
+ this.toFields(model, data) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + Number(data.id);
|
||||
var sql = 'UPDATE ' + this.tableEscaped(model) + ' SET '
|
||||
+ this.toFields(model, data) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + Number(data.id);
|
||||
|
||||
this.query(sql, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
this.query(sql, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check if a model instance exists for the given id value
|
||||
* @param {String} model The model name
|
||||
|
@ -163,13 +162,13 @@ BaseSQL.prototype.save = function (model, data, callback) {
|
|||
* @param {Function} callback The callback function
|
||||
*/
|
||||
BaseSQL.prototype.exists = function (model, id, callback) {
|
||||
var sql = 'SELECT 1 FROM ' +
|
||||
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + Number(id) + ' LIMIT 1';
|
||||
var sql = 'SELECT 1 FROM ' +
|
||||
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + Number(id) + ' LIMIT 1';
|
||||
|
||||
this.query(sql, function (err, data) {
|
||||
if (err) return callback(err);
|
||||
callback(null, data.length === 1);
|
||||
});
|
||||
this.query(sql, function (err, data) {
|
||||
if (err) return callback(err);
|
||||
callback(null, data.length === 1);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -179,17 +178,17 @@ BaseSQL.prototype.exists = function (model, id, callback) {
|
|||
* @param {Function} callback The callback function
|
||||
*/
|
||||
BaseSQL.prototype.find = function find(model, id, callback) {
|
||||
var sql = 'SELECT * FROM ' +
|
||||
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + id + ' LIMIT 1';
|
||||
var sql = 'SELECT * FROM ' +
|
||||
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + id + ' LIMIT 1';
|
||||
|
||||
this.query(sql, function (err, data) {
|
||||
if (data && data.length === 1) {
|
||||
data[0].id = id;
|
||||
} else {
|
||||
data = [null];
|
||||
}
|
||||
callback(err, this.fromDatabase(model, data[0]));
|
||||
}.bind(this));
|
||||
this.query(sql, function (err, data) {
|
||||
if (data && data.length === 1) {
|
||||
data[0].id = id;
|
||||
} else {
|
||||
data = [null];
|
||||
}
|
||||
callback(err, this.fromDatabase(model, data[0]));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -199,12 +198,12 @@ BaseSQL.prototype.find = function find(model, id, callback) {
|
|||
* @param {Function} callback The callback function
|
||||
*/
|
||||
BaseSQL.prototype.delete = BaseSQL.prototype.destroy = function destroy(model, id, callback) {
|
||||
var sql = 'DELETE FROM ' +
|
||||
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + id;
|
||||
var sql = 'DELETE FROM ' +
|
||||
this.tableEscaped(model) + ' WHERE ' + this.idColumnEscaped(model) + ' = ' + id;
|
||||
|
||||
this.command(sql, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
this.command(sql, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -214,12 +213,12 @@ BaseSQL.prototype.delete = BaseSQL.prototype.destroy = function destroy(model, i
|
|||
* @param {Function} callback The callback function
|
||||
*/
|
||||
BaseSQL.prototype.deleteAll = BaseSQL.prototype.destroyAll = function destroyAll(model, callback) {
|
||||
this.command('DELETE FROM ' + this.tableEscaped(model), function (err) {
|
||||
if (err) {
|
||||
return callback(err, []);
|
||||
}
|
||||
callback(err);
|
||||
}.bind(this));
|
||||
this.command('DELETE FROM ' + this.tableEscaped(model), function (err) {
|
||||
if (err) {
|
||||
return callback(err, []);
|
||||
}
|
||||
callback(err);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -230,27 +229,27 @@ BaseSQL.prototype.deleteAll = BaseSQL.prototype.destroyAll = function destroyAll
|
|||
* @param {Object} where The where clause
|
||||
*/
|
||||
BaseSQL.prototype.count = function count(model, callback, where) {
|
||||
var self = this;
|
||||
var props = this._models[model].properties;
|
||||
var self = this;
|
||||
var props = this._models[model].properties;
|
||||
|
||||
this.queryOne('SELECT count(*) as cnt FROM ' +
|
||||
this.tableEscaped(model) + ' ' + buildWhere(where), function (err, res) {
|
||||
if (err) return callback(err);
|
||||
callback(err, res && res.cnt);
|
||||
this.queryOne('SELECT count(*) as cnt FROM ' +
|
||||
this.tableEscaped(model) + ' ' + buildWhere(where), function (err, res) {
|
||||
if (err) return callback(err);
|
||||
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]));
|
||||
}
|
||||
});
|
||||
|
||||
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 ') : '';
|
||||
}
|
||||
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
|
||||
*/
|
||||
BaseSQL.prototype.updateAttributes = function updateAttrs(model, id, data, cb) {
|
||||
data.id = id;
|
||||
this.save(model, data, cb);
|
||||
data.id = id;
|
||||
this.save(model, data, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Disconnect from the connector
|
||||
*/
|
||||
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
|
||||
*/
|
||||
BaseSQL.prototype.automigrate = function (models, cb) {
|
||||
var self = this;
|
||||
var wait = 0;
|
||||
if ((!cb) && ('function' === typeof models)) {
|
||||
cb = models;
|
||||
models = undefined;
|
||||
}
|
||||
// First argument is a model name
|
||||
if ('string' === typeof models) {
|
||||
models = [models];
|
||||
}
|
||||
var self = this;
|
||||
var wait = 0;
|
||||
if ((!cb) && ('function' === typeof models)) {
|
||||
cb = models;
|
||||
models = undefined;
|
||||
}
|
||||
// First argument is a model name
|
||||
if ('string' === typeof models) {
|
||||
models = [models];
|
||||
}
|
||||
|
||||
models = models || Object.keys(this._models);
|
||||
models.forEach(function (model) {
|
||||
if (model in self._models) {
|
||||
wait++;
|
||||
self.dropTable(model, function () {
|
||||
// console.log('drop', model);
|
||||
self.createTable(model, function (err) {
|
||||
// console.log('create', model);
|
||||
if (err) console.log(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
if (wait === 0) cb();
|
||||
|
||||
function done() {
|
||||
if (--wait === 0 && cb) {
|
||||
cb();
|
||||
}
|
||||
models = models || Object.keys(this._models);
|
||||
models.forEach(function (model) {
|
||||
if (model in self._models) {
|
||||
wait++;
|
||||
self.dropTable(model, function () {
|
||||
// console.log('drop', model);
|
||||
self.createTable(model, function (err) {
|
||||
// console.log('create', model);
|
||||
if (err) console.log(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
this.command('CREATE TABLE ' + this.tableEscaped(model) +
|
||||
' (\n ' + this.propertiesSQL(model) + '\n)', cb);
|
||||
this.command('CREATE TABLE ' + this.tableEscaped(model) +
|
||||
' (\n ' + this.propertiesSQL(model) + '\n)', cb);
|
||||
};
|
||||
|
||||
|
|
104
lib/types.js
104
lib/types.js
|
@ -1,61 +1,61 @@
|
|||
module.exports = function (Types) {
|
||||
|
||||
var List = require('./list.js');
|
||||
var GeoPoint = require('./geo').GeoPoint;
|
||||
var List = require('./list.js');
|
||||
var GeoPoint = require('./geo').GeoPoint;
|
||||
|
||||
/**
|
||||
* Schema types
|
||||
*/
|
||||
Types.Text = function Text(value) {
|
||||
if (!(this instanceof Text)) {
|
||||
return value;
|
||||
}
|
||||
this.value = value;
|
||||
}; // Text type
|
||||
/**
|
||||
* Schema types
|
||||
*/
|
||||
Types.Text = function Text(value) {
|
||||
if (!(this instanceof Text)) {
|
||||
return value;
|
||||
}
|
||||
this.value = value;
|
||||
}; // Text type
|
||||
|
||||
Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function () {
|
||||
return this.value;
|
||||
};
|
||||
Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function () {
|
||||
return this.value;
|
||||
};
|
||||
|
||||
Types.JSON = function JSON(value) {
|
||||
if (!(this instanceof JSON)) {
|
||||
return value;
|
||||
}
|
||||
this.value = value;
|
||||
}; // JSON Object
|
||||
Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function () {
|
||||
return this.value;
|
||||
};
|
||||
Types.JSON = function JSON(value) {
|
||||
if (!(this instanceof JSON)) {
|
||||
return value;
|
||||
}
|
||||
this.value = value;
|
||||
}; // JSON Object
|
||||
Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function () {
|
||||
return this.value;
|
||||
};
|
||||
|
||||
Types.Any = function Any(value) {
|
||||
if (!(this instanceof Any)) {
|
||||
return value;
|
||||
}
|
||||
this.value = value;
|
||||
}; // Any Type
|
||||
Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function () {
|
||||
return this.value;
|
||||
};
|
||||
Types.Any = function Any(value) {
|
||||
if (!(this instanceof Any)) {
|
||||
return value;
|
||||
}
|
||||
this.value = value;
|
||||
}; // Any Type
|
||||
Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function () {
|
||||
return this.value;
|
||||
};
|
||||
|
||||
Types.schemaTypes = {};
|
||||
Types.registerType = function (type, names) {
|
||||
names = names || [];
|
||||
names = names.concat([type.name]);
|
||||
for (var n = 0; n < names.length; n++) {
|
||||
this.schemaTypes[names[n].toLowerCase()] = type;
|
||||
}
|
||||
};
|
||||
Types.schemaTypes = {};
|
||||
Types.registerType = function (type, names) {
|
||||
names = names || [];
|
||||
names = names.concat([type.name]);
|
||||
for (var n = 0; n < names.length; n++) {
|
||||
this.schemaTypes[names[n].toLowerCase()] = type;
|
||||
}
|
||||
};
|
||||
|
||||
Types.registerType(Types.Text);
|
||||
Types.registerType(Types.JSON);
|
||||
Types.registerType(Types.Any);
|
||||
Types.registerType(Types.Text);
|
||||
Types.registerType(Types.JSON);
|
||||
Types.registerType(Types.Any);
|
||||
|
||||
Types.registerType(String);
|
||||
Types.registerType(Number);
|
||||
Types.registerType(Boolean);
|
||||
Types.registerType(Date);
|
||||
Types.registerType(Buffer, ['Binary']);
|
||||
Types.registerType(Array);
|
||||
Types.registerType(GeoPoint);
|
||||
Types.registerType(Object);
|
||||
}
|
||||
Types.registerType(String);
|
||||
Types.registerType(Number);
|
||||
Types.registerType(Boolean);
|
||||
Types.registerType(Date);
|
||||
Types.registerType(Buffer, ['Binary']);
|
||||
Types.registerType(Array);
|
||||
Types.registerType(GeoPoint);
|
||||
Types.registerType(Object);
|
||||
};
|
161
lib/utils.js
161
lib/utils.js
|
@ -8,54 +8,56 @@ exports.mergeSettings = mergeSettings;
|
|||
var traverse = require('traverse');
|
||||
|
||||
function safeRequire(module) {
|
||||
try {
|
||||
return require(module);
|
||||
} catch (e) {
|
||||
console.log('Run "npm install loopback-datasource-juggler ' + module + '" command to use loopback-datasource-juggler using ' + module + ' database engine');
|
||||
process.exit(1);
|
||||
}
|
||||
try {
|
||||
return require(module);
|
||||
} catch (e) {
|
||||
console.log('Run "npm install loopback-datasource-juggler ' + module
|
||||
+ '" command to use loopback-datasource-juggler using ' + module
|
||||
+ ' database engine');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function fieldsToArray(fields, properties) {
|
||||
if(!fields) return;
|
||||
|
||||
// include all properties by default
|
||||
var result = properties;
|
||||
|
||||
if(typeof fields === 'string') {
|
||||
return [fields];
|
||||
if (!fields) return;
|
||||
|
||||
// include all properties by default
|
||||
var result = properties;
|
||||
|
||||
if (typeof fields === 'string') {
|
||||
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) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function selectFields(fields) {
|
||||
|
@ -63,10 +65,10 @@ function selectFields(fields) {
|
|||
return function (obj) {
|
||||
var result = {};
|
||||
var key;
|
||||
|
||||
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
key = fields[i];
|
||||
|
||||
|
||||
result[key] = obj[key];
|
||||
}
|
||||
return result;
|
||||
|
@ -79,24 +81,25 @@ function selectFields(fields) {
|
|||
* @returns {exports.map|*}
|
||||
*/
|
||||
function removeUndefined(query) {
|
||||
if (typeof query !== 'object' || query === null) {
|
||||
return query;
|
||||
if (typeof query !== 'object' || query === null) {
|
||||
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)) {
|
||||
// This object is not a plain object
|
||||
this.update(x, true); // Stop navigating into this object
|
||||
return x;
|
||||
}
|
||||
if (!Array.isArray(x) && (typeof x === 'object' && x !== null
|
||||
&& x.constructor !== Object)) {
|
||||
// This object is not a plain object
|
||||
this.update(x, true); // Stop navigating into this object
|
||||
return x;
|
||||
}
|
||||
|
||||
return x;
|
||||
});
|
||||
return x;
|
||||
});
|
||||
}
|
||||
|
||||
var url = require('url');
|
||||
|
@ -108,25 +111,25 @@ var qs = require('qs');
|
|||
* @returns {Object} The settings object
|
||||
*/
|
||||
function parseSettings(urlStr) {
|
||||
if(!urlStr) {
|
||||
return {};
|
||||
if (!urlStr) {
|
||||
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 = {};
|
||||
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;
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,7 +26,7 @@ function Validatable() {
|
|||
|
||||
/**
|
||||
* Validate presence. This validation fails when validated field is blank.
|
||||
*
|
||||
*
|
||||
* Default error message "can't be blank"
|
||||
*
|
||||
* @example presence of title
|
||||
|
@ -96,7 +96,7 @@ Validatable.validatesNumericalityOf = getConfigurator('numericality');
|
|||
/**
|
||||
* Validate inclusion in set
|
||||
*
|
||||
* @example
|
||||
* @example
|
||||
* ```
|
||||
* User.validatesInclusionOf('gender', {in: ['male', 'female']});
|
||||
* User.validatesInclusionOf('role', {
|
||||
|
@ -203,121 +203,121 @@ Validatable.validatesUniquenessOf = getConfigurator('uniqueness', {async: true})
|
|||
* Presence validator
|
||||
*/
|
||||
function validatePresence(attr, conf, err) {
|
||||
if (blank(this[attr])) {
|
||||
err();
|
||||
}
|
||||
if (blank(this[attr])) {
|
||||
err();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Length validator
|
||||
*/
|
||||
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;
|
||||
if (conf.min && len < conf.min) {
|
||||
err('min');
|
||||
}
|
||||
if (conf.max && len > conf.max) {
|
||||
err('max');
|
||||
}
|
||||
if (conf.is && len !== conf.is) {
|
||||
err('is');
|
||||
}
|
||||
var len = this[attr].length;
|
||||
if (conf.min && len < conf.min) {
|
||||
err('min');
|
||||
}
|
||||
if (conf.max && len > conf.max) {
|
||||
err('max');
|
||||
}
|
||||
if (conf.is && len !== conf.is) {
|
||||
err('is');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Numericality validator
|
||||
*/
|
||||
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') {
|
||||
return err('number');
|
||||
}
|
||||
if (conf.int && this[attr] !== Math.round(this[attr])) {
|
||||
return err('int');
|
||||
}
|
||||
if (typeof this[attr] !== 'number') {
|
||||
return err('number');
|
||||
}
|
||||
if (conf.int && this[attr] !== Math.round(this[attr])) {
|
||||
return err('int');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inclusion validator
|
||||
*/
|
||||
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])) {
|
||||
err()
|
||||
}
|
||||
if (!~conf.in.indexOf(this[attr])) {
|
||||
err()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclusion validator
|
||||
*/
|
||||
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])) {
|
||||
err()
|
||||
}
|
||||
if (~conf.in.indexOf(this[attr])) {
|
||||
err()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format validator
|
||||
*/
|
||||
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 (!this[attr].match(conf['with'])) {
|
||||
err();
|
||||
}
|
||||
} else {
|
||||
err();
|
||||
if (typeof this[attr] === 'string') {
|
||||
if (!this[attr].match(conf['with'])) {
|
||||
err();
|
||||
}
|
||||
} else {
|
||||
err();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validator
|
||||
*/
|
||||
function validateCustom(attr, conf, err, done) {
|
||||
conf.customValidator.call(this, err, done);
|
||||
conf.customValidator.call(this, err, done);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniqueness validator
|
||||
*/
|
||||
function validateUniqueness(attr, conf, err, done) {
|
||||
var cond = {where: {}};
|
||||
cond.where[attr] = this[attr];
|
||||
this.constructor.find(cond, function (error, found) {
|
||||
if (error) {
|
||||
return err();
|
||||
}
|
||||
if (found.length > 1) {
|
||||
err();
|
||||
} else if (found.length === 1 && (!this.id || !found[0].id || found[0].id.toString() != this.id.toString())) {
|
||||
err();
|
||||
}
|
||||
done();
|
||||
}.bind(this));
|
||||
var cond = {where: {}};
|
||||
cond.where[attr] = this[attr];
|
||||
this.constructor.find(cond, function (error, found) {
|
||||
if (error) {
|
||||
return err();
|
||||
}
|
||||
if (found.length > 1) {
|
||||
err();
|
||||
} else if (found.length === 1 && (!this.id || !found[0].id || found[0].id.toString() != this.id.toString())) {
|
||||
err();
|
||||
}
|
||||
done();
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
var validators = {
|
||||
presence: validatePresence,
|
||||
length: validateLength,
|
||||
numericality: validateNumericality,
|
||||
inclusion: validateInclusion,
|
||||
exclusion: validateExclusion,
|
||||
format: validateFormat,
|
||||
custom: validateCustom,
|
||||
uniqueness: validateUniqueness
|
||||
presence: validatePresence,
|
||||
length: validateLength,
|
||||
numericality: validateNumericality,
|
||||
inclusion: validateInclusion,
|
||||
exclusion: validateExclusion,
|
||||
format: validateFormat,
|
||||
custom: validateCustom,
|
||||
uniqueness: validateUniqueness
|
||||
};
|
||||
|
||||
function getConfigurator(name, opts) {
|
||||
return function () {
|
||||
configure(this, name, arguments, opts);
|
||||
};
|
||||
return function () {
|
||||
configure(this, name, arguments, opts);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -341,193 +341,193 @@ function getConfigurator(name, opts) {
|
|||
* ```
|
||||
*/
|
||||
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
|
||||
if (!this.constructor._validations) {
|
||||
cleanErrors(this);
|
||||
if (callback) {
|
||||
this.trigger('validate', function (validationsDone) {
|
||||
validationsDone.call(inst, function() {
|
||||
callback(valid);
|
||||
});
|
||||
});
|
||||
}
|
||||
return valid;
|
||||
// exit with success when no errors
|
||||
if (!this.constructor._validations) {
|
||||
cleanErrors(this);
|
||||
if (callback) {
|
||||
this.trigger('validate', function (validationsDone) {
|
||||
validationsDone.call(inst, function () {
|
||||
callback(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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (!async) {
|
||||
validationsDone.call(inst, function() {
|
||||
if (valid) cleanErrors(inst);
|
||||
if (callback) {
|
||||
callback(valid);
|
||||
}
|
||||
});
|
||||
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) {
|
||||
Object.defineProperty(inst, 'errors', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: false
|
||||
});
|
||||
Object.defineProperty(inst, 'errors', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: false
|
||||
});
|
||||
}
|
||||
|
||||
function validationFailed(inst, v, cb) {
|
||||
var attr = v[0];
|
||||
var conf = v[1];
|
||||
var opts = v[2] || {};
|
||||
var attr = v[0];
|
||||
var conf = v[1];
|
||||
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)
|
||||
// that can be specified in conf
|
||||
if (skipValidation(inst, conf, 'if')) return false;
|
||||
if (skipValidation(inst, conf, 'unless')) return false;
|
||||
// here we should check skip validation conditions (if, unless)
|
||||
// that can be specified in conf
|
||||
if (skipValidation(inst, conf, 'if')) return false;
|
||||
if (skipValidation(inst, conf, 'unless')) return false;
|
||||
|
||||
var fail = false;
|
||||
var validator = validators[conf.validation];
|
||||
var validatorArguments = [];
|
||||
validatorArguments.push(attr);
|
||||
validatorArguments.push(conf);
|
||||
validatorArguments.push(function onerror(kind) {
|
||||
var message, code = conf.validation;
|
||||
if (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);
|
||||
});
|
||||
var fail = false;
|
||||
var validator = validators[conf.validation];
|
||||
var validatorArguments = [];
|
||||
validatorArguments.push(attr);
|
||||
validatorArguments.push(conf);
|
||||
validatorArguments.push(function onerror(kind) {
|
||||
var message, code = conf.validation;
|
||||
if (conf.message) {
|
||||
message = conf.message;
|
||||
}
|
||||
validator.apply(inst, validatorArguments);
|
||||
return fail;
|
||||
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);
|
||||
return fail;
|
||||
}
|
||||
|
||||
function skipValidation(inst, conf, kind) {
|
||||
var doValidate = true;
|
||||
if (typeof conf[kind] === 'function') {
|
||||
doValidate = conf[kind].call(inst);
|
||||
if (kind === 'unless') doValidate = !doValidate;
|
||||
} else if (typeof conf[kind] === 'string') {
|
||||
if (typeof inst[conf[kind]] === 'function') {
|
||||
doValidate = inst[conf[kind]].call(inst);
|
||||
if (kind === 'unless') doValidate = !doValidate;
|
||||
} else if (inst.__data.hasOwnProperty(conf[kind])) {
|
||||
doValidate = inst[conf[kind]];
|
||||
if (kind === 'unless') doValidate = !doValidate;
|
||||
} else {
|
||||
doValidate = kind === 'if';
|
||||
}
|
||||
var doValidate = true;
|
||||
if (typeof conf[kind] === 'function') {
|
||||
doValidate = conf[kind].call(inst);
|
||||
if (kind === 'unless') doValidate = !doValidate;
|
||||
} else if (typeof conf[kind] === 'string') {
|
||||
if (typeof inst[conf[kind]] === 'function') {
|
||||
doValidate = inst[conf[kind]].call(inst);
|
||||
if (kind === 'unless') doValidate = !doValidate;
|
||||
} else if (inst.__data.hasOwnProperty(conf[kind])) {
|
||||
doValidate = inst[conf[kind]];
|
||||
if (kind === 'unless') doValidate = !doValidate;
|
||||
} else {
|
||||
doValidate = kind === 'if';
|
||||
}
|
||||
return !doValidate;
|
||||
}
|
||||
return !doValidate;
|
||||
}
|
||||
|
||||
var defaultMessages = {
|
||||
presence: 'can\'t be blank',
|
||||
length: {
|
||||
min: 'too short',
|
||||
max: 'too long',
|
||||
is: 'length is wrong'
|
||||
},
|
||||
common: {
|
||||
blank: 'is blank',
|
||||
'null': 'is null'
|
||||
},
|
||||
numericality: {
|
||||
'int': 'is not an integer',
|
||||
'number': 'is not a number'
|
||||
},
|
||||
inclusion: 'is not included in the list',
|
||||
exclusion: 'is reserved',
|
||||
uniqueness: 'is not unique'
|
||||
presence: 'can\'t be blank',
|
||||
length: {
|
||||
min: 'too short',
|
||||
max: 'too long',
|
||||
is: 'length is wrong'
|
||||
},
|
||||
common: {
|
||||
blank: 'is blank',
|
||||
'null': 'is null'
|
||||
},
|
||||
numericality: {
|
||||
'int': 'is not an integer',
|
||||
'number': 'is not a number'
|
||||
},
|
||||
inclusion: 'is not included in the list',
|
||||
exclusion: 'is reserved',
|
||||
uniqueness: 'is not unique'
|
||||
};
|
||||
|
||||
function nullCheck(attr, conf, err) {
|
||||
var isNull = this[attr] === null || !(attr in this);
|
||||
if (isNull) {
|
||||
if (!conf.allowNull) {
|
||||
err('null');
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (blank(this[attr])) {
|
||||
if (!conf.allowBlank) {
|
||||
err('blank');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
var isNull = this[attr] === null || !(attr in this);
|
||||
if (isNull) {
|
||||
if (!conf.allowNull) {
|
||||
err('null');
|
||||
}
|
||||
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
|
||||
*/
|
||||
function blank(v) {
|
||||
if (typeof v === 'undefined') return true;
|
||||
if (v instanceof Array && v.length === 0) return true;
|
||||
if (v === null) return true;
|
||||
if (typeof v == 'string' && v === '') return true;
|
||||
return false;
|
||||
if (typeof v === 'undefined') return true;
|
||||
if (v instanceof Array && v.length === 0) return true;
|
||||
if (v === null) return true;
|
||||
if (typeof v == 'string' && v === '') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function configure(cls, validation, args, opts) {
|
||||
if (!cls._validations) {
|
||||
Object.defineProperty(cls, '_validations', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
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]);
|
||||
if (!cls._validations) {
|
||||
Object.defineProperty(cls, '_validations', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
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]);
|
||||
});
|
||||
}
|
||||
|
||||
function Errors() {
|
||||
Object.defineProperty(this, 'codes', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
});
|
||||
Object.defineProperty(this, 'codes', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: {}
|
||||
});
|
||||
}
|
||||
|
||||
Errors.prototype.add = function (field, message, code) {
|
||||
code = code || 'invalid';
|
||||
if (!this[field]) {
|
||||
this[field] = [];
|
||||
this.codes[field] = [];
|
||||
}
|
||||
this[field].push(message);
|
||||
this.codes[field].push(code);
|
||||
code = code || 'invalid';
|
||||
if (!this[field]) {
|
||||
this[field] = [];
|
||||
this.codes[field] = [];
|
||||
}
|
||||
this[field].push(message);
|
||||
this.codes[field].push(code);
|
||||
};
|
||||
|
||||
function ErrorCodes(messages) {
|
||||
var c = this;
|
||||
Object.keys(messages).forEach(function(field) {
|
||||
c[field] = messages[field].codes;
|
||||
});
|
||||
var c = this;
|
||||
Object.keys(messages).forEach(function (field) {
|
||||
c[field] = messages[field].codes;
|
||||
});
|
||||
}
|
||||
|
||||
function ValidationError(obj) {
|
||||
if (!(this instanceof ValidationError)) return new ValidationError(obj);
|
||||
if (!(this instanceof ValidationError)) return new ValidationError(obj);
|
||||
|
||||
this.name = 'ValidationError';
|
||||
this.message = 'The Model instance is not valid. ' +
|
||||
'See `details` property of the error object for more info.';
|
||||
this.statusCode = 422;
|
||||
this.name = 'ValidationError';
|
||||
this.message = 'The Model instance is not valid. ' +
|
||||
'See `details` property of the error object for more info.';
|
||||
this.statusCode = 422;
|
||||
|
||||
this.details = {
|
||||
context: obj && obj.constructor && obj.constructor.modelName,
|
||||
codes: obj.errors && obj.errors.codes,
|
||||
messages: obj.errors
|
||||
};
|
||||
this.details = {
|
||||
context: obj && obj.constructor && obj.constructor.modelName,
|
||||
codes: obj.errors && obj.errors.codes,
|
||||
messages: obj.errors
|
||||
};
|
||||
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
|
||||
util.inherits(ValidationError, Error);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "loopback-datasource-juggler",
|
||||
"version": "1.2.12",
|
||||
"version": "1.2.13",
|
||||
"description": "LoopBack DataSoure Juggler",
|
||||
"keywords": [
|
||||
"StrongLoop",
|
||||
|
|
|
@ -2,347 +2,345 @@
|
|||
var should = require('./init.js');
|
||||
var db, User;
|
||||
|
||||
describe('basic-querying', function() {
|
||||
describe('basic-querying', function () {
|
||||
|
||||
before(function(done) {
|
||||
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);
|
||||
before(function (done) {
|
||||
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);
|
||||
|
||||
describe('findById', function() {
|
||||
});
|
||||
|
||||
before(function(done) {
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('findById', function () {
|
||||
|
||||
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) {
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
describe('find', function () {
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
before(seed);
|
||||
|
||||
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', function (done) {
|
||||
User.find(function (err, users) {
|
||||
should.exists(users);
|
||||
should.not.exists(err);
|
||||
users.should.have.lengthOf(6);
|
||||
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 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 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 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);
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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']);
|
||||
});
|
||||
|
||||
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']);
|
||||
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) {
|
||||
User.count(function(err, n) {
|
||||
should.not.exist(err);
|
||||
should.exist(n);
|
||||
n.should.equal(6);
|
||||
done();
|
||||
});
|
||||
});
|
||||
before(seed);
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
it('should query total count', function (done) {
|
||||
User.count(function (err, n) {
|
||||
should.not.exist(err);
|
||||
should.exist(n);
|
||||
n.should.equal(6);
|
||||
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) {
|
||||
User.all({order: 'id'}, function(err, users) {
|
||||
User.findOne(function(e, u) {
|
||||
should.not.exist(e);
|
||||
should.exist(u);
|
||||
u.id.toString().should.equal(users[0].id.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
before(seed);
|
||||
|
||||
it('should find first record (default sort by id)', function (done) {
|
||||
User.all({order: 'id'}, function (err, users) {
|
||||
User.findOne(function (e, u) {
|
||||
should.not.exist(e);
|
||||
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() {
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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 () {
|
||||
|
||||
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) {
|
||||
var count = 0;
|
||||
var beatles = [
|
||||
{
|
||||
name: 'John Lennon',
|
||||
email: 'john@b3atl3s.co.uk',
|
||||
role: 'lead',
|
||||
order: 2
|
||||
}, {
|
||||
name: 'Paul McCartney',
|
||||
email: 'paul@b3atl3s.co.uk',
|
||||
role: 'lead',
|
||||
order: 1
|
||||
},
|
||||
{name: 'George Harrison', order: 5},
|
||||
{name: 'Ringo Starr', order: 6},
|
||||
{name: 'Pete Best', order: 4},
|
||||
{name: 'Stuart Sutcliffe', order: 3}
|
||||
];
|
||||
User.destroyAll(function() {
|
||||
beatles.forEach(function(beatle) {
|
||||
User.create(beatle, ok);
|
||||
});
|
||||
var count = 0;
|
||||
var beatles = [
|
||||
{
|
||||
name: 'John Lennon',
|
||||
email: 'john@b3atl3s.co.uk',
|
||||
role: 'lead',
|
||||
order: 2
|
||||
},
|
||||
{
|
||||
name: 'Paul McCartney',
|
||||
email: 'paul@b3atl3s.co.uk',
|
||||
role: 'lead',
|
||||
order: 1
|
||||
},
|
||||
{name: 'George Harrison', order: 5},
|
||||
{name: 'Ringo Starr', order: 6},
|
||||
{name: 'Pete Best', order: 4},
|
||||
{name: 'Stuart Sutcliffe', order: 3}
|
||||
];
|
||||
User.destroyAll(function () {
|
||||
beatles.forEach(function (beatle) {
|
||||
User.create(beatle, ok);
|
||||
});
|
||||
});
|
||||
|
||||
function ok() {
|
||||
if (++count === beatles.length) {
|
||||
done();
|
||||
}
|
||||
function ok() {
|
||||
if (++count === beatles.length) {
|
||||
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;
|
||||
|
||||
describe('datatypes', function() {
|
||||
describe('datatypes', function () {
|
||||
|
||||
before(function(done){
|
||||
db = getSchema();
|
||||
Model = db.define('Model', {
|
||||
str: String,
|
||||
date: Date,
|
||||
num: Number,
|
||||
bool: Boolean,
|
||||
list: {type: []},
|
||||
});
|
||||
db.automigrate(function() {
|
||||
Model.destroyAll(done);
|
||||
});
|
||||
before(function (done) {
|
||||
db = getSchema();
|
||||
Model = db.define('Model', {
|
||||
str: String,
|
||||
date: Date,
|
||||
num: Number,
|
||||
bool: Boolean,
|
||||
list: {type: []},
|
||||
});
|
||||
db.automigrate(function () {
|
||||
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) {
|
||||
var d = new Date, id;
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
describe('defaults', function() {
|
||||
var Server;
|
||||
describe('defaults', function () {
|
||||
var Server;
|
||||
|
||||
before(function() {
|
||||
Server = db.define('Server', {
|
||||
host: String,
|
||||
port: {type: Number, default: 80}
|
||||
});
|
||||
before(function () {
|
||||
Server = db.define('Server', {
|
||||
host: String,
|
||||
port: {type: Number, default: 80}
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply defaults on new', function() {
|
||||
var s = new Server;
|
||||
s.port.should.equal(80);
|
||||
});
|
||||
it('should apply defaults on new', function () {
|
||||
var s = new Server;
|
||||
s.port.should.equal(80);
|
||||
});
|
||||
|
||||
it('should apply defaults on create', function(done) {
|
||||
Server.create(function(err, s) {
|
||||
s.port.should.equal(80);
|
||||
done();
|
||||
});
|
||||
it('should apply defaults on create', function (done) {
|
||||
Server.create(function (err, s) {
|
||||
s.port.should.equal(80);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply defaults on read', function(done) {
|
||||
db.defineProperty('Server', 'host', {
|
||||
type: String,
|
||||
default: 'localhost'
|
||||
});
|
||||
Server.all(function (err, servers) {
|
||||
(new String('localhost')).should.equal(servers[0].host);
|
||||
done();
|
||||
});
|
||||
it('should apply defaults on read', function (done) {
|
||||
db.defineProperty('Server', 'host', {
|
||||
type: String,
|
||||
default: 'localhost'
|
||||
});
|
||||
Server.all(function (err, servers) {
|
||||
(new String('localhost')).should.equal(servers[0].host);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,391 +2,423 @@
|
|||
var should = require('./init.js');
|
||||
|
||||
var j = require('../'),
|
||||
Schema = j.Schema,
|
||||
AbstractClass = j.AbstractClass,
|
||||
Hookable = j.Hookable,
|
||||
Schema = j.Schema,
|
||||
AbstractClass = j.AbstractClass,
|
||||
Hookable = j.Hookable,
|
||||
|
||||
db, User;
|
||||
db, User;
|
||||
|
||||
describe('hooks', function() {
|
||||
describe('hooks', function () {
|
||||
|
||||
before(function(done) {
|
||||
db = getSchema();
|
||||
before(function (done) {
|
||||
db = getSchema();
|
||||
|
||||
User = db.define('User', {
|
||||
email: {type: String, index: true},
|
||||
name: String,
|
||||
password: String,
|
||||
state: String
|
||||
});
|
||||
|
||||
db.automigrate(done);
|
||||
User = db.define('User', {
|
||||
email: {type: String, index: true},
|
||||
name: String,
|
||||
password: String,
|
||||
state: String
|
||||
});
|
||||
|
||||
describe('initialize', function() {
|
||||
db.automigrate(done);
|
||||
});
|
||||
|
||||
afterEach(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();
|
||||
});
|
||||
});
|
||||
describe('initialize', function () {
|
||||
|
||||
afterEach(function () {
|
||||
User.afterInitialize = null;
|
||||
});
|
||||
|
||||
describe('create', function() {
|
||||
|
||||
afterEach(removeHooks('Create'));
|
||||
|
||||
it('should be triggered on create', function(done) {
|
||||
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();
|
||||
});
|
||||
});
|
||||
it('should be triggered on new', function (done) {
|
||||
User.afterInitialize = function () {
|
||||
done();
|
||||
};
|
||||
new User;
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
describe('create', function () {
|
||||
|
||||
it('should not be triggered on new+save', function() {
|
||||
User.beforeUpdate = function(next) {
|
||||
should.fail('This should not be called');
|
||||
next();
|
||||
};
|
||||
(new User).save();
|
||||
});
|
||||
afterEach(removeHooks('Create'));
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should be triggered on create', function (done) {
|
||||
addHooks('Create', done);
|
||||
User.create();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be triggered on new', function () {
|
||||
User.beforeCreate = function (next) {
|
||||
should.fail('This should not be called');
|
||||
next();
|
||||
};
|
||||
var u = new User;
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
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 () {
|
||||
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) {
|
||||
var called = false, random = String(Math.floor(Math.random() * 1000));
|
||||
User['before' + name] = function(next, data) {
|
||||
called = true;
|
||||
data.email = random;
|
||||
next();
|
||||
};
|
||||
User['after' + name] = function(next) {
|
||||
(new Boolean(called)).should.equal(true);
|
||||
this.email.should.equal(random);
|
||||
done();
|
||||
};
|
||||
var called = false, random = String(Math.floor(Math.random() * 1000));
|
||||
User['before' + name] = function (next, data) {
|
||||
called = true;
|
||||
data.email = random;
|
||||
next();
|
||||
};
|
||||
User['after' + name] = function (next) {
|
||||
(new Boolean(called)).should.equal(true);
|
||||
this.email.should.equal(random);
|
||||
done();
|
||||
};
|
||||
}
|
||||
|
||||
function removeHooks(name) {
|
||||
return function() {
|
||||
User['after' + name] = null;
|
||||
User['before' + name] = null;
|
||||
};
|
||||
return function () {
|
||||
User['after' + name] = null;
|
||||
User['before' + name] = null;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,208 +4,209 @@ var should = require('./init.js');
|
|||
var db, User, Post, Passport, City, Street, Building;
|
||||
var nbSchemaRequests = 0;
|
||||
|
||||
describe('include', function() {
|
||||
describe('include', function () {
|
||||
|
||||
before(setup);
|
||||
before(setup);
|
||||
|
||||
it('should fetch belongsTo relation', function(done) {
|
||||
Passport.all({include: 'owner'}, function (err, passports) {
|
||||
passports.length.should.be.ok;
|
||||
passports.forEach(function(p) {
|
||||
p.__cachedRelations.should.have.property('owner');
|
||||
var owner = p.__cachedRelations.owner;
|
||||
if (!p.ownerId) {
|
||||
should.not.exist(owner);
|
||||
} else {
|
||||
should.exist(owner);
|
||||
owner.id.should.equal(p.ownerId);
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
it('should fetch belongsTo relation', function (done) {
|
||||
Passport.all({include: 'owner'}, function (err, passports) {
|
||||
passports.length.should.be.ok;
|
||||
passports.forEach(function (p) {
|
||||
p.__cachedRelations.should.have.property('owner');
|
||||
var owner = p.__cachedRelations.owner;
|
||||
if (!p.ownerId) {
|
||||
should.not.exist(owner);
|
||||
} else {
|
||||
should.exist(owner);
|
||||
owner.id.should.equal(p.ownerId);
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch hasMany relation', function(done) {
|
||||
User.all({include: 'posts'}, function (err, users) {
|
||||
should.not.exist(err);
|
||||
should.exist(users);
|
||||
users.length.should.be.ok;
|
||||
users.forEach(function(u) {
|
||||
u.__cachedRelations.should.have.property('posts');
|
||||
u.__cachedRelations.posts.forEach(function(p) {
|
||||
p.userId.should.equal(u.id);
|
||||
});
|
||||
});
|
||||
done();
|
||||
it('should fetch hasMany relation', function (done) {
|
||||
User.all({include: 'posts'}, function (err, users) {
|
||||
should.not.exist(err);
|
||||
should.exist(users);
|
||||
users.length.should.be.ok;
|
||||
users.forEach(function (u) {
|
||||
u.__cachedRelations.should.have.property('posts');
|
||||
u.__cachedRelations.posts.forEach(function (p) {
|
||||
p.userId.should.equal(u.id);
|
||||
});
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch Passport - Owner - Posts', function(done) {
|
||||
Passport.all({include: {owner: 'posts'}}, function(err, passports) {
|
||||
should.not.exist(err);
|
||||
should.exist(passports);
|
||||
passports.length.should.be.ok;
|
||||
passports.forEach(function(p) {
|
||||
p.__cachedRelations.should.have.property('owner');
|
||||
var user = p.__cachedRelations.owner;
|
||||
if (!p.ownerId) {
|
||||
should.not.exist(user);
|
||||
} else {
|
||||
should.exist(user);
|
||||
user.id.should.equal(p.ownerId);
|
||||
user.__cachedRelations.should.have.property('posts');
|
||||
user.__cachedRelations.posts.forEach(function(pp) {
|
||||
pp.userId.should.equal(user.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
it('should fetch Passport - Owner - Posts', function (done) {
|
||||
Passport.all({include: {owner: 'posts'}}, function (err, passports) {
|
||||
should.not.exist(err);
|
||||
should.exist(passports);
|
||||
passports.length.should.be.ok;
|
||||
passports.forEach(function (p) {
|
||||
p.__cachedRelations.should.have.property('owner');
|
||||
var user = p.__cachedRelations.owner;
|
||||
if (!p.ownerId) {
|
||||
should.not.exist(user);
|
||||
} else {
|
||||
should.exist(user);
|
||||
user.id.should.equal(p.ownerId);
|
||||
user.__cachedRelations.should.have.property('posts');
|
||||
user.__cachedRelations.posts.forEach(function (pp) {
|
||||
pp.userId.should.equal(user.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch Passports - User - Posts - User', function(done) {
|
||||
Passport.all({
|
||||
include: {owner: {posts: 'author'}}
|
||||
}, function(err, passports) {
|
||||
should.not.exist(err);
|
||||
should.exist(passports);
|
||||
passports.length.should.be.ok;
|
||||
passports.forEach(function(p) {
|
||||
p.__cachedRelations.should.have.property('owner');
|
||||
var user = p.__cachedRelations.owner;
|
||||
if (!p.ownerId) {
|
||||
should.not.exist(user);
|
||||
} else {
|
||||
should.exist(user);
|
||||
user.id.should.equal(p.ownerId);
|
||||
user.__cachedRelations.should.have.property('posts');
|
||||
user.__cachedRelations.posts.forEach(function(pp) {
|
||||
pp.userId.should.equal(user.id);
|
||||
pp.__cachedRelations.should.have.property('author');
|
||||
var author = pp.__cachedRelations.author;
|
||||
author.id.should.equal(user.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
it('should fetch Passports - User - Posts - User', function (done) {
|
||||
Passport.all({
|
||||
include: {owner: {posts: 'author'}}
|
||||
}, function (err, passports) {
|
||||
should.not.exist(err);
|
||||
should.exist(passports);
|
||||
passports.length.should.be.ok;
|
||||
passports.forEach(function (p) {
|
||||
p.__cachedRelations.should.have.property('owner');
|
||||
var user = p.__cachedRelations.owner;
|
||||
if (!p.ownerId) {
|
||||
should.not.exist(user);
|
||||
} else {
|
||||
should.exist(user);
|
||||
user.id.should.equal(p.ownerId);
|
||||
user.__cachedRelations.should.have.property('posts');
|
||||
user.__cachedRelations.posts.forEach(function (pp) {
|
||||
pp.userId.should.equal(user.id);
|
||||
pp.__cachedRelations.should.have.property('author');
|
||||
var author = pp.__cachedRelations.author;
|
||||
author.id.should.equal(user.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch User - Posts AND Passports', function(done) {
|
||||
User.all({include: ['posts', 'passports']}, function(err, users) {
|
||||
should.not.exist(err);
|
||||
should.exist(users);
|
||||
users.length.should.be.ok;
|
||||
users.forEach(function(user) {
|
||||
user.__cachedRelations.should.have.property('posts');
|
||||
user.__cachedRelations.should.have.property('passports');
|
||||
user.__cachedRelations.posts.forEach(function(p) {
|
||||
p.userId.should.equal(user.id);
|
||||
});
|
||||
user.__cachedRelations.passports.forEach(function(pp) {
|
||||
pp.ownerId.should.equal(user.id);
|
||||
});
|
||||
});
|
||||
done();
|
||||
it('should fetch User - Posts AND Passports', function (done) {
|
||||
User.all({include: ['posts', 'passports']}, function (err, users) {
|
||||
should.not.exist(err);
|
||||
should.exist(users);
|
||||
users.length.should.be.ok;
|
||||
users.forEach(function (user) {
|
||||
user.__cachedRelations.should.have.property('posts');
|
||||
user.__cachedRelations.should.have.property('passports');
|
||||
user.__cachedRelations.posts.forEach(function (p) {
|
||||
p.userId.should.equal(user.id);
|
||||
});
|
||||
user.__cachedRelations.passports.forEach(function (pp) {
|
||||
pp.ownerId.should.equal(user.id);
|
||||
});
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function setup(done) {
|
||||
db = getSchema();
|
||||
City = db.define('City');
|
||||
Street = db.define('Street');
|
||||
Building = db.define('Building');
|
||||
User = db.define('User', {
|
||||
name: String,
|
||||
age: Number
|
||||
});
|
||||
Passport = db.define('Passport', {
|
||||
number: String
|
||||
});
|
||||
Post = db.define('Post', {
|
||||
title: String
|
||||
});
|
||||
db = getSchema();
|
||||
City = db.define('City');
|
||||
Street = db.define('Street');
|
||||
Building = db.define('Building');
|
||||
User = db.define('User', {
|
||||
name: String,
|
||||
age: Number
|
||||
});
|
||||
Passport = db.define('Passport', {
|
||||
number: String
|
||||
});
|
||||
Post = db.define('Post', {
|
||||
title: String
|
||||
});
|
||||
|
||||
Passport.belongsTo('owner', {model: User});
|
||||
User.hasMany('passports', {foreignKey: 'ownerId'});
|
||||
User.hasMany('posts', {foreignKey: 'userId'});
|
||||
Post.belongsTo('author', {model: User, foreignKey: 'userId'});
|
||||
Passport.belongsTo('owner', {model: User});
|
||||
User.hasMany('passports', {foreignKey: 'ownerId'});
|
||||
User.hasMany('posts', {foreignKey: 'userId'});
|
||||
Post.belongsTo('author', {model: User, foreignKey: 'userId'});
|
||||
|
||||
db.automigrate(function() {
|
||||
var createdUsers = [];
|
||||
var createdPassports = [];
|
||||
var createdPosts = [];
|
||||
createUsers();
|
||||
function createUsers() {
|
||||
clearAndCreate(
|
||||
User,
|
||||
[
|
||||
{name: 'User A', age: 21},
|
||||
{name: 'User B', age: 22},
|
||||
{name: 'User C', age: 23},
|
||||
{name: 'User D', age: 24},
|
||||
{name: 'User E', age: 25}
|
||||
],
|
||||
function(items) {
|
||||
createdUsers = items;
|
||||
createPassports();
|
||||
}
|
||||
);
|
||||
db.automigrate(function () {
|
||||
var createdUsers = [];
|
||||
var createdPassports = [];
|
||||
var createdPosts = [];
|
||||
createUsers();
|
||||
function createUsers() {
|
||||
clearAndCreate(
|
||||
User,
|
||||
[
|
||||
{name: 'User A', age: 21},
|
||||
{name: 'User B', age: 22},
|
||||
{name: 'User C', age: 23},
|
||||
{name: 'User D', age: 24},
|
||||
{name: 'User E', age: 25}
|
||||
],
|
||||
function (items) {
|
||||
createdUsers = items;
|
||||
createPassports();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createPassports() {
|
||||
clearAndCreate(
|
||||
Passport,
|
||||
[
|
||||
{number: '1', ownerId: createdUsers[0].id},
|
||||
{number: '2', ownerId: createdUsers[1].id},
|
||||
{number: '3'}
|
||||
],
|
||||
function(items) {
|
||||
createdPassports = items;
|
||||
createPosts();
|
||||
}
|
||||
);
|
||||
function createPassports() {
|
||||
clearAndCreate(
|
||||
Passport,
|
||||
[
|
||||
{number: '1', ownerId: createdUsers[0].id},
|
||||
{number: '2', ownerId: createdUsers[1].id},
|
||||
{number: '3'}
|
||||
],
|
||||
function (items) {
|
||||
createdPassports = items;
|
||||
createPosts();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createPosts() {
|
||||
clearAndCreate(
|
||||
Post,
|
||||
[
|
||||
{title: 'Post A', userId: createdUsers[0].id},
|
||||
{title: 'Post B', userId: createdUsers[0].id},
|
||||
{title: 'Post C', userId: createdUsers[0].id},
|
||||
{title: 'Post D', userId: createdUsers[1].id},
|
||||
{title: 'Post E'}
|
||||
],
|
||||
function(items) {
|
||||
createdPosts = items;
|
||||
done();
|
||||
}
|
||||
);
|
||||
function createPosts() {
|
||||
clearAndCreate(
|
||||
Post,
|
||||
[
|
||||
{title: 'Post A', userId: createdUsers[0].id},
|
||||
{title: 'Post B', userId: createdUsers[0].id},
|
||||
{title: 'Post C', userId: createdUsers[0].id},
|
||||
{title: 'Post D', userId: createdUsers[1].id},
|
||||
{title: 'Post E'}
|
||||
],
|
||||
function (items) {
|
||||
createdPosts = items;
|
||||
done();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function clearAndCreate(model, data, callback) {
|
||||
var createdItems = [];
|
||||
model.destroyAll(function () {
|
||||
nextItem(null, null);
|
||||
});
|
||||
var createdItems = [];
|
||||
model.destroyAll(function () {
|
||||
nextItem(null, null);
|
||||
});
|
||||
|
||||
var itemIndex = 0;
|
||||
function nextItem(err, lastItem) {
|
||||
if (lastItem !== null) {
|
||||
createdItems.push(lastItem);
|
||||
}
|
||||
if (itemIndex >= data.length) {
|
||||
callback(createdItems);
|
||||
return;
|
||||
}
|
||||
model.create(data[itemIndex], nextItem);
|
||||
itemIndex++;
|
||||
var itemIndex = 0;
|
||||
|
||||
function nextItem(err, lastItem) {
|
||||
if (lastItem !== null) {
|
||||
createdItems.push(lastItem);
|
||||
}
|
||||
if (itemIndex >= data.length) {
|
||||
callback(createdItems);
|
||||
return;
|
||||
}
|
||||
model.create(data[itemIndex], nextItem);
|
||||
itemIndex++;
|
||||
}
|
||||
}
|
||||
|
|
24
test/init.js
24
test/init.js
|
@ -1,21 +1,21 @@
|
|||
module.exports = require('should');
|
||||
|
||||
/*
|
||||
if (!process.env.TRAVIS) {
|
||||
if (typeof __cov === 'undefined') {
|
||||
process.on('exit', function () {
|
||||
require('semicov').report();
|
||||
});
|
||||
}
|
||||
if (!process.env.TRAVIS) {
|
||||
if (typeof __cov === 'undefined') {
|
||||
process.on('exit', function () {
|
||||
require('semicov').report();
|
||||
});
|
||||
}
|
||||
|
||||
require('semicov').init('lib');
|
||||
}
|
||||
*/
|
||||
require('semicov').init('lib');
|
||||
}
|
||||
*/
|
||||
|
||||
var Schema = require('../').Schema;
|
||||
|
||||
if (!('getSchema' in global)) {
|
||||
global.getSchema = function() {
|
||||
return new Schema('memory');
|
||||
};
|
||||
global.getSchema = function () {
|
||||
return new Schema('memory');
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,99 +3,99 @@ var ModelBuilder = require('../lib/model-builder').ModelBuilder;
|
|||
var introspectType = require('../lib/introspection')(ModelBuilder);
|
||||
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() {
|
||||
assert.equal(introspectType('123'), 'string');
|
||||
assert.equal(introspectType(true), 'boolean');
|
||||
assert.equal(introspectType(false), 'boolean');
|
||||
assert.equal(introspectType(12), 'number');
|
||||
assert.equal(introspectType(new Date()), 'date');
|
||||
});
|
||||
it('should handle simple types', function () {
|
||||
assert.equal(introspectType('123'), 'string');
|
||||
assert.equal(introspectType(true), 'boolean');
|
||||
assert.equal(introspectType(false), 'boolean');
|
||||
assert.equal(introspectType(12), 'number');
|
||||
assert.equal(introspectType(new Date()), 'date');
|
||||
});
|
||||
|
||||
it('should handle array types', function() {
|
||||
var type = introspectType(['123']);
|
||||
assert.deepEqual(type, ['string'], 'type should be ["string"]');
|
||||
type = introspectType([1]);
|
||||
assert.deepEqual(type, ['number'], 'type should be ["number"]');
|
||||
// Stop at first known type
|
||||
type = introspectType([1, '123']);
|
||||
assert.deepEqual(type, ['number'], 'type should be ["number"]');
|
||||
type = introspectType([null, '123']);
|
||||
assert.deepEqual(type, ['string'], 'type should be ["string"]');
|
||||
it('should handle array types', function () {
|
||||
var type = introspectType(['123']);
|
||||
assert.deepEqual(type, ['string'], 'type should be ["string"]');
|
||||
type = introspectType([1]);
|
||||
assert.deepEqual(type, ['number'], 'type should be ["number"]');
|
||||
// Stop at first known type
|
||||
type = introspectType([1, '123']);
|
||||
assert.deepEqual(type, ['number'], 'type should be ["number"]');
|
||||
type = introspectType([null, '123']);
|
||||
assert.deepEqual(type, ['string'], 'type should be ["string"]');
|
||||
|
||||
type = introspectType([]);
|
||||
assert.equal(type, 'array');
|
||||
});
|
||||
type = introspectType([]);
|
||||
assert.equal(type, 'array');
|
||||
});
|
||||
|
||||
it('should return Any for null or undefined', function() {
|
||||
assert.equal(introspectType(null), ModelBuilder.Any);
|
||||
assert.equal(introspectType(undefined), ModelBuilder.Any);
|
||||
});
|
||||
it('should return Any for null or undefined', function () {
|
||||
assert.equal(introspectType(null), ModelBuilder.Any);
|
||||
assert.equal(introspectType(undefined), ModelBuilder.Any);
|
||||
});
|
||||
|
||||
it('should return a schema for object', function() {
|
||||
var json = {a: 'str', b: 0, c: true};
|
||||
var type = introspectType(json);
|
||||
assert.equal(type.a, 'string');
|
||||
assert.equal(type.b, 'number');
|
||||
assert.equal(type.c, 'boolean');
|
||||
});
|
||||
it('should return a schema for object', function () {
|
||||
var json = {a: 'str', b: 0, c: true};
|
||||
var type = introspectType(json);
|
||||
assert.equal(type.a, 'string');
|
||||
assert.equal(type.b, 'number');
|
||||
assert.equal(type.c, 'boolean');
|
||||
});
|
||||
|
||||
it('should handle nesting objects', function() {
|
||||
var json = {a: 'str', b: 0, c: true, d: {x: 10, y: 5}};
|
||||
var type = introspectType(json);
|
||||
assert.equal(type.a, 'string');
|
||||
assert.equal(type.b, 'number');
|
||||
assert.equal(type.c, 'boolean');
|
||||
assert.equal(type.d.x, 'number');
|
||||
assert.equal(type.d.y, 'number');
|
||||
});
|
||||
it('should handle nesting objects', function () {
|
||||
var json = {a: 'str', b: 0, c: true, d: {x: 10, y: 5}};
|
||||
var type = introspectType(json);
|
||||
assert.equal(type.a, 'string');
|
||||
assert.equal(type.b, 'number');
|
||||
assert.equal(type.c, 'boolean');
|
||||
assert.equal(type.d.x, 'number');
|
||||
assert.equal(type.d.y, 'number');
|
||||
});
|
||||
|
||||
it('should handle nesting arrays', function() {
|
||||
var json = {a: 'str', b: 0, c: true, d: [1, 2]};
|
||||
var type = introspectType(json);
|
||||
assert.equal(type.a, 'string');
|
||||
assert.equal(type.b, 'number');
|
||||
assert.equal(type.c, 'boolean');
|
||||
assert.deepEqual(type.d, ['number']);
|
||||
});
|
||||
it('should handle nesting arrays', function () {
|
||||
var json = {a: 'str', b: 0, c: true, d: [1, 2]};
|
||||
var type = introspectType(json);
|
||||
assert.equal(type.a, 'string');
|
||||
assert.equal(type.b, 'number');
|
||||
assert.equal(type.c, 'boolean');
|
||||
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 = {
|
||||
name: 'Joe',
|
||||
age: 30,
|
||||
birthday: new Date(),
|
||||
vip: true,
|
||||
address: {
|
||||
street: '1 Main St',
|
||||
city: 'San Jose',
|
||||
state: 'CA',
|
||||
zipcode: '95131',
|
||||
country: 'US'
|
||||
},
|
||||
friends: ['John', 'Mary'],
|
||||
emails: [
|
||||
{label: 'work', id: 'x@sample.com'},
|
||||
{label: 'home', id: 'x@home.com'}
|
||||
],
|
||||
tags: []
|
||||
};
|
||||
var json = {
|
||||
name: 'Joe',
|
||||
age: 30,
|
||||
birthday: new Date(),
|
||||
vip: true,
|
||||
address: {
|
||||
street: '1 Main St',
|
||||
city: 'San Jose',
|
||||
state: 'CA',
|
||||
zipcode: '95131',
|
||||
country: 'US'
|
||||
},
|
||||
friends: ['John', 'Mary'],
|
||||
emails: [
|
||||
{label: 'work', id: 'x@sample.com'},
|
||||
{label: 'home', id: 'x@home.com'}
|
||||
],
|
||||
tags: []
|
||||
};
|
||||
|
||||
var copy = traverse(json).clone();
|
||||
var copy = traverse(json).clone();
|
||||
|
||||
var schema = introspectType(json);
|
||||
var schema = introspectType(json);
|
||||
|
||||
var builder = new ModelBuilder();
|
||||
var Model = builder.define('MyModel', schema, {idInjection: false});
|
||||
var builder = new ModelBuilder();
|
||||
var Model = builder.define('MyModel', schema, {idInjection: false});
|
||||
|
||||
// FIXME: [rfeng] The constructor mutates the arguments
|
||||
var obj = new Model(json);
|
||||
// FIXME: [rfeng] The constructor mutates the arguments
|
||||
var obj = new Model(json);
|
||||
|
||||
obj = obj.toObject();
|
||||
obj = obj.toObject();
|
||||
|
||||
assert.deepEqual(obj, copy);
|
||||
done();
|
||||
});
|
||||
assert.deepEqual(obj, copy);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -4,36 +4,36 @@ var should = require('./init.js');
|
|||
var Schema = require('../').Schema;
|
||||
var ModelBuilder = require('../').ModelBuilder;
|
||||
|
||||
describe('JSON property', function() {
|
||||
var dataSource, Model;
|
||||
describe('JSON property', function () {
|
||||
var dataSource, Model;
|
||||
|
||||
it('should be defined', function() {
|
||||
dataSource = getSchema();
|
||||
Model = dataSource.define('Model', {propertyName: ModelBuilder.JSON});
|
||||
var m = new Model;
|
||||
(new Boolean('propertyName' in m)).should.eql(true);
|
||||
should.not.exist(m.propertyName);
|
||||
});
|
||||
it('should be defined', function () {
|
||||
dataSource = getSchema();
|
||||
Model = dataSource.define('Model', {propertyName: ModelBuilder.JSON});
|
||||
var m = new Model;
|
||||
(new Boolean('propertyName' in m)).should.eql(true);
|
||||
should.not.exist(m.propertyName);
|
||||
});
|
||||
|
||||
it('should accept JSON in constructor and return object', function() {
|
||||
var m = new Model({
|
||||
propertyName: '{"foo": "bar"}'
|
||||
});
|
||||
m.propertyName.should.be.an.Object;
|
||||
m.propertyName.foo.should.equal('bar');
|
||||
it('should accept JSON in constructor and return object', function () {
|
||||
var m = new Model({
|
||||
propertyName: '{"foo": "bar"}'
|
||||
});
|
||||
m.propertyName.should.be.an.Object;
|
||||
m.propertyName.foo.should.equal('bar');
|
||||
});
|
||||
|
||||
it('should accept object in setter and return object', function() {
|
||||
var m = new Model;
|
||||
m.propertyName = {"foo": "bar"};
|
||||
m.propertyName.should.be.an.Object;
|
||||
m.propertyName.foo.should.equal('bar');
|
||||
});
|
||||
it('should accept object in setter and return object', function () {
|
||||
var m = new Model;
|
||||
m.propertyName = {"foo": "bar"};
|
||||
m.propertyName.should.be.an.Object;
|
||||
m.propertyName.foo.should.equal('bar');
|
||||
});
|
||||
|
||||
it('should accept string in setter and return string', function() {
|
||||
var m = new Model;
|
||||
m.propertyName = '{"foo": "bar"}';
|
||||
m.propertyName.should.be.a.String;
|
||||
m.propertyName.should.equal('{"foo": "bar"}');
|
||||
});
|
||||
it('should accept string in setter and return string', function () {
|
||||
var m = new Model;
|
||||
m.propertyName = '{"foo": "bar"}';
|
||||
m.propertyName.should.be.a.String;
|
||||
m.propertyName.should.equal('{"foo": "bar"}');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,8 +3,8 @@ var should = require('./init.js');
|
|||
|
||||
var loopbackData = require('../');
|
||||
|
||||
describe('loopback-datasource-juggler', function() {
|
||||
it('should expose version', function () {
|
||||
loopbackData.version.should.equal(require('../package.json').version);
|
||||
});
|
||||
describe('loopback-datasource-juggler', function () {
|
||||
it('should expose version', function () {
|
||||
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 ValidationError = require('..').ValidationError;
|
||||
|
||||
describe('manipulation', function() {
|
||||
describe('manipulation', function () {
|
||||
|
||||
before(function(done) {
|
||||
db = getSchema();
|
||||
|
||||
Person = db.define('Person', {
|
||||
name: String,
|
||||
gender: String,
|
||||
married: Boolean,
|
||||
age: {type: Number, index: true},
|
||||
dob: Date,
|
||||
createdAt: {type: Number, default: Date.now}
|
||||
});
|
||||
|
||||
db.automigrate(done);
|
||||
before(function (done) {
|
||||
db = getSchema();
|
||||
|
||||
Person = db.define('Person', {
|
||||
name: String,
|
||||
gender: String,
|
||||
married: Boolean,
|
||||
age: {type: Number, index: true},
|
||||
dob: Date,
|
||||
createdAt: {type: Number, default: Date.now}
|
||||
});
|
||||
|
||||
describe('create', function() {
|
||||
db.automigrate(done);
|
||||
|
||||
before(function(done) {
|
||||
Person.destroyAll(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should create instance', function(done) {
|
||||
Person.create({name: 'Anatoliy'}, function(err, p) {
|
||||
p.name.should.equal('Anatoliy');
|
||||
should.not.exist(err);
|
||||
should.exist(p);
|
||||
Person.findById(p.id, function(err, person) {
|
||||
person.id.should.equal(p.id);
|
||||
person.name.should.equal('Anatoliy');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('create', function () {
|
||||
|
||||
it('should return instance of object', function(done) {
|
||||
var person = Person.create(function(err, p) {
|
||||
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);
|
||||
});
|
||||
before(function (done) {
|
||||
Person.destroyAll(done);
|
||||
});
|
||||
|
||||
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 create instance', function (done) {
|
||||
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 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should return instance of object', function (done) {
|
||||
var person = Person.create(function (err, p) {
|
||||
p.id.should.eql(person.id);
|
||||
done();
|
||||
});
|
||||
should.exist(person);
|
||||
person.should.be.an.instanceOf(Person);
|
||||
should.not.exist(person.id);
|
||||
});
|
||||
|
||||
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');
|
||||
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'});
|
||||
});
|
||||
|
||||
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 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 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 () {
|
||||
|
||||
it('should be able to define plain models', function (done) {
|
||||
var modelBuilder = new ModelBuilder();
|
||||
it('should be able to define plain models', function (done) {
|
||||
var modelBuilder = new ModelBuilder();
|
||||
|
||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||
name: "string",
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: "number"
|
||||
});
|
||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||
name: "string",
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: "number"
|
||||
});
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
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();
|
||||
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();
|
||||
|
||||
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) {
|
||||
var modelBuilder = new ModelBuilder();
|
||||
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 User = new ModelDefinition(modelBuilder, 'User', {
|
||||
name: "string",
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: "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");
|
||||
|
||||
User.build();
|
||||
assert.equal(json.properties.address.type, 'Address');
|
||||
|
||||
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);
|
||||
done();
|
||||
|
||||
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 modelBuilder = new ModelBuilder();
|
||||
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 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
|
||||
}
|
||||
});
|
||||
assert.equal(json.properties.address.type, '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(typeof User.properties.address.type, 'function');
|
||||
done();
|
||||
|
||||
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 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 be able to define referencing models', function (done) {
|
||||
var modelBuilder = new ModelBuilder();
|
||||
it('should report correct table/column names', 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
|
||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||
userId: {type: String, id: true, oracle: {column: 'ID'}},
|
||||
name: "string"
|
||||
}, {oracle: {table: 'USER'}});
|
||||
|
||||
});
|
||||
|
||||
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();
|
||||
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');
|
||||
|
||||
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);
|
||||
|
||||
|
||||
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);
|
||||
});
|
||||
assert(anotherChild.prototype instanceof baseChild);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -4,80 +4,81 @@ Text = Schema.Text
|
|||
require('./spec_helper').init exports
|
||||
|
||||
schemas =
|
||||
neo4j:
|
||||
url: 'http://localhost:7474/'
|
||||
mongoose:
|
||||
url: 'mongodb://localhost/test'
|
||||
redis: {}
|
||||
memory: {}
|
||||
cradle: {}
|
||||
nano:
|
||||
url: 'http://localhost:5984/nano-test'
|
||||
neo4j:
|
||||
url: 'http://localhost:7474/'
|
||||
mongoose:
|
||||
url: 'mongodb://localhost/test'
|
||||
redis: {}
|
||||
memory: {}
|
||||
cradle: {}
|
||||
nano:
|
||||
url: 'http://localhost:5984/nano-test'
|
||||
|
||||
testOrm = (dataSource) ->
|
||||
User = Post = 'unknown'
|
||||
maxUsers = 100
|
||||
maxPosts = 50000
|
||||
users = []
|
||||
|
||||
User = Post = 'unknown'
|
||||
maxUsers = 100
|
||||
maxPosts = 50000
|
||||
users = []
|
||||
it 'should define simple', (test) ->
|
||||
User = dataSource.define 'User', {
|
||||
name: String,
|
||||
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', {
|
||||
name: String,
|
||||
bio: Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
}
|
||||
User.hasMany(Post, {as: 'posts', foreignKey: 'userId'})
|
||||
Post.belongsTo(User, {as: 'author', foreignKey: 'userId'})
|
||||
|
||||
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 }
|
||||
test.done()
|
||||
|
||||
User.hasMany(Post, {as: 'posts', foreignKey: 'userId'})
|
||||
Post.belongsTo(User, {as: 'author', foreignKey: 'userId'})
|
||||
it 'should create users', (test) ->
|
||||
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) ->
|
||||
wait = maxUsers
|
||||
done = (e, u) ->
|
||||
users.push(u)
|
||||
test.done() if --wait == 0
|
||||
User.create(done) for i in [1..maxUsers]
|
||||
it 'do some queries using foreign keys', (test) ->
|
||||
wait = 4
|
||||
done = ->
|
||||
test.done() if --wait == 0
|
||||
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]
|
||||
|
||||
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]
|
||||
return
|
||||
|
||||
it 'do some queries using foreign keys', (test) ->
|
||||
wait = 4
|
||||
done = -> test.done() if --wait == 0
|
||||
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)
|
||||
it 'should destroy all data', (test) ->
|
||||
Post.destroyAll ->
|
||||
User.destroyAll(test.done)
|
||||
|
||||
Object.keys(schemas).forEach (schemaName) ->
|
||||
return if process.env.ONLY && process.env.ONLY != schemaName
|
||||
context schemaName, ->
|
||||
dataSource = new Schema schemaName, schemas[schemaName]
|
||||
testOrm(dataSource)
|
||||
return if process.env.ONLY && process.env.ONLY != schemaName
|
||||
context schemaName, ->
|
||||
dataSource = new Schema schemaName, schemas[schemaName]
|
||||
testOrm(dataSource)
|
||||
|
||||
|
|
|
@ -3,251 +3,251 @@ var should = require('./init.js');
|
|||
|
||||
var db, Book, Chapter, Author, Reader;
|
||||
|
||||
describe('relations', function() {
|
||||
before(function(done) {
|
||||
db = getSchema();
|
||||
Book = db.define('Book', {name: String});
|
||||
Chapter = db.define('Chapter', {name: {type: String, index: true}});
|
||||
Author = db.define('Author', {name: String});
|
||||
Reader = db.define('Reader', {name: String});
|
||||
describe('relations', function () {
|
||||
before(function (done) {
|
||||
db = getSchema();
|
||||
Book = db.define('Book', {name: String});
|
||||
Chapter = db.define('Chapter', {name: {type: String, index: true}});
|
||||
Author = db.define('Author', {name: String});
|
||||
Reader = db.define('Reader', {name: String});
|
||||
|
||||
db.automigrate(function() {
|
||||
Book.destroyAll(function() {
|
||||
Chapter.destroyAll(function() {
|
||||
Author.destroyAll(function() {
|
||||
Reader.destroyAll(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
db.automigrate(function () {
|
||||
Book.destroyAll(function () {
|
||||
Chapter.destroyAll(function () {
|
||||
Author.destroyAll(function () {
|
||||
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() {
|
||||
db.disconnect();
|
||||
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);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
});
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
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('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('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.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);
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
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 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;
|
||||
|
||||
describe('dataSource', function() {
|
||||
describe('dataSource', function () {
|
||||
|
||||
it('should define Model', function() {
|
||||
Model = db.define('Model');
|
||||
Model.dataSource.should.eql(db);
|
||||
var m = new Model;
|
||||
m.getDataSource().should.eql(db);
|
||||
it('should define Model', function () {
|
||||
Model = db.define('Model');
|
||||
Model.dataSource.should.eql(db);
|
||||
var m = new Model;
|
||||
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() {
|
||||
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;
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
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;
|
||||
|
||||
describe('sc0pe', function() {
|
||||
describe('sc0pe', function () {
|
||||
|
||||
before(function() {
|
||||
db = getSchema();
|
||||
Railway = db.define('Railway', {
|
||||
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}
|
||||
});
|
||||
before(function () {
|
||||
db = getSchema();
|
||||
Railway = db.define('Railway', {
|
||||
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}
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function(done) {
|
||||
Railway.destroyAll(function() {
|
||||
Station.destroyAll(done);
|
||||
});
|
||||
beforeEach(function (done) {
|
||||
Railway.destroyAll(function () {
|
||||
Station.destroyAll(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should define scope with query', function(done) {
|
||||
Station.scope('active', {where: {isActive: true}});
|
||||
Station.active.create(function(err, station) {
|
||||
should.not.exist(err);
|
||||
should.exist(station);
|
||||
should.exist(station.isActive);
|
||||
station.isActive.should.be.true;
|
||||
done();
|
||||
});
|
||||
it('should define scope with query', function (done) {
|
||||
Station.scope('active', {where: {isActive: true}});
|
||||
Station.active.create(function (err, station) {
|
||||
should.not.exist(err);
|
||||
should.exist(station);
|
||||
should.exist(station.isActive);
|
||||
station.isActive.should.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow scope chaining', function(done) {
|
||||
Station.scope('active', {where: {isActive: true}});
|
||||
Station.scope('subway', {where: {isUndeground: true}});
|
||||
Station.active.subway.create(function(err, station) {
|
||||
should.not.exist(err);
|
||||
should.exist(station);
|
||||
station.isActive.should.be.true;
|
||||
station.isUndeground.should.be.true;
|
||||
done();
|
||||
})
|
||||
});
|
||||
it('should allow scope chaining', function (done) {
|
||||
Station.scope('active', {where: {isActive: true}});
|
||||
Station.scope('subway', {where: {isUndeground: true}});
|
||||
Station.active.subway.create(function (err, station) {
|
||||
should.not.exist(err);
|
||||
should.exist(station);
|
||||
station.isActive.should.be.true;
|
||||
station.isUndeground.should.be.true;
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('should query all', function(done) {
|
||||
Station.scope('active', {where: {isActive: true}});
|
||||
Station.scope('inactive', {where: {isActive: false}});
|
||||
Station.scope('ground', {where: {isUndeground: true}});
|
||||
Station.active.ground.create(function() {
|
||||
Station.inactive.ground.create(function() {
|
||||
Station.ground.inactive(function(err, ss) {
|
||||
ss.should.have.lengthOf(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should query all', function (done) {
|
||||
Station.scope('active', {where: {isActive: true}});
|
||||
Station.scope('inactive', {where: {isActive: false}});
|
||||
Station.scope('ground', {where: {isUndeground: true}});
|
||||
Station.active.ground.create(function () {
|
||||
Station.inactive.ground.create(function () {
|
||||
Station.ground.inactive(function (err, ss) {
|
||||
ss.should.have.lengthOf(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,56 +1,56 @@
|
|||
/*
|
||||
if (!process.env.TRAVIS) {
|
||||
var semicov = require('semicov');
|
||||
semicov.init('lib', 'LoopbackData');
|
||||
process.on('exit', semicov.report);
|
||||
}
|
||||
*/
|
||||
if (!process.env.TRAVIS) {
|
||||
var semicov = require('semicov');
|
||||
semicov.init('lib', 'LoopbackData');
|
||||
process.on('exit', semicov.report);
|
||||
}
|
||||
*/
|
||||
|
||||
try {
|
||||
global.sinon = require('sinon');
|
||||
global.sinon = require('sinon');
|
||||
} catch (e) {
|
||||
// ignore
|
||||
// ignore
|
||||
}
|
||||
|
||||
var group_name = false, EXT_EXP;
|
||||
function it(should, test_case) {
|
||||
check_external_exports();
|
||||
if (group_name) {
|
||||
EXT_EXP[group_name][should] = test_case;
|
||||
} else {
|
||||
EXT_EXP[should] = test_case;
|
||||
}
|
||||
check_external_exports();
|
||||
if (group_name) {
|
||||
EXT_EXP[group_name][should] = test_case;
|
||||
} else {
|
||||
EXT_EXP[should] = test_case;
|
||||
}
|
||||
}
|
||||
|
||||
global.it = it;
|
||||
|
||||
function context(name, tests) {
|
||||
check_external_exports();
|
||||
EXT_EXP[name] = {};
|
||||
group_name = name;
|
||||
tests({
|
||||
before: function (f) {
|
||||
it('setUp', f);
|
||||
},
|
||||
after: function (f) {
|
||||
it('tearDown', f);
|
||||
}
|
||||
});
|
||||
group_name = false;
|
||||
check_external_exports();
|
||||
EXT_EXP[name] = {};
|
||||
group_name = name;
|
||||
tests({
|
||||
before: function (f) {
|
||||
it('setUp', f);
|
||||
},
|
||||
after: function (f) {
|
||||
it('tearDown', f);
|
||||
}
|
||||
});
|
||||
group_name = false;
|
||||
}
|
||||
|
||||
global.context = context;
|
||||
|
||||
exports.init = function init(external_exports) {
|
||||
EXT_EXP = external_exports;
|
||||
if (external_exports.done) {
|
||||
external_exports.done();
|
||||
}
|
||||
EXT_EXP = external_exports;
|
||||
if (external_exports.done) {
|
||||
external_exports.done();
|
||||
}
|
||||
};
|
||||
|
||||
function check_external_exports() {
|
||||
if (!EXT_EXP) throw new Error(
|
||||
'Before run this, please ensure that ' +
|
||||
'require("spec_helper").init(exports); called');
|
||||
if (!EXT_EXP) throw new Error(
|
||||
'Before run this, please ensure that ' +
|
||||
'require("spec_helper").init(exports); called');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
{
|
||||
"title": {
|
||||
"type": "String"
|
||||
},
|
||||
"author": {
|
||||
"type": "String",
|
||||
"default": "Raymond"
|
||||
},
|
||||
"body": "String",
|
||||
"date": {
|
||||
"type": "Date"
|
||||
},
|
||||
"hidden": "Boolean",
|
||||
"comments": ["String"]
|
||||
"title": {
|
||||
"type": "String"
|
||||
},
|
||||
"author": {
|
||||
"type": "String",
|
||||
"default": "Raymond"
|
||||
},
|
||||
"body": "String",
|
||||
"date": {
|
||||
"type": "Date"
|
||||
},
|
||||
"hidden": "Boolean",
|
||||
"comments": ["String"]
|
||||
}
|
|
@ -1,83 +1,83 @@
|
|||
[
|
||||
{
|
||||
"name": "Address",
|
||||
"properties": {
|
||||
"label": "string",
|
||||
"street": "string",
|
||||
"city": "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": "Address",
|
||||
"properties": {
|
||||
"label": "string",
|
||||
"street": "string",
|
||||
"city": "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"
|
||||
}
|
||||
}
|
||||
|
||||
]
|
|
@ -4,11 +4,9 @@ var fieldsToArray = utils.fieldsToArray;
|
|||
var removeUndefined = utils.removeUndefined;
|
||||
var mergeSettings = utils.mergeSettings;
|
||||
|
||||
describe('util.fieldsToArray', function () {
|
||||
it('Turn objects and strings into an array of fields to include when finding models', function () {
|
||||
|
||||
describe('util.fieldsToArray', function(){
|
||||
it('Turn objects and strings into an array of fields to include when finding models', function() {
|
||||
|
||||
|
||||
function sample(fields) {
|
||||
var properties = ['foo', 'bar', 'bat', 'baz'];
|
||||
return {
|
||||
|
@ -17,7 +15,7 @@ describe('util.fieldsToArray', function(){
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sample(false).expect(undefined);
|
||||
sample(null).expect(undefined);
|
||||
sample({}).expect(undefined);
|
||||
|
@ -30,90 +28,90 @@ describe('util.fieldsToArray', function(){
|
|||
});
|
||||
});
|
||||
|
||||
describe('util.removeUndefined', function(){
|
||||
it('Remove undefined values from the query object', function() {
|
||||
var q1 = {where: {x: 1, y: undefined}};
|
||||
should.deepEqual(removeUndefined(q1), {where: {x: 1}});
|
||||
describe('util.removeUndefined', function () {
|
||||
it('Remove undefined values from the query object', function () {
|
||||
var q1 = {where: {x: 1, y: undefined}};
|
||||
should.deepEqual(removeUndefined(q1), {where: {x: 1}});
|
||||
|
||||
var q2 = {where: {x: 1, y: 2}};
|
||||
should.deepEqual(removeUndefined(q2), {where: {x: 1, y: 2}});
|
||||
var q2 = {where: {x: 1, y: 2}};
|
||||
should.deepEqual(removeUndefined(q2), {where: {x: 1, y: 2}});
|
||||
|
||||
var q3 = {where: {x: 1, y: {in: [2, undefined]}}};
|
||||
should.deepEqual(removeUndefined(q3), {where: {x: 1, y: {in: [2]}}});
|
||||
var q3 = {where: {x: 1, y: {in: [2, undefined]}}};
|
||||
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 q4 = {where: {x: 1, y: date}};
|
||||
should.deepEqual(removeUndefined(q4), {where: {x: 1, y: date}});
|
||||
var date = new Date();
|
||||
var q4 = {where: {x: 1, y: date}};
|
||||
should.deepEqual(removeUndefined(q4), {where: {x: 1, y: date}});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('util.parseSettings', function(){
|
||||
it('Parse a full url into a settings object', function() {
|
||||
var url = 'mongodb://x:y@localhost:27017/mydb?w=2';
|
||||
var settings = utils.parseSettings(url);
|
||||
should.equal(settings.hostname, 'localhost');
|
||||
should.equal(settings.port, 27017);
|
||||
should.equal(settings.host, 'localhost');
|
||||
should.equal(settings.user, 'x');
|
||||
should.equal(settings.password, 'y');
|
||||
should.equal(settings.database, 'mydb');
|
||||
should.equal(settings.connector, 'mongodb');
|
||||
should.equal(settings.w, '2');
|
||||
should.equal(settings.url, 'mongodb://x:y@localhost:27017/mydb?w=2');
|
||||
describe('util.parseSettings', function () {
|
||||
it('Parse a full url into a settings object', function () {
|
||||
var url = 'mongodb://x:y@localhost:27017/mydb?w=2';
|
||||
var settings = utils.parseSettings(url);
|
||||
should.equal(settings.hostname, 'localhost');
|
||||
should.equal(settings.port, 27017);
|
||||
should.equal(settings.host, 'localhost');
|
||||
should.equal(settings.user, 'x');
|
||||
should.equal(settings.password, 'y');
|
||||
should.equal(settings.database, 'mydb');
|
||||
should.equal(settings.connector, 'mongodb');
|
||||
should.equal(settings.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() {
|
||||
var url = 'mongodb://localhost:27017/mydb/abc?w=2';
|
||||
var settings = utils.parseSettings(url);
|
||||
should.equal(settings.hostname, 'localhost');
|
||||
should.equal(settings.port, 27017);
|
||||
should.equal(settings.host, 'localhost');
|
||||
should.equal(settings.user, undefined);
|
||||
should.equal(settings.password, undefined);
|
||||
should.equal(settings.database, 'mydb');
|
||||
should.equal(settings.connector, 'mongodb');
|
||||
should.equal(settings.w, '2');
|
||||
should.equal(settings.url, 'mongodb://localhost:27017/mydb/abc?w=2');
|
||||
it('Parse a url without auth into a settings object', function () {
|
||||
var url = 'mongodb://localhost:27017/mydb/abc?w=2';
|
||||
var settings = utils.parseSettings(url);
|
||||
should.equal(settings.hostname, 'localhost');
|
||||
should.equal(settings.port, 27017);
|
||||
should.equal(settings.host, 'localhost');
|
||||
should.equal(settings.user, undefined);
|
||||
should.equal(settings.password, undefined);
|
||||
should.equal(settings.database, 'mydb');
|
||||
should.equal(settings.connector, 'mongodb');
|
||||
should.equal(settings.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() {
|
||||
var url = 'mysql://127.0.0.1:3306/mydb?x[a]=1&x[b]=2&engine=InnoDB';
|
||||
var settings = utils.parseSettings(url);
|
||||
should.equal(settings.hostname, '127.0.0.1');
|
||||
should.equal(settings.port, 3306);
|
||||
should.equal(settings.host, '127.0.0.1');
|
||||
should.equal(settings.user, undefined);
|
||||
should.equal(settings.password, undefined);
|
||||
should.equal(settings.database, 'mydb');
|
||||
should.equal(settings.connector, 'mysql');
|
||||
should.equal(settings.x.a, '1');
|
||||
should.equal(settings.x.b, '2');
|
||||
should.equal(settings.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 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 settings = utils.parseSettings(url);
|
||||
should.equal(settings.hostname, '127.0.0.1');
|
||||
should.equal(settings.port, 3306);
|
||||
should.equal(settings.host, '127.0.0.1');
|
||||
should.equal(settings.user, undefined);
|
||||
should.equal(settings.password, undefined);
|
||||
should.equal(settings.database, 'mydb');
|
||||
should.equal(settings.connector, 'mysql');
|
||||
should.equal(settings.x.a, '1');
|
||||
should.equal(settings.x.b, '2');
|
||||
should.equal(settings.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() {
|
||||
var url = 'memory://?x=1';
|
||||
var settings = utils.parseSettings(url);
|
||||
should.equal(settings.hostname, '');
|
||||
should.equal(settings.user, undefined);
|
||||
should.equal(settings.password, undefined);
|
||||
should.equal(settings.database, undefined);
|
||||
should.equal(settings.connector, 'memory');
|
||||
should.equal(settings.x, '1');
|
||||
should.equal(settings.url, 'memory://?x=1');
|
||||
it('Parse a url without auth into a settings object', function () {
|
||||
var url = 'memory://?x=1';
|
||||
var settings = utils.parseSettings(url);
|
||||
should.equal(settings.hostname, '');
|
||||
should.equal(settings.user, undefined);
|
||||
should.equal(settings.password, undefined);
|
||||
should.equal(settings.database, undefined);
|
||||
should.equal(settings.connector, 'memory');
|
||||
should.equal(settings.x, '1');
|
||||
should.equal(settings.url, 'memory://?x=1');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -5,198 +5,198 @@ var j = require('../'), db, User;
|
|||
var ValidationError = j.ValidationError;
|
||||
|
||||
function getValidAttributes() {
|
||||
return {
|
||||
name: 'Anatoliy',
|
||||
email: 'email@example.com',
|
||||
state: '',
|
||||
age: 26,
|
||||
gender: 'male',
|
||||
createdByAdmin: false,
|
||||
createdByScript: true
|
||||
};
|
||||
return {
|
||||
name: 'Anatoliy',
|
||||
email: 'email@example.com',
|
||||
state: '',
|
||||
age: 26,
|
||||
gender: 'male',
|
||||
createdByAdmin: false,
|
||||
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) {
|
||||
User.destroyAll(function() {
|
||||
delete User._validations;
|
||||
describe('lifecycle', function () {
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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;
|
||||
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();
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('lifecycle', function() {
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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 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();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
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() {
|
||||
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;
|
||||
var user = new User(getValidAttributes())
|
||||
user.isValid().should.be.true;
|
||||
|
||||
user.createdByScript = false;
|
||||
user.isValid().should.be.false;
|
||||
user.errors.domain.should.eql(['can\'t be blank']);
|
||||
|
||||
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() {
|
||||
User.validatesPresenceOf('domain', {unless: 'createdByScript'});
|
||||
|
||||
var user = new User(getValidAttributes())
|
||||
user.isValid().should.be.true;
|
||||
|
||||
user.createdByScript = false;
|
||||
user.isValid().should.be.false;
|
||||
user.errors.domain.should.eql(['can\'t be blank']);
|
||||
|
||||
user.domain = 'domain';
|
||||
user.isValid().should.be.true;
|
||||
it('should handle same object modification', function (done) {
|
||||
User.validatesUniquenessOf('email');
|
||||
var u = new User({email: 'hey'});
|
||||
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('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) {
|
||||
User.validatesUniquenessOf('email');
|
||||
var u = new User({email: 'hey'});
|
||||
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('format', function () {
|
||||
it('should validate format');
|
||||
it('should overwrite default blank message with custom format message');
|
||||
});
|
||||
|
||||
});
|
||||
describe('numericality', function () {
|
||||
it('should validate numericality');
|
||||
});
|
||||
|
||||
describe('format', function() {
|
||||
it('should validate format');
|
||||
it('should overwrite default blank message with custom format message');
|
||||
});
|
||||
describe('inclusion', function () {
|
||||
it('should validate inclusion');
|
||||
});
|
||||
|
||||
describe('numericality', function() {
|
||||
it('should validate numericality');
|
||||
});
|
||||
describe('exclusion', function () {
|
||||
it('should validate exclusion');
|
||||
});
|
||||
|
||||
describe('inclusion', function() {
|
||||
it('should validate inclusion');
|
||||
});
|
||||
describe('length', function () {
|
||||
it('should validate length');
|
||||
});
|
||||
|
||||
describe('exclusion', function() {
|
||||
it('should validate exclusion');
|
||||
});
|
||||
|
||||
describe('length', function() {
|
||||
it('should validate length');
|
||||
});
|
||||
|
||||
describe('custom', function() {
|
||||
it('should validate using custom sync validation');
|
||||
it('should validate using custom async validation');
|
||||
});
|
||||
describe('custom', function () {
|
||||
it('should validate using custom sync validation');
|
||||
it('should validate using custom async validation');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue