Skip to content

Commit

Permalink
feat: Introduce variation point for targeted decoration of "instantiate"
Browse files Browse the repository at this point in the history
  • Loading branch information
Iku-turso committed Feb 11, 2022
1 parent fe5e1a9 commit 70b8918
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ describe('createContainer.global-decoration', () => {
const decoratorInjectable = getInjectable({
id: 'some-decorator',
injectionToken: decorationInjectionToken,
instantiate:
() => instantiateToBeDecorated => (di, instantiationParameter) =>

instantiate: () => ({
decorate: instantiateToBeDecorated => (di, instantiationParameter) =>
`decorated-instance(${instantiateToBeDecorated(
di,
`decorated-parameter(${instantiationParameter})`,
)})`,
}),
});

const childInjectable = getInjectable({
Expand Down Expand Up @@ -46,24 +48,26 @@ describe('createContainer.global-decoration', () => {
id: 'some-decorator-1',
injectionToken: decorationInjectionToken,

instantiate:
() => instantiateToBeDecorated => (di, instantiationParameter) =>
instantiate: () => ({
decorate: instantiateToBeDecorated => (di, instantiationParameter) =>
`decorated-instance-1(${instantiateToBeDecorated(
di,
`decorated-parameter-1(${instantiationParameter})`,
)})`,
}),
});

const decoratorInjectable2 = getInjectable({
id: 'some-decorator-2',
injectionToken: decorationInjectionToken,

instantiate:
() => instantiateToBeDecorated => (di, instantiationParameter) =>
instantiate: () => ({
decorate: instantiateToBeDecorated => (di, instantiationParameter) =>
`decorated-instance-2(${instantiateToBeDecorated(
di,
`decorated-parameter-2(${instantiationParameter})`,
)})`,
}),
});

const injectable = getInjectable({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import forEach from 'lodash/fp/forEach';
import get from 'lodash/fp/get';
import getCycles from './getCycles/getCycles';
import getInjectionToken from '../getInjectionToken/getInjectionToken';
import has from 'lodash/fp/has';
import invoke from 'lodash/fp/invoke';
import isFunction from 'lodash/fp/isFunction';
import isUndefined from 'lodash/fp/isUndefined';
Expand All @@ -15,12 +16,14 @@ import last from 'lodash/fp/last';
import lifecycleEnum, { nonStoredInstanceKey } from './lifecycleEnum';
import map from 'lodash/fp/map';
import matches from 'lodash/fp/matches';
import not from 'lodash/fp/negate';
import once from 'lodash/fp/once';
import reject from 'lodash/fp/reject';
import sortBy from 'lodash/fp/sortBy';
import tap from 'lodash/fp/tap';
import { identity } from 'lodash/fp';
import { isPromise, pipeline } from '@ogre-tools/fp';
import { curry, overSome } from 'lodash';

export default (...listOfGetRequireContexts) => {
let injectables = [];
Expand Down Expand Up @@ -258,9 +261,11 @@ const autoRegisterInjectables = ({ getRequireContextForInjectables, di }) => {
);
};

const isRelatedTo = alias => injectable =>
injectable.id === alias.id ||
(injectable.injectionToken && injectable.injectionToken === alias);
const isRelatedTo = curry(
(alias, injectable) =>
injectable.id === alias.id ||
(injectable.injectionToken && injectable.injectionToken === alias),
);

const getRelatedInjectable = ({ injectables, alias, context }) => {
const relatedInjectables = getRelatedInjectables({ injectables, alias });
Expand Down Expand Up @@ -355,7 +360,7 @@ const getInstance = ({
// Prevent recursive decoration
injectable.injectionToken === decorationInjectionToken
? identity
: withGlobalDecoratorsFor(di),
: withDecoratorsFor(di, injectable),

withErrorMonitoring,
);
Expand Down Expand Up @@ -430,11 +435,27 @@ const reportErrorForFor = di => {
};
};

const withGlobalDecoratorsFor =
di =>
toBeDecorated =>
(...args) => {
const globalDecorators = di.injectMany(decorationInjectionToken);
const withDecoratorsFor = (di, injectable) => {
const isRelevantDecorator = isRelevantDecoratorFor(injectable);

return pipeline(toBeDecorated, ...globalDecorators)(...args);
};
return toBeDecorated =>
(...args) => {
const decorators = pipeline(
di.injectMany(decorationInjectionToken),
filter(isRelevantDecorator),
map('decorate'),
);

return pipeline(toBeDecorated, ...decorators)(...args);
};
};

const isGlobalDecorator = not(has('target'));

const isTargetedDecoratorFor = injectable =>
conforms({
target: alias => isRelatedTo(alias, injectable),
});

const isRelevantDecoratorFor = injectable =>
overSome([isGlobalDecorator, isTargetedDecoratorFor(injectable)]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import getDi from '../test-utils/getDiForUnitTesting';
import getInjectable from '../getInjectable/getInjectable';
import { decorationInjectionToken } from './createContainer';
import getInjectionToken from '../getInjectionToken/getInjectionToken';

describe('createContainer.targeted-decoration', () => {
it('given decorator targeting child, when parent is injected, decorates instance and instantiation parameter of only child', () => {
const decoratorInjectable = getInjectable({
id: 'some-child-decorator',
injectionToken: decorationInjectionToken,

instantiate: () => ({
decorate: instantiateToBeDecorated => (di, instantiationParameter) =>
`decorated-instance(${instantiateToBeDecorated(
di,
`decorated-parameter(${instantiationParameter})`,
)})`,

target: childInjectable,
}),
});

const childInjectable = getInjectable({
id: 'some-child-injectable',

instantiate: (di, instantiationParameter) =>
`child(${instantiationParameter})`,
});

const parentInjectable = getInjectable({
id: 'some-parent-injectable',

instantiate: (di, instantiationParameter) => {
const childInstance = di.inject(childInjectable, 'child-parameter');

return `parent(${instantiationParameter}) -> ${childInstance}`;
},
});

const di = getDi(parentInjectable, childInjectable, decoratorInjectable);

const actual = di.inject(parentInjectable, 'parent-parameter');

expect(actual).toBe(
'parent(parent-parameter) -> decorated-instance(child(decorated-parameter(child-parameter)))',
);
});

it('given decorator targeting an injection token and child implementing the token, when parent is injected, decorates instance and instantiation parameter of only the child', () => {
const someInjectionTokenForTargetedDecoration = getInjectionToken({
id: 'some-injection-token-for-targeted-decoration',
});

const decoratorInjectable = getInjectable({
id: 'some-injection-token-decorator',
injectionToken: decorationInjectionToken,

instantiate: () => ({
decorate: instantiateToBeDecorated => (di, instantiationParameter) =>
`decorated-instance(${instantiateToBeDecorated(
di,
`decorated-parameter(${instantiationParameter})`,
)})`,

target: someInjectionTokenForTargetedDecoration,
}),
});

const childInjectable = getInjectable({
id: 'some-child-injectable',

instantiate: (di, instantiationParameter) =>
`child(${instantiationParameter})`,

injectionToken: someInjectionTokenForTargetedDecoration,
});

const parentInjectable = getInjectable({
id: 'some-parent-injectable',

instantiate: (di, instantiationParameter) => {
const childInstance = di.inject(childInjectable, 'child-parameter');

return `parent(${instantiationParameter}) -> ${childInstance}`;
},
});

const di = getDi(parentInjectable, childInjectable, decoratorInjectable);

const actual = di.inject(parentInjectable, 'parent-parameter');

expect(actual).toBe(
'parent(parent-parameter) -> decorated-instance(child(decorated-parameter(child-parameter)))',
);
});
});

0 comments on commit 70b8918

Please sign in to comment.