Skip to content

Commit

Permalink
Merge pull request #11440 from dgeb/registry-reform
Browse files Browse the repository at this point in the history
Container / Registry Reform
  • Loading branch information
rwjblue committed Jul 23, 2015
2 parents 4ff7ba7 + c353c38 commit 1d7b833
Show file tree
Hide file tree
Showing 45 changed files with 651 additions and 304 deletions.
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

0 comments on commit 1d7b833

Please sign in to comment.