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 5, 2018
1 parent 442788d commit e4fab05
Show file tree
Hide file tree
Showing 15 changed files with 420 additions and 95 deletions.
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

0 comments on commit e4fab05

Please sign in to comment.