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

[FEATURE ember-container-inject-owner] Inject fake container with deprecations. #12609

Merged
merged 1 commit into from
Nov 16, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions packages/container/lib/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { assert, deprecate } from 'ember-metal/debug';
import dictionary from 'ember-metal/dictionary';
import isEnabled from 'ember-metal/features';
import { setOwner } from './owner';
import { buildFakeContainerWithDeprecations } from 'ember-runtime/mixins/container_proxy';

/**
A container used to instantiate and cache objects.
Expand All @@ -23,6 +24,10 @@ function Container(registry, options) {
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);

if (isEnabled('ember-container-inject-owner')) {
this._fakeContainerToInject = buildFakeContainerWithDeprecations(this);
}
}

Container.prototype = {
Expand Down Expand Up @@ -243,8 +248,11 @@ function factoryFor(container, fullName) {

var injectedFactory = factory.extend(injections);

// TODO - remove all `container` injections when Ember reaches v3.0.0
if (isEnabled('ember-container-inject-owner')) {
injectDeprecatedContainer(injectedFactory.prototype, container);
} else {
injectedFactory.prototype.container = container;
}

injectedFactory.reopenClass(factoryInjections);
Expand Down Expand Up @@ -273,12 +281,6 @@ function injectionsFor(container, fullName) {

setOwner(injections, container.owner);

// TODO - Inject a `FakeContainer` instead here. The `FakeContainer` will
// proxy all properties of the container with deprecations.
if (!isEnabled('ember-container-inject-owner')) {
injections.container = container;
}

return injections;
}

Expand Down Expand Up @@ -330,8 +332,21 @@ function instantiate(container, fullName) {
// assume the factory was extendable
// to create time injections
// TODO: support new'ing for instantiation and merge injections for pure JS Functions
obj = factory.create(injectionsFor(container, fullName));
let injections = injectionsFor(container, fullName);

// Ensure that a container is available to an object during instantiation.
// TODO - remove when Ember reaches v3.0.0
if (isEnabled('ember-container-inject-owner')) {
// This "fake" container will be replaced after instantiation with a
// property that raises deprecations every time it is accessed.
injections.container = container._fakeContainerToInject;
} else {
injections.container = container;
}

obj = factory.create(injections);

// TODO - remove when Ember reaches v3.0.0
if (isEnabled('ember-container-inject-owner')) {
injectDeprecatedContainer(obj, container);
}
Expand Down
50 changes: 49 additions & 1 deletion packages/container/tests/container_test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Ember from 'ember-metal/core';
import Registry from 'container/registry';
import factory from 'container/tests/test-helpers/factory';
import { getOwner } from 'container/owner';
import isEnabled from 'ember-metal/features';

var originalModelInjections;
Expand Down Expand Up @@ -519,7 +520,7 @@ QUnit.test('Lazy injection validations are cached', function() {
});

if (isEnabled('ember-container-inject-owner')) {
QUnit.test('A deprecated `container` property is appended to every instantiated object', function() {
QUnit.test('A deprecated `container` property is appended to every object instantiated from an extendable factory', function() {
let registry = new Registry();
let container = registry.container();
let PostController = factory();
Expand All @@ -535,6 +536,53 @@ if (isEnabled('ember-container-inject-owner')) {
strictEqual(c, container);
}, 'Using the injected `container` is deprecated. Please use the `getOwner` helper instead to access the owner of this object.');
});

QUnit.test('A deprecated `container` property is appended to every object instantiated from a non-extendable factory, and a fake container is available during instantiation.', function() {
expect(8);

let owner = {};
let registry = new Registry();
let container = registry.container({ owner });

// Define a simple non-extendable factory
let PostController = function() {};
PostController.create = function(options) {
ok(options.container, 'fake container has been injected and is available during `create`.');

expectDeprecation(function() {
options.container.lookup('abc:one');
}, 'Using the injected `container` is deprecated. Please use the `getOwner` helper to access the owner of this object and then call `lookup` instead.');

expectDeprecation(function() {
options.container.lookupFactory('abc:two');
}, 'Using the injected `container` is deprecated. Please use the `getOwner` helper to access the owner of this object and then call `_lookupFactory` instead.');

// non-deprecated usage of `lookup` and `_lookupFactory`
owner.lookup = function(fullName) {
equal(fullName, 'abc:one', 'lookup on owner called properly');
};
owner._lookupFactory = function(fullName) {
equal(fullName, 'abc:two', '_lookupFactory on owner called properly');
};
let foundOwner = getOwner(options);
foundOwner.lookup('abc:one');
foundOwner._lookupFactory('abc:two');

return new PostController(options);
};

registry.register('controller:post', PostController);
let postController = container.lookup('controller:post');

expectDeprecation(function() {
Ember.get(postController, 'container');
}, 'Using the injected `container` is deprecated. Please use the `getOwner` helper instead to access the owner of this object.');

expectDeprecation(function() {
let c = postController.container;
strictEqual(c, container, 'Injected container is now regular (not fake) container, but access is still deprecated.');
}, 'Using the injected `container` is deprecated. Please use the `getOwner` helper instead to access the owner of this object.');
});
} else {
QUnit.test('A `container` property is appended to every instantiated object', function() {
let registry = new Registry();
Expand Down
30 changes: 30 additions & 0 deletions packages/ember-runtime/lib/mixins/container_proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@submodule ember-runtime
*/
import run from 'ember-metal/run_loop';
import { deprecate } from 'ember-metal/debug';
import { Mixin } from 'ember-metal/mixin';


Expand Down Expand Up @@ -95,3 +96,32 @@ function containerAlias(name) {
return this.__container__[name](...arguments);
};
}

export function buildFakeContainerWithDeprecations(container) {
let fakeContainer = {};
let propertyMappings = {
lookup: 'lookup',
lookupFactory: '_lookupFactory'
};

for (let containerProperty in propertyMappings) {
fakeContainer[containerProperty] = buildFakeContainerFunction(container, containerProperty, propertyMappings[containerProperty]);
}

return fakeContainer;
}

function buildFakeContainerFunction(container, containerProperty, ownerProperty) {
return function() {
deprecate(
`Using the injected \`container\` is deprecated. Please use the \`getOwner\` helper to access the owner of this object and then call \`${ownerProperty}\` instead.`,
false,
{
id: 'ember-application.injected-container',
until: '3.0.0',
url: 'http://emberjs.com/deprecations/v2.x#toc_injected-container-access'
}
);
return container[containerProperty](...arguments);
};
}