Skip to content

Commit

Permalink
Use public API for container/registry when possible.
Browse files Browse the repository at this point in the history
Ember 2.3 will include `Ember.getOwner` and `Ember.setOwner` as public
API's to access various registry and container features. As of Ember
2.3, using `this.container` in any instance looked up from the container
will issue a deprecation (instructing the user to use
`Ember.getOwner(this)` instead).

This PR fixes all deprecations against ember#canary channel by adding a
simplified polyfill so that the internal code can always operate with
the `Ember.getOwner` API.
  • Loading branch information
rwjblue committed Nov 10, 2015
1 parent f1ccad8 commit 969fddc
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 30 deletions.
8 changes: 7 additions & 1 deletion packages/ember-data/lib/serializers/json-serializer.js
Expand Up @@ -3,6 +3,10 @@ import coerceId from "ember-data/system/coerce-id";
import normalizeModelName from "ember-data/system/normalize-model-name";
import { modelHasAttributeOrRelationshipNamedType } from "ember-data/utils";

import {
getOwner
} from 'ember-data/utils';

import { errorsArrayToHash } from "ember-data/adapters/errors";

var get = Ember.get;
Expand Down Expand Up @@ -1366,8 +1370,10 @@ export default Serializer.extend({
@return {DS.Transform} transform
*/
transformFor: function(attributeType, skipAssertion) {
var transform = this.container.lookup('transform:' + attributeType);
var transform = getOwner(this).lookup('transform:' + attributeType);

Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform);

return transform;
}
});
23 changes: 18 additions & 5 deletions packages/ember-data/lib/system/model/internal-model.js
Expand Up @@ -4,6 +4,10 @@ import Relationships from "ember-data/system/relationships/state/create";
import Snapshot from "ember-data/system/snapshot";
import EmptyObject from "ember-data/system/empty-object";

import {
getOwner
} from 'ember-data/utils';

var Promise = Ember.RSVP.Promise;
var get = Ember.get;
var set = Ember.set;
Expand Down Expand Up @@ -46,11 +50,10 @@ var guid = 0;
@class InternalModel
*/

export default function InternalModel(type, id, store, container, data) {
export default function InternalModel(type, id, store, _, data) {
this.type = type;
this.id = id;
this.store = store;
this.container = container;
this._data = data || new EmptyObject();
this.modelName = type.modelName;
this.dataHasInitialized = false;
Expand Down Expand Up @@ -110,17 +113,27 @@ InternalModel.prototype = {
constructor: InternalModel,
materializeRecord: function() {
Ember.assert("Materialized " + this.modelName + " record with id:" + this.id + "more than once", this.record === null || this.record === undefined);

// lookupFactory should really return an object that creates
// instances with the injections applied
this.record = this.type._create({
var createOptions = {
store: this.store,
container: this.container,
_internalModel: this,
id: this.id,
currentState: get(this, 'currentState'),
isError: this.isError,
adapterError: this.error
});
};

if (Ember.setOwner) {
// ensure that `Ember.getOwner(this)` works inside a model instance
Ember.setOwner(createOptions, getOwner(this.store));
} else {
createOptions.container = this.store.container;
}

this.record = this.type._create(createOptions);

this._triggerDeferredTriggers();
},

Expand Down
19 changes: 19 additions & 0 deletions packages/ember-data/lib/system/model/model.js
Expand Up @@ -843,4 +843,23 @@ Model.reopenClass({
modelName: null
});

// if `Ember.setOwner` is defined, accessing `this.container` is
// deprecated (but functional). In "standard" Ember usage, this
// deprecation is actually created via an `.extend` of the factory
// inside the container itself, but that only happens on models
// with MODEL_FACTORY_INJECTIONS enabled :(
if (Ember.setOwner) {
Object.defineProperty(Model.prototype, 'container', {
configurable: true,
enumerable: false,
get() {
Ember.deprecate('Using the injected `container` is deprecated. Please use the `getOwner` helper instead to access the owner of this object.',
false,
{ id: 'ember-application.injected-container', until: '3.0.0' });

return this.store.container;
}
});
}

export default Model;
22 changes: 15 additions & 7 deletions packages/ember-data/lib/system/store.js
Expand Up @@ -40,6 +40,10 @@ import {
_queryRecord
} from "ember-data/system/store/finders";

import {
getOwner
} from 'ember-data/utils';

import coerceId from "ember-data/system/coerce-id";

import RecordArrayManager from "ember-data/system/record-array-manager";
Expand Down Expand Up @@ -220,7 +224,7 @@ Store = Service.extend({
store: this
});
this._pendingSave = [];
this._instanceCache = new ContainerInstanceCache(this.container);
this._instanceCache = new ContainerInstanceCache(getOwner(this));
//Used to keep track of all the find requests that need to be coalesced
this._pendingFetch = Map.create();
},
Expand Down Expand Up @@ -1475,11 +1479,12 @@ Store = Service.extend({
// container.registry = 2.1
// container._registry = 1.11 - 2.0
// container = < 1.11
var registry = this.container.registry || this.container._registry || this.container;
var mixin = this.container.lookupFactory('mixin:' + normalizedModelName);
var owner = getOwner(this);

var mixin = owner._lookupFactory('mixin:' + normalizedModelName);
if (mixin) {
//Cache the class as a model
registry.register('model:' + normalizedModelName, DS.Model.extend(mixin));
owner.register('model:' + normalizedModelName, DS.Model.extend(mixin));
}
var factory = this.modelFactoryFor(normalizedModelName);
if (factory) {
Expand Down Expand Up @@ -1518,7 +1523,10 @@ Store = Service.extend({
modelFactoryFor: function(modelName) {
Ember.assert('Passing classes to store methods has been removed. Please pass a dasherized string instead of '+ Ember.inspect(modelName), typeof modelName === 'string');
var normalizedKey = normalizeModelName(modelName);
return this.container.lookupFactory('model:' + normalizedKey);

var owner = getOwner(this);

return owner._lookupFactory('model:' + normalizedKey);
},

/**
Expand Down Expand Up @@ -1701,7 +1709,7 @@ Store = Service.extend({
},

_hasModelFor: function(type) {
return this.container.lookupFactory(`model:${type}`);
return getOwner(this)._lookupFactory(`model:${type}`);
},

_pushInternalModel: function(data) {
Expand Down Expand Up @@ -1867,7 +1875,7 @@ Store = Service.extend({

// lookupFactory should really return an object that creates
// instances with the injections applied
var internalModel = new InternalModel(type, id, this, this.container, data);
var internalModel = new InternalModel(type, id, this, null, data);

// if we're creating an item, this process will be done
// later, once the object has been persisted.
Expand Down
10 changes: 5 additions & 5 deletions packages/ember-data/lib/system/store/container-instance-cache.js
Expand Up @@ -19,9 +19,9 @@ import EmptyObject from "ember-data/system/empty-object";
* @class ContainerInstanceCache
*
*/
export default function ContainerInstanceCache(container) {
this._container = container;
this._cache = new EmptyObject();
export default function ContainerInstanceCache(owner) {
this._owner = owner;
this._cache = new EmptyObject();
}

ContainerInstanceCache.prototype = new EmptyObject();
Expand Down Expand Up @@ -55,7 +55,7 @@ Ember.merge(ContainerInstanceCache.prototype, {
instanceFor: function(key) {
let cache = this._cache;
if (!cache[key]) {
let instance = this._container.lookup(key);
let instance = this._owner.lookup(key);
if (instance) {
cache[key] = instance;
}
Expand All @@ -74,7 +74,7 @@ Ember.merge(ContainerInstanceCache.prototype, {
cacheEntry.destroy();
}
}
this._container = null;
this._owner = null;
},

constructor: ContainerInstanceCache,
Expand Down
32 changes: 31 additions & 1 deletion packages/ember-data/lib/utils.js
Expand Up @@ -54,7 +54,37 @@ function modelHasAttributeOrRelationshipNamedType(modelClass) {
return get(modelClass, 'attributes').has('type') || get(modelClass, 'relationshipsByName').has('type');
}

/*
ember-container-inject-owner is a new feature in Ember 2.3 that finally provides a public
API for looking items up. This function serves as a super simple polyfill to avoid
triggering deprecations.
*/
function getOwner(context) {
var owner;

if (Ember.getOwner) {
owner = Ember.getOwner(context);
}

if (!owner && context.container) {
owner = context.container;
}

if (owner && owner.lookupFactory && !owner._lookupFactory) {
// `owner` is a container, we are just making this work
owner._lookupFactory = owner.lookupFactory;
owner.register = function() {
var registry = owner.registry || owner._registry || owner;

return registry.register(...arguments);
};
}

return owner;
}

export {
assertPolymorphicType,
modelHasAttributeOrRelationshipNamedType
modelHasAttributeOrRelationshipNamedType,
getOwner
};
14 changes: 7 additions & 7 deletions packages/ember-data/tests/unit/model/internal-model-test.js
@@ -1,16 +1,16 @@
module("unit/model/internal-model - Internal Model");

var mockModelFactory = {
_create: function() {
return { trigger: function() {} };
},
function MockModelFactory () { }

eachRelationship: function() {
}
MockModelFactory._create = function() {
return { trigger: function() {} };
};

MockModelFactory.eachRelationship = function() { };

test("Materializing a model twice errors out", function() {
expect(1);
var internalModel = new DS.InternalModel(mockModelFactory, null, null, null);
var internalModel = new DS.InternalModel(MockModelFactory, null, { }, null);

internalModel.materializeRecord();
expectAssertion(function() {
Expand Down
2 changes: 1 addition & 1 deletion packages/ember-data/tests/unit/store/model-for-test.js
Expand Up @@ -13,7 +13,7 @@ module("unit/store/model_for - DS.Store#modelFor", {
"blog.post": DS.Model.extend()
});
store = env.store;
container = store.container;
container = env.container;
registry = env.registry;
},

Expand Down
Expand Up @@ -6,7 +6,7 @@ module("unit/store/serializer_for - DS.Store#serializerFor", {
Person = DS.Model.extend({});
var env = setupStore({ person: Person });
store = env.store;
container = store.container;
container = env.container;
registry = env.registry;
},

Expand Down
26 changes: 24 additions & 2 deletions tests/ember-configuration.js
Expand Up @@ -17,6 +17,8 @@
ENV['ENABLE_OPTIONAL_FEATURES'] = !!QUnit.urlParams.enableoptionalfeatures;
ENV['RAISE_ON_DEPRECATION'] = true;

var Owner;

window.async = function(callback, timeout) {
var timer;
stop();
Expand Down Expand Up @@ -52,13 +54,33 @@
};

window.setupStore = function(options) {
var container, registry;
var container, registry, owner;
var env = {};
options = options || {};

// This is done once upon first setupStore call (we cannot do it eagerly
// because this file is loaded before Ember itself).
if (!Owner) {
if (Ember._RegistryProxyMixin && Ember._ContainerProxyMixin) {
Owner = Ember.Object.extend(Ember._RegistryProxyMixin, Ember._ContainerProxyMixin);
} else {
Owner = Ember.Object.extend();
}
}

if (Ember.Registry) {

registry = env.registry = new Ember.Registry();
container = env.container = registry.container();

owner = Owner.create({
__registry__: registry
});

container = env.container = registry.container({
owner: owner
});

owner.__container__ = container;
} else {
container = env.container = new Ember.Container();
registry = env.registry = container;
Expand Down

0 comments on commit 969fddc

Please sign in to comment.