Skip to content

Commit

Permalink
[FEAT] Implements invokeHelper
Browse files Browse the repository at this point in the history
  • Loading branch information
NullVoxPopuli committed Sep 29, 2020
1 parent 4ee4f62 commit 713d4aa
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 5 deletions.
2 changes: 1 addition & 1 deletion packages/@ember/-internals/glimmer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ export { OutletState } from './lib/utils/outlet';
export { setComponentManager, setModifierManager, setHelperManager } from './lib/utils/managers';
export { capabilities } from './lib/component-managers/custom';
export { capabilities as modifierCapabilities } from './lib/modifiers/custom';
export { helperCapabilities, HelperManager } from './lib/helpers/custom';
export { helperCapabilities, HelperManager, invokeHelper } from './lib/helpers/custom';
export { isSerializationFirstNode } from './lib/utils/serialization-first-node-helpers';
export { setComponentTemplate, getComponentTemplate } from './lib/utils/component-template';
export { CapturedRenderNode } from './lib/utils/debug-render-tree';
68 changes: 64 additions & 4 deletions packages/@ember/-internals/glimmer/lib/helpers/custom.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { getOwner } from '@ember/-internals/owner';
import { assert } from '@ember/debug';
import { DEBUG } from '@glimmer/env';
import { Arguments, Helper as GlimmerHelper } from '@glimmer/interfaces';
import { createComputeRef, UNDEFINED_REFERENCE } from '@glimmer/reference';
import { associateDestroyableChild, isDestroyed, isDestroying } from '@glimmer/runtime';
import { Cache, createCache } from '@glimmer/validator';
import { argsProxyFor } from '../utils/args-proxy';
import { getHelperManager } from '../utils/managers';

export type HelperDefinition = object;

Expand Down Expand Up @@ -46,7 +50,7 @@ export interface HelperManager<HelperStateBucket = unknown> {

export interface HelperManagerWithValue<HelperStateBucket = unknown>
extends HelperManager<HelperStateBucket> {
getValue(bucket: HelperStateBucket): unknown;
getValue(bucket: HelperStateBucket, args: Arguments): unknown;
}

function hasValue(manager: HelperManager): manager is HelperManagerWithValue {
Expand All @@ -62,20 +66,76 @@ function hasDestroyable(manager: HelperManager): manager is HelperManagerWithDes
return manager.capabilities.hasDestroyable;
}

// Tests:
// manager does not have value
// tracked args
// internal helper state
// injecting services
// - with component
// destruction
// - context
// - cache
// - when component gets destroyed
// - call getValue after cache is destroyed: error
// - destroying the context also destroys the cache
export function invokeHelper(
context: object,
definition: HelperDefinition,
computeArgs: (context: object) => Arguments
) {
// TODO: make one test where there is no owner
// example:
// class {}
const owner = getOwner(context);
const manager = getHelperManager(owner, definition)!;

// TODO: figure out why assert isn't using the TS assert thing
assert(`Expected helper manager to exist`, manager);

let helper: unknown;

// Cache reference needed by associateDestroyableChild
let cache: Cache<unknown> = createCache(() => {
assert(`Cache has already been destroyed`, isDestroying(cache) || isDestroyed(cache));

let args = computeArgs(context);

if (helper === undefined) {
helper = manager.createHelper(definition, args);

if (hasDestroyable(manager)) {
let destroyable = manager.getDestroyable(helper);

associateDestroyableChild(context, cache);
associateDestroyableChild(cache, destroyable);
}
}

if (hasValue(manager)) {
return manager.getValue(helper, args);
}

return;
});

return cache;
}

export default function customHelper(
manager: HelperManager<unknown>,
definition: HelperDefinition
): GlimmerHelper {
return (args, vm) => {
const bucket = manager.createHelper(definition, argsProxyFor(args.capture(), 'helper'));
return (vmArgs, vm) => {
const args = argsProxyFor(vmArgs.capture(), 'helper');
const bucket = manager.createHelper(definition, args);

if (hasDestroyable(manager)) {
vm.associateDestroyable(manager.getDestroyable(bucket));
}

if (hasValue(manager)) {
return createComputeRef(
() => manager.getValue(bucket),
() => manager.getValue(bucket, args),
null,
DEBUG && manager.getDebugName && manager.getDebugName(definition)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import {
setHelperManager,
setModifierManager,
helperCapabilities,
invokeHelper,
Helper,
Component,
} from '@ember/-internals/glimmer';
import { tracked, set } from '@ember/-internals/metal';
import { setOwner } from '@ember/-internals/owner';
Expand Down Expand Up @@ -292,4 +295,38 @@ if (EMBER_GLIMMER_HELPER_MANAGER) {
}
}
);

moduleFor(
'Helpers test: invokeHelper',
class extends RenderingTestCase {
['@test it works with the example from the RFC Summary']() {
class PlusOneHelper extends Helper {
compute([num]) {
return num + 1;
}
}

class PlusOne extends Component {
plusOne = invokeHelper(this, PlusOneHelper, () => {
return {
positional: [this.args.number],
};
});
}

this.registerComponent('plus-one', {
template: `{{this.plusOne.value}}`,
ComponentClass: PlusOne,
});

this.render(`<PlusOne @number={{4}} />`);

this.assertText('5');

runTask(() => this.rerender());

this.assertText('5');
}
}
);
}

0 comments on commit 713d4aa

Please sign in to comment.