Merge branch 'master' of github.com:strongloop/asteroid
This commit is contained in:
commit
e910a834a6
118
README.md
118
README.md
|
@ -90,6 +90,8 @@ Define an asteroid model.
|
||||||
age: Number
|
age: Number
|
||||||
});
|
});
|
||||||
|
|
||||||
|
### Validation (expiremental)
|
||||||
|
|
||||||
#### Model.validatesPresenceOf(properties...)
|
#### Model.validatesPresenceOf(properties...)
|
||||||
|
|
||||||
Require a model to include a property to be considered valid.
|
Require a model to include a property to be considered valid.
|
||||||
|
@ -154,9 +156,11 @@ Attach a model to a [DataSource](#data-source). Attaching a [DataSource](#data-s
|
||||||
|
|
||||||
**Note:** until a model is attached to a data source it will **not** have any **attached methods**.
|
**Note:** until a model is attached to a data source it will **not** have any **attached methods**.
|
||||||
|
|
||||||
#### Attached Methods
|
#### CRUD and Query Mixins
|
||||||
|
|
||||||
Attached methods are added by attaching a vanilla model to a data source with a connector. Each [connector](#connectors) enables its own set of operations that are attached to a `Model` as methods. To see available methods for a data source with a connector call `dataSource.operations()`.
|
Mixins are added by attaching a vanilla model to a data source with a connector. Each [connector](#connectors) enables its own set of operations that are attached to a `Model` as methods. To see available methods for a data source with a connector call `dataSource.operations()`.
|
||||||
|
|
||||||
|
#### Static Methods
|
||||||
|
|
||||||
##### Model.create([data], [callback])
|
##### Model.create([data], [callback])
|
||||||
|
|
||||||
|
@ -166,53 +170,15 @@ Create an instance of Model with given data and save to the attached data source
|
||||||
console.log(user instanceof User); // true
|
console.log(user instanceof User); // true
|
||||||
});
|
});
|
||||||
|
|
||||||
##### model.save([options], [callback])
|
##### Model.count([query], callback)
|
||||||
|
|
||||||
Save an instance of a Model to the attached data source.
|
Query count of Model instances in data source. Optional query param allows to count filtered set of Model instances.
|
||||||
|
|
||||||
var joe = new User({first: 'Joe', last: 'Bob'});
|
User.count({approved: true}, function(err, count) {
|
||||||
joe.save(function(err, user) {
|
console.log(count); // 2081
|
||||||
if(user.errors) {
|
|
||||||
console.log(user.errors);
|
|
||||||
} else {
|
|
||||||
console.log(user.id);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
##### model.updateAttributes(data, [callback])
|
##### Model.all(filter, callback)
|
||||||
|
|
||||||
Save specified attributes to the attached data source.
|
|
||||||
|
|
||||||
user.updateAttributes({
|
|
||||||
first: 'updatedFirst',
|
|
||||||
name: 'updatedLast'
|
|
||||||
}, fn);
|
|
||||||
|
|
||||||
##### Model.upsert(data, callback)
|
|
||||||
|
|
||||||
Update when record with id=data.id found, insert otherwise. **Note:** no setters, validations or hooks applied when using upsert.
|
|
||||||
|
|
||||||
##### model.destroy([callback])
|
|
||||||
|
|
||||||
Remove a model from the attached data source.
|
|
||||||
|
|
||||||
model.destroy(function(err) {
|
|
||||||
// model instance destroyed
|
|
||||||
});
|
|
||||||
|
|
||||||
##### Model.destroyAll(callback)
|
|
||||||
|
|
||||||
Delete all Model instances from data source. **Note:** destroyAll method does not perform destroy hooks.
|
|
||||||
|
|
||||||
##### Model.find(id, callback)
|
|
||||||
|
|
||||||
Find instance by id.
|
|
||||||
|
|
||||||
User.find(23, function(err, user) {
|
|
||||||
console.info(user.id); // 23
|
|
||||||
});
|
|
||||||
|
|
||||||
Model.all(filter, callback);
|
|
||||||
|
|
||||||
Find all instances of Model, matched by query. Fields used for filter and sort should be declared with `{index: true}` in model definition.
|
Find all instances of Model, matched by query. Fields used for filter and sort should be declared with `{index: true}` in model definition.
|
||||||
|
|
||||||
|
@ -230,15 +196,31 @@ Find the second page of 10 users over age 21 in descending order.
|
||||||
|
|
||||||
**Note:** See the specific connector's [docs](#connectors) for more info.
|
**Note:** See the specific connector's [docs](#connectors) for more info.
|
||||||
|
|
||||||
##### Model.count([query], callback)
|
##### Model.destroyAll(callback)
|
||||||
|
|
||||||
Query count of Model instances in data source. Optional query param allows to count filtered set of Model instances.
|
Delete all Model instances from data source. **Note:** destroyAll method does not perform destroy hooks.
|
||||||
|
|
||||||
User.count({approved: true}, function(err, count) {
|
##### Model.find(id, callback)
|
||||||
console.log(count); // 2081
|
|
||||||
|
Find instance by id.
|
||||||
|
|
||||||
|
User.find(23, function(err, user) {
|
||||||
|
console.info(user.id); // 23
|
||||||
});
|
});
|
||||||
|
|
||||||
#### Static Methods
|
##### Model.findOne(filter, callback)
|
||||||
|
|
||||||
|
Find a single instance that matches the given filter.
|
||||||
|
|
||||||
|
User.find(23, function(err, user) {
|
||||||
|
console.info(user.id); // 23
|
||||||
|
});
|
||||||
|
|
||||||
|
##### Model.upsert(data, callback)
|
||||||
|
|
||||||
|
Update when record with id=data.id found, insert otherwise. **Note:** no setters, validations or hooks applied when using upsert.
|
||||||
|
|
||||||
|
##### Custom Static Methods
|
||||||
|
|
||||||
Define a static model method.
|
Define a static model method.
|
||||||
|
|
||||||
|
@ -277,6 +259,38 @@ Setup the static model method to be exposed to clients as a [remote method](#rem
|
||||||
|
|
||||||
#### Instance Methods
|
#### Instance Methods
|
||||||
|
|
||||||
|
##### model.save([options], [callback])
|
||||||
|
|
||||||
|
Save an instance of a Model to the attached data source.
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
##### model.updateAttributes(data, [callback])
|
||||||
|
|
||||||
|
Save specified attributes to the attached data source.
|
||||||
|
|
||||||
|
user.updateAttributes({
|
||||||
|
first: 'updatedFirst',
|
||||||
|
name: 'updatedLast'
|
||||||
|
}, fn);
|
||||||
|
|
||||||
|
##### model.destroy([callback])
|
||||||
|
|
||||||
|
Remove a model from the attached data source.
|
||||||
|
|
||||||
|
model.destroy(function(err) {
|
||||||
|
// model instance destroyed
|
||||||
|
});
|
||||||
|
|
||||||
|
##### Custom Instance Methods
|
||||||
|
|
||||||
Define an instance method.
|
Define an instance method.
|
||||||
|
|
||||||
User.prototype.logout = function (fn) {
|
User.prototype.logout = function (fn) {
|
||||||
|
@ -289,7 +303,7 @@ Define a remote model instance method.
|
||||||
|
|
||||||
#### Remote Methods
|
#### Remote Methods
|
||||||
|
|
||||||
Both instance and static methods can be exposed to clients. A remote method must accept a callback with the conventional `fn(err, result, ...)` signature.
|
Both instance and static methods can be exposed to clients. A remote method must accept a callback with the conventional `fn(err, result, ...)` signature.
|
||||||
|
|
||||||
##### asteroid.remoteMethod(fn, [options]);
|
##### asteroid.remoteMethod(fn, [options]);
|
||||||
|
|
||||||
|
@ -312,7 +326,7 @@ Expose a remote method.
|
||||||
- **accepts** - (optional) an arguments description specifying the remote method's arguments. A
|
- **accepts** - (optional) an arguments description specifying the remote method's arguments. A
|
||||||
- **returns** - (optional) an arguments description specifying the remote methods callback arguments.
|
- **returns** - (optional) an arguments description specifying the remote methods callback arguments.
|
||||||
- **http** - (advanced / optional, object) http routing info
|
- **http** - (advanced / optional, object) http routing info
|
||||||
- **http.path** - the relative path the method will be exposed at. May be a path fragment (eg. '/:myArg') which will be populated by an arg of the same name in the accepts description.
|
- **http.path** - the path relative to the model the method will be exposed at. May be a path fragment (eg. '/:myArg') which will be populated by an arg of the same name in the accepts description. For example the stats method above will be at the whole path `/products/stats`.
|
||||||
- **http.verb** - (get, post, put, del, all) - the route verb the method will be available from.
|
- **http.verb** - (get, post, put, del, all) - the route verb the method will be available from.
|
||||||
|
|
||||||
**Argument Description**
|
**Argument Description**
|
||||||
|
|
144
gen-tests.js
144
gen-tests.js
|
@ -1,144 +0,0 @@
|
||||||
/**
|
|
||||||
* Generate asteroid unit tests from README...
|
|
||||||
*/
|
|
||||||
|
|
||||||
fs = require('fs')
|
|
||||||
|
|
||||||
readme = fs.readFileSync('../README.md').toString();
|
|
||||||
|
|
||||||
alias = {
|
|
||||||
myModel: 'Model',
|
|
||||||
model: 'Model',
|
|
||||||
ctx: 'Model',
|
|
||||||
dataSource: 'DataSource',
|
|
||||||
geoPoint: 'GeoPoint'
|
|
||||||
};
|
|
||||||
|
|
||||||
function getName(line) {
|
|
||||||
var name = line
|
|
||||||
.split('.')[0]
|
|
||||||
.replace(/#+\s/, '');
|
|
||||||
|
|
||||||
return alias[name] || name;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Doc(line, lineNum, docIndex) {
|
|
||||||
this.name = getName(line);
|
|
||||||
|
|
||||||
line = line.replace(/#+\s/, '');
|
|
||||||
|
|
||||||
this.line = line;
|
|
||||||
this.lineNum = lineNum;
|
|
||||||
this.docIndex = docIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
Doc.prototype.nextDoc = function () {
|
|
||||||
return docs[this.docIndex + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
Doc.prototype.contents = function () {
|
|
||||||
var nextDoc = this.nextDoc();
|
|
||||||
var endIndex = lines.length - 1;
|
|
||||||
var contents = [];
|
|
||||||
|
|
||||||
if(nextDoc) {
|
|
||||||
endIndex = nextDoc.lineNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(var i = this.lineNum; i < endIndex; i++) {
|
|
||||||
contents.push(lines[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
Doc.prototype.example = function () {
|
|
||||||
var content = this.contents();
|
|
||||||
var result = [];
|
|
||||||
|
|
||||||
content.forEach(function (line) {
|
|
||||||
if(line.substr(0, 4) === ' ') {
|
|
||||||
result.push(line.substr(4, line.length))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Doc.prototype.desc = function () {
|
|
||||||
var content = this.contents();
|
|
||||||
var result = [];
|
|
||||||
var first;
|
|
||||||
|
|
||||||
content.forEach(function (line) {
|
|
||||||
if(first) {
|
|
||||||
// ignore
|
|
||||||
} else if(line[0] === '#' || line[0] === ' ') {
|
|
||||||
// ignore
|
|
||||||
} else {
|
|
||||||
first = line;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// only want the first sentence (to keep it brief)
|
|
||||||
if(first) {
|
|
||||||
first = first.split(/\.\s|\n/)[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
lines = readme.split('\n')
|
|
||||||
docs = [];
|
|
||||||
|
|
||||||
lines.forEach(function (line, i) {
|
|
||||||
if(!(line[0] === '#' && ~line.indexOf('.'))) return;
|
|
||||||
|
|
||||||
var doc = new Doc(line, i, docs.length);
|
|
||||||
|
|
||||||
docs.push(doc);
|
|
||||||
});
|
|
||||||
|
|
||||||
var _ = require('underscore');
|
|
||||||
var sh = require('shelljs');
|
|
||||||
|
|
||||||
var byName = _.groupBy(docs, function (doc) {
|
|
||||||
return doc.name;
|
|
||||||
});
|
|
||||||
|
|
||||||
sh.rm('-rf', 'g-tests');
|
|
||||||
sh.mkdir('g-tests');
|
|
||||||
|
|
||||||
Object.keys(byName).forEach(function (group) {
|
|
||||||
var testFile = [
|
|
||||||
"describe('" + group + "', function() {",
|
|
||||||
""];
|
|
||||||
|
|
||||||
byName[group].forEach(function (doc) {
|
|
||||||
var example = doc.example();
|
|
||||||
var exampleLines = example && example.length && example;
|
|
||||||
|
|
||||||
testFile = testFile.concat([
|
|
||||||
" describe('" + doc.line + "', function() {",
|
|
||||||
" it(\"" + doc.desc() + "\", function(done) {"]);
|
|
||||||
|
|
||||||
if(exampleLines) {
|
|
||||||
exampleLines.unshift("/* example - ");
|
|
||||||
exampleLines.push("*/")
|
|
||||||
testFile = testFile.concat(
|
|
||||||
exampleLines.map(function (l) {
|
|
||||||
return ' ' + l;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
testFile.push(
|
|
||||||
" done(new Error('test not implemented'));",
|
|
||||||
" });",
|
|
||||||
" });",
|
|
||||||
"});"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
testFile.join('\n').to('g-tests/' + group + '.test.js');
|
|
||||||
});
|
|
|
@ -153,7 +153,7 @@ asteroid.createModel = function (name, properties, options) {
|
||||||
assert(typeof name === 'string', 'Cannot create a model without a name');
|
assert(typeof name === 'string', 'Cannot create a model without a name');
|
||||||
|
|
||||||
var mb = new ModelBuilder();
|
var mb = new ModelBuilder();
|
||||||
var ModelCtor = mb.define(name, properties, arguments);
|
var ModelCtor = mb.define(name, properties, options);
|
||||||
|
|
||||||
ModelCtor.shared = true;
|
ModelCtor.shared = true;
|
||||||
ModelCtor.sharedCtor = function (data, id, fn) {
|
ModelCtor.sharedCtor = function (data, id, fn) {
|
||||||
|
@ -179,7 +179,18 @@ asteroid.createModel = function (name, properties, options) {
|
||||||
} else if(data) {
|
} else if(data) {
|
||||||
fn(null, new ModelCtor(data));
|
fn(null, new ModelCtor(data));
|
||||||
} else if(id) {
|
} else if(id) {
|
||||||
ModelCtor.find(id, fn);
|
ModelCtor.find(id, function (err, model) {
|
||||||
|
if(err) {
|
||||||
|
fn(err);
|
||||||
|
} else if(model) {
|
||||||
|
fn(null, model);
|
||||||
|
} else {
|
||||||
|
err = new Error('could not find a model with id ' + id);
|
||||||
|
err.statusCode = 404;
|
||||||
|
|
||||||
|
fn(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
fn(new Error('must specify an id or data'));
|
fn(new Error('must specify an id or data'));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue