Comparing version 0.1.0 to 0.1.1
@@ -66,3 +66,7 @@ var eventEmitter = require('events').EventEmitter; | ||
Document.prototype.update = function(newDoc) { | ||
//TODO | ||
} | ||
var document = module.exports = exports = Document; |
107
lib/index.js
@@ -14,19 +14,45 @@ /*! | ||
this.models = {}; | ||
this.defaultSettings = { | ||
host: 'localhost', | ||
port: 28015, | ||
db: 'test', | ||
poolMax: 10, | ||
poolMin: 1, | ||
enforce: { type: true, missing: false, extra: false } | ||
}; | ||
this.options = {}; | ||
}; | ||
Thinky.prototype.connect = function(options) { | ||
var self = this; | ||
if (!(options instanceof Object)) options = {} | ||
this.host = options.host || 'localhost'; | ||
this.port = options.port || 28015; | ||
this.db = options.db || 'test'; | ||
if (!(options instanceof Object)) { | ||
options = {}; | ||
} | ||
else if (options === null) { | ||
options = {}; | ||
} | ||
else if (Object.prototype.toString.call(options) === '[object Array]') { | ||
options = {}; | ||
} | ||
//TODO use this.setOptions and add checks | ||
this.options = { | ||
host: options.host || this.defaultSettings.host, | ||
port: options.port || this.defaultSettings.port, | ||
db: options.db || this.defaultSettings.db, | ||
poolMax: options.poolMax || 10, | ||
poolMin: options.poolMin || 1 | ||
poolMin: options.poolMin || 1, | ||
enforce: this.options.enforce || this.defaultSettings.enforce | ||
} | ||
this.createPool(); | ||
//TODO add option for not using a pool | ||
}; | ||
Thinky.prototype.createPool = function () { | ||
var self = this; | ||
this.pool = gpool.Pool({ | ||
@@ -36,5 +62,4 @@ name: 'rethinkdbPool', | ||
r.connect({ | ||
host: self.host, | ||
port: self.port, | ||
db: self.db | ||
host: self.options.host, | ||
port: self.options.port | ||
}, function(error, conn) { | ||
@@ -47,4 +72,4 @@ return cb(error, conn); | ||
}, | ||
max: self.poolMax, | ||
min: self.poolMin, | ||
max: self.options.poolMax, | ||
min: self.options.poolMin, | ||
idleTimeoutMillis: 30000, | ||
@@ -57,3 +82,3 @@ log: function(what, level) { | ||
}) | ||
}; | ||
} | ||
@@ -63,11 +88,35 @@ Thinky.prototype.getOptions = function () { | ||
}; | ||
Thinky.prototype.setOptions = function (options, overwrite) { | ||
//TODO if we change poolMax or poolMin, we have to recreate a pool | ||
if (overwrite) { | ||
this.options = options; | ||
Thinky.prototype.setOptions = function (options) { | ||
shouldDrain = false; | ||
for(var key in options) { | ||
if ((key === 'host') || (key === 'port')) shouldDrain = true; | ||
this.setOption(key, options[key], false); | ||
} | ||
if (shouldDrain === true) { | ||
this.disconnect(); | ||
this.createPool(); | ||
} | ||
return this.options; | ||
}; | ||
Thinky.prototype.setOption = function (key, value, drainPool) { | ||
drainPool = drainPool || true; | ||
//TODO Update generic pool to be able to change the pool size. | ||
if (key === 'enforce') { | ||
this.options.enforce = this.checkEnforce(value, this.defaultSettings.enforce); | ||
} | ||
else { | ||
for(var key in options) { | ||
this.options[key] = options[key] | ||
if (value === null) { | ||
this.options[key] = this.defaultSettings[key]; | ||
} | ||
else { | ||
this.options[key] = value; | ||
} | ||
if (drainPool === true) { | ||
if ((key === 'host') || (key === 'port')) { | ||
this.disconnect(); | ||
this.createPool(); | ||
} | ||
} | ||
} | ||
@@ -77,8 +126,20 @@ return this.options; | ||
Thinky.prototype.checkEnforce = function(newValue, defaultValue) { | ||
if (newValue === true) { | ||
return {missing: true, extra: true, type: true} | ||
} | ||
else if (newValue === false) { | ||
return {missing: false, extra: false, type: false} | ||
} | ||
else if ((newValue != null) && (typeof newValue === 'object') | ||
&& (typeof settings.enforce.extra === 'boolean') | ||
&& (typeof settings.enforce.type === 'boolean') | ||
&& (typeof settings.enforce.missing === 'boolean')) { | ||
return newValue | ||
} | ||
else { | ||
return defaultValue | ||
} | ||
Thinky.prototype.setOption = function (key, value) { | ||
//TODO if we change poolMax or poolMin, we have to recreate a pool | ||
this.options[key] = value; | ||
return this; | ||
}; | ||
} | ||
@@ -85,0 +146,0 @@ Thinky.prototype.getOption = function (key) { |
111
lib/model.js
var r = require('rethinkdb'); | ||
var Document = require('./document.js'); | ||
function Model(name, schema, settings, pool) { | ||
function Model(name, schema, settings, thinky) { | ||
settings = settings || {}; | ||
@@ -9,10 +9,16 @@ | ||
this.schema = schema; | ||
this.settings = {}; | ||
this.settings.primaryKey = (settings.primaryKey)? settings.primaryKey: 'id'; | ||
this.settings.enforce = (settings.enforce)? settings.enforce: false; | ||
this.settings.pool = pool; | ||
this.checkEnforce = thinky.checkEnforce; // TODO fix ugliness | ||
this.settings = { | ||
primaryKey: settings.primaryKey || 'id', | ||
enforce: this.checkEnforce(settings.enforce, thinky.options.enforce) | ||
}; | ||
this.thinky = thinky; | ||
this.pool = thinky.pool; | ||
this.thinkyOptions = thinky.options | ||
} | ||
Model.compile = function(name, schema, settings, thinky) { | ||
modelProto = new Model(name, schema, settings, thinky.pool); | ||
modelProto = new Model(name, schema, settings, thinky); | ||
function model(doc, docSettings) { | ||
@@ -22,3 +28,3 @@ doc = doc || {}; | ||
docSettings = docSettings || {}; | ||
var enforce = (docSettings.enforce != null)? docSettings.enforce: modelProto.settings.enforce; | ||
var enforce = modelProto.checkEnforce(docSettings.enforce, modelProto.settings.enforce); | ||
@@ -47,4 +53,5 @@ result = {} | ||
//TODO Let people default arrays/object | ||
//TODO Implement maxLenght, minLenght | ||
Model.prototype.createBasedOnSchema = function(result, doc, originalDoc, enforce, prefix, schema) { | ||
var enforce = enforce || false; | ||
var enforce = enforce; // Add another check here? | ||
var prefix = prefix || ''; | ||
@@ -82,3 +89,3 @@ | ||
if (enforce) { | ||
if (enforce.extra === true) { | ||
// TODO Throw if doc has too many fields | ||
@@ -96,3 +103,6 @@ } | ||
} | ||
else if (enforce) { | ||
else if ((enforce.missing === true) && (doc[key] == null)) { | ||
throw new Error("Value for "+prefix+"["+key+"] must be defined") | ||
} | ||
else if ((doc[key] != null) && (enforce.type === true)) { | ||
throw new Error("Value for "+prefix+"["+key+"] must be a "+type.name) | ||
@@ -110,3 +120,3 @@ } | ||
result[key] = schema_key['default'](originalDoc); | ||
if ((enforce) && (result[key] !== typeOf)) { | ||
if ((enforce.type === true) && (typeof result[key] !== typeOf)) { | ||
throw new Error("The default function did not return a "+type.name+" for "+prefix+"["+key+"]"); | ||
@@ -119,3 +129,6 @@ } | ||
} | ||
else if (enforce) { | ||
else if ((enforce.missing === true) && (doc[key] == null)) { | ||
throw new Error("Value for "+prefix+"["+key+"] must be defined") | ||
} | ||
else if ((doc[key] != null) && (enforce.type === true)) { | ||
throw new Error("Value for "+prefix+"["+key+"] must be a "+type.name); | ||
@@ -139,13 +152,5 @@ } | ||
// Called on the model | ||
Model.prototype.getSettings = function() { | ||
return this.settings; | ||
} | ||
Model.prototype.getSettings2 = function() { | ||
return this.settings; | ||
} | ||
@@ -156,16 +161,7 @@ Model.prototype.getPrimaryKey = function() { | ||
// Called by the document | ||
Model.prototype.getDocument = function() { | ||
return this.__proto__; | ||
} | ||
Model.prototype.defineOnModel = function(name, fn) { | ||
this.getModel()[name] = fn; | ||
} | ||
Model.prototype.save = function(callback) { | ||
//TODO Implement replace | ||
var self = this; // The document | ||
var model = this.getModel(); | ||
model.getSettings().pool.acquire( function(error, connection) { | ||
model.pool.acquire( function(error, connection) { | ||
if (error) { | ||
@@ -179,3 +175,4 @@ return callback(error, null); | ||
var primaryKey = model.getPrimaryKey(); | ||
r.table(model.name).get(self[primaryKey]).update(self, {returnVals: true}).run(connection, function(error, result) { | ||
//TODO remove the r.expr around self when the driver will be fixed regarding circular reference | ||
r.db(model.thinkyOptions.db).table(model.name).get(self[primaryKey]).update(r.expr(self), {returnVals: true}).run(connection, function(error, result) { | ||
if (error) { | ||
@@ -191,3 +188,3 @@ if ((callback) && (typeof callback === 'function')) callback(error, null);; | ||
if ((callback) && (typeof callback === 'function')) callback(error, self); | ||
model.getSettings().pool.release(connection); | ||
model.pool.release(connection); | ||
self.emit('save', self, result.old_val); | ||
@@ -199,3 +196,4 @@ } | ||
else { | ||
r.table(model.name).insert(self, {returnVals: true}).run(connection, function(error, result) { | ||
//TODO remove the r.expr around self when the driver will be fixed regarding circular reference | ||
r.db(model.thinkyOptions.db).table(model.name).insert(r.expr(self), {returnVals: true}).run(connection, function(error, result) { | ||
if (error) { | ||
@@ -212,6 +210,7 @@ if ((callback) && (typeof callback === 'function')) callback(error, null); | ||
// We can release the connection because we get back an object | ||
model.getSettings().pool.release(connection); | ||
model.pool.release(connection); | ||
self.emit('save', self, result.old_val); | ||
} | ||
}); | ||
} | ||
@@ -225,3 +224,3 @@ }); | ||
var model = this.__proto__; | ||
model.getSettings().pool.acquire( function(error, connection) { | ||
model.pool.acquire( function(error, connection) { | ||
if (error) { | ||
@@ -231,7 +230,7 @@ return callback(error, null); | ||
if (Object.prototype.toString.call(id) === '[object Array]') { | ||
var table = r.table(model.name); | ||
var table = r.db(model.thinkyOptions.db).table(model.name); | ||
table.getAll.apply(table, id).run( connection, function(error, cursor) { | ||
if (error) { | ||
callback(error, null); | ||
model.getSettings().pool.release(connection); | ||
model.pool.release(connection); | ||
} | ||
@@ -242,3 +241,3 @@ else { | ||
callback(error, null); | ||
model.getSettings().pool.release(connection); | ||
model.pool.release(connection); | ||
} | ||
@@ -253,3 +252,3 @@ else { | ||
callback(null, docs); | ||
model.getSettings().pool.release(connection); | ||
model.pool.release(connection); | ||
} | ||
@@ -263,3 +262,3 @@ }); | ||
//TODO Handle type error | ||
r.table(model.name).get(id).run( connection, function(error, result) { | ||
r.db(model.thinkyOptions.db).table(model.name).get(id).run( connection, function(error, result) { | ||
var doc = null; | ||
@@ -270,3 +269,3 @@ if ((error == null) && (result != null)) { | ||
callback(error, doc); | ||
model.getSettings().pool.release(connection); | ||
model.pool.release(connection); | ||
}); | ||
@@ -280,10 +279,10 @@ } | ||
var model = this.__proto__; | ||
model.getSettings().pool.acquire( function(error, connection) { | ||
model.pool.acquire( function(error, connection) { | ||
if (error) { | ||
return callback(error, null); | ||
} | ||
r.table(model.name).filter(filter).run( connection, function(error, cursor) { | ||
r.db(model.thinkyOptions.db).table(model.name).filter(filter).run( connection, function(error, cursor) { | ||
if (error) { | ||
callback(error, null); | ||
model.getSettings().pool.release(connection); | ||
model.pool.release(connection); | ||
} | ||
@@ -294,3 +293,3 @@ else { | ||
callback(error, null); | ||
model.getSettings().pool.release(connection); | ||
model.pool.release(connection); | ||
} | ||
@@ -305,3 +304,3 @@ else { | ||
callback(null, docs); | ||
model.getSettings().pool.release(connection); | ||
model.pool.release(connection); | ||
} | ||
@@ -318,3 +317,19 @@ }); | ||
Model.prototype.count = function(callback) { | ||
//TODO | ||
var self = this; | ||
var model = this.__proto__; | ||
model.pool.acquire( function(error, connection) { | ||
if (error) { | ||
return callback(error, null); | ||
} | ||
r.db(model.thinkyOptions.db).table(model.name).count().run( connection, function(error, result) { | ||
if (error) { | ||
callback(error, null); | ||
model.pool.release(connection); | ||
} | ||
else { | ||
callback(null, result); | ||
model.pool.release(connection); | ||
} | ||
}); | ||
}); | ||
} | ||
@@ -321,0 +336,0 @@ |
{ | ||
"name": "thinky", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "RethinkDB ORM for Node.js", | ||
@@ -26,3 +26,6 @@ "main": "lib/index.js", | ||
"url": "https://github.com/neumino/thinky/issues" | ||
}, | ||
"dependencies":{ | ||
"generic-pool": ">= 2.0.3" | ||
} | ||
} |
268
README.md
@@ -1,47 +0,261 @@ | ||
Thinky | ||
==== | ||
# Thinky | ||
JavaScript ORM for RethinkDB. | ||
JavaScript ORM for RethinkDB. | ||
_Note_: Alpha release | ||
### Quick start | ||
How to use | ||
==== | ||
Simple case: | ||
Install: | ||
``` | ||
var thinky = require('lib/index.js'); | ||
npm install thinky | ||
``` | ||
Use | ||
```javascript | ||
var thinky = require('thinky'); | ||
thinky.connect({}); | ||
var Cat = thinky.createModel('Cat', {name: String}); | ||
// Create a model | ||
var Cat = thinky.createModel('Cat', {name: String}); | ||
// Create custom methods | ||
Cat.define('hello', function() { console.log("Hello, I'm "+this.name) }); | ||
cat = new Cat({name: 'Catou'}); | ||
cat.hello(); | ||
cat.insert(function(err, result) { | ||
// Create a new object | ||
kitty = new Cat({name: 'Kitty'}); | ||
kitty.hello(); // Log "Hello, I'm Kitty | ||
kitty.save(function(err, result) { | ||
if (err) throw err; | ||
cat = result; | ||
console.log("Kitty has been saved in the database"); | ||
}) | ||
``` | ||
### Docs | ||
_Note_: Work in progress. | ||
Advanced case: | ||
#### Thinky | ||
__Thinky.connect(__ options __)__ | ||
options (object): object with the fields | ||
- host: RethinkDB host (default "localhost") | ||
- port: RethinkDB port for client (default to 28015) | ||
- db: default database (default to "test") | ||
- poolMax: The maximum number of connections in the pool (default to 10) | ||
- poolMin: The minimum number of connections in the pool (default to 1) | ||
- enforce: represents if the schemas should be enforced or not. Its value can be: | ||
- an object with the 3 fields: | ||
- missing // throw on missing fields // default to false | ||
- extra // throw if extra fields are provided // default to false | ||
- type // throw if the type is not the one expected // default to true | ||
- a boolean that set all 3 parameters to the same value | ||
_Note_: The behavior of enforce may change. Since there are more than two cases | ||
- Be flexible | ||
- Forbid extra fields | ||
- Forbid missing fields | ||
- Forbid extra AND missing fields | ||
__Thinky.getOptions()__ | ||
Returns all the options previously set. | ||
__Thinky.getOption(__ optionName __)__ | ||
Returns the value for _optionName_. Possible values: | ||
- host: RethinkDB host | ||
- port: RethinkDB port for client | ||
- db: default database | ||
- poolMax: The maximum number of connections in the pool | ||
- poolMin: The minimum number of connections in the pool | ||
- enforce: Boolean that represent if the schemas should be enforced or not | ||
__Thinky.setOptions(__ options __)__ | ||
Overwrite the options defined in _options_. | ||
The argument _options_ is an object that can have the following fields | ||
- host: RethinkDB host (default "localhost") | ||
- port: RethinkDB port for client (default to 28015) | ||
- db: default database (default to "test") | ||
- poolMax: The maximum number of connections in the pool (default to 10) | ||
- poolMin: The minimum number of connections in the pool (default to 1) | ||
Setting a value to null will delete the value. | ||
_Note_: Almost useless for now since we don't recreate/update the pool | ||
__Thinky.disconnect()__ | ||
Close all the connections. | ||
__Thinky.createModel(__ name, schema, settings __)__ | ||
Create a new model | ||
- name: name of the model | ||
- schema: An object which fields can have the following values: | ||
- String | ||
- Number | ||
- Boolean | ||
- Array | ||
- Object | ||
- settings (object): settings for the model | ||
- enforce: Boolean that represent if the schemas should be enforced or not | ||
Valid schema can be: | ||
``` | ||
Read the code (or the tests) | ||
{ name: String } | ||
{ name: { type: String } } // {name: "Kitty"} or { name: { type: "Kitty" } }? | ||
{ name: { type: String, default: value/function } | ||
{ name: { type: [String, Number, ...] } | ||
{ age: { type: Number, min: ..., max: ...} } | ||
{ comments: { type: Array, min: ..., max: ...} } | ||
{ arrayOfStrings: [ String ] } | ||
{ arrayOfStrings: [ String, maxLength, minLength ] } | ||
``` | ||
Coming soon: default with object and arrays. | ||
Contribute | ||
==== | ||
You are welcome to do a pull request | ||
TODO | ||
==== | ||
- Fully test insert/update/replace | ||
- Add filter | ||
#### Model | ||
__Model.compile(__ name, schema, settings, thinky __)__ | ||
_Internal method_ | ||
__Model.createBasedOnSchema(__ result, doc, originalDoc, enforce, prefix, schema __)__ | ||
_Internal method_ | ||
__Model.checkType(__ result, doc, originalDoc, schema, key, type, typeOf, prefix, enforce __)__ | ||
_Internal method_ | ||
__Model.define(__ key, method __)__ | ||
Define a method on the model that can be called by any instances of the model. | ||
__Model.setSchema(__ schema __)__ | ||
Change the schema -- Not tested (I think) | ||
__Model.getSettings(__ __)__ | ||
Return the settings of the model. | ||
__Model.getDocument(__ __)__ | ||
Return the document. | ||
__Model.getPrimaryKey(__ __)__ | ||
Return the primary key | ||
__Model.save(__ callback, overwrite __)__ | ||
Save the object in the database. Thinky will call insert or update depending | ||
on whether how the object was created. | ||
overwrite: not implemented yet | ||
__Model.get(__ id or [ids], callback __)__ | ||
Retrieve one or more documents | ||
__Model.filter(__ filterFunction __)__ | ||
Retrieve document based on the filter. | ||
__Model.count(__ __)__ | ||
Return the number of element in the table of your model. | ||
__Model.mapReduce(__ filterFunction __)__ | ||
Not yet implemented | ||
#### Document | ||
__Document.getDocument(__ __)__ | ||
_Internal method?_ | ||
__Document.getModel(__ __)__ | ||
Return the model of the document. | ||
__Document.getSettings(__ __)__ | ||
__Document.define(__ name, method __)__ | ||
__Document.replace(__ newDoc __)__ | ||
_Not implemented yet_ | ||
All method of EventEmitter are available on Document. They do not pollute the document itself. | ||
### Internals | ||
When you create a new object from a model, the object has the following chain of prototypes | ||
object -> DocumentObject -> Document -> model | ||
Run the tests | ||
``` | ||
mocha | ||
``` | ||
### Contribute | ||
You are welcome to do a pull request. | ||
### TODO | ||
- Write the docs | ||
- Get a cake | ||
- Add more complex queries | ||
- Update pool when poolMax/poolMin changes | ||
About | ||
==== | ||
### About | ||
Author: Michel Tu -- orphee@gmail.com -- www.justonepixel.com | ||
License | ||
==== | ||
### License | ||
Copyright (c) 2013 Michel Tu <orphee@gmail.com> | ||
@@ -48,0 +262,0 @@ |
@@ -170,3 +170,3 @@ var thinky = require('../lib/index.js'); | ||
}); | ||
it('should miss the String field', function() { | ||
it('should miss the String field', function() { | ||
Cat = thinky.createModel('Cat', { fieldString: {_type: String }}); | ||
@@ -186,3 +186,6 @@ minou = new Cat({}); | ||
var value = "noString"; | ||
Cat = thinky.createModel('Cat', { fieldString: {_type: String, default: function() { return value }}}); | ||
Cat = thinky.createModel('Cat', { fieldString: { | ||
_type: String, | ||
default: function() { return value } | ||
}}); | ||
minou = new Cat({}); | ||
@@ -194,3 +197,6 @@ should(Object.prototype.toString.call(catou) === '[object Object]'); | ||
var value = "noString"; | ||
Cat = thinky.createModel('Cat', { fieldString: {_type: String, default: function(doc) { return doc.value }}}); | ||
Cat = thinky.createModel('Cat', { fieldString: { | ||
_type: String, | ||
default: function(doc) { return doc.value } | ||
}}); | ||
minou = new Cat({value: value}); | ||
@@ -333,4 +339,16 @@ should(Object.prototype.toString.call(catou) === '[object Object]'); | ||
}); | ||
describe('count', function() { | ||
it('should return the number of document in the table', function(done){ | ||
Cat.filter(function(doc) { return true }, | ||
function(error, result) { | ||
should.not.exists(error); | ||
Cat.count( function(error, resultCount) { | ||
should.not.exists(error); | ||
result.should.have.length(resultCount); | ||
done(); | ||
}); | ||
} | ||
) | ||
}); | ||
}); | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
49643
1063
278
1
+ Addedgeneric-pool@>= 2.0.3
+ Addedgeneric-pool@3.9.0(transitive)