minor tweaks + more testing...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-06-07 21:28:04 +03:00
parent f0411d01d3
commit 26e3d8a825
6 changed files with 104 additions and 33 deletions

3
.gitignore vendored
View File

@ -1,7 +1,6 @@
.npm* .*
*.vim *.vim
*.sw[po] *.sw[po]
.git
coverage coverage
node_modules node_modules
npm-debug.log npm-debug.log

View File

@ -1,8 +1,6 @@
.npm* .*
*.vim *.vim
*.sw[po] *.sw[po]
.git
.github
coverage coverage
node_modules node_modules
npm-debug.log npm-debug.log

View File

@ -844,6 +844,15 @@ or:
- attribute values are identical. - attribute values are identical.
Non-strict match
```
match(base, obj, true)
-> bool
```
Like the default case but uses _equality_ instead of _identity_ to match
values.
## Limitations ## Limitations

View File

@ -115,11 +115,19 @@ function(text, tab_size, leading_tabs){
// - attr names are the same and, // - attr names are the same and,
// - attr values are identical. // - attr values are identical.
// //
//
// Non-strict match...
// match(a, b, true)
//
// This is similar to the default case but uses equality rather than
// identity to match values.
//
//
// NOTE: this will do a shallow test using Object.keys(..) thus .__proto__ // NOTE: this will do a shallow test using Object.keys(..) thus .__proto__
// attributes are ignored... // attributes are ignored...
var match = var match =
module.match = module.match =
function(base, obj){ function(base, obj, non_strict){
// identity... // identity...
if(base === obj){ if(base === obj){
return true } return true }
@ -135,7 +143,10 @@ function(base, obj){
return [k, obj[k]] }) return [k, obj[k]] })
while(o.length > 0){ while(o.length > 0){
var [k, v] = o.pop() var [k, v] = o.pop()
if(!base.hasOwnProperty(k) || base[k] !== v){ if(!base.hasOwnProperty(k)
|| (non_strict ?
base[k] != v
: base[k] !== v)){
return false } } return false } }
return true } return true }
@ -162,6 +173,12 @@ module.STOP =
// sources(obj, '__call__', callback) // sources(obj, '__call__', callback)
// -> list // -> list
// -> [] // -> []
//
// Get full chain...
// sources(obj)
// sources(obj, callback)
// -> list
//
// //
// callback(obj) // callback(obj)
// -> STOP // -> STOP
@ -192,11 +209,17 @@ module.STOP =
var sources = var sources =
module.sources = module.sources =
function(obj, name, callback){ function(obj, name, callback){
// get full chain...
if(typeof(name) == 'function'){
callback = name
name = undefined
}
var o var o
var res = [] var res = []
while(obj != null){ while(obj != null){
//if(obj.hasOwnProperty(name)){ //if(obj.hasOwnProperty(name)){
if(obj.hasOwnProperty(name) if(name === undefined
|| obj.hasOwnProperty(name)
|| (name == '__call__' && typeof(obj) == 'function')){ || (name == '__call__' && typeof(obj) == 'function')){
// handle callback... // handle callback...
o = callback o = callback
@ -258,11 +281,13 @@ function(obj, name, callback, props){
var c = typeof(callback) == 'function' var c = typeof(callback) == 'function'
&& function(obj){ && function(obj){
return callback(_get(obj, name), obj) } return callback(_get(obj, name), obj) }
return sources(...(c ? return c ?
[obj, name, c] // NOTE: we do not need to handle the callback return values as
: [obj, name])) // this is fully done in sources(..)
.map(function(obj){ sources(obj, name, c)
return _get(obj, name) }) } : sources(obj, name)
.map(function(obj){
return _get(obj, name) }) }
// Find the next parent attribute in the prototype chain. // Find the next parent attribute in the prototype chain.

View File

@ -1,10 +1,10 @@
{ {
"name": "ig-object", "name": "ig-object",
"version": "5.0.6", "version": "5.0.7",
"description": "", "description": "",
"main": "object.js", "main": "object.js",
"scripts": { "scripts": {
"test": "node ./test.js", "test": "c8 node ./test.js",
"cover-lcov": "c8 -r lcov node ./test.js", "cover-lcov": "c8 -r lcov node ./test.js",
"cover": "c8 node ./test.js", "cover": "c8 node ./test.js",
"prepublishOnly": "npm test" "prepublishOnly": "npm test"

76
test.js
View File

@ -85,13 +85,13 @@ var arrayCmp = function(a, b){
var ka = Object.keys(a) var ka = Object.keys(a)
var kb = Object.keys(a) var kb = Object.keys(a)
return a === b return a === b
|| ka.length == kb.length || (a.length == b.length
&& ka && ka
// keep only non matching stuff... // keep only non matching stuff...
.filter(function(k){ .filter(function(k){
return a[k] !== b[k] return a[k] !== b[k]
&& a[k] != a[k] }) && a[k] != a[k] })
.length == 0 } .length == 0) }
@ -407,6 +407,7 @@ module.setups = {
Object.entries(objs) Object.entries(objs)
.forEach(function([k, o]){ .forEach(function([k, o]){
assert(typeof(o) == 'function', 'instance is callable', k) }) assert(typeof(o) == 'function', 'instance is callable', k) })
return Object.assign(res, objs) }, return Object.assign(res, objs) },
// inherit from native constructors... // inherit from native constructors...
@ -465,15 +466,24 @@ module.setups = {
x: 'c', x: 'c',
method: function(){ method: function(){
assert(arrayCmp( assert(arrayCmp(
object.values(c, 'x').join(''), object.values(c, 'x'),
['c', 'a', 'b']), ['c', 'a', 'b']),
'reach all values of attr') 'reach all values of attr')
assert(arrayCmp(
object.values(c, 'x', function(v, o){
return v.toUpperCase() }),
['C', 'A', 'B']),
'reach all values of attr')
assert(arrayCmp( assert(arrayCmp(
object.sources(c, 'method'), object.sources(c, 'method'),
[c, a]), [c, a]),
'reach all values of method') 'reach all values of method')
assert(object.parent(c, 'x') == 'b', 'reach parent attr') assert(
assert(object.parentCall(c.method, this) == 'a', 'reach parent method', 'c') object.parent(c, 'x') == 'b',
'reach parent attr')
assert(
object.parentCall(c.method, this) == 'a',
'reach parent method', 'c')
return 'c' }, return 'c' },
}, },
d: d = { d: d = {
@ -498,10 +508,16 @@ module.setups = {
Z: Z = class extends Y { Z: Z = class extends Y {
x = 'z' x = 'z'
method(){ method(){
// XXX this is almost the same as for js_prototype...
assert(arrayCmp( assert(arrayCmp(
object.values(c, 'x').join(''), object.values(c, 'x'),
['z', 'y', 'x']), ['z', 'y', 'x']),
'reach all values of attr (class)') 'reach all values of attr (class)')
assert(arrayCmp(
object.values(c, 'x', function(v, o){
return v.toUpperCase() }),
['C', 'A', 'B']),
'reach all values of attr (class)')
assert(arrayCmp( assert(arrayCmp(
object.sources(c, 'method'), object.sources(c, 'method'),
[Z.prototype, X.prototype]), [Z.prototype, X.prototype]),
@ -621,15 +637,39 @@ module.tests = {
// callables... // callables...
callables: function(assert, setup){ callables: function(assert, setup){
// test special case .values(x, '__call__')
var test = function(obj, name){
var a, b
return assert(arrayCmp(
a = object.values(obj, '__call__')
.map(function(func){
return func.call(obj) })
.flat(),
// get all callables in prototype chain and call them...
b = object.sources(obj)
.filter(function(o){
return typeof(o) == 'function'
|| o.hasOwnProperty('__call__') })
.map(function(o){
return o.hasOwnProperty('__call__') ?
o.__call__.call(obj)
// NOTE: not all callables are instances of Function...
: Reflect.apply(Function.prototype, o, [obj]) })),
'values of .__call__ of '+ name +': got:', a, 'expected:', b) }
instances(setup) instances(setup)
.forEach(function([k, o]){ .filter(function([_, o]){
// NOTE: not all callables are instances of Function... // NOTE: not all callables are instances of Function...
typeof(o) == 'function' return typeof(o) == 'function' })
&& (o.__non_function ? .forEach(function([k, o]){
assert(!(o instanceof Function), 'non-instanceof Function', k) o.__non_function ?
: assert(o instanceof Function, 'instanceof Function', k)) assert(!(o instanceof Function), 'non-instanceof Function', k)
typeof(o) == 'function' : assert(o instanceof Function, 'instanceof Function', k)
&& assert(o(), 'call', k) })
assert(o(), 'call', k)
test(o, k)
})
return setup }, return setup },
} }