diff --git a/addon/models/resource.js b/addon/models/resource.js index 73f97bd..5bb66ed 100644 --- a/addon/models/resource.js +++ b/addon/models/resource.js @@ -103,7 +103,7 @@ const Resource = Ember.Object.extend(ResourceOperationsMixin, { _attributes: null, /** - Flag for new instance, e.g. not peristed + Flag for new instance, e.g. not persisted @property isNew @type Boolean @@ -413,21 +413,24 @@ Resource.reopenClass({ `attributes`, `links` and `relationships` */ create(properties) { + properties = properties || {}; const prototype = {}; const attrs = Ember.String.w('_attributes attributes links meta relationships'); for (let i = 0; i < attrs.length; i++) { prototype[attrs[i]] = {}; } + // JSONAPI ids MUST be strings. + // Without an id we default isNew to true (unless otherwise specified) + if (properties.hasOwnProperty('id')) { + properties.id = properties.id.toString(); + } else if (typeof properties.isNew === 'undefined') { + properties.isNew = true; + } + const instance = this._super(prototype); - if (properties) { - if (properties.hasOwnProperty('id')) { - // JSONAPI ids MUST be strings. - properties.id = properties.id.toString(); - } + instance.setProperties(properties); - instance.setProperties(properties); - } let type = singularize(instance.get('type')); let msg = (type) ? Ember.String.capitalize(type) : 'Resource'; let factory = 'model:' + type; diff --git a/blueprints/jsonapi-model/files/__root__/__path__/__name__.js b/blueprints/jsonapi-model/files/__root__/__path__/__name__.js index fa45305..cc5d41a 100644 --- a/blueprints/jsonapi-model/files/__root__/__path__/__name__.js +++ b/blueprints/jsonapi-model/files/__root__/__path__/__name__.js @@ -13,7 +13,6 @@ let <%= classifiedModuleName %> = Resource.extend({ getDefaults() { return { - isNew: true, attributes: {} }; } diff --git a/tests/unit/adapters/application-test.js b/tests/unit/adapters/application-test.js index 63c1852..a95c333 100644 --- a/tests/unit/adapters/application-test.js +++ b/tests/unit/adapters/application-test.js @@ -276,7 +276,7 @@ test('#findRelated is called with optional type for the resource', function (ass }); test('#createResource', function(assert) { - assert.expect(6); + assert.expect(8); let done = assert.async(); const adapter = this.subject({type: 'posts', url: '/posts'}); @@ -287,6 +287,7 @@ test('#createResource', function(assert) { let newResource = postFactory.create(data); assert.equal(newResource.get('id'), null, 'new resource does not have an id'); + assert.equal(newResource.get('isNew'), true, 'new resource isNew'); adapter.serializer = { serialize: function () { return data; } }; let persistedResource = postFactory.create(postMock.data); @@ -301,6 +302,7 @@ test('#createResource', function(assert) { '#fetch called with url and options with data' ); assert.equal(newResource.get('id'), postMock.data.id, 'new resource now has an id'); + assert.equal(newResource.get('isNew'), false, 'new resource no longer isNew'); assert.deepEqual(resp, newResource, 'response is the same resource instance sent as arg'); done(); }); diff --git a/tests/unit/models/resource-test.js b/tests/unit/models/resource-test.js index d6ba535..3f67c1d 100644 --- a/tests/unit/models/resource-test.js +++ b/tests/unit/models/resource-test.js @@ -27,12 +27,25 @@ moduleFor('model:resource', 'Unit | Model | resource', { } }); -test('it creates an instance, default flag for isNew is false', function(assert) { +test('it creates an instance', function (assert) { let resource = this.subject(); assert.ok(!!resource); - assert.equal(resource.get('isNew'), false, 'default value for isNew flag set to `false`'); }); +test('creating an instance WITHOUT id has flag for isNew set to true', function(assert) { + let resource = this.subject(); + assert.equal(resource.get('isNew'), true, 'without id, default value for isNew flag set to `true`'); +}); +test('creating an instance WITH id has flag for isNew set to false', function(assert) { + let resource = this.subject({id: 1}); + assert.equal(resource.get('isNew'), false, 'without id, default value for isNew flag set to `false`'); +}); +test('creating an instance allows isNew regardless of id/defaults', function (assert) { + let notIsNewResource = this.subject({isNew: false}); + let yesIsNewResource = this.subject({id: 1, isNew: true}); + assert.equal(notIsNewResource.get('isNew'), false, 'without id, isNew property is honored'); + assert.equal(yesIsNewResource.get('isNew'), true, 'with id, isNew property is honored'); +}); test('in creating instances, ids are cast to string', function (assert) { let id = 1; let post = this.container.lookup('model:post').create({ @@ -118,7 +131,8 @@ test('attr() helper creates a computed property using a unique (protected) attri test('#changedAttributes', function(assert) { let post = this.container.lookup('model:post').create({ - attributes: {id: '1', title: 'Wyatt Earp', excerpt: 'Was a gambler.'} + id: 1, + attributes: {title: 'Wyatt Earp', excerpt: 'Was a gambler.'} }); assert.equal(post.get('excerpt'), 'Was a gambler.', 'excerpt is set "Was a gambler."'); post.set('excerpt', 'Became a deputy.'); @@ -131,7 +145,8 @@ test('#changedAttributes', function(assert) { test('#previousAttributes', function(assert) { let post = this.container.lookup('model:post').create({ - id: '1', attributes: {title: 'Wyatt Earp', excerpt: 'Was a gambler.'} + id: '1', + attributes: {title: 'Wyatt Earp', excerpt: 'Was a gambler.'} }); assert.equal(post.get('excerpt'), 'Was a gambler.', 'excerpt is set to "Was a gambler."'); post.set('excerpt', 'Became a deputy.'); @@ -144,7 +159,8 @@ test('#previousAttributes', function(assert) { test('#rollback resets attributes based on #previousAttributes', function(assert) { let post = this.container.lookup('model:post').create({ - id: '1', attributes: {title: 'Wyatt Earp', excerpt: 'Was a gambler.'} + id: '1', + attributes: {title: 'Wyatt Earp', excerpt: 'Was a gambler.'} }); assert.equal(post.get('excerpt'), 'Was a gambler.', 'excerpt is set to "Was a gambler."'); post.set('excerpt', 'Became a deputy.');