From ff9d79a6a5435161cdca8dee2c452e74b848c3aa Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Mon, 8 Jun 2020 04:16:03 +0300 Subject: [PATCH] added .matchPartial(..) and reworked .hasMixin(..) + testing... Signed-off-by: Alex A. Naanou --- README.md | 16 ++++++++++++ object.js | 31 +++++++++++++++++++--- package.json | 2 +- test.js | 72 +++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 114 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3788cfd..8483024 100755 --- a/README.md +++ b/README.md @@ -854,6 +854,22 @@ Like the default case but uses _equality_ instead of _identity_ to match values. +### `matchPartial(..)` + +``` +match(base, obj) + -> bool + +// non-strict version... +match(base, obj, true) + -> bool +``` + +Like `.match(..)` but will check for a partial match, i.e. when `obj` is +a non-strict subset of `base`. + + + ## Limitations ### Can not mix unrelated native types diff --git a/object.js b/object.js index a071b56..a1adfb0 100755 --- a/object.js +++ b/object.js @@ -151,6 +151,20 @@ function(base, obj, non_strict){ return true } +// Like .match(..) but will test if obj's attributes are included in base +var matchPartial = +module.matchPartial = +function(base, obj, non_strict){ + return base === obj + || Object.entries(obj) + .filter(function([n, v]){ + return !base.hasOwnProperty(n) + || (non_strict ? + base[n] != v + : base[n] !== v) }) + .length == 0 } + + //--------------------------------------------------------------------- // Prototype chain content access... @@ -502,8 +516,8 @@ function(base, ...objects){ // -> undefined // // -// NOTE: if base matches directly callback(..) will get undefined as parent // NOTE: this will also match base... +// NOTE: if base matches directly callback(..) will get undefined as parent // NOTE: for more docs on the callback(..) see sources(..) var mixins = module.mixins = @@ -540,10 +554,20 @@ function(base, object, callback){ // -> bool // // +// NOTE: to test for a flat mixin directly use .matchPartial(base, object) var hasMixin = module.hasMixin = function(base, object){ - return mixins(base, object, function(){ return module.STOP }).length > 0 } + return ( + // normal mixin... + mixins(base, object, function(){ return module.STOP }) + .length > 0 + // flat mixin search... + || sources(base, function(p){ + return matchPartial(p, object) ? + module.STOP + : [] }) + .length > 0 )} // Mix-out sets of methods/props/attrs out of an object prototype chain... @@ -886,7 +910,8 @@ function Constructor(name, a, b, c){ !!constructor_proto && (proto.__proto__ = constructor_proto.prototype) } - // the constructor base... + // the constructor base... + /* c8 ignore next 9 */ var _constructor = function Constructor(){ // create raw instance... var obj = _constructor.__rawinstance__ ? diff --git a/package.json b/package.json index 7b5ba50..7077823 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ig-object", - "version": "5.0.7", + "version": "5.0.8", "description": "", "main": "object.js", "scripts": { diff --git a/test.js b/test.js index cc98611..c2ecd9a 100755 --- a/test.js +++ b/test.js @@ -101,14 +101,16 @@ var arrayCmp = function(a, b){ var constructors = function(obj){ return Object.entries(obj) .filter(function([k, o]){ - return k[0] == k[0].toUpperCase() + return !k.startsWith('_') + && k[0] == k[0].toUpperCase() && o.prototype }) } // an instance is a thing that starts with a lowercase and has a .constructor var instances = function(obj){ return Object.entries(obj) .filter(function([k, o]){ - return k[0] == k[0].toLowerCase() + return !k.startsWith('_') + && k[0] == k[0].toLowerCase() && o.constructor }) } @@ -319,6 +321,7 @@ var ArgvParser = function(spec){ var setups = module.setups = { // basic constructor and inheritance... + // XXX constructor methods... basic: function(assert){ var X, Y, A, B, C return { @@ -326,10 +329,12 @@ module.setups = { Y: Y = assert(object.C('Y', { }), `C`), A: A = assert(object.C('A', Y, { }), `inherit (gen1)`), - B: B = assert(object.C('B', A, { }), `inherit (gen2)`), + B: B = assert(object.C('B', A, { }, { }), `inherit (gen2)`), C: C = assert(object.C('C', B, { }), `inherit (gen3)`), } }, + // XXX constructor methods... + // initialization... init: function(assert){ var A, B, C @@ -377,6 +382,9 @@ module.setups = { B: B = assert(object.C('B', { __non_function: true, __call__: function(){ + assert( + object.parentCall(B.prototype, '__call__', this, ...arguments) === undefined, + 'call non-existent parent method', 'B') return 'B' }, }), 'callable'), @@ -590,6 +598,64 @@ module.modifiers = { {__created_raw: true}) }) return res }, + // mixins... + mixin_instance: function(assert, setup, flat){ + var mixin = setup.__mixin_instance = { + __mixin_instance: true, + + // XXX + } + mixin.__mixin_instance = mixin + instances(setup) + .forEach(function([n, o]){ + // mixin once per chain... + if(o.__mixin_instance){ + return } + assert(!object.hasMixin(o, mixin), 'pre mixin test', n) + assert(flat ? + object.mixinFlat(o, mixin) + : object.mixin(o, mixin), + flat ? + 'mixin (flat)' + :'mixin', n) + assert(object.hasMixin(o, mixin), 'mixin test', n) + }) + return setup }, + mixin_instance_flat: function(assert, setup){ + return this.mixin_instance(assert, setup, true) }, + mixin_constructor: function(assert, setup, flat){ + var mixin = setup.__mixin_constructor = { + __mixin_constructor: true, + + // XXX + } + mixin.__mixin_constructor = mixin + // XXX do we care about order??? + constructors(setup) + .forEach(function([n, o]){ + // special case: can't non-flat mixin into an Object... + if(!flat && o === Object){ + return } + // mixin once per chain... + if(o.prototype.__mixin_constructor){ + return } + assert(!object.hasMixin(o.prototype, mixin), 'pre mixin test', n) + assert(flat ? + object.mixinFlat(o.prototype, mixin) + : object.mixin(o.prototype, mixin), + flat ? + 'mixin (flat)' + : 'mixin', n) + assert(object.hasMixin(o.prototype, mixin), 'mixin test', n) + }) + return setup }, + mixin_constructor_flat: function(assert, setup){ + return this.mixin_constructor(assert, setup, true) }, + /*/ XXX + mixout: function(assert, setup){ + return {} + }, + //*/ // sanity checks... //