From 6f236063dbc11b6fddb3e10e31d1ba00103a709e Mon Sep 17 00:00:00 2001 From: Justin Meyer Date: Mon, 3 Sep 2018 10:42:56 -0500 Subject: [PATCH 1/3] uses proper defineExpando even in instantiation --- can-define.js | 65 +++++++++++++++++++++++++++++--- define-helpers/define-helpers.js | 55 +-------------------------- map/map-test.js | 11 ++++++ 3 files changed, 72 insertions(+), 59 deletions(-) diff --git a/can-define.js b/can-define.js index 472d73e..72d19b4 100644 --- a/can-define.js +++ b/can-define.js @@ -1013,11 +1013,7 @@ define.setup = function(props, sealed) { if(definitions[prop] !== undefined) { map[prop] = value; } else { - var def = define.makeSimpleGetterSetter(prop); - instanceDefinitions[prop] = {}; - Object_defineNamedPrototypeProperty(map, prop, def); - // possibly convert value to List or DefineMap - map[prop] = define.types.observable(value); + define.expando(map, prop, value); } }); if(canReflect.size(instanceDefinitions) > 0) { @@ -1034,6 +1030,65 @@ define.setup = function(props, sealed) { } //!steal-remove-end }; + + +var returnFirstArg = function(arg){ + return arg; +}; + +define.expando = function(map, prop, value) { + if(define._specialKeys[prop]) { + // ignores _data and _computed + return true; + } + // first check if it's already a constructor define + var constructorDefines = map._define.definitions; + if(constructorDefines && constructorDefines[prop]) { + return; + } + // next if it's already on this instances + var instanceDefines = map._instanceDefinitions; + if(!instanceDefines) { + if(Object.isSealed(map)) { + return; + } + Object.defineProperty(map, "_instanceDefinitions", { + configurable: true, + enumerable: false, + writable: true, + value: {} + }); + instanceDefines = map._instanceDefinitions; + } + if(!instanceDefines[prop]) { + var defaultDefinition = map._define.defaultDefinition || {type: define.types.observable}; + define.property(map, prop, defaultDefinition, {},{}); + // possibly convert value to List or DefineMap + if(defaultDefinition.type) { + map._data[prop] = define.make.set.type(prop, defaultDefinition.type, returnFirstArg).call(map, value); + } else { + map._data[prop] = define.types.observable(value); + } + + instanceDefines[prop] = defaultDefinition; + if(!map.__inSetup) { + queues.batch.start(); + map.dispatch({ + type: "can.keys", + target: map + }); + if(map._data[prop] !== undefined) { + map.dispatch({ + type: prop, + target: map, + patches: [{type: "set", key: prop, value: map._data[prop]}], + },[map._data[prop], undefined]); + } + queues.batch.stop(); + } + return true; + } +}; define.replaceWith = defineLazyValue; define.eventsProto = eventsProto; define.defineConfigurableAndNotEnumerable = defineConfigurableAndNotEnumerable; diff --git a/define-helpers/define-helpers.js b/define-helpers/define-helpers.js index 6e102d7..9d678a6 100644 --- a/define-helpers/define-helpers.js +++ b/define-helpers/define-helpers.js @@ -5,62 +5,9 @@ var queues = require("can-queues"); var dev = require("can-log/dev/dev"); var ensureMeta = require("../ensure-meta"); -var returnFirstArg = function(arg){ - return arg; -}; var defineHelpers = { // returns `true` if the value was defined and set - defineExpando: function(map, prop, value) { - if(define._specialKeys[prop]) { - // ignores _data and _computed - return true; - } - // first check if it's already a constructor define - var constructorDefines = map._define.definitions; - if(constructorDefines && constructorDefines[prop]) { - return; - } - // next if it's already on this instances - var instanceDefines = map._instanceDefinitions; - if(!instanceDefines) { - if(Object.isSealed(map)) { - return; - } - Object.defineProperty(map, "_instanceDefinitions", { - configurable: true, - enumerable: false, - writable: true, - value: {} - }); - instanceDefines = map._instanceDefinitions; - } - if(!instanceDefines[prop]) { - var defaultDefinition = map._define.defaultDefinition || {type: define.types.observable}; - define.property(map, prop, defaultDefinition, {},{}); - // possibly convert value to List or DefineMap - if(defaultDefinition.type) { - map._data[prop] = define.make.set.type(prop, defaultDefinition.type, returnFirstArg).call(map, value); - } else { - map._data[prop] = define.types.observable(value); - } - - instanceDefines[prop] = defaultDefinition; - queues.batch.start(); - map.dispatch({ - type: "can.keys", - target: map - }); - if(map._data[prop] !== undefined) { - map.dispatch({ - type: prop, - target: map, - patches: [{type: "set", key: prop, value: map._data[prop]}], - },[map._data[prop], undefined]); - } - queues.batch.stop(); - return true; - } - }, + defineExpando: define.expando, reflectSerialize: function(unwrapped){ var constructorDefinitions = this._define.definitions; var defaultDefinition = this._define.defaultDefinition; diff --git a/map/map-test.js b/map/map-test.js index f04ccd4..181fe5d 100644 --- a/map/map-test.js +++ b/map/map-test.js @@ -1426,3 +1426,14 @@ QUnit.test("type called with `this` as the map (#349)", function(){ var map = new Type(); QUnit.equal(map.foo, 5); }); + +QUnit.test("expandos use default type (#383)", function(){ + var AllNumbers = DefineMap.extend({ + "*": {type: "number"} + }); + + var someNumbers = new AllNumbers({ + version: "24" + }); + QUnit.ok(someNumbers.version === 24, "is 24"); +}); From 3ee775b20f383806a67870a6ae5f3b21578c5e72 Mon Sep 17 00:00:00 2001 From: Justin Meyer Date: Mon, 3 Sep 2018 15:20:01 -0500 Subject: [PATCH 2/3] partially fixes #369, methods are still enumerable --- can-define.js | 3 ++- map/map-test.js | 19 +++++++++++++++++++ map/map.js | 5 ++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/can-define.js b/can-define.js index 72d19b4..625d870 100644 --- a/can-define.js +++ b/can-define.js @@ -929,7 +929,8 @@ getDefinitionsAndMethods = function(defines, baseDefines, typePrototype) { } }); if(defaults) { - defines["*"] = defaults; + // we should move this property off the prototype. + defineConfigurableAndNotEnumerable(defines,"*", defaults); } return {definitions: definitions, methods: methods, defaultDefinition: defaultDefinition}; }; diff --git a/map/map-test.js b/map/map-test.js index 181fe5d..a7e892a 100644 --- a/map/map-test.js +++ b/map/map-test.js @@ -1437,3 +1437,22 @@ QUnit.test("expandos use default type (#383)", function(){ }); QUnit.ok(someNumbers.version === 24, "is 24"); }); + +QUnit.test("do not enumerate anything other than key properties (#369)", function(){ + var Type = DefineMap.extend({ + aProp: "string", + aMethod: function(){} + }); + + var instance = new Type({aProp: "VALUE", anExpando: "VALUE"}); + + var props = {}; + for(var prop in instance) { + props[prop] = true; + } + QUnit.deepEqual(props,{ + aProp: true, + anExpando: true, + aMethod: true // TODO: this should be removed someday + }); +}); diff --git a/map/map.js b/map/map.js index 14e1354..38600cb 100644 --- a/map/map.js +++ b/map/map.js @@ -86,14 +86,13 @@ var DefineMap = Construct.extend("DefineMap",{ for(key in DefineMap.prototype) { define.defineConfigurableAndNotEnumerable(prototype, key, prototype[key]); } - - this.prototype.setup = function(props){ + define.defineConfigurableAndNotEnumerable(prototype, "setup", function(props){ define.setup.call( this, props || {}, this.constructor.seal ); - }; + }); } else { for(key in prototype) { define.defineConfigurableAndNotEnumerable(prototype, key, prototype[key]); From aeb480e8ee52daaf0fbb13f4e87e0e4dacdf5e8e Mon Sep 17 00:00:00 2001 From: Justin Meyer Date: Mon, 3 Sep 2018 15:24:40 -0500 Subject: [PATCH 3/3] 2.5.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e450679..7b6b557 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "can-define", - "version": "2.5.9", + "version": "2.5.10", "description": "Create observable objects with JS dot operator compatibility", "main": "can-define.js", "scripts": {