mixin-a-lot v4.0.11
mixin.a.lot
What is it?
A small aspect-oriented JavaScript mixin API.
Why use it?
- You can advise mixin behavior with your own functions.
- You can advise the mixing process with your own functions.
- You can opt-out of mixin data and behavior.
- It has no dependencies.
Install
$ npm i mixin-a-lot
If you want type definitions
$ tsd link
Examples
Import the module:
import {mix} from 'mixin-a-lot';
Mixing
A mixin is just a plain old JavaScript object. Mix it into your object, function or prototype.
Any mixed-in functions will always be called on the target context.
class Person {
constructor(name) {
this.name = name;
}
};
let named = {
sayName() {
console.log(this.name);
}
};
// mix into a Function
mix(Person, named);
Person.sayName(); // 'Person'
// or mix into its prototype
mix(Person.prototype, named);
new Person('Victor').sayName(); // 'Victor'
// or mix into a random object
let object = {name: 'object'};
mix(object, named);
object.sayName(); // 'object'
Adapters
Take
let loggerMixin = {
errLog: '/logs/app.err',
infLog: '/logs/app.log',
log: function(logEvent) {
let {didError, message} = logEvent;
if (didError) {
fs.writeFile(this.errLog, `${this.logname}:${message}`);
} else {
fs.writeFile(this.infLog, `${this.logname}:${message}`);
}
},
};
as a starting point.
You can write adapters to and from mixin methods.
let logger = {
logname: 'prefix_logger'
};
let prefixMessage = function(error, message) {
let prefix;
if (!error) {
prefix = 'INFO';
} else {
prefix = 'ERROR';
if (!message) {
message = error.message;
}
}
return {didError: !!error, message: `${prefix}:${message}`};
};
mix(logger, loggerMixin, {
adapterTo: {
log: prefixMessage,
},
});
// writes 'prefix_logger:ERROR:error connecting to DB' to /logs/app.err
myLogger.log(new IOError('error connecting to DB'));
// writes 'prefix_logger:INFO:request @ /user/:id from yangmillstheory' to /logs/app.log
myLogger.log(null, `request @ /user/:id from ${user}`);
Adapters are called on the target context before or after the mixin method.
An example of an post-adapter can be seen in the tests. It logs all messages written to disk to the console
as well.
Adapters can be chained, to give an execution flow like
pre adapter -> mixin method -> post adapter
Pre and post mixing routines
Each mixin can specify pre and post mix routines. Each routine is invoked on the context with the mixin as a target.
This is a good place to run validation, finalization, or set default properties.
// shared/mixins/logger.js
let loggers = new WeakSet(); // ES6 only
let loggerMixin = {
// ...
// properties as before
// ...
// new
premix(target) {
if (typeof target.logname !== 'string') {
throw new TypeError(`Expected string logname; got ${target.logname}`);
}
},
// new
postmix(target) {
loggers.add(target);
},
};
premix
and postmix
won't be mixed into the target.
Opting out or overriding
You want some shared data or behavior, but not all of it.
mix(mixee, {
method1() {
// ...
},
method2() {
// ...
},
foo: true,
}, {
omit: ['method1']
});
mixee.method1 // undefined
mixee.method2 // function() { ... }
mixee.foo // true
Or, you want to override some data or behavior.
let named = {
name: 'mixin',
sayName() {
console.log(this.name);
},
};
let mixee = {
name: 'mixee'
};
mix(mixee, named, {omit: ['name']});
mixee.sayName() // 'mixee'
// probably not what you want
mix(mixee, named);
mixee.sayName() // 'mixin'
Tests for all these examples can be found here.
API
mix(target: Object, mixin: IMixin, options?: Object)
Mix own properties from mixin
into target
. options
can be an object literal with
omit
: array of strings which are property names ofmixin
to exclude from mixingadapterTo
: object literal mapping mixin method names to adapters to themadapterFrom
: object literal mapping mixin method names to adapters from them
mixin
can have two special properties
premix
: function called onmixin
before mixing (but aftermix
is called) withtarget
as the argumentpostmix
: function called onmixin
after mixing (but beforemix
returns) withtarget
as the argument
These properties will not be copied into target
.
Development
Development is in snake_case
TypeScript.
Get the source:
$ git clone git@github.com:username/mixin.a.lot.git
Install dependencies:
$ cd mixin.a.lot && sudo npm i
Develop (watch for changes and execute tests):
$ ./node_modules/.bin/gulp dev
To see all the available tasks:
$ ./node_modules/.bin/gulp -T
License
MIT © 2016, Victor Alvarez
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago