loopback-datasource-juggler/README.md

328 lines
11 KiB
Markdown
Raw Normal View History

2012-01-19 18:11:44 +00:00
## About [<img src="https://secure.travis-ci.org/1602/jugglingdb.png" />](http://travis-ci.org/#!/1602/jugglingdb)
2011-10-05 21:18:38 +00:00
JugglingDB is cross-db ORM for nodejs, providing **common interface** to access
most popular database formats. Currently supported are: mysql, sqlite3,
postgres, couchdb, mongodb, redis, neo4j and js-memory-storage (yep,
self-written engine for test-usage only). You can add your favorite database
adapter, checkout one of the existing adapters to learn how, it's super-easy, I
guarantee.
Jugglingdb also works on client-side (using WebService and Memory adapters),
which allows to write rich client-side apps talking to server using JSON API.
2011-10-05 21:18:38 +00:00
## Installation
2012-01-18 15:53:25 +00:00
npm install jugglingdb
2011-10-05 21:18:38 +00:00
2012-12-15 13:03:06 +00:00
plus you should install appropriated adapter, for example for redis:
npm install jugglingdb-redis
check following list of available adapters
## JugglingDB adapters
<table>
<thead>
<tr>
<th>Database type</th>
<th>Package name</th>
<th>Maintainer</th>
<th>Build status</th>
</tr>
</thead>
<tbody>
2012-12-15 13:20:18 +00:00
<!-- Firebird -->
2012-12-15 13:03:06 +00:00
<tr>
<td><a href="http://firebirdsql.org"><img src="http://firebirdsql.org/favicon.ico" alt="Firebird"/></a> Firebird</td>
<td><a href="http://github.com/hgourvest/jugglingdb-firebird">jugglingdb-firebird</a></td>
<td><a href="http://github.com/hgourvest">Henri Gourvest</a></td>
2012-12-15 13:20:18 +00:00
<td></td>
2012-12-15 13:03:06 +00:00
</tr>
2012-12-15 13:20:18 +00:00
<!-- MongoDB -->
2012-12-15 13:03:06 +00:00
<tr>
<td><a href="http://www.mongodb.org"><img src="http://mongodb.ru/favicon.ico" alt="MongoDB" /></a> MongoDB</td>
<td><a href="https://github.com/1602/jugglingdb-mongodb">jugglingdb-mongodb</a></td>
2012-12-15 13:03:06 +00:00
<td><a href="/anatoliychakkaev">Anatoliy Chakkaev</a></td>
<td><a href="https://travis-ci.org/1602/jugglingdb-mongodb"><img src="https://travis-ci.org/1602/jugglingdb-mongodb.png?branch=master" alt="Build Status" /></a></td>
</tr>
2012-12-15 13:20:18 +00:00
<!-- MySQL -->
2012-12-15 13:03:06 +00:00
<tr>
<td><a href="http://www.mysql.com/"><img src="https://github.com/1602/jugglingdb/raw/master/media/mysql.ico" style="vertical-align:middle"" alt="MySQL" /></a> MySQL</td>
<td><a href="https://github.com/1602/jugglingdb-mysql">jugglingdb-mysql</a></td>
2012-12-15 13:03:06 +00:00
<td><a href="/anatoliychakkaev">Anatoliy Chakkaev</a></td>
<td><a href="https://travis-ci.org/1602/jugglingdb-mysql"><img src="https://travis-ci.org/1602/jugglingdb-mysql.png?branch=master" alt="Build Status" /></a></td>
</tr>
2012-12-15 13:20:18 +00:00
<!-- CouchDB / nano -->
<tr>
2012-12-15 14:50:11 +00:00
<td><a href="http://couchdb.apache.org/"><img width="16" src="http://couchdb.apache.org/favicon.ico" style="vertical-align:middle"" alt="CouchDB" /></a> CouchDB / nano</td>
<td><a href="https://github.com/1602/jugglingdb-nano">jugglingdb-nano</a></td>
2012-12-15 13:20:18 +00:00
<td><a href="/nrw">Nicholas Westlake</a></td>
<td><a href="https://travis-ci.org/1602/jugglingdb-nano"><img src="https://travis-ci.org/1602/jugglingdb-nano.png?branch=master" alt="Build Status" /></a></td>
</tr>
<!-- PostgreSQL -->
<tr>
<td><a href="http://www.postgresql.org/"><img src="http://www.postgresql.org/favicon.ico" style="vertical-align:middle"" alt="PostgreSQL" /></a> PostgreSQL</td>
<td><a href="https://github.com/1602/jugglingdb-postgres">jugglingdb-postgres</a></td>
2012-12-15 13:20:18 +00:00
<td><a href="/anatoliychakkaev">Anatoliy Chakkaev</a></td>
<td><a href="https://travis-ci.org/1602/jugglingdb-postgres"><img src="https://travis-ci.org/1602/jugglingdb-postgres.png?branch=master" alt="Build Status" /></a></td>
</tr>
<!-- Redis -->
2012-12-15 13:03:06 +00:00
<tr>
<td><a href="http://redis.io/"><img src="http://redis.io/images/favicon.png" alt="Redis" /></a> Redis</td>
<td><a href="https://github.com/1602/jugglingdb-redis">jugglingdb-redis</a></td>
2012-12-15 13:03:06 +00:00
<td><a href="/anatoliychakkaev">Anatoliy Chakkaev</a></td>
<td><a href="https://travis-ci.org/1602/jugglingdb-redis"><img src="https://travis-ci.org/1602/jugglingdb-redis.png?branch=master" alt="Build Status" /></a></td>
</tr>
2012-12-15 13:20:18 +00:00
<!-- SQLite -->
<tr>
<td><a href="http://www.sqlite.org/"><img width="16" src="https://github.com/1602/jugglingdb/raw/master/media/sqlite.png" style="vertical-align:middle"" alt="SQLite" /></a> SQLite</td>
<td><a href="https://github.com/1602/jugglingdb-sqlite3">jugglingdb-sqlite3</a></td>
2012-12-15 13:20:18 +00:00
<td><a href="/anatoliychakkaev">Anatoliy Chakkaev</a></td>
<td><a href="https://travis-ci.org/1602/jugglingdb-sqlite3"><img src="https://travis-ci.org/1602/jugglingdb-sqlite3.png?branch=master" alt="Build Status" /></a></td>
</tr>
<tr>
<td>WebService</td>
<td>built-in</td>
<td><a href="/anatoliychakkaev">Anatoliy Chakkaev</a></td>
<td>n/a</td>
</tr>
<tr>
<td>Memory (bogus)</td>
<td>built-in</td>
<td><a href="/anatoliychakkaev">Anatoliy Chakkaev</a></td>
<td>n/a</td>
</tr>
<tr>
<td>Neo4j</td>
<td>built-in</td>
<td><a href="/anatoliychakkaev"><strike>Anatoliy Chakkaev</strike></a> /!\ Looking for
maintainer</td>
<td>n/a</td>
</tr>
2012-12-15 13:20:18 +00:00
2012-12-15 13:03:06 +00:00
</tbody>
</table>
2012-01-10 16:44:56 +00:00
## Participation
2012-10-18 14:13:33 +00:00
- Check status of project on trello board: https://trello.com/board/jugglingdb/4f0a0b1e27d3103c64288388
- Make sure all tests pass (`npm test` command)
- Feel free to vote and comment on cards (tickets/issues), if you want to join team -- send me a message with your email.
2012-01-10 16:44:56 +00:00
2012-12-14 15:25:32 +00:00
If you want to create your own jugglingdb adapter, you should publish your
adapter package with name `jugglingdb-ADAPTERNAME`. Creating adapter is simple,
check [jugglingdb-redis](https://github.com/1602/jugglingdb-redis) for example. JugglingDB core
2012-12-14 15:25:32 +00:00
exports common tests each adapter should pass, you could create your adapter in
TDD style, check that adapter pass all tests defined in `test/common_test.js`.
2011-10-05 21:18:38 +00:00
## Usage
```javascript
2012-10-18 14:13:33 +00:00
var Schema = require('jugglingdb').Schema;
2012-12-14 15:25:32 +00:00
var schema = new Schema('redis', {port: 6379}); //port number depends on your configuration
2011-10-05 21:18:38 +00:00
// define models
var Post = schema.define('Post', {
title: { type: String, length: 255 },
content: { type: Schema.Text },
2011-10-11 19:28:27 +00:00
date: { type: Date, default: Date.now },
2012-11-10 05:28:07 +00:00
published: { type: Boolean, default: false, index: true }
2011-10-05 21:18:38 +00:00
});
2012-11-10 05:28:07 +00:00
2011-10-06 07:26:26 +00:00
// simplier way to describe model
2011-10-05 21:18:38 +00:00
var User = schema.define('User', {
name: String,
2011-10-06 07:26:26 +00:00
bio: Schema.Text,
2011-10-05 21:18:38 +00:00
approved: Boolean,
joinedAt: Date,
age: Number
}, {
restPath: '/users' // tell WebService adapter which path use as API endpoint
2011-10-05 21:18:38 +00:00
});
2012-11-10 05:28:07 +00:00
// define any custom method
User.prototype.getNameAndAge = function () {
return this.name + ', ' + this.age;
};
// models also accessible in schema:
schema.models.User;
schema.models.Post;
2011-10-05 21:18:38 +00:00
// setup relationships
User.hasMany(Post, {as: 'posts', foreignKey: 'userId'});
// creates instance methods:
// user.posts(conds)
2011-10-15 15:57:35 +00:00
// user.posts.build(data) // like new Post({userId: user.id});
// user.posts.create(data) // build and save
2011-10-05 21:18:38 +00:00
Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
// creates instance methods:
// post.author(callback) -- getter when called with function
// post.author() -- sync getter when called without params
// post.author(user) -- setter when called with object
2012-02-04 13:25:39 +00:00
schema.automigrate(); // required only for mysql NOTE: it will drop User and Post tables
2011-10-05 21:18:38 +00:00
// work with models:
var user = new User;
user.save(function (err) {
2011-10-15 15:57:35 +00:00
var post = user.posts.build({title: 'Hello world'});
2011-10-05 21:18:38 +00:00
post.save(console.log);
});
// or just call it as function (with the same result):
var user = User();
user.save(...);
2011-10-05 21:18:38 +00:00
// Common API methods
// just instantiate model
new Post
// save model (of course async)
Post.create(cb);
// all posts
Post.all(cb)
// all posts by user
2012-01-31 07:51:55 +00:00
Post.all({where: {userId: user.id}, order: 'id', limit: 10, skip: 20});
2011-10-05 21:18:38 +00:00
// the same as prev
user.posts(cb)
2012-11-10 05:28:07 +00:00
// get one latest post
Post.findOne({where: {published: true}, order: 'date DESC'}, cb);
2011-10-05 21:18:38 +00:00
// same as new Post({userId: user.id});
2011-10-15 15:57:35 +00:00
user.posts.build
2011-10-05 21:18:38 +00:00
// save as Post.create({userId: user.id}, cb);
2011-10-15 15:57:35 +00:00
user.posts.create(cb)
2011-10-05 21:18:38 +00:00
// find instance by id
User.find(1, cb)
// count instances
2012-01-31 07:51:55 +00:00
User.count([conditions, ]cb)
2011-10-05 21:18:38 +00:00
// destroy instance
user.destroy(cb);
// destroy all instances
User.destroyAll(cb);
2011-10-10 13:39:10 +00:00
// Setup validations
User.validatesPresenceOf('name', 'email')
User.validatesLengthOf('password', {min: 5, message: {min: 'Password is too short'}});
User.validatesInclusionOf('gender', {in: ['male', 'female']});
User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']});
User.validatesNumericalityOf('age', {int: true});
User.validatesUniquenessOf('email', {message: 'email is not unique'});
2011-10-10 13:39:10 +00:00
user.isValid(function (valid) {
if (!valid) {
user.errors // hash of errors {attr: [errmessage, errmessage, ...], attr: ...}
}
})
```
## Callbacks
The following callbacks supported:
- afterInitialize
- beforeCreate
- afterCreate
- beforeSave
- afterSave
- beforeUpdate
- afterUpdate
- beforeDestroy
- afterDestroy
- beforeValidation
- afterValidation
Each callback is class method of the model, it should accept single argument: `next`, this is callback which
should be called after end of the hook. Except `afterInitialize` because this method is syncronous (called after `new Model`).
## Object lifecycle:
```javascript
var user = new User;
// afterInitialize
user.save(callback);
// beforeValidation
// afterValidation
// beforeSave
// beforeCreate
// afterCreate
// afterSave
// callback
user.updateAttribute('email', 'email@example.com', callback);
// beforeValidation
// afterValidation
// beforeUpdate
// afterUpdate
// callback
user.destroy(callback);
// beforeDestroy
// afterDestroy
// callback
User.create(data, callback);
// beforeValidate
// afterValidate
// beforeCreate
// afterCreate
// callback
2011-10-05 21:18:38 +00:00
```
Read the tests for usage examples: ./test/common_test.js
2011-10-10 13:39:10 +00:00
Validations: ./test/validations_test.js
2011-10-05 21:18:38 +00:00
## Your own database adapter
2011-10-06 20:21:27 +00:00
To use custom adapter, pass it's package name as first argument to `Schema` constructor:
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
var mySchema = new Schema('mycouch', {host:.., port:...});
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
In that case your adapter should be named as 'jugglingdb-mycouch' npm package.
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
## Testing
2012-12-10 13:34:44 +00:00
2013-01-15 10:53:35 +00:00
Core of jugglingdb tests only basic features (database-agnostic) like
validations, hooks and runs db-specific tests using memory storage. It also
exports complete bucket of tests for external running. Each adapter should run
this bucket (example from `jugglingdb-redis`):
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
var jdb = require('jugglingdb'),
Schema = jdb.Schema,
test = jdb.test;
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
var schema = new Schema(__dirname + '/..', {host: 'localhost', database: 1});
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
test(module.exports, schema);
Each adapter could add specific tests to standart bucket:
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
test.it('should do something special', function (test) {
test.done();
});
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
Or it could tell core to skip some test from bucket:
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
test.skip('name of test case');
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
To run tests use this command:
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
npm test
2011-10-05 21:18:38 +00:00
2013-01-15 10:53:35 +00:00
Before running make sure you've installed package (`npm install`) and if you
running some specific adapter tests, ensure you've configured database
correctly (host, port, username, password).
2011-10-05 21:18:38 +00:00
## Contributing
If you have found a bug please write unit test, and make sure all other tests still pass before pushing code to repo.
## License
MIT