@dojo/compose
Advanced tools
Comparing version 2.0.0-beta.21 to 2.0.0-beta.22
@@ -0,0 +0,0 @@ export interface AdvisingFunction extends Function { |
@@ -0,0 +0,0 @@ (function (factory) { |
@@ -0,0 +0,0 @@ import { EventCancelableObject } from '@dojo/interfaces/core'; |
@@ -0,0 +0,0 @@ (function (factory) { |
@@ -0,0 +0,0 @@ import { Destroyable } from '@dojo/interfaces/bases'; |
@@ -0,0 +0,0 @@ (function (factory) { |
@@ -0,0 +0,0 @@ import { EventTargettedObject } from '@dojo/interfaces/core'; |
@@ -20,2 +20,6 @@ (function (factory) { | ||
/** | ||
* A map that contains event type names that contain wildcards and their RegExp mapping | ||
*/ | ||
var regexMap = new Map_1.default(); | ||
/** | ||
* A guard which determines if the value is `Actionable` | ||
@@ -51,2 +55,26 @@ * | ||
/** | ||
* Internal function to check if a target string matches a glob string that contains wildcards. | ||
* Note: Due to limited use cases in event type name, currently only `*` that matches 0 or more characters is supported. | ||
* | ||
* @param globString The glob string that contains wildcards pattern | ||
* @param targetString The string under test | ||
* @return boolean match result | ||
*/ | ||
function isGlobMatch(globString, targetString) { | ||
if (globString.indexOf('*') !== -1) { | ||
var regex = void 0; | ||
if (regexMap.has(globString)) { | ||
regex = regexMap.get(globString); | ||
} | ||
else { | ||
regex = new RegExp("^" + globString.replace(/\*/g, '.*') + "$"); | ||
regexMap.set(globString, regex); | ||
} | ||
return regex.test(targetString); | ||
} | ||
else { | ||
return globString === targetString; | ||
} | ||
} | ||
/** | ||
* Creates a new instance of an `Evented` | ||
@@ -59,6 +87,8 @@ */ | ||
emit: function (event) { | ||
var method = listenersMap.get(this).get(event.type); | ||
if (method) { | ||
method.call(this, event); | ||
} | ||
var _this = this; | ||
listenersMap.get(this).forEach(function (method, type) { | ||
if (isGlobMatch(type, event.type)) { | ||
method.call(_this, event); | ||
} | ||
}); | ||
}, | ||
@@ -65,0 +95,0 @@ on: function () { |
@@ -0,0 +0,0 @@ import { State, StatefulMixin, StatefulOptions } from '@dojo/interfaces/bases'; |
@@ -0,0 +0,0 @@ (function (factory) { |
139
compose.d.ts
@@ -54,3 +54,2 @@ import { BeforeAdvice, AfterAdvice, AroundAdvice } from './aspect'; | ||
* | ||
* @deprecated | ||
* @param extension The object literal, class or factory to extend | ||
@@ -72,3 +71,2 @@ * @template T The original type of the factory | ||
* | ||
* @deprecated | ||
* @param base The base compose factory to extend | ||
@@ -122,2 +120,23 @@ * @param extension The object literal, class or factory that is the extension | ||
} | ||
export interface ComposeFactory<T, O extends Options> extends ComposeMixinable<T, O> { | ||
/** | ||
* Add an initialization function to the factory's chain | ||
* | ||
* @param name An optional name for the function for debugging purposes | ||
* @param init The initialization function | ||
*/ | ||
init<P extends O>(name: string, init: ComposeInitializationFunction<T, P>): ComposeFactory<T, P>; | ||
init<P extends O>(init: ComposeInitializationFunction<T, P>): ComposeFactory<T, P>; | ||
} | ||
export interface Compose { | ||
/** | ||
* Add the supplied initialization function to the factory's chain | ||
* | ||
* @param name Optional name for the init function | ||
* @param base The base compose factory | ||
* @param init The initialization function | ||
*/ | ||
init<T, O, P extends O>(name: string, base: ComposeFactory<T, O>, init: ComposeInitializationFunction<T, P>): ComposeFactory<T, P>; | ||
init<T, O, P extends O>(base: ComposeFactory<T, O>, init: ComposeInitializationFunction<T, P>): ComposeFactory<T, P>; | ||
} | ||
export interface OverlayFunction<T> { | ||
@@ -218,6 +237,9 @@ /** | ||
* | ||
* @param mixin An object literal that describes what to mixin | ||
* @param mixin An object literal that describes what to mixin, or a ComposeCreatedMixin that has "recorded" | ||
* a series of operations to be performed to the base factory. | ||
*/ | ||
mixin<U, P, S>(mixin: ComposeCreatedMixin<T, U, P, S>): ComposeFactory<T & U, O & P> & S; | ||
mixin<U, P>(mixin: ComposeMixinable<U, P>): ComposeFactory<T & U, O & P>; | ||
mixin<U, P>(mixin: ComposeMixinDescriptor<T, O, U, P>): ComposeFactory<T & U, O & P>; | ||
mixin<U, P, S>(mixin: ComposeCreatedMixin<T, U, P, S>): ComposeFactory<T & U, O & P> & S; | ||
} | ||
@@ -229,6 +251,8 @@ export interface Compose { | ||
* @param base The base factory that is the target of the mixin | ||
* @param mixin An object literal that describes what to mixin | ||
* @param mixin An object literal that describes what to mixin, or a ComposeCreatedMixin that has "recorded" | ||
* a series of operations to be performed to the base factory. | ||
*/ | ||
mixin<T, O, U, P>(base: ComposeFactory<T, O>, mixin: ComposeMixinable<U, P>): ComposeFactory<T & U, O & P>; | ||
mixin<T, O, U, P>(base: ComposeFactory<T, O>, mixin: ComposeMixinDescriptor<T, O, U, P>): ComposeFactory<T & U, O & P>; | ||
mixin<T, O, U, P, S>(base: ComposeFactory<T, O>, mixin: ComposeCreatedMixin<T, U, P, S>): ComposeFactory<T & U, O & P> & S; | ||
} | ||
@@ -314,2 +338,109 @@ export interface ComposeFactory<T, O extends Options> extends ComposeMixinable<T, O> { | ||
} | ||
/** | ||
* Provides essentially the same methods as a compose factory, but is not a callable function. | ||
* The arguments passed to each method modify the type of the generics, and when mixed into a ComposeFactory | ||
* the resulting factory is the same as if these calls were made directly on the factory. | ||
* The T and O generics behave similarly to in a compose factory, the S generic keeps track of any static modifications | ||
* that have been made to the mixin, and the Target type indicates what type this mixin needs to be mixed into. The | ||
* instance type in any initializer functions passed to init will be typed as Target & T if not specified. | ||
*/ | ||
export interface ComposeCreatedMixin<Target, T, O, S> { | ||
/** | ||
* Extend a compose factory prototype with the supplied object literal, class, or | ||
* factory. | ||
* | ||
* @param extension The object literal, class or factory that is the extension | ||
* @template T The base type of the factory | ||
* @template U The type of the extension | ||
* @template O The type of the base factory options | ||
* @template P The type of the extension factory options | ||
*/ | ||
extend<U>(extension: U | GenericClass<U>): ComposeCreatedMixin<Target, T & U, O, S>; | ||
extend<U>(className: string, extension: U | GenericClass<U>): ComposeCreatedMixin<Target, T & U, O, S>; | ||
extend<U, P>(extension: ComposeFactory<U, P>): ComposeCreatedMixin<Target, T & U, O & P, S>; | ||
extend<U, P>(className: string, extension: ComposeFactory<U, P>): ComposeCreatedMixin<Target, T & U, O & P, S>; | ||
/** | ||
* Override certain properties on the existing factory, returning a new factory. If the properties | ||
* are not present in the existing factory, override will throw. | ||
* | ||
* @param properties The properties to override | ||
*/ | ||
override(properties: any): this; | ||
override(className: string, properties: any): this; | ||
/** | ||
* Add static properties to a factory | ||
* | ||
* @param staticProperties An object literal that contains methods and properties that should be "static" (e.g. | ||
* added to the factory, instead of the factory's prototype) | ||
*/ | ||
static<Z>(staticProperties: Z): ComposeCreatedMixin<Target, T, O, S & Z>; | ||
/** | ||
* A static method that takes a compose factory and applies an overlay function to the factory, | ||
* returning a new compose factory with a mutated prototype. | ||
* | ||
* @param overlayFunction The function which receives the base factory's prototype | ||
* @template T The type of the factory's prototype | ||
* @template O The options for the factory's creation | ||
*/ | ||
overlay(overlayFunction: OverlayFunction<T>): this; | ||
/** | ||
* Mixin additional mixins, initialization logic, and aspect advice into the factory | ||
* | ||
* @param mixin An object literal that describes what to mixin, or a ComposeCreatedMixin that has "recorded" | ||
* a series of operations to be performed to the base factory. | ||
*/ | ||
mixin<U, P>(mixin: ComposeMixinable<U, P>): ComposeCreatedMixin<Target, T & U, O & P, S>; | ||
mixin<U, P>(mixin: ComposeMixinDescriptor<T, O, U, P>): ComposeCreatedMixin<Target, T & U, O & P, S>; | ||
mixin<U, P, Z>(mixin: ComposeCreatedMixin<T, U, P, Z>): ComposeCreatedMixin<Target, T & U, O & P, S & Z>; | ||
/** | ||
* Extract a method from another Class or Factory and add it to the returned factory | ||
* | ||
* @param base The base Class or Factory | ||
* @param method The name of the method to extract | ||
*/ | ||
from(base: GenericClass<any> | ComposeFactory<any, any>, method: string): this; | ||
/** | ||
* Apply advice *before* the named method (join-point) | ||
* | ||
* @param method The method to apply the advice to | ||
* @param advice The advice to be applied | ||
*/ | ||
before(method: string, advice: BeforeAdvice): this; | ||
/** | ||
* Apply advice *after* the named method (join-point) | ||
* | ||
* @param method The method to apply the advice to | ||
* @param advice The advice to be applied | ||
*/ | ||
after<P>(method: string, advice: AfterAdvice<P>): this; | ||
/** | ||
* Apply advice *around* the named method (join-point) | ||
* | ||
* @param method The method to apply the advice to | ||
* @param advice The advice to be applied | ||
*/ | ||
around<P>(method: string, advice: AroundAdvice<P>): this; | ||
/** | ||
* Provide an object literal which can contain a map of advice to apply | ||
* | ||
* @param advice An object literal which contains the maps of advice to apply | ||
*/ | ||
aspect(advice: AspectAdvice): this; | ||
/** | ||
* Add the supplied initialization function to the factory's chain | ||
* | ||
* @param name Optional name for the init function | ||
* @param init The initialization function | ||
*/ | ||
init<P extends O>(name: string, init: ComposeInitializationFunction<Target & T, P>): ComposeCreatedMixin<Target, T, P, S>; | ||
init<P extends O>(init: ComposeInitializationFunction<Target & T, P>): ComposeCreatedMixin<Target, T, P, S>; | ||
} | ||
export interface Compose { | ||
/** | ||
* Creates a ComposeCreatedMixin. If provided, the target is used to determine the initial types for the mixin target, | ||
* base, and in the case that the target is a ComposeFactory, the options. | ||
* @param target Optional parameter that allows the Target, T, and sometimes O types to be inferred | ||
*/ | ||
createMixin<Target, O, S>(target?: GenericClass<Target> | Target | ComposeFactory<Target, O>): ComposeCreatedMixin<Target, Target, O, S>; | ||
} | ||
export interface ComposeFactory<T, O extends Options> extends ComposeMixinable<T, O> { | ||
@@ -316,0 +447,0 @@ /** |
229
compose.js
@@ -7,7 +7,6 @@ (function (factory) { | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "@dojo/core/instrument", "@dojo/core/lang", "@dojo/shim/array", "@dojo/shim/WeakMap", "@dojo/shim/Symbol", "./aspect"], factory); | ||
define(["require", "exports", "@dojo/core/lang", "@dojo/shim/array", "@dojo/shim/WeakMap", "@dojo/shim/Symbol", "./aspect"], factory); | ||
} | ||
})(function (require, exports) { | ||
"use strict"; | ||
var instrument_1 = require("@dojo/core/instrument"); | ||
var lang_1 = require("@dojo/core/lang"); | ||
@@ -136,5 +135,5 @@ var array_1 = require("@dojo/shim/array"); | ||
function getInitFunctionNames(factory) { | ||
var initFns = privateFactoryData.get(factory).initFns; | ||
if (initFns) { | ||
return initFns.map(function (fn) { return fn.name; }); | ||
var factoryData = privateFactoryData.get(factory); | ||
if (factoryData) { | ||
return factoryData.initFns.map(function (fn) { return fn.name; }); | ||
} | ||
@@ -147,3 +146,2 @@ } | ||
* | ||
* @deprecated | ||
*/ | ||
@@ -171,2 +169,3 @@ var doExtend = rebase(extend); | ||
var doStatic = rebase(_static); | ||
var doInit = rebase(init); | ||
/** | ||
@@ -187,3 +186,2 @@ * Take a mixin and return a factory descriptor for the mixin | ||
} | ||
; | ||
/** | ||
@@ -207,3 +205,4 @@ * Generate a factory descriptor for a class | ||
factoryDescriptor: doFactoryDescriptor, | ||
static: doStatic | ||
static: doStatic, | ||
init: doInit | ||
}; | ||
@@ -327,3 +326,3 @@ /** | ||
assignFactoryName(factory, className); | ||
/* freeze the factory, so it cannot be accidently modified */ | ||
/* freeze the factory, so it cannot be accidentally modified */ | ||
Object.freeze(factory); | ||
@@ -343,3 +342,2 @@ return factory; | ||
function extend(base, className, extension) { | ||
instrument_1.deprecated({ message: 'This function will be removed, use "override" instead.', name: 'extend' }); | ||
if (typeof className !== 'string') { | ||
@@ -379,2 +377,24 @@ extension = className; | ||
/** | ||
* The internal implementation of adding an initialization functoin | ||
* | ||
* @param base The base compose factory | ||
* @param className The optional name for the init function | ||
* @param initFunction The initialization function | ||
*/ | ||
function init(base, className, initFunction) { | ||
if (typeof className !== 'string') { | ||
initFunction = className; | ||
className = undefined; | ||
} | ||
/* Label the initFunction */ | ||
if (initFunction && className) { | ||
assignFunctionName(initFunction, "init" + className); | ||
} | ||
return createFactory({ | ||
className: className, | ||
factories: [base], | ||
initFunction: initFunction | ||
}); | ||
} | ||
/** | ||
* Internal implementation of the overlay functionality, to allow a function to modify a | ||
@@ -445,37 +465,49 @@ * compose factory prototype | ||
/** | ||
* A custom type guard that determines if a value is a ComposeCreatedMixin | ||
*/ | ||
function isCreatedMixin(value) { | ||
return value instanceof Mixin; | ||
} | ||
/** | ||
* The internal implementation of mixin in values into a compose factory | ||
* | ||
* @param base The base compose factory that is the target for being mixed in | ||
* @param name Optional name parameter | ||
* @param toMixin The value to be mixed in | ||
*/ | ||
function mixin(base, toMixin) { | ||
/* ensure we are dealing with a mixinDescriptor */ | ||
var mixinDescriptor = isComposeMixinable(toMixin) ? toMixin.factoryDescriptor() : toMixin; | ||
/* destructure out most of the factory creation options */ | ||
var mixin = mixinDescriptor.mixin, initFunction = mixinDescriptor.initialize, aspectAdvice = mixinDescriptor.aspectAdvice, className = mixinDescriptor.className; | ||
/* we will at least be using the base factory to create the new one */ | ||
var factories = [base]; | ||
var proto; | ||
/* if mixin is a compose factory, we will pass it as a factory used to create the new factory */ | ||
if (isComposeFactory(mixin)) { | ||
factories.push(mixin); | ||
if (isCreatedMixin(toMixin)) { | ||
return execute(base, toMixin); | ||
} | ||
else { | ||
/* of which, we can have a constructor function/class, or an object literal (or undefined) */ | ||
proto = typeof mixin === 'function' ? mixin.prototype : mixin; | ||
/* ensure we are dealing with a mixinDescriptor */ | ||
var mixinDescriptor = isComposeMixinable(toMixin) ? toMixin.factoryDescriptor() : toMixin; | ||
/* destructure out most of the factory creation options */ | ||
var mixin_1 = mixinDescriptor.mixin, initFunction = mixinDescriptor.initialize, aspectAdvice = mixinDescriptor.aspectAdvice, className = mixinDescriptor.className; | ||
/* we will at least be using the base factory to create the new one */ | ||
var factories = [base]; | ||
var proto = void 0; | ||
/* if mixin is a compose factory, we will pass it as a factory used to create the new factory */ | ||
if (isComposeFactory(mixin_1)) { | ||
factories.push(mixin_1); | ||
} | ||
else { | ||
/* of which, we can have a constructor function/class, or an object literal (or undefined) */ | ||
proto = typeof mixin_1 === 'function' ? mixin_1.prototype : mixin_1; | ||
} | ||
/* convert the advice, if any, to the format used by createFactory */ | ||
var advice = aspectAdviceToAdviceMap(aspectAdvice); | ||
/* label the initFn */ | ||
if (initFunction) { | ||
assignFunctionName(initFunction, "mixin" + (className || (isComposeFactory(mixin_1) && mixin_1.name) || base.name)); | ||
} | ||
/* return the newly created factory */ | ||
return createFactory({ | ||
advice: advice, | ||
factories: factories, | ||
initFunction: initFunction, | ||
className: className, | ||
proto: proto | ||
}); | ||
} | ||
/* convert the advice, if any, to the format used by createFactory */ | ||
var advice = aspectAdviceToAdviceMap(aspectAdvice); | ||
/* label the initFn */ | ||
if (initFunction) { | ||
assignFunctionName(initFunction, "mixin" + (className || (isComposeFactory(mixin) && mixin.name) || base.name)); | ||
} | ||
/* return the newly created factory */ | ||
return createFactory({ | ||
advice: advice, | ||
factories: factories, | ||
initFunction: initFunction, | ||
className: className, | ||
proto: proto | ||
}); | ||
} | ||
@@ -614,4 +646,124 @@ /** | ||
} | ||
/** | ||
* Class that provides an implementation of the ComposeCreatedMixin interface. All method calls record their arguments | ||
* and which method was called. When the mixin is applied these calls are retrieved and executed on the target factory. | ||
*/ | ||
var Mixin = (function () { | ||
function Mixin() { | ||
this._calls = []; | ||
} | ||
Mixin.prototype.static = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['static', args]); | ||
return this; | ||
}; | ||
Mixin.prototype.extend = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['extend', args]); | ||
return this; | ||
}; | ||
Mixin.prototype.mixin = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['mixin', args]); | ||
return this; | ||
}; | ||
Mixin.prototype.overlay = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['overlay', args]); | ||
return this; | ||
}; | ||
Mixin.prototype.override = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['override', args]); | ||
return this; | ||
}; | ||
Mixin.prototype.from = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['from', args]); | ||
return this; | ||
}; | ||
Mixin.prototype.before = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['before', args]); | ||
return this; | ||
}; | ||
Mixin.prototype.after = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['after', args]); | ||
return this; | ||
}; | ||
Mixin.prototype.around = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['around', args]); | ||
return this; | ||
}; | ||
Mixin.prototype.aspect = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['aspect', args]); | ||
return this; | ||
}; | ||
Mixin.prototype.init = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
this._calls.push(['init', args]); | ||
return this; | ||
}; | ||
return Mixin; | ||
}()); | ||
/** | ||
* Creates a ComposeCreatedMixin. If provided, the target is used to determine the initial types for the mixin target, | ||
* base, and in the case that the target is a ComposeFactory, the options. | ||
* @param target Optional parameter that allows the Target, T, and sometimes O types to be inferred | ||
*/ | ||
function createMixin(target) { | ||
var mixin = new Mixin(); | ||
return mixin; | ||
} | ||
/** | ||
* Applies a ComposeCreatedMixin to a ComposeFactory, returning a new ComposeFactory | ||
* @param _base The compose factory to apply the mixin to | ||
* @param toMixin The mixin to apply | ||
*/ | ||
function execute(_base, toMixin) { | ||
var base = _base, mixin = toMixin, calls = mixin._calls; | ||
for (var i = 0; i < calls.length; i++) { | ||
var _a = calls[i], fn = _a[0], args = _a[1]; | ||
base = base[fn].apply(base, args); | ||
} | ||
return base; | ||
} | ||
function create(className, base, initFunction) { | ||
/* disambugate arguments */ | ||
/* disambiguate arguments */ | ||
if (typeof className !== 'string') { | ||
@@ -673,3 +825,4 @@ initFunction = base; | ||
around: around, | ||
aspect: aspect | ||
aspect: aspect, | ||
createMixin: createMixin | ||
}); | ||
@@ -676,0 +829,0 @@ Object.defineProperty(exports, "__esModule", { value: true }); |
@@ -0,0 +0,0 @@ import compose, { isComposeFactory } from './compose'; |
@@ -0,0 +0,0 @@ (function (factory) { |
{ | ||
"name": "@dojo/compose", | ||
"version": "2.0.0-beta.21", | ||
"version": "2.0.0-beta.22", | ||
"description": "A composition library, which works well in a TypeScript environment.", | ||
@@ -5,0 +5,0 @@ "homepage": "http://dojotoolkit.org", |
250
README.md
@@ -5,3 +5,3 @@ # @dojo/compose | ||
[![codecov.io](http://codecov.io/github/dojo/compose/coverage.svg?branch=master)](http://codecov.io/github/dojo/compose?branch=master) | ||
[![npm version](https://badge.fury.io/js/@dojo/compose.svg)](http://badge.fury.io/js/@dojo/compose) | ||
[![npm version](https://badge.fury.io/js/%40dojo%2Fcompose.svg)](https://badge.fury.io/js/%40dojo%2Fcompose) | ||
@@ -182,150 +182,65 @@ A composition library, which works well in a TypeScript environment. | ||
``` | ||
### Adding Initialization Functions | ||
### Mixing in Traits/State | ||
As factories are extended or otherwise modified, it is often desirable to | ||
provide additional initialization logic for the new factory. The `init` method | ||
can be used to provide a new initializer to an existing factory. The type | ||
of the instance and options will default to the type of the compose factory | ||
prototype and the type of the options argument for the last provided | ||
initializer. | ||
Oftentimes the need arises to take an existing class and add not just properties, but also behavior, or traits. The `compose` module's default export has a `mixin` property that provides this functionality. It can be used to mix in another compose class: | ||
```typescript | ||
import * as compose from 'dojo/compose'; | ||
const fooFactory = compose.create({ | ||
foo: 'bar' | ||
const createFoo = compose({ | ||
foo: '' | ||
}, (instance, options: { foo: string } = { foo: 'foo' }) => { | ||
// Instance type is inferred based on the type passed to | ||
// compose | ||
instance.foo = options.foo; | ||
}); | ||
const barFactory = compose.create({ | ||
bar: function () { | ||
console.log('bar'); | ||
} | ||
}); | ||
const fooBarFactory = compose.mixin(fooFactory, barFactory); | ||
const fooBar = fooBarFactory(); | ||
fooBar.bar(); // logs "bar" | ||
const createFooWithNewInitializer = createFoo | ||
.init((instance, options?) => { | ||
// If we don't type the options it defaults to { foo: string } | ||
instance.foo = (options && options.foo) || instance.foo; | ||
}); | ||
const createFooBar = createFoo | ||
.extend({ bar: 'bar' }) | ||
.init((instance, options?) => { | ||
// Instance type is updated as the factory prototype is | ||
// modified, it now has foo and bar properties | ||
instance.foo = instance.bar = (options && options.foo) || instance.foo; | ||
}); | ||
``` | ||
NOTE: Using mixin on a ComposeFactory will result in the init function for the mixed in factory to be called first, and any init functions for the base will follow. | ||
It can also be used to mix in an ES6 class. | ||
Note that when mixing in an ES6 class only methods will be mixed into the resulting class, not state. | ||
```typescript | ||
import * as compose from 'dojo/compose'; | ||
const fooFactory = compose.create({ | ||
foo: 'bar' | ||
}); | ||
Sometimes, as in the `createFooBar` example above, additional properties may need to be added to the options parameter of the initialize function. A new type can be specified as a generic or by explicitly typing options in the function declaration. | ||
class Bar { | ||
bar() { console.log('bar'); } | ||
} | ||
const fooBarFactory = compose.mixin(fooFactory, { mixin: Bar }); | ||
const fooBar = fooBarFactory(); | ||
fooBar.bar(); // logs "bar" | ||
``` | ||
It can also mixin in a plain object, but extend would be more appropriate in this case: | ||
```typescript | ||
import * as compose from 'dojo/compose'; | ||
const fooFactory = compose.create({ | ||
foo: 'bar' | ||
const createFoo = compose({ | ||
foo: '' | ||
}, (instance, options: { foo: string } = { foo: 'foo' }) => { | ||
instance.foo = options.foo; | ||
}); | ||
const bar = { | ||
bar() { console.log('bar'); } | ||
} | ||
const fooBarFactory = compose.mixin(fooFactory, { mixin: bar }); | ||
const fooBar = fooBarFactory(); | ||
fooBar.bar(); // logs "bar" | ||
``` | ||
The real benefit of using `mixin` is in those cases where simply modifying the type is not enough, and there is additional behavior that needs to be included via an initialization function or aspects. | ||
```typescript | ||
import * as compose from 'dojo/compose'; | ||
const fooFactory = compose.create({ | ||
foo: 'bar', | ||
doSomething: function() { | ||
console.log('something'); | ||
} | ||
}); | ||
const bar = { | ||
bar: 'uninitialized' | ||
}; | ||
const initBar = function(instance: { bar: string }) { | ||
instance.bar = 'initialized'; | ||
}; | ||
const bazFactory = compose.create({ | ||
baz: 'baz' | ||
}, function(instance: { baz: string }) { | ||
instance.baz = 'also initialized'; | ||
}); | ||
const bazAspect: AspectAdvice = { | ||
after: { | ||
doSomething: function() { | ||
console.log('something else'); | ||
} | ||
} | ||
}; | ||
const fooBarBazFactory = fooFactory | ||
.mixin({ | ||
mixin: bar, | ||
initialize: initBar | ||
}) | ||
.mixin({ | ||
mixin: bazFactory, | ||
aspectAdvice: bazAspect | ||
const createFooBar = createFoo | ||
.extend({ bar: 'bar' }) | ||
// Extend options type with generic | ||
.init<{ foo: string, bar: string }>((instance, options?) => { | ||
instance.foo = (options && options.foo) || 'foo'; | ||
instance.bar = (options && options.bar) || 'bar'; | ||
}); | ||
const fooBarBaz = fooBarBazFactory(); | ||
console.log(fooBarBaz.bar); // logs 'initialized' | ||
console.log(fooBarBaz.baz); // logs 'also initialized' | ||
fooBarBaz.doSomething(); // logs 'something' and then 'something else' | ||
const createFooBarToo = createFoo | ||
.extend({ bar: 'bar' }) | ||
// Extend options type in function signature | ||
.init(instance, options?: { foo: string, bar: string }) => { | ||
instance.foo = (options && options.foo) || 'foo'; | ||
instance.bar = (options && options.bar) || 'bar'; | ||
}); | ||
``` | ||
Additionally, anything with a `factoryDescriptor` function that returns a `ComposeMixinDescriptor` object can be passed directy to mixin. | ||
```typescript | ||
const createFoo = compose({ | ||
foo: '' | ||
}) | ||
const mixin = { | ||
factoryDescriptor: function() { | ||
return { | ||
mixin: { | ||
bar: 1 | ||
}, | ||
initialize: function(fooBar: { bar: number; foo: string; }) { | ||
fooBar.bar = 3; | ||
fooBar.foo = 'bar'; | ||
} | ||
}; | ||
} | ||
}; | ||
const createFooBar = createFoo.mixin(mixin); | ||
const fooBar = createFooBar(); | ||
console.log(fooBar.foo) // logs 'foo' | ||
console.log(fooBar.bar) // logs 3 | ||
``` | ||
The previous example, where a `ComposeFactory` was passed directly to `mixin` is possible because as a convenience all instances of `ComposeFactory` | ||
are initialized with a version of the `factoryDescriptor` function that simply returns the factory itself as the `mixin` property. If a more complicated | ||
factory descriptor is required, the `factoryDescriptor` method can be overridden using the `static` method, documented below. | ||
### Merging of Arrays | ||
When mixing in or extending classes which contain array literals as a value of a property, `compose` will merge these values | ||
instead of over writting, which it does with other value types. | ||
instead of over writing, which it does with other value types. | ||
@@ -376,3 +291,3 @@ For example, if I have an array of strings in my original class, and provide a mixin which shares the same property that is | ||
let fooBarFactory: FooBarClass = compose(Foo).mixin({ mixin: <any> Bar }); | ||
let fooBarFactory: FooBarClass = compose(Foo).extend(Bar); | ||
@@ -460,2 +375,70 @@ let fooBar = fooBarFactory<number, any>(); | ||
### Mixins | ||
One of the goals of compose is to enable the reuse of code, and to allow | ||
clean separation of concerns. Mixins provide a way to encapsulate | ||
functionality that may be reused across many different factories. | ||
This example shows how to create and apply a mixin: | ||
```typescript | ||
const createFoo = compose({ foo: 'foo'}); | ||
const fooMixin = compose.createMixin(createFoo); | ||
createFoo.mixin(fooMixin); | ||
``` | ||
In this case the mixin won't actually do anything, because we applied it | ||
immediately after creating it. Another thing to note in this exapmle, is | ||
that passing `createFoo` to `createMixin` is optional, but is generally | ||
a good idea. This lets the mixin know that it should be mixed into something | ||
that provides at least the same functionality as `createFoo`, so the mixin | ||
can automatically include the prototype and options types from `createFoo`. | ||
In order to create a mixin that's actually useful, we can use any of the | ||
`ComposeFactory` methods discussed above. The mixin will record these calls, | ||
and when mixed into a factory will apply them as if they were called directly | ||
on the factory. | ||
```typescript | ||
const createFoo = compose({ | ||
foo: 'foo' | ||
}, (instance, options?: { foo: string }) => { | ||
instance.foo = (options && options.foo) || 'foo'; | ||
}); | ||
const createFooBar = createFoo.extend({ bar: 'bar'}); | ||
const fooMixin = compose.createMixin(createFoo) | ||
// Because we passed createFoo, the types of instance and options | ||
// are both { foo: string } | ||
.init((instance, options?) => { | ||
instance.foo = (options && options.foo) + 'bar'; | ||
}); | ||
.extend({ baz: 'baz'}); | ||
const createFooBaz = createFoo.mixin(fooMixin); | ||
/* Equivalent to calling | ||
createFoo | ||
.init((instance, options?) => { | ||
instance.foo = (options && options.foo) + 'bar'; | ||
}); | ||
.extend({ baz: 'baz'}); | ||
*/ | ||
const createFooBarBaz = createFooBar.mixin(fooMixin); | ||
/* Equivalent to calling | ||
createFooBar | ||
.init((instance, options?) => { | ||
instance.foo = (options && options.foo) + 'bar'; | ||
}); | ||
.extend({ baz: 'baz'}); | ||
*/ | ||
``` | ||
Compose also provides the ability to mixin a factory directly, or a | ||
`FactoryDescriptor` object, but these are allowed only for the backwards | ||
compatibility. The `createMixin` API is the preferred method for creating | ||
and applying mixins. | ||
## How do I use this package? | ||
@@ -484,3 +467,2 @@ | ||
``` | ||
## How do I contribute? | ||
@@ -487,0 +469,0 @@ |
@@ -0,0 +0,0 @@ export interface CancelableEvent<T extends string, U> { |
@@ -0,0 +0,0 @@ (function (factory) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
196703
1958
484