Comparing version 3.0.0 to 3.1.0
@@ -5,2 +5,12 @@ # Change Log | ||
<a name="3.1.0"></a> | ||
# [3.1.0](https://github.com/untool/mixinable/compare/v3.0.0...v3.1.0) (2018-07-23) | ||
### Features | ||
* introduce `callable` aliases ([1884832](https://github.com/untool/mixinable/commit/1884832)), closes [#42](https://github.com/untool/mixinable/issues/42) | ||
<a name="3.0.0"></a> | ||
@@ -7,0 +17,0 @@ # [3.0.0](https://github.com/untool/mixinable/compare/v2.1.0...v3.0.0) (2018-05-30) |
175
index.js
@@ -7,3 +7,4 @@ 'use strict'; | ||
return function mixin() { | ||
return createMixinable(strategies, argsToArray(arguments).map(createMixin)); | ||
var mixins = argsToArray(arguments).map(createMixin); | ||
return createMixinable(strategies, mixins); | ||
}; | ||
@@ -14,5 +15,5 @@ }; | ||
exports.override = function override(functions) { | ||
exports.callable = exports.override = function override(functions) { | ||
var args = argsToArray(arguments).slice(1); | ||
var fn = [].concat(functions).pop(); | ||
var fn = functions.slice().pop(); | ||
if (isFunction(fn)) { | ||
@@ -35,4 +36,4 @@ return fn.apply(null, args); | ||
if (isPromise(result)) { | ||
return result.then(function(value) { | ||
return fn.apply(null, [value].concat(args)); | ||
return result.then(function(result) { | ||
return fn.apply(null, [result].concat(args)); | ||
}); | ||
@@ -44,46 +45,40 @@ } | ||
exports.compose = function compose(_functions) { | ||
exports.compose = function compose(functions) { | ||
var args = argsToArray(arguments).slice(1); | ||
var functions = [].concat(_functions).reverse(); | ||
return exports.pipe.apply(null, [functions].concat(args)); | ||
var fn = exports.pipe.bind(null, functions.slice().reverse()); | ||
return fn.apply(null, args); | ||
}; | ||
exports.async = { | ||
override: function overrideAsync() { | ||
return ensureAsync(exports.override.apply(null, arguments)); | ||
}, | ||
parallel: function parallelAsync() { | ||
return ensureAsync(exports.parallel.apply(null, arguments)); | ||
}, | ||
pipe: function pipeAsync() { | ||
return ensureAsync(exports.pipe.apply(null, arguments)); | ||
}, | ||
compose: function composeAsync() { | ||
return ensureAsync(exports.compose.apply(null, arguments)); | ||
}, | ||
callable: asynchronize(exports.callable), | ||
override: asynchronize(exports.override), | ||
parallel: asynchronize(exports.parallel), | ||
pipe: asynchronize(exports.pipe), | ||
compose: asynchronize(exports.compose), | ||
}; | ||
exports.sync = { | ||
override: function overrideSync() { | ||
return ensureSync(exports.override.apply(null, arguments)); | ||
}, | ||
sequence: function sequenceSync() { | ||
return ensureSync(exports.parallel.apply(null, arguments)); | ||
}, | ||
parallel: function parallelSync() { | ||
return ensureSync(exports.parallel.apply(null, arguments)); | ||
}, | ||
pipe: function pipeSync() { | ||
return ensureSync(exports.pipe.apply(null, arguments)); | ||
}, | ||
compose: function composeSync() { | ||
return ensureSync(exports.compose.apply(null, arguments)); | ||
}, | ||
callable: asynchronize(exports.callable), | ||
override: synchronize(exports.override), | ||
sequence: synchronize(exports.parallel), | ||
parallel: synchronize(exports.parallel), | ||
pipe: synchronize(exports.pipe), | ||
compose: synchronize(exports.compose), | ||
}; | ||
// classy helpers | ||
// core functions | ||
function createMixin(definition) { | ||
if (isFunction(definition)) { | ||
return definition; | ||
} | ||
function Mixin() { | ||
getConstructor(definition).apply(this, arguments); | ||
} | ||
Mixin.prototype = Object.create(getPrototype(definition)); | ||
Mixin.prototype.constructor = Mixin; | ||
return Mixin; | ||
} | ||
function createMixinable(strategies, mixins) { | ||
var constructor = getConstructor(strategies); | ||
var prototype = getPrototype(strategies); | ||
function Mixinable() { | ||
@@ -94,3 +89,3 @@ var args = argsToArray(arguments); | ||
} | ||
constructor.apply(this, args); | ||
getConstructor(strategies).apply(this, args); | ||
bootstrapMixinable( | ||
@@ -103,3 +98,3 @@ this, | ||
} | ||
Mixinable.prototype = Object.create(prototype); | ||
Mixinable.prototype = Object.create(getPrototype(strategies)); | ||
Mixinable.prototype.constructor = Mixinable; | ||
@@ -109,16 +104,2 @@ return Mixinable; | ||
function createMixin(definition) { | ||
var constructor = getConstructor(definition); | ||
var prototype = getPrototype(definition); | ||
if (constructor === definition) { | ||
return definition; | ||
} | ||
function Mixin() { | ||
constructor.apply(this, arguments); | ||
} | ||
Mixin.prototype = Object.create(prototype); | ||
Mixin.prototype.constructor = Mixin; | ||
return Mixin; | ||
} | ||
function bootstrapMixinable(mixinable, mixinstances) { | ||
@@ -129,9 +110,8 @@ mixinstances.forEach(bindMethods); | ||
mixinable, | ||
mixinstances | ||
.filter(function(mixinstance) { | ||
return isFunction(mixinstance[method]); | ||
}) | ||
.map(function(mixinstance) { | ||
return mixinstance[method]; | ||
}) | ||
mixinstances.reduce(function(functions, mixinstance) { | ||
if (isFunction(mixinstance[method])) { | ||
functions.push(mixinstance[method]); | ||
} | ||
return functions; | ||
}, []) | ||
); | ||
@@ -144,2 +124,4 @@ mixinstances.forEach(function(mixinstance) { | ||
// classy helpers | ||
function getConstructor(obj) { | ||
@@ -162,2 +144,27 @@ if (isFunction(obj)) { | ||
function bindMethods(obj) { | ||
getMethodNames(obj).forEach(function(method) { | ||
obj[method] = obj[method].bind(obj); | ||
}); | ||
return obj; | ||
} | ||
function getMethodNames(obj) { | ||
return getPropertyNames(obj).filter(function(prop) { | ||
return prop !== 'constructor' && typeof obj[prop] === 'function'; | ||
}); | ||
} | ||
function getPropertyNames(obj) { | ||
var props = []; | ||
do { | ||
Object.getOwnPropertyNames(obj || {}).forEach(function(prop) { | ||
if (props.indexOf(prop) === -1) { | ||
props.push(prop); | ||
} | ||
}); | ||
} while ((obj = Object.getPrototypeOf(obj || {})) !== Object.prototype); | ||
return props; | ||
} | ||
// utilities | ||
@@ -185,37 +192,17 @@ | ||
function ensureAsync(obj) { | ||
return isPromise(obj) ? obj : Promise.resolve(obj); | ||
function asynchronize(fn) { | ||
return function asyncFn() { | ||
var obj = fn.apply(null, arguments); | ||
return isPromise(obj) ? obj : Promise.resolve(obj); | ||
}; | ||
} | ||
function ensureSync(obj) { | ||
if (isPromise(obj)) { | ||
throw new Error('got promise in sync mode'); | ||
} | ||
return obj; | ||
function synchronize(fn) { | ||
return function syncFn() { | ||
var obj = fn.apply(null, arguments); | ||
if (isPromise(obj)) { | ||
throw new Error('got promise in sync mode'); | ||
} | ||
return obj; | ||
}; | ||
} | ||
function bindMethods(obj) { | ||
getMethodNames(obj).forEach(function(method) { | ||
obj[method] = obj[method].bind(obj); | ||
}); | ||
return obj; | ||
} | ||
function getMethodNames(obj) { | ||
return getPropertyNames(obj).filter(function(prop) { | ||
return prop !== 'constructor' && typeof obj[prop] === 'function'; | ||
}); | ||
} | ||
function getPropertyNames(obj) { | ||
var props = []; | ||
do { | ||
obj && | ||
Object.getOwnPropertyNames(obj).forEach(function(prop) { | ||
if (props.indexOf(prop) === -1) { | ||
props.push(prop); | ||
} | ||
}); | ||
} while (obj && (obj = Object.getPrototypeOf(obj)) !== Object.prototype); | ||
return props; | ||
} |
{ | ||
"name": "mixinable", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "Functional JavaScript Mixin Utility", | ||
@@ -8,8 +8,10 @@ "main": "index.js", | ||
"test": "ava --verbose", | ||
"lint": "eslint --ignore-path .gitignore \"**/*.js\"", | ||
"fmt": "prettier --write --ignore-path .gitignore '**/*.{js,json}'", | ||
"lint": "eslint --ignore-path .gitignore '**/*.js'", | ||
"fmt": "prettier --write --ignore-path .gitignore '**/*.js'", | ||
"release": "standard-version", | ||
"postrelease": "git push --follow-tags; npm publish", | ||
"update": "yarn upgrade-interactive --latest", | ||
"reset": "git clean -dfx && yarn", | ||
"commitmsg": "commitlint -e $GIT_PARAMS", | ||
"precommit": "lint-staged" | ||
"precommit": "lint-staged", | ||
"postrelease": "git push --follow-tags; npm publish" | ||
}, | ||
@@ -30,7 +32,8 @@ "publishConfig": { | ||
"devDependencies": { | ||
"@commitlint/cli": "^6.1.3", | ||
"@commitlint/config-conventional": "^6.1.3", | ||
"@commitlint/cli": "^7.0.0", | ||
"@commitlint/config-conventional": "^7.0.0", | ||
"@commitlint/travis-cli": "^7.0.0", | ||
"ava": "^0.25.0", | ||
"cz-conventional-changelog": "^2.1.0", | ||
"eslint": "^4.19.0", | ||
"eslint": "^5.0.0", | ||
"eslint-config-prettier": "^2.9.0", | ||
@@ -40,8 +43,12 @@ "eslint-plugin-prettier": "^2.6.0", | ||
"lint-staged": "^7.0.0", | ||
"prettier": "^1.11.1", | ||
"prettier": "^1.13.4", | ||
"standard-version": "^4.2.0" | ||
}, | ||
"renovate": { | ||
"extends": ["config:base"], | ||
"ignoreDeps": ["prettier"], | ||
"extends": [ | ||
"config:base" | ||
], | ||
"ignoreDeps": [ | ||
"prettier" | ||
], | ||
"lockFileMaintenance": { | ||
@@ -57,8 +64,13 @@ "enabled": true | ||
"trailingComma": "es5", | ||
"proseWrap": "never", | ||
"singleQuote": true | ||
"singleQuote": true, | ||
"proseWrap": "never" | ||
}, | ||
"eslintConfig": { | ||
"extends": ["eslint:recommended", "prettier"], | ||
"plugins": ["prettier"], | ||
"extends": [ | ||
"eslint:recommended", | ||
"prettier" | ||
], | ||
"plugins": [ | ||
"prettier" | ||
], | ||
"rules": { | ||
@@ -75,6 +87,17 @@ "prettier/prettier": "error" | ||
"lint-staged": { | ||
"*.{js,json}": ["prettier --write", "git add"] | ||
"*.js": [ | ||
"prettier --write", | ||
"git add" | ||
] | ||
}, | ||
"commitlint": { | ||
"extends": ["@commitlint/config-conventional"] | ||
"extends": [ | ||
"@commitlint/config-conventional" | ||
], | ||
"rules": { | ||
"scope-empty": [ | ||
2, | ||
"always" | ||
] | ||
} | ||
}, | ||
@@ -81,0 +104,0 @@ "config": { |
# mixinable | ||
[![travis](https://img.shields.io/travis/untool/mixinable.svg)](https://travis-ci.org/untool/mixinable) [![npm](https://img.shields.io/npm/v/mixinable.svg)](https://www.npmjs.com/package/mixinable) <br/> | ||
[![travis](https://img.shields.io/travis/untool/mixinable/master.svg)](https://travis-ci.org/untool/mixinable) [![npm](https://img.shields.io/npm/v/mixinable.svg)](https://www.npmjs.com/package/mixinable) <br/> | ||
@@ -161,2 +161,4 @@ `mixinable` is a small functional utility library allowing you to use [mixins](https://addyosmani.com/resources/essentialjsdesignpatterns/book/#mixinpatternjavascript) in your code. More specifically, it allows you to create mixin containers that apply mixin method application strategies to mixin method implementations. | ||
`override` is aliased to `callable` in all places, enabling implementers to communicate their intentions more clearly: `override` is often used to provide callable (utility) methods. | ||
#### `define.parallel` | ||
@@ -198,2 +200,4 @@ | ||
If you want to make sure `parallel` never returns a `Promise`, please use it as `define.sync.parallel` - or, better yet, use it as `define.sync.sequence`, for that is technically correct. | ||
#### `define.pipe` | ||
@@ -287,7 +291,7 @@ | ||
All of the strategies described above return a `Promise` if one of their implementations does. If you want them to always return a `Promise` please use `define.async.{override,parallel,pipe,compose}`. | ||
All of the strategies described above return a `Promise` if one of their implementations does. If you want them to always return a `Promise` please use `define.async.{override/callable,parallel,pipe,compose}`. | ||
##### `define.sync` | ||
If you want to make sure one of the strategies never returns a `Promise` please use `define.sync.{override,parallel/sequence,pipe,compose}`. If you do, an `Error` will be thrown if a `Promise` is returned. | ||
If you want to make sure one of the strategies never returns a `Promise` please use `define.sync.{override/callable,parallel/sequence,pipe,compose}`. If you do, an `Error` will be thrown if a `Promise` is returned. | ||
@@ -294,0 +298,0 @@ ### Contributing |
31
test.js
@@ -10,4 +10,5 @@ 'use strict'; | ||
test('exports test', function(t) { | ||
t.plan(14); | ||
t.plan(17); | ||
t.is(typeof mixinable, 'function', 'main export is a function'); | ||
t.is(typeof mixinable.callable, 'function', 'callable is a function'); | ||
t.is(typeof mixinable.override, 'function', 'override is a function'); | ||
@@ -17,2 +18,3 @@ t.is(typeof mixinable.parallel, 'function', 'parallel is a function'); | ||
t.is(typeof mixinable.compose, 'function', 'compose is a function'); | ||
t.is(typeof async.callable, 'function', 'async.callable is a function'); | ||
t.is(typeof async.override, 'function', 'async.override is a function'); | ||
@@ -22,2 +24,3 @@ t.is(typeof async.parallel, 'function', 'async.parallel is a function'); | ||
t.is(typeof async.compose, 'function', 'async.compose is a function'); | ||
t.is(typeof sync.callable, 'function', 'sync.callable is a function'); | ||
t.is(typeof sync.override, 'function', 'sync.override is a function'); | ||
@@ -118,2 +121,28 @@ t.is(typeof sync.parallel, 'function', 'sync.parallel is a function'); | ||
test('callable helper test', function(t) { | ||
t.plan(2); | ||
var arg = 1; | ||
var instance = mixinable({ | ||
foo: mixinable.callable, | ||
})( | ||
{ | ||
foo: function() { | ||
t.fail('1st implementation should not be called'); | ||
}, | ||
}, | ||
{ | ||
foo: function() { | ||
t.fail('2nd implementation should not be called'); | ||
}, | ||
}, | ||
{ | ||
foo: function(_arg) { | ||
t.pass('3rd implementation is being called'); | ||
t.is(_arg, arg, '3rd implementation receives correct arg'); | ||
}, | ||
} | ||
)(); | ||
instance.foo(arg); | ||
}); | ||
test('override helper test', function(t) { | ||
@@ -120,0 +149,0 @@ t.plan(2); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
789
307
205035
12