diff --git a/FEATURES.md b/FEATURES.md index 50e7e20c91c..04e67e93e0a 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -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`. diff --git a/features.json b/features.json index d937f4e9829..4ebc2c76ce5 100644 --- a/features.json +++ b/features.json @@ -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", diff --git a/packages/container/lib/container.js b/packages/container/lib/container.js index 4e2e33db331..73591bac588 100644 --- a/packages/container/lib/container.js +++ b/packages/container/lib/container.js @@ -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. @@ -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); @@ -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 @@ -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); }, /** @@ -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)); }, /** @@ -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); } @@ -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; } @@ -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]; @@ -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; } @@ -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]; @@ -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]; @@ -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; } @@ -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; @@ -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); } } @@ -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; diff --git a/packages/container/lib/registry.js b/packages/container/lib/registry.js index e3839592f57..348f5ea4e8f 100644 --- a/packages/container/lib/registry.js +++ b/packages/container/lib/registry.js @@ -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 @@ -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 @@ -624,9 +623,9 @@ Registry.prototype = { }, /** + @private @method knownForType @param {String} type the type to iterate over - @private */ knownForType(type) { let fallbackKnown, resolverKnown; diff --git a/packages/ember-application/lib/system/application-instance.js b/packages/ember-application/lib/system/application-instance.js index 268488512ae..2facc3ea01b 100644 --- a/packages/ember-application/lib/system/application-instance.js +++ b/packages/ember-application/lib/system/application-instance.js @@ -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 @@ -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. @@ -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. // @@ -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(), /** @@ -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; }, @@ -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)); }, /** @@ -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; @@ -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; diff --git a/packages/ember-application/lib/system/application.js b/packages/ember-application/lib/system/application.js index aae02d78bc2..d7b61617071 100644 --- a/packages/ember-application/lib/system/application.js +++ b/packages/ember-application/lib/system/application.js @@ -38,6 +38,7 @@ import LinkToComponent from 'ember-routing-views/views/link'; import RoutingService from 'ember-routing/services/routing'; import ContainerDebugAdapter from 'ember-extension-support/container_debug_adapter'; import { _loaded } from 'ember-runtime/system/lazy_load'; +import RegistryProxy from 'ember-runtime/mixins/registry_proxy'; import environment from 'ember-metal/environment'; function props(obj) { @@ -152,7 +153,7 @@ var librariesRegistered = false; Ember.Application.initializer({ name: 'api-adapter', - initialize: function(container, application) { + initialize: function(application) { application.register('api-adapter:main', ApiAdapter); } }); @@ -194,7 +195,7 @@ var librariesRegistered = false; @public */ -var Application = Namespace.extend({ +var Application = Namespace.extend(RegistryProxy, { _suppressDeferredDeprecation: true, /** @@ -314,7 +315,7 @@ var Application = Namespace.extend({ @return {Ember.Registry} the configured registry */ buildRegistry() { - var registry = this.registry = Application.buildRegistry(this); + var registry = this.__registry__ = Application.buildRegistry(this); return registry; }, @@ -328,9 +329,7 @@ var Application = Namespace.extend({ */ buildInstance() { return ApplicationInstance.create({ - customEvents: get(this, 'customEvents'), - rootElement: get(this, 'rootElement'), - applicationRegistry: this.registry + application: this }); }, @@ -339,13 +338,13 @@ var Application = Namespace.extend({ // For the default instance only, set the view registry to the global // Ember.View.views hash for backwards-compatibility. - EmberView.views = instance.container.lookup('-view-registry:main'); + EmberView.views = instance.lookup('-view-registry:main'); // TODO2.0: Legacy support for App.__container__ // and global methods on App that rely on a single, // default instance. this.__deprecatedInstance__ = instance; - this.__container__ = instance.container; + this.__container__ = instance.__container__; return instance; }, @@ -425,120 +424,17 @@ var Application = Namespace.extend({ }, /** - Registers a factory that can be used for dependency injection (with - `App.inject`) or for service lookup. Each factory is registered with - a full name including two parts: `type:name`. + Calling initialize manually is not supported. - A simple example: + Please see Ember.Application#advanceReadiness and + Ember.Application#deferReadiness. - ```javascript - var App = Ember.Application.create(); - - App.Orange = Ember.Object.extend(); - App.register('fruit:favorite', App.Orange); - ``` - - Ember will resolve factories from the `App` namespace automatically. - For example `App.CarsController` will be discovered and returned if - an application requests `controller:cars`. - - An example of registering a controller with a non-standard name: - - ```javascript - var App = Ember.Application.create(); - var Session = Ember.Controller.extend(); - - App.register('controller:session', Session); - - // The Session controller can now be treated like a normal controller, - // despite its non-standard name. - App.ApplicationController = Ember.Controller.extend({ - needs: ['session'] - }); - ``` - - Registered factories are **instantiated** by having `create` - called on them. Additionally they are **singletons**, each time - they are looked up they return the same instance. - - Some examples modifying that default behavior: - - ```javascript - var App = Ember.Application.create(); - - App.Person = Ember.Object.extend(); - App.Orange = Ember.Object.extend(); - App.Email = Ember.Object.extend(); - App.session = Ember.Object.create(); - - App.register('model:user', App.Person, { singleton: false }); - App.register('fruit:favorite', App.Orange); - App.register('communication:main', App.Email, { singleton: false }); - App.register('session', App.session, { instantiate: false }); - ``` - - @method register - @param fullName {String} type:name (e.g., 'model:user') - @param factory {Function} (e.g., App.Person) - @param options {Object} (optional) disable instantiation or singleton usage - @public - **/ - register() { - this.registry.register(...arguments); - }, - - /** - Define a dependency injection onto a specific factory or all factories - of a type. - - When Ember instantiates a controller, view, or other framework component - it can attach a dependency to that component. This is often used to - provide services to a set of framework components. - - An example of providing a session object to all controllers: - - ```javascript - var App = Ember.Application.create(); - var Session = Ember.Object.extend({ isAuthenticated: false }); - - // A factory must be registered before it can be injected - App.register('session:main', Session); - - // Inject 'session:main' onto all factories of the type 'controller' - // with the name 'session' - App.inject('controller', 'session', 'session:main'); - - App.IndexController = Ember.Controller.extend({ - isLoggedIn: Ember.computed.alias('session.isAuthenticated') - }); - ``` - - Injections can also be performed on specific factories. - - ```javascript - App.inject(, , ) - App.inject('route', 'source', 'source:main') - App.inject('route:application', 'email', 'model:email') - ``` - - It is important to note that injections can only be performed on - classes that are instantiated by Ember itself. Instantiating a class - directly (via `create` or `new`) bypasses the dependency injection - system. - - **Note:** Ember-Data instantiates its models in a unique manner, and consequently - injections onto models (or all models) will not work as expected. Injections - on models can be enabled by setting `Ember.MODEL_FACTORY_INJECTIONS` - to `true`. - - @method inject - @param factoryNameOrType {String} - @param property {String} - @param injectionName {String} - @public - **/ - inject() { - this.registry.injection(...arguments); + @private + @deprecated + @method initialize + **/ + initialize() { + Ember.deprecate('Calling initialize manually is not supported. Please see Ember.Application#advanceReadiness and Ember.Application#deferReadiness'); }, /** @@ -566,7 +462,7 @@ var Application = Namespace.extend({ this._bootPromise = defer.promise; this._bootResolver = defer; - this.runInitializers(this.registry); + this.runInitializers(); runLoadHooks('application', this); this.advanceReadiness(); @@ -672,11 +568,20 @@ var Application = Namespace.extend({ @private @method runInitializers */ - runInitializers(registry) { + runInitializers() { var App = this; this._runInitializer('initializers', function(name, initializer) { Ember.assert('No application initializer named \'' + name + '\'', !!initializer); - initializer.initialize(registry, App); + if (initializer.initialize.length === 2) { + if (isEnabled('ember-registry-container-reform')) { + Ember.deprecate('The `initialize` method for Application initializer \'' + name + '\' should take only one argument - `App`, an instance of an `Application`.', + false, + { id: 'ember-application.app-initializer-initialize-arguments', until: '3.0.0' }); + } + initializer.initialize(App.__registry__, App); + } else { + initializer.initialize(App); + } }); }, @@ -833,7 +738,7 @@ Application.reopenClass({ Ember.Application.initializer({ name: 'namedInitializer', - initialize: function(container, application) { + initialize: function(application) { Ember.debug('Running namedInitializer!'); } }); @@ -849,7 +754,7 @@ Application.reopenClass({ Ember.Application.initializer({ name: 'first', - initialize: function(container, application) { + initialize: function(application) { Ember.debug('First initializer!'); } }); @@ -865,7 +770,7 @@ Application.reopenClass({ name: 'second', after: 'first', - initialize: function(container, application) { + initialize: function(application) { Ember.debug('Second initializer!'); } }); @@ -882,7 +787,7 @@ Application.reopenClass({ name: 'pre', before: 'first', - initialize: function(container, application) { + initialize: function(application) { Ember.debug('Pre initializer!'); } }); @@ -900,7 +805,7 @@ Application.reopenClass({ name: 'post', after: ['first', 'second'], - initialize: function(container, application) { + initialize: function(application) { Ember.debug('Post initializer!'); } }); @@ -911,22 +816,8 @@ Application.reopenClass({ // DEBUG: Post initializer! ``` - * `initialize` is a callback function that receives two arguments, `container` - and `application` on which you can operate. - - Example of using `container` to preload data into the store: - - ```javascript - Ember.Application.initializer({ - name: 'preload-data', - - initialize: function(container, application) { - var store = container.lookup('store:main'); - - store.pushPayload(preloadedData); - } - }); - ``` + * `initialize` is a callback function that receives one argument, + `application`, on which you can operate. Example of using `application` to register an adapter: @@ -934,7 +825,7 @@ Application.reopenClass({ Ember.Application.initializer({ name: 'api-adapter', - initialize: function(container, application) { + initialize: function(application) { application.register('api-adapter:main', ApiAdapter); } }); diff --git a/packages/ember-application/tests/system/dependency_injection/default_resolver_test.js b/packages/ember-application/tests/system/dependency_injection/default_resolver_test.js index 13c371d2afb..14b531fe27a 100644 --- a/packages/ember-application/tests/system/dependency_injection/default_resolver_test.js +++ b/packages/ember-application/tests/system/dependency_injection/default_resolver_test.js @@ -24,7 +24,7 @@ QUnit.module('Ember.Application Dependency Injection - default resolver', { originalLookup = Ember.lookup; application = run(Application, 'create'); - registry = application.registry; + registry = application.__registry__; locator = application.__container__; originalLoggerInfo = Logger.info; }, diff --git a/packages/ember-application/tests/system/dependency_injection/normalization_test.js b/packages/ember-application/tests/system/dependency_injection/normalization_test.js index 669dcbc9116..e00f29a21f7 100644 --- a/packages/ember-application/tests/system/dependency_injection/normalization_test.js +++ b/packages/ember-application/tests/system/dependency_injection/normalization_test.js @@ -6,7 +6,7 @@ var application, registry; QUnit.module('Ember.Application Dependency Injection – normalization', { setup() { application = run(Application, 'create'); - registry = application.registry; + registry = application.__registry__; }, teardown() { diff --git a/packages/ember-application/tests/system/dependency_injection/to_string_test.js b/packages/ember-application/tests/system/dependency_injection/to_string_test.js index 15e837ee2d7..cef176b7be2 100644 --- a/packages/ember-application/tests/system/dependency_injection/to_string_test.js +++ b/packages/ember-application/tests/system/dependency_injection/to_string_test.js @@ -56,7 +56,7 @@ QUnit.test('with a custom resolver', function() { }); }); - App.registry.register('model:peter', EmberObject.extend()); + App.register('model:peter', EmberObject.extend()); var peter = App.__container__.lookup('model:peter'); var guid = guidFor(peter); diff --git a/packages/ember-application/tests/system/dependency_injection_test.js b/packages/ember-application/tests/system/dependency_injection_test.js index ad99ecb8df3..f5637737890 100644 --- a/packages/ember-application/tests/system/dependency_injection_test.js +++ b/packages/ember-application/tests/system/dependency_injection_test.js @@ -27,7 +27,7 @@ QUnit.module('Ember.Application Dependency Injection', { application.register('communication:main', application.Email, { singleton: false }); application.register('controller:postIndex', application.PostIndexController, { singleton: true }); - registry = application.registry; + registry = application.__registry__; locator = application.__container__; lookup = Ember.lookup = {}; diff --git a/packages/ember-application/tests/system/initializers_test.js b/packages/ember-application/tests/system/initializers_test.js index a8de931891e..f1c966a4e54 100644 --- a/packages/ember-application/tests/system/initializers_test.js +++ b/packages/ember-application/tests/system/initializers_test.js @@ -3,6 +3,7 @@ import run from 'ember-metal/run_loop'; import Application from 'ember-application/system/application'; import jQuery from 'ember-views/system/jquery'; import Registry from 'container/registry'; +import isEnabled from 'ember-metal/features'; var app; @@ -33,24 +34,44 @@ QUnit.test('initializers require proper \'name\' and \'initialize\' properties', }); }); -QUnit.test('initializers are passed a registry and App', function() { - var MyApplication = Application.extend(); +if (isEnabled('ember-registry-container-reform')) { + QUnit.test('initializers are passed an App', function() { + var MyApplication = Application.extend(); - MyApplication.initializer({ - name: 'initializer', - initialize(registry, App) { - ok(registry instanceof Registry, 'initialize is passed a registry'); - ok(App instanceof Application, 'initialize is passed an Application'); - } + MyApplication.initializer({ + name: 'initializer', + initialize(App) { + ok(App instanceof Application, 'initialize is passed an Application'); + } + }); + + run(function() { + app = MyApplication.create({ + router: false, + rootElement: '#qunit-fixture' + }); + }); }); +} else { + QUnit.test('initializers are passed a registry and App', function() { + var MyApplication = Application.extend(); - run(function() { - app = MyApplication.create({ - router: false, - rootElement: '#qunit-fixture' + MyApplication.initializer({ + name: 'initializer', + initialize(registry, App) { + ok(registry instanceof Registry, 'initialize is passed a registry'); + ok(App instanceof Application, 'initialize is passed an Application'); + } + }); + + run(function() { + app = MyApplication.create({ + router: false, + rootElement: '#qunit-fixture' + }); }); }); -}); +} QUnit.test('initializers can be registered in a specified order', function() { var order = []; @@ -322,13 +343,13 @@ QUnit.test('initializers are per-app', function() { var FirstApp = Application.extend(); FirstApp.initializer({ name: 'shouldNotCollide', - initialize(registry) {} + initialize(application) {} }); var SecondApp = Application.extend(); SecondApp.initializer({ name: 'shouldNotCollide', - initialize(registry) {} + initialize(application) {} }); }); @@ -339,7 +360,7 @@ QUnit.test('initializers should be executed in their own context', function() { MyApplication.initializer({ name: 'coolInitializer', myProperty: 'cool', - initialize(registry, application) { + initialize(application) { equal(this.myProperty, 'cool', 'should have access to its own context'); } }); @@ -351,3 +372,26 @@ QUnit.test('initializers should be executed in their own context', function() { }); }); }); + +if (isEnabled('ember-registry-container-reform')) { + QUnit.test('initializers should throw a deprecation warning when receiving a second argument', function() { + expect(1); + + var MyApplication = Application.extend(); + + MyApplication.initializer({ + name: 'deprecated', + initialize(registry, application) { + } + }); + + expectDeprecation(function() { + run(function() { + app = MyApplication.create({ + router: false, + rootElement: '#qunit-fixture' + }); + }); + }, /The `initialize` method for Application initializer 'deprecated' should take only one argument - `App`, an instance of an `Application`./); + }); +} diff --git a/packages/ember-application/tests/system/reset_test.js b/packages/ember-application/tests/system/reset_test.js index 61a45d9901b..309f21275c7 100644 --- a/packages/ember-application/tests/system/reset_test.js +++ b/packages/ember-application/tests/system/reset_test.js @@ -8,6 +8,7 @@ import View from 'ember-views/views/view'; import Controller from 'ember-runtime/controllers/controller'; import jQuery from 'ember-views/system/jquery'; import Registry from 'container/registry'; +import isEnabled from 'ember-metal/features'; var application, Application; @@ -240,15 +241,27 @@ QUnit.test('With ember-data like initializer and constant', function() { }) }; - Application.initializer({ - name: 'store', - initialize(registry, application) { - registry.unregister('store:main'); - registry.register('store:main', application.Store); + if (isEnabled('ember-registry-container-reform')) { + Application.initializer({ + name: 'store', + initialize(application) { + application.unregister('store:main'); + application.register('store:main', application.Store); - application.__container__.lookup('store:main'); - } - }); + application.__container__.lookup('store:main'); + } + }); + } else { + Application.initializer({ + name: 'store', + initialize(registry, application) { + registry.unregister('store:main'); + registry.register('store:main', application.Store); + + application.__container__.lookup('store:main'); + } + }); + } run(function() { application = Application.create(); diff --git a/packages/ember-application/tests/system/visit_test.js b/packages/ember-application/tests/system/visit_test.js index d0ae84987dd..3157134859d 100644 --- a/packages/ember-application/tests/system/visit_test.js +++ b/packages/ember-application/tests/system/visit_test.js @@ -65,7 +65,7 @@ if (isEnabled('ember-application-visit')) { app.instanceInitializer({ name: 'register-application-template', initialize(app) { - app.registry.register('template:application', compile('

Hello world

')); + app.register('template:application', compile('

Hello world

')); } }); }); @@ -97,11 +97,11 @@ if (isEnabled('ember-application-visit')) { app.instanceInitializer({ name: 'register-application-template', initialize(app) { - app.registry.register('template:application', compile('

Hello world

{{view "child"}}')); - app.registry.register('view:application', View.extend({ + app.register('template:application', compile('

Hello world

{{view "child"}}')); + app.register('view:application', View.extend({ elementId: 'my-cool-app' })); - app.registry.register('view:child', View.extend({ + app.register('view:child', View.extend({ elementId: 'child-view' })); } diff --git a/packages/ember-extension-support/tests/data_adapter_test.js b/packages/ember-extension-support/tests/data_adapter_test.js index d9d59173576..52fe4c53872 100644 --- a/packages/ember-extension-support/tests/data_adapter_test.js +++ b/packages/ember-extension-support/tests/data_adapter_test.js @@ -26,7 +26,7 @@ QUnit.module('Data Adapter', { App = EmberApplication.create(); App.toString = function() { return 'App'; }; App.deferReadiness(); - App.registry.register('data-adapter:main', DataAdapter); + App.register('data-adapter:main', DataAdapter); }); }, teardown() { @@ -88,7 +88,7 @@ QUnit.test('Model types added with custom container-debug-adapter', function() { return [PostClass]; } }); - App.registry.register('container-debug-adapter:main', StubContainerDebugAdapter); + App.register('container-debug-adapter:main', StubContainerDebugAdapter); adapter = App.__container__.lookup('data-adapter:main'); adapter.reopen({ diff --git a/packages/ember-htmlbars/lib/hooks/has-helper.js b/packages/ember-htmlbars/lib/hooks/has-helper.js index 351d62655b7..f791879d3b3 100644 --- a/packages/ember-htmlbars/lib/hooks/has-helper.js +++ b/packages/ember-htmlbars/lib/hooks/has-helper.js @@ -8,7 +8,7 @@ export default function hasHelperHook(env, scope, helperName) { var container = env.container; if (validateLazyHelperName(helperName, container, env.hooks.keywords, env.knownHelpers)) { var containerName = 'helper:' + helperName; - if (container._registry.has(containerName)) { + if (container.registry.has(containerName)) { return true; } } diff --git a/packages/ember-htmlbars/lib/system/bootstrap.js b/packages/ember-htmlbars/lib/system/bootstrap.js index ecc30546d5c..89bb889f694 100644 --- a/packages/ember-htmlbars/lib/system/bootstrap.js +++ b/packages/ember-htmlbars/lib/system/bootstrap.js @@ -75,7 +75,7 @@ function _bootstrap() { } function registerComponentLookup(app) { - app.registry.register('component-lookup:main', ComponentLookup); + app.register('component-lookup:main', ComponentLookup); } /* diff --git a/packages/ember-htmlbars/lib/system/discover-known-helpers.js b/packages/ember-htmlbars/lib/system/discover-known-helpers.js index 9f647393020..ca07d767294 100644 --- a/packages/ember-htmlbars/lib/system/discover-known-helpers.js +++ b/packages/ember-htmlbars/lib/system/discover-known-helpers.js @@ -2,7 +2,7 @@ import isEnabled from 'ember-metal/features'; import dictionary from 'ember-metal/dictionary'; export default function discoverKnownHelpers(container) { - let registry = container && container._registry; + let registry = container && container.registry; let helpers = dictionary(null); if (isEnabled('ember-htmlbars-dashless-helpers')) { diff --git a/packages/ember-htmlbars/lib/system/lookup-helper.js b/packages/ember-htmlbars/lib/system/lookup-helper.js index 0958d4e8b46..e10cf348d6c 100644 --- a/packages/ember-htmlbars/lib/system/lookup-helper.js +++ b/packages/ember-htmlbars/lib/system/lookup-helper.js @@ -46,7 +46,7 @@ export function findHelper(name, view, env) { var container = env.container; if (validateLazyHelperName(name, container, env.hooks.keywords, env.knownHelpers)) { var helperName = 'helper:' + name; - if (container._registry.has(helperName)) { + if (container.registry.has(helperName)) { helper = container.lookupFactory(helperName); if (isLegacyBareHelper(helper)) { Ember.deprecate(`The helper "${name}" is a deprecated bare function helper. Please use Ember.Helper.build to wrap helper functions.`, false, { id: 'ember-htmlbars.legacy-bare-helper', until: '3.0.0' }); diff --git a/packages/ember-htmlbars/lib/utils/is-component.js b/packages/ember-htmlbars/lib/utils/is-component.js index dc2e10575be..37cdce9cd96 100644 --- a/packages/ember-htmlbars/lib/utils/is-component.js +++ b/packages/ember-htmlbars/lib/utils/is-component.js @@ -13,6 +13,6 @@ export default function isComponent(env, scope, path) { var container = env.container; if (!container) { return false; } if (!CONTAINS_DASH_CACHE.get(path)) { return false; } - return container._registry.has('component:' + path) || - container._registry.has('template:components/' + path); + return container.registry.has('component:' + path) || + container.registry.has('template:components/' + path); } diff --git a/packages/ember-htmlbars/tests/system/lookup-helper_test.js b/packages/ember-htmlbars/tests/system/lookup-helper_test.js index 69bc1056ec5..b1b938c4bbb 100644 --- a/packages/ember-htmlbars/tests/system/lookup-helper_test.js +++ b/packages/ember-htmlbars/tests/system/lookup-helper_test.js @@ -66,7 +66,7 @@ QUnit.test('does a lookup in the container if the name contains a dash (and help }; var someName = Helper.extend(); - view.container._registry.register('helper:some-name', someName); + view.container.registry.register('helper:some-name', someName); var actual = lookupHelper('some-name', view, env); @@ -82,7 +82,7 @@ QUnit.test('does a lookup in the container if the name is found in knownHelpers' env.knownHelpers['t'] = true; var t = Helper.extend(); - view.container._registry.register('helper:t', t); + view.container.registry.register('helper:t', t); var actual = lookupHelper('t', view, env); @@ -101,7 +101,7 @@ QUnit.test('looks up a shorthand helper in the container', function() { function someName() { called = true; } - view.container._registry.register('helper:some-name', makeHelper(someName)); + view.container.registry.register('helper:some-name', makeHelper(someName)); var actual = lookupHelper('some-name', view, env); @@ -121,7 +121,7 @@ QUnit.test('fails with a useful error when resolving a function', function() { }; function someName() {} - view.container._registry.register('helper:some-name', someName); + view.container.registry.register('helper:some-name', someName); var actual; expectDeprecation(function() { diff --git a/packages/ember-routing-htmlbars/lib/keywords/render.js b/packages/ember-routing-htmlbars/lib/keywords/render.js index 1b26abfc41d..c08a00bcd57 100644 --- a/packages/ember-routing-htmlbars/lib/keywords/render.js +++ b/packages/ember-routing-htmlbars/lib/keywords/render.js @@ -77,7 +77,7 @@ export default { Ember.assert( 'You used `{{render \'' + name + '\'}}`, but \'' + name + '\' can not be ' + 'found as either a template or a view.', - container._registry.has('view:' + name) || container._registry.has(templateName) || !!template + container.registry.has('view:' + name) || container.registry.has(templateName) || !!template ); var view = container.lookup('view:' + name); @@ -105,7 +105,7 @@ export default { Ember.assert( 'The controller name you supplied \'' + controllerName + '\' ' + 'did not resolve to a controller.', - container._registry.has(controllerFullName) + container.registry.has(controllerFullName) ); } else { controllerName = name; diff --git a/packages/ember-routing-htmlbars/tests/helpers/render_test.js b/packages/ember-routing-htmlbars/tests/helpers/render_test.js index fbb527c5bee..51790b4d614 100644 --- a/packages/ember-routing-htmlbars/tests/helpers/render_test.js +++ b/packages/ember-routing-htmlbars/tests/helpers/render_test.js @@ -93,7 +93,7 @@ QUnit.test('{{render}} helper should have assertion if neither template nor view QUnit.test('{{render}} helper should not have assertion if template is supplied in block-form', function() { var template = '

HI

{{#render \'good\'}} {{name}}{{/render}}'; var controller = EmberController.extend({ container: container }); - container._registry.register('controller:good', EmberController.extend({ name: 'Rob' })); + container.registry.register('controller:good', EmberController.extend({ name: 'Rob' })); view = EmberView.create({ container: container, controller: controller.create(), @@ -114,7 +114,7 @@ QUnit.test('{{render}} helper should not have assertion if view exists without a template: compile(template) }); - container._registry.register('view:oops', EmberView.extend()); + container.registry.register('view:oops', EmberView.extend()); runAppend(view); @@ -148,7 +148,7 @@ QUnit.test('{{render}} helper should render given template with a supplied model postController = this; } }); - container._registry.register('controller:post', PostController); + container.registry.register('controller:post', PostController); Ember.TEMPLATES['post'] = compile('

{{model.title}}

'); @@ -184,7 +184,7 @@ QUnit.test('{{render}} helper with a supplied model should not fire observers on }) }); - container._registry.register('controller:post', PostController); + container.registry.register('controller:post', PostController); Ember.TEMPLATES['post'] = compile('

{{title}}

'); @@ -196,7 +196,7 @@ QUnit.test('{{render}} helper with a supplied model should not fire observers on QUnit.test('{{render}} helper should raise an error when a given controller name does not resolve to a controller', function() { var template = '

HI

{{render "home" controller="postss"}}'; var controller = EmberController.extend({ container: container }); - container._registry.register('controller:posts', EmberController.extend()); + container.registry.register('controller:posts', EmberController.extend()); view = EmberView.create({ container: container, controller: controller.create(), @@ -214,7 +214,7 @@ QUnit.test('{{render}} helper should render with given controller', function() { var template = '{{render "home" controller="posts"}}'; var controller = EmberController.extend({ container: container }); var id = 0; - container._registry.register('controller:posts', EmberController.extend({ + container.registry.register('controller:posts', EmberController.extend({ init() { this._super.apply(this, arguments); this.uniqueId = id++; @@ -285,7 +285,7 @@ QUnit.test('{{render}} helper should render templates with models multiple times } } }); - container._registry.register('controller:post', PostController, { singleton: false }); + container.registry.register('controller:post', PostController, { singleton: false }); Ember.TEMPLATES['post'] = compile('

{{model.title}}

'); @@ -327,7 +327,7 @@ QUnit.test('{{render}} helper should not leak controllers', function() { postController = this; } }); - container._registry.register('controller:post', PostController); + container.registry.register('controller:post', PostController); Ember.TEMPLATES['post'] = compile('

{{title}}

'); @@ -361,7 +361,7 @@ QUnit.test('{{render}} helper should not treat invocations with falsy contexts a } } }); - container._registry.register('controller:post', PostController, { singleton: false }); + container.registry.register('controller:post', PostController, { singleton: false }); Ember.TEMPLATES['post'] = compile('

{{#unless model}}NOTHING{{/unless}}

'); @@ -402,7 +402,7 @@ QUnit.test('{{render}} helper should render templates both with and without mode } } }); - container._registry.register('controller:post', PostController, { singleton: false }); + container.registry.register('controller:post', PostController, { singleton: false }); Ember.TEMPLATES['post'] = compile('

Title:{{model.title}}

'); @@ -432,7 +432,7 @@ QUnit.test('{{render}} helper should link child controllers to the parent contro role: 'Mom' }); - container._registry.register('controller:posts', EmberController.extend()); + container.registry.register('controller:posts', EmberController.extend()); view = EmberView.create({ container: container, @@ -512,7 +512,7 @@ QUnit.test('{{render}} works with dot notation', function() { this.uniqueId = id++; } }); - container._registry.register('controller:blog.post', BlogPostController); + container.registry.register('controller:blog.post', BlogPostController); view = EmberView.create({ container: container, @@ -569,10 +569,10 @@ QUnit.test('{{render}} helper should let view provide its own template', functio template: compile(template) }); - container._registry.register('template:fish', compile('Hello fish!')); - container._registry.register('template:other', compile('Hello other!')); + container.registry.register('template:fish', compile('Hello fish!')); + container.registry.register('template:other', compile('Hello other!')); - container._registry.register('view:fish', EmberView.extend({ + container.registry.register('view:fish', EmberView.extend({ templateName: 'other' })); @@ -590,9 +590,9 @@ QUnit.test('{{render}} helper should not require view to provide its own templat template: compile(template) }); - container._registry.register('template:fish', compile('Hello fish!')); + container.registry.register('template:fish', compile('Hello fish!')); - container._registry.register('view:fish', EmberView.extend()); + container.registry.register('view:fish', EmberView.extend()); runAppend(view); diff --git a/packages/ember-routing/lib/initializers/routing-service.js b/packages/ember-routing/lib/initializers/routing-service.js index 747e62c6bc6..85f6ebdf52e 100644 --- a/packages/ember-routing/lib/initializers/routing-service.js +++ b/packages/ember-routing/lib/initializers/routing-service.js @@ -1,14 +1,27 @@ import { onLoad } from 'ember-runtime/system/lazy_load'; import RoutingService from 'ember-routing/services/routing'; +import isEnabled from 'ember-metal/features'; + +let initialize; +if (isEnabled('ember-registry-container-reform')) { + initialize = function(application) { + // Register the routing service... + application.register('service:-routing', RoutingService); + // Then inject the app router into it + application.inject('service:-routing', 'router', 'router:main'); + }; +} else { + initialize = function(registry, application) { + // Register the routing service... + registry.register('service:-routing', RoutingService); + // Then inject the app router into it + registry.injection('service:-routing', 'router', 'router:main'); + }; +} onLoad('Ember.Application', function(Application) { Application.initializer({ name: 'routing-service', - initialize(registry) { - // Register the routing service... - registry.register('service:-routing', RoutingService); - // Then inject the app router into it - registry.injection('service:-routing', 'router', 'router:main'); - } + initialize }); }); diff --git a/packages/ember-routing/lib/system/generate_controller.js b/packages/ember-routing/lib/system/generate_controller.js index 1d4d959da90..6b1de503094 100644 --- a/packages/ember-routing/lib/system/generate_controller.js +++ b/packages/ember-routing/lib/system/generate_controller.js @@ -26,7 +26,7 @@ export function generateControllerFactory(container, controllerName, context) { fullName = `controller:${controllerName}`; - container._registry.register(fullName, Factory); + container.registry.register(fullName, Factory); return Factory; } diff --git a/packages/ember-routing/lib/system/router.js b/packages/ember-routing/lib/system/router.js index 49489e1742a..52740d494f4 100644 --- a/packages/ember-routing/lib/system/router.js +++ b/packages/ember-routing/lib/system/router.js @@ -479,7 +479,7 @@ var EmberRouter = EmberObject.extend(Evented, { seen[name] = true; if (!handler) { - container._registry.register(routeName, DefaultRoute.extend()); + container.registry.register(routeName, DefaultRoute.extend()); handler = container.lookup(routeName); if (get(this, 'namespace.LOG_ACTIVE_GENERATION')) { @@ -832,7 +832,7 @@ function findChildRouteName(parentRoute, originatingChildRoute, name) { function routeHasBeenDefined(router, name) { var container = router.container; return router.hasRoute(name) && - (container._registry.has(`template:${name}`) || container._registry.has(`route:${name}`)); + (container.registry.has(`template:${name}`) || container.registry.has(`route:${name}`)); } function triggerEvent(handlerInfos, ignoreFailure, args) { diff --git a/packages/ember-routing/tests/system/controller_for_test.js b/packages/ember-routing/tests/system/controller_for_test.js index d8bfb54939a..bdb2c2f7cae 100644 --- a/packages/ember-routing/tests/system/controller_for_test.js +++ b/packages/ember-routing/tests/system/controller_for_test.js @@ -50,7 +50,7 @@ QUnit.module('Ember.controllerFor', { setup() { namespace = Namespace.create(); container = buildContainer(namespace); - container._registry.register('controller:app', Controller.extend()); + container.registry.register('controller:app', Controller.extend()); appController = container.lookup('controller:app'); }, teardown() { diff --git a/packages/ember-runtime/lib/mixins/container_proxy.js b/packages/ember-runtime/lib/mixins/container_proxy.js new file mode 100644 index 00000000000..3c308918b0f --- /dev/null +++ b/packages/ember-runtime/lib/mixins/container_proxy.js @@ -0,0 +1,87 @@ +import run from 'ember-metal/run_loop'; +import { get } from 'ember-metal/property_get'; +import { Mixin } from 'ember-metal/mixin'; + +export default Mixin.create({ + /** + The container stores state. + + @private + @property {Ember.Container} __container__ + */ + __container__: null, + + /** + Given a fullName return a corresponding instance. + + The default behaviour is for lookup to return a singleton instance. + The singleton is scoped to the container, allowing multiple containers + to all have their own locally scoped singletons. + + ```javascript + var registry = new Registry(); + var container = registry.container(); + + registry.register('api:twitter', Twitter); + + var twitter = container.lookup('api:twitter'); + + twitter instanceof Twitter; // => true + + // by default the container will return singletons + var twitter2 = container.lookup('api:twitter'); + twitter2 instanceof Twitter; // => true + + twitter === twitter2; //=> true + ``` + + If singletons are not wanted an optional flag can be provided at lookup. + + ```javascript + var registry = new Registry(); + var container = registry.container(); + + registry.register('api:twitter', Twitter); + + var twitter = container.lookup('api:twitter', { singleton: false }); + var twitter2 = container.lookup('api:twitter', { singleton: false }); + + twitter === twitter2; //=> false + ``` + + @public + @method lookup + @param {String} fullName + @param {Object} options + @return {any} + */ + lookup: containerAlias('lookup'), + + /** + Given a fullName return the corresponding factory. + + @private + @method lookupFactory + @param {String} fullName + @return {any} + */ + lookupFactory: containerAlias('lookupFactory'), + + /** + @private + */ + willDestroy() { + this._super(...arguments); + + if (this.__container__) { + run(this.__container__, 'destroy'); + } + } +}); + +function containerAlias(name) { + return function () { + var container = get(this, '__container__'); + return container[name](...arguments); + }; +} diff --git a/packages/ember-runtime/lib/mixins/registry_proxy.js b/packages/ember-runtime/lib/mixins/registry_proxy.js new file mode 100644 index 00000000000..2ba5c570958 --- /dev/null +++ b/packages/ember-runtime/lib/mixins/registry_proxy.js @@ -0,0 +1,250 @@ +import { get } from 'ember-metal/property_get'; +import { Mixin } from 'ember-metal/mixin'; + +export default Mixin.create({ + __registry__: null, + + /** + Given a fullName return the corresponding factory. + + @public + @method resolveRegistration + @param {String} fullName + @return {Function} fullName's factory + */ + resolveRegistration: registryAlias('resolve'), + + /** + Registers a factory that can be used for dependency injection (with + `inject`) or for service lookup. Each factory is registered with + a full name including two parts: `type:name`. + + A simple example: + + ```javascript + var App = Ember.Application.create(); + + App.Orange = Ember.Object.extend(); + App.register('fruit:favorite', App.Orange); + ``` + + Ember will resolve factories from the `App` namespace automatically. + For example `App.CarsController` will be discovered and returned if + an application requests `controller:cars`. + + An example of registering a controller with a non-standard name: + + ```javascript + var App = Ember.Application.create(); + var Session = Ember.Controller.extend(); + + App.register('controller:session', Session); + + // The Session controller can now be treated like a normal controller, + // despite its non-standard name. + App.ApplicationController = Ember.Controller.extend({ + needs: ['session'] + }); + ``` + + Registered factories are **instantiated** by having `create` + called on them. Additionally they are **singletons**, each time + they are looked up they return the same instance. + + Some examples modifying that default behavior: + + ```javascript + var App = Ember.Application.create(); + + App.Person = Ember.Object.extend(); + App.Orange = Ember.Object.extend(); + App.Email = Ember.Object.extend(); + App.session = Ember.Object.create(); + + App.register('model:user', App.Person, { singleton: false }); + App.register('fruit:favorite', App.Orange); + App.register('communication:main', App.Email, { singleton: false }); + App.register('session', App.session, { instantiate: false }); + ``` + + @public + @method register + @param fullName {String} type:name (e.g., 'model:user') + @param factory {Function} (e.g., App.Person) + @param options {Object} (optional) disable instantiation or singleton usage + @public + */ + register: registryAlias('register'), + + /** + Unregister a factory. + + ```javascript + var App = Ember.Application.create(); + var User = Ember.Object.extend(); + App.register('model:user', User); + + App.resolveRegistration('model:user').create() instanceof User //=> true + + App.unregister('model:user') + App.resolveRegistration('model:user') === undefined //=> true + ``` + + @public + @method unregister + @param {String} fullName + */ + unregister: registryAlias('unregister'), + + /** + Check if a factory is registered. + + @public + @method hasRegistration + @param {String} fullName + @return {Boolean} + */ + hasRegistration: registryAlias('has'), + + /** + Register an option for a particular factory. + + @public + @method registerOption + @param {String} fullName + @param {String} optionName + @param {Object} options + */ + registerOption: registryAlias('option'), + + /** + Return a specific registered option for a particular factory. + + @public + @method registeredOption + @param {String} fullName + @param {String} optionName + @return {Object} options + */ + registeredOption: registryAlias('getOption'), + + /** + Register options for a particular factory. + + @public + @method registerOptions + @param {String} fullName + @param {Object} options + */ + registerOptions: registryAlias('options'), + + /** + Return registered options for a particular factory. + + @public + @method registeredOptions + @param {String} fullName + @return {Object} options + */ + registeredOptions: registryAlias('getOptions'), + + /** + Allow registering options for all factories of a type. + + ```javascript + var App = Ember.Application.create(); + var appInstance = App.buildInstance(); + + // if all of type `connection` must not be singletons + appInstance.optionsForType('connection', { singleton: false }); + + appInstance.register('connection:twitter', TwitterConnection); + appInstance.register('connection:facebook', FacebookConnection); + + var twitter = appInstance.lookup('connection:twitter'); + var twitter2 = appInstance.lookup('connection:twitter'); + + twitter === twitter2; // => false + + var facebook = appInstance.lookup('connection:facebook'); + var facebook2 = appInstance.lookup('connection:facebook'); + + facebook === facebook2; // => false + ``` + + @public + @method registerOptionsForType + @param {String} type + @param {Object} options + */ + registerOptionsForType: registryAlias('optionsForType'), + + /** + Return the registered options for all factories of a type. + + @public + @method registeredOptionsForType + @param {String} type + @return {Object} options + */ + registeredOptionsForType: registryAlias('getOptionsForType'), + + /** + Define a dependency injection onto a specific factory or all factories + of a type. + + When Ember instantiates a controller, view, or other framework component + it can attach a dependency to that component. This is often used to + provide services to a set of framework components. + + An example of providing a session object to all controllers: + + ```javascript + var App = Ember.Application.create(); + var Session = Ember.Object.extend({ isAuthenticated: false }); + + // A factory must be registered before it can be injected + App.register('session:main', Session); + + // Inject 'session:main' onto all factories of the type 'controller' + // with the name 'session' + App.inject('controller', 'session', 'session:main'); + + App.IndexController = Ember.Controller.extend({ + isLoggedIn: Ember.computed.alias('session.isAuthenticated') + }); + ``` + + Injections can also be performed on specific factories. + + ```javascript + App.inject(, , ) + App.inject('route', 'source', 'source:main') + App.inject('route:application', 'email', 'model:email') + ``` + + It is important to note that injections can only be performed on + classes that are instantiated by Ember itself. Instantiating a class + directly (via `create` or `new`) bypasses the dependency injection + system. + + **Note:** Ember-Data instantiates its models in a unique manner, and consequently + injections onto models (or all models) will not work as expected. Injections + on models can be enabled by setting `Ember.MODEL_FACTORY_INJECTIONS` + to `true`. + + @public + @method inject + @param factoryNameOrType {String} + @param property {String} + @param injectionName {String} + **/ + inject: registryAlias('injection') +}); + +function registryAlias(name) { + return function () { + var registry = get(this, '__registry__'); + return registry[name](...arguments); + }; +} diff --git a/packages/ember-testing/lib/initializers.js b/packages/ember-testing/lib/initializers.js index a20aef46edc..252c905f1cb 100644 --- a/packages/ember-testing/lib/initializers.js +++ b/packages/ember-testing/lib/initializers.js @@ -7,7 +7,7 @@ onLoad('Ember.Application', function(Application) { Application.initializer({ name: name, - initialize(registry, application) { + initialize(application) { if (application.testing) { application.deferReadiness(); } diff --git a/packages/ember-views/lib/component_lookup.js b/packages/ember-views/lib/component_lookup.js index e3d842892ec..8ab5b1f0fce 100644 --- a/packages/ember-views/lib/component_lookup.js +++ b/packages/ember-views/lib/component_lookup.js @@ -15,10 +15,10 @@ export default EmberObject.extend({ var fullName = 'component:' + name; var templateFullName = 'template:components/' + name; - var templateRegistered = container && container._registry.has(templateFullName); + var templateRegistered = container && container.registry.has(templateFullName); if (templateRegistered) { - container._registry.injection(fullName, 'layout', templateFullName); + container.registry.injection(fullName, 'layout', templateFullName); } var Component = container.lookupFactory(fullName); @@ -27,7 +27,7 @@ export default EmberObject.extend({ // or a template has been registered. if (templateRegistered || Component) { if (!Component) { - container._registry.register(fullName, Ember.Component); + container.registry.register(fullName, Ember.Component); Component = container.lookupFactory(fullName); } return Component; diff --git a/packages/ember/tests/component_registration_test.js b/packages/ember/tests/component_registration_test.js index 834ff8c12b1..96e16b8a225 100644 --- a/packages/ember/tests/component_registration_test.js +++ b/packages/ember/tests/component_registration_test.js @@ -56,7 +56,7 @@ function boot(callback, startURL='/') { location: 'none' }); - registry = App.registry; + registry = App.__registry__; container = App.__container__; if (callback) { callback(); } diff --git a/packages/ember/tests/default_initializers_test.js b/packages/ember/tests/default_initializers_test.js index 8c79d1f32f5..777aca7ecd8 100644 --- a/packages/ember/tests/default_initializers_test.js +++ b/packages/ember/tests/default_initializers_test.js @@ -26,10 +26,8 @@ QUnit.test('Default objects are registered', function(assert) { App.instanceInitializer({ name: 'test', initialize(instance) { - var registry = instance.registry; - - assert.strictEqual(registry.resolve('component:-text-field'), TextField, 'TextField was registered'); - assert.strictEqual(registry.resolve('component:-checkbox'), Checkbox, 'Checkbox was registered'); + assert.strictEqual(instance.resolveRegistration('component:-text-field'), TextField, 'TextField was registered'); + assert.strictEqual(instance.resolveRegistration('component:-checkbox'), Checkbox, 'Checkbox was registered'); } }); diff --git a/packages/ember/tests/helpers/helper_registration_test.js b/packages/ember/tests/helpers/helper_registration_test.js index 1b5765bced5..d8b718ae394 100644 --- a/packages/ember/tests/helpers/helper_registration_test.js +++ b/packages/ember/tests/helpers/helper_registration_test.js @@ -51,7 +51,7 @@ var boot = function(callback) { location: 'none' }); - registry = App.registry; + registry = App.__registry__; container = App.__container__; if (callback) { callback(); } diff --git a/packages/ember/tests/helpers/link_to_test.js b/packages/ember/tests/helpers/link_to_test.js index 02553b01bdc..d8eaa90281a 100644 --- a/packages/ember/tests/helpers/link_to_test.js +++ b/packages/ember/tests/helpers/link_to_test.js @@ -60,7 +60,7 @@ function sharedSetup() { }); Router = App.Router; - registry = App.registry; + registry = App.__registry__; container = App.__container__; } diff --git a/packages/ember/tests/helpers/link_to_test/link_to_transitioning_classes_test.js b/packages/ember/tests/helpers/link_to_test/link_to_transitioning_classes_test.js index 1df44eeb81e..c3db7c38c00 100644 --- a/packages/ember/tests/helpers/link_to_test/link_to_transitioning_classes_test.js +++ b/packages/ember/tests/helpers/link_to_test/link_to_transitioning_classes_test.js @@ -66,7 +66,7 @@ function sharedSetup() { }); Router = App.Router; - registry = App.registry; + registry = App.__registry__; container = App.__container__; } diff --git a/packages/ember/tests/helpers/link_to_test/link_to_with_query_params_test.js b/packages/ember/tests/helpers/link_to_test/link_to_with_query_params_test.js index 85bae339905..91a10ae60d6 100644 --- a/packages/ember/tests/helpers/link_to_test/link_to_with_query_params_test.js +++ b/packages/ember/tests/helpers/link_to_test/link_to_with_query_params_test.js @@ -54,7 +54,7 @@ function sharedSetup() { }); Router = App.Router; - registry = App.registry; + registry = App.__registry__; container = App.__container__; } diff --git a/packages/ember/tests/integration/multiple-app-test.js b/packages/ember/tests/integration/multiple-app-test.js index 2d14638de64..bd2a070fa12 100644 --- a/packages/ember/tests/integration/multiple-app-test.js +++ b/packages/ember/tests/integration/multiple-app-test.js @@ -17,7 +17,7 @@ function startApp(rootElement) { location: 'none' }); - var registry = application.__container__._registry; + var registry = application.__registry__; registry.register('component:special-button', Ember.Component.extend({ actions: { diff --git a/packages/ember/tests/integration/view_test.js b/packages/ember/tests/integration/view_test.js index 86b1a2fd915..ad693ddda4a 100644 --- a/packages/ember/tests/integration/view_test.js +++ b/packages/ember/tests/integration/view_test.js @@ -37,7 +37,7 @@ QUnit.module('View Integration', { location: 'none' }); - registry = App.__container__._registry; + registry = App.__registry__; }); setupExample(); diff --git a/packages/ember/tests/routing/basic_test.js b/packages/ember/tests/routing/basic_test.js index 24752c8ef8b..4b082e4dbe4 100644 --- a/packages/ember/tests/routing/basic_test.js +++ b/packages/ember/tests/routing/basic_test.js @@ -68,7 +68,7 @@ QUnit.module('Basic Routing', { App.LoadingRoute = Ember.Route.extend({ }); - registry = App.registry; + registry = App.__registry__; container = App.__container__; Ember.TEMPLATES.application = compile('{{outlet}}'); diff --git a/packages/ember/tests/routing/query_params_test.js b/packages/ember/tests/routing/query_params_test.js index 6c4880627d0..49a1a481d2e 100644 --- a/packages/ember/tests/routing/query_params_test.js +++ b/packages/ember/tests/routing/query_params_test.js @@ -7,7 +7,7 @@ import EmberHandlebars from 'ember-htmlbars/compat'; var compile = EmberHandlebars.compile; -var Router, App, router, registry, container; +var Router, App, router, container; var get = Ember.get; function bootApplication() { @@ -71,10 +71,9 @@ function sharedSetup() { App.deferReadiness(); - registry = App.registry; container = App.__container__; - registry.register('location:test', TestLocation); + App.register('location:test', TestLocation); startingURL = expectedReplaceURL = expectedPushURL = ''; diff --git a/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js b/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js index 4720e06fe98..10dccddbb4a 100644 --- a/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js +++ b/packages/ember/tests/routing/query_params_test/model_dependent_state_with_query_params_test.js @@ -69,7 +69,7 @@ function sharedSetup() { App.deferReadiness(); - registry = App.registry; + registry = App.__registry__; container = App.__container__; registry.register('location:test', TestLocation); diff --git a/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js b/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js index 3cd1dd924de..be4c9bde1d2 100644 --- a/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js +++ b/packages/ember/tests/routing/query_params_test/overlapping_query_params_test.js @@ -57,7 +57,7 @@ function sharedSetup() { App.deferReadiness(); - registry = App.registry; + registry = App.__registry__; container = App.__container__; registry.register('location:test', TestLocation); diff --git a/packages/ember/tests/routing/query_params_test/query_params_paramless_link_to_test.js b/packages/ember/tests/routing/query_params_test/query_params_paramless_link_to_test.js index 031b2b8e27e..27b7c5c7852 100644 --- a/packages/ember/tests/routing/query_params_test/query_params_paramless_link_to_test.js +++ b/packages/ember/tests/routing/query_params_test/query_params_paramless_link_to_test.js @@ -53,7 +53,7 @@ function sharedSetup() { App.deferReadiness(); - registry = App.registry; + registry = App.__registry__; container = App.__container__; registry.register('location:test', TestLocation); diff --git a/packages/ember/tests/routing/toplevel_dom_test.js b/packages/ember/tests/routing/toplevel_dom_test.js index 88749154d04..c1dba67785a 100644 --- a/packages/ember/tests/routing/toplevel_dom_test.js +++ b/packages/ember/tests/routing/toplevel_dom_test.js @@ -59,7 +59,7 @@ QUnit.test('Topmost template always get an element', function() { }); QUnit.test('If topmost view has its own element, it doesn\'t get wrapped in a higher element', function() { - App.registry.register('view:application', EmberView.extend({ + App.register('view:application', EmberView.extend({ classNames: ['im-special'] })); bootApplication(); diff --git a/tests/node/app-boot-test.js b/tests/node/app-boot-test.js index 8fd3563e8a3..2304232bcd1 100644 --- a/tests/node/app-boot-test.js +++ b/tests/node/app-boot-test.js @@ -63,7 +63,7 @@ function registerDOMHelper(app) { app.instanceInitializer({ name: 'register-dom-helper', initialize: function(app) { - app.registry.register('renderer:-dom', { + app.register('renderer:-dom', { create: function() { return new Ember._Renderer(domHelper, false); } @@ -78,7 +78,7 @@ function registerTemplates(app, templates) { initialize: function(app) { for (var key in templates) { - app.registry.register('template:' + key, compile(templates[key])); + app.register('template:' + key, compile(templates[key])); } } }); @@ -90,7 +90,7 @@ function registerControllers(app, controllers) { initialize: function(app) { for (var key in controllers) { - app.registry.register('controller:' + key, controllers[key]); + app.register('controller:' + key, controllers[key]); } } });