├── .gitignore ├── .jshintrc ├── .travis.yml ├── README.md ├── index.js ├── package.json ├── substitute.js └── test └── substitute.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "asi": true, 3 | "laxcomma": true, 4 | "laxbreak": true, 5 | "globalstrict": true, 6 | "strict": true, 7 | "node": true 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | 4 | node_js: 5 | - "0.10" 6 | - "0.12" 7 | - "iojs" 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![travis][travis-image]][travis-url] 2 | [![dep][dep-image]][dep-url] 3 | [![npm][npm-image]][npm-url] 4 | 5 | [travis-image]: https://img.shields.io/travis/nathan7/level-promise.svg?style=flat 6 | [travis-url]: https://travis-ci.org/nathan7/level-promise 7 | [dep-image]: https://img.shields.io/gemnasium/nathan7/level-promise.svg?style=flat 8 | [dep-url]: https://gemnasium.com/nathan7/level-promise 9 | [npm-image]: https://img.shields.io/npm/v/level-promise.svg?style=flat 10 | [npm-url]: https://npmjs.org/package/level-promise 11 | 12 | # level-promise 13 | 14 | Promised LevelUp. 15 | 16 | ## Installation 17 | 18 | $ npm install level-promise 19 | 20 | ## Warning 21 | 22 | At the application level, promises vs callbacks is yours to choose. 23 | If you use this in a *LevelUp extension* that isn't explicitly about promises, I will find you and I will destroy you. 24 | Play nice with the rest, use callbacks for your extension. 25 | If your extension works with level-manifest, it'll work with this. 26 | 27 | ## Usage 28 | 29 | `LevelPromise(db)` (or `LevelPromise.install(db)`, if that suits your tastes) and you're off! 30 | Every method marked as async by level-manifest will now return a promise when you don't pass it a callback. 31 | It recurses into sublevels. 32 | 33 | ## License 34 | 35 | MIT 36 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Manifest = require('level-manifest') 3 | , substitute = require('./substitute') 4 | , __hop = {}.hasOwnProperty 5 | , extraMethods = ['open', 'close'] 6 | 7 | exports = module.exports = install 8 | exports.install = install 9 | 10 | function install(db) { 11 | _install(db) 12 | return db 13 | } 14 | 15 | function _install(db, manifest) { 16 | manifest = manifest || new Manifest(db) 17 | 18 | var methods = manifest.methods 19 | for (var methodName in methods) if (__hop.call(methods, methodName)) { 20 | var method = methods[methodName] 21 | if (method.type === 'async') 22 | substitute(db, methodName, db[methodName]) 23 | if (method.type === 'object') 24 | _install(db[methodName], method) 25 | } 26 | 27 | extraMethods.forEach(function(methodName) { 28 | if (!manifest.methods[methodName] && typeof db[methodName] == 'function') 29 | substitute(db, methodName, db[methodName]) 30 | }) 31 | 32 | var sublevels = manifest.sublevels || {} 33 | for (var sublevelName in sublevels) if (__hop.call(sublevels, sublevelName)) 34 | _install(db.sublevels[sublevelName], sublevels[sublevelName]) 35 | 36 | if (typeof db.sublevel == 'function') { 37 | var Sublevel = db.sublevel 38 | db.sublevel = function(sublevelName) { 39 | var existing = __hop.call(sublevels, sublevelName) 40 | , sublevel = Sublevel.apply(this, arguments) 41 | if (!existing) 42 | _install(sublevel) 43 | return sublevel 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "level-promise", 3 | "version": "2.1.1", 4 | "description": "Promise'd LevelUp.", 5 | "main": "index.js", 6 | "dependencies": { 7 | "level-manifest": ">=1.1.1 <3.0.0", 8 | "promise": ">=3.2 <8" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/nathan7/level-promise.git" 13 | }, 14 | "keywords": [ 15 | "promise", 16 | "leveldb", 17 | "levelup" 18 | ], 19 | "author": "Nathan Zadoks", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/nathan7/level-promise/issues" 23 | }, 24 | "devDependencies": { 25 | "chai": "^3.0.0", 26 | "mocha": "^2.2.5", 27 | "sinon": "^1.15.3" 28 | }, 29 | "scripts": { 30 | "test": "mocha" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /substitute.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var Promise = require('promise') 3 | 4 | exports = module.exports = substitute 5 | 6 | function substitute(db, methodName, method) { 7 | if (typeof method !== 'function') return 8 | db[methodName] = _wrap(method, Promise.denodeify(method)) 9 | } 10 | 11 | function _wrap(method, methodP) { 12 | return function() { 13 | var lastArgument = arguments[arguments.length - 1] 14 | if (typeof lastArgument == 'function') 15 | method.apply(this, arguments) 16 | else 17 | return methodP.apply(this, arguments) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/substitute.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | /*global describe, beforeEach, it*/ 3 | /*jshint expr: true*/ 4 | var sinon = require('sinon') 5 | , Promise = require('promise') 6 | , substitute = require('../substitute.js') 7 | , expect = require('chai').expect 8 | 9 | describe('substitute', function() { 10 | var DB = {} 11 | , METHOD_NAME = 'METHOD_NAME' 12 | , METHOD = sinon.spy() 13 | , PROMISE_METHOD = sinon.spy() 14 | 15 | Promise.denodeify = sinon.stub() 16 | Promise.denodeify.withArgs(METHOD).returns(PROMISE_METHOD) 17 | Promise.denodeify.withArgs(undefined).throws(Error('no method to denodify')) 18 | 19 | beforeEach(function() { 20 | METHOD.reset() 21 | PROMISE_METHOD.reset() 22 | Promise.denodeify.reset() 23 | }) 24 | 25 | 26 | it('wrapps the named method', function() { 27 | substitute(DB, METHOD_NAME, METHOD) 28 | expect(Promise.denodeify.calledWith(METHOD)).to.be.true 29 | }) 30 | 31 | it('calls a denodeify version if there is no callback', function() { 32 | substitute(DB, METHOD_NAME, METHOD) 33 | DB[METHOD_NAME]() 34 | 35 | expect(PROMISE_METHOD.calledOnce).to.be.true 36 | }) 37 | 38 | it('calls the original version if there is a callback', function() { 39 | substitute(DB, METHOD_NAME, METHOD) 40 | DB[METHOD_NAME](function() {}) 41 | 42 | expect(METHOD.calledOnce).to.be.true 43 | expect(PROMISE_METHOD.called).to.be.false 44 | }) 45 | 46 | it('does not throw if no method is given', function () { 47 | expect(function () { 48 | substitute(DB, METHOD_NAME, undefined) 49 | }).not.to.throw() 50 | }) 51 | 52 | }) 53 | --------------------------------------------------------------------------------