From 81d7e2dc954945d507bcedbee0e9bcac754c4bf1 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 14:14:00 -0800 Subject: [PATCH 01/13] Remove double extend, condense triple reopenClass to single reopenClass --- addon/-private/system/model/model.js | 33 +++++++++++++--------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index 85e0e053272..51421551c98 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -30,11 +30,11 @@ function intersection (array1, array2) { return result; } -var RESERVED_MODEL_PROPS = [ +const RESERVED_MODEL_PROPS = [ 'currentState', 'data', 'store' ]; -var retrieveFromCurrentState = computed('currentState', function(key) { +const retrieveFromCurrentState = computed('currentState', function(key) { return get(this._internalModel.currentState, key); }).readOnly(); @@ -51,7 +51,7 @@ var retrieveFromCurrentState = computed('currentState', function(key) { @extends Ember.Object @uses Ember.Evented */ -var Model = Ember.Object.extend(Ember.Evented, { +const Model = Ember.Object.extend(Ember.Evented, { _internalModel: null, store: null, @@ -886,7 +886,7 @@ var Model = Ember.Object.extend(Ember.Evented, { @since 2.5.0 @return {BelongsToReference} reference for this relationship */ - belongsTo: function(name) { + belongsTo(name) { return this._internalModel.referenceFor('belongsTo', name); }, @@ -947,14 +947,21 @@ var Model = Ember.Object.extend(Ember.Evented, { @since 2.5.0 @return {HasManyReference} reference for this relationship */ - hasMany: function(name) { + hasMany(name) { return this._internalModel.referenceFor('hasMany', name); }, setId: Ember.observer('id', function () { this._internalModel.setId(this.get('id')); }) -}); +}, + DebuggerInfoMixin, + BelongsToMixin, + DidDefinePropertyMixin, + RelationshipsInstanceMethodsMixin, + HasManyMixin, + AttrInstanceMethodsMixin +); /** @property data @@ -1022,7 +1029,7 @@ Model.reopenClass({ @static */ modelName: null -}); +}, RelationshipsClassMethodsMixin, AttrClassMethodsMixin); // if `Ember.setOwner` is defined, accessing `this.container` is // deprecated (but functional). In "standard" Ember usage, this @@ -1068,14 +1075,4 @@ if (isEnabled('ds-rollback-attribute')) { }); } - -Model.reopenClass(RelationshipsClassMethodsMixin); -Model.reopenClass(AttrClassMethodsMixin); - -export default Model.extend( - DebuggerInfoMixin, - BelongsToMixin, - DidDefinePropertyMixin, - RelationshipsInstanceMethodsMixin, - HasManyMixin, - AttrInstanceMethodsMixin); +export default Model; From 97f21b7a992b4fa8e1e1b00d72c9f056f0275cb1 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 14:35:25 -0800 Subject: [PATCH 02/13] move relationship class methods into the single reopen directly --- addon/-private/system/model/model.js | 469 ++++++++++++++++++++- addon/-private/system/relationships/ext.js | 466 +------------------- 2 files changed, 466 insertions(+), 469 deletions(-) diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index 51421551c98..f1e4e89db9c 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -1,14 +1,15 @@ import Ember from 'ember'; -import { assert, deprecate } from "ember-data/-private/debug"; +import { assert, deprecate, warn } from "ember-data/-private/debug"; import { PromiseObject } from "ember-data/-private/system/promise-proxies"; import Errors from "ember-data/-private/system/model/errors"; import DebuggerInfoMixin from 'ember-data/-private/system/debug/debug-info'; import { BelongsToMixin } from 'ember-data/-private/system/relationships/belongs-to'; import { HasManyMixin } from 'ember-data/-private/system/relationships/has-many'; -import { DidDefinePropertyMixin, RelationshipsClassMethodsMixin, RelationshipsInstanceMethodsMixin } from 'ember-data/-private/system/relationships/ext'; +import { DidDefinePropertyMixin, RelationshipsInstanceMethodsMixin } from 'ember-data/-private/system/relationships/ext'; import { AttrClassMethodsMixin, AttrInstanceMethodsMixin } from 'ember-data/-private/system/model/attr'; import isEnabled from 'ember-data/-private/features'; import RootState from 'ember-data/-private/system/model/states'; +import EmptyObject from "ember-data/-private/system/empty-object"; const { get, @@ -1028,8 +1029,468 @@ Model.reopenClass({ @readonly @static */ - modelName: null -}, RelationshipsClassMethodsMixin, AttrClassMethodsMixin); + modelName: null, + + /* + These class methods below provide relationship + introspection abilities about relationships. + + A note about the computed properties contained here: + + **These properties are effectively sealed once called for the first time.** + To avoid repeatedly doing expensive iteration over a model's fields, these + values are computed once and then cached for the remainder of the runtime of + your application. + + If your application needs to modify a class after its initial definition + (for example, using `reopen()` to add additional attributes), make sure you + do it before using your model with the store, which uses these properties + extensively. + */ + + /** + For a given relationship name, returns the model type of the relationship. + + For example, if you define a model like this: + + ```app/models/post.js + import DS from 'ember-data'; + + export default DS.Model.extend({ + comments: DS.hasMany('comment') + }); + ``` + + Calling `store.modelFor('post').typeForRelationship('comments', store)` will return `Comment`. + + @method typeForRelationship + @static + @param {String} name the name of the relationship + @param {store} store an instance of DS.Store + @return {DS.Model} the type of the relationship, or undefined + */ + typeForRelationship(name, store) { + var relationship = get(this, 'relationshipsByName').get(name); + return relationship && store.modelFor(relationship.type); + }, + + inverseMap: Ember.computed(function() { + return new EmptyObject(); + }), + + /** + Find the relationship which is the inverse of the one asked for. + + For example, if you define models like this: + + ```app/models/post.js + import DS from 'ember-data'; + + export default DS.Model.extend({ + comments: DS.hasMany('message') + }); + ``` + + ```app/models/message.js + import DS from 'ember-data'; + + export default DS.Model.extend({ + owner: DS.belongsTo('post') + }); + ``` + + store.modelFor('post').inverseFor('comments', store) -> { type: App.Message, name: 'owner', kind: 'belongsTo' } + store.modelFor('message').inverseFor('owner', store) -> { type: App.Post, name: 'comments', kind: 'hasMany' } + + @method inverseFor + @static + @param {String} name the name of the relationship + @param {DS.Store} store + @return {Object} the inverse relationship, or null + */ + inverseFor(name, store) { + var inverseMap = get(this, 'inverseMap'); + if (inverseMap[name]) { + return inverseMap[name]; + } else { + var inverse = this._findInverseFor(name, store); + inverseMap[name] = inverse; + return inverse; + } + }, + + //Calculate the inverse, ignoring the cache + _findInverseFor(name, store) { + + var inverseType = this.typeForRelationship(name, store); + if (!inverseType) { + return null; + } + + var propertyMeta = this.metaForProperty(name); + //If inverse is manually specified to be null, like `comments: DS.hasMany('message', { inverse: null })` + var options = propertyMeta.options; + if (options.inverse === null) { return null; } + + var inverseName, inverseKind, inverse; + + //If inverse is specified manually, return the inverse + if (options.inverse) { + inverseName = options.inverse; + inverse = Ember.get(inverseType, 'relationshipsByName').get(inverseName); + + assert("We found no inverse relationships by the name of '" + inverseName + "' on the '" + inverseType.modelName + + "' model. This is most likely due to a missing attribute on your model definition.", !Ember.isNone(inverse)); + + inverseKind = inverse.kind; + } else { + //No inverse was specified manually, we need to use a heuristic to guess one + if (propertyMeta.type === propertyMeta.parentType.modelName) { + warn(`Detected a reflexive relationship by the name of '${name}' without an inverse option. Look at http://emberjs.com/guides/models/defining-models/#toc_reflexive-relation for how to explicitly specify inverses.`, false, { + id: 'ds.model.reflexive-relationship-without-inverse' + }); + } + + var possibleRelationships = findPossibleInverses(this, inverseType); + + if (possibleRelationships.length === 0) { return null; } + + var filteredRelationships = possibleRelationships.filter((possibleRelationship) => { + var optionsForRelationship = inverseType.metaForProperty(possibleRelationship.name).options; + return name === optionsForRelationship.inverse; + }); + + assert("You defined the '" + name + "' relationship on " + this + ", but you defined the inverse relationships of type " + + inverseType.toString() + " multiple times. Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", + filteredRelationships.length < 2); + + if (filteredRelationships.length === 1 ) { + possibleRelationships = filteredRelationships; + } + + assert("You defined the '" + name + "' relationship on " + this + ", but multiple possible inverse relationships of type " + + this + " were found on " + inverseType + ". Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", + possibleRelationships.length === 1); + + inverseName = possibleRelationships[0].name; + inverseKind = possibleRelationships[0].kind; + } + + function findPossibleInverses(type, inverseType, relationshipsSoFar) { + var possibleRelationships = relationshipsSoFar || []; + + var relationshipMap = get(inverseType, 'relationships'); + if (!relationshipMap) { return possibleRelationships; } + + var relationships = relationshipMap.get(type.modelName); + + relationships = relationships.filter((relationship) => { + var optionsForRelationship = inverseType.metaForProperty(relationship.name).options; + + if (!optionsForRelationship.inverse) { + return true; + } + + return name === optionsForRelationship.inverse; + }); + + if (relationships) { + possibleRelationships.push.apply(possibleRelationships, relationships); + } + + //Recurse to support polymorphism + if (type.superclass) { + findPossibleInverses(type.superclass, inverseType, possibleRelationships); + } + + return possibleRelationships; + } + + return { + type: inverseType, + name: inverseName, + kind: inverseKind + }; + }, + + /** + The model's relationships as a map, keyed on the type of the + relationship. The value of each entry is an array containing a descriptor + for each relationship with that type, describing the name of the relationship + as well as the type. + + For example, given the following model definition: + + ```app/models/blog.js + import DS from 'ember-data'; + + export default DS.Model.extend({ + users: DS.hasMany('user'), + owner: DS.belongsTo('user'), + posts: DS.hasMany('post') + }); + ``` + + This computed property would return a map describing these + relationships, like this: + + ```javascript + import Ember from 'ember'; + import Blog from 'app/models/blog'; + import User from 'app/models/user'; + import Post from 'app/models/post'; + + var relationships = Ember.get(Blog, 'relationships'); + relationships.get(User); + //=> [ { name: 'users', kind: 'hasMany' }, + // { name: 'owner', kind: 'belongsTo' } ] + relationships.get(Post); + //=> [ { name: 'posts', kind: 'hasMany' } ] + ``` + + @property relationships + @static + @type Ember.Map + @readOnly + */ + + relationships: relationshipsDescriptor, + + /** + A hash containing lists of the model's relationships, grouped + by the relationship kind. For example, given a model with this + definition: + + ```app/models/blog.js + import DS from 'ember-data'; + + export default DS.Model.extend({ + users: DS.hasMany('user'), + owner: DS.belongsTo('user'), + + posts: DS.hasMany('post') + }); + ``` + + This property would contain the following: + + ```javascript + import Ember from 'ember'; + import Blog from 'app/models/blog'; + + var relationshipNames = Ember.get(Blog, 'relationshipNames'); + relationshipNames.hasMany; + //=> ['users', 'posts'] + relationshipNames.belongsTo; + //=> ['owner'] + ``` + + @property relationshipNames + @static + @type Object + @readOnly + */ + relationshipNames: Ember.computed(function() { + var names = { + hasMany: [], + belongsTo: [] + }; + + this.eachComputedProperty((name, meta) => { + if (meta.isRelationship) { + names[meta.kind].push(name); + } + }); + + return names; + }), + + /** + An array of types directly related to a model. Each type will be + included once, regardless of the number of relationships it has with + the model. + + For example, given a model with this definition: + + ```app/models/blog.js + import DS from 'ember-data'; + + export default DS.Model.extend({ + users: DS.hasMany('user'), + owner: DS.belongsTo('user'), + + posts: DS.hasMany('post') + }); + ``` + + This property would contain the following: + + ```javascript + import Ember from 'ember'; + import Blog from 'app/models/blog'; + + var relatedTypes = Ember.get(Blog, 'relatedTypes'); + //=> [ User, Post ] + ``` + + @property relatedTypes + @static + @type Ember.Array + @readOnly + */ + relatedTypes: relatedTypesDescriptor, + + /** + A map whose keys are the relationships of a model and whose values are + relationship descriptors. + + For example, given a model with this + definition: + + ```app/models/blog.js + import DS from 'ember-data'; + + export default DS.Model.extend({ + users: DS.hasMany('user'), + owner: DS.belongsTo('user'), + + posts: DS.hasMany('post') + }); + ``` + + This property would contain the following: + + ```javascript + import Ember from 'ember'; + import Blog from 'app/models/blog'; + + var relationshipsByName = Ember.get(Blog, 'relationshipsByName'); + relationshipsByName.get('users'); + //=> { key: 'users', kind: 'hasMany', type: 'user', options: Object, isRelationship: true } + relationshipsByName.get('owner'); + //=> { key: 'owner', kind: 'belongsTo', type: 'user', options: Object, isRelationship: true } + ``` + + @property relationshipsByName + @static + @type Ember.Map + @readOnly + */ + relationshipsByName: relationshipsByNameDescriptor, + + /** + A map whose keys are the fields of the model and whose values are strings + describing the kind of the field. A model's fields are the union of all of its + attributes and relationships. + + For example: + + ```app/models/blog.js + import DS from 'ember-data'; + + export default DS.Model.extend({ + users: DS.hasMany('user'), + owner: DS.belongsTo('user'), + + posts: DS.hasMany('post'), + + title: DS.attr('string') + }); + ``` + + ```js + import Ember from 'ember'; + import Blog from 'app/models/blog'; + + var fields = Ember.get(Blog, 'fields'); + fields.forEach(function(kind, field) { + console.log(field, kind); + }); + + // prints: + // users, hasMany + // owner, belongsTo + // posts, hasMany + // title, attribute + ``` + + @property fields + @static + @type Ember.Map + @readOnly + */ + fields: Ember.computed(function() { + var map = Map.create(); + + this.eachComputedProperty((name, meta) => { + if (meta.isRelationship) { + map.set(name, meta.kind); + } else if (meta.isAttribute) { + map.set(name, 'attribute'); + } + }); + + return map; + }).readOnly(), + + /** + Given a callback, iterates over each of the relationships in the model, + invoking the callback with the name of each relationship and its relationship + descriptor. + + @method eachRelationship + @static + @param {Function} callback the callback to invoke + @param {any} binding the value to which the callback's `this` should be bound + */ + eachRelationship(callback, binding) { + get(this, 'relationshipsByName').forEach((relationship, name) => { + callback.call(binding, name, relationship); + }); + }, + + /** + Given a callback, iterates over each of the types related to a model, + invoking the callback with the related type's class. Each type will be + returned just once, regardless of how many different relationships it has + with a model. + + @method eachRelatedType + @static + @param {Function} callback the callback to invoke + @param {any} binding the value to which the callback's `this` should be bound + */ + eachRelatedType(callback, binding) { + let relationshipTypes = get(this, 'relatedTypes'); + + for (let i = 0; i < relationshipTypes.length; i++) { + let type = relationshipTypes[i]; + callback.call(binding, type); + } + }, + + determineRelationshipType(knownSide, store) { + let knownKey = knownSide.key; + let knownKind = knownSide.kind; + let inverse = this.inverseFor(knownKey, store); + // let key; + let otherKind; + + if (!inverse) { + return knownKind === 'belongsTo' ? 'oneToNone' : 'manyToNone'; + } + + // key = inverse.name; + otherKind = inverse.kind; + + if (otherKind === 'belongsTo') { + return knownKind === 'belongsTo' ? 'oneToOne' : 'manyToOne'; + } else { + return knownKind === 'belongsTo' ? 'oneToMany' : 'manyToMany'; + } + } + +}, AttrClassMethodsMixin); // if `Ember.setOwner` is defined, accessing `this.container` is // deprecated (but functional). In "standard" Ember usage, this diff --git a/addon/-private/system/relationships/ext.js b/addon/-private/system/relationships/ext.js index c99d1a3dfa5..ad2a4c881ce 100644 --- a/addon/-private/system/relationships/ext.js +++ b/addon/-private/system/relationships/ext.js @@ -1,10 +1,9 @@ import Ember from 'ember'; -import { assert, warn } from "ember-data/-private/debug"; +import { assert } from "ember-data/-private/debug"; import { typeForRelationshipMeta, relationshipFromMeta } from "ember-data/-private/system/relationship-meta"; -import EmptyObject from "ember-data/-private/system/empty-object"; var get = Ember.get; var Map = Ember.Map; @@ -139,469 +138,6 @@ export const DidDefinePropertyMixin = Ember.Mixin.create({ } }); -/* - These DS.Model extensions add class methods that provide relationship - introspection abilities about relationships. - - A note about the computed properties contained here: - - **These properties are effectively sealed once called for the first time.** - To avoid repeatedly doing expensive iteration over a model's fields, these - values are computed once and then cached for the remainder of the runtime of - your application. - - If your application needs to modify a class after its initial definition - (for example, using `reopen()` to add additional attributes), make sure you - do it before using your model with the store, which uses these properties - extensively. -*/ - -export const RelationshipsClassMethodsMixin = Ember.Mixin.create({ - - /** - For a given relationship name, returns the model type of the relationship. - - For example, if you define a model like this: - - ```app/models/post.js - import DS from 'ember-data'; - - export default DS.Model.extend({ - comments: DS.hasMany('comment') - }); - ``` - - Calling `store.modelFor('post').typeForRelationship('comments', store)` will return `Comment`. - - @method typeForRelationship - @static - @param {String} name the name of the relationship - @param {store} store an instance of DS.Store - @return {DS.Model} the type of the relationship, or undefined - */ - typeForRelationship(name, store) { - var relationship = get(this, 'relationshipsByName').get(name); - return relationship && store.modelFor(relationship.type); - }, - - inverseMap: Ember.computed(function() { - return new EmptyObject(); - }), - - /** - Find the relationship which is the inverse of the one asked for. - - For example, if you define models like this: - - ```app/models/post.js - import DS from 'ember-data'; - - export default DS.Model.extend({ - comments: DS.hasMany('message') - }); - ``` - - ```app/models/message.js - import DS from 'ember-data'; - - export default DS.Model.extend({ - owner: DS.belongsTo('post') - }); - ``` - - store.modelFor('post').inverseFor('comments', store) -> { type: App.Message, name: 'owner', kind: 'belongsTo' } - store.modelFor('message').inverseFor('owner', store) -> { type: App.Post, name: 'comments', kind: 'hasMany' } - - @method inverseFor - @static - @param {String} name the name of the relationship - @param {DS.Store} store - @return {Object} the inverse relationship, or null - */ - inverseFor(name, store) { - var inverseMap = get(this, 'inverseMap'); - if (inverseMap[name]) { - return inverseMap[name]; - } else { - var inverse = this._findInverseFor(name, store); - inverseMap[name] = inverse; - return inverse; - } - }, - - //Calculate the inverse, ignoring the cache - _findInverseFor(name, store) { - - var inverseType = this.typeForRelationship(name, store); - if (!inverseType) { - return null; - } - - var propertyMeta = this.metaForProperty(name); - //If inverse is manually specified to be null, like `comments: DS.hasMany('message', { inverse: null })` - var options = propertyMeta.options; - if (options.inverse === null) { return null; } - - var inverseName, inverseKind, inverse; - - //If inverse is specified manually, return the inverse - if (options.inverse) { - inverseName = options.inverse; - inverse = Ember.get(inverseType, 'relationshipsByName').get(inverseName); - - assert("We found no inverse relationships by the name of '" + inverseName + "' on the '" + inverseType.modelName + - "' model. This is most likely due to a missing attribute on your model definition.", !Ember.isNone(inverse)); - - inverseKind = inverse.kind; - } else { - //No inverse was specified manually, we need to use a heuristic to guess one - if (propertyMeta.type === propertyMeta.parentType.modelName) { - warn(`Detected a reflexive relationship by the name of '${name}' without an inverse option. Look at http://emberjs.com/guides/models/defining-models/#toc_reflexive-relation for how to explicitly specify inverses.`, false, { - id: 'ds.model.reflexive-relationship-without-inverse' - }); - } - - var possibleRelationships = findPossibleInverses(this, inverseType); - - if (possibleRelationships.length === 0) { return null; } - - var filteredRelationships = possibleRelationships.filter((possibleRelationship) => { - var optionsForRelationship = inverseType.metaForProperty(possibleRelationship.name).options; - return name === optionsForRelationship.inverse; - }); - - assert("You defined the '" + name + "' relationship on " + this + ", but you defined the inverse relationships of type " + - inverseType.toString() + " multiple times. Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", - filteredRelationships.length < 2); - - if (filteredRelationships.length === 1 ) { - possibleRelationships = filteredRelationships; - } - - assert("You defined the '" + name + "' relationship on " + this + ", but multiple possible inverse relationships of type " + - this + " were found on " + inverseType + ". Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", - possibleRelationships.length === 1); - - inverseName = possibleRelationships[0].name; - inverseKind = possibleRelationships[0].kind; - } - - function findPossibleInverses(type, inverseType, relationshipsSoFar) { - var possibleRelationships = relationshipsSoFar || []; - - var relationshipMap = get(inverseType, 'relationships'); - if (!relationshipMap) { return possibleRelationships; } - - var relationships = relationshipMap.get(type.modelName); - - relationships = relationships.filter((relationship) => { - var optionsForRelationship = inverseType.metaForProperty(relationship.name).options; - - if (!optionsForRelationship.inverse) { - return true; - } - - return name === optionsForRelationship.inverse; - }); - - if (relationships) { - possibleRelationships.push.apply(possibleRelationships, relationships); - } - - //Recurse to support polymorphism - if (type.superclass) { - findPossibleInverses(type.superclass, inverseType, possibleRelationships); - } - - return possibleRelationships; - } - - return { - type: inverseType, - name: inverseName, - kind: inverseKind - }; - }, - - /** - The model's relationships as a map, keyed on the type of the - relationship. The value of each entry is an array containing a descriptor - for each relationship with that type, describing the name of the relationship - as well as the type. - - For example, given the following model definition: - - ```app/models/blog.js - import DS from 'ember-data'; - - export default DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - posts: DS.hasMany('post') - }); - ``` - - This computed property would return a map describing these - relationships, like this: - - ```javascript - import Ember from 'ember'; - import Blog from 'app/models/blog'; - import User from 'app/models/user'; - import Post from 'app/models/post'; - - var relationships = Ember.get(Blog, 'relationships'); - relationships.get(User); - //=> [ { name: 'users', kind: 'hasMany' }, - // { name: 'owner', kind: 'belongsTo' } ] - relationships.get(Post); - //=> [ { name: 'posts', kind: 'hasMany' } ] - ``` - - @property relationships - @static - @type Ember.Map - @readOnly - */ - - relationships: relationshipsDescriptor, - - /** - A hash containing lists of the model's relationships, grouped - by the relationship kind. For example, given a model with this - definition: - - ```app/models/blog.js - import DS from 'ember-data'; - - export default DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post') - }); - ``` - - This property would contain the following: - - ```javascript - import Ember from 'ember'; - import Blog from 'app/models/blog'; - - var relationshipNames = Ember.get(Blog, 'relationshipNames'); - relationshipNames.hasMany; - //=> ['users', 'posts'] - relationshipNames.belongsTo; - //=> ['owner'] - ``` - - @property relationshipNames - @static - @type Object - @readOnly - */ - relationshipNames: Ember.computed(function() { - var names = { - hasMany: [], - belongsTo: [] - }; - - this.eachComputedProperty((name, meta) => { - if (meta.isRelationship) { - names[meta.kind].push(name); - } - }); - - return names; - }), - - /** - An array of types directly related to a model. Each type will be - included once, regardless of the number of relationships it has with - the model. - - For example, given a model with this definition: - - ```app/models/blog.js - import DS from 'ember-data'; - - export default DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post') - }); - ``` - - This property would contain the following: - - ```javascript - import Ember from 'ember'; - import Blog from 'app/models/blog'; - - var relatedTypes = Ember.get(Blog, 'relatedTypes'); - //=> [ User, Post ] - ``` - - @property relatedTypes - @static - @type Ember.Array - @readOnly - */ - relatedTypes: relatedTypesDescriptor, - - /** - A map whose keys are the relationships of a model and whose values are - relationship descriptors. - - For example, given a model with this - definition: - - ```app/models/blog.js - import DS from 'ember-data'; - - export default DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post') - }); - ``` - - This property would contain the following: - - ```javascript - import Ember from 'ember'; - import Blog from 'app/models/blog'; - - var relationshipsByName = Ember.get(Blog, 'relationshipsByName'); - relationshipsByName.get('users'); - //=> { key: 'users', kind: 'hasMany', type: 'user', options: Object, isRelationship: true } - relationshipsByName.get('owner'); - //=> { key: 'owner', kind: 'belongsTo', type: 'user', options: Object, isRelationship: true } - ``` - - @property relationshipsByName - @static - @type Ember.Map - @readOnly - */ - relationshipsByName: relationshipsByNameDescriptor, - - /** - A map whose keys are the fields of the model and whose values are strings - describing the kind of the field. A model's fields are the union of all of its - attributes and relationships. - - For example: - - ```app/models/blog.js - import DS from 'ember-data'; - - export default DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post'), - - title: DS.attr('string') - }); - ``` - - ```js - import Ember from 'ember'; - import Blog from 'app/models/blog'; - - var fields = Ember.get(Blog, 'fields'); - fields.forEach(function(kind, field) { - console.log(field, kind); - }); - - // prints: - // users, hasMany - // owner, belongsTo - // posts, hasMany - // title, attribute - ``` - - @property fields - @static - @type Ember.Map - @readOnly - */ - fields: Ember.computed(function() { - var map = Map.create(); - - this.eachComputedProperty((name, meta) => { - if (meta.isRelationship) { - map.set(name, meta.kind); - } else if (meta.isAttribute) { - map.set(name, 'attribute'); - } - }); - - return map; - }).readOnly(), - - /** - Given a callback, iterates over each of the relationships in the model, - invoking the callback with the name of each relationship and its relationship - descriptor. - - @method eachRelationship - @static - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound - */ - eachRelationship(callback, binding) { - get(this, 'relationshipsByName').forEach((relationship, name) => { - callback.call(binding, name, relationship); - }); - }, - - /** - Given a callback, iterates over each of the types related to a model, - invoking the callback with the related type's class. Each type will be - returned just once, regardless of how many different relationships it has - with a model. - - @method eachRelatedType - @static - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound - */ - eachRelatedType(callback, binding) { - let relationshipTypes = get(this, 'relatedTypes'); - - for (let i = 0; i < relationshipTypes.length; i++) { - let type = relationshipTypes[i]; - callback.call(binding, type); - } - }, - - determineRelationshipType(knownSide, store) { - let knownKey = knownSide.key; - let knownKind = knownSide.kind; - let inverse = this.inverseFor(knownKey, store); - // let key; - let otherKind; - - if (!inverse) { - return knownKind === 'belongsTo' ? 'oneToNone' : 'manyToNone'; - } - - // key = inverse.name; - otherKind = inverse.kind; - - if (otherKind === 'belongsTo') { - return knownKind === 'belongsTo' ? 'oneToOne' : 'manyToOne'; - } else { - return knownKind === 'belongsTo' ? 'oneToMany' : 'manyToMany'; - } - } - -}); - export const RelationshipsInstanceMethodsMixin = Ember.Mixin.create({ /** Given a callback, iterates over each of the relationships in the model, From 3adb790fc6c75260375357aa88be53ab156cf6d7 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 14:39:06 -0800 Subject: [PATCH 03/13] move debug-info mixin into DS.Model directly --- addon/-private/system/debug/debug-info.js | 66 ----------------------- addon/-private/system/model/model.js | 65 +++++++++++++++++++++- 2 files changed, 63 insertions(+), 68 deletions(-) delete mode 100644 addon/-private/system/debug/debug-info.js diff --git a/addon/-private/system/debug/debug-info.js b/addon/-private/system/debug/debug-info.js deleted file mode 100644 index bebc89c38e7..00000000000 --- a/addon/-private/system/debug/debug-info.js +++ /dev/null @@ -1,66 +0,0 @@ -import Ember from "ember"; - -export default Ember.Mixin.create({ - - /** - Provides info about the model for debugging purposes - by grouping the properties into more semantic groups. - - Meant to be used by debugging tools such as the Chrome Ember Extension. - - - Groups all attributes in "Attributes" group. - - Groups all belongsTo relationships in "Belongs To" group. - - Groups all hasMany relationships in "Has Many" group. - - Groups all flags in "Flags" group. - - Flags relationship CPs as expensive properties. - - @method _debugInfo - @for DS.Model - @private - */ - _debugInfo() { - let attributes = ['id']; - let relationships = { }; - let expensiveProperties = []; - - this.eachAttribute((name, meta) => attributes.push(name)); - - let groups = [ - { - name: 'Attributes', - properties: attributes, - expand: true - } - ]; - - this.eachRelationship((name, relationship) => { - let properties = relationships[relationship.kind]; - - if (properties === undefined) { - properties = relationships[relationship.kind] = []; - groups.push({ - name: relationship.name, - properties, - expand: true - }); - } - properties.push(name); - expensiveProperties.push(name); - }); - - groups.push({ - name: 'Flags', - properties: ['isLoaded', 'hasDirtyAttributes', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid'] - }); - - return { - propertyInfo: { - // include all other mixins / properties (not just the grouped ones) - includeOtherProperties: true, - groups: groups, - // don't pre-calculate unless cached - expensiveProperties: expensiveProperties - } - }; - } -}); diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index f1e4e89db9c..aeaa312ab20 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -954,9 +954,70 @@ const Model = Ember.Object.extend(Ember.Evented, { setId: Ember.observer('id', function () { this._internalModel.setId(this.get('id')); - }) + }), + + /** + Provides info about the model for debugging purposes + by grouping the properties into more semantic groups. + + Meant to be used by debugging tools such as the Chrome Ember Extension. + + - Groups all attributes in "Attributes" group. + - Groups all belongsTo relationships in "Belongs To" group. + - Groups all hasMany relationships in "Has Many" group. + - Groups all flags in "Flags" group. + - Flags relationship CPs as expensive properties. + + @method _debugInfo + @for DS.Model + @private + */ + _debugInfo() { + let attributes = ['id']; + let relationships = { }; + let expensiveProperties = []; + + this.eachAttribute((name, meta) => attributes.push(name)); + + let groups = [ + { + name: 'Attributes', + properties: attributes, + expand: true + } + ]; + + this.eachRelationship((name, relationship) => { + let properties = relationships[relationship.kind]; + + if (properties === undefined) { + properties = relationships[relationship.kind] = []; + groups.push({ + name: relationship.name, + properties, + expand: true + }); + } + properties.push(name); + expensiveProperties.push(name); + }); + + groups.push({ + name: 'Flags', + properties: ['isLoaded', 'hasDirtyAttributes', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid'] + }); + + return { + propertyInfo: { + // include all other mixins / properties (not just the grouped ones) + includeOtherProperties: true, + groups: groups, + // don't pre-calculate unless cached + expensiveProperties: expensiveProperties + } + }; + } }, - DebuggerInfoMixin, BelongsToMixin, DidDefinePropertyMixin, RelationshipsInstanceMethodsMixin, From da438fa3c8432efae022f24b838760128569a09d Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 14:39:49 -0800 Subject: [PATCH 04/13] move belongsTo mixin into DS.Model directly --- addon/-private/system/model/model.js | 7 ++++--- addon/-private/system/relationships/belongs-to.js | 10 ---------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index aeaa312ab20..a94d5ac859c 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -2,8 +2,6 @@ import Ember from 'ember'; import { assert, deprecate, warn } from "ember-data/-private/debug"; import { PromiseObject } from "ember-data/-private/system/promise-proxies"; import Errors from "ember-data/-private/system/model/errors"; -import DebuggerInfoMixin from 'ember-data/-private/system/debug/debug-info'; -import { BelongsToMixin } from 'ember-data/-private/system/relationships/belongs-to'; import { HasManyMixin } from 'ember-data/-private/system/relationships/has-many'; import { DidDefinePropertyMixin, RelationshipsInstanceMethodsMixin } from 'ember-data/-private/system/relationships/ext'; import { AttrClassMethodsMixin, AttrInstanceMethodsMixin } from 'ember-data/-private/system/model/attr'; @@ -1016,9 +1014,12 @@ const Model = Ember.Object.extend(Ember.Evented, { expensiveProperties: expensiveProperties } }; + }, + + notifyBelongsToChanged(key) { + this.notifyPropertyChange(key); } }, - BelongsToMixin, DidDefinePropertyMixin, RelationshipsInstanceMethodsMixin, HasManyMixin, diff --git a/addon/-private/system/relationships/belongs-to.js b/addon/-private/system/relationships/belongs-to.js index 15a9d875409..082300fc467 100644 --- a/addon/-private/system/relationships/belongs-to.js +++ b/addon/-private/system/relationships/belongs-to.js @@ -133,13 +133,3 @@ export default function belongsTo(modelName, options) { } }).meta(meta); } - -/* - These observers observe all `belongsTo` relationships on the record. See - `relationships/ext` to see how these observers get their dependencies. -*/ -export const BelongsToMixin = Ember.Mixin.create({ - notifyBelongsToChanged(key) { - this.notifyPropertyChange(key); - } -}); From dcf5dab1d03d000f5574e7390bfbcc1a3ac2e749 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 14:41:56 -0800 Subject: [PATCH 05/13] move did-define-property mixin into DS.Model directly --- addon/-private/system/model/model.js | 41 ++++++++++++++++++- addon/-private/system/relationships/ext.js | 46 ---------------------- 2 files changed, 40 insertions(+), 47 deletions(-) diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index a94d5ac859c..254e6152b03 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -3,7 +3,7 @@ import { assert, deprecate, warn } from "ember-data/-private/debug"; import { PromiseObject } from "ember-data/-private/system/promise-proxies"; import Errors from "ember-data/-private/system/model/errors"; import { HasManyMixin } from 'ember-data/-private/system/relationships/has-many'; -import { DidDefinePropertyMixin, RelationshipsInstanceMethodsMixin } from 'ember-data/-private/system/relationships/ext'; +import { RelationshipsInstanceMethodsMixin } from 'ember-data/-private/system/relationships/ext'; import { AttrClassMethodsMixin, AttrInstanceMethodsMixin } from 'ember-data/-private/system/model/attr'; import isEnabled from 'ember-data/-private/features'; import RootState from 'ember-data/-private/system/model/states'; @@ -1018,6 +1018,45 @@ const Model = Ember.Object.extend(Ember.Evented, { notifyBelongsToChanged(key) { this.notifyPropertyChange(key); + }, + + /** + This Ember.js hook allows an object to be notified when a property + is defined. + + In this case, we use it to be notified when an Ember Data user defines a + belongs-to relationship. In that case, we need to set up observers for + each one, allowing us to track relationship changes and automatically + reflect changes in the inverse has-many array. + + This hook passes the class being set up, as well as the key and value + being defined. So, for example, when the user does this: + + ```javascript + DS.Model.extend({ + parent: DS.belongsTo('user') + }); + ``` + + This hook would be called with "parent" as the key and the computed + property returned by `DS.belongsTo` as the value. + + @method didDefineProperty + @param {Object} proto + @param {String} key + @param {Ember.ComputedProperty} value + */ + didDefineProperty(proto, key, value) { + // Check if the value being set is a computed property. + if (value instanceof Ember.ComputedProperty) { + + // If it is, get the metadata for the relationship. This is + // populated by the `DS.belongsTo` helper when it is creating + // the computed property. + var meta = value.meta(); + + meta.parentType = proto.constructor; + } } }, DidDefinePropertyMixin, diff --git a/addon/-private/system/relationships/ext.js b/addon/-private/system/relationships/ext.js index ad2a4c881ce..8f089688c33 100644 --- a/addon/-private/system/relationships/ext.js +++ b/addon/-private/system/relationships/ext.js @@ -92,52 +92,6 @@ var relationshipsByNameDescriptor = Ember.computed(function() { add support for one-to-many relationships. */ -/** - @class Model - @namespace DS -*/ -export const DidDefinePropertyMixin = Ember.Mixin.create({ - - /** - This Ember.js hook allows an object to be notified when a property - is defined. - - In this case, we use it to be notified when an Ember Data user defines a - belongs-to relationship. In that case, we need to set up observers for - each one, allowing us to track relationship changes and automatically - reflect changes in the inverse has-many array. - - This hook passes the class being set up, as well as the key and value - being defined. So, for example, when the user does this: - - ```javascript - DS.Model.extend({ - parent: DS.belongsTo('user') - }); - ``` - - This hook would be called with "parent" as the key and the computed - property returned by `DS.belongsTo` as the value. - - @method didDefineProperty - @param {Object} proto - @param {String} key - @param {Ember.ComputedProperty} value - */ - didDefineProperty(proto, key, value) { - // Check if the value being set is a computed property. - if (value instanceof Ember.ComputedProperty) { - - // If it is, get the metadata for the relationship. This is - // populated by the `DS.belongsTo` helper when it is creating - // the computed property. - var meta = value.meta(); - - meta.parentType = proto.constructor; - } - } -}); - export const RelationshipsInstanceMethodsMixin = Ember.Mixin.create({ /** Given a callback, iterates over each of the relationships in the model, From 6955418cfb37be24555fcca219c8c6a49834b00a Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 14:44:55 -0800 Subject: [PATCH 06/13] move relationship methods into DS.Model directly --- addon/-private/system/model/model.js | 68 +++++++++++++++++++++- addon/-private/system/relationships/ext.js | 63 -------------------- 2 files changed, 65 insertions(+), 66 deletions(-) diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index 254e6152b03..46e95e3c68c 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -3,7 +3,6 @@ import { assert, deprecate, warn } from "ember-data/-private/debug"; import { PromiseObject } from "ember-data/-private/system/promise-proxies"; import Errors from "ember-data/-private/system/model/errors"; import { HasManyMixin } from 'ember-data/-private/system/relationships/has-many'; -import { RelationshipsInstanceMethodsMixin } from 'ember-data/-private/system/relationships/ext'; import { AttrClassMethodsMixin, AttrInstanceMethodsMixin } from 'ember-data/-private/system/model/attr'; import isEnabled from 'ember-data/-private/features'; import RootState from 'ember-data/-private/system/model/states'; @@ -1057,10 +1056,73 @@ const Model = Ember.Object.extend(Ember.Evented, { meta.parentType = proto.constructor; } + }, + + /** + Given a callback, iterates over each of the relationships in the model, + invoking the callback with the name of each relationship and its relationship + descriptor. + + + The callback method you provide should have the following signature (all + parameters are optional): + + ```javascript + function(name, descriptor); + ``` + + - `name` the name of the current property in the iteration + - `descriptor` the meta object that describes this relationship + + The relationship descriptor argument is an object with the following properties. + + - **key** String the name of this relationship on the Model + - **kind** String "hasMany" or "belongsTo" + - **options** Object the original options hash passed when the relationship was declared + - **parentType** DS.Model the type of the Model that owns this relationship + - **type** String the type name of the related Model + + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. + + Example + + ```app/serializers/application.js + import DS from 'ember-data'; + + export default DS.JSONSerializer.extend({ + serialize: function(record, options) { + var json = {}; + + record.eachRelationship(function(name, descriptor) { + if (descriptor.kind === 'hasMany') { + var serializedHasManyName = name.toUpperCase() + '_IDS'; + json[serializedHasManyName] = record.get(name).mapBy('id'); + } + }); + + return json; + } + }); + ``` + + @method eachRelationship + @param {Function} callback the callback to invoke + @param {any} binding the value to which the callback's `this` should be bound + */ + eachRelationship(callback, binding) { + this.constructor.eachRelationship(callback, binding); + }, + + relationshipFor(name) { + return get(this.constructor, 'relationshipsByName').get(name); + }, + + inverseFor(key) { + return this.constructor.inverseFor(key, this.store); } + }, - DidDefinePropertyMixin, - RelationshipsInstanceMethodsMixin, HasManyMixin, AttrInstanceMethodsMixin ); diff --git a/addon/-private/system/relationships/ext.js b/addon/-private/system/relationships/ext.js index 8f089688c33..84be4595106 100644 --- a/addon/-private/system/relationships/ext.js +++ b/addon/-private/system/relationships/ext.js @@ -93,68 +93,5 @@ var relationshipsByNameDescriptor = Ember.computed(function() { */ export const RelationshipsInstanceMethodsMixin = Ember.Mixin.create({ - /** - Given a callback, iterates over each of the relationships in the model, - invoking the callback with the name of each relationship and its relationship - descriptor. - - - The callback method you provide should have the following signature (all - parameters are optional): - - ```javascript - function(name, descriptor); - ``` - - - `name` the name of the current property in the iteration - - `descriptor` the meta object that describes this relationship - - The relationship descriptor argument is an object with the following properties. - - - **key** String the name of this relationship on the Model - - **kind** String "hasMany" or "belongsTo" - - **options** Object the original options hash passed when the relationship was declared - - **parentType** DS.Model the type of the Model that owns this relationship - - **type** String the type name of the related Model - - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. - - Example - - ```app/serializers/application.js - import DS from 'ember-data'; - - export default DS.JSONSerializer.extend({ - serialize: function(record, options) { - var json = {}; - - record.eachRelationship(function(name, descriptor) { - if (descriptor.kind === 'hasMany') { - var serializedHasManyName = name.toUpperCase() + '_IDS'; - json[serializedHasManyName] = record.get(name).mapBy('id'); - } - }); - - return json; - } - }); - ``` - - @method eachRelationship - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound - */ - eachRelationship(callback, binding) { - this.constructor.eachRelationship(callback, binding); - }, - - relationshipFor(name) { - return get(this.constructor, 'relationshipsByName').get(name); - }, - - inverseFor(key) { - return this.constructor.inverseFor(key, this.store); - } }); From 5909f8a5f9bc78ff9904411a87f6f3cf51946898 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 14:48:23 -0800 Subject: [PATCH 07/13] fix relationship descriptor wiring --- addon/-private/system/model/model.js | 5 +++++ addon/-private/system/relationships/ext.js | 20 +++----------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index 46e95e3c68c..b54f304d761 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -7,6 +7,11 @@ import { AttrClassMethodsMixin, AttrInstanceMethodsMixin } from 'ember-data/-pri import isEnabled from 'ember-data/-private/features'; import RootState from 'ember-data/-private/system/model/states'; import EmptyObject from "ember-data/-private/system/empty-object"; +import { + relationshipsByNameDescriptor, + relatedTypesDescriptor, + relationshipsDescriptor +} from 'ember-data/-private/system/relationships/ext'; const { get, diff --git a/addon/-private/system/relationships/ext.js b/addon/-private/system/relationships/ext.js index 84be4595106..d4042949ec8 100644 --- a/addon/-private/system/relationships/ext.js +++ b/addon/-private/system/relationships/ext.js @@ -5,11 +5,10 @@ import { relationshipFromMeta } from "ember-data/-private/system/relationship-meta"; -var get = Ember.get; var Map = Ember.Map; var MapWithDefault = Ember.MapWithDefault; -var relationshipsDescriptor = Ember.computed(function() { +export const relationshipsDescriptor = Ember.computed(function() { if (Ember.testing === true && relationshipsDescriptor._cacheable === true) { relationshipsDescriptor._cacheable = false; } @@ -36,7 +35,7 @@ var relationshipsDescriptor = Ember.computed(function() { return map; }).readOnly(); -var relatedTypesDescriptor = Ember.computed(function() { +export const relatedTypesDescriptor = Ember.computed(function() { if (Ember.testing === true && relatedTypesDescriptor._cacheable === true) { relatedTypesDescriptor._cacheable = false; } @@ -64,7 +63,7 @@ var relatedTypesDescriptor = Ember.computed(function() { return types; }).readOnly(); -var relationshipsByNameDescriptor = Ember.computed(function() { +export const relationshipsByNameDescriptor = Ember.computed(function() { if (Ember.testing === true && relationshipsByNameDescriptor._cacheable === true) { relationshipsByNameDescriptor._cacheable = false; } @@ -82,16 +81,3 @@ var relationshipsByNameDescriptor = Ember.computed(function() { return map; }).readOnly(); - -/** - @module ember-data -*/ - -/* - This file defines several extensions to the base `DS.Model` class that - add support for one-to-many relationships. -*/ - -export const RelationshipsInstanceMethodsMixin = Ember.Mixin.create({ - -}); From 5a99691c6e3c1adfc6b6e8a77ed8816e046b6831 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 14:49:44 -0800 Subject: [PATCH 08/13] fix missing Map --- addon/-private/system/model/model.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index b54f304d761..a151217c9c1 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -15,7 +15,8 @@ import { const { get, - computed + computed, + Map } = Ember; /** From afe5fe42fd60c5259d231950865905bd705a9bac Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 17:04:21 -0800 Subject: [PATCH 09/13] move hasMany mixin into DS.Model --- addon/-private/system/model/model.js | 15 ++++++++++----- addon/-private/system/relationships/has-many.js | 11 ----------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index a151217c9c1..153304dd112 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -2,7 +2,6 @@ import Ember from 'ember'; import { assert, deprecate, warn } from "ember-data/-private/debug"; import { PromiseObject } from "ember-data/-private/system/promise-proxies"; import Errors from "ember-data/-private/system/model/errors"; -import { HasManyMixin } from 'ember-data/-private/system/relationships/has-many'; import { AttrClassMethodsMixin, AttrInstanceMethodsMixin } from 'ember-data/-private/system/model/attr'; import isEnabled from 'ember-data/-private/features'; import RootState from 'ember-data/-private/system/model/states'; @@ -1126,12 +1125,18 @@ const Model = Ember.Object.extend(Ember.Evented, { inverseFor(key) { return this.constructor.inverseFor(key, this.store); + }, + + notifyHasManyAdded(key) { + //We need to notifyPropertyChange in the adding case because we need to make sure + //we fetch the newly added record in case it is unloaded + //TODO(Igor): Consider whether we could do this only if the record state is unloaded + + //Goes away once hasMany is double promisified + this.notifyPropertyChange(key); } -}, - HasManyMixin, - AttrInstanceMethodsMixin -); +}, AttrInstanceMethodsMixin); /** @property data diff --git a/addon/-private/system/relationships/has-many.js b/addon/-private/system/relationships/has-many.js index cd2848cf386..fc82f89bdbf 100644 --- a/addon/-private/system/relationships/has-many.js +++ b/addon/-private/system/relationships/has-many.js @@ -159,14 +159,3 @@ export default function hasMany(type, options) { } }).meta(meta); } - -export const HasManyMixin = Ember.Mixin.create({ - notifyHasManyAdded(key) { - //We need to notifyPropertyChange in the adding case because we need to make sure - //we fetch the newly added record in case it is unloaded - //TODO(Igor): Consider whether we could do this only if the record state is unloaded - - //Goes away once hasMany is double promisified - this.notifyPropertyChange(key); - } -}); From aa5223d9c60e6f12239ceb832389adc1ecffab49 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 17:05:32 -0800 Subject: [PATCH 10/13] move attr mixin into DS.Model --- addon/-private/system/model/attr.js | 7 ------- addon/-private/system/model/model.js | 9 ++++++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/addon/-private/system/model/attr.js b/addon/-private/system/model/attr.js index e111424e977..edb637dd126 100644 --- a/addon/-private/system/model/attr.js +++ b/addon/-private/system/model/attr.js @@ -217,10 +217,3 @@ export const AttrClassMethodsMixin = Ember.Mixin.create({ }); } }); - - -export const AttrInstanceMethodsMixin = Ember.Mixin.create({ - eachAttribute(callback, binding) { - this.constructor.eachAttribute(callback, binding); - } -}); diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index 153304dd112..02835b4524b 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -2,7 +2,7 @@ import Ember from 'ember'; import { assert, deprecate, warn } from "ember-data/-private/debug"; import { PromiseObject } from "ember-data/-private/system/promise-proxies"; import Errors from "ember-data/-private/system/model/errors"; -import { AttrClassMethodsMixin, AttrInstanceMethodsMixin } from 'ember-data/-private/system/model/attr'; +import { AttrClassMethodsMixin } from 'ember-data/-private/system/model/attr'; import isEnabled from 'ember-data/-private/features'; import RootState from 'ember-data/-private/system/model/states'; import EmptyObject from "ember-data/-private/system/empty-object"; @@ -1134,9 +1134,12 @@ const Model = Ember.Object.extend(Ember.Evented, { //Goes away once hasMany is double promisified this.notifyPropertyChange(key); - } + }, -}, AttrInstanceMethodsMixin); + eachAttribute(callback, binding) { + this.constructor.eachAttribute(callback, binding); + } +}); /** @property data From 836d24e93baf5155271e58734695ff5007274623 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 17:06:51 -0800 Subject: [PATCH 11/13] move attr class mixin into unified reopenClass --- addon/-private/system/model/attr.js | 219 --------------------------- addon/-private/system/model/model.js | 205 ++++++++++++++++++++++++- 2 files changed, 203 insertions(+), 221 deletions(-) delete mode 100644 addon/-private/system/model/attr.js diff --git a/addon/-private/system/model/attr.js b/addon/-private/system/model/attr.js deleted file mode 100644 index edb637dd126..00000000000 --- a/addon/-private/system/model/attr.js +++ /dev/null @@ -1,219 +0,0 @@ -import Ember from 'ember'; -import { assert } from "ember-data/-private/debug"; - - -var get = Ember.get; -var Map = Ember.Map; - -/** - @module ember-data -*/ - -/** - @class Model - @namespace DS -*/ - -export const AttrClassMethodsMixin = Ember.Mixin.create({ - /** - A map whose keys are the attributes of the model (properties - described by DS.attr) and whose values are the meta object for the - property. - - Example - - ```app/models/person.js - import DS from 'ember-data'; - - export default DS.Model.extend({ - firstName: attr('string'), - lastName: attr('string'), - birthday: attr('date') - }); - ``` - - ```javascript - import Ember from 'ember'; - import Person from 'app/models/person'; - - var attributes = Ember.get(Person, 'attributes') - - attributes.forEach(function(meta, name) { - console.log(name, meta); - }); - - // prints: - // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} - // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} - // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} - ``` - - @property attributes - @static - @type {Ember.Map} - @readOnly - */ - attributes: Ember.computed(function() { - var map = Map.create(); - - this.eachComputedProperty((name, meta) => { - if (meta.isAttribute) { - assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + this.toString(), name !== 'id'); - - meta.name = name; - map.set(name, meta); - } - }); - - return map; - }).readOnly(), - - /** - A map whose keys are the attributes of the model (properties - described by DS.attr) and whose values are type of transformation - applied to each attribute. This map does not include any - attributes that do not have an transformation type. - - Example - - ```app/models/person.js - import DS from 'ember-data'; - - export default DS.Model.extend({ - firstName: attr(), - lastName: attr('string'), - birthday: attr('date') - }); - ``` - - ```javascript - import Ember from 'ember'; - import Person from 'app/models/person'; - - var transformedAttributes = Ember.get(Person, 'transformedAttributes') - - transformedAttributes.forEach(function(field, type) { - console.log(field, type); - }); - - // prints: - // lastName string - // birthday date - ``` - - @property transformedAttributes - @static - @type {Ember.Map} - @readOnly - */ - transformedAttributes: Ember.computed(function() { - var map = Map.create(); - - this.eachAttribute((key, meta) => { - if (meta.type) { - map.set(key, meta.type); - } - }); - - return map; - }).readOnly(), - - /** - Iterates through the attributes of the model, calling the passed function on each - attribute. - - The callback method you provide should have the following signature (all - parameters are optional): - - ```javascript - function(name, meta); - ``` - - - `name` the name of the current property in the iteration - - `meta` the meta object for the attribute property in the iteration - - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. - - Example - - ```javascript - import DS from 'ember-data'; - - var Person = DS.Model.extend({ - firstName: attr('string'), - lastName: attr('string'), - birthday: attr('date') - }); - - Person.eachAttribute(function(name, meta) { - console.log(name, meta); - }); - - // prints: - // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} - // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} - // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} - ``` - - @method eachAttribute - @param {Function} callback The callback to execute - @param {Object} [binding] the value to which the callback's `this` should be bound - @static - */ - eachAttribute(callback, binding) { - get(this, 'attributes').forEach((meta, name) => { - callback.call(binding, name, meta); - }); - }, - - /** - Iterates through the transformedAttributes of the model, calling - the passed function on each attribute. Note the callback will not be - called for any attributes that do not have an transformation type. - - The callback method you provide should have the following signature (all - parameters are optional): - - ```javascript - function(name, type); - ``` - - - `name` the name of the current property in the iteration - - `type` a string containing the name of the type of transformed - applied to the attribute - - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. - - Example - - ```javascript - import DS from 'ember-data'; - - var Person = DS.Model.extend({ - firstName: attr(), - lastName: attr('string'), - birthday: attr('date') - }); - - Person.eachTransformedAttribute(function(name, type) { - console.log(name, type); - }); - - // prints: - // lastName string - // birthday date - ``` - - @method eachTransformedAttribute - @param {Function} callback The callback to execute - @param {Object} [binding] the value to which the callback's `this` should be bound - @static - */ - eachTransformedAttribute(callback, binding) { - get(this, 'transformedAttributes').forEach((type, name) => { - callback.call(binding, name, type); - }); - } -}); diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index 02835b4524b..af8bcf811d8 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -1665,9 +1665,210 @@ Model.reopenClass({ } else { return knownKind === 'belongsTo' ? 'oneToMany' : 'manyToMany'; } - } + }, + + /** + A map whose keys are the attributes of the model (properties + described by DS.attr) and whose values are the meta object for the + property. + + Example + + ```app/models/person.js + import DS from 'ember-data'; + + export default DS.Model.extend({ + firstName: attr('string'), + lastName: attr('string'), + birthday: attr('date') + }); + ``` + + ```javascript + import Ember from 'ember'; + import Person from 'app/models/person'; + + var attributes = Ember.get(Person, 'attributes') -}, AttrClassMethodsMixin); + attributes.forEach(function(meta, name) { + console.log(name, meta); + }); + + // prints: + // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} + // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} + // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} + ``` + + @property attributes + @static + @type {Ember.Map} + @readOnly + */ + attributes: Ember.computed(function() { + var map = Map.create(); + + this.eachComputedProperty((name, meta) => { + if (meta.isAttribute) { + assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + this.toString(), name !== 'id'); + + meta.name = name; + map.set(name, meta); + } + }); + + return map; + }).readOnly(), + + /** + A map whose keys are the attributes of the model (properties + described by DS.attr) and whose values are type of transformation + applied to each attribute. This map does not include any + attributes that do not have an transformation type. + + Example + + ```app/models/person.js + import DS from 'ember-data'; + + export default DS.Model.extend({ + firstName: attr(), + lastName: attr('string'), + birthday: attr('date') + }); + ``` + + ```javascript + import Ember from 'ember'; + import Person from 'app/models/person'; + + var transformedAttributes = Ember.get(Person, 'transformedAttributes') + + transformedAttributes.forEach(function(field, type) { + console.log(field, type); + }); + + // prints: + // lastName string + // birthday date + ``` + + @property transformedAttributes + @static + @type {Ember.Map} + @readOnly + */ + transformedAttributes: Ember.computed(function() { + var map = Map.create(); + + this.eachAttribute((key, meta) => { + if (meta.type) { + map.set(key, meta.type); + } + }); + + return map; + }).readOnly(), + + /** + Iterates through the attributes of the model, calling the passed function on each + attribute. + + The callback method you provide should have the following signature (all + parameters are optional): + + ```javascript + function(name, meta); + ``` + + - `name` the name of the current property in the iteration + - `meta` the meta object for the attribute property in the iteration + + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. + + Example + + ```javascript + import DS from 'ember-data'; + + var Person = DS.Model.extend({ + firstName: attr('string'), + lastName: attr('string'), + birthday: attr('date') + }); + + Person.eachAttribute(function(name, meta) { + console.log(name, meta); + }); + + // prints: + // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} + // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} + // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} + ``` + + @method eachAttribute + @param {Function} callback The callback to execute + @param {Object} [binding] the value to which the callback's `this` should be bound + @static + */ + eachAttribute(callback, binding) { + get(this, 'attributes').forEach((meta, name) => { + callback.call(binding, name, meta); + }); + }, + + /** + Iterates through the transformedAttributes of the model, calling + the passed function on each attribute. Note the callback will not be + called for any attributes that do not have an transformation type. + + The callback method you provide should have the following signature (all + parameters are optional): + + ```javascript + function(name, type); + ``` + + - `name` the name of the current property in the iteration + - `type` a string containing the name of the type of transformed + applied to the attribute + + Note that in addition to a callback, you can also pass an optional target + object that will be set as `this` on the context. + + Example + + ```javascript + import DS from 'ember-data'; + + var Person = DS.Model.extend({ + firstName: attr(), + lastName: attr('string'), + birthday: attr('date') + }); + + Person.eachTransformedAttribute(function(name, type) { + console.log(name, type); + }); + + // prints: + // lastName string + // birthday date + ``` + + @method eachTransformedAttribute + @param {Function} callback The callback to execute + @param {Object} [binding] the value to which the callback's `this` should be bound + @static + */ + eachTransformedAttribute(callback, binding) { + get(this, 'transformedAttributes').forEach((type, name) => { + callback.call(binding, name, type); + }); + } +}); // if `Ember.setOwner` is defined, accessing `this.container` is // deprecated (but functional). In "standard" Ember usage, this From ed0b83bfd65d4f8431883892b18463aba68ab9ce Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 17:07:32 -0800 Subject: [PATCH 12/13] remove forgotten import --- addon/-private/system/model/model.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index af8bcf811d8..7a3b8937829 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -2,7 +2,6 @@ import Ember from 'ember'; import { assert, deprecate, warn } from "ember-data/-private/debug"; import { PromiseObject } from "ember-data/-private/system/promise-proxies"; import Errors from "ember-data/-private/system/model/errors"; -import { AttrClassMethodsMixin } from 'ember-data/-private/system/model/attr'; import isEnabled from 'ember-data/-private/features'; import RootState from 'ember-data/-private/system/model/states'; import EmptyObject from "ember-data/-private/system/empty-object"; From 61a962c3cfee4bbbbaeddc16928fd50ab7e9cfb8 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Tue, 6 Dec 2016 17:09:25 -0800 Subject: [PATCH 13/13] ES6 code cleanup --- addon/-private/system/model/model.js | 126 +++++++++++++-------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/addon/-private/system/model/model.js b/addon/-private/system/model/model.js index 7a3b8937829..4a0ba389e5f 100644 --- a/addon/-private/system/model/model.js +++ b/addon/-private/system/model/model.js @@ -22,7 +22,7 @@ const { */ function intersection (array1, array2) { - var result = []; + let result = []; array1.forEach((element) => { if (array2.indexOf(element) >= 0) { result.push(element); @@ -91,7 +91,7 @@ const Model = Ember.Object.extend(Ember.Evented, { Example ```javascript - var record = store.createRecord('model'); + let record = store.createRecord('model'); record.get('isLoaded'); // true store.findRecord('model', 1).then(function(model) { @@ -113,7 +113,7 @@ const Model = Ember.Object.extend(Ember.Evented, { Example ```javascript - var record = store.createRecord('model'); + let record = store.createRecord('model'); record.get('hasDirtyAttributes'); // true store.findRecord('model', 1).then(function(model) { @@ -140,9 +140,9 @@ const Model = Ember.Object.extend(Ember.Evented, { Example ```javascript - var record = store.createRecord('model'); + let record = store.createRecord('model'); record.get('isSaving'); // false - var promise = record.save(); + let promise = record.save(); record.get('isSaving'); // true promise.then(function() { record.get('isSaving'); // false @@ -165,7 +165,7 @@ const Model = Ember.Object.extend(Ember.Evented, { Example ```javascript - var record = store.createRecord('model'); + let record = store.createRecord('model'); record.get('isDeleted'); // false record.deleteRecord(); @@ -175,7 +175,7 @@ const Model = Ember.Object.extend(Ember.Evented, { record.get('isSaving'); // false // Persisting the deletion - var promise = record.save(); + let promise = record.save(); record.get('isDeleted'); // true record.get('isSaving'); // true @@ -201,7 +201,7 @@ const Model = Ember.Object.extend(Ember.Evented, { Example ```javascript - var record = store.createRecord('model'); + let record = store.createRecord('model'); record.get('isNew'); // true record.save().then(function(model) { @@ -237,7 +237,7 @@ const Model = Ember.Object.extend(Ember.Evented, { Example ```javascript - var record = store.createRecord('model'); + let record = store.createRecord('model'); record.get('dirtyType'); // 'created' ``` @@ -293,7 +293,7 @@ const Model = Ember.Object.extend(Ember.Evented, { attribute. ```javascript - var record = store.createRecord('model'); + let record = store.createRecord('model'); record.get('id'); // null store.findRecord('model', 1).then(function(model) { @@ -420,8 +420,8 @@ const Model = Ember.Object.extend(Ember.Evented, { */ toJSON(options) { // container is for lazy transform lookups - var serializer = this.store.serializerFor('-default'); - var snapshot = this._internalModel.createSnapshot(); + let serializer = this.store.serializerFor('-default'); + let snapshot = this._internalModel.createSnapshot(); return serializer.serialize(snapshot, options); }, @@ -547,7 +547,7 @@ const Model = Ember.Object.extend(Ember.Evented, { export default Ember.Route.extend({ actions: { delete: function() { - var controller = this.controller; + let controller = this.controller; controller.get('model').destroyRecord().then(function() { controller.transitionToRoute('model.index'); }); @@ -601,8 +601,8 @@ const Model = Ember.Object.extend(Ember.Evented, { */ _notifyProperties(keys) { Ember.beginPropertyChanges(); - var key; - for (var i = 0, length = keys.length; i < length; i++) { + let key; + for (let i = 0, length = keys.length; i < length; i++) { key = keys[i]; this.notifyPropertyChange(key); } @@ -631,7 +631,7 @@ const Model = Ember.Object.extend(Ember.Evented, { ``` ```javascript - var mascot = store.createRecord('mascot'); + let mascot = store.createRecord('mascot'); mascot.changedAttributes(); // {} @@ -796,10 +796,10 @@ const Model = Ember.Object.extend(Ember.Evented, { @param {String} name */ trigger(name) { - var length = arguments.length; - var args = new Array(length - 1); + let length = arguments.length; + let args = new Array(length - 1); - for (var i = 1; i < length; i++) { + for (let i = 1; i < length; i++) { args[i - 1] = arguments[i]; } @@ -818,7 +818,7 @@ const Model = Ember.Object.extend(Ember.Evented, { // This is a temporary solution until we refactor DS.Model to not // rely on the data property. willMergeMixin(props) { - var constructor = this.constructor; + let constructor = this.constructor; assert('`' + intersection(Object.keys(props), RESERVED_MODEL_PROPS)[0] + '` is a reserved property name on DS.Model objects. Please choose a different property name for ' + constructor.toString(), !intersection(Object.keys(props), RESERVED_MODEL_PROPS)[0]); assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + constructor.toString(), Object.keys(props).indexOf('id') === -1); }, @@ -839,7 +839,7 @@ const Model = Ember.Object.extend(Ember.Evented, { ``` ```javascript - var blog = store.push({ + let blog = store.push({ data: { type: 'blog', id: 1, @@ -850,19 +850,19 @@ const Model = Ember.Object.extend(Ember.Evented, { } } }); - var userRef = blog.belongsTo('user'); + let userRef = blog.belongsTo('user'); // check if the user relationship is loaded - var isLoaded = userRef.value() !== null; + let isLoaded = userRef.value() !== null; // get the record of the reference (null if not yet available) - var user = userRef.value(); + let user = userRef.value(); // get the identifier of the reference if (userRef.remoteType() === "id") { - var id = userRef.id(); + let id = userRef.id(); } else if (userRef.remoteType() === "link") { - var link = userRef.link(); + let link = userRef.link(); } // load user (via store.findRecord or store.findBelongsTo) @@ -903,7 +903,7 @@ const Model = Ember.Object.extend(Ember.Evented, { comments: DS.hasMany({ async: true }) }); - var blog = store.push({ + let blog = store.push({ data: { type: 'blog', id: 1, @@ -917,19 +917,19 @@ const Model = Ember.Object.extend(Ember.Evented, { } } }); - var commentsRef = blog.hasMany('comments'); + let commentsRef = blog.hasMany('comments'); // check if the comments are loaded already - var isLoaded = commentsRef.value() !== null; + let isLoaded = commentsRef.value() !== null; // get the records of the reference (null if not yet available) - var comments = commentsRef.value(); + let comments = commentsRef.value(); // get the identifier of the reference if (commentsRef.remoteType() === "ids") { - var ids = commentsRef.ids(); + let ids = commentsRef.ids(); } else if (commentsRef.remoteType() === "link") { - var link = commentsRef.link(); + let link = commentsRef.link(); } // load comments (via store.findMany or store.findHasMany) @@ -1056,7 +1056,7 @@ const Model = Ember.Object.extend(Ember.Evented, { // If it is, get the metadata for the relationship. This is // populated by the `DS.belongsTo` helper when it is creating // the computed property. - var meta = value.meta(); + let meta = value.meta(); meta.parentType = proto.constructor; } @@ -1096,11 +1096,11 @@ const Model = Ember.Object.extend(Ember.Evented, { export default DS.JSONSerializer.extend({ serialize: function(record, options) { - var json = {}; + let json = {}; record.eachRelationship(function(name, descriptor) { if (descriptor.kind === 'hasMany') { - var serializedHasManyName = name.toUpperCase() + '_IDS'; + let serializedHasManyName = name.toUpperCase() + '_IDS'; json[serializedHasManyName] = record.get(name).mapBy('id'); } }); @@ -1194,7 +1194,7 @@ Model.reopenClass({ keys to underscore (instead of dasherized), you might use the following code: ```javascript - export default var PostSerializer = DS.RESTSerializer.extend({ + export default const PostSerializer = DS.RESTSerializer.extend({ payloadKeyFromModelName: function(modelName) { return Ember.String.underscore(modelName); } @@ -1246,7 +1246,7 @@ Model.reopenClass({ @return {DS.Model} the type of the relationship, or undefined */ typeForRelationship(name, store) { - var relationship = get(this, 'relationshipsByName').get(name); + let relationship = get(this, 'relationshipsByName').get(name); return relationship && store.modelFor(relationship.type); }, @@ -1285,11 +1285,11 @@ Model.reopenClass({ @return {Object} the inverse relationship, or null */ inverseFor(name, store) { - var inverseMap = get(this, 'inverseMap'); + let inverseMap = get(this, 'inverseMap'); if (inverseMap[name]) { return inverseMap[name]; } else { - var inverse = this._findInverseFor(name, store); + let inverse = this._findInverseFor(name, store); inverseMap[name] = inverse; return inverse; } @@ -1298,17 +1298,17 @@ Model.reopenClass({ //Calculate the inverse, ignoring the cache _findInverseFor(name, store) { - var inverseType = this.typeForRelationship(name, store); + let inverseType = this.typeForRelationship(name, store); if (!inverseType) { return null; } - var propertyMeta = this.metaForProperty(name); + let propertyMeta = this.metaForProperty(name); //If inverse is manually specified to be null, like `comments: DS.hasMany('message', { inverse: null })` - var options = propertyMeta.options; + let options = propertyMeta.options; if (options.inverse === null) { return null; } - var inverseName, inverseKind, inverse; + let inverseName, inverseKind, inverse; //If inverse is specified manually, return the inverse if (options.inverse) { @@ -1327,12 +1327,12 @@ Model.reopenClass({ }); } - var possibleRelationships = findPossibleInverses(this, inverseType); + let possibleRelationships = findPossibleInverses(this, inverseType); if (possibleRelationships.length === 0) { return null; } - var filteredRelationships = possibleRelationships.filter((possibleRelationship) => { - var optionsForRelationship = inverseType.metaForProperty(possibleRelationship.name).options; + let filteredRelationships = possibleRelationships.filter((possibleRelationship) => { + let optionsForRelationship = inverseType.metaForProperty(possibleRelationship.name).options; return name === optionsForRelationship.inverse; }); @@ -1353,15 +1353,15 @@ Model.reopenClass({ } function findPossibleInverses(type, inverseType, relationshipsSoFar) { - var possibleRelationships = relationshipsSoFar || []; + let possibleRelationships = relationshipsSoFar || []; - var relationshipMap = get(inverseType, 'relationships'); + let relationshipMap = get(inverseType, 'relationships'); if (!relationshipMap) { return possibleRelationships; } - var relationships = relationshipMap.get(type.modelName); + let relationships = relationshipMap.get(type.modelName); relationships = relationships.filter((relationship) => { - var optionsForRelationship = inverseType.metaForProperty(relationship.name).options; + let optionsForRelationship = inverseType.metaForProperty(relationship.name).options; if (!optionsForRelationship.inverse) { return true; @@ -1416,7 +1416,7 @@ Model.reopenClass({ import User from 'app/models/user'; import Post from 'app/models/post'; - var relationships = Ember.get(Blog, 'relationships'); + let relationships = Ember.get(Blog, 'relationships'); relationships.get(User); //=> [ { name: 'users', kind: 'hasMany' }, // { name: 'owner', kind: 'belongsTo' } ] @@ -1454,7 +1454,7 @@ Model.reopenClass({ import Ember from 'ember'; import Blog from 'app/models/blog'; - var relationshipNames = Ember.get(Blog, 'relationshipNames'); + let relationshipNames = Ember.get(Blog, 'relationshipNames'); relationshipNames.hasMany; //=> ['users', 'posts'] relationshipNames.belongsTo; @@ -1467,7 +1467,7 @@ Model.reopenClass({ @readOnly */ relationshipNames: Ember.computed(function() { - var names = { + let names = { hasMany: [], belongsTo: [] }; @@ -1505,7 +1505,7 @@ Model.reopenClass({ import Ember from 'ember'; import Blog from 'app/models/blog'; - var relatedTypes = Ember.get(Blog, 'relatedTypes'); + let relatedTypes = Ember.get(Blog, 'relatedTypes'); //=> [ User, Post ] ``` @@ -1540,7 +1540,7 @@ Model.reopenClass({ import Ember from 'ember'; import Blog from 'app/models/blog'; - var relationshipsByName = Ember.get(Blog, 'relationshipsByName'); + let relationshipsByName = Ember.get(Blog, 'relationshipsByName'); relationshipsByName.get('users'); //=> { key: 'users', kind: 'hasMany', type: 'user', options: Object, isRelationship: true } relationshipsByName.get('owner'); @@ -1578,7 +1578,7 @@ Model.reopenClass({ import Ember from 'ember'; import Blog from 'app/models/blog'; - var fields = Ember.get(Blog, 'fields'); + let fields = Ember.get(Blog, 'fields'); fields.forEach(function(kind, field) { console.log(field, kind); }); @@ -1596,7 +1596,7 @@ Model.reopenClass({ @readOnly */ fields: Ember.computed(function() { - var map = Map.create(); + let map = Map.create(); this.eachComputedProperty((name, meta) => { if (meta.isRelationship) { @@ -1687,7 +1687,7 @@ Model.reopenClass({ import Ember from 'ember'; import Person from 'app/models/person'; - var attributes = Ember.get(Person, 'attributes') + let attributes = Ember.get(Person, 'attributes') attributes.forEach(function(meta, name) { console.log(name, meta); @@ -1705,7 +1705,7 @@ Model.reopenClass({ @readOnly */ attributes: Ember.computed(function() { - var map = Map.create(); + let map = Map.create(); this.eachComputedProperty((name, meta) => { if (meta.isAttribute) { @@ -1741,7 +1741,7 @@ Model.reopenClass({ import Ember from 'ember'; import Person from 'app/models/person'; - var transformedAttributes = Ember.get(Person, 'transformedAttributes') + let transformedAttributes = Ember.get(Person, 'transformedAttributes') transformedAttributes.forEach(function(field, type) { console.log(field, type); @@ -1758,7 +1758,7 @@ Model.reopenClass({ @readOnly */ transformedAttributes: Ember.computed(function() { - var map = Map.create(); + let map = Map.create(); this.eachAttribute((key, meta) => { if (meta.type) { @@ -1791,7 +1791,7 @@ Model.reopenClass({ ```javascript import DS from 'ember-data'; - var Person = DS.Model.extend({ + let Person = DS.Model.extend({ firstName: attr('string'), lastName: attr('string'), birthday: attr('date') @@ -1842,7 +1842,7 @@ Model.reopenClass({ ```javascript import DS from 'ember-data'; - var Person = DS.Model.extend({ + let Person = DS.Model.extend({ firstName: attr(), lastName: attr('string'), birthday: attr('date')