diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..e20a9680d23 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7d3b8cb945f..719df70414a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ tmp/ tests/source/ dist/ .idea +*.iml .DS_Store .project diff --git a/README.md b/README.md index d5367a694f9..c1786969818 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ The latest passing build from the "master" branch is available on Similarly, the latest passing build from the "beta" branch can be found on [http://emberjs.com/builds/#/beta](http://emberjs.com/builds/#/beta) -Or build ember-data.js yourself. Clone the repository and run `npm run dist` +Or build ember-data.js yourself. Clone the repository and run `npm run build:production` after [setup](#setup). You'll find ember-data.js in the `dist` directory. #### Internet Explorer 8 @@ -53,7 +53,7 @@ controllers in your app. First thing's first: tell Ember Data about the models in your application. For example, imagine we're writing a blog reader app. Here's what your model definition would look like if you're using -globals (that is, not something like Ember App Kit or ember-cli): +globals (that is, not something like or ember-cli): ```js var attr = DS.attr; @@ -75,7 +75,7 @@ App.Comment = DS.Model.extend({ }); ``` -If you're using ES6 modules (via Ember App Kit or ember-cli), your +If you're using ES6 modules (via ember-cli), your models would look like this: ```js @@ -167,7 +167,7 @@ post [The Road to Ember Data 1. Ensure that [Node.js](http://nodejs.org/) is installed. 2. Run `npm install` to ensure the required dependencies are installed. -3. Run `npm run dist` to build Ember Data. The builds will be placed in the `dist/` directory. +3. Run `npm run build:production` to build Ember Data. The builds will be placed in the `dist/` directory. # Contribution diff --git a/bower.json b/bower.json index 07ea8de1097..1dc4ca9ce93 100644 --- a/bower.json +++ b/bower.json @@ -2,16 +2,12 @@ "name": "ember-data", "private": true, "dependencies": { - "ember": "~1.10.0" + "ember": "~1.11.1" }, "devDependencies": { "qunit": "~1.17.0", "jquery": "~1.10.x", - "handlebars": ">= 2.0.0 < 3.0.0", "loader.js": "~1.0.0", "es5-shim": "~4.0.3" - }, - "resolutions": { - "handlebars": ">= 2.0.0 < 3.0.0" } } diff --git a/config/package-manager-files/package.json b/config/package-manager-files/package.json index 4123b810594..d1cc0aef923 100644 --- a/config/package-manager-files/package.json +++ b/config/package-manager-files/package.json @@ -8,5 +8,12 @@ "main": "./ember-data.js", "dependencies": { "ember": ">= 1.8.1 < 2.0.0" + }, + "jspm": { + "main": "ember-data", + "shim": { + "ember-data": { "deps": ["ember"], "exports": "DS" }, + "ember-data.prod": { "deps": ["ember"], "exports": "DS" } + } } } diff --git a/config/s3ProjectConfig.js b/config/s3ProjectConfig.js index 2f0d9a167cf..fa8c7239b24 100644 --- a/config/s3ProjectConfig.js +++ b/config/s3ProjectConfig.js @@ -1,32 +1,46 @@ -module.exports = function(revision, tag, date){ +function fileMap(revision,tag,date) { return { - 'ember-data.js': fileObject('ember-data.js', 'text/javascript', revision, tag, date), - 'ember-data.js.map': fileObject('ember-data.js.map', 'application/json', revision, tag, date), - 'ember-data.min.js': fileObject('ember-data.min.js', 'text/javascript', revision, tag, date), - 'ember-data.prod.js': fileObject('ember-data.prod.js', 'text/javascript', revision, tag, date) - } -} + 'ember-data.js': fileObject('ember-data', '.js', 'text/javascript', revision, tag, date), + 'ember-data.js.map': fileObject('ember-data.js', '.map', 'application/json', revision, tag, date), + 'ember-data.min.js': fileObject('ember-data.min', '.js', 'text/javascript', revision, tag, date), + 'ember-data.prod.js': fileObject('ember-data.prod', '.js', 'text/javascript', revision, tag, date), + 'docs/data.json': fileObject('ember-data-docs', '.json', 'application/json', revision, tag, date) + }; +}; -function fileObject(fileName, contentType, currentRevision, tag, date) { - var filePath = '/' + fileName; - return { +function fileObject(baseName, extension, contentType, currentRevision, tag, date){ + var fullName = '/' + baseName + extension; + var obj = { contentType: contentType, - destinations: { - canary: [ - 'canary' + filePath, - 'canary/daily/' + date + filePath, - 'canary/shas/' + currentRevision + filePath - ], - stable: [ - 'stable' + filePath, - 'stable/daily/' + date + filePath, - 'stable/shas/' + currentRevision + filePath - ], - beta: [ - 'beta' + filePath, - 'beta/daily/' + date + filePath, - 'beta/shas/' + currentRevision + filePath - ] - } - } + destinations: { + canary: [ + 'latest' + fullName, + 'canary' + fullName, + 'canary/daily/' + date + fullName, + 'canary/shas/' + currentRevision + fullName + ], + release: [ + 'stable' + fullName, + 'release' + fullName, + 'release/daily/' + date + fullName, + 'release/shas/' + currentRevision + fullName + ], + beta: [ + 'beta' + fullName, + 'beta/daily/' + date + fullName, + 'beta/shas/' + currentRevision + fullName + ], + wildcard: [] + } + }; + + if (tag) { + for (var key in obj.destinations) { + obj.destinations[key].push('tags/' + tag + fullName); + } + } + + return obj; } + +module.exports = fileMap; diff --git a/lib/jscs-rules/require-spaces-after-closing-parenthesis-in-function-declaration.js b/lib/jscs-rules/require-spaces-after-closing-parenthesis-in-function-declaration.js index 8ead028da71..690574ad3a3 100644 --- a/lib/jscs-rules/require-spaces-after-closing-parenthesis-in-function-declaration.js +++ b/lib/jscs-rules/require-spaces-after-closing-parenthesis-in-function-declaration.js @@ -25,20 +25,10 @@ module.exports.prototype = { check: function(file, errors) { var beforeOpeningRoundBrace = this._beforeOpeningRoundBrace; var beforeOpeningCurlyBrace = this._beforeOpeningCurlyBrace; - var tokens = file.getTokens(); file.iterateNodesByType(['FunctionDeclaration'], function(node) { - var nodeBeforeRoundBrace = node; - // named function - if (node.id) { - nodeBeforeRoundBrace = node.id; - } - - var functionTokenPos = file.getTokenPosByRangeStart(nodeBeforeRoundBrace.range[0]); - var functionToken = tokens[functionTokenPos]; - - var nextTokenPos = file.getTokenPosByRangeStart(functionToken.range[1]); - var nextToken = tokens[nextTokenPos]; + var functionToken = file.getFirstNodeToken(node.id || node); + var nextToken = file.getNextToken(functionToken); if (beforeOpeningRoundBrace) { if (nextToken) { @@ -56,8 +46,8 @@ module.exports.prototype = { } } - var tokenBeforeBodyPos = file.getTokenPosByRangeStart(node.body.range[0] - 1); - var tokenBeforeBody = tokens[tokenBeforeBodyPos]; + // errors if no token is found unless `includeComments` is passed + var tokenBeforeBody = file.getPrevToken(node.body, { includeComments: true }); if (beforeOpeningCurlyBrace) { if (tokenBeforeBody) { diff --git a/package.json b/package.json index 23740bfa88f..8d11b2fd98c 100644 --- a/package.json +++ b/package.json @@ -52,12 +52,12 @@ "ejs": "^1.0.0", "ember-cli": "^0.1.15", "ember-inflector": "^1.5.0", - "ember-publisher": "0.0.6", + "ember-publisher": "0.0.7", "es6-module-transpiler": "^0.9.5", "es6-module-transpiler-amd-formatter": "^0.2.4", "es6-module-transpiler-package-resolver": "^1.0.1", "git-repo-version": "0.0.2", - "jscs": "^1.11.0", + "jscs": "^1.12.0", "testem": "^0.6.19", "yuidocjs": "~0.3.46" } diff --git a/packages/activemodel-adapter/lib/system/active-model-serializer.js b/packages/activemodel-adapter/lib/system/active-model-serializer.js index 3ebb0d97b4a..c4c14b84366 100644 --- a/packages/activemodel-adapter/lib/system/active-model-serializer.js +++ b/packages/activemodel-adapter/lib/system/active-model-serializer.js @@ -262,7 +262,7 @@ var ActiveModelSerializer = RESTSerializer.extend({ type.eachRelationship(function(key, relationship) { var payloadKey, payload; if (relationship.options.polymorphic) { - payloadKey = this.keyForAttribute(key); + payloadKey = this.keyForAttribute(key, "deserialize"); payload = hash[payloadKey]; if (payload && payload.type) { payload.type = this.typeForRoot(payload.type); @@ -273,7 +273,7 @@ var ActiveModelSerializer = RESTSerializer.extend({ }); } } else { - payloadKey = this.keyForRelationship(key, relationship.kind); + payloadKey = this.keyForRelationship(key, relationship.kind, "deserialize"); if (!hash.hasOwnProperty(payloadKey)) { return; } payload = hash[payloadKey]; } diff --git a/packages/ember-data/lib/adapters/build-url-mixin.js b/packages/ember-data/lib/adapters/build-url-mixin.js index 88ae5a822a3..a9600a22be4 100644 --- a/packages/ember-data/lib/adapters/build-url-mixin.js +++ b/packages/ember-data/lib/adapters/build-url-mixin.js @@ -43,31 +43,145 @@ export default Ember.Mixin.create({ @method buildURL @param {String} type - @param {String|Array} id single id or array of ids + @param {String|Array|Object} id single id or array of ids or query @param {DS.Snapshot|Array} snapshot single snapshot or array of snapshots @param {String} requestType @return {String} url */ buildURL: function(type, id, snapshot, requestType) { + switch (requestType) { + case 'find': + return this.urlForFind(id, type, snapshot); + case 'findAll': + return this.urlForFindAll(type); + case 'findQuery': + return this.urlForFindQuery(id, type); + case 'findMany': + return this.urlForFindMany(id, type, snapshot); + case 'findHasMany': + return this.urlForFindHasMany(id, type); + case 'findBelongsTo': + return this.urlForFindBelongsTo(id, type); + case 'createRecord': + return this.urlForCreateRecord(type, snapshot); + case 'deleteRecord': + return this.urlForDeleteRecord(id, type, snapshot); + default: + return this._buildURL(type, id); + } + }, + + /** + @method _buildURL + @private + @param {String} type + @param {String} id + @return {String} url + */ + _buildURL: function(type, id) { var url = []; var host = get(this, 'host'); var prefix = this.urlPrefix(); + var path; - if (type) { url.push(this.pathForType(type)); } - - //We might get passed in an array of ids from findMany - //in which case we don't want to modify the url, as the - //ids will be passed in through a query param - if (id && !Ember.isArray(id)) { url.push(encodeURIComponent(id)); } + if (type) { + path = this.pathForType(type); + if (path) { url.push(path); } + } + if (id) { url.push(encodeURIComponent(id)); } if (prefix) { url.unshift(prefix); } url = url.join('/'); - if (!host && url) { url = '/' + url; } + if (!host && url && url.charAt(0) !== '/') { + url = '/' + url; + } return url; }, + /** + * @method urlForFind + * @param {String} id + * @param {String} type + * @param {DS.Snapshot} snapshot + * @return {String} url + */ + urlForFind: function(id, type, snapshot) { + return this._buildURL(type, id); + }, + + /** + * @method urlForFindAll + * @param {String} type + * @return {String} url + */ + urlForFindAll: function(type) { + return this._buildURL(type); + }, + + /** + * @method urlForFindQuery + * @param {Object} query + * @param {String} type + * @return {String} url + */ + urlForFindQuery: function(query, type) { + return this._buildURL(type); + }, + + /** + * @method urlForFindMany + * @param {Array} ids + * @param {String} type + * @param {Array} snapshots + * @return {String} url + */ + urlForFindMany: function(ids, type, snapshots) { + return this._buildURL(type); + }, + + /** + * @method urlForFindHasMany + * @param {String} id + * @param {String} type + * @return {String} url + */ + urlForFindHasMany: function(id, type) { + return this._buildURL(type, id); + }, + + /** + * @method urlForFindBelongTo + * @param {String} id + * @param {String} type + * @return {String} url + */ + urlForFindBelongsTo: function(id, type) { + return this._buildURL(type, id); + }, + + /** + * @method urlForCreateRecord + * @param {String} type + * @param {DS.Snapshot} snapshot + * @return {String} url + */ + urlForCreateRecord: function(type, snapshot) { + return this._buildURL(type); + }, + + /** + * @method urlForDeleteRecord + * @param {String} id + * @param {String} type + * @param {DS.Snapshot} snapshot + * @return {String} url + */ + urlForDeleteRecord: function(id, type, snapshot) { + return this._buildURL(type, id); + }, + /** @method urlPrefix @private diff --git a/packages/ember-data/lib/adapters/rest-adapter.js b/packages/ember-data/lib/adapters/rest-adapter.js index 16cc3939205..e52fa54a9da 100644 --- a/packages/ember-data/lib/adapters/rest-adapter.js +++ b/packages/ember-data/lib/adapters/rest-adapter.js @@ -391,7 +391,7 @@ export default Adapter.extend(BuildURLMixin, { @return {Promise} promise */ findQuery: function(store, type, query) { - var url = this.buildURL(type.typeKey, null, null, 'findQuery'); + var url = this.buildURL(type.typeKey, query, null, 'findQuery'); if (this.sortQueryParams) { query = this.sortQueryParams(query); diff --git a/packages/ember-data/lib/main.js b/packages/ember-data/lib/main.js index b5a73ab9d15..5265ace5735 100644 --- a/packages/ember-data/lib/main.js +++ b/packages/ember-data/lib/main.js @@ -8,7 +8,7 @@ Ember.RSVP.Promise.cast = Ember.RSVP.Promise.cast || Ember.RSVP.resolve; Ember.runInDebug(function() { - if (Ember.VERSION.match(/1\.[0-7]\./)) { + if (Ember.VERSION.match(/^1\.[0-7]\./)) { throw new Ember.Error("Ember Data requires at least Ember 1.8.0, but you have " + Ember.VERSION + ". Please upgrade your version of Ember, then upgrade Ember Data"); diff --git a/packages/ember-data/lib/serializers/embedded-records-mixin.js b/packages/ember-data/lib/serializers/embedded-records-mixin.js index dcd095a53ed..0f09bae6d7c 100644 --- a/packages/ember-data/lib/serializers/embedded-records-mixin.js +++ b/packages/ember-data/lib/serializers/embedded-records-mixin.js @@ -64,8 +64,9 @@ var camelize = Ember.String.camelize; Embedded records must have a model defined to be extracted and serialized. Note that when defining any relationships on your model such as `belongsTo` and `hasMany`, you should not both specify `async:true` and also indicate through the serializer's - `attrs` attribute that the related model should be embedded. If a model is - declared embedded, then do not use `async:true`. + `attrs` attribute that the related model should be embedded for deserialization. + If a model is declared embedded for deserialization (`embedded: 'always'`, + `deserialize: 'record'` or `deserialize: 'records'`), then do not use `async:true`. To successfully extract and serialize embedded records the model relationships must be setup correcty See the @@ -123,11 +124,11 @@ var EmbeddedRecordsMixin = Ember.Mixin.create({ return extractEmbeddedRecords(this, this.store, type, normalizedHash); }, - keyForRelationship: function(key, type) { - if (this.hasDeserializeRecordsOption(key)) { - return this.keyForAttribute(key); + keyForRelationship: function(key, type, method) { + if ((method === 'serialize' && this.hasSerializeRecordsOption(key)) || (method === 'deserialize' && this.hasDeserializeRecordsOption(key))) { + return this.keyForAttribute(key, method); } else { - return this._super(key, type) || key; + return this._super(key, type, method) || key; } }, @@ -191,14 +192,14 @@ var EmbeddedRecordsMixin = Ember.Mixin.create({ var embeddedSnapshot = snapshot.belongsTo(attr); var key; if (includeIds) { - key = this.keyForRelationship(attr, relationship.kind); + key = this.keyForRelationship(attr, relationship.kind, 'serialize'); if (!embeddedSnapshot) { json[key] = null; } else { json[key] = embeddedSnapshot.id; } } else if (includeRecords) { - key = this.keyForAttribute(attr); + key = this.keyForAttribute(attr, 'serialize'); if (!embeddedSnapshot) { json[key] = null; } else { @@ -299,10 +300,10 @@ var EmbeddedRecordsMixin = Ember.Mixin.create({ var includeRecords = this.hasSerializeRecordsOption(attr); var key; if (includeIds) { - key = this.keyForRelationship(attr, relationship.kind); + key = this.keyForRelationship(attr, relationship.kind, 'serialize'); json[key] = snapshot.hasMany(attr, { ids: true }); } else if (includeRecords) { - key = this.keyForAttribute(attr); + key = this.keyForAttribute(attr, 'serialize'); json[key] = snapshot.hasMany(attr).map(function(embeddedSnapshot) { var embeddedJson = embeddedSnapshot.record.serialize({ includeId: true }); this.removeEmbeddedForeignKey(snapshot, embeddedSnapshot, relationship, embeddedJson); @@ -335,7 +336,7 @@ var EmbeddedRecordsMixin = Ember.Mixin.create({ if (parentRecord) { var name = parentRecord.name; var embeddedSerializer = this.store.serializerFor(embeddedSnapshot.type); - var parentKey = embeddedSerializer.keyForRelationship(name, parentRecord.kind); + var parentKey = embeddedSerializer.keyForRelationship(name, parentRecord.kind, 'deserialize'); if (parentKey) { delete json[parentKey]; } diff --git a/packages/ember-data/lib/serializers/json-serializer.js b/packages/ember-data/lib/serializers/json-serializer.js index cfd4e5706a4..e8d65ab5d64 100644 --- a/packages/ember-data/lib/serializers/json-serializer.js +++ b/packages/ember-data/lib/serializers/json-serializer.js @@ -87,6 +87,8 @@ export default Serializer.extend({ ```javascript { + "firstName": "Harry", + "lastName": "Houdini", "career": "magician" } ``` @@ -201,7 +203,7 @@ export default Serializer.extend({ if (this.keyForAttribute) { type.eachAttribute(function(key) { - payloadKey = this.keyForAttribute(key); + payloadKey = this.keyForAttribute(key, 'deserialize'); if (key === payloadKey) { return; } if (!hash.hasOwnProperty(payloadKey)) { return; } @@ -220,7 +222,7 @@ export default Serializer.extend({ if (this.keyForRelationship) { type.eachRelationship(function(key, relationship) { - payloadKey = this.keyForRelationship(key, relationship.kind); + payloadKey = this.keyForRelationship(key, relationship.kind, 'deserialize'); if (key === payloadKey) { return; } if (!hash.hasOwnProperty(payloadKey)) { return; } @@ -572,7 +574,7 @@ export default Serializer.extend({ var belongsTo = snapshot.belongsTo(key); - key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key; + key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo", "serialize") : key; json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.record.toJSON(); } @@ -594,7 +596,7 @@ export default Serializer.extend({ // the serializer var payloadKey = this._getMappedKey(key); if (payloadKey === key && this.keyForRelationship) { - payloadKey = this.keyForRelationship(key, "belongsTo"); + payloadKey = this.keyForRelationship(key, "belongsTo", "serialize"); } //Need to check whether the id is there for new&async records @@ -644,7 +646,7 @@ export default Serializer.extend({ // the serializer payloadKey = this._getMappedKey(key); if (payloadKey === key && this.keyForRelationship) { - payloadKey = this.keyForRelationship(key, "hasMany"); + payloadKey = this.keyForRelationship(key, "hasMany", "serialize"); } var relationshipType = snapshot.type.determineRelationshipType(relationship); diff --git a/packages/ember-data/lib/serializers/rest-serializer.js b/packages/ember-data/lib/serializers/rest-serializer.js index 69a66b5f374..13ed513141c 100644 --- a/packages/ember-data/lib/serializers/rest-serializer.js +++ b/packages/ember-data/lib/serializers/rest-serializer.js @@ -47,8 +47,9 @@ function coerceId(id) { ``` You can also implement `keyForRelationship`, which takes the name - of the relationship as the first parameter, and the kind of - relationship (`hasMany` or `belongsTo`) as the second parameter. + of the relationship as the first parameter, the kind of + relationship (`hasMany` or `belongsTo`) as the second parameter, and + the method (`serialize` or `deserialize`) as the third parameter. @class RESTSerializer @namespace DS @@ -739,7 +740,7 @@ var RESTSerializer = JSONSerializer.extend({ serializePolymorphicType: function(snapshot, json, relationship) { var key = relationship.key; var belongsTo = snapshot.belongsTo(key); - key = this.keyForAttribute ? this.keyForAttribute(key) : key; + key = this.keyForAttribute ? this.keyForAttribute(key, "serialize") : key; if (Ember.isNone(belongsTo)) { json[key + "Type"] = null; } else { diff --git a/packages/ember-data/lib/system/adapter.js b/packages/ember-data/lib/system/adapter.js index 3ec222adb35..fa4080380d1 100644 --- a/packages/ember-data/lib/system/adapter.js +++ b/packages/ember-data/lib/system/adapter.js @@ -230,13 +230,12 @@ var Adapter = Ember.Object.extend({ ``` @method serialize - @param {DS.Model} record + @param {DS.Snapshot} snapshot @param {Object} options - @return {Object} serialized record + @return {Object} serialized snapshot */ - serialize: function(record, options) { - var snapshot = record._createSnapshot(); - return get(record, 'store').serializerFor(snapshot.typeKey).serialize(snapshot, options); + serialize: function(snapshot, options) { + return get(snapshot.record, 'store').serializerFor(snapshot.typeKey).serialize(snapshot, options); }, /** diff --git a/packages/ember-data/lib/system/many-array.js b/packages/ember-data/lib/system/many-array.js index f7ea2e7f7d1..b2a67cdf325 100644 --- a/packages/ember-data/lib/system/many-array.js +++ b/packages/ember-data/lib/system/many-array.js @@ -100,12 +100,12 @@ export default Ember.Object.extend(Ember.MutableArray, Ember.Evented, { */ isLoaded: false, - /** - The relationship which manages this array. + /** + The relationship which manages this array. - @property {ManyRelationship} relationship - @private - */ + @property {ManyRelationship} relationship + @private + */ relationship: null, internalReplace: function(idx, amt, objects) { diff --git a/packages/ember-data/lib/system/model/attributes.js b/packages/ember-data/lib/system/model/attributes.js index 0b10a0179f6..505d4bd38bd 100644 --- a/packages/ember-data/lib/system/model/attributes.js +++ b/packages/ember-data/lib/system/model/attributes.js @@ -3,6 +3,8 @@ import { Map } from "ember-data/system/map"; +import computedPolyfill from "ember-data/utils/computed-polyfill"; + /** @module ember-data */ @@ -296,8 +298,15 @@ export default function attr(type, options) { options: options }; - return Ember.computed(function(key, value) { - if (arguments.length > 1) { + return computedPolyfill({ + get: function(key) { + if (hasValue(this, key)) { + return getValue(this, key); + } else { + return getDefaultValue(this, options, key); + } + }, + set: function(key, value) { Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + this.constructor.toString(), key !== 'id'); var oldValue = getValue(this, key); @@ -315,14 +324,6 @@ export default function attr(type, options) { } return value; - } else if (hasValue(this, key)) { - return getValue(this, key); - } else { - return getDefaultValue(this, options, key); } - - // `data` is never set directly. However, it may be - // invalidated from the state manager's setData - // event. }).meta(meta); } diff --git a/packages/ember-data/lib/system/model/errors.js b/packages/ember-data/lib/system/model/errors.js index e39527e6bc9..458e1443f2c 100644 --- a/packages/ember-data/lib/system/model/errors.js +++ b/packages/ember-data/lib/system/model/errors.js @@ -57,14 +57,14 @@ import { ```handlebars - {{#each error in model.errors.username}} + {{#each model.errors.username as |error|}}
{{error.message}}
{{/each}} - {{#each error in model.errors.email}} + {{#each model.errors.email as |error|}}
{{error.message}}
@@ -75,7 +75,7 @@ import { object to get an array of all the error strings. ```handlebars - {{#each message in model.errors.messages}} + {{#each model.errors.messages as |message|}}
{{message}}
@@ -156,7 +156,7 @@ export default Ember.Object.extend(Ember.Enumerable, Ember.Evented, { record. This is useful for displaying all errors to the user. ```handlebars - {{#each message in model.errors.messages}} + {{#each model.errors.messages as |message|}}
{{message}}
diff --git a/packages/ember-data/lib/system/model/model.js b/packages/ember-data/lib/system/model/model.js index 7cb352ddc80..7a4fbcb60d7 100644 --- a/packages/ember-data/lib/system/model/model.js +++ b/packages/ember-data/lib/system/model/model.js @@ -17,12 +17,10 @@ var forEach = Ember.ArrayPolyfills.forEach; var map = Ember.ArrayPolyfills.map; var intersection = Ember.EnumerableUtils.intersection; var RESERVED_MODEL_PROPS = [ - 'attributes', 'currentState', 'data', - 'relatedTypes', 'relationshipNames', 'relationships', - 'relationshipsByName', 'transformedAttributes', 'store' + 'currentState', 'data', 'store' ]; -var retrieveFromCurrentState = Ember.computed('currentState', function(key, value) { +var retrieveFromCurrentState = Ember.computed('currentState', function(key) { return get(get(this, 'currentState'), key); }).readOnly(); @@ -369,13 +367,13 @@ var Model = Ember.Object.extend(Ember.Evented, { ```handlebars - {{#each error in model.errors.username}} + {{#each model.errors.username as |error|}}
{{error.message}}
{{/each}} - {{#each error in model.errors.email}} + {{#each model.errors.email as |error|}}
{{error.message}}
@@ -387,7 +385,7 @@ var Model = Ember.Object.extend(Ember.Evented, { object to get an array of all the error strings. ```handlebars - {{#each message in model.errors.messages}} + {{#each model.errors.messages as |message|}}
{{message}}
diff --git a/packages/ember-data/lib/system/relationships/belongs-to.js b/packages/ember-data/lib/system/relationships/belongs-to.js index 5a63a0432a6..3a7c681f790 100644 --- a/packages/ember-data/lib/system/relationships/belongs-to.js +++ b/packages/ember-data/lib/system/relationships/belongs-to.js @@ -1,5 +1,7 @@ import Model from 'ember-data/system/model'; +import computedPolyfill from "ember-data/utils/computed-polyfill"; + /** `DS.belongsTo` is used to define One-To-One and One-To-Many relationships on a [DS.Model](/api/data/classes/DS.Model.html). @@ -76,9 +78,12 @@ function belongsTo(type, options) { key: null }; - return Ember.computed(function(key, value) { - if (arguments.length>1) { - if ( value === undefined ) { + return computedPolyfill({ + get: function(key) { + return this._relationships[key].getRecord(); + }, + set: function(key, value) { + if (value === undefined) { value = null; } if (value && value.then) { @@ -86,9 +91,9 @@ function belongsTo(type, options) { } else { this._relationships[key].setRecord(value); } - } - return this._relationships[key].getRecord(); + return this._relationships[key].getRecord(); + } }).meta(meta); } diff --git a/packages/ember-data/lib/system/relationships/state/belongs-to.js b/packages/ember-data/lib/system/relationships/state/belongs-to.js index fe40557b864..f54216d5f95 100644 --- a/packages/ember-data/lib/system/relationships/state/belongs-to.js +++ b/packages/ember-data/lib/system/relationships/state/belongs-to.js @@ -24,12 +24,9 @@ BelongsToRelationship.prototype.updateFromAdapter = function(data) { this.updateLink(data.links[key]); } - if (value === undefined) { - return; + if (value !== undefined) { + this.setCanonicalRecord(value); } - - this.setCanonicalRecord(value); - this.setHasData(true); }; BelongsToRelationship.prototype.setRecord = function(newRecord) { @@ -38,6 +35,7 @@ BelongsToRelationship.prototype.setRecord = function(newRecord) { } else if (this.inverseRecord) { this.removeRecord(this.inverseRecord); } + this.setHasData(true); }; BelongsToRelationship.prototype.setCanonicalRecord = function(newRecord) { @@ -46,6 +44,7 @@ BelongsToRelationship.prototype.setCanonicalRecord = function(newRecord) { } else if (this.inverseRecord) { this.removeCanonicalRecord(this.inverseRecord); } + this.setHasData(true); }; BelongsToRelationship.prototype._super$addCanonicalRecord = Relationship.prototype.addCanonicalRecord; diff --git a/packages/ember-data/lib/system/relationships/state/has-many.js b/packages/ember-data/lib/system/relationships/state/has-many.js index 38a3e4cb379..da9d68c4886 100644 --- a/packages/ember-data/lib/system/relationships/state/has-many.js +++ b/packages/ember-data/lib/system/relationships/state/has-many.js @@ -33,7 +33,6 @@ ManyRelationship.prototype.updateFromAdapter = function(data) { if (value !== undefined) { this.updateRecordsFromAdapter(value); - this.setHasData(true); } }; diff --git a/packages/ember-data/lib/system/relationships/state/relationship.js b/packages/ember-data/lib/system/relationships/state/relationship.js index 4c8f3a6c04b..08b67482de3 100644 --- a/packages/ember-data/lib/system/relationships/state/relationship.js +++ b/packages/ember-data/lib/system/relationships/state/relationship.js @@ -86,6 +86,7 @@ Relationship.prototype = { } } this.flushCanonicalLater(); + this.setHasData(true); }, removeCanonicalRecords: function(records, idx) { @@ -126,6 +127,7 @@ Relationship.prototype = { } this.record.updateRecordArraysLater(); } + this.setHasData(true); }, removeRecord: function(record) { @@ -229,13 +231,14 @@ Relationship.prototype = { var self = this; //TODO Once we have adapter support, we need to handle updated and canonical changes self.computeChanges(records); + self.setHasData(true); }, notifyRecordRelationshipAdded: Ember.K, notifyRecordRelationshipRemoved: Ember.K, - setHasData: function() { - this.hasData = true; + setHasData: function(value) { + this.hasData = value; } }; diff --git a/packages/ember-data/lib/system/snapshot.js b/packages/ember-data/lib/system/snapshot.js index 3e55e481790..0929d35169f 100644 --- a/packages/ember-data/lib/system/snapshot.js +++ b/packages/ember-data/lib/system/snapshot.js @@ -66,7 +66,6 @@ Snapshot.prototype = { ```javascript // store.push('post', { id: 1, author: 'Tomster', title: 'Ember.js rocks' }); - postSnapshot.id; // => '1' ``` @@ -113,7 +112,6 @@ Snapshot.prototype = { ```javascript // store.push('post', { id: 1, author: 'Tomster', title: 'Ember.js rocks' }); - postSnapshot.attr('author'); // => 'Tomster' postSnapshot.attr('title'); // => 'Ember.js rocks' ``` @@ -138,7 +136,6 @@ Snapshot.prototype = { ```javascript // store.push('post', { id: 1, author: 'Tomster', title: 'Hello World' }); - postSnapshot.attributes(); // => { author: 'Tomster', title: 'Ember.js rocks' } ``` @@ -163,26 +160,31 @@ Snapshot.prototype = { ```javascript // store.push('post', { id: 1, title: 'Hello World' }); // store.createRecord('comment', { body: 'Lorem ipsum', post: post }); - commentSnapshot.belongsTo('post'); // => DS.Snapshot commentSnapshot.belongsTo('post', { id: true }); // => '1' + + // store.push('comment', { id: 1, body: 'Lorem ipsum' }); + commentSnapshot.belongsTo('post'); // => undefined ``` - Calling `belongsTo` will return a new Snapshot as long as there's any - data available, such as an ID. If there's no data available `belongsTo` will - return undefined. + Calling `belongsTo` will return a new Snapshot as long as there's any known + data for the relationship available, such as an ID. If the relationship is + known but unset, `belongsTo` will return `null`. If the contents of the + relationship is unknown `belongsTo` will return `undefined`. Note: Relationships are loaded lazily and cached upon first access. @method belongsTo @param {String} keyName @param {Object} [options] - @return {DS.Snapshot|String|undefined} A snapshot or ID of a belongsTo relationship, or undefined + @return {DS.Snapshot|String|null|undefined} A snapshot or ID of a known + relationship or null if the relationship is known but unset. undefined + will be returned if the contents of the relationship is unknown. */ belongsTo: function(keyName, options) { var id = options && options.id; + var relationship, inverseRecord, hasData; var result; - var relationship, inverseRecord; if (id && keyName in this._belongsToIds) { return this._belongsToIds[keyName]; @@ -197,16 +199,24 @@ Snapshot.prototype = { throw new Ember.Error("Model '" + Ember.inspect(this.record) + "' has no belongsTo relationship named '" + keyName + "' defined."); } + hasData = get(relationship, 'hasData'); inverseRecord = get(relationship, 'inverseRecord'); - if (id) { + + if (hasData) { if (inverseRecord) { - result = get(inverseRecord, 'id'); + if (id) { + result = get(inverseRecord, 'id'); + } else { + result = inverseRecord._createSnapshot(); + } + } else { + result = null; } + } + + if (id) { this._belongsToIds[keyName] = result; } else { - if (inverseRecord) { - result = inverseRecord._createSnapshot(); - } this._belongsToRelationships[keyName] = result; } @@ -226,9 +236,11 @@ Snapshot.prototype = { ```javascript // store.push('post', { id: 1, title: 'Hello World', comments: [2, 3] }); - postSnapshot.hasMany('comments'); // => [DS.Snapshot, DS.Snapshot] postSnapshot.hasMany('comments', { ids: true }); // => ['2', '3'] + + // store.push('post', { id: 1, title: 'Hello World' }); + postSnapshot.hasMany('comments'); // => undefined ``` Note: Relationships are loaded lazily and cached upon first access. @@ -236,12 +248,14 @@ Snapshot.prototype = { @method hasMany @param {String} keyName @param {Object} [options] - @return {Array} An array of snapshots or IDs of a hasMany relationship + @return {Array|undefined} An array of snapshots or IDs of a known + relationship or an empty array if the relationship is known but unset. + undefined will be returned if the contents of the relationship is unknown. */ hasMany: function(keyName, options) { var ids = options && options.ids; - var results = []; - var relationship, members; + var relationship, members, hasData; + var results; if (ids && keyName in this._hasManyIds) { return this._hasManyIds[keyName]; @@ -256,17 +270,23 @@ Snapshot.prototype = { throw new Ember.Error("Model '" + Ember.inspect(this.record) + "' has no hasMany relationship named '" + keyName + "' defined."); } + hasData = get(relationship, 'hasData'); members = get(relationship, 'members'); - if (ids) { + if (hasData) { + results = []; members.forEach(function(member) { - results.push(get(member, 'id')); + if (ids) { + results.push(get(member, 'id')); + } else { + results.push(member._createSnapshot()); + } }); + } + + if (ids) { this._hasManyIds[keyName] = results; } else { - members.forEach(function(member) { - results.push(member._createSnapshot()); - }); this._hasManyRelationships[keyName] = results; } diff --git a/packages/ember-data/lib/system/store.js b/packages/ember-data/lib/system/store.js index de7e59b9bc9..0be3f96e189 100644 --- a/packages/ember-data/lib/system/store.js +++ b/packages/ember-data/lib/system/store.js @@ -1073,6 +1073,7 @@ Store = Service.extend({ } typeMap.findAllCache = null; + typeMap.metadata = Ember.create(null); }, /** diff --git a/packages/ember-data/lib/utils/computed-polyfill.js b/packages/ember-data/lib/utils/computed-polyfill.js new file mode 100644 index 00000000000..5384ae7022d --- /dev/null +++ b/packages/ember-data/lib/utils/computed-polyfill.js @@ -0,0 +1,35 @@ +import supportsComputedGetterSetter from './supports-computed-getter-setter'; + +var computed = Ember.computed; + +export default function() { + var polyfillArguments = []; + var config = arguments[arguments.length - 1]; + + if (typeof config === 'function' || supportsComputedGetterSetter) { + return computed.apply(null, arguments); + } + + for (var i = 0, l = arguments.length - 1; i < l; i++) { + polyfillArguments.push(arguments[i]); + } + + var func; + if (config.set) { + func = function(key, value) { + if (arguments.length > 1) { + return config.set.call(this, key, value); + } else { + return config.get.call(this, key); + } + }; + } else { + func = function(key) { + return config.get.call(this, key); + }; + } + + polyfillArguments.push(func); + + return computed.apply(null, polyfillArguments); +} diff --git a/packages/ember-data/lib/utils/supports-computed-getter-setter.js b/packages/ember-data/lib/utils/supports-computed-getter-setter.js new file mode 100644 index 00000000000..19f290a4027 --- /dev/null +++ b/packages/ember-data/lib/utils/supports-computed-getter-setter.js @@ -0,0 +1,13 @@ +var supportsComputedGetterSetter; + +try { + Ember.computed({ + get: function() { }, + set: function() { } + }); + supportsComputedGetterSetter = true; +} catch(e) { + supportsComputedGetterSetter = false; +} + +export default supportsComputedGetterSetter; diff --git a/packages/ember-data/tests/integration/adapter/build-url-mixin-test.js b/packages/ember-data/tests/integration/adapter/build-url-mixin-test.js index 63906a03640..53053784d2f 100644 --- a/packages/ember-data/tests/integration/adapter/build-url-mixin-test.js +++ b/packages/ember-data/tests/integration/adapter/build-url-mixin-test.js @@ -291,3 +291,16 @@ test('buildURL - buildURL takes a record from delete', function() { }); }); +test('buildURL - with absolute namespace', function() { + run(function() { + adapter.setProperties({ + namespace: '/api/v1' + }); + }); + + ajaxResponse({ posts: [{ id: 1 }] }); + + run(store, 'find', 'post', 1).then(async(function(post) { + equal(passedUrl, "/api/v1/posts/1"); + })); +}); diff --git a/packages/ember-data/tests/integration/adapter/find-all-test.js b/packages/ember-data/tests/integration/adapter/find-all-test.js index 2a6ea767c4d..b870cd536e1 100644 --- a/packages/ember-data/tests/integration/adapter/find-all-test.js +++ b/packages/ember-data/tests/integration/adapter/find-all-test.js @@ -57,8 +57,9 @@ test("When all records for a type are requested, a rejection should reject the p expect(5); var count = 0; - store = createStore({ adapter: DS.Adapter.extend({ - findAll: function(store, type, since) { + store = createStore({ + adapter: DS.Adapter.extend({ + findAll: function(store, type, since) { // this will get called twice ok(true, "the adapter's findAll method should be invoked"); diff --git a/packages/ember-data/tests/integration/records/unload-test.js b/packages/ember-data/tests/integration/records/unload-test.js index 25a3fa078d8..75f609ca054 100644 --- a/packages/ember-data/tests/integration/records/unload-test.js +++ b/packages/ember-data/tests/integration/records/unload-test.js @@ -68,6 +68,24 @@ test("can unload all records for a given type", function () { equal(env.store.all('person').get('length'), 0); }); +test("Unloading all records for a given type clears saved meta data.", function () { + + function metadataKeys(type) { + return Ember.keys(env.store.metadataFor(type)); + } + + run(function() { + env.store.setMetadataFor('person', { count: 10 }); + }); + + Ember.run(function() { + env.store.unloadAll('person'); + }); + + deepEqual(metadataKeys('person'), [], 'Metadata for person is empty'); + +}); + test("removes findAllCache after unloading all records", function () { var adam, bob; run(function() { diff --git a/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js b/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js index 93f83a2f29f..70d90ab191c 100644 --- a/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js +++ b/packages/ember-data/tests/integration/serializers/embedded-records-mixin-test.js @@ -934,6 +934,41 @@ test("serialize with embedded object (belongsTo relationship) supports serialize }); }); +test("serialize with embedded object (belongsTo relationship) supports serialize:id in conjunction with deserialize:records", function() { + env.registry.register('adapter:superVillain', DS.ActiveModelAdapter); + env.registry.register('serializer:superVillain', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { + attrs: { + secretLab: { serialize: 'id', deserialize: 'records' } + } + })); + + var serializer = env.container.lookup("serializer:superVillain"); + + // records with an id, persisted + var tom, json; + + run(function() { + tom = env.store.createRecord( + SuperVillain, + { firstName: "Tom", lastName: "Dale", id: "1", + secretLab: env.store.createRecord(SecretLab, { minionCapacity: 5000, vicinity: "California, USA", id: "101" }), + homePlanet: env.store.createRecord(HomePlanet, { name: "Villain League", id: "123" }) + } + ); + }); + + run(function() { + json = serializer.serialize(tom._createSnapshot()); + }); + + deepEqual(json, { + first_name: get(tom, "firstName"), + last_name: get(tom, "lastName"), + home_planet_id: get(tom, "homePlanet").get("id"), + secret_lab_id: get(tom, "secretLab").get("id") + }); +}); + test("serialize with embedded object (belongsTo relationship) supports serialize:false", function() { env.registry.register('adapter:superVillain', DS.ActiveModelAdapter); env.registry.register('serializer:superVillain', DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { diff --git a/packages/ember-data/tests/integration/serializers/json-serializer-test.js b/packages/ember-data/tests/integration/serializers/json-serializer-test.js index 62ac17b5e97..7a7af6450aa 100644 --- a/packages/ember-data/tests/integration/serializers/json-serializer-test.js +++ b/packages/ember-data/tests/integration/serializers/json-serializer-test.js @@ -88,7 +88,7 @@ test("serializeBelongsTo with null", function() { test("async serializeBelongsTo with null", function() { Comment.reopen({ post: DS.belongsTo('post', { async: true }) - }); + }); run(function() { comment = env.store.createRecord(Comment, { body: "Omakase is delicious", post: null }); }); diff --git a/packages/ember-data/tests/integration/snapshot-test.js b/packages/ember-data/tests/integration/snapshot-test.js index ee1a03a2c2a..efbe9c0ad93 100644 --- a/packages/ember-data/tests/integration/snapshot-test.js +++ b/packages/ember-data/tests/integration/snapshot-test.js @@ -124,6 +124,19 @@ test("snapshot.belongsTo() returns undefined if relationship is undefined", func }); }); +test("snapshot.belongsTo() returns null if relationship is unset", function() { + expect(1); + + run(function() { + env.store.push('post', { id: 1, title: 'Hello World' }); + var comment = env.store.push('comment', { id: 2, body: 'This is comment', post: null }); + var snapshot = comment._createSnapshot(); + var relationship = snapshot.belongsTo('post'); + + equal(relationship, null, 'relationship is unset'); + }); +}); + test("snapshot.belongsTo() returns a snapshot if relationship is set", function() { expect(3); @@ -139,6 +152,86 @@ test("snapshot.belongsTo() returns a snapshot if relationship is set", function( }); }); +test("snapshot.belongsTo() returns undefined if relationship is a link", function() { + expect(1); + + run(function() { + var comment = env.store.push('comment', { id: 2, body: 'This is comment', links: { post: 'post' } }); + var snapshot = comment._createSnapshot(); + var relationship = snapshot.belongsTo('post'); + + equal(relationship, undefined, 'relationship is undefined'); + }); +}); + +test("snapshot.belongsTo() returns a snapshot if relationship link has been fetched", function() { + expect(2); + + env.adapter.findBelongsTo = function(store, snapshot, link, relationship) { + return Ember.RSVP.resolve({ id: 1, title: 'Hello World' }); + }; + + run(function() { + var comment = env.store.push('comment', { id: 2, body: 'This is comment', links: { post: 'post' } }); + + comment.get('post').then(function(post) { + var snapshot = comment._createSnapshot(); + var relationship = snapshot.belongsTo('post'); + + ok(relationship instanceof DS.Snapshot, 'snapshot is an instance of DS.Snapshot'); + equal(relationship.id, '1', 'post id is correct'); + }); + }); +}); + +test("snapshot.belongsTo() and snapshot.hasMany() returns correctly when adding an object to a hasMany relationship", function() { + expect(4); + + run(function() { + var post = env.store.push('post', { id: 1, title: 'Hello World' }); + var comment = env.store.push('comment', { id: 2, body: 'blabla' }); + + post.get('comments').then(function(comments) { + comments.addObject(comment); + + var postSnapshot = post._createSnapshot(); + var commentSnapshot = comment._createSnapshot(); + + var hasManyRelationship = postSnapshot.hasMany('comments'); + var belongsToRelationship = commentSnapshot.belongsTo('post'); + + ok(hasManyRelationship instanceof Array, 'hasMany relationship is an instance of Array'); + equal(hasManyRelationship.length, 1, 'hasMany relationship contains related object'); + + ok(belongsToRelationship instanceof DS.Snapshot, 'belongsTo relationship is an instance of DS.Snapshot'); + equal(belongsToRelationship.attr('title'), 'Hello World', 'belongsTo relationship contains related object'); + }); + }); +}); + +test("snapshot.belongsTo() and snapshot.hasMany() returns correctly when setting an object to a belongsTo relationship", function() { + expect(4); + + run(function() { + var post = env.store.push('post', { id: 1, title: 'Hello World' }); + var comment = env.store.push('comment', { id: 2, body: 'blabla' }); + + comment.set('post', post); + + var postSnapshot = post._createSnapshot(); + var commentSnapshot = comment._createSnapshot(); + + var hasManyRelationship = postSnapshot.hasMany('comments'); + var belongsToRelationship = commentSnapshot.belongsTo('post'); + + ok(hasManyRelationship instanceof Array, 'hasMany relationship is an instance of Array'); + equal(hasManyRelationship.length, 1, 'hasMany relationship contains related object'); + + ok(belongsToRelationship instanceof DS.Snapshot, 'belongsTo relationship is an instance of DS.Snapshot'); + equal(belongsToRelationship.attr('title'), 'Hello World', 'belongsTo relationship contains related object'); + }); +}); + test("snapshot.hasMany() returns ID if option.id is set", function() { expect(1); @@ -152,14 +245,26 @@ test("snapshot.hasMany() returns ID if option.id is set", function() { }); }); -test("snapshot.hasMany() returns empty array if relationship is undefined", function() { - expect(2); +test("snapshot.hasMany() returns undefined if relationship is undefined", function() { + expect(1); run(function() { var post = env.store.push('post', { id: 1, title: 'Hello World' }); var snapshot = post._createSnapshot(); var relationship = snapshot.hasMany('comments'); + equal(relationship, undefined, 'relationship is undefined'); + }); +}); + +test("snapshot.hasMany() returns empty array if relationship is unset", function() { + expect(2); + + run(function() { + var post = env.store.push('post', { id: 1, title: 'Hello World', comments: null }); + var snapshot = post._createSnapshot(); + var relationship = snapshot.hasMany('comments'); + ok(relationship instanceof Array, 'relationship is an instance of Array'); equal(relationship.length, 0, 'relationship is empty'); }); @@ -199,6 +304,38 @@ test("snapshot.hasMany() returns array of IDs if option.ids is set", function() }); }); +test("snapshot.hasMany() returns undefined if relationship is a link", function() { + expect(1); + + run(function() { + var post = env.store.push('post', { id: 1, title: 'Hello World', links: { comments: 'comments' } }); + var snapshot = post._createSnapshot(); + var relationship = snapshot.hasMany('comments'); + + equal(relationship, undefined, 'relationship is undefined'); + }); +}); + +test("snapshot.hasMany() returns array of snapshots if relationship link has been fetched", function() { + expect(2); + + env.adapter.findHasMany = function(store, snapshot, link, relationship) { + return Ember.RSVP.resolve([{ id: 2, body: 'This is comment' }]); + }; + + run(function() { + var post = env.store.push('post', { id: 1, title: 'Hello World', links: { comments: 'comments' } }); + + post.get('comments').then(function(comments) { + var snapshot = post._createSnapshot(); + var relationship = snapshot.hasMany('comments'); + + ok(relationship instanceof Array, 'relationship is an instance of Array'); + equal(relationship.length, 1, 'relationship has one item'); + }); + }); +}); + test("snapshot.hasMany() respects the order of items in the relationship", function() { expect(3); diff --git a/packages/ember-data/tests/unit/adapters/build-url-mixin/path-for-type-test.js b/packages/ember-data/tests/unit/adapters/build-url-mixin/path-for-type-test.js index c2239fe5582..fcf068d8946 100644 --- a/packages/ember-data/tests/unit/adapters/build-url-mixin/path-for-type-test.js +++ b/packages/ember-data/tests/unit/adapters/build-url-mixin/path-for-type-test.js @@ -2,7 +2,16 @@ var env, adapter; module("unit/adapters/build-url-mixin/path-for-type - DS.BuildURLMixin#pathForType", { setup: function() { - var Adapter = DS.Adapter.extend(DS.BuildURLMixin); + + // test for overriden pathForType methods which return null path values + var customPathForType = { + pathForType: function(type) { + if (type === 'rootModel') { return ''; } + return this._super(type); + } + }; + + var Adapter = DS.Adapter.extend(DS.BuildURLMixin, customPathForType); env = setupStore({ adapter: Adapter @@ -23,3 +32,106 @@ test('pathForType - works with dasherized types', function() { test('pathForType - works with underscored types', function() { equal(adapter.pathForType('super_user'), "superUsers"); }); + +test('buildURL - works with empty paths', function() { + equal(adapter.buildURL('rootModel', 1), "/1"); +}); + +test('buildURL - find requestType delegates to urlForFind', function() { + expect(4); + var snapshotStub = { snapshot: true }; + var originalMethod = adapter.urlForFind; + adapter.urlForFind = function(id, type, snapshot) { + equal(id, 1); + equal(type, 'super-user'); + equal(snapshot, snapshotStub); + return originalMethod.apply(this, arguments); + }; + equal(adapter.buildURL('super-user', 1, snapshotStub, 'find'), '/superUsers/1'); +}); + +test('buildURL - findAll requestType delegates to urlForFindAll', function() { + expect(2); + var originalMethod = adapter.urlForFindAll; + adapter.urlForFindAll = function(type) { + equal(type, 'super-user'); + return originalMethod.apply(this, arguments); + }; + equal(adapter.buildURL('super-user', null, null, 'findAll'), '/superUsers'); +}); + +test('buildURL - findQuery requestType delegates to urlForFindQuery', function() { + expect(3); + var originalMethod = adapter.urlForFindQuery; + var queryStub = { limit: 10 }; + adapter.urlForFindQuery = function(query, type) { + equal(query, queryStub); + equal(type, 'super-user'); + return originalMethod.apply(this, arguments); + }; + equal(adapter.buildURL('super-user', queryStub, null, 'findQuery'), '/superUsers'); +}); + +test('buildURL - findMany requestType delegates to urlForFindMany', function() { + expect(3); + var originalMethod = adapter.urlForFindMany; + var idsStub = [1, 2, 3]; + adapter.urlForFindMany = function(ids, type) { + equal(ids, idsStub); + equal(type, 'super-user'); + return originalMethod.apply(this, arguments); + }; + equal(adapter.buildURL('super-user', idsStub, null, 'findMany'), '/superUsers'); +}); + +test('buildURL - findHasMany requestType delegates to urlForFindHasMany', function() { + expect(3); + var originalMethod = adapter.urlForFindHasMany; + adapter.urlForFindHasMany = function(id, type) { + equal(id, 1); + equal(type, 'super-user'); + return originalMethod.apply(this, arguments); + }; + equal(adapter.buildURL('super-user', 1, null, 'findHasMany'), '/superUsers/1'); +}); + +test('buildURL - findBelongsTo requestType delegates to urlForFindBelongsTo', function() { + expect(3); + var originalMethod = adapter.urlForFindBelongsTo; + adapter.urlForFindBelongsTo = function(id, type) { + equal(id, 1); + equal(type, 'super-user'); + return originalMethod.apply(this, arguments); + }; + equal(adapter.buildURL('super-user', 1, null, 'findBelongsTo'), '/superUsers/1'); +}); + +test('buildURL - createRecord requestType delegates to urlForFindBelongsTo', function() { + expect(3); + var snapshotStub = { snapshot: true }; + var originalMethod = adapter.urlForCreateRecord; + adapter.urlForCreateRecord = function(type, snapshot) { + equal(type, 'super-user'); + equal(snapshot, snapshotStub); + return originalMethod.apply(this, arguments); + }; + equal(adapter.buildURL('super-user', null, snapshotStub, 'createRecord'), '/superUsers'); +}); + +test('buildURL - deleteRecord requestType delegates to urlForDeleteRecord', function() { + expect(4); + var snapshotStub = { snapshot: true }; + var originalMethod = adapter.urlForDeleteRecord; + adapter.urlForDeleteRecord = function(id, type, snapshot) { + equal(id, 1); + equal(type, 'super-user'); + equal(snapshot, snapshotStub); + return originalMethod.apply(this, arguments); + }; + equal(adapter.buildURL('super-user', 1, snapshotStub, 'deleteRecord'), '/superUsers/1'); +}); + +test('buildURL - unknown requestType', function() { + equal(adapter.buildURL('super-user', 1, null, 'unknown'), '/superUsers/1'); + equal(adapter.buildURL('super-user', null, null, 'unknown'), '/superUsers'); +}); diff --git a/packages/ember-data/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js b/packages/ember-data/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js index f5a63fdf310..2de89a7561c 100644 --- a/packages/ember-data/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js +++ b/packages/ember-data/tests/unit/adapters/rest-adapter/group-records-for-find-many-test.js @@ -32,7 +32,7 @@ module("unit/adapters/rest_adapter/group_records_for_find_many_test - DS.RESTAda Store = createStore({ adapter: GroupsAdapter, testRecord: DS.Model.extend() - }); + }); } }); diff --git a/packages/ember-data/tests/unit/model-test.js b/packages/ember-data/tests/unit/model-test.js index 3240fec1100..35b8dc40b9e 100644 --- a/packages/ember-data/tests/unit/model-test.js +++ b/packages/ember-data/tests/unit/model-test.js @@ -725,11 +725,9 @@ test("A subclass of DS.Model can not use the `store` property", function() { }); test("A subclass of DS.Model can not use reserved properties", function() { - expect(9); + expect(3); [ - 'attributes', 'currentState', 'data', - 'relatedTypes', 'relationshipNames', 'relationships', - 'relationshipsByName', 'transformedAttributes', 'store' + 'currentState', 'data', 'store' ].forEach(function(reservedProperty) { var invalidExtendObject = {}; invalidExtendObject[reservedProperty] = DS.attr(); diff --git a/packages/ember-data/tests/unit/model/merge-test.js b/packages/ember-data/tests/unit/model/merge-test.js index ed3a91ea2d8..bd84896158b 100644 --- a/packages/ember-data/tests/unit/model/merge-test.js +++ b/packages/ember-data/tests/unit/model/merge-test.js @@ -45,7 +45,7 @@ test("When a record is in flight, pushes are applied underneath the in flight ch var adapter = DS.Adapter.extend({ updateRecord: function(store, type, snapshot) { - // Make sure saving isn't resolved synchronously + // Make sure saving isn't resolved synchronously return new Ember.RSVP.Promise(function(resolve, reject) { run.next(null, resolve, { id: 1, name: "Senor Thomas Dale, Esq.", city: "Portland" }); }); diff --git a/packages/ember/lib/main.js b/packages/ember/lib/main.js index 0cce0afccb8..ddf3b4462ee 100644 --- a/packages/ember/lib/main.js +++ b/packages/ember/lib/main.js @@ -1,2 +1,2 @@ // Shim Ember module -export default self.Ember; +export default Ember; diff --git a/tests/ember-configuration.js b/tests/ember-configuration.js index d7f05d6c1d5..bda654b8593 100644 --- a/tests/ember-configuration.js +++ b/tests/ember-configuration.js @@ -48,11 +48,10 @@ window.setupStore = function(options) { var container, registry; - var emberChannel = QUnit.urlParams.emberchannel || "release"; var env = {}; options = options || {}; - if (emberChannel.match(/^beta|canary$/i)) { + if (Ember.Registry) { registry = env.registry = new Ember.Registry(); container = env.container = registry.container(); } else {