Initial working store, model, connection, and collection

This commit is contained in:
Ritchie Martori 2013-04-10 10:55:13 -07:00
parent cd8d679df0
commit 7c2e73f53a
40 changed files with 1018 additions and 67 deletions

View File

@ -2,6 +2,9 @@ var asteroid = require('../../');
var app = asteroid(); var app = asteroid();
app.use(asteroid.configure()); app.use(asteroid.configure());
app.use(asteroid.bodyParser());
app.use(asteroid.resources()); app.use(asteroid.resources());
app.listen(3000); app.listen(3000);
process.memory_store_cache = {};

View File

@ -1,11 +1,3 @@
{ {
"module": "data-store", "module": "store"
"options": {
"database": "asteroid-examples-todos",
"port": 27017,
"host": "127.0.0.1"
},
"dependencies": {
"db": "memory"
}
} }

1
example/todos/index.js Normal file
View File

@ -0,0 +1 @@
throw new Error('foo');

View File

@ -17,6 +17,6 @@
] ]
}, },
"dependencies": { "dependencies": {
"data-store": "db" "store": "db"
} }
} }

View File

@ -14,7 +14,7 @@ todos.on('before:validate', function (todo, ctx) {
} }
}); });
todos.on('before:create', function (todo, ctx, done) { todos.on('create', function (todo, ctx, done) {
ctx.errorUnless(ctx.isEmail(todos.creator)); ctx.errorUnless(ctx.isEmail(todos.creator));
todos.model.count({owner: todo.owner}, function (err) { todos.model.count({owner: todo.owner}, function (err) {

View File

@ -1,20 +0,0 @@
{
"module": "users-collection",
"options": {
"root": "/users",
"name": "users",
"properties": [
{
"name": "email",
"type": "string"
},
{
"name": "password",
"type": "password"
}
]
},
"dependencies": {
"store": "store"
}
}

View File

@ -17,6 +17,8 @@ module.exports = configure;
function configure(root) { function configure(root) {
var moduleLoader = ModuleLoader.create(root || '.'); var moduleLoader = ModuleLoader.create(root || '.');
process.__asteroidCache = {};
return function configureMiddleware(req, res, next) { return function configureMiddleware(req, res, next) {
req.modules = res.modules = moduleLoader; req.modules = res.modules = moduleLoader;
moduleLoader.load(next); moduleLoader.load(next);

1
node_modules/.bin/_mocha generated vendored Symbolic link
View File

@ -0,0 +1 @@
../mocha/bin/_mocha

1
node_modules/.bin/mocha generated vendored Symbolic link
View File

@ -0,0 +1 @@
../mocha/bin/mocha

View File

@ -1,13 +1,28 @@
# asteroid-module # asteroid-module
v0.0.1 v0.0.1
## Install ## About
An `AsteroidModule` is an abstract class that provides a base for all asteroid modules. Its constructor takes an `options` argument provided by a `config.json`. It is also supplied with dependencies it lists on its constructor based on information in the `config.json` file.
slnode install asteroid-module
## Example ## Example
var AsteroidModule = require('asteroid-module'); See [resource](../resource) for an example asteroid module.
var asteroidModule = AsteroidModule.create();
asteroidModule.myMethod(); ## AsteroidModule.dependencies
An asteroid module may define dependencies on other modules that can be configured in `config.json`. Eg. the [collection](../collection/lib/collection.js) module defines a [model](../model) dependency.
Collection.dependencies = {
model: 'model'
}
A configuration then must define:
{
"dependencies": {
"model": "some-model-module"
}
}
Where `some-model-module` is an existing `model` instance.

26
node_modules/collection/README.md generated vendored
View File

@ -1,13 +1,21 @@
# collection # collection
v0.0.1
## Install
slnode install collection
## Example ## About
var Collection = require('collection'); A `Collection` inherits from the [resource](../resource) class. It provides HTTP access to a supplied [model](../model) instance.
var collection = Collection.create();
collection.myMethod(); ## Config
Supports all configuration of a [resource](../resource).
### Options
#### name (optional)
If defined, overrides the default name (based on the path).
### Dependencies
#### model
Requires a path to an existing model instance.

View File

@ -8,7 +8,7 @@ module.exports = Collection;
* Module dependencies. * Module dependencies.
*/ */
var Resource = require('resource').Resource var Resource = require('resource')
, debug = require('debug')('collection') , debug = require('debug')('collection')
, util = require('util') , util = require('util')
, inherits = util.inherits , inherits = util.inherits
@ -26,13 +26,11 @@ function Collection(options) {
this.options = options; this.options = options;
// collection middleware // model
this.app.use(function (req, res, next) { this.model = this.dependencies.model.schema;
req.asyncEmit('request', next);
});
// setup http routes // setup http routes
// this.setupRoutes(this.app); this.setupRoutes(this.app);
debug('created with options', options); debug('created with options', options);
} }
@ -48,22 +46,33 @@ inherits(Collection, Resource);
*/ */
Collection.dependencies = { Collection.dependencies = {
'store': 'store' 'model': 'model'
} }
Collection.prototype.setupRoutes = function (app) { Collection.prototype.setupRoutes = function (app) {
var store = this.store; var Model = this.model;
var done = this.done; var ctx;
var Model = this.store; var collection = this;
var emit = this.asyncEmit;
app.use(function (req, res, next) {
ctx = collection.createContext(req, res, next);
ctx.emit('request', function () {
ctx.emit(req.method.toLowerCase(), function () {
next();
});
});
});
// create // create
app.post('/', create); app.post('/', create);
app.post('/new', create); app.post('/new', create);
function create(req, res, next) { function create(req, res, next) {
req.asyncEmit('create', function () { console.log(req.body);
var o = store.save(req.body, res.done);
ctx.emit('create', function (done) {
Model.create(req.body, done);
}); });
} }
@ -82,24 +91,44 @@ Collection.prototype.setupRoutes = function (app) {
query.limit = 1; query.limit = 1;
} }
req.asyncEmit('query', query, function () { if(query.limit) query.limit = Number(query.limit);
store.all(query, res.done); if(query.skip) query.skip = Number(query.skip);
ctx.emit('query', query, function (done) {
Model.all(query, done);
}); });
} }
// update // update
app.put('/:id', function (req, res) { app.put('/:id', function (req, res) {
req.asyncEmit('update', req.body, function () { var body = req.body || {};
store.updateAttributes(req.body, res.done); var id = req.param('id');
if(Number(id) == id) {
id = Number(id);
}
ctx.emit('find', function () {
Model.find(id, function (err, m) {
if(err) {
ctx.done(err);
} else {
ctx.emit('validate', body, function () {
ctx.emit('update', body, function (done) {
m.updateAttributes(body, done);
});
});
}
});
}); });
}); });
// delete // delete
app.destroy('/:id', function () { app.del('/:id', function () {
var id = req.param('id'); var id = req.param('id');
req.asyncEmit('delete', id, function () { ctx.emit('delete', id, function (done) {
store.destroy(id, res.done); Model.destroy(id, done);
}); });
}); });
} }

10
node_modules/connection/.gitignore generated vendored Normal file
View File

@ -0,0 +1,10 @@
.DS_Store
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.swp
*.swo
node_modules/

13
node_modules/connection/README.md generated vendored Normal file
View File

@ -0,0 +1,13 @@
# connection
## About
Provides a base class for implementing access to a persistence layer.
### Options
#### hostname
#### port
#### database
#### username
#### password

12
node_modules/connection/example/example.js generated vendored Normal file
View File

@ -0,0 +1,12 @@
/**
* A generated `Connection` example...
*
* Examples should show a working module api
* and be used in tests to continously check
* they function as expected.
*/
var Connection = require('../');
var connection = Connection.create();
connection.myMethod();

5
node_modules/connection/index.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
/**
* connection ~ public api
*/
module.exports = require('./lib/connection');

36
node_modules/connection/lib/connection.js generated vendored Normal file
View File

@ -0,0 +1,36 @@
/**
* Expose `Connection`.
*/
module.exports = Connection;
/**
* Module dependencies.
*/
var AsteroidModule = require('asteroid-module')
,
, debug = require('debug')('connection')
, util = require('util')
, inherits = util.inherits
, assert = require('assert');
/**
* Create a new `Connection` with the given `options`.
*
* @param {Object} options
* @return {Connection}
*/
function Connection(options) {
AsteroidModule.apply(this, arguments);
this.options = options;
debug('created with options', options);
}
/**
* Inherit from `AsteroidModule`.
*/
inherits(Connection, AsteroidModule);

14
node_modules/connection/package.json generated vendored Normal file
View File

@ -0,0 +1,14 @@
{
"name": "connection",
"description": "connection",
"version": "0.0.1",
"scripts": {
"test": "mocha"
},
"dependencies": {
"debug": "latest"
},
"devDependencies": {
"mocha": "latest"
}
}

24
node_modules/connection/test/connection.test.js generated vendored Normal file
View File

@ -0,0 +1,24 @@
var Connection = require('../');
describe('Connection', function(){
var connection;
beforeEach(function(){
connection = new Connection;
});
describe('.myMethod', function(){
// example sync test
it('should <description of behavior>', function() {
connection.myMethod();
});
// example async test
it('should <description of behavior>', function(done) {
setTimeout(function () {
connection.myMethod();
done();
}, 0);
});
});
});

5
node_modules/connection/test/support.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
/**
* connection test setup and support.
*/
assert = require('assert');

10
node_modules/model/.gitignore generated vendored Normal file
View File

@ -0,0 +1,10 @@
.DS_Store
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.swp
*.swo
node_modules/

37
node_modules/model/README.md generated vendored Normal file
View File

@ -0,0 +1,37 @@
# model
## About
Provides a schema protected api to a data [store](../store).
### Options
#### namespace
A table, collection, url, or other namespace.
#### properties
An array of properties describing the model's schema.
"properties": [
{
"name": "title",
"type": "string"
},
{
"name": "done",
"type": "boolean"
},
{
"name": "order",
"type": "number"
}
]
}
### Dependencies
#### store
A [store](../store) module instance.

12
node_modules/model/example/example.js generated vendored Normal file
View File

@ -0,0 +1,12 @@
/**
* A generated `Model` example...
*
* Examples should show a working module api
* and be used in tests to continously check
* they function as expected.
*/
var Model = require('../');
var model = Model.create();
model.myMethod();

5
node_modules/model/index.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
/**
* model ~ public api
*/
module.exports = require('./lib/model');

72
node_modules/model/lib/model.js generated vendored Normal file
View File

@ -0,0 +1,72 @@
/**
* Expose `Model`.
*/
module.exports = Model;
/**
* Module dependencies.
*/
var AsteroidModule = require('asteroid-module')
, debug = require('debug')('model')
, util = require('util')
, inherits = util.inherits
, assert = require('assert');
/**
* Create a new `Model` with the given `options`.
*
* @param {Object} options
* @return {Model}
*/
function Model(options) {
AsteroidModule.apply(this, arguments);
// throw an error if args are not supplied
// assert(typeof options === 'object', 'Model requires an options object');
this.options = options;
debug('created with options', options);
var dependencies = this.dependencies;
var store = dependencies.store;
var schema = this.schema = store.schema;
assert(Array.isArray(options.properties), 'the ' + options._name + ' model requires an options.properties array');
// define schema
this.schema = schema.define(options.namespace || options._name, this.buildSchemaDefinition(options.properties));
}
/**
* Inherit from `AsteroidModule`.
*/
inherits(Model, AsteroidModule);
/**
* Dependencies.
*/
Model.dependencies = {
'store': 'store'
};
/**
* Build a jugglingdb compatibile schema definition from property array.
*/
Model.prototype.buildSchemaDefinition = function (properties) {
return properties.reduce(function (prev, cur) {
prev[cur.name] = types[cur.type];
return prev;
}, {});
}
var types = [String, Number, Date, Array, Boolean].reduce(function (prev, cur) {
prev[cur.name.toLowerCase()] = cur
return prev;
}, {});

14
node_modules/model/package.json generated vendored Normal file
View File

@ -0,0 +1,14 @@
{
"name": "model",
"description": "model",
"version": "0.0.1",
"scripts": {
"test": "mocha"
},
"dependencies": {
"debug": "latest"
},
"devDependencies": {
"mocha": "latest"
}
}

24
node_modules/model/test/model.test.js generated vendored Normal file
View File

@ -0,0 +1,24 @@
var Model = require('../');
describe('Model', function(){
var model;
beforeEach(function(){
model = new Model;
});
describe('.myMethod', function(){
// example sync test
it('should <description of behavior>', function() {
model.myMethod();
});
// example async test
it('should <description of behavior>', function(done) {
setTimeout(function () {
model.myMethod();
done();
}, 0);
});
});
});

5
node_modules/model/test/support.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
/**
* model test setup and support.
*/
assert = require('assert');

114
node_modules/resource/lib/http-context.js generated vendored Normal file
View File

@ -0,0 +1,114 @@
/**
* Expose `HttpContext`.
*/
module.exports = HttpContext;
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter
, debug = require('debug')('http-context')
, util = require('util')
, inherits = util.inherits
, assert = require('assert');
/**
* Create a new `HttpContext` with the given `options`.
*
* @param {Object} options
* @return {HttpContext}
*/
function HttpContext(resource, req, res, next) {
EventEmitter.apply(this, arguments);
this.resource = resource;
this.req = req;
this.res = res;
this.next = next;
}
/**
* Inherit from `EventEmitter`.
*/
inherits(HttpContext, EventEmitter);
/**
* Override the default emitter behavior to support async or sync hooks before and after an event.
*/
HttpContext.prototype.emit = function (ev) {
var ctx = this;
var resource = this.resource;
var origArgs = arguments;
var args = Array.prototype.slice.call(arguments, 0)
var success = arguments[arguments.length - 1];
assert(typeof success === 'function', 'ctx.emit requires a callback');
args.pop();
var evName = ev;
assert(typeof evName === 'string');
args.shift();
var listeners = resource.listeners(evName);
var listener;
// start
next();
function next(err) {
if(err) return fail(err);
try {
if(listener = listeners.shift()) {
var expectsCallback = listener._expects === args.length + 2;
// if a listener expects all the `args`
// plus ctx, and a callback
if(expectsCallback) {
// include ctx (this) and pass next to continue
listener.apply(resource, args.concat([this, next]));
} else {
// dont include the callback
listener.apply(resource, args.concat([this]));
// call next directly
next();
}
} else {
success(done);
}
} catch(e) {
fail(e);
}
}
function fail(err) {
ctx.done(err);
}
function done(err, result) {
if(err) {
return fail(err);
}
ctx.emit.apply(ctx,
['after:' + evName] // after event
.concat(args) // include original arguments/data
.concat([function () { // success callback
ctx.done.call(ctx, err, result);
}])
);
};
}
HttpContext.prototype.done = function (err, result) {
if(err) {
this.next(err);
} else {
this.res.send(result);
}
}

View File

@ -10,6 +10,7 @@ module.exports = Resource;
var asteroid = require('asteroid') var asteroid = require('asteroid')
, AsteroidModule = require('asteroid-module') , AsteroidModule = require('asteroid-module')
, HttpContext = require('./http-context')
, debug = require('debug')('asteroid:resource') , debug = require('debug')('asteroid:resource')
, util = require('util') , util = require('util')
, inherits = util.inherits , inherits = util.inherits
@ -52,5 +53,31 @@ inherits(Resource, AsteroidModule);
*/ */
Resource.prototype.mount = function (parent) { Resource.prototype.mount = function (parent) {
this.parent = parent;
parent.use(this.options.path, this.app); parent.use(this.options.path, this.app);
}
/**
* Create an http context bound to the current resource.
*/
Resource.prototype.createContext = function (req, res, next) {
return new HttpContext(this, req, res, next);
}
/**
* Override `on` to determine how many arguments an event handler expects.
*/
Resource.prototype.on = function () {
var fn = arguments[arguments.length - 1];
if(typeof fn === 'function') {
// parse expected arguments from function src
// fn.listener handles the wrapped function during `.once()`
var src = (fn.listener || fn).toString();
fn._expects = src.split('{')[0].split(',').length;
}
AsteroidModule.prototype.on.apply(this, arguments);
} }

95
node_modules/resource/test/http-context.test.js generated vendored Normal file
View File

@ -0,0 +1,95 @@
var HttpContext = require('../lib/http-context.js');
var Resource = require('../lib/resource.js');
describe('HttpContext', function(){
var ctx;
var resource;
function createRequest() {
return {};
}
function createResponse() {
return {};
}
beforeEach(function(){
resource = new Resource({path: '/foo'});
ctx = new HttpContext(resource, createRequest(), createResponse());
});
describe('.emit(ev, arg, done)', function(){
it('should emit events on a resource', function(done) {
var emitted, data;
resource.once('foo', function (arg, ctx, fn) {
emitted = true;
data = arg;
fn();
});
ctx.emit('foo', {bar: true}, function () {
assert(emitted, 'event should be emitted');
assert(data, 'arg should be supplied');
assert(data.bar, 'arg should be the correct object');
done();
});
});
it('should handle multiple args', function(done) {
var emitted, data;
resource.once('foo', function (arg1, arg2, arg3, arg4, ctx, fn) {
emitted = true;
assert(arg1 === 1, 'arg1 should equal 1');
assert(arg2 === 2, 'arg2 should equal 2');
assert(arg3 === 3, 'arg3 should equal 3');
assert(arg4 === 4, 'arg4 should equal 4');
fn();
});
ctx.emit('foo', 1, 2, 3, 4, function (fn) {
assert(emitted, 'event should be emitted');
done();
});
});
it('should have an after event', function(done) {
var emitted, emittedAfter;
ctx.done = done;
resource.once('foo', function (arg1, arg2, arg3, arg4, ctx, fn) {
emitted = true;
fn();
});
resource.once('after:foo', function (arg1, arg2, arg3, arg4, ctx, fn) {
emittedAfter = true;
fn();
});
ctx.emit('foo', 1, 2, 3, 4, function (fn) {
assert(emitted, 'event should be emitted');
fn();
});
});
it('should be able to emit synchronously', function(done) {
var emitted, data;
resource.once('foo', function (arg1, arg2, arg3, arg4, ctx) {
emitted = true;
assert(arg1 === 1, 'arg1 should equal 1');
assert(arg2 === 2, 'arg2 should equal 2');
assert(arg3 === 3, 'arg3 should equal 3');
assert(arg4 === 4, 'arg4 should equal 4');
});
ctx.emit('foo', 1, 2, 3, 4, function () {
assert(emitted);
done();
});
});
});
});

10
node_modules/store/.gitignore generated vendored Normal file
View File

@ -0,0 +1,10 @@
.DS_Store
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.swp
*.swo
node_modules/

15
node_modules/store/README.md generated vendored Normal file
View File

@ -0,0 +1,15 @@
# store
## About
Provides an in memory data store. The backing persistence can be swapped out by providing a connection.
### Options
tbd
### Dependencies
#### connection (optional)
By default data will be persisted in memory. You can swap out the persistence layer by providing a connection.

12
node_modules/store/example/example.js generated vendored Normal file
View File

@ -0,0 +1,12 @@
/**
* A generated `Store` example...
*
* Examples should show a working module api
* and be used in tests to continously check
* they function as expected.
*/
var Store = require('../');
var store = Store.create();
store.myMethod();

5
node_modules/store/index.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
/**
* store ~ public api
*/
module.exports = require('./lib/store');

260
node_modules/store/lib/memory.js generated vendored Normal file
View File

@ -0,0 +1,260 @@
exports.initialize = function initializeSchema(schema, callback) {
schema.adapter = new Memory();
schema.adapter.connect(callback);
};
function Memory(m) {
if (m) {
this.isTransaction = true;
this.cache = m.cache;
this.ids = m.ids;
this._models = m._models;
} else {
this.isTransaction = false;
// use asteroid cache, otherwise state will be reset during configuration
this.cache = process.__asteroidCache.memoryStore || (process.__asteroidCache.memoryStore = {});
this.ids = {};
this._models = {};
}
}
Memory.prototype.connect = function(callback) {
if (this.isTransaction) {
this.onTransactionExec = callback;
} else {
process.nextTick(callback);
}
};
Memory.prototype.define = function defineModel(descr) {
var m = descr.model.modelName;
this._models[m] = descr;
// allow reuse of data
this.cache[m] = this.cache[m] || {};
this.ids[m] = 1;
};
Memory.prototype.create = function create(model, data, callback) {
var id = data.id || this.ids[model]++;
data.id = id;
this.cache[model][id] = JSON.stringify(data);
process.nextTick(function() {
callback(null, id);
});
};
Memory.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);
});
}
});
};
Memory.prototype.save = function save(model, data, callback) {
this.cache[model][data.id] = 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].hasOwnProperty(id));
}.bind(this));
};
Memory.prototype.find = function find(model, id, callback) {
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);
};
Memory.prototype.fromDb = function(model, data) {
if (!data) return null;
data = JSON.parse(data);
var props = this._models[model].properties;
Object.keys(data).forEach(function (key) {
var val = data[key];
if (typeof val === 'undefined' || val === null) {
return;
}
if (props[key]) {
switch(props[key].type.name) {
case 'Date':
val = new Date(val.toString().replace(/GMT.*$/, 'GMT'));
break;
case 'Boolean':
val = new Boolean(val);
break;
}
}
data[key] = val;
});
return data;
};
Memory.prototype.all = function all(model, filter, callback) {
var self = this;
var nodes = [];
var data = this.cache[model];
var keys = Object.keys(data);
var scanned = 0;
while(scanned < keys.length) {
nodes.push(this.fromDb(model, data[keys[scanned]]));
scanned++;
}
if (filter) {
// do we need some sorting?
if (filter.order) {
var props = this._models[model].properties;
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));
}
// do we need some filtration?
if (filter.where) {
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
}
// skip
if(filter.skip) {
nodes = nodes.slice(filter.skip, nodes.length);
}
if(filter.limit) {
nodes = nodes.slice(0, 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[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') {
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;
}
}
// not strict equality
return (example !== null ? example.toString() : example) == (value !== null ? value.toString() : value);
}
}
Memory.prototype.destroyAll = function destroyAll(model, callback) {
Object.keys(this.cache[model]).forEach(function (id) {
delete this.cache[model][id];
}.bind(this));
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) {
data = data.filter(function (id) {
var ok = true;
Object.keys(where).forEach(function (key) {
if (JSON.parse(cache[id])[key] != where[key]) {
ok = false;
}
});
return ok;
});
}
process.nextTick(function () {
callback(null, data.length);
});
};
Memory.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
data.id = id;
var base = JSON.parse(this.cache[model][id]);
this.save(model, merge(base, data), cb);
};
Memory.prototype.transaction = function () {
return new Memory(this);
};
Memory.prototype.exec = function(callback) {
this.onTransactionExec();
setTimeout(callback, 50);
};
function merge(base, update) {
if (!base) return update;
Object.keys(update).forEach(function (key) {
base[key] = update[key];
});
return base;
}

40
node_modules/store/lib/store.js generated vendored Normal file
View File

@ -0,0 +1,40 @@
/**
* Expose `Store`.
*/
module.exports = Store;
/**
* Module dependencies.
*/
var AsteroidModule = require('asteroid-module')
, Schema = require('jugglingdb').Schema
, debug = require('debug')('store')
, util = require('util')
, inherits = util.inherits
, assert = require('assert');
/**
* Create a new `Store` with the given `options`.
*
* @param {Object} options
* @return {Store}
*/
function Store(options) {
AsteroidModule.apply(this, arguments);
// throw an error if args are not supplied
// assert(typeof options === 'object', 'Store requires an options object');
this.options = options;
debug('created with options', options);
var dependencies = this.dependencies;
var connection = (dependencies && dependencies.connection) || {};
var adapter = this.adapter = (connection && connection.adapter) || require('./memory');
this.schema = new Schema(adapter, connection.options);
}

14
node_modules/store/package.json generated vendored Normal file
View File

@ -0,0 +1,14 @@
{
"name": "store",
"description": "store",
"version": "0.0.1",
"scripts": {
"test": "mocha"
},
"dependencies": {
"debug": "latest"
},
"devDependencies": {
"mocha": "latest"
}
}

24
node_modules/store/test/store.test.js generated vendored Normal file
View File

@ -0,0 +1,24 @@
var Store = require('../');
describe('Store', function(){
var store;
beforeEach(function(){
store = new Store;
});
describe('.myMethod', function(){
// example sync test
it('should <description of behavior>', function() {
store.myMethod();
});
// example async test
it('should <description of behavior>', function(done) {
setTimeout(function () {
store.myMethod();
done();
}, 0);
});
});
});

5
node_modules/store/test/support.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
/**
* store test setup and support.
*/
assert = require('assert');