Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Namespaced invocation #16319

Merged
merged 1 commit into from
Mar 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 18 additions & 29 deletions packages/container/lib/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,21 +146,15 @@ export default class Container {
let normalizedName = this.registry.normalize(fullName);

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

if (options.source) {
let expandedFullName = this.registry.expandLocalLookup(fullName, options);
// if expandLocalLookup returns falsey, we do not support local lookup
if (!EMBER_MODULE_UNIFICATION) {
if (!expandedFullName) {
return;
}

normalizedName = expandedFullName;
} else if (expandedFullName) {
// with ember-module-unification, if expandLocalLookup returns something,
// pass it to the resolve without the source
normalizedName = expandedFullName;
options = {};
assert(
'EMBER_MODULE_UNIFICATION must be enabled to pass a namespace option to factoryFor',
EMBER_MODULE_UNIFICATION || !options.namespace
);

if (options.source || options.namespace) {
normalizedName = this.registry.expandLocalLookup(fullName, options);
if (!normalizedName) {
return;
}
}

Expand Down Expand Up @@ -206,22 +200,17 @@ function isInstantiatable(container, fullName) {
}

function lookup(container, fullName, options = {}) {
let normalizedName = fullName;
if (options.source) {
let expandedFullName = container.registry.expandLocalLookup(fullName, options);
assert(
'EMBER_MODULE_UNIFICATION must be enabled to pass a namespace option to lookup',
EMBER_MODULE_UNIFICATION || !options.namespace
);

if (!EMBER_MODULE_UNIFICATION) {
// if expandLocalLookup returns falsey, we do not support local lookup
if (!expandedFullName) {
return;
}
let normalizedName = fullName;

normalizedName = expandedFullName;
} else if (expandedFullName) {
// with ember-module-unification, if expandLocalLookup returns something,
// pass it to the resolve without the source
normalizedName = expandedFullName;
options = {};
if (options.source || options.namespace) {
normalizedName = container.registry.expandLocalLookup(fullName, options);
if (!normalizedName) {
return;
}
}

Expand Down
58 changes: 25 additions & 33 deletions packages/container/lib/registry.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { dictionary, assign, intern } from 'ember-utils';
import { assert, deprecate } from 'ember-debug';
import { EMBER_MODULE_UNIFICATION } from 'ember/features';
import Container from './container';
import { DEBUG } from 'ember-env-flags';
import { ENV } from 'ember-environment';
Expand Down Expand Up @@ -321,8 +320,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 +586,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 +615,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,48 +634,40 @@ 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) {
// when `source` is provided expand normalizedName
// and source into the full normalizedName
let expandedNormalizedName = registry.expandLocalLookup(normalizedName, options);

// if expandLocalLookup returns falsey, we do not support local lookup
if (!EMBER_MODULE_UNIFICATION) {
if (!expandedNormalizedName) {
return;
}

normalizedName = expandedNormalizedName;
} else if (expandedNormalizedName) {
// with ember-module-unification, if expandLocalLookup returns something,
// pass it to the resolve without the source
normalizedName = expandedNormalizedName;
options = {};
function resolve(registry, _normalizedName, options={}) {
let normalizedName = _normalizedName;
// when `source` is provided expand normalizedName
// and source into the full normalizedName
if (options.source || options.namespace) {
normalizedName = registry.expandLocalLookup(_normalizedName, options);
if (!normalizedName) {
return;
}
}

Expand All @@ -702,8 +694,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.');
}
});

}
41 changes: 39 additions & 2 deletions packages/container/tests/registry_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -698,9 +698,7 @@ moduleFor('Registry', class extends AbstractTestCase{

let resolver = {
resolve(name) {
if (EMBER_MODULE_UNIFICATION && name === 'foo:baz') { return; }
resolvedFullNames.push(name);

return 'yippie!';
},

Expand Down Expand Up @@ -760,6 +758,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 +774,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'
);
}
});
}
Loading