Compare commits

..

11 Commits

Author SHA1 Message Date
90d9a25b3f 1.0.19 2016-11-18 00:42:29 +01:00
e49811eecd now allows adding a saveable decorator 2016-11-18 00:42:25 +01:00
9cbdf317dc 1.0.18 2016-11-17 22:41:05 +01:00
d55339013a improve README 2016-11-17 22:41:01 +01:00
9acdbd2842 1.0.17 2016-11-17 22:36:16 +01:00
dfce8cfcc0 add NeDB support 2016-11-17 22:36:12 +01:00
38446fc79b 1.0.16 2016-11-17 12:20:56 +01:00
af8c41ad6c added nedb to the mix 2016-11-17 12:20:52 +01:00
3a24f829b4 improve README 2016-09-14 01:03:25 +02:00
67db1325c1 1.0.15 2016-09-14 01:02:23 +02:00
6683c05c22 added Decorator support for easy connection between classes and collections 2016-09-14 01:02:11 +02:00
18 changed files with 534 additions and 136 deletions

View File

@ -2,12 +2,28 @@
> Note: Still in Beta > Note: Still in Beta
## Availabililty
[![npm](https://push.rocks/assets/repo-button-npm.svg)](https://www.npmjs.com/package/smartdata)
[![git](https://push.rocks/assets/repo-button-git.svg)](https://gitlab.com/pushrocks/smartdata)
[![git](https://push.rocks/assets/repo-button-mirror.svg)](https://github.com/pushrocks/smartdata)
[![docs](https://push.rocks/assets/repo-button-docs.svg)](https://pushrocks.gitlab.io/smartdata/)
## Status for master
[![build status](https://gitlab.com/pushrocks/smartdata/badges/master/build.svg)](https://gitlab.com/pushrocks/smartdata/commits/master)
[![coverage report](https://gitlab.com/pushrocks/smartdata/badges/master/coverage.svg)](https://gitlab.com/pushrocks/smartdata/commits/master)
[![Dependency Status](https://david-dm.org/pushrocks/smartdata.svg)](https://david-dm.org/pushrocks/smartdata)
[![bitHound Dependencies](https://www.bithound.io/github/pushrocks/smartdata/badges/dependencies.svg)](https://www.bithound.io/github/pushrocks/smartdata/master/dependencies/npm)
[![bitHound Code](https://www.bithound.io/github/pushrocks/smartdata/badges/code.svg)](https://www.bithound.io/github/pushrocks/smartdata)
[![TypeScript](https://img.shields.io/badge/TypeScript-2.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%206.x.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
smartdata is an ODM that adheres to TypeScript practices and uses classes to organize data. smartdata is an ODM that adheres to TypeScript practices and uses classes to organize data.
It uses MongoDB as persistent storage. It uses MongoDB or NeDb as persistent storage.
## Intention ## Intention
There are many ODMs out there, however when we searched for an ODM that uses TypeScript, There are many ODMs out there, however when we searched for an ODM that uses TypeScript,
acts smart while still embracing an the NoSQL idea... we didn't find a matching solution. acts smart while still embracing the NoSQL idea we didn't find a matching solution.
This is why we started smartdata. This is why we started smartdata.
How MongoDB terms map to smartdata classes How MongoDB terms map to smartdata classes
@ -21,12 +37,16 @@ Document | smartdata.DbDoc
### class Db ### class Db
represents a Database. Naturally it has .connect() etc. methods on it. represents a Database. Naturally it has .connect() etc. methods on it.
Since it is a class you can have multiple DBs defined. Since it is a class you can have multiple DBs defined.
```typescript ```javascript
import * as smartdata from 'smartdata' import * as smartdata from 'smartdata'
// mongodb
let myDb1 = new smartdata.Db('someConnectionUrl') let myDb1 = new smartdata.Db('someConnectionUrl')
let myDb2 = new smartdata.Db('someConnectionUrl') let myDb2 = new smartdata.Db('someConnectionUrl')
// nedb
let myDb3 = new smartdata('/some/path/for/persistence', 'nedb') // you may set first argument to null for just in memory db
myDb1.connect() myDb1.connect()
myDb2.connect() myDb2.connect()
@ -38,20 +58,23 @@ represents a collection of objects.
A collection is defined by the object class (that is extending smartdata.dbdoc) it respresents A collection is defined by the object class (that is extending smartdata.dbdoc) it respresents
So to get to get access to a specific collection you document So to get to get access to a specific collection you document
```typescript ```javascript
// continues from the block before... // continues from the block before...
class myObject extends smartdata.DbDoc { // read the next block about DbDoc @Collection(myDb1)
class myObject extends smartdata.DbDoc<myObject> { // read the next block about DbDoc
property1:string property1:string
property2:number property2:number
constructor(optionsArg:{ constructor(optionsArg:{
property1:string, property1:string,
property2:number property2:number
}) { }) {
super(this,myDb1) super()
} }
} }
let myCollection = myDb1.getCollectionByName<myObject>(myObject) let myCollection = myDb1.getCollectionByName<myObject>(myObject)
// start to instantiate classes from scratch or database
``` ```
> Alert: You NEVER instantiate a collection. > Alert: You NEVER instantiate a collection.
@ -71,10 +94,6 @@ DbDoc extends your class with the following methods:
So now we can **store** instances of classes to Db... So now we can **store** instances of classes to Db...
How do we **get** a new class instance from a Doc in the DB? How do we **get** a new class instance from a Doc in the DB?
Easy! Take a look at the constructor. When you specify `optionsArg.queryArg`
smartdata will fill in the data from the database!
But when you specify a `optionsArg.dataArg` instead
the data for the class is taken from there :)
## TypeScript ## TypeScript
How does TypeScript play into this? How does TypeScript play into this?

8
dist/index.js vendored
View File

@ -2,7 +2,7 @@
function __export(m) { function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
} }
__export(require('./smartdata.classes.db')); __export(require("./smartdata.classes.db"));
__export(require('./smartdata.classes.dbcollection')); __export(require("./smartdata.classes.dbcollection"));
__export(require('./smartdata.classes.dbdoc')); __export(require("./smartdata.classes.dbdoc"));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBRUEsaUJBQWMsd0JBQ2QsQ0FBQyxFQURxQztBQUN0QyxpQkFBYyxrQ0FDZCxDQUFDLEVBRCtDO0FBQ2hELGlCQUFjLDJCQUNkLENBQUMsRUFEd0MifQ== //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBRUEsNENBQXNDO0FBQ3RDLHNEQUFnRDtBQUNoRCwrQ0FBeUMifQ==

View File

@ -2,13 +2,21 @@
import * as plugins from './smartdata.plugins'; import * as plugins from './smartdata.plugins';
import { Objectmap } from 'lik'; import { Objectmap } from 'lik';
import { DbCollection } from './smartdata.classes.dbcollection'; import { DbCollection } from './smartdata.classes.dbcollection';
/**
* interface - indicates the database type
*/
export declare type TDbType = 'mongodb' | 'nedb';
/**
* interface - indicates the connection status of the db
*/
export declare type TConnectionStatus = 'disconnected' | 'connected' | 'failed'; export declare type TConnectionStatus = 'disconnected' | 'connected' | 'failed';
export declare class Db { export declare class Db {
dbType: TDbType;
dbUrl: string; dbUrl: string;
db: plugins.mongodb.Db; db: plugins.mongodb.Db;
status: TConnectionStatus; status: TConnectionStatus;
collections: Objectmap<DbCollection<any>>; collections: Objectmap<DbCollection<any>>;
constructor(dbUrl: string); constructor(dbUrlArg: string, dbTypeArg?: TDbType);
/** /**
* connects to the database that was specified during instance creation * connects to the database that was specified during instance creation
*/ */
@ -21,4 +29,5 @@ export declare class Db {
* gets a collection by name: string * gets a collection by name: string
*/ */
getCollectionByName<T>(nameArg: string): plugins.q.Promise<DbCollection<T>>; getCollectionByName<T>(nameArg: string): plugins.q.Promise<DbCollection<T>>;
addCollection(dbCollectionArg: DbCollection<any>): void;
} }

View File

@ -1,10 +1,11 @@
"use strict"; "use strict";
const plugins = require('./smartdata.plugins'); const plugins = require("./smartdata.plugins");
const lik_1 = require('lik'); const lik_1 = require("lik");
class Db { class Db {
constructor(dbUrl) { constructor(dbUrlArg, dbTypeArg = 'mongodb') {
this.collections = new lik_1.Objectmap(); this.collections = new lik_1.Objectmap();
this.dbUrl = dbUrl; this.dbType = dbTypeArg;
this.dbUrl = dbUrlArg;
} }
// basic connection stuff ---------------------------------------------- // basic connection stuff ----------------------------------------------
/** /**
@ -12,6 +13,7 @@ class Db {
*/ */
connect() { connect() {
let done = plugins.q.defer(); let done = plugins.q.defer();
if (this.dbType === 'mongodb') {
plugins.mongodb.MongoClient.connect(this.dbUrl, (err, db) => { plugins.mongodb.MongoClient.connect(this.dbUrl, (err, db) => {
if (err) { if (err) {
console.log(err); console.log(err);
@ -21,6 +23,10 @@ class Db {
plugins.beautylog.success(`connected to database at ${this.dbUrl}`); plugins.beautylog.success(`connected to database at ${this.dbUrl}`);
done.resolve(this.db); done.resolve(this.db);
}); });
}
else if (this.dbType === 'nedb') {
this.db = null;
}
return done.promise; return done.promise;
} }
/** /**
@ -28,7 +34,9 @@ class Db {
*/ */
close() { close() {
let done = plugins.q.defer(); let done = plugins.q.defer();
if (this.dbType === 'mongodb') {
this.db.close(); this.db.close();
}
plugins.beautylog.ok(`disconnected to database at ${this.dbUrl}`); plugins.beautylog.ok(`disconnected to database at ${this.dbUrl}`);
done.resolve(); done.resolve();
return done.promise; return done.promise;
@ -48,6 +56,9 @@ class Db {
return done.promise; return done.promise;
} }
; ;
addCollection(dbCollectionArg) {
this.collections.add(dbCollectionArg);
}
} }
exports.Db = Db; exports.Db = Db;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYXRhLmNsYXNzZXMuZGIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGRhdGEuY2xhc3Nlcy5kYi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsTUFBWSxPQUFPLFdBQU0scUJBQ3pCLENBQUMsQ0FENkM7QUFDOUMsc0JBQTBCLEtBRTFCLENBQUMsQ0FGOEI7QUFNL0I7SUFNSSxZQUFZLEtBQWE7UUFGekIsZ0JBQVcsR0FBRyxJQUFJLGVBQVMsRUFBcUIsQ0FBQTtRQUc1QyxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQTtJQUN0QixDQUFDO0lBRUQsd0VBQXdFO0lBRXhFOztPQUVHO0lBQ0gsT0FBTztRQUNILElBQUksSUFBSSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUE7UUFDNUIsT0FBTyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRTtZQUNwRCxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUE7WUFBQyxDQUFDO1lBQzdCLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQTtZQUMvQixJQUFJLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQTtZQUNaLE9BQU8sQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLDRCQUE0QixJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQTtZQUNuRSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUN6QixDQUFDLENBQUMsQ0FBQTtRQUNGLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFBO0lBQ3ZCLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUs7UUFDRCxJQUFJLElBQUksR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFBO1FBQzVCLElBQUksQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUE7UUFDZixPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQywrQkFBK0IsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUE7UUFDakUsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFBO1FBQ2QsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUE7SUFDdkIsQ0FBQztJQUVELDRFQUE0RTtJQUU1RTs7T0FFRztJQUNILG1CQUFtQixDQUFJLE9BQWU7UUFDbEMsSUFBSSxJQUFJLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQXFCLENBQUE7UUFDL0MsSUFBSSxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDLGVBQWU7WUFDekQsTUFBTSxDQUFDLGVBQWUsQ0FBQyxJQUFJLEtBQUssT0FBTyxDQUFBO1FBQzNDLENBQUMsQ0FBQyxDQUFBO1FBQ0YsRUFBRSxDQUFDLENBQUMsZ0JBQWdCLEtBQUssSUFBSSxDQUFDLENBQUMsQ0FBQztZQUM1QixJQUFJLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLENBQUE7UUFDbEMsQ0FBQztRQUNELE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFBO0lBQ3ZCLENBQUM7O0FBQ0wsQ0FBQztBQXJEWSxVQUFFLEtBcURkLENBQUEifQ== //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYXRhLmNsYXNzZXMuZGIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGRhdGEuY2xhc3Nlcy5kYi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsK0NBQThDO0FBQzlDLDZCQUErQjtBQWMvQjtJQU9JLFlBQVksUUFBZ0IsRUFBRSxZQUFxQixTQUFTO1FBRjVELGdCQUFXLEdBQUcsSUFBSSxlQUFTLEVBQXFCLENBQUE7UUFHNUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUE7UUFDdkIsSUFBSSxDQUFDLEtBQUssR0FBRyxRQUFRLENBQUE7SUFDekIsQ0FBQztJQUVELHdFQUF3RTtJQUV4RTs7T0FFRztJQUNILE9BQU87UUFDSCxJQUFJLElBQUksR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFBO1FBQzVCLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQztZQUM1QixPQUFPLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO2dCQUNwRCxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO29CQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQUMsQ0FBQztnQkFDN0IsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFBO2dCQUMvQixJQUFJLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQTtnQkFDWixPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyw0QkFBNEIsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUE7Z0JBQ25FLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFBO1lBQ3pCLENBQUMsQ0FBQyxDQUFBO1FBQ04sQ0FBQztRQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLE1BQU0sQ0FBQyxDQUFDLENBQUM7WUFDaEMsSUFBSSxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUE7UUFDbEIsQ0FBQztRQUNELE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFBO0lBQ3ZCLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUs7UUFDRCxJQUFJLElBQUksR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFBO1FBQzVCLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQztZQUM1QixJQUFJLENBQUMsRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFBO1FBQ25CLENBQUM7UUFDRCxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQywrQkFBK0IsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUE7UUFDakUsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFBO1FBQ2QsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUE7SUFDdkIsQ0FBQztJQUVELDRFQUE0RTtJQUU1RTs7T0FFRztJQUNILG1CQUFtQixDQUFJLE9BQWU7UUFDbEMsSUFBSSxJQUFJLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQXFCLENBQUE7UUFDL0MsSUFBSSxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDLGVBQWU7WUFDekQsTUFBTSxDQUFDLGVBQWUsQ0FBQyxJQUFJLEtBQUssT0FBTyxDQUFBO1FBQzNDLENBQUMsQ0FBQyxDQUFBO1FBQ0YsRUFBRSxDQUFDLENBQUMsZ0JBQWdCLEtBQUssSUFBSSxDQUFDLENBQUMsQ0FBQztZQUM1QixJQUFJLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLENBQUE7UUFDbEMsQ0FBQztRQUNELE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFBO0lBQ3ZCLENBQUM7SUFBQSxDQUFDO0lBRUYsYUFBYSxDQUFDLGVBQWtDO1FBQzVDLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLGVBQWUsQ0FBQyxDQUFBO0lBQ3pDLENBQUM7Q0FFSjtBQWxFRCxnQkFrRUMifQ==

View File

@ -4,14 +4,24 @@ import { Db } from './smartdata.classes.db';
export interface IFindOptions { export interface IFindOptions {
limit?: number; limit?: number;
} }
export interface IDocValidation<T> {
(doc: T): boolean;
}
export declare function Collection(db: Db): (constructor: any) => void;
export declare class DbCollection<T> { export declare class DbCollection<T> {
/**
* the collection that is used, defaults to mongodb collection,
* can be nedb datastore (sub api of mongodb)
*/
collection: plugins.mongodb.Collection; collection: plugins.mongodb.Collection;
name: string; name: string;
db: Db;
objectValidation: IDocValidation<T>;
constructor(nameArg: string, dbArg: Db); constructor(nameArg: string, dbArg: Db);
/** /**
* adds a validation function that all newly inserted and updated objects have to pass * adds a validation function that all newly inserted and updated objects have to pass
*/ */
addObjectValidation(funcArg: any): void; addDocValidation(funcArg: IDocValidation<T>): void;
/** /**
* finds an object in the DbCollection * finds an object in the DbCollection
*/ */
@ -24,5 +34,8 @@ export declare class DbCollection<T> {
* inserts many objects at once into the DbCollection * inserts many objects at once into the DbCollection
*/ */
insertMany(docArrayArg: T[]): plugins.q.Promise<void>; insertMany(docArrayArg: T[]): plugins.q.Promise<void>;
private checkDoc(doc); /**
* checks a Doc for constraints
*/
private checkDoc(docArg);
} }

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,34 @@
import { Db } from './smartdata.classes.db';
import { DbCollection } from './smartdata.classes.dbcollection'; import { DbCollection } from './smartdata.classes.dbcollection';
export declare type TDocCreation = 'db' | 'data' | 'mixed'; export declare type TDocCreation = 'db' | 'data' | 'mixed';
/**
* sva - saveable decorator to be used on class properties
*/
export declare function saveable(target: DbDoc<any>, key: string): void;
export declare class DbDoc<T> { export declare class DbDoc<T> {
/**
* the collection object an Doc belongs to
*/
collection: DbCollection<T>; collection: DbCollection<T>;
/**
* how the Doc in memory was created, may prove useful later.
*/
creationType: TDocCreation; creationType: TDocCreation;
constructor(collectionNameArg: string, dbArg: Db); /**
* an array of saveable properties of a doc
*/
saveableProperties: string[];
/**
* class constructor
*/
constructor();
/**
* saves this instance but not any connected items
* may lead to data inconsistencies, but is faster
*/
save(): void; save(): void;
/**
* also store any referenced objects to DB
* better for data consistency
*/
saveDeep(): void; saveDeep(): void;
} }

View File

@ -1,13 +1,39 @@
"use strict"; "use strict";
const smartdata_classes_dbcollection_1 = require('./smartdata.classes.dbcollection'); /**
* sva - saveable decorator to be used on class properties
*/
function saveable(target, key) {
console.log('called sva');
if (!target.saveableProperties) {
target.saveableProperties = [];
}
target.saveableProperties.push(key);
}
exports.saveable = saveable;
class DbDoc { class DbDoc {
constructor(collectionNameArg, dbArg) { /**
this.collection = new smartdata_classes_dbcollection_1.DbCollection(collectionNameArg, dbArg); * class constructor
*/
constructor() {
this.collection = this.constructor['dbCollection'];
} }
/**
* saves this instance but not any connected items
* may lead to data inconsistencies, but is faster
*/
save() { save() {
let saveableObject = {};
for (let propertyNameString of this.saveableProperties) {
saveableObject[propertyNameString] = this[propertyNameString];
} }
}
/**
* also store any referenced objects to DB
* better for data consistency
*/
saveDeep() { saveDeep() {
this.save();
} }
} }
exports.DbDoc = DbDoc; exports.DbDoc = DbDoc;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYXRhLmNsYXNzZXMuZGJkb2MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGRhdGEuY2xhc3Nlcy5kYmRvYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBR0EsaURBQTZCLGtDQUU3QixDQUFDLENBRjhEO0FBSS9EO0lBR0ksWUFBWSxpQkFBeUIsRUFBRSxLQUFTO1FBQzVDLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSw2Q0FBWSxDQUFJLGlCQUFpQixFQUFFLEtBQUssQ0FBQyxDQUFBO0lBQ25FLENBQUM7SUFDRCxJQUFJO0lBRUosQ0FBQztJQUNELFFBQVE7SUFFUixDQUFDO0FBQ0wsQ0FBQztBQVpZLGFBQUssUUFZakIsQ0FBQSJ9 //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYXRhLmNsYXNzZXMuZGJkb2MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGRhdGEuY2xhc3Nlcy5kYmRvYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBT0E7O0dBRUc7QUFDSCxrQkFBeUIsTUFBa0IsRUFBRSxHQUFXO0lBQ3BELE9BQU8sQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUE7SUFDekIsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDO1FBQUMsTUFBTSxDQUFDLGtCQUFrQixHQUFHLEVBQUUsQ0FBQTtJQUFDLENBQUM7SUFDbEUsTUFBTSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQTtBQUN2QyxDQUFDO0FBSkQsNEJBSUM7QUFFRDtJQWlCSTs7T0FFRztJQUNIO1FBQ0ksSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLGNBQWMsQ0FBQyxDQUFBO0lBQ3RELENBQUM7SUFFRDs7O09BR0c7SUFDSCxJQUFJO1FBQ0EsSUFBSSxjQUFjLEdBQUcsRUFBRSxDQUFBO1FBQ3ZCLEdBQUcsQ0FBQyxDQUFDLElBQUksa0JBQWtCLElBQUksSUFBSSxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQztZQUNyRCxjQUFjLENBQUMsa0JBQWtCLENBQUMsR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsQ0FBQTtRQUNqRSxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7T0FHRztJQUNILFFBQVE7UUFDSixJQUFJLENBQUMsSUFBSSxFQUFFLENBQUE7SUFDZixDQUFDO0NBQ0o7QUExQ0Qsc0JBMENDIn0=

View File

@ -1,5 +1,8 @@
import 'typings-global'; import 'typings-global';
export import assert = require('assert'); import * as assert from 'assert';
export import beautylog = require('beautylog'); import * as beautylog from 'beautylog';
export import mongodb = require('mongodb'); import * as lodash from 'lodash';
export import q = require('q'); import * as mongodb from 'mongodb';
import * as q from 'q';
declare let nedb: any;
export { assert, beautylog, lodash, mongodb, q, nedb };

View File

@ -1,7 +1,15 @@
"use strict"; "use strict";
require('typings-global'); require("typings-global");
exports.assert = require('assert'); const assert = require("assert");
exports.beautylog = require('beautylog'); exports.assert = assert;
exports.mongodb = require('mongodb'); const beautylog = require("beautylog");
exports.q = require('q'); exports.beautylog = beautylog;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYXRhLnBsdWdpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGRhdGEucGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsUUFBTyxnQkFDUCxDQUFDLENBRHNCO0FBQ1QsY0FBTSxXQUFXLFFBQVEsQ0FBQyxDQUFBO0FBQzFCLGlCQUFTLFdBQVcsV0FBVyxDQUFDLENBQUE7QUFDaEMsZUFBTyxXQUFXLFNBQVMsQ0FBQyxDQUFBO0FBQzVCLFNBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQSJ9 const lodash = require("lodash");
exports.lodash = lodash;
const mongodb = require("mongodb");
exports.mongodb = mongodb;
const q = require("q");
exports.q = q;
let nedb = require('nedb');
exports.nedb = nedb;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYXRhLnBsdWdpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGRhdGEucGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsMEJBQXVCO0FBQ3ZCLGlDQUFnQztBQVE1Qix3QkFBTTtBQVBWLHVDQUFzQztBQVFsQyw4QkFBUztBQVBiLGlDQUFnQztBQVE1Qix3QkFBTTtBQVBWLG1DQUFrQztBQVE5QiwwQkFBTztBQVBYLHVCQUFzQjtBQVFsQixjQUFDO0FBUEwsSUFBSSxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFBO0FBUXRCLG9CQUFJIn0=

View File

@ -1,6 +1,6 @@
{ {
"name": "smartdata", "name": "smartdata",
"version": "1.0.14", "version": "1.0.19",
"description": "do more with data", "description": "do more with data",
"main": "dist/index.js", "main": "dist/index.js",
"typings": "dist/index.d.ts", "typings": "dist/index.d.ts",
@ -19,20 +19,25 @@
}, },
"homepage": "https://gitlab.com/pushrocks/smartdata#README", "homepage": "https://gitlab.com/pushrocks/smartdata#README",
"dependencies": { "dependencies": {
"@types/mongodb": "^2.1.32", "@types/lodash": "^4.14.39",
"@types/q": "0.0.30", "@types/mongodb": "^2.1.34",
"beautylog": "^5.0.23", "@types/nedb": "0.0.31",
"lik": "^1.0.15", "@types/q": "0.0.32",
"mongodb": "^2.2.9", "beautylog": "^6.0.0",
"lik": "^1.0.24",
"lodash": "^4.17.2",
"mongodb": "^2.2.11",
"nedb": "^1.8.0",
"q": "^1.4.1", "q": "^1.4.1",
"runtime-type-checks": "0.0.4",
"typings-global": "^1.0.14" "typings-global": "^1.0.14"
}, },
"devDependencies": { "devDependencies": {
"@types/shelljs": "^0.3.30", "@types/shelljs": "^0.3.32",
"@types/should": "^8.1.29", "@types/should": "^8.1.30",
"shelljs": "^0.7.4", "shelljs": "^0.7.5",
"should": "^11.1.0", "should": "^11.1.1",
"smartstring": "^2.0.17", "smartstring": "^2.0.22",
"typings-test": "^1.0.3" "typings-test": "^1.0.3"
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -18,8 +18,6 @@ interface ITestObject1 {
let testDbCollection: smartdata.DbCollection<ITestObject1> let testDbCollection: smartdata.DbCollection<ITestObject1>
describe('mongodb', function () { describe('mongodb', function () {
it('should start mongodb', function (done) { it('should start mongodb', function (done) {
this.timeout(30000) this.timeout(30000)
@ -72,22 +70,30 @@ describe('smartdata', function () {
testDb.close() testDb.close()
}) })
it('should create an extended class', function () { it('should create an extended class', function () {
@smartdata.Collection(testDb)
class TestCar extends smartdata.DbDoc<TestCar> { class TestCar extends smartdata.DbDoc<TestCar> {
@smartdata.saveable
color: string color: string
constructor(optionsArg: { constructor(optionsArg: {
color: string, color: string,
property2: number property2: number
}) { }) {
super('testCar',testDb) super()
this.color = optionsArg.color
} }
}; };
let testCarInstance = new TestCar({ let testCarInstance = new TestCar({
color: 'red', color: 'red',
property2: 2 property2: 2
}) })
should(testCarInstance.saveableProperties[0]).equal('color')
console.log(TestCar)
should(testCarInstance.collection).be.instanceof(smartdata.DbCollection)
should(testCarInstance).be.instanceof(smartdata.DbDoc) should(testCarInstance).be.instanceof(smartdata.DbDoc)
testCarInstance.save() testCarInstance.save()
it('should get a collection for testCar',function() { it('should get a collection for testCar', function () {
}) })
}) })
}) })
@ -104,3 +110,24 @@ describe('mongodb', function () {
mongoChildProcess.kill('SIGTERM') mongoChildProcess.kill('SIGTERM')
}) })
}) })
describe('smartdata with nedb', function () {
let testDb: smartdata.Db
let testCollection: smartdata.DbCollection<ITestObject1>
it('should create a DB with nedb', function () {
testDb = new smartdata.Db('any', 'nedb')
testDb.connect()
testCollection = new smartdata.DbCollection<ITestObject1>('anyName', testDb)
})
it('should insert a doc', function(done){
testCollection.insertOne({ value1: 'hi' }).then(() => {done()})
})
it('should find the inserted document', function(done){
testCollection.find({ value1: 'hi' }).then(x => {
console.log(x)
done()
})
})
})

View File

@ -3,16 +3,26 @@ import { Objectmap } from 'lik'
import { DbCollection } from './smartdata.classes.dbcollection' import { DbCollection } from './smartdata.classes.dbcollection'
/**
* interface - indicates the database type
*/
export type TDbType = 'mongodb' | 'nedb'
/**
* interface - indicates the connection status of the db
*/
export type TConnectionStatus = 'disconnected' | 'connected' | 'failed' export type TConnectionStatus = 'disconnected' | 'connected' | 'failed'
export class Db { export class Db {
dbType: TDbType
dbUrl: string dbUrl: string
db: plugins.mongodb.Db db: plugins.mongodb.Db
status: TConnectionStatus status: TConnectionStatus
collections = new Objectmap<DbCollection<any>>() collections = new Objectmap<DbCollection<any>>()
constructor(dbUrl: string) { constructor(dbUrlArg: string, dbTypeArg: TDbType = 'mongodb') {
this.dbUrl = dbUrl this.dbType = dbTypeArg
this.dbUrl = dbUrlArg
} }
// basic connection stuff ---------------------------------------------- // basic connection stuff ----------------------------------------------
@ -22,6 +32,7 @@ export class Db {
*/ */
connect(): plugins.q.Promise<any> { connect(): plugins.q.Promise<any> {
let done = plugins.q.defer() let done = plugins.q.defer()
if (this.dbType === 'mongodb') {
plugins.mongodb.MongoClient.connect(this.dbUrl, (err, db) => { plugins.mongodb.MongoClient.connect(this.dbUrl, (err, db) => {
if (err) { console.log(err) } if (err) { console.log(err) }
plugins.assert.equal(null, err) plugins.assert.equal(null, err)
@ -29,6 +40,9 @@ export class Db {
plugins.beautylog.success(`connected to database at ${this.dbUrl}`) plugins.beautylog.success(`connected to database at ${this.dbUrl}`)
done.resolve(this.db) done.resolve(this.db)
}) })
} else if (this.dbType === 'nedb') {
this.db = null
}
return done.promise return done.promise
} }
@ -37,7 +51,9 @@ export class Db {
*/ */
close(): plugins.q.Promise<any> { close(): plugins.q.Promise<any> {
let done = plugins.q.defer() let done = plugins.q.defer()
if (this.dbType === 'mongodb') {
this.db.close() this.db.close()
}
plugins.beautylog.ok(`disconnected to database at ${this.dbUrl}`) plugins.beautylog.ok(`disconnected to database at ${this.dbUrl}`)
done.resolve() done.resolve()
return done.promise return done.promise
@ -58,4 +74,9 @@ export class Db {
} }
return done.promise return done.promise
}; };
addCollection(dbCollectionArg: DbCollection<any>) {
this.collections.add(dbCollectionArg)
}
} }

View File

@ -5,32 +5,70 @@ export interface IFindOptions {
limit?: number limit?: number
} }
export interface IDocValidation<T> {
(doc: T): boolean
}
export function Collection(db: Db) {
return function (constructor) {
constructor['dbCollection'] = new DbCollection(constructor.name, db)
}
}
export class DbCollection<T> { export class DbCollection<T> {
/**
* the collection that is used, defaults to mongodb collection,
* can be nedb datastore (sub api of mongodb)
*/
collection: plugins.mongodb.Collection collection: plugins.mongodb.Collection
name: string name: string
db: Db
objectValidation: IDocValidation<T> = null
constructor(nameArg: string, dbArg: Db) { constructor(nameArg: string, dbArg: Db) {
this.name = nameArg this.name = nameArg
this.db = dbArg
if (this.db.dbType === 'mongodb') {
this.collection = dbArg.db.collection(nameArg) this.collection = dbArg.db.collection(nameArg)
} else {
this.collection = new plugins.nedb()
}
} }
/** /**
* adds a validation function that all newly inserted and updated objects have to pass * adds a validation function that all newly inserted and updated objects have to pass
*/ */
addObjectValidation(funcArg) { } addDocValidation(funcArg: IDocValidation<T>) {
this.objectValidation = funcArg
}
/** /**
* finds an object in the DbCollection * finds an object in the DbCollection
*/ */
find(docMatchArg: T | any, optionsArg?: IFindOptions): plugins.q.Promise<T[]> { find(docMatchArg: T | any, optionsArg?: IFindOptions): plugins.q.Promise<T[]> {
let done = plugins.q.defer<T[]>() let done = plugins.q.defer<T[]>()
if (this.db.dbType === 'mongodb') {
let findCursor = this.collection.find(docMatchArg) let findCursor = this.collection.find(docMatchArg)
if (optionsArg) { if (optionsArg) {
if ( optionsArg.limit ) { findCursor = findCursor.limit(1) } if (optionsArg.limit) { findCursor = findCursor.limit(1) }
} }
findCursor.toArray((err, docs) => { findCursor.toArray((err, docs) => {
if (err) { throw err } if (err) {
done.reject(err)
throw err
}
done.resolve(docs) done.resolve(docs)
}) })
} else if (this.db.dbType === 'nedb') {
this.collection.find(docMatchArg, (err, docs) => {
if (err) {
done.reject(err)
throw err
}
done.resolve(docs)
})
}
return done.promise return done.promise
} }
@ -39,9 +77,23 @@ export class DbCollection<T> {
*/ */
insertOne(docArg: T): plugins.q.Promise<void> { insertOne(docArg: T): plugins.q.Promise<void> {
let done = plugins.q.defer<void>() let done = plugins.q.defer<void>()
this.checkDoc(docArg).then(() => { this.checkDoc(docArg).then(
() => {
if (this.db.dbType === 'mongodb') {
this.collection.insertOne(docArg) this.collection.insertOne(docArg)
.then(() => { done.resolve() }) .then(() => { done.resolve() })
} else if (this.db.dbType === 'nedb') {
this.collection.insert(docArg, (err, newDoc) => {
if (err) {
done.reject(err)
throw err
}
done.resolve()
})
}
},
() => {
done.reject(new Error('one the docs did not pass validation'))
}) })
return done.promise return done.promise
} }
@ -52,19 +104,41 @@ export class DbCollection<T> {
insertMany(docArrayArg: T[]): plugins.q.Promise<void> { insertMany(docArrayArg: T[]): plugins.q.Promise<void> {
let done = plugins.q.defer<void>() let done = plugins.q.defer<void>()
let checkDocPromiseArray: plugins.q.Promise<void>[] = [] let checkDocPromiseArray: plugins.q.Promise<void>[] = []
for (let docArg of docArrayArg){ for (let docArg of docArrayArg) {
checkDocPromiseArray.push(this.checkDoc(docArg)) checkDocPromiseArray.push(this.checkDoc(docArg))
} }
plugins.q.all(checkDocPromiseArray).then(() => { plugins.q.all(checkDocPromiseArray).then(() => {
if (this.db.dbType === 'mongodb') {
this.collection.insertMany(docArrayArg) this.collection.insertMany(docArrayArg)
.then(() => { done.resolve() }) .then(() => { done.resolve() })
} else if (this.db.dbType === 'nedb') {
let paramArray = plugins.lodash.concat<any>(docArrayArg, (err, newDoc) => {
if (err) {
done.reject(err)
throw err
}
done.resolve()
})
this.collection.insert.apply(null, paramArray)
}
}) })
return done.promise return done.promise
} }
private checkDoc(doc: T): plugins.q.Promise<void> { /**
* checks a Doc for constraints
*/
private checkDoc(docArg: T): plugins.q.Promise<void> {
let done = plugins.q.defer<void>() let done = plugins.q.defer<void>()
let validationResult = true
if (this.objectValidation) {
validationResult = this.objectValidation(docArg)
}
if (validationResult) {
done.resolve() done.resolve()
} else {
done.reject('validation of object did not pass')
}
return done.promise return done.promise
} }
} }

View File

@ -5,16 +5,55 @@ import { DbCollection } from './smartdata.classes.dbcollection'
export type TDocCreation = 'db' | 'data' | 'mixed' export type TDocCreation = 'db' | 'data' | 'mixed'
/**
* sva - saveable decorator to be used on class properties
*/
export function saveable(target: DbDoc<any>, key: string) {
console.log('called sva')
if (!target.saveableProperties) { target.saveableProperties = [] }
target.saveableProperties.push(key)
}
export class DbDoc<T> { export class DbDoc<T> {
/**
* the collection object an Doc belongs to
*/
collection: DbCollection<T> collection: DbCollection<T>
/**
* how the Doc in memory was created, may prove useful later.
*/
creationType: TDocCreation creationType: TDocCreation
constructor(collectionNameArg: string, dbArg: Db ) {
this.collection = new DbCollection<T>(collectionNameArg, dbArg) /**
* an array of saveable properties of a doc
*/
saveableProperties: string[]
/**
* class constructor
*/
constructor() {
this.collection = this.constructor['dbCollection']
} }
/**
* saves this instance but not any connected items
* may lead to data inconsistencies, but is faster
*/
save() { save() {
let saveableObject = {}
for (let propertyNameString of this.saveableProperties) {
saveableObject[propertyNameString] = this[propertyNameString]
}
} }
saveDeep() {
/**
* also store any referenced objects to DB
* better for data consistency
*/
saveDeep() {
this.save()
} }
} }

View File

@ -1,5 +1,16 @@
import 'typings-global' import 'typings-global'
export import assert = require('assert') import * as assert from 'assert'
export import beautylog = require('beautylog') import * as beautylog from 'beautylog'
export import mongodb = require('mongodb') import * as lodash from 'lodash'
export import q = require('q') import * as mongodb from 'mongodb'
import * as q from 'q'
let nedb = require('nedb')
export {
assert,
beautylog,
lodash,
mongodb,
q,
nedb
}

5
tsconfig.json Normal file
View File

@ -0,0 +1,5 @@
{
"compilerOptions": {
"experimentalDecorators": true
}
}