-
-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add ES2015 Proxy approach (requires Node.js 6)
- Loading branch information
1 parent
8dc81c1
commit 6c25373
Showing
5 changed files
with
199 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,9 @@ | ||
language: node_js | ||
node_js: | ||
- '6' | ||
- '4' | ||
matrix: | ||
include: | ||
- node_js: '6' | ||
env: NPM_TEST_SCRIPT=test | ||
- node_js: '4' | ||
env: NPM_TEST_SCRIPT=test-node4 | ||
script: | ||
- npm run $NPM_TEST_SCRIPT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
'use strict'; | ||
|
||
const processFn = (fn, opts) => function () { | ||
const that = this; | ||
const P = opts.promiseModule; | ||
const args = new Array(arguments.length); | ||
|
||
for (let i = 0; i < arguments.length; i++) { | ||
args[i] = arguments[i]; | ||
} | ||
|
||
return new P((resolve, reject) => { | ||
args.push((err, result, ...results) => { | ||
if (err) { | ||
reject(err); | ||
} else if (opts.multiArgs) { | ||
resolve([result, ...results]); | ||
} else { | ||
resolve(result); | ||
} | ||
}); | ||
|
||
fn.apply(that, args); | ||
}); | ||
}; | ||
|
||
module.exports = (obj, opts) => { | ||
opts = Object.assign({ | ||
exclude: [/.+Sync$/], | ||
promiseModule: Promise | ||
}, opts); | ||
|
||
const filter = key => { | ||
const match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key); | ||
return opts.include ? opts.include.some(match) : !opts.exclude.some(match); | ||
}; | ||
|
||
const cache = new Map(); | ||
|
||
const handler = { | ||
get: (target, key) => { | ||
let cached = cache.get(key); | ||
|
||
if (!cached) { | ||
const x = target[key]; | ||
|
||
cached = typeof x === 'function' && filter(key) ? processFn(x, opts) : x; | ||
cache.set(key, cached); | ||
} | ||
|
||
return cached; | ||
} | ||
}; | ||
|
||
if (!opts.excludeMain) { | ||
const main = Symbol('main'); | ||
|
||
handler.apply = (target, thisArg, argumentsList) => { | ||
let cached = cache.get(main); | ||
|
||
if (!cached) { | ||
cached = processFn(target, opts); | ||
cache.set(main, cached); | ||
} | ||
|
||
return cached.apply(thisArg, argumentsList); | ||
}; | ||
} | ||
|
||
return new Proxy(obj, handler); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import fs from 'fs'; | ||
import test from 'ava'; | ||
import pinkiePromise from 'pinkie-promise'; | ||
import fn from './proxy'; | ||
|
||
const fixture = cb => setImmediate(() => cb(null, 'unicorn')); | ||
const fixture2 = (x, cb) => setImmediate(() => cb(null, x)); | ||
const fixture3 = cb => setImmediate(() => cb(null, 'unicorn', 'rainbow')); | ||
const fixture4 = cb => setImmediate(() => { | ||
cb(null, 'unicorn'); | ||
return 'rainbow'; | ||
}); | ||
|
||
fixture4.meow = cb => { | ||
setImmediate(() => { | ||
cb(null, 'unicorn'); | ||
}); | ||
}; | ||
|
||
const fixture5 = () => 'rainbow'; | ||
|
||
const fixtureModule = { | ||
method1: fixture, | ||
method2: fixture, | ||
method3: fixture5 | ||
}; | ||
|
||
test('main', async t => { | ||
t.is(typeof fn(fixture)().then, 'function'); | ||
t.is(await fn(fixture)(), 'unicorn'); | ||
}); | ||
|
||
test('pass argument', async t => { | ||
t.is(await fn(fixture2)('rainbow'), 'rainbow'); | ||
}); | ||
|
||
test('custom Promise module', async t => { | ||
t.is(await fn(fixture, {promiseModule: pinkiePromise})(), 'unicorn'); | ||
}); | ||
|
||
test('multiArgs option', async t => { | ||
t.deepEqual(await fn(fixture3, {multiArgs: true})(), ['unicorn', 'rainbow']); | ||
}); | ||
|
||
test('wrap core method', async t => { | ||
t.is(JSON.parse(await fn(fs.readFile)('package.json')).name, 'pify'); | ||
}); | ||
|
||
test('module support', async t => { | ||
t.is(JSON.parse(await fn(fs).readFile('package.json')).name, 'pify'); | ||
}); | ||
|
||
test('module support - doesn\'t transform *Sync methods by default', t => { | ||
t.is(JSON.parse(fn(fs).readFileSync('package.json')).name, 'pify'); | ||
}); | ||
|
||
test('module support - preserves non-function members', t => { | ||
const module = { | ||
method: () => {}, | ||
nonMethod: 3 | ||
}; | ||
|
||
t.deepEqual(Object.keys(module), Object.keys(fn(module))); | ||
}); | ||
|
||
test('module support - transforms only members in options.include', t => { | ||
const pModule = fn(fixtureModule, { | ||
include: ['method1', 'method2'] | ||
}); | ||
|
||
t.is(typeof pModule.method1().then, 'function'); | ||
t.is(typeof pModule.method2().then, 'function'); | ||
t.not(typeof pModule.method3().then, 'function'); | ||
}); | ||
|
||
test('module support - doesn\'t transform members in options.exclude', t => { | ||
const pModule = fn(fixtureModule, { | ||
exclude: ['method3'] | ||
}); | ||
|
||
t.is(typeof pModule.method1().then, 'function'); | ||
t.is(typeof pModule.method2().then, 'function'); | ||
t.not(typeof pModule.method3().then, 'function'); | ||
}); | ||
|
||
test('module support - options.include over options.exclude', t => { | ||
const pModule = fn(fixtureModule, { | ||
include: ['method1', 'method2'], | ||
exclude: ['method2', 'method3'] | ||
}); | ||
|
||
t.is(typeof pModule.method1().then, 'function'); | ||
t.is(typeof pModule.method2().then, 'function'); | ||
t.not(typeof pModule.method3().then, 'function'); | ||
}); | ||
|
||
test('module support — function modules', t => { | ||
const pModule = fn(fixture4); | ||
|
||
t.is(typeof pModule().then, 'function'); | ||
t.is(typeof pModule.meow().then, 'function'); | ||
}); | ||
|
||
test('module support — function modules exclusion', t => { | ||
const pModule = fn(fixture4, { | ||
excludeMain: true | ||
}); | ||
|
||
t.is(typeof pModule.meow().then, 'function'); | ||
t.not(typeof pModule(() => {}).then, 'function'); | ||
}); |