Last active
January 26, 2017 01:51
-
-
Save koresar/55c6ea8c842f2a16247bd3ad24b8eb22 to your computer and use it in GitHub Desktop.
Collision allowance and protection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const stampit = require('stampit'); | |
const ProtectMethodCollisions = stampit({ | |
statics: { | |
protectMethodCollisions(name) { | |
if (this.compose.methods && this.compose.methods[name]) { | |
const method = this.compose.methods[name]; | |
function collisionProtectedMethodWrapper() { | |
return method.apply(this, arguments); | |
} | |
collisionProtectedMethodWrapper.protectCollisions = true; // core logic in this line! | |
return this.methods({[name]: collisionProtectedMethodWrapper}); | |
} | |
return this.compose(); | |
} | |
} | |
}); | |
const ProtectRedrawMethodCollisions = ProtectMethodCollisions.protectMethodCollisions('redraw'); | |
const Dog = stampit(); // whatever stamp | |
const DogWithProtectedRedraw = Dog.compose(ProtectRedrawMethodCollisions); | |
const DeferMethodCollisions = stampit({ | |
deepConf: { | |
DeferMethodCollisions: [] | |
}, | |
statics: { | |
deferMethodCollisions(name) { | |
return this.deepConf({DeferMethodCollisions: [name]}); | |
} | |
}, | |
composers: ({stamp, composables}) => { | |
const methodsArray = composables.map(c => isStamp(c) ? c.compose : c).map(d => d && d.methods).filter(d => d); | |
stamp.compose.deepConfiguration.DeferMethodCollisions.forEach(methodName => { | |
const methods = methodsArray.map(metadata => metadata && metadata[methodName]).filter(m => m); | |
if (methods.length > 1) { | |
const collisionProtectedMethods = methods.filter(m => m.protectCollisions); // find protected methods | |
if (collisionProtectedMethods.length > 0) { | |
throw new Error(`Attempt to compose collision protected method "${methodName}"`) | |
} | |
stamp.compose.methods[methodName] = function () { | |
for (let i = 0; i < methods.length; i++) method.apply(this, arguments); | |
} | |
} | |
}); | |
} | |
}); | |
const DeferRedrawMethodCollisions = DeferMethodCollisions.deferMethodCollisions('redraw'); | |
const Circle = stampit({methods: {redraw(){ console.log('Circle'); }}}).compose(DeferRedrawMethodCollisions); | |
const Triangle = stampit({methods: {redraw(){ console.log('Triangle'); }}}).compose(DeferRedrawMethodCollisions); | |
stampit() | |
.compose(Circle) // all good | |
.compose(Triangle) // all good | |
.compose(DogWithProtectedRedraw); // THROWS !!! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const stampit = require('stampit'); | |
const isStamp = require('stampit/isStamp'); | |
const _ = require('lodash'); | |
const MethodCollisions = stampit({ | |
statics: { | |
methodCollisions(protectionType, ...names) { | |
if (!this.compose.methods) return this.compose(); | |
const newMethods = {}; | |
_.forEach(names, name => { | |
const method = this.compose.methods[name]; | |
if (!_.isFunction(method)) return; | |
// Wrap the original method | |
newMethods[name] = _.wrap(method); | |
// Mark the method | |
newMethods[name].collision = protectionType; | |
}); | |
// Clone self making sure no collisions happen | |
const newStamp = this.compose(); | |
// Overwrite old methods with the new methods | |
_.assign(newStamp.compose.methods, newMethods); | |
return newStamp; | |
}, | |
protectMethodCollisions(...names) { | |
return this.methodCollisions('protect', ...names); | |
}, | |
deferMethodCollisions(...names) { | |
return this.methodCollisions('defer', ...names); | |
} | |
}, | |
composers: ({stamp, composables}) => { | |
const methodsMetadataArray = _(composables) | |
.map(c => isStamp(c) ? c.compose : c) | |
.map('methods') | |
.compact() | |
.value(); | |
const methodsStore = {}; // all methods collected to arrays | |
_.forEach(methodsMetadataArray, methodsMetadataObject => { | |
_.toPairs(methodsMetadataObject).forEach(([methodName, method]) => { | |
methodsStore[methodName] = (methodsStore[methodName] || []).concat(method); | |
}); | |
}); | |
_.toPairs(methodsStore).forEach(([methodName, methods]) => { | |
if (methods.length === 1) { | |
// No conflicts at all | |
methodsStore[methodName] = methods[0]; | |
} else if (methods.some(m => m.collision === 'protect')) { | |
// Something protected got collided | |
throw new Error(`Attempt to compose collision protected method "${methodName}"`) | |
} else if (methods.some(m => m.collision === 'defer')) { | |
// Wrap several methods to the one method. | |
// Retrieving original methods: | |
methods = _.flatten(methods.map(m => m.collidedMethods || m)); | |
// Wrapping: | |
methodsStore[methodName] = function () { | |
for (let i = 0; i < methods.length; i++) { | |
methods[i].apply(this, arguments); | |
} | |
}; | |
// Marking as deferred, and remembering what we have wrapped | |
methodsStore[methodName].collision = 'defer'; | |
methodsStore[methodName].collidedMethods = methods; | |
} else { | |
// No collision set up. Thus last method wins. | |
methodsStore[methodName] = methods[methods.length - 1]; | |
} | |
}); | |
stamp.compose.methods = methodsStore; | |
} | |
}); | |
const Dog = stampit({methods: {redraw(){ console.log('Dog'); }}}); // whatever stamp | |
const DogWithProtectedRedraw = Dog | |
.compose(MethodCollisions).protectMethodCollisions('redraw'); | |
const Circle = stampit({methods: {redraw(){ console.log('Circle'); }}}) | |
.compose(MethodCollisions).deferMethodCollisions('redraw'); | |
const Triangle = stampit({methods: {redraw(){ console.log('Triangle'); }}}) | |
.compose(MethodCollisions).deferMethodCollisions('redraw'); | |
let s = stampit().compose(Circle); // all good | |
s = s.compose(Triangle); // all good | |
s = s.compose(DogWithProtectedRedraw); // THROWS !!! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const stampit = require('stampit'); | |
const isStamp = require('stampit/isStamp'); | |
const flatten = arr => arr.reduce((a, b) => a.concat(b), []); | |
const MethodCollisions = stampit({ | |
statics: { | |
methodCollisions(protectionType, ...names) { | |
if (!this.compose.methods) return this.compose(); | |
const newMethods = {}; | |
names.forEach(name => { | |
const method = this.compose.methods[name]; | |
if (typeof method !== 'function') return; | |
// Wrap the original method and | |
newMethods[name] = function () { | |
return method.apply(this, arguments); | |
}; | |
// Mark the method | |
newMethods[name].collision = protectionType; | |
}); | |
// Clone self making sure no collisions happen | |
const newStamp = this.compose(); | |
// Overwrite old methods with the new methods | |
Object.assign(newStamp.compose.methods, newMethods); | |
return newStamp; | |
}, | |
protectMethodCollisions(...names) { | |
return this.methodCollisions('protect', ...names); | |
}, | |
deferMethodCollisions(...names) { | |
return this.methodCollisions('defer', ...names); | |
} | |
}, | |
composers: ({stamp, composables}) => { | |
const methodsMetadataArray = composables | |
.map(c => isStamp(c) ? c.compose : c) | |
.map(d => d.methods) | |
.filter(m => m); | |
const methodsStore = {}; // all methods collected to arrays | |
methodsMetadataArray.forEach(methodsMetadataObject => { | |
Object.keys(methodsMetadataObject).forEach(methodName => { | |
const method = methodsMetadataObject[methodName]; | |
methodsStore[methodName] = (methodsStore[methodName] || []).concat(method); | |
}); | |
}); | |
Object.keys(methodsStore).forEach(methodName => { | |
let methods = methodsStore[methodName]; | |
if (methods.length === 1) { | |
// No conflicts at all | |
methodsStore[methodName] = methods[0]; | |
} else if (methods.some(m => m.collision === 'protect')) { | |
// Something protected got collided | |
throw new Error(`Attempt to compose collision protected method "${methodName}"`) | |
} else if (methods.some(m => m.collision === 'defer')) { | |
// Wrap several methods to the one method. | |
// Retrieving original methods: | |
methods = flatten(methods.map(m => m.collidedMethods || m)); | |
// Wrapping: | |
methodsStore[methodName] = function () { | |
for (let i = 0; i < methods.length; i++) { | |
methods[i].apply(this, arguments); | |
} | |
}; | |
// Marking as deferred, and remembering what we have wrapped | |
methodsStore[methodName].collision = 'defer'; | |
methodsStore[methodName].collidedMethods = methods; | |
} else { | |
// No collision set up. Thus last method wins. | |
methodsStore[methodName] = methods[methods.length - 1]; | |
} | |
}); | |
stamp.compose.methods = methodsStore; | |
} | |
}); | |
const Dog = stampit({methods: {redraw(){ console.log('Dog'); }}}); // whatever stamp | |
const DogWithProtectedRedraw = Dog | |
.compose(MethodCollisions).protectMethodCollisions('redraw'); | |
const Circle = stampit({methods: {redraw(){ console.log('Circle'); }}}) | |
.compose(MethodCollisions).deferMethodCollisions('redraw'); | |
const Triangle = stampit({methods: {redraw(){ console.log('Triangle'); }}}) | |
.compose(MethodCollisions).deferMethodCollisions('redraw'); | |
let s = stampit().compose(Circle); // all good | |
s = s.compose(Triangle); // all good | |
s = s.compose(DogWithProtectedRedraw); // THROWS !!! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment