Skip to content

Commit

Permalink
Introduce namespace::name invocation
Browse files Browse the repository at this point in the history
  • Loading branch information
mixonic committed Mar 4, 2018
1 parent 442788d commit ad81146
Show file tree
Hide file tree
Showing 14 changed files with 365 additions and 42 deletions.
4 changes: 2 additions & 2 deletions packages/container/lib/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export default class Container {

assert('fullName must be a proper full name', this.registry.isValidFullName(normalizedName));

if (options.source) {
if (options.source || options.namespace) {
let expandedFullName = this.registry.expandLocalLookup(fullName, options);
// if expandLocalLookup returns falsey, we do not support local lookup
if (!EMBER_MODULE_UNIFICATION) {
Expand Down Expand Up @@ -207,7 +207,7 @@ function isInstantiatable(container, fullName) {

function lookup(container, fullName, options = {}) {
let normalizedName = fullName;
if (options.source) {
if (options.source || options.namespace) {
let expandedFullName = container.registry.expandLocalLookup(fullName, options);

if (!EMBER_MODULE_UNIFICATION) {
Expand Down
33 changes: 18 additions & 15 deletions packages/container/lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,9 @@ export default class Registry {
}

let source = options && options.source && this.normalize(options.source);
let namespace = (options && options.namespace) || undefined;

return has(this, this.normalize(fullName), source);
return has(this, this.normalize(fullName), source, namespace);
}

/**
Expand Down Expand Up @@ -586,13 +587,12 @@ export default class Registry {
expandLocalLookup(fullName, options) {
if (this.resolver !== null && this.resolver.expandLocalLookup) {
assert('fullName must be a proper full name', this.isValidFullName(fullName));
assert('options.source must be provided to expandLocalLookup', options && options.source);
assert('options.source must be a proper full name', this.isValidFullName(options.source));
assert('options.source must be a proper full name', !options.source || this.isValidFullName(options.source));

let normalizedFullName = this.normalize(fullName);
let normalizedSource = this.normalize(options.source);

return expandLocalLookup(this, normalizedFullName, normalizedSource);
return expandLocalLookup(this, normalizedFullName, normalizedSource, options.namespace);
} else if (this.fallback !== null) {
return this.fallback.expandLocalLookup(fullName, options);
} else {
Expand All @@ -616,13 +616,14 @@ if (DEBUG) {

for (let key in hash) {
if (hash.hasOwnProperty(key)) {
let { specifier, source } = hash[key];
let { specifier, source, namespace } = hash[key];
assert(`Expected a proper full name, given '${specifier}'`, this.isValidFullName(specifier));

injections.push({
property: key,
specifier,
source
source,
namespace
});
}
}
Expand All @@ -634,32 +635,34 @@ if (DEBUG) {
if (!injections) { return; }

for (let i = 0; i < injections.length; i++) {
let {specifier, source} = injections[i];
let {specifier, source, namespace} = injections[i];

assert(`Attempting to inject an unknown injection: '${specifier}'`, this.has(specifier, {source}));
assert(`Attempting to inject an unknown injection: '${specifier}'`, this.has(specifier, {source, namespace}));
}
};
}

function expandLocalLookup(registry, normalizedName, normalizedSource) {
function expandLocalLookup(registry, normalizedName, normalizedSource, namespace) {
let cache = registry._localLookupCache;
let normalizedNameCache = cache[normalizedName];

if (!normalizedNameCache) {
normalizedNameCache = cache[normalizedName] = Object.create(null);
}

let cached = normalizedNameCache[normalizedSource];
let cacheKey = namespace || normalizedSource;

let cached = normalizedNameCache[cacheKey];

if (cached !== undefined) { return cached; }

let expanded = registry.resolver.expandLocalLookup(normalizedName, normalizedSource);
let expanded = registry.resolver.expandLocalLookup(normalizedName, normalizedSource, namespace);

return normalizedNameCache[normalizedSource] = expanded;
return normalizedNameCache[cacheKey] = expanded;
}

function resolve(registry, normalizedName, options) {
if (options && options.source) {
if (options && (options.source || options.namespace)) {
// when `source` is provided expand normalizedName
// and source into the full normalizedName
let expandedNormalizedName = registry.expandLocalLookup(normalizedName, options);
Expand Down Expand Up @@ -702,8 +705,8 @@ function resolve(registry, normalizedName, options) {
return resolved;
}

function has(registry, fullName, source) {
return registry.resolve(fullName, { source }) !== undefined;
function has(registry, fullName, source, namespace) {
return registry.resolve(fullName, { source, namespace }) !== undefined;
}

const privateNames = dictionary(null);
Expand Down
61 changes: 59 additions & 2 deletions packages/container/tests/container_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,9 @@ if (EMBER_MODULE_UNIFICATION) {
let registry = new Registry();
let resolveCount = 0;
let expandedKey = 'boom, special expanded key';
registry.expandLocalLookup = function() {
registry.expandLocalLookup = (specifier, options) => {
this.assert.strictEqual(specifier, lookup, 'specifier is expanded');
this.assert.strictEqual(options.source, expectedSource, 'source is expanded');
return expandedKey;
};
registry.resolve = function(fullName) {
Expand All @@ -673,7 +675,9 @@ if (EMBER_MODULE_UNIFICATION) {
let expectedSource = 'template:routes/application';
let registry = new Registry();
let expandedKey = 'boom, special expanded key';
registry.expandLocalLookup = function() {
registry.expandLocalLookup = (specifier, options) => {
this.assert.strictEqual(specifier, lookup, 'specifier is expanded');
this.assert.strictEqual(options.source, expectedSource, 'source is expanded');
return expandedKey;
};
registry.resolve = function(fullName) {
Expand All @@ -690,5 +694,58 @@ if (EMBER_MODULE_UNIFICATION) {
this.assert.ok(container.cache[expandedKey] instanceof PrivateComponent,
'The correct factory was stored in the cache with the correct key which includes the source.');
}

['@test The container can expand and resolve a namespace to factoryFor'](assert) {
let PrivateComponent = factory();
let lookup = 'component:my-input';
let expectedNamespace = 'my-addon';
let registry = new Registry();
let resolveCount = 0;
let expandedKey = 'boom, special expanded key';
registry.expandLocalLookup = (specifier, options) => {
this.assert.strictEqual(specifier, lookup, 'specifier is expanded');
this.assert.strictEqual(options.namespace, expectedNamespace, 'namespace is expanded');
return expandedKey;
};
registry.resolve = function(fullName) {
resolveCount++;
if (fullName === expandedKey) {
return PrivateComponent;
}
};

let container = registry.container();

assert.strictEqual(container.factoryFor(lookup, { namespace: expectedNamespace }).class, PrivateComponent, 'The correct factory was provided');
assert.strictEqual(container.factoryFor(lookup, { namespace: expectedNamespace }).class, PrivateComponent, 'The correct factory was provided again');
assert.equal(resolveCount, 1, 'resolve called only once and a cached factory was returned the second time');
}

['@test The container can expand and resolve a namespace to lookup']() {
let PrivateComponent = factory();
let lookup = 'component:my-input';
let expectedNamespace = 'my-addon';
let registry = new Registry();
let expandedKey = 'boom, special expanded key';
registry.expandLocalLookup = (specifier, options) => {
this.assert.strictEqual(specifier, lookup, 'specifier is expanded');
this.assert.strictEqual(options.namespace, expectedNamespace, 'namespace is expanded');
return expandedKey;
};
registry.resolve = function(fullName) {
if (fullName === expandedKey) {
return PrivateComponent;
}
};

let container = registry.container();

let result = container.lookup(lookup, { namespace: expectedNamespace });
this.assert.ok(result instanceof PrivateComponent, 'The correct factory was provided');

this.assert.ok(container.cache[expandedKey] instanceof PrivateComponent,
'The correct factory was stored in the cache with the correct key which includes the source.');
}
});

}
39 changes: 39 additions & 0 deletions packages/container/tests/registry_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,11 @@ if (EMBER_MODULE_UNIFICATION) {
resolver.add({specifier, source}, PrivateComponent);
let registry = new Registry({ resolver });

assert.strictEqual(
registry.resolve(specifier),
undefined,
'Not returned when specifier not scoped'
);
assert.strictEqual(
registry.resolve(specifier, { source }),
PrivateComponent,
Expand All @@ -771,5 +776,39 @@ if (EMBER_MODULE_UNIFICATION) {
'The correct factory was provided again'
);
}

['@test The registry can pass a namespace to the resolver'](assert) {
let PrivateComponent = factory();
let type = 'component';
let name = 'my-input';
let specifier = `${type}:${name}`;
let source = 'template:routes/application';
let namespace = 'my-addon';

let resolver = new ModuleBasedTestResolver();
resolver.add({specifier, source, namespace}, PrivateComponent);
let registry = new Registry({ resolver });

assert.strictEqual(
registry.resolve(specifier),
undefined,
'Not returned when specifier not scoped'
);
assert.strictEqual(
registry.resolve(specifier, {source}),
undefined,
'Not returned when specifier is missing namespace'
);
assert.strictEqual(
registry.resolve(specifier, { source, namespace }),
PrivateComponent,
'The correct factory was provided'
);
assert.strictEqual(
registry.resolve(specifier, { source, namespace }),
PrivateComponent,
'The correct factory was provided again'
);
}
});
}
32 changes: 25 additions & 7 deletions packages/ember-glimmer/lib/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,11 @@ function instrumentationPayload(name: string) {
return { object: `component:${name}` };
}

function makeOptions(moduleName: string) {
return moduleName !== undefined ? { source: `template:${moduleName}`} : undefined;
function makeOptions(moduleName: string, namespace?: string): LookupOptions {
return {
source: moduleName !== undefined ? `template:${moduleName}` : undefined,
namespace
};
}

const BUILTINS_HELPERS = {
Expand Down Expand Up @@ -208,15 +211,17 @@ export default class RuntimeResolver implements IRuntimeResolver<OwnedTemplateMe
return handle;
}

private _lookupHelper(name: string, meta: OwnedTemplateMeta): Option<Helper> {
const helper = this.builtInHelpers[name];
private _lookupHelper(_name: string, meta: OwnedTemplateMeta): Option<Helper> {
const helper = this.builtInHelpers[_name];
if (helper !== undefined) {
return helper;
}

const { owner, moduleName } = meta;

const options: LookupOptions | undefined = makeOptions(moduleName);
const {name, namespace} = this._parseNameForNamespace(_name);

const options: LookupOptions = makeOptions(moduleName, namespace);

const factory = owner.factoryFor(`helper:${name}`, options) || owner.factoryFor(`helper:${name}`);

Expand Down Expand Up @@ -257,8 +262,21 @@ export default class RuntimeResolver implements IRuntimeResolver<OwnedTemplateMe
return null;
}

private _lookupComponentDefinition(name: string, meta: OwnedTemplateMeta): Option<ComponentDefinition> {
let { layout, component } = lookupComponent(meta.owner, name, makeOptions(meta.moduleName));
private _parseNameForNamespace(_name: string) {
let name = _name;
let namespace = undefined;
let namespaceDelimiterOffset = _name.indexOf('::');
if (namespaceDelimiterOffset !== -1) {
name = _name.slice(namespaceDelimiterOffset+2);
namespace = _name.slice(0, namespaceDelimiterOffset);
}

return {name, namespace};
}

private _lookupComponentDefinition(_name: string, meta: OwnedTemplateMeta): Option<ComponentDefinition> {
let {name, namespace} = this._parseNameForNamespace(_name);
let { layout, component } = lookupComponent(meta.owner, name, makeOptions(meta.moduleName, namespace));

if (layout && !component && ENV._TEMPLATE_ONLY_GLIMMER_COMPONENTS) {
return new TemplateOnlyComponentDefinition(layout);
Expand Down
Loading

0 comments on commit ad81146

Please sign in to comment.