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

Container / Registry Reform #11440

Merged
merged 12 commits into from
Jul 23, 2015
12 changes: 12 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,15 @@ for a detailed explanation.

Implemencts RFC https://github.com/emberjs/rfcs/pull/65, adding support for
custom deprecation and warning handlers.

* `ember-registry-container-reform`

Implements RFC https://github.com/emberjs/rfcs/pull/46, fully encapsulating
and privatizing the `Container` and `Registry` classes by exposing a select
subset of public methods on `Application` and `ApplicationInstance`.

`Application` initializers now receive a single argument to `initialize`:
`application`.

Likewise, `ApplicationInstance` initializers still receive a single argument
to initialize: `applicationInstance`.
3 changes: 2 additions & 1 deletion features.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"ember-htmlbars-get-helper": true,
"ember-htmlbars-helper": true,
"ember-htmlbars-dashless-helpers": true,
"ember-debug-handlers": null
"ember-debug-handlers": null,
"ember-registry-container-reform": null
},
"debugStatements": [
"Ember.warn",
Expand Down
47 changes: 30 additions & 17 deletions packages/container/lib/container.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Ember from 'ember-metal/core'; // Ember.assert
import dictionary from 'ember-metal/dictionary';
import isEnabled from 'ember-metal/features';

/**
A container used to instantiate and cache objects.
Expand All @@ -15,7 +16,7 @@ import dictionary from 'ember-metal/dictionary';
@class Container
*/
function Container(registry, options) {
this._registry = registry;
this.registry = registry;
this.cache = dictionary(options && options.cache ? options.cache : null);
this.factoryCache = dictionary(options && options.factoryCache ? options.factoryCache : null);
this.validationCache = dictionary(options && options.validationCache ? options.validationCache : null);
Expand All @@ -24,11 +25,11 @@ function Container(registry, options) {
Container.prototype = {
/**
@private
@property _registry
@property registry
@type Registry
@since 1.11.0
*/
_registry: null,
registry: null,

/**
@private
Expand Down Expand Up @@ -96,8 +97,8 @@ Container.prototype = {
@return {any}
*/
lookup(fullName, options) {
Ember.assert('fullName must be a proper full name', this._registry.validateFullName(fullName));
return lookup(this, this._registry.normalize(fullName), options);
Ember.assert('fullName must be a proper full name', this.registry.validateFullName(fullName));
return lookup(this, this.registry.normalize(fullName), options);
},

/**
Expand All @@ -109,8 +110,8 @@ Container.prototype = {
@return {any}
*/
lookupFactory(fullName) {
Ember.assert('fullName must be a proper full name', this._registry.validateFullName(fullName));
return factoryFor(this, this._registry.normalize(fullName));
Ember.assert('fullName must be a proper full name', this.registry.validateFullName(fullName));
return factoryFor(this, this.registry.normalize(fullName));
},

/**
Expand Down Expand Up @@ -139,7 +140,7 @@ Container.prototype = {
*/
reset(fullName) {
if (arguments.length > 0) {
resetMember(this, this._registry.normalize(fullName));
resetMember(this, this.registry.normalize(fullName));
} else {
resetCache(this);
}
Expand All @@ -157,7 +158,7 @@ function lookup(container, fullName, options) {

if (value === undefined) { return; }

if (container._registry.getOption(fullName, 'singleton') !== false && options.singleton !== false) {
if (container.registry.getOption(fullName, 'singleton') !== false && options.singleton !== false) {
container.cache[fullName] = value;
}

Expand All @@ -178,7 +179,7 @@ function buildInjections(container) {
}
}

container._registry.validateInjections(injections);
container.registry.validateInjections(injections);

for (i = 0, l = injections.length; i < l; i++) {
injection = injections[i];
Expand All @@ -194,7 +195,7 @@ function factoryFor(container, fullName) {
if (cache[fullName]) {
return cache[fullName];
}
var registry = container._registry;
var registry = container.registry;
var factory = registry.resolve(fullName);
if (factory === undefined) { return; }

Expand Down Expand Up @@ -228,7 +229,7 @@ function factoryFor(container, fullName) {
}

function injectionsFor(container, fullName) {
var registry = container._registry;
var registry = container.registry;
var splitName = fullName.split(':');
var type = splitName[0];

Expand All @@ -242,7 +243,7 @@ function injectionsFor(container, fullName) {
}

function factoryInjectionsFor(container, fullName) {
var registry = container._registry;
var registry = container.registry;
var splitName = fullName.split(':');
var type = splitName[0];

Expand All @@ -258,7 +259,7 @@ function instantiate(container, fullName) {
var factory = factoryFor(container, fullName);
var lazyInjections, validationCache;

if (container._registry.getOption(fullName, 'instantiate') === false) {
if (container.registry.getOption(fullName, 'instantiate') === false) {
return factory;
}

Expand All @@ -273,9 +274,9 @@ function instantiate(container, fullName) {
// Ensure that all lazy injections are valid at instantiation time
if (!validationCache[fullName] && typeof factory._lazyInjections === 'function') {
lazyInjections = factory._lazyInjections();
lazyInjections = container._registry.normalizeInjectionsHash(lazyInjections);
lazyInjections = container.registry.normalizeInjectionsHash(lazyInjections);

container._registry.validateInjections(lazyInjections);
container.registry.validateInjections(lazyInjections);
}

validationCache[fullName] = true;
Expand All @@ -301,7 +302,7 @@ function eachDestroyable(container, callback) {
key = keys[i];
value = cache[key];

if (container._registry.getOption(key, 'instantiate') !== false) {
if (container.registry.getOption(key, 'instantiate') !== false) {
callback(value);
}
}
Expand Down Expand Up @@ -331,4 +332,16 @@ function resetMember(container, fullName) {
}
}

// Once registry / container reform is enabled, we no longer need to expose
// Container#_registry, since Container itself will be fully private.
if (!isEnabled('ember-registry-container-reform')) {
Object.defineProperty(Container, '_registry', {
configurable: true,
enumerable: false,
get() {
return this.registry;
}
});
}

export default Container;
7 changes: 3 additions & 4 deletions packages/container/lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ var VALID_FULL_NAME_REGEXP = /^[^:]+.+:[^:]+$/;
A `Registry` stores the factory and option information needed by a
`Container` to instantiate and cache objects.

The public API for `Registry` is still in flux and should not be considered
stable.
The API for `Registry` is still in flux and should not be considered stable.

@private
@class Registry
Expand Down Expand Up @@ -276,7 +275,7 @@ Registry.prototype = {
},

/**
normalize a fullName based on the applications conventions
Normalize a fullName based on the application's conventions

@private
@method normalize
Expand Down Expand Up @@ -624,9 +623,9 @@ Registry.prototype = {
},

/**
@private
@method knownForType
@param {String} type the type to iterate over
@private
*/
knownForType(type) {
let fallbackKnown, resolverKnown;
Expand Down
103 changes: 65 additions & 38 deletions packages/ember-application/lib/system/application-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
@private
*/

import Ember from 'ember-metal'; // Ember.deprecate
import isEnabled from 'ember-metal/features';
import { get } from 'ember-metal/property_get';
import { set } from 'ember-metal/property_set';
import EmberObject from 'ember-runtime/system/object';
import run from 'ember-metal/run_loop';
import { computed } from 'ember-metal/computed';
import Registry from 'container/registry';
import RegistryProxy from 'ember-runtime/mixins/registry_proxy';
import ContainerProxy from 'ember-runtime/mixins/container_proxy';

/**
The `ApplicationInstance` encapsulates all of the stateful aspects of a
Expand All @@ -34,33 +38,14 @@ import Registry from 'container/registry';
@public
*/

export default EmberObject.extend({
let ApplicationInstance = EmberObject.extend(RegistryProxy, ContainerProxy, {
/**
The application instance's container. The container stores all of the
instance-specific state for this application run.
The `Application` for which this is an instance.

@property {Ember.Container} container
@public
*/
container: null,

/**
The application's registry. The registry contains the classes, templates,
and other code that makes up the application.

@property {Ember.Registry} registry
@property {Ember.Application} application
@private
*/
applicationRegistry: null,

/**
The registry for this application instance. It should use the
`applicationRegistry` as a fallback.

@property {Ember.Registry} registry
@private
*/
registry: null,
application: null,

/**
The DOM events for which the event dispatcher should listen.
Expand Down Expand Up @@ -88,17 +73,22 @@ export default EmberObject.extend({
init() {
this._super(...arguments);

var application = get(this, 'application');

set(this, 'customEvents', get(application, 'customEvents'));
set(this, 'rootElement', get(application, 'rootElement'));

// Create a per-instance registry that will use the application's registry
// as a fallback for resolving registrations.
this.registry = new Registry({
fallback: this.applicationRegistry,
resolver: this.applicationRegistry.resolver
var applicationRegistry = get(application, '__registry__');
var registry = this.__registry__ = new Registry({
fallback: applicationRegistry
});
this.registry.normalizeFullName = this.applicationRegistry.normalizeFullName;
this.registry.makeToString = this.applicationRegistry.makeToString;
registry.normalizeFullName = applicationRegistry.normalizeFullName;
registry.makeToString = applicationRegistry.makeToString;

// Create a per-instance container from the instance's registry
this.container = this.registry.container();
this.__container__ = registry.container();

// Register this instance in the per-instance registry.
//
Expand All @@ -107,11 +97,11 @@ export default EmberObject.extend({
// to notify us when it has created the root-most view. That view is then
// appended to the rootElement, in the case of apps, to the fixture harness
// in tests, or rendered to a string in the case of FastBoot.
this.registry.register('-application-instance:main', this, { instantiate: false });
this.register('-application-instance:main', this, { instantiate: false });
},

router: computed(function() {
return this.container.lookup('router:main');
return this.lookup('router:main');
}).readOnly(),

/**
Expand Down Expand Up @@ -155,9 +145,7 @@ export default EmberObject.extend({
*/
startRouting() {
var router = get(this, 'router');
var isModuleBasedResolver = !!this.registry.resolver.moduleBasedResolver;

router.startRouting(isModuleBasedResolver);
router.startRouting(isResolverModuleBased(this));
this._didSetupRouter = true;
},

Expand All @@ -175,8 +163,7 @@ export default EmberObject.extend({
this._didSetupRouter = true;

var router = get(this, 'router');
var isModuleBasedResolver = !!this.registry.resolver.moduleBasedResolver;
router.setupRouter(isModuleBasedResolver);
router.setupRouter(isResolverModuleBased(this));
},

/**
Expand All @@ -198,7 +185,7 @@ export default EmberObject.extend({
@private
*/
setupEventDispatcher() {
var dispatcher = this.container.lookup('event_dispatcher:main');
var dispatcher = this.lookup('event_dispatcher:main');
dispatcher.setup(this.customEvents, this.rootElement);

return dispatcher;
Expand All @@ -209,6 +196,46 @@ export default EmberObject.extend({
*/
willDestroy() {
this._super(...arguments);
run(this.container, 'destroy');
run(this.__container__, 'destroy');
}
});

function isResolverModuleBased(applicationInstance) {
return !!applicationInstance.application.__registry__.resolver.moduleBasedResolver;
}

if (isEnabled('ember-registry-container-reform')) {
Object.defineProperty(ApplicationInstance, 'container', {
configurable: true,
enumerable: false,
get() {
var instance = this;
return {
lookup() {
Ember.deprecate('Using `ApplicationInstance.container.lookup` is deprecated. Please use `ApplicationInstance.lookup` instead.',
false,
{ id: 'ember-application.app-instance-container', until: '3.0.0' });
return instance.lookup(...arguments);
}
};
}
});
} else {
Object.defineProperty(ApplicationInstance, 'container', {
configurable: true,
enumerable: false,
get() {
return this.__container__;
}
});

Object.defineProperty(ApplicationInstance, 'registry', {
configurable: true,
enumerable: false,
get() {
return this.__registry__;
}
});
}

export default ApplicationInstance;
Loading