Comparing version 0.1.2 to 2.0.0
194
index.js
@@ -1,184 +0,52 @@ | ||
// Dependencies | ||
var Model = require('./lib/model') | ||
var callsite = require('callsite') | ||
var util = require('./lib/util') | ||
var Metadata = require('./lib/metadata') | ||
var Storage = require('./lib/client') | ||
var redis = require('redis') | ||
'use strict' | ||
// Supported types, minus object and array | ||
// object and arrays have a special check | ||
var supportedTypes = ['string', 'boolean', 'number', 'any'] | ||
const Schema = require('./lib/schema') | ||
const Storage = require('./lib/storage') | ||
var shelf | ||
function Shelf (appName, options) { | ||
// TODO handle more options | ||
// options: { | ||
// auth stuff, | ||
// everything you can use on createClient | ||
// } | ||
options = options || {} | ||
var supportedOptions = { | ||
host: options.host || undefined, | ||
port: options.port || undefined | ||
} | ||
this.appName = appName | ||
this.storage = new Storage(supportedOptions) | ||
// Switch the default extend and make | ||
// this extend method public | ||
// exports.extend = this.extend.bind(this) | ||
} | ||
let storage = Storage(options) | ||
Shelf.prototype.extend = function (options) { | ||
options = options || {} | ||
options.prefix = this.appName | ||
function extend (options) { | ||
options = options || {} | ||
options.prefix = appName | ||
options.storage = storage | ||
// Support 'props' and 'properties' naming to define properties for a model | ||
options.props = options.props || options.properties | ||
// Same thing for model's key/keys | ||
options.keys = options.keys || options.key | ||
// Same thing for model's default values | ||
options.defaultValues = options.def || options.defaultValues || {} | ||
options.props = options.props || options.properties | ||
options.keys = options.keys || options.key | ||
// Sane checks | ||
var nameCheck = (!options.name || typeof options.name !== 'string') | ||
var propertiesCheck = (!options.props || Object.keys(options.props).length <= 0) | ||
var keyCheck = (!options.keys || options.keys.length <= 0) | ||
if (nameCheck) { | ||
var stack = callsite() | ||
throw new Error('You need to define a valid name for the model in ' + stack[1].getFileName()) | ||
} | ||
if (propertiesCheck) { | ||
throw new Error('Model ' + options.name + ': must have at least one property defined') | ||
} | ||
if (keyCheck) { | ||
throw new Error('Model ' + options.name + ': must have at least one key defined') | ||
} | ||
options.strict = typeof options.strict !== 'undefined' ? options.strict : true | ||
// check if all properties types are supported | ||
for (var k in options.props) { | ||
if (options.props.hasOwnProperty(k)) { | ||
var t | ||
options.props[k] = options.props[k].toLowerCase() | ||
// If its an object or an array | ||
if (util.isObject(options.props[k]) || util.isArray(options.props[k])) { | ||
t = 1 | ||
} else { | ||
t = supportedTypes.indexOf(options.props[k].toLowerCase()) | ||
} | ||
var type = options.props[k] | ||
if (typeof type !== 'string') { | ||
type = Object.prototype.toString.call(type) | ||
} | ||
// Check if type is supported | ||
if (t < 0) { | ||
throw new Error('Model ' + options.name + ': The type of ' + k + ' property isn\'t supported -> ' + type) | ||
} | ||
if (!options.prefix || typeof options.prefix !== 'string') { | ||
throw new Error('You need to define a valid prefix for the app') | ||
} | ||
} | ||
// Keys check | ||
options.keys.forEach(function (key) { | ||
// keys can only be root properties | ||
if (key.indexOf('.') > 0) { | ||
throw new Error('Model ' + options.name + ': The model\'s keys need to be root properties') | ||
if (!options.name || typeof options.name !== 'string') { | ||
throw new Error('You need to define a valid name for the model') | ||
} | ||
if (!options.props[key]) { | ||
throw new Error('Model ' + options.name + ': The model\'s keys need to be a defined property in Shelf.extend') | ||
if (!options.props.isJoi || options.props._type !== 'object') { | ||
throw new Error('Model ' + options.name + ': must have props defined as a Joi Object Schema') | ||
} | ||
}) | ||
options.storage = this.storage | ||
return new Model(options) | ||
} | ||
Shelf.prototype.loadMetadata = function () { | ||
var self = this | ||
var selectDatabase = function (meta) { | ||
self.metadata = meta | ||
// Select the database the app uses | ||
self.storage.select(meta.dbIndex, function () { | ||
// Set ready to true and process the offline queue | ||
self.storage.__client__.on_ready() | ||
}) | ||
// Push everything from mountQueue to the | ||
// redis module internal offline_queue | ||
while (self.storage.mountQueue.length > 0) { | ||
self.storage.__client__.offline_queue.push(self.storage.mountQueue.shift()) | ||
if (options.props._inner.children.length === 0) { | ||
throw new Error('Model ' + options.name + ': must have at least one property defined') | ||
} | ||
if (!options.keys || options.keys.length <= 0) { | ||
throw new Error('Model ' + options.name + ': must have at least one key defined') | ||
} | ||
// Fire ready event again and | ||
// iterate over offline_queue | ||
// to send all commands that were | ||
// queued | ||
self.storage.__client__.on_ready() | ||
self.storage.mountQueue = null | ||
} | ||
this.storage.once('ready', function () { | ||
// node_redis module secret flags. shhhh! | ||
self.storage.__client__.ready = false | ||
self.storage.__client__.send_anyway = false | ||
var connectionClient = redis.createClient() | ||
// Select database 0 and then get the metadata we need | ||
connectionClient.get(self.appName + ':metadata', function (err, metadata) { | ||
if (err) { | ||
throw err | ||
options.keys.forEach((key) => { | ||
if (key.indexOf('.') > 0) { | ||
throw new Error('Model ' + options.name + ': The model\'s keys need to be root properties') | ||
} | ||
if (!metadata) { | ||
metadata = new Metadata(self.storage) | ||
metadata.build(selectDatabase) | ||
if (!options.props._inner.children.find((child) => child.key === key)) { | ||
throw new Error('Model ' + options.name + ': The model\'s key ' + key + ' need to be a defined property in Shelf.extend') | ||
} | ||
try { | ||
metadata = JSON.parse(metadata) | ||
} catch (e) { | ||
throw e | ||
} | ||
return selectDatabase(metadata) | ||
}) | ||
}) | ||
} | ||
function appRegister (appName, options) { | ||
shelf = new Shelf(appName, options) | ||
// TODO review the name of this option and | ||
// which state is deafult | ||
options.deploy = options.deploy || 'dedicated' | ||
if (options.deploy === 'dedicated') { | ||
shelf.storage.mountQueue = null | ||
} else { | ||
shelf.loadMetadata() | ||
return Schema(options) | ||
} | ||
return shelf | ||
return { | ||
extend | ||
} | ||
} | ||
// u mad homie? | ||
function defaultExtend () { | ||
throw new Error('First you need to mount the shelf on the wall. Otherwise how would you put books on the shelf?') | ||
} | ||
// Public API | ||
exports.mount = appRegister | ||
exports.extend = defaultExtend | ||
// Export api | ||
module.exports = exports | ||
module.exports = Shelf |
@@ -1,13 +0,41 @@ | ||
// Dependecies | ||
var nodeUtil = require('util') | ||
var extend = require('xtend') | ||
'use strict' | ||
var util = extend({}, nodeUtil) | ||
const _ = require('lodash') | ||
util.isObject = function (item) { | ||
return Object.prototype.toString.call(item) === '[object Object]' | ||
function Util () { | ||
function keyFn (prefix, name, keys) { | ||
if (!prefix || typeof prefix !== 'string') { | ||
throw new Error('Prefix must be a string') | ||
} | ||
if (!name || typeof name !== 'string') { | ||
throw new Error('Name must be a string') | ||
} | ||
if (!keys || !_.isArray(keys)) { | ||
throw new Error('Keys must be an array') | ||
} | ||
if (keys.length === 0) { | ||
throw new Error('Keys must have values') | ||
} | ||
return function (obj) { | ||
if (!obj || !_.isObject(obj)) { | ||
throw new Error('Invalid object to extract key') | ||
} | ||
let finalKey = prefix + ':' + name + ':' | ||
keys.forEach((key) => { | ||
if (obj[key]) { | ||
finalKey += obj[key] + ':' | ||
} else { | ||
throw new Error('Missing the key ' + key + ' from the keys object') | ||
} | ||
}) | ||
return finalKey.slice(0, -1) | ||
} | ||
} | ||
return { | ||
keyFn | ||
} | ||
} | ||
util.generator = require('./util/generator') | ||
module.exports = util | ||
module.exports = Util() |
{ | ||
"name": "shelf", | ||
"version": "0.1.2", | ||
"version": "2.0.0", | ||
"description": "Redis Object Document Mapper (ODM)", | ||
@@ -8,2 +8,3 @@ "main": "index.js", | ||
"test": "./node_modules/lab/bin/lab --verbose", | ||
"test-cov": "./node_modules/lab/bin/lab --verbose -c", | ||
"lint": "standard --verbose | snazzy" | ||
@@ -13,3 +14,4 @@ }, | ||
"pre-commit": [ | ||
"lint" | ||
"lint", | ||
"test" | ||
], | ||
@@ -33,2 +35,6 @@ "keywords": [ | ||
"email": "j.goncalo.antunes@gmail.com" | ||
}, | ||
{ | ||
"name": "Pedro Carvalho", | ||
"email": "pedromatosdecarvalho@gmail.com" | ||
} | ||
@@ -38,16 +44,14 @@ ], | ||
"devDependencies": { | ||
"code": "^2.0.1", | ||
"lab": "^7.3.0", | ||
"mocha": "^2.2.5", | ||
"pre-commit": "^1.1.1", | ||
"code": "^2.1.0", | ||
"lab": "^8.2.0", | ||
"pre-commit": "^1.1.2", | ||
"proxyquire": "^1.7.4", | ||
"snazzy": "^2.0.1", | ||
"standard": "^5.2.2" | ||
"standard": "^6.0.4" | ||
}, | ||
"dependencies": { | ||
"callsite": "^1.0.0", | ||
"hiredis": "^0.4.1", | ||
"lodash": "^3.10.1", | ||
"redis": "^1.0.0", | ||
"xtend": "^4.0.0" | ||
"joi": "^7.2.3", | ||
"lodash": "^4.3.0", | ||
"redis": "^2.4.2" | ||
} | ||
} |
@@ -1,2 +0,50 @@ | ||
# Shelf [WIP] | ||
Shelf is a Object Document Mapper (ODM) for node.js who uses Redis as its store. | ||
![shelf Logo](https://avatars1.githubusercontent.com/u/14891842?v=3&s=200) | ||
Object Document Mapper (ODM) for Node.js who uses Redis as its store. | ||
[![Build Status](https://travis-ci.org/shelf-js/shelf.svg?branch=master)](https://travis-ci.org/shelf-js/shelf) | ||
[![npm version](https://img.shields.io/npm/v/shelf.svg)](https://www.npmjs.com/package/shelf) | ||
# Introduction | ||
If you need to store complex objects do Redis, but don't want to go through the trouble of translating your objects to a key, value store, then Shelf is for you. | ||
Shelf gives you CRUD operation on your objects over Redis, so you can create complex schemas and validate models. | ||
Shelf uses [joi](https://github.com/hapijs/joi) for schema validation. | ||
> If you're using 0.10 or 0.12 node version, please use `shelf@1`. You can also look at the code at the `1.x.x` branch. | ||
# Example | ||
```javascript | ||
const Shelf = require('shelf') | ||
const Joi = require('joi') | ||
const Storage = Shelf('MyApp', { | ||
port: 6379, | ||
host: '127.0.0.1' | ||
}) | ||
const MyModel = Storage.extend({ | ||
name: 'MySchema', | ||
props: Joi.object().keys({ | ||
prop1: Joi.string(), | ||
prop2: Joi.string() | ||
}), | ||
keys: ['prop1'] | ||
}) | ||
let myModel = MyModel({prop1: 'Hello'}) | ||
myModel.save() | ||
``` | ||
You can (and should) move your DB connection (Shelf) and Schema (returned by the extend method) to different files to get better readability. | ||
Check the examples for further information. | ||
# Contributing | ||
We use [standard js](https://github.com/feross/standard). | ||
In order to run the tests you should have an Redis instance running locally. |
@@ -1,13 +0,158 @@ | ||
var Lab = require('lab') | ||
var Code = require('code') | ||
var lab = exports.lab = Lab.script() | ||
var TestSchema = require('./testSchema') | ||
'use strict' | ||
lab.experiment('Model creation', function () { | ||
lab.test('Default values', function (done) { | ||
var test = new TestSchema({prop1: 'Hello'}) | ||
Code.expect(test.prop1).to.equal('Hello') | ||
Code.expect(test.prop2).to.equal('World') | ||
const Lab = require('lab') | ||
const Code = require('code') | ||
const lab = exports.lab = Lab.script() | ||
const Shelf = require('../index') | ||
const Joi = require('joi') | ||
lab.experiment('Shelf Init', () => { | ||
lab.test('Empty prefix', (done) => { | ||
let shelfInstance = Shelf(null, { | ||
host: '127.0.0.1', | ||
port: 6379 | ||
}) | ||
Code.expect( | ||
() => shelfInstance.extend() | ||
).to.throw(Error, 'You need to define a valid prefix for the app') | ||
done() | ||
}) | ||
lab.test('Invalid prefix', (done) => { | ||
let shelfInstance = Shelf(123, { | ||
host: '127.0.0.1', | ||
port: 6379 | ||
}) | ||
Code.expect( | ||
() => shelfInstance.extend() | ||
).to.throw(Error, 'You need to define a valid prefix for the app') | ||
done() | ||
}) | ||
lab.test('Empty name', (done) => { | ||
let shelfInstance = Shelf('Test', { | ||
host: '127.0.0.1', | ||
port: 6379 | ||
}) | ||
Code.expect( | ||
() => shelfInstance.extend({name: null}) | ||
).to.throw(Error, 'You need to define a valid name for the model') | ||
done() | ||
}) | ||
lab.test('Invalid name', (done) => { | ||
let shelfInstance = Shelf('Test', { | ||
host: '127.0.0.1', | ||
port: 6379 | ||
}) | ||
Code.expect( | ||
() => shelfInstance.extend({name: 123}) | ||
).to.throw(Error, 'You need to define a valid name for the model') | ||
done() | ||
}) | ||
lab.test('Invalid Props', (done) => { | ||
let shelfInstance = Shelf('Test', { | ||
host: '127.0.0.1', | ||
port: 6379 | ||
}) | ||
Code.expect( | ||
() => shelfInstance.extend({name: '123', props: {isJoi: false}}) | ||
).to.throw(Error, 'Model 123: must have props defined as a Joi Object Schema') | ||
done() | ||
}) | ||
lab.test('Invalid Props', (done) => { | ||
let shelfInstance = Shelf('Test', { | ||
host: '127.0.0.1', | ||
port: 6379 | ||
}) | ||
Code.expect( | ||
() => shelfInstance.extend({name: '123', props: {isJoi: true, _type: 'array'}}) | ||
).to.throw(Error, 'Model 123: must have props defined as a Joi Object Schema') | ||
done() | ||
}) | ||
lab.test('Invalid Props', (done) => { | ||
let shelfInstance = Shelf('Test', { | ||
host: '127.0.0.1', | ||
port: 6379 | ||
}) | ||
Code.expect( | ||
() => shelfInstance.extend({name: '123', props: Joi.object().keys({})}) | ||
).to.throw(Error, 'Model 123: must have at least one property defined') | ||
done() | ||
}) | ||
lab.test('Invalid Props', (done) => { | ||
let shelfInstance = Shelf('Test', { | ||
host: '127.0.0.1', | ||
port: 6379 | ||
}) | ||
Code.expect( | ||
() => shelfInstance.extend({name: '123', props: Joi.object().keys({prop1: Joi.string()})}) | ||
).to.throw(Error, 'Model 123: must have at least one key defined') | ||
done() | ||
}) | ||
lab.test('Invalid Props', (done) => { | ||
let shelfInstance = Shelf('Test', { | ||
host: '127.0.0.1', | ||
port: 6379 | ||
}) | ||
Code.expect( | ||
() => shelfInstance.extend({ | ||
name: '123', | ||
props: Joi.object().keys({prop1: Joi.string()}), | ||
keys: ['prop1.name2'] | ||
}) | ||
).to.throw(Error, 'Model 123: The model\'s keys need to be root properties') | ||
done() | ||
}) | ||
lab.test('Invalid Props', (done) => { | ||
let shelfInstance = Shelf('Test', { | ||
host: '127.0.0.1', | ||
port: 6379 | ||
}) | ||
Code.expect( | ||
() => shelfInstance.extend({ | ||
name: '123', | ||
props: Joi.object().keys({prop1: Joi.string()}), | ||
keys: ['prop2'] | ||
}) | ||
).to.throw(Error, 'Model 123: The model\'s key prop2 need to be a defined property in Shelf.extend') | ||
done() | ||
}) | ||
lab.test('OK', (done) => { | ||
let shelfInstance = Shelf('Test') | ||
shelfInstance.extend({ | ||
name: '123', | ||
props: Joi.object().keys({prop1: Joi.string()}), | ||
keys: ['prop1'] | ||
}) | ||
done() | ||
}) | ||
}) |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
20340
3
18
604
1
51
1
+ Addedjoi@^7.2.3
+ Addeddouble-ended-queue@2.1.0-0(transitive)
+ Addedhoek@3.0.44.3.1(transitive)
+ Addedisemail@2.2.1(transitive)
+ Addedjoi@7.3.0(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedmoment@2.30.1(transitive)
+ Addedredis@2.8.0(transitive)
+ Addedredis-commands@1.7.0(transitive)
+ Addedredis-parser@2.6.0(transitive)
+ Addedtopo@2.1.1(transitive)
- Removedcallsite@^1.0.0
- Removedhiredis@^0.4.1
- Removedxtend@^4.0.0
- Removedbindings@1.5.0(transitive)
- Removedcallsite@1.0.0(transitive)
- Removedfile-uri-to-path@1.0.0(transitive)
- Removedhiredis@0.4.1(transitive)
- Removedlodash@3.10.1(transitive)
- Removednan@2.22.0(transitive)
- Removedredis@1.0.0(transitive)
- Removedxtend@4.0.2(transitive)
Updatedlodash@^4.3.0
Updatedredis@^2.4.2