added .matchPartial(..) and reworked .hasMixin(..) + testing...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-06-08 04:16:03 +03:00
parent 26e3d8a825
commit ff9d79a6a5
4 changed files with 114 additions and 7 deletions

View File

@ -854,6 +854,22 @@ Like the default case but uses _equality_ instead of _identity_ to match
values. 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 ## Limitations
### Can not mix unrelated native types ### Can not mix unrelated native types

View File

@ -151,6 +151,20 @@ function(base, obj, non_strict){
return true } 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... // Prototype chain content access...
@ -502,8 +516,8 @@ function(base, ...objects){
// -> undefined // -> undefined
// //
// //
// NOTE: if base matches directly callback(..) will get undefined as parent
// NOTE: this will also match base... // 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(..) // NOTE: for more docs on the callback(..) see sources(..)
var mixins = var mixins =
module.mixins = module.mixins =
@ -540,10 +554,20 @@ function(base, object, callback){
// -> bool // -> bool
// //
// //
// NOTE: to test for a flat mixin directly use .matchPartial(base, object)
var hasMixin = var hasMixin =
module.hasMixin = module.hasMixin =
function(base, object){ 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... // 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 !!constructor_proto
&& (proto.__proto__ = constructor_proto.prototype) } && (proto.__proto__ = constructor_proto.prototype) }
// the constructor base... // the constructor base...
/* c8 ignore next 9 */
var _constructor = function Constructor(){ var _constructor = function Constructor(){
// create raw instance... // create raw instance...
var obj = _constructor.__rawinstance__ ? var obj = _constructor.__rawinstance__ ?

View File

@ -1,6 +1,6 @@
{ {
"name": "ig-object", "name": "ig-object",
"version": "5.0.7", "version": "5.0.8",
"description": "", "description": "",
"main": "object.js", "main": "object.js",
"scripts": { "scripts": {

72
test.js
View File

@ -101,14 +101,16 @@ var arrayCmp = function(a, b){
var constructors = function(obj){ var constructors = function(obj){
return Object.entries(obj) return Object.entries(obj)
.filter(function([k, o]){ .filter(function([k, o]){
return k[0] == k[0].toUpperCase() return !k.startsWith('_')
&& k[0] == k[0].toUpperCase()
&& o.prototype }) } && o.prototype }) }
// an instance is a thing that starts with a lowercase and has a .constructor // an instance is a thing that starts with a lowercase and has a .constructor
var instances = function(obj){ var instances = function(obj){
return Object.entries(obj) return Object.entries(obj)
.filter(function([k, o]){ .filter(function([k, o]){
return k[0] == k[0].toLowerCase() return !k.startsWith('_')
&& k[0] == k[0].toLowerCase()
&& o.constructor }) } && o.constructor }) }
@ -319,6 +321,7 @@ var ArgvParser = function(spec){
var setups = var setups =
module.setups = { module.setups = {
// basic constructor and inheritance... // basic constructor and inheritance...
// XXX constructor methods...
basic: function(assert){ basic: function(assert){
var X, Y, A, B, C var X, Y, A, B, C
return { return {
@ -326,10 +329,12 @@ module.setups = {
Y: Y = assert(object.C('Y', { }), `C`), Y: Y = assert(object.C('Y', { }), `C`),
A: A = assert(object.C('A', Y, { }), `inherit (gen1)`), 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)`), C: C = assert(object.C('C', B, { }), `inherit (gen3)`),
} }, } },
// XXX constructor methods...
// initialization... // initialization...
init: function(assert){ init: function(assert){
var A, B, C var A, B, C
@ -377,6 +382,9 @@ module.setups = {
B: B = assert(object.C('B', { B: B = assert(object.C('B', {
__non_function: true, __non_function: true,
__call__: function(){ __call__: function(){
assert(
object.parentCall(B.prototype, '__call__', this, ...arguments) === undefined,
'call non-existent parent method', 'B')
return 'B' return 'B'
}, },
}), 'callable'), }), 'callable'),
@ -590,6 +598,64 @@ module.modifiers = {
{__created_raw: true}) }) {__created_raw: true}) })
return res }, 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... // sanity checks...
// //