mirror of
				https://github.com/flynx/object.js.git
				synced 2025-10-30 19:10:11 +00:00 
			
		
		
		
	added mixins(..) and hasMixin(..) functions + refactoring + docs...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									858151e53f
								
							
						
					
					
						commit
						86ffc914c3
					
				
							
								
								
									
										59
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								README.md
									
									
									
									
									
								
							| @ -437,6 +437,12 @@ sources(<object>, <name>, <callback>) | |||||||
| 	-> <list> | 	-> <list> | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | ``` | ||||||
|  | callback(<source>) | ||||||
|  | 	-> 'stop' | false | ||||||
|  | 	-> undefined | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| ### `parent(..)` | ### `parent(..)` | ||||||
| 
 | 
 | ||||||
| @ -497,9 +503,36 @@ This will copy the content of each input object without touching the | |||||||
| objects themselves, making them fully reusable. | objects themselves, making them fully reusable. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | ### `mixins(..)` | ||||||
|  | 
 | ||||||
|  | Get matching mixins | ||||||
|  | ``` | ||||||
|  | mixins(<base>, <object>) | ||||||
|  | mixins(<base>, [<object>, ..]) | ||||||
|  | mixins(<base>, <object>, <callback>) | ||||||
|  | mixins(<base>, [<object>, ..], <callback>) | ||||||
|  | 	-> list | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | callback(<match>, <object>, <parent>) | ||||||
|  | 	-> 'stop' | false | ||||||
|  | 	-> undefined | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### `hasMixin(..)` | ||||||
|  | 
 | ||||||
|  | Check of object has mixin | ||||||
|  | ``` | ||||||
|  | hasMixin(<base>, <mixin>) | ||||||
|  | 	-> <bool> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ### `mixout(..)` | ### `mixout(..)` | ||||||
| 
 | 
 | ||||||
| Remove the first occurrence of each object out of a prototype chain | Remove the first match for each object out of a prototype chain | ||||||
| ``` | ``` | ||||||
| mixout(<base>, <object>, ..) | mixout(<base>, <object>, ..) | ||||||
| mixout(<base>, 'first', <object>, ..) | mixout(<base>, 'first', <object>, ..) | ||||||
| @ -512,13 +545,6 @@ mixout(<base>, 'all', <object>, ..) | |||||||
| 	-> <base> | 	-> <base> | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| This relies on first level object structure to identify the target  |  | ||||||
| objects in the prototype chain, for a successful match the following  |  | ||||||
| must apply: |  | ||||||
| - attribute count must match, |  | ||||||
| - attribute names must match, |  | ||||||
| - attribute values must be identical. |  | ||||||
| 
 |  | ||||||
| This is the opposite of `mixin(..)` | This is the opposite of `mixin(..)` | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -600,6 +626,23 @@ This is used to format `.toString(..)` return values for nested functions | |||||||
| to make source printing in console more pleasant to read. | to make source printing in console more pleasant to read. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | ### `match(..)` | ||||||
|  | 
 | ||||||
|  | Test if the two objects match in attributes and attribute values | ||||||
|  | ``` | ||||||
|  | match(base, obj) | ||||||
|  | 	-> bool | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | This relies on first level object structure to identify the target  | ||||||
|  | objects in the prototype chain, for a successful match the following  | ||||||
|  | must apply: | ||||||
|  | - attribute count must match, | ||||||
|  | - attribute names must match, | ||||||
|  | - attribute values must be identical. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| ## Limitations | ## Limitations | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										149
									
								
								object.js
									
									
									
									
									
								
							
							
						
						
									
										149
									
								
								object.js
									
									
									
									
									
								
							| @ -57,6 +57,33 @@ function(text, tab_size){ | |||||||
| 		.trim() } | 		.trim() } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | // Match two objects...
 | ||||||
|  | //
 | ||||||
|  | // XXX this will match any two objects with no enumerable keys...
 | ||||||
|  | var match =  | ||||||
|  | module.match = | ||||||
|  | function(base, obj){ | ||||||
|  | 	// identity...
 | ||||||
|  | 	if(base === obj){ | ||||||
|  | 		return true } | ||||||
|  | 	// typeof -- sanity check...
 | ||||||
|  | 	if(typeof(base) != typeof(obj)){ | ||||||
|  | 		return false } | ||||||
|  | 	// attr count...
 | ||||||
|  | 	//var o = Object.entries(obj)
 | ||||||
|  | 	var o = Object.keys(Object.getOwnPropertyDescriptors(obj)) | ||||||
|  | 	if(Object.keys(Object.getOwnPropertyDescriptors(base)).length != o.length){ | ||||||
|  | 		return false } | ||||||
|  | 	// names and values...
 | ||||||
|  | 	o = o.map(function(k){ | ||||||
|  | 			return [k, obj[k]] }) | ||||||
|  | 	while(o.length > 0){ | ||||||
|  | 		var [k, v] = o.pop() | ||||||
|  | 		if(!base.hasOwnProperty(k) || base[k] !== v){ | ||||||
|  | 			return false } } | ||||||
|  | 	return true } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| //---------------------------------------------------------------------
 | //---------------------------------------------------------------------
 | ||||||
| // Prototype chain content access...
 | // Prototype chain content access...
 | ||||||
| @ -239,59 +266,99 @@ function(proto, name, that, ...args){ | |||||||
| 
 | 
 | ||||||
| // Mix a set of methods/props/attrs into an object...
 | // Mix a set of methods/props/attrs into an object...
 | ||||||
| // 
 | // 
 | ||||||
| //	mixinFlat(root, object, ...)
 | //	mixinFlat(base, object, ...)
 | ||||||
| //		-> root
 | //		-> base
 | ||||||
| //
 | //
 | ||||||
| //
 | //
 | ||||||
| // NOTE: essentially this is just like Object.assign(..) but copies 
 | // NOTE: essentially this is just like Object.assign(..) but copies 
 | ||||||
| // 		properties directly rather than copying property values...
 | // 		properties directly rather than copying property values...
 | ||||||
| var mixinFlat =  | var mixinFlat =  | ||||||
| module.mixinFlat =  | module.mixinFlat =  | ||||||
| function(root, ...objects){ | function(base, ...objects){ | ||||||
| 	return objects | 	return objects | ||||||
| 		.reduce(function(root, cur){ | 		.reduce(function(base, cur){ | ||||||
| 			Object.keys(cur) | 			Object.keys(cur) | ||||||
| 				.map(function(k){ | 				.map(function(k){ | ||||||
| 					Object.defineProperty(root, k, | 					Object.defineProperty(base, k, | ||||||
| 						Object.getOwnPropertyDescriptor(cur, k)) }) | 						Object.getOwnPropertyDescriptor(cur, k)) }) | ||||||
| 			return root }, root) } | 			return base }, base) } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // Mix sets of methods/props/attrs into an object as prototypes...
 | // Mix sets of methods/props/attrs into an object as prototypes...
 | ||||||
| //
 | //
 | ||||||
| // 	mixin(root, object, ..)
 | // 	mixin(base, object, ..)
 | ||||||
| // 		-> root
 | // 		-> base
 | ||||||
| //
 | //
 | ||||||
| //
 | //
 | ||||||
| // This will create a new object per set of methods given and 
 | // This will create a new object per set of methods given and 
 | ||||||
| // mixinFlat(..) the method set into this object leaving the 
 | // mixinFlat(..) the method set into this object leaving the 
 | ||||||
| // original objects intact.
 | // original objects intact.
 | ||||||
| // 
 | // 
 | ||||||
| // 		root <-- object1_copy <-- .. <-- objectN_copy <- root.__proto__
 | // 		base <-- object1_copy <-- .. <-- objectN_copy <- base.__proto__
 | ||||||
| // 				
 | // 				
 | ||||||
| //
 | //
 | ||||||
| // NOTE: this will only mix in non-empty objects...
 | // NOTE: this will only mix in non-empty objects...
 | ||||||
| var mixin =  | var mixin =  | ||||||
| module.mixin =  | module.mixin =  | ||||||
| function(root, ...objects){ | function(base, ...objects){ | ||||||
| 	root.__proto__ = objects | 	base.__proto__ = objects | ||||||
| 		.reduce(function(res, cur){ | 		.reduce(function(res, cur){ | ||||||
| 			return Object.keys(cur).length > 0 ? | 			return Object.keys(cur).length > 0 ? | ||||||
| 				module.mixinFlat(Object.create(res), cur)  | 				module.mixinFlat(Object.create(res), cur)  | ||||||
| 				: res }, root.__proto__)  | 				: res }, base.__proto__)  | ||||||
| 	return root } | 	return base } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // Get matching mixins...
 | ||||||
|  | //
 | ||||||
|  | // NOTE: if base matches directly callback(..) will get undefined as parent
 | ||||||
|  | // NOTE: this will also match base...
 | ||||||
|  | var mixins = | ||||||
|  | module.mixins = | ||||||
|  | function(base, object, callback){ | ||||||
|  | 	object = object instanceof Array ? | ||||||
|  | 		object | ||||||
|  | 		: [object] | ||||||
|  | 	var res = [] | ||||||
|  | 	var stop | ||||||
|  | 	var parent | ||||||
|  | 	while(base != null){ | ||||||
|  | 		// match each object...
 | ||||||
|  | 		for(var obj of object){ | ||||||
|  | 			if(match(base, obj)){ | ||||||
|  | 				res.push(base) | ||||||
|  | 				stop = callback  | ||||||
|  | 					&& callback(base, obj, parent) | ||||||
|  | 				if(stop === true || stop == 'stop'){ | ||||||
|  | 					return res }  | ||||||
|  | 				// match found, no need to test further...
 | ||||||
|  | 				break } } | ||||||
|  | 		parent = base | ||||||
|  | 		base = base.__proto__ } | ||||||
|  | 	return res } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // Check of base has mixin...
 | ||||||
|  | //
 | ||||||
|  | // 	hasMixin(base, mixin)
 | ||||||
|  | // 		-> bool
 | ||||||
|  | //
 | ||||||
|  | var hasMixin = | ||||||
|  | module.hasMixin = | ||||||
|  | function(base, object){ | ||||||
|  | 	return mixins(base, object, function(){ return '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...
 | ||||||
| //
 | //
 | ||||||
| // 	Mix-out first occurrence of each matching object...
 | // 	Mix-out first occurrence of each matching object...
 | ||||||
| // 	mixout(root, object, ..)
 | // 	mixout(base, object, ..)
 | ||||||
| // 	mixout(root, 'first', object, ..)
 | // 	mixout(base, 'first', object, ..)
 | ||||||
| // 		-> root
 | // 		-> base
 | ||||||
| //
 | //
 | ||||||
| // 	Mix-out all occurrences of each matching object...
 | // 	Mix-out all occurrences of each matching object...
 | ||||||
| // 	mixout(root, 'all', object, ..)
 | // 	mixout(base, 'all', object, ..)
 | ||||||
| // 		-> root
 | // 		-> base
 | ||||||
| //
 | //
 | ||||||
| //
 | //
 | ||||||
| // This will match an object to a mixin iff:
 | // This will match an object to a mixin iff:
 | ||||||
| @ -303,43 +370,25 @@ function(root, ...objects){ | |||||||
| // NOTE: this is the opposite to mixin(..)
 | // NOTE: this is the opposite to mixin(..)
 | ||||||
| var mixout = | var mixout = | ||||||
| module.mixout = | module.mixout = | ||||||
| function(root, ...objects){ | function(base, ...objects){ | ||||||
| 	var all = objects[0] == 'all' ? | 	var all = objects[0] == 'all' ? | ||||||
| 			!!objects.shift() | 			!!objects.shift() | ||||||
| 		: objects[0] == 'first' ? | 		: objects[0] == 'first' ? | ||||||
| 			!objects.shift() | 			!objects.shift() | ||||||
| 		// default...
 |  | ||||||
| 		: false | 		: false | ||||||
| 
 | 	var remove = [] | ||||||
| 	var _match = function(root, obj){ | 	mixins(base, objects, function(match, obj, parent){ | ||||||
| 		// identity...
 | 		parent && remove.push(parent) | ||||||
| 		if(root === obj){ | 		// when removing the first occurrence, don't check for obj again...
 | ||||||
| 			return true } | 		all || objects.splice(objects.indexOf(obj), 1) }) | ||||||
| 		// attr count...
 | 	// NOTE: we are removing on a separate stage so as not to mess with
 | ||||||
| 		if(Object.keys(root).length != Object.keys(obj).length){ | 	// 		mixins(..) iterating...
 | ||||||
| 			return false } | 	remove | ||||||
| 		// names and values...
 | 		// XXX not sure why we need to reverse here -- needs more thought...
 | ||||||
| 		var e = Object.entries(obj) | 		.reverse() | ||||||
| 		while(e.length > 0){ | 		.forEach(function(p){ | ||||||
| 			var [k, v] = e.pop() | 			p.__proto__ = p.__proto__.__proto__ }) | ||||||
| 			if(!root.hasOwnProperty(k) || root[k] !== v){ | 	return base } | ||||||
| 				return false } } |  | ||||||
| 		return true } |  | ||||||
| 	var _drop = function(obj){ |  | ||||||
| 		var cur = root |  | ||||||
| 		var found = false |  | ||||||
| 		while(cur.__proto__ != null  |  | ||||||
| 				// continue iff ...
 |  | ||||||
| 				&& (all || !found)){ |  | ||||||
| 			found = _match(cur.__proto__, obj) |  | ||||||
| 			found |  | ||||||
| 				&& (cur.__proto__ = cur.__proto__.__proto__)  |  | ||||||
| 			cur = cur.__proto__ } } |  | ||||||
| 
 |  | ||||||
| 	// do the work...
 |  | ||||||
| 	objects.map(_drop) |  | ||||||
| 
 |  | ||||||
| 	return root } |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "ig-object", |   "name": "ig-object", | ||||||
|   "version": "3.2.0", |   "version": "3.3.0", | ||||||
|   "description": "", |   "description": "", | ||||||
|   "main": "object.js", |   "main": "object.js", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user