From ee5b351398a492da3bd35ef9c5fa562d40fb80bd Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Thu, 30 Jan 2014 11:51:34 -0800 Subject: [PATCH] Make sure own properties are copied by toObject for non-strict mode See https://github.com/strongloop/loopback/issues/162 --- lib/model.js | 27 +++++++++++++++++++++++---- test/loopback-dl.test.js | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/lib/model.js b/lib/model.js index dbc23d5f..c6c0c5de 100644 --- a/lib/model.js +++ b/lib/model.js @@ -199,16 +199,20 @@ ModelBaseClass.toString = function () { /** * Convert instance to Object * - * @param {Boolean} onlySchema - restrict properties to dataSource only, default false + * @param {Boolean} onlySchema - restrict properties to dataSource only, default true * when onlySchema == true, only properties defined in dataSource returned, * otherwise all enumerable properties returned * @returns {Object} - canonical object representation (no getters and setters) */ ModelBaseClass.prototype.toObject = function (onlySchema) { + if(onlySchema === undefined) { + onlySchema = true; + } var data = {}; var self = this; - var schemaLess = this.constructor.definition.settings.strict === false || !onlySchema; + var strict = this.constructor.definition.settings.strict; + var schemaLess = strict === false || !onlySchema; this.constructor.forEachProperty(function (propertyName) { if (self[propertyName] instanceof List) { data[propertyName] = self[propertyName].toObject(!schemaLess); @@ -223,10 +227,25 @@ ModelBaseClass.prototype.toObject = function (onlySchema) { } }); + var val = null; if (schemaLess) { - for (var propertyName in self.__data) { + // Find its own properties which can be set via myModel.myProperty = 'myValue'. + // If the property is not declared in the model definition, no setter will be + // triggered to add it to __data + for (var propertyName in self) { + if(self.hasOwnProperty(propertyName) && (!data.hasOwnProperty(propertyName))) { + val = self[propertyName]; + if (val !== undefined && val !== null && val.toObject) { + data[propertyName] = val.toObject(!schemaLess); + } else { + data[propertyName] = val; + } + } + } + // Now continue to check __data + for (propertyName in self.__data) { if (!data.hasOwnProperty(propertyName)) { - var val = self.hasOwnProperty(propertyName) ? self[propertyName] : self.__data[propertyName]; + val = self.hasOwnProperty(propertyName) ? self[propertyName] : self.__data[propertyName]; if (val !== undefined && val !== null && val.toObject) { data[propertyName] = val.toObject(!schemaLess); } else { diff --git a/test/loopback-dl.test.js b/test/loopback-dl.test.js index 0f49514f..36434222 100644 --- a/test/loopback-dl.test.js +++ b/test/loopback-dl.test.js @@ -54,6 +54,26 @@ describe('ModelBuilder define model', function () { done(null, User); }); + it('should ignore non-predefined properties in strict mode', function (done) { + var modelBuilder = new ModelBuilder(); + + var User = modelBuilder.define('User', {name: String, bio: String}, {strict: true}); + + var user = new User({name: 'Joe'}); + user.age = 10; + user.bio = 'me'; + + assert(user.name === 'Joe'); + assert(user.bio === 'me'); + assert(user.toObject().age === undefined); + assert(user.toObject(true).age === undefined); + assert(user.toObject(false).age === 10); + assert(user.toObject().bio === 'me'); + assert(user.toObject(true).bio === 'me'); + assert(user.toObject(false).bio === 'me'); + done(null, User); + }); + it('should throw when unknown properties are used if strict=throw', function (done) { var modelBuilder = new ModelBuilder(); @@ -83,6 +103,26 @@ describe('ModelBuilder define model', function () { done(null, User); }); + it('should take non-predefined properties in non-strict mode', function (done) { + var modelBuilder = new ModelBuilder(); + + var User = modelBuilder.define('User', {name: String, bio: String}, {strict: false}); + + var user = new User({name: 'Joe'}); + user.age = 10; + user.bio = 'me'; + + assert(user.name === 'Joe'); + assert(user.bio === 'me'); + assert(user.toObject().age === 10); + assert(user.toObject(false).age === 10); + assert(user.toObject(true).age === 10); + assert(user.toObject().bio === 'me'); + assert(user.toObject(true).bio === 'me'); + assert(user.toObject(false).bio === 'me'); + done(null, User); + }); + it('should use false as the default value for strict', function (done) { var modelBuilder = new ModelBuilder();