Updated docs, updated tests

This commit is contained in:
Ritchie Martori 2013-06-12 15:44:38 -07:00
parent 5df77ac4d0
commit 166451443a
8 changed files with 572 additions and 431 deletions

117
README.md
View File

@ -142,7 +142,7 @@ Validate the model instance.
Attach a model to a [DataSource](#data-source). Attaching a [DataSource](#data-source) updates the model with additional methods and behaviors.
var oracle = asteroid.createDataSource({
connector: 'oracle',
connector: require('asteroid-oracle'),
host: '111.22.333.44',
database: 'MYDB',
username: 'username',
@ -187,7 +187,7 @@ Save specified attributes to the attached data source.
name: 'updatedLast'
}, fn);
##### model.upsert(data, callback)
##### Model.upsert(data, callback)
Update when record with id=data.id found, insert otherwise. **Note:** no setters, validations or hooks applied when using upsert.
@ -335,58 +335,47 @@ Each argument may define any of the [asteroid types](#asteroid-types).
- The callback is an assumed argument and does not need to be specified in the accepts array.
- The err argument is also assumed and does not need to be specified in the returns array.
#### Hooks
Run a function before or after a model method is called.
User.before('save', function(user, next) {
console.log('about to save', user);
next();
});
User.after('save', function(user, next) {
console.log('after save complete', user);
next();
});
Prevent the method from being called by passing an error to `next()`.
User.before('delete', function(user, next) {
// prevent all delete calls
next(new Error('deleting is disabled'));
});
User.after('delete', function(user, next) {
console.log('deleted', user);
next();
});
#### Remote Hooks
Run a function before or after a remote method is called by a client.
User.beforeRemote('save', function(ctx, user, next) {
if(ctx.user.id === user.id) {
// *.save === prototype.save
User.beforeRemote('*.save', function(ctx, user, next) {
if(ctx.user) {
next();
} else {
next(new Error('must be logged in to update'))
}
});
User.afterRemote('save', function(ctx, user, next) {
User.afterRemote('*.save', function(ctx, user, next) {
console.log('user has been saved', user);
next();
});
Remote hooks also support wildcards. Run a function before any remote method is called.
// ** will match both prototype.* and *.*
User.beforeRemote('**', function(ctx, user, next) {
console.log(ctx.methodString, 'was invoked remotely'); // users.prototype.save was invoked remotely
next();
});
Other wildcard examples
// run before any static method eg. User.all
User.beforeRemote('*', ...);
// run before any instance method eg. User.prototype.save
User.beforeRemote('prototype.*', ...);
#### Context
Remote hooks are provided with a Context `ctx` that contains raw access to the transport specific objects. The `ctx` object also has a set of consistent apis that are consistent across transports.
Remote hooks are provided with a Context `ctx` object which contains transport specific data (eg. for http: `req` and `res`). The `ctx` object also has a set of consistent apis across transports.
##### ctx.me
##### ctx.user
The id of the user calling the method remotely. **Note:** this is undefined if a user is not logged in.
A `Model` representing the user calling the method remotely. **Note:** this is undefined if the remote method is not invoked by a logged in user.
##### Rest
@ -442,36 +431,10 @@ Query and create the related models.
TODO: implement / document
#### Model.availableHooks()
Return a list of available hooks.
console.log(User.availableHooks()); // ['save', ...]
#### Shared Methods
Any static or instance method can be decorated as `shared`. These methods are exposed over the provided transport (eg. [asteroid.rest](#rest)).
#### Model.availableMethods()
Returns the currently available api of a model as well as descriptions of any modified behavior or methods from attached data sources.
User.attachTo(oracle);
console.log(User.availableMethods());
Output:
{
'User.all': {
accepts: [{arg: 'filter', type: 'object', description: '...'}],
returns: [{arg: 'users', type: ['User']}]
},
'User.find': {
accepts: [{arg: 'id', type: 'any'}],
returns: [{arg: 'items', type: 'User'}]
},
...
}
### Data Source
@ -512,7 +475,7 @@ Synchronously Discover a set of models based on tables or collections in a data
#### dataSource.defineOperation(name, options, fn)
Define and enable a new operation available to all model's attached to the data source.
Define a new operation available to all model's attached to the data source.
var maps = asteroid.createDataSource({
connector: require('asteroid-rest'),
@ -542,31 +505,14 @@ Define and enable a new operation available to all model's attached to the data
console.log(point.lat, point.long); // 24.224424 44.444445
});
#### dataSource.enable(operation)
#### dataSource.enableRemote(operation)
Enable a data source operation. Each [connector](#connector) has its own set of set enabled and disabled operations. You can always list these by calling `dataSource.operations()`.
// all rest data source operations are
// disabled by default
var twitter = asteroid.createDataSource({
connector: require('asteroid-rest'),
url: 'http://api.twitter.com'
});
Enable remote access to a data source operation. Each [connector](#connector) has its own set of set remotely enabled and disabled operations. You can always list these by calling `dataSource.operations()`.
// enable an operation
twitter.enable('find');
// enable remote access
twitter.enableRemote('find')
**Notes:**
- only enabled operations will be added to attached models
- data sources must enable / disable operations before attaching or creating models
#### dataSource.disableRemote(operation)
#### dataSource.disable(operation)
Disable a data source operation. Each [connector](#connector) has its own set of set enabled and disabled operations. You can always list these by calling `dataSource.operations()`.
Disable remote access to a data source operation. Each [connector](#connector) has its own set of set enabled and disabled operations. You can always list these by calling `dataSource.operations()`.
// all rest data source operations are
// disabled by default
@ -575,9 +521,6 @@ Disable a data source operation. Each [connector](#connector) has its own set of
host: '...',
...
});
// disable an operation completely
oracle.disable('destroyAll');
// or only disable it as a remote method
oracle.disableRemote('destroyAll');

View File

@ -54,6 +54,7 @@ app._models = [];
app.model = function (Model) {
this._models.push(Model);
Model.app = this;
}
/**
@ -74,15 +75,12 @@ app.remoteObjects = function () {
var models = this.models();
// add in models
Object.keys(models)
.forEach(function (name) {
var ModelCtor = models[name];
// only add shared models
if(ModelCtor.shared && typeof ModelCtor.sharedCtor === 'function') {
result[name] = ModelCtor;
}
});
models.forEach(function (ModelCtor) {
// only add shared models
if(ModelCtor.shared && typeof ModelCtor.sharedCtor === 'function') {
result[ModelCtor.pluralModelName] = ModelCtor;
}
});
return result;
}

View File

@ -81,7 +81,13 @@ asteroid.errorHandler.title = 'Asteroid';
*/
asteroid.createDataSource = function (name, options) {
return new DataSource(name, options);
var ds = new DataSource(name, options);
ds.createModel = function (name, properties, settings) {
var Model = asteroid.createModel(name, properties, settings);
Model.attachTo(ds);
return Model;
}
return ds;
}
/**
@ -93,8 +99,102 @@ asteroid.createDataSource = function (name, options) {
*/
asteroid.createModel = function (name, properties, options) {
assert(typeof name === 'string', 'Cannot create a model without a name');
var mb = new ModelBuilder();
return mb.define(name, properties, arguments);
var ModelCtor = mb.define(name, properties, arguments);
var hasMany = ModelCtor.hasMany;
if(hasMany) {
ModelCtor.hasMany = function (anotherClass, params) {
var origArgs = arguments;
var thisClass = this, 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.schema.models) {
if (name.toLowerCase() === anotherClassName) {
anotherClass = this.schema.models[name];
}
}
}
}
var pluralized = i8n.pluralize(anotherClass.modelName);
var methodName = params.as ||
i8n.camelize(pluralized, true);
var proxyMethodName = 'get' + i8n.titleize(pluralized, true);
// create a proxy method
var fn = this.prototype[proxyMethodName] = function () {
// this cannot be a shared method
// because it is defined when you
// inside a property getter...
this[methodName].apply(thisClass, arguments);
};
fn.shared = true;
fn.http = {verb: 'get', path: '/' + methodName};
hasMany.apply(this, arguments);
};
}
ModelCtor.shared = true;
ModelCtor.sharedCtor = function (data, id, fn) {
if(typeof data === 'function') {
fn = data;
data = null;
id = null;
} else if (typeof id === 'function') {
fn = id;
id = null;
}
if(id && data) {
var model = new ModelCtor(data);
model.id = id;
fn(null, model);
} else if(data) {
fn(null, new ModelCtor(data));
} else if(id) {
ModelCtor.find(id, fn);
} else {
fn(new Error('must specify an id or data'));
}
};
ModelCtor.sharedCtor.accepts = [
{arg: 'data', type: 'object'},
{arg: 'id', type: 'any'}
];
ModelCtor.sharedCtor.http = [
{path: '/'},
{path: '/:id'}
];
// before remote hook
ModelCtor.beforeRemote = function (name, fn) {
var remotes = this.app.remotes();
remotes.before(ModelCtor.pluralModelName + '.' + name, function (ctx, next) {
fn(ctx, ctx.instance, next);
});
}
// after remote hook
ModelCtor.afterRemote = function (name, fn) {
var remotes = this.app.remotes();
remotes.before(ModelCtor.pluralModelName + '.' + name, function (ctx, next) {
fn(ctx, ctx.instance, next);
});
}
return ModelCtor;
}
/**

View File

@ -14,7 +14,9 @@
"inflection": "~1.2.5"
},
"devDependencies": {
"mocha": "latest"
"mocha": "latest",
"sl-task-emitter": "0.0.x",
"supertest": "latest"
},
"optionalDependencies": {
"jugglingdb-oracle": "git+ssh://git@github.com:strongloop/jugglingdb-oracle.git"

View File

@ -81,53 +81,12 @@ describe('DataSource', function() {
// });
// });
describe('dataSource.enable(operation)', function() {
it("Enable a data source operation", function() {
// enable an operation
memory.disable('find');
var find = memory.getOperation('find');
assert.equal(find.name, 'find');
assert.equal(find.enabled, false);
assert.equal(find.remoteEnabled, false);
memory.enable('find');
assert.equal(find.name, 'find');
assert.equal(find.enabled, true);
assert.equal(find.remoteEnabled, false);
memory.enableRemote('find');
assert.equal(find.remoteEnabled, true);
});
});
describe('dataSource.disable(operation)', function() {
it("Disable a data source operation", function() {
var find = memory.getOperation('all');
assert.equal(find.name, 'all');
assert.equal(find.enabled, true);
assert.equal(find.remoteEnabled, true);
memory.disableRemote('all');
assert.equal(find.name, 'all');
assert.equal(find.enabled, true);
assert.equal(find.remoteEnabled, false);
memory.disable('all');
assert.equal(find.name, 'all');
assert.equal(find.enabled, false);
assert.equal(find.remoteEnabled, false);
});
});
describe('dataSource.operations()', function() {
it("List the enabled and disabled operations.", function() {
// assert the defaults
// - true: the method should be remote enabled
// - false: the method should not be remote enabled
// -
existsAndShared('_forDB', false);
existsAndShared('create', true);
existsAndShared('updateOrCreate', false);
@ -154,7 +113,6 @@ describe('DataSource', function() {
function existsAndShared(name, isRemoteEnabled) {
var op = memory.getOperation(name);
console.log(op.name, op.remoteEnabled, isRemoteEnabled);
assert(op.remoteEnabled === isRemoteEnabled, name + ' ' + (isRemoteEnabled ? 'should' : 'should not') + ' be remote enabled');
}
});

View File

@ -1,41 +1,41 @@
describe('GeoPoint', function() {
describe('geoPoint.distanceTo(geoPoint, options)', function() {
it("Get the distance to another `GeoPoint`.", function(done) {
/* example -
var here = new GeoPoint({lat: 10, long: 10});
var there = new GeoPoint({lat: 5, long: 5});
console.log(here.distanceTo(there, {type: 'miles'})); // 438
*/
done(new Error('test not implemented'));
});
});
describe('GeoPoint.distanceBetween(a, b, options)', function() {
it("Get the distance between two points.", function(done) {
/* example -
GeoPoint.distanceBetween(here, there, {type: 'miles'}) // 438
*/
done(new Error('test not implemented'));
});
});
describe('geoPoint.lat', function() {
it("The latitude point in degrees", function(done) {
done(new Error('test not implemented'));
});
});
describe('geoPoint.long', function() {
it("The longitude point in degrees", function(done) {
/* example -
app.use(asteroid.rest());
app.use(asteroid.sio);
*/
done(new Error('test not implemented'));
});
});
});
// describe('GeoPoint', function() {
//
// describe('geoPoint.distanceTo(geoPoint, options)', function() {
// it("Get the distance to another `GeoPoint`.", function(done) {
// /* example -
// var here = new GeoPoint({lat: 10, long: 10});
// var there = new GeoPoint({lat: 5, long: 5});
// console.log(here.distanceTo(there, {type: 'miles'})); // 438
// */
// done(new Error('test not implemented'));
// });
// });
//
// describe('GeoPoint.distanceBetween(a, b, options)', function() {
// it("Get the distance between two points.", function(done) {
// /* example -
// GeoPoint.distanceBetween(here, there, {type: 'miles'}) // 438
// */
// done(new Error('test not implemented'));
// });
// });
//
// describe('geoPoint.lat', function() {
// it("The latitude point in degrees", function(done) {
// done(new Error('test not implemented'));
// });
// });
//
// describe('geoPoint.long', function() {
// it("The longitude point in degrees", function(done) {
// /* example -
// app.use(asteroid.rest());
//
//
// app.use(asteroid.sio);
//
// */
// done(new Error('test not implemented'));
// });
// });
// });

View File

@ -1,195 +1,250 @@
describe('Model', function() {
var User, memory;
beforeEach(function () {
memory = asteroid.createDataSource({connector: asteroid.Memory});
User = memory.createModel('user', {
'first': String,
'last': String,
'age': Number,
'password': String,
'gender': String,
'domain': String,
'email': String
});
})
describe('Model.validatesPresenceOf(properties...)', function() {
it("Require a model to include a property to be considered valid.", function(done) {
/* example -
it("Require a model to include a property to be considered valid.", function() {
User.validatesPresenceOf('first', 'last', 'age');
*/
done(new Error('test not implemented'));
var joe = new User({first: 'joe'});
assert(joe.isValid() === false, 'model should not validate');
assert(joe.errors.last, 'should have a missing last error');
assert(joe.errors.age, 'should have a missing age error');
});
});
describe('Model.validatesLengthOf(property, options)', function() {
it("Require a property length to be within a specified range.", function(done) {
/* example -
it("Require a property length to be within a specified range.", function() {
User.validatesLengthOf('password', {min: 5, message: {min: 'Password is too short'}});
*/
done(new Error('test not implemented'));
var joe = new User({password: '1234'});
assert(joe.isValid() === false, 'model should not be valid');
assert(joe.errors.password, 'should have password error');
});
});
describe('Model.validatesInclusionOf(property, options)', function() {
it("Require a value for `property` to be in the specified array.", function(done) {
/* example -
it("Require a value for `property` to be in the specified array.", function() {
User.validatesInclusionOf('gender', {in: ['male', 'female']});
*/
done(new Error('test not implemented'));
var foo = new User({gender: 'bar'});
assert(foo.isValid() === false, 'model should not be valid');
assert(foo.errors.gender, 'should have gender error');
});
});
describe('Model.validatesExclusionOf(property, options)', function() {
it("Require a value for `property` to not exist in the specified array.", function(done) {
/* example -
it("Require a value for `property` to not exist in the specified array.", function() {
User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']});
*/
done(new Error('test not implemented'));
var foo = new User({domain: 'www'});
var bar = new User({domain: 'billing'});
var bat = new User({domain: 'admin'});
assert(foo.isValid() === false);
assert(bar.isValid() === false);
assert(bat.isValid() === false);
assert(foo.errors.domain, 'model should have a domain error');
assert(bat.errors.domain, 'model should have a domain error');
assert(bat.errors.domain, 'model should have a domain error');
});
});
describe('Model.validatesNumericalityOf(property, options)', function() {
it("Require a value for `property` to be a specific type of `Number`.", function(done) {
/* example -
it("Require a value for `property` to be a specific type of `Number`.", function() {
User.validatesNumericalityOf('age', {int: true});
*/
done(new Error('test not implemented'));
var joe = new User({age: 10.2});
assert(joe.isValid() === false);
var bob = new User({age: 0});
assert(bob.isValid() === true);
assert(joe.errors.age, 'model should have an age error');
});
});
describe('Model.validatesUniquenessOf(property, options)', function() {
it("Ensure the value for `property` is unique.", function(done) {
/* example -
User.validatesUniquenessOf('email', {message: 'email is not unique'});
*/
done(new Error('test not implemented'));
var joe = new User({email: 'joe@joe.com'});
var joe2 = new User({email: 'joe@joe.com'});
joe.save(function () {
joe2.save(function (err) {
assert(err, 'should get a validation error');
assert(joe2.errors.email, 'model should have email error');
done();
});
});
});
});
describe('myModel.isValid()', function() {
it("Validate the model instance.", function(done) {
/* example -
user.isValid(function (valid) {
if (!valid) {
user.errors // hash of errors {attr: [errmessage, errmessage, ...], attr: ...}
}
});
*/
done(new Error('test not implemented'));
it("Validate the model instance.", function() {
User.validatesNumericalityOf('age', {int: true});
var user = new User({first: 'joe', age: 'flarg'})
var valid = user.isValid();
assert(valid === false);
assert(user.errors.age, 'model should have age error');
});
});
describe('Model.attachTo(dataSource)', function() {
it("Attach a model to a [DataSource](#data-source)", function(done) {
/* example -
var oracle = asteroid.createDataSource({
connector: 'oracle',
host: '111.22.333.44',
database: 'MYDB',
username: 'username',
password: 'password'
});
User.attachTo(oracle);
it("Attach a model to a [DataSource](#data-source)", function() {
var MyModel = asteroid.createModel('my-model', {name: String});
*/
done(new Error('test not implemented'));
assert(MyModel.all === undefined, 'should not have data access methods');
MyModel.attachTo(memory);
assert(typeof MyModel.all === 'function', 'should have data access methods after attaching to a data source');
});
});
describe('Model.create([data], [callback])', function() {
it("Create an instance of Model with given data and save to the attached data source.", function(done) {
/* example -
User.create({first: 'Joe', last: 'Bob'}, function(err, user) {
console.log(user instanceof User); // true
assert(user instanceof User);
done();
});
*/
done(new Error('test not implemented'));
});
});
describe('model.save([options], [callback])', function() {
it("Save an instance of a Model to the attached data source.", function(done) {
/* example -
var joe = new User({first: 'Joe', last: 'Bob'});
joe.save(function(err, user) {
if(user.errors) {
console.log(user.errors);
} else {
console.log(user.id);
}
assert(user.id);
assert(!err);
assert(!user.errors);
done();
});
*/
done(new Error('test not implemented'));
});
});
describe('model.updateAttributes(data, [callback])', function() {
it("Save specified attributes to the attached data source.", function(done) {
/* example -
user.updateAttributes({
first: 'updatedFirst',
name: 'updatedLast'
}, fn);
*/
done(new Error('test not implemented'));
User.create({first: 'joe', age: 100}, function (err, user) {
assert(!err);
assert.equal(user.first, 'joe');
user.updateAttributes({
first: 'updatedFirst',
last: 'updatedLast'
}, function (err, updatedUser) {
assert(!err);
assert.equal(updatedUser.first, 'updatedFirst');
assert.equal(updatedUser.last, 'updatedLast');
assert.equal(updatedUser.age, 100);
done();
});
});
});
});
describe('model.upsert(data, callback)', function() {
describe('Model.upsert(data, callback)', function() {
it("Update when record with id=data.id found, insert otherwise", function(done) {
/* example -
*/
done(new Error('test not implemented'));
User.upsert({first: 'joe', id: 7}, function (err, user) {
assert(!err);
assert.equal(user.first, 'joe');
User.upsert({first: 'bob', id: 7}, function (err, updatedUser) {
assert(!err);
assert.equal(updatedUser.first, 'bob');
done();
});
});
});
});
describe('model.destroy([callback])', function() {
it("Remove a model from the attached data source.", function(done) {
/* example -
model.destroy(function(err) {
// model instance destroyed
User.create({first: 'joe', last: 'bob'}, function (err, user) {
User.find(user.id, function (err, foundUser) {
assert.equal(user.id, foundUser.id);
foundUser.destroy(function () {
User.find(user.id, function (err, notFound) {
assert(!err);
assert.equal(notFound, null);
done();
});
});
});
});
*/
done(new Error('test not implemented'));
});
});
describe('Model.destroyAll(callback)', function() {
it("Delete all Model instances from data source", function(done) {
done(new Error('test not implemented'));
(new TaskEmitter())
.task(User, 'create', {first: 'jill'})
.task(User, 'create', {first: 'bob'})
.task(User, 'create', {first: 'jan'})
.task(User, 'create', {first: 'sam'})
.task(User, 'create', {first: 'suzy'})
.on('done', function () {
User.count(function (err, count) {
assert.equal(count, 5);
User.destroyAll(function () {
User.count(function (err, count) {
assert.equal(count, 0);
done();
});
});
});
});
});
});
describe('Model.find(id, callback)', function() {
it("Find instance by id.", function(done) {
/* example -
User.find(23, function(err, user) {
console.info(user.id); // 23
User.create({first: 'michael', last: 'jordan', id: 23}, function () {
User.find(23, function (err, user) {
assert.equal(user.id, 23);
assert.equal(user.first, 'michael');
assert.equal(user.last, 'jordan');
done();
});
});
User.all({where: {age: {gt: 21}}, order: 'age DESC', limit: 10, skip: 10})
*/
done(new Error('test not implemented'));
});
});
describe('Model.count([query], callback)', function() {
it("Query count of Model instances in data source", function(done) {
/* example -
User.count({approved: true}, function(err, count) {
console.log(count); // 2081
});
User.login = function (username, password, fn) {
var passwordHash = hashPassword(password);
this.findOne({username: username}, function (err, user) {
var failErr = new Error('login failed');
if(err) {
fn(err);
} else if(!user) {
fn(failErr);
} else if(user.password === passwordHash) {
MySessionModel.create({userId: user.id}, function (err, session) {
fn(null, session.id);
});
} else {
fn(failErr);
}
(new TaskEmitter())
.task(User, 'create', {first: 'jill', age: 100})
.task(User, 'create', {first: 'bob', age: 200})
.task(User, 'create', {first: 'jan'})
.task(User, 'create', {first: 'sam'})
.task(User, 'create', {first: 'suzy'})
.on('done', function () {
User.count({age: {gt: 99}}, function (err, count) {
assert.equal(count, 2);
done();
});
});
});
});
describe('Remote Methods', function(){
beforeEach(function () {
User.login = function (username, password, fn) {
if(username === 'foo' && password === 'bar') {
fn(null, 123);
} else {
throw new Error('bad username and password!');
}
}
asteroid.remoteMethod(
User,
User.login,
{
accepts: [
@ -197,167 +252,250 @@ describe('Model', function() {
{arg: 'password', type: 'string', required: true}
],
returns: {arg: 'sessionId', type: 'any'},
http: {path: '/sign-in'}
http: {path: '/sign-in', verb: 'get'}
}
);
User.prototype.logout = function (fn) {
MySessionModel.destroyAll({userId: this.id}, fn);
}
asteroid.remoteMethod(User, User.prototype.logout);
*/
done(new Error('test not implemented'));
app.use(asteroid.rest());
app.model(User);
});
});
describe('example remote method', function () {
it('should allow calling remotely', function(done) {
request(app)
.get('/users/sign-in?username=foo&password=bar')
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res){
if(err) return done(err);
assert(res.body.$data === 123);
done();
});
});
});
describe('Model.beforeRemote(name, fn)', function(){
it('Run a function before a remote method is called by a client.', function(done) {
var hookCalled = false;
User.beforeRemote('*.save', function(ctx, user, next) {
hookCalled = true;
next();
});
// invoke save
request(app)
.post('/users')
.send({data: {first: 'foo', last: 'bar'}})
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if(err) return done(err);
assert(hookCalled, 'hook wasnt called');
done();
});
});
});
describe('Model.afterRemote(name, fn)', function(){
it('Run a function after a remote method is called by a client.', function(done) {
var beforeCalled = false;
var afterCalled = false;
User.beforeRemote('*.save', function(ctx, user, next) {
assert(!afterCalled);
beforeCalled = true;
next();
});
User.afterRemote('*.save', function(ctx, user, next) {
assert(beforeCalled);
afterCalled = true;
next();
});
// invoke save
request(app)
.post('/users')
.send({data: {first: 'foo', last: 'bar'}})
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if(err) return done(err);
assert(beforeCalled, 'before hook was not called');
assert(afterCalled, 'after hook was not called');
done();
});
});
});
describe('Remote Method invoking context', function () {
// describe('ctx.user', function() {
// it("The remote user model calling the method remotely", function(done) {
// done(new Error('test not implemented'));
// });
// });
describe('ctx.me', function() {
it("The id of the user calling the method remotely", function(done) {
done(new Error('test not implemented'));
});
});
describe('ctx.req', function() {
it("The express ServerRequest object", function(done) {
var hookCalled = false;
User.beforeRemote('*.save', function(ctx, user, next) {
hookCalled = true;
assert(ctx.req);
assert(ctx.req.url);
assert(ctx.req.method);
assert(ctx.res);
assert(ctx.res.write);
assert(ctx.res.end);
next();
});
// invoke save
request(app)
.post('/users')
.send({data: {first: 'foo', last: 'bar'}})
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if(err) return done(err);
assert(hookCalled);
done();
});
});
});
describe('ctx.req', function() {
it("The express ServerRequest object", function(done) {
done(new Error('test not implemented'));
});
});
describe('ctx.res', function() {
it("The express ServerResponse object", function(done) {
/* example -
*/
done(new Error('test not implemented'));
});
describe('ctx.res', function() {
it("The express ServerResponse object", function(done) {
var hookCalled = false;
User.beforeRemote('*.save', function(ctx, user, next) {
hookCalled = true;
assert(ctx.req);
assert(ctx.req.url);
assert(ctx.req.method);
assert(ctx.res);
assert(ctx.res.write);
assert(ctx.res.end);
next();
});
// invoke save
request(app)
.post('/users')
.send({data: {first: 'foo', last: 'bar'}})
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res) {
if(err) return done(err);
assert(hookCalled);
done();
});
});
});
})
});
describe('Model.hasMany(Model)', function() {
it("Define a one to many relationship.", function(done) {
/* example -
var Book = memory.createModel('book', {title: String, author: String});
var Chapter = memory.createModel('chapter', {title: String});
// by referencing model
Book.hasMany(Chapter);
// specify the name
Book.hasMany('chapters', {model: Chapter});
Book.create(function(err, book) {
Book.create({title: 'Into the Wild', author: 'Jon Krakauer'}, function(err, book) {
// using 'chapters' scope for build:
var c = book.chapters.build({name: 'Chapter 1'});
// same as:
c = new Chapter({name: 'Chapter 1', bookId: book.id});
// using 'chapters' scope for create:
book.chapters.create();
// same as:
Chapter.create({bookId: book.id});
// using scope for querying:
book.chapters(function(err, chapters) {
// all chapters with bookId = book.id
});
book.chapters({where: {name: 'test'}}, function(err, chapters) {
// all chapters with bookId = book.id and name = 'test'
var c = book.chapters.build({title: 'Chapter 1'});
book.chapters.create({title: 'Chapter 2'}, function () {
c.save(function () {
Chapter.count({bookId: book.id}, function (err, count) {
assert.equal(count, 2);
book.chapters({where: {title: 'Chapter 1'}}, function(err, chapters) {
assert.equal(chapters.length, 1);
assert.equal(chapters[0].title, 'Chapter 1');
done();
});
});
});
});
});
*/
done(new Error('test not implemented'));
});
});
describe('Model.hasAndBelongsToMany()', function() {
it("TODO: implement / document", function(done) {
/* example -
*/
done(new Error('test not implemented'));
});
});
// describe('Model.hasAndBelongsToMany()', function() {
// it("TODO: implement / document", function(done) {
// /* example -
//
// */
// done(new Error('test not implemented'));
// });
// });
describe('Model.availableHooks()', function() {
it("Return a list of available hooks.", function(done) {
/* example -
console.log(User.availableHooks()); // ['save', ...]
*/
done(new Error('test not implemented'));
});
});
// describe('Model.remoteMethods()', function() {
// it("Return a list of enabled remote methods.", function() {
// app.model(User);
// User.remoteMethods(); // ['save', ...]
// });
// });
describe('Model.availableMethods()', function() {
it("Returns the currently available api of a model as well as descriptions of any modified behavior or methods from attached data sources.", function(done) {
/* example -
User.attachTo(oracle);
console.log(User.availableMethods());
{
'User.all': {
accepts: [{arg: 'filter', type: 'object', description: '...'}],
returns: [{arg: 'users', type: ['User']}]
},
'User.find': {
accepts: [{arg: 'id', type: 'any'}],
returns: [{arg: 'items', type: 'User'}]
},
...
}
var oracle = asteroid.createDataSource({
connector: 'oracle',
host: '111.22.333.44',
database: 'MYDB',
username: 'username',
password: 'password'
});
*/
done(new Error('test not implemented'));
});
});
// describe('Model.availableMethods()', function() {
// it("Returns the currently available api of a model as well as descriptions of any modified behavior or methods from attached data sources.", function(done) {
// /* example -
// User.attachTo(oracle);
// console.log(User.availableMethods());
//
// {
// 'User.all': {
// accepts: [{arg: 'filter', type: 'object', description: '...'}],
// returns: [{arg: 'users', type: ['User']}]
// },
// 'User.find': {
// accepts: [{arg: 'id', type: 'any'}],
// returns: [{arg: 'items', type: 'User'}]
// },
// ...
// }
// var oracle = asteroid.createDataSource({
// connector: 'oracle',
// host: '111.22.333.44',
// database: 'MYDB',
// username: 'username',
// password: 'password'
// });
//
// */
// done(new Error('test not implemented'));
// });
// });
describe('Model.before(name, fn)', function(){
it('Run a function before a method is called.', function() {
// User.before('save', function(user, next) {
// console.log('about to save', user);
//
// next();
// });
//
// User.before('delete', function(user, next) {
// // prevent all delete calls
// next(new Error('deleting is disabled'));
// });
// User.beforeRemote('save', function(ctx, user, next) {
// if(ctx.user.id === user.id) {
// next();
// } else {
// next(new Error('must be logged in to update'))
// }
// });
throw new Error('not implemented');
});
});
describe('Model.after(name, fn)', function(){
it('Run a function after a method is called.', function() {
throw new Error('not implemented');
});
});
describe('Model.beforeRemote(name, fn)', function(){
it('Run a function before a remote method is called by a client.', function() {
throw new Error('not implemented');
});
});
describe('Model.afterRemote(name, fn)', function(){
it('Run a function after a remote method is called by a client.', function() {
throw new Error('not implemented');
});
});
// describe('Model.before(name, fn)', function(){
// it('Run a function before a method is called.', function() {
// // User.before('save', function(user, next) {
// // console.log('about to save', user);
// //
// // next();
// // });
// //
// // User.before('delete', function(user, next) {
// // // prevent all delete calls
// // next(new Error('deleting is disabled'));
// // });
// // User.beforeRemote('save', function(ctx, user, next) {
// // if(ctx.user.id === user.id) {
// // next();
// // } else {
// // next(new Error('must be logged in to update'))
// // }
// // });
//
// throw new Error('not implemented');
// });
// });
//
// describe('Model.after(name, fn)', function(){
// it('Run a function after a method is called.', function() {
//
// throw new Error('not implemented');
// });
// });
});

View File

@ -6,6 +6,8 @@ assert = require('assert');
asteroid = require('../');
memoryConnector = asteroid.Memory;
app = null;
TaskEmitter = require('sl-task-emitter');
request = require('supertest');
beforeEach(function () {
app = asteroid();