From e8ddf924c0d19880afd00acb19c0aaf9f8b2133d Mon Sep 17 00:00:00 2001 From: koresar Date: Sun, 17 Jun 2018 00:15:16 +1000 Subject: [PATCH 1/5] Convert instanceof to ES5. --- packages/instanceof/index.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/instanceof/index.js b/packages/instanceof/index.js index c5e2daf..25628fb 100644 --- a/packages/instanceof/index.js +++ b/packages/instanceof/index.js @@ -1,16 +1,15 @@ -const compose = require('@stamp/compose'); +var compose = require('@stamp/compose'); -const stampSymbol = Symbol.for('stamp'); - -module.exports = compose({ +module.exports = typeof Symbol === 'undefined' ? compose() : compose({ methods: {}, - composers: [({ stamp }) => { + composers: [function (opts) { + var stamp = opts.stamp; // Attaching to object prototype to save memory - stamp.compose.methods[stampSymbol] = stamp; + stamp.compose.methods[Symbol.for('stamp')] = stamp; Object.defineProperty(stamp, Symbol.hasInstance, { - value (obj) { - return obj && obj[stampSymbol] === stamp; + value: function value (obj) { + return obj && obj[Symbol.for('stamp')] === stamp; } }); }] From a170b8acca98a7b23a10bdf5d8fb98dbb5349017 Mon Sep 17 00:00:00 2001 From: koresar Date: Fri, 31 Aug 2018 20:01:36 +1000 Subject: [PATCH 2/5] Commit progress --- packages/collision/README.md | 4 +- packages/collision/__tests__/index.js | 181 ++++----------- packages/collision/index.js | 309 +++++++++++++------------- packages/core/merge.js | 2 +- 4 files changed, 211 insertions(+), 285 deletions(-) diff --git a/packages/collision/README.md b/packages/collision/README.md index d1b3073..2a285fb 100644 --- a/packages/collision/README.md +++ b/packages/collision/README.md @@ -41,9 +41,9 @@ component.draw(); // will draw() all three primitives Forbid or Defer an exclusive method `stamp.collisionSetup({forbid: ['methodName1'], defer: ['methodName2']}) -> Stamp` -#### collisionProtectAnyMethod +#### collisionForbidAll Forbid any collisions, excluding those allowed -`stamp.collisionProtectAnyMethod({allow: ['methoName']}) -> Stamp` +`stamp.collisionForbidAll() -> Stamp` #### collisionSettingsReset Remove any Collision settings from the stamp diff --git a/packages/collision/__tests__/index.js b/packages/collision/__tests__/index.js index 574a613..e1682e4 100644 --- a/packages/collision/__tests__/index.js +++ b/packages/collision/__tests__/index.js @@ -1,20 +1,20 @@ -var compose = require('@stamp/compose'); -var Collision = require('..'); +var compose = require("@stamp/compose"); +var Collision = require(".."); -describe('@stamp/collision', function () { - it('defer', function () { +describe("@stamp/collision", function () { + it("defer", function () { var draw1 = jest.fn(); - draw1.mockReturnValueOnce({a: 1}); + draw1.mockReturnValueOnce({ a: 1 }); var Defer1 = compose({ methods: { draw: draw1 } }, - Collision.collisionSetup({defer: ['draw']}) + Collision.collisionSetup({ defer: { methods: { draw: true } } }) ); var draw2 = jest.fn(); - draw2.mockReturnValueOnce({b: 2}); - var Defer2 = Collision.collisionSetup({defer: ['draw']}).compose({ + draw2.mockReturnValueOnce({ b: 2 }); + var Defer2 = Collision.collisionSetup({ defer: { methods: { draw: true } } }).compose({ methods: { draw: draw2 } @@ -25,33 +25,33 @@ describe('@stamp/collision', function () { var result = obj.draw(); - expect(result).toEqual([{a: 1}, {b: 2}]); + expect(result).toEqual([{ a: 1 }, { b: 2 }]); expect(draw1).toBeCalled(); expect(draw2).toBeCalled(); }); - it('defer + regular', function () { + it("defer + regular", function () { var Defer = compose({ methods: { draw: function () {} } }, - Collision.collisionSetup({defer: ['draw']}) + Collision.collisionSetup({ methods: { draw: "defer" } }) ); - var Regular = compose({methods: {draw: function () {}}}); + var Regular = compose({ methods: { draw: function () {} } }); - expect(function () {compose(Defer, Regular)}).toThrow(); - expect(function () {compose(Regular, Defer)}).toThrow(); + expect(function () {compose(Defer, Regular);}).toThrow(); + expect(function () {compose(Regular, Defer);}).toThrow(); }); - it('defer without conflicts', function () { + it("defer without conflicts", function () { var Defer = compose({ methods: { draw: function () {} } }, - Collision.collisionSetup({defer: ['draw']}) + Collision.collisionSetup({ methods: { draw: "defer" } }) ); var Regular = compose({ methods: { @@ -59,24 +59,24 @@ describe('@stamp/collision', function () { } }); - expect(function () {compose(Defer, Regular)}).not.toThrow(); - expect(function () {compose(Regular, Defer)}).not.toThrow(); + expect(function () {compose(Defer, Regular);}).not.toThrow(); + expect(function () {compose(Regular, Defer);}).not.toThrow(); }); - it('forbid', function () { + it("forbid", function () { var Forbid = compose({ methods: { draw: function () {} } }, - Collision.collisionSetup({forbid: ['draw']}) + Collision.collisionSetup({ methods: { draw: "forbid" } }) ); var Defer = compose({ methods: { draw: function () {} } }, - Collision.collisionSetup({defer: ['draw']}) + Collision.collisionSetup({ methods: { draw: "defer" } }) ); var Regular = compose({ methods: { @@ -85,112 +85,29 @@ describe('@stamp/collision', function () { } ); - expect(function () {compose(Forbid, Defer)}).toThrow(); - expect(function () {compose(Defer, Forbid)}).toThrow(); + expect(function () {compose(Forbid, Defer);}).toThrow(); + expect(function () {compose(Defer, Forbid);}).toThrow(); - expect(function () {compose(Regular, Forbid)}).toThrow(); - expect(function () {compose(Forbid, Regular)}).toThrow(); + expect(function () {compose(Regular, Forbid);}).toThrow(); + expect(function () {compose(Forbid, Regular);}).toThrow(); }); - it('collisionProtectAnyMethod', function () { - var FooBar = compose({ - methods: { - foo: function () {}, - bar: function () {} - } - }, - Collision.collisionProtectAnyMethod() - ); - var Foo = compose({ - methods: { - foo: function () {} - } - }, - Collision.collisionProtectAnyMethod() - ); - var Bar = compose({ - methods: { - bar: function () {} - } - }, - Collision.collisionProtectAnyMethod() - ); - - expect(function () {compose(FooBar, Foo)}).toThrow(); - expect(function () {compose(Foo, FooBar)}).toThrow(); - expect(function () {compose(FooBar, Bar)}).toThrow(); - expect(function () {compose(Bar, FooBar)}).toThrow(); - expect(function () {compose(Foo, Bar)}).not.toThrow(); - expect(function () {compose(Bar, Foo)}).not.toThrow(); - }); - - it('collisionProtectAnyMethod multiple times same stamp', function () { - var Base = compose({ - methods: { - foo: function () {}, - bar: function () {} - } - }, - Collision.collisionProtectAnyMethod() - ); - var Stamp1 = compose(Base); - var Stamp2 = compose(Base); - var Stamp3 = compose(Base); - - expect(function () {compose(Stamp1, Stamp2)}).not.toThrow(); - expect(function () {compose(Stamp1, Stamp2, Stamp3)}).not.toThrow(); - expect(function () {compose(Stamp1).compose(Stamp2, Stamp3)}).not.toThrow(); - expect(function () {compose(Stamp1, Stamp2).compose(Stamp3)}).not.toThrow(); - }); - - it('collisionProtectAnyMethod + allow', function () { - var FooBar = compose({ - methods: { - foo: function () {}, - bar: function () {} - } - }, - Collision.collisionProtectAnyMethod({allow: ['foo']}) - ); - var Foo = compose({ - methods: { - foo: function () {} - } - }, - Collision.collisionProtectAnyMethod() - ); - var Bar = compose({ - methods: { - bar: function () {} - } - }, - Collision.collisionProtectAnyMethod() - ); - - expect(function () {compose(FooBar, Foo)}).not.toThrow(); - expect(function () {compose(Foo, FooBar)}).not.toThrow(); - expect(function () {compose(FooBar, Bar)}).toThrow(); - expect(function () {compose(Bar, FooBar)}).toThrow(); - expect(function () {compose(Foo, Bar)}).not.toThrow(); - expect(function () {compose(Bar, Foo)}).not.toThrow(); - }); - - it('forbid + allow', function () { - expect(function () {Collision.collisionSetup({allow: ['foo'], forbid: ['foo']})}).toThrow(/Collision/); + it("forbid + allow", function () { + expect(function () {Collision.collisionSetup({ methods: { foo: "allow" } });}).toThrow(/Collision/); }); - it('can be used as a standalone function', function () { + it("can be used as a standalone function", function () { var collisionSetup = Collision.collisionSetup; - expect(function () {collisionSetup({allow: ['foo'], forbid: ['foo']})}).toThrow(/Collision/); + expect(function () {collisionSetup({ methods: { foo: "allow" } });}).toThrow(/Collision/); }); - it('forbid without conflicts', function () { + it("forbid without conflicts", function () { var Forbid = compose({ methods: { draw: function () {} } }, - Collision.collisionSetup({forbid: ['draw']}) + Collision.collisionSetup({ methods: { draw: "forbid" } }) ); var Regular = compose({ methods: { @@ -199,17 +116,17 @@ describe('@stamp/collision', function () { } ); - expect(function () {compose(Forbid, Regular)}).not.toThrow(); - expect(function () {compose(Regular, Forbid)}).not.toThrow(); + expect(function () {compose(Forbid, Regular);}).not.toThrow(); + expect(function () {compose(Regular, Forbid);}).not.toThrow(); }); - it('collisionSettingsReset', function () { + it("collisionSettingsReset", function () { var Forbid = compose({ methods: { draw: function () {} } }, - Collision.collisionSetup({forbid: ['draw']}) + Collision.collisionSetup({ methods: { draw: "forbid" } }) ); var Resetted1 = Forbid.collisionSettingsReset(); var Defer = compose({ @@ -217,7 +134,7 @@ describe('@stamp/collision', function () { draw: function () {} } }, - Collision.collisionSetup({defer: ['draw']}) + Collision.collisionSetup({ methods: { draw: "defer" } }) ); var Resetted2 = Defer.collisionSettingsReset(); var Regular = compose({ @@ -227,20 +144,20 @@ describe('@stamp/collision', function () { } ); - expect(function () {compose(Resetted1, Resetted2)}).not.toThrow(); - expect(function () {compose(Resetted1, Regular)}).not.toThrow(); - expect(function () {compose(Resetted2, Resetted1)}).not.toThrow(); - expect(function () {compose(Regular, Resetted1)}).not.toThrow(); + expect(function () {compose(Resetted1, Resetted2);}).not.toThrow(); + expect(function () {compose(Resetted1, Regular);}).not.toThrow(); + expect(function () {compose(Resetted2, Resetted1);}).not.toThrow(); + expect(function () {compose(Regular, Resetted1);}).not.toThrow(); }); - it('broken metadata', function () { + it("broken metadata", function () { var UndefinedMethod = Collision - .collisionSetup({forbid: ['foo']}) - .compose({ - methods: { - draw: null - } - }); + .collisionSetup({ methods: { foo: "forbid" } }) + .compose({ + methods: { + draw: null + } + }); var NoDefer = compose(Collision, { methods: { @@ -259,7 +176,7 @@ describe('@stamp/collision', function () { NoForbid.compose.deepConfiguration.Collision.forbid = null; - expect(function () {compose(UndefinedMethod, NoForbid)}).not.toThrow(); - expect(function () {compose(UndefinedMethod, NoDefer)}).not.toThrow(); + expect(function () {compose(UndefinedMethod, NoForbid);}).not.toThrow(); + expect(function () {compose(UndefinedMethod, NoDefer);}).not.toThrow(); }); }); diff --git a/packages/collision/index.js b/packages/collision/index.js index c7761ad..77b2aff 100644 --- a/packages/collision/index.js +++ b/packages/collision/index.js @@ -1,186 +1,195 @@ -var compose = require('@stamp/compose'); -var assign = require('@stamp/core/assign'); -var isStamp = require('@stamp/is/stamp'); -var isObject = require('@stamp/is/object'); -var isArray = require('@stamp/is/array'); - -function dedupe(array) { - var result = []; - for (var i = 0; i < array.length; i++) { - var item = array[i]; - if (result.indexOf(item) < 0) result.push(item); +var compose = require("@stamp/compose"); +var assign = require("@stamp/core/assign"); +var is = require("@stamp/is"); + +function forEach(maybeArray, fn) { + if (!is.isArray(maybeArray) && is.isObject(maybeArray)) maybeArray = Object.keys(maybeArray); + if (is.isArray(maybeArray) && maybeArray.length > 0) { + for (var i = 0; i < maybeArray.length; i++) { + fn(maybeArray[i]); + } } - return result; } -function makeProxyFunction(functions, name) { - function deferredFn() { - 'use strict'; - const results = []; +function deferredFn(functions) { + return function deferredFn() { + "use strict"; + var results = []; for (var i = 0; i < functions.length; i++) { results.push(functions[i].apply(this, arguments)); // jshint ignore:line } return results; + }; +} + +function pipedFn(functions) { + return function pipedFn() { + "use strict"; + for (var i = 0, result; i < functions.length; i++) { + result = functions[i].call(this, result); // jshint ignore:line + } + return result; + }; +} + +var delayTypes = { + defer: deferredFn, + pipe: pipedFn +}; + +function makeProxyFunction(functions, name, delayType) { + var fns = []; + var haveRegularFns = false, haveProxiesFns = false; + for (var i = 0; i < functions.length; i++) { + var f = functions[i]; + if (!is.isFunction(f)) continue; + + if (is.isArray(f._proxiedFunctions)) { + haveProxiesFns = true; + fns = fns.concat(f._proxiedFunctions); + } else { + haveRegularFns = true; + fns.push(f); + } + + if (haveRegularFns && haveProxiesFns) + throw new Error("Attempt to " + delayType + " with regular method"); } - Object.defineProperty(deferredFn, 'name', { + var fn = delayTypes[delayType](fns); + fn._proxiedFunctions = fns; + Object.defineProperty(fn, "name", { value: name, configurable: true }); - return deferredFn; + return fn; } -function getCollisionSettings(descriptor) { - return descriptor && - descriptor.deepConfiguration && - descriptor.deepConfiguration.Collision; +function isForbidden(allSettings, metadataType, metadataName) { + if (!allSettings || !allSettings.forbidAll) return false; + if (allSettings.forbidAll === true) return true; + if (allSettings.forbidAll[metadataType]) return true; + + if (!allSettings.forbid) return false; + if (allSettings.forbid[metadataType] && allSettings.forbid[metadataType][metadataName]) return true; + + return false; } -function descriptorHasSetting(descriptor, setting, methodName) { - var settings = getCollisionSettings(descriptor); - var settingsFor = settings && settings[setting]; - return isArray(settingsFor) && settingsFor.indexOf(methodName) >= 0; +function checkAmbiguousSetup(allSettings) { + if (!allSettings.defer && !allSettings.pipe) return; + + forEach(allSettings.defer, function (metadataType) { + forEach(allSettings.defer[metadataType], function (metadataName) { + if (isForbidden(allSettings, metadataType, metadataName)) { + throw new Error("Ambiguous Collision settings. The `" + metadataName + "` " + metadataType + " are both deferred and forbidden"); + } + }); + }); + + forEach(allSettings.pipe, function (metadataType) { + forEach(allSettings.pipe[metadataType], function (metadataName) { + if (isForbidden(allSettings, metadataType, metadataName)) { + throw new Error("Ambiguous Collision settings. The `" + metadataName + "` " + metadataType + " are both piped and forbidden"); + } + }); + }); + + var mergedDelayedDescriptor = assign({}, allSettings.defer, allSettings.pipe); + forEach(mergedDelayedDescriptor, function (metadataType) { + forEach(mergedDelayedDescriptor[metadataType], function (metadataName) { + if ( + allSettings.defer && allSettings.defer[metadataType] && allSettings.defer[metadataType] && + allSettings.pipe && allSettings.pipe[metadataType] && allSettings.pipe[metadataType] + ) { + throw new Error("Ambiguous Collision settings. The `" + metadataName + "` " + metadataType + " are both piped and deferred"); + } + }); + }); } -function forbidsCollision(descriptor, methodName) { - var settings = getCollisionSettings(descriptor); - if (settings && settings.forbidAll) { - return !descriptorHasSetting(descriptor, 'allow', methodName); - } - return descriptorHasSetting(descriptor, 'forbid', methodName); +function checkCollisions(allSettings, finalDescriptor, composables) { + var aggregatedComposables = {}; + forEach(composables, function (composable) { + var descriptor = composable.compose || composable || {}; + forEach(descriptor, function (metadataType) { + if (!is.isPlainObject(descriptor[metadataType])) return; + forEach(descriptor[metadataType], function (metadataName) { + aggregatedComposables[metadataType] = aggregatedComposables[metadataType] || {}; + aggregatedComposables[metadataType][metadataName] = aggregatedComposables[metadataType][metadataName] || []; + aggregatedComposables[metadataType][metadataName].push(descriptor[metadataType][metadataName]); + }); + }); + }); + + forEach(aggregatedComposables, function (metadataType) { + forEach(aggregatedComposables[metadataType], function (metadataName) { + if (aggregatedComposables[metadataType][metadataName].length > 1) { + if (isForbidden(allSettings, metadataType, metadataName)) { + throw Error("Collisions of `" + metadataName + "` " + metadataType + " are forbidden."); + } + } + }); + }); + + return aggregatedComposables; } -function defersCollision(descriptor, methodName) { - return descriptorHasSetting(descriptor, 'defer', methodName); +function mutateFinalDescriptor(allSettings, finalDescriptor, aggregatedComposables) { + forEach(delayTypes, function (delayType) { + forEach(aggregatedComposables, function (metadataType) { + forEach(aggregatedComposables[metadataType], function (metadataName) { + if (allSettings && allSettings[delayType] && allSettings[delayType][metadataType] && allSettings[delayType][metadataType][metadataName]) { + finalDescriptor[metadataType][metadataName] = makeProxyFunction(aggregatedComposables[metadataType][metadataName], metadataName, delayType); + } + }); + }); + }); } var Collision = compose({ - deepConfiguration: {Collision: {defer: [], forbid: []}}, + deepConfiguration: { Collision: {} }, staticProperties: { collisionSetup: function (opts) { - 'use strict'; - var Stamp = this && this.compose ? this : Collision; - return Stamp.compose({deepConfiguration: {Collision: opts}}); - + "use strict"; + var Stamp = is.isStamp(this && this.compose) ? this : Collision; + return Stamp.compose({ deepConfiguration: { Collision: opts } }); + }, + collisionForbidAll: function (metadataType) { + if (!metadataType) { + return this.collisionSetup({ forbidAll: true }); + } else { + var opts = { forbidAll: {} }; + opts[metadataType] = true; + return this.collisionSetup(opts); + } + }, + collisionForbid: function (opts) { + return this.collisionSetup({ forbid: opts }); + }, + collisionDeferMethod: function (methodName) { + var methods = {}; + methods[methodName] = true; + return this.collisionSetup({ defer: { methods: methods } }); + }, + collisionPipeMethod: function (methodName) { + var methods = {}; + methods[methodName] = true; + return this.collisionSetup({ pipe: { methods: methods } }); }, collisionSettingsReset: function () { return this.collisionSetup(null); - }, - collisionProtectAnyMethod: function (opts) { - return this.collisionSetup(assign({}, opts, {forbidAll: true})); } }, composers: [function (opts) { - var descriptor = opts.stamp.compose; - var settings = getCollisionSettings(descriptor); - if (!isObject(settings)) return; - - var i, methodName; - // Deduping is an important part of the logic - if (isArray(settings.defer)) { - settings.defer = dedupe(settings.defer); - } - if (isArray(settings.forbid)) { - settings.forbid = dedupe(settings.forbid); - } - if (isArray(settings.allow)) { - settings.allow = dedupe(settings.allow); - } - - // Make sure settings are not ambiguous - if (isArray(settings.forbid)) { - var checkDefer = isArray(settings.defer) && settings.defer.length > 0; - var checkAllow = isArray(settings.allow) && settings.allow.length > 0; - if (checkDefer || checkAllow) { - for (i = 0; i < settings.forbid.length; i++) { - var forbiddenMethodName = settings.forbid[i]; - if (checkDefer && settings.defer.indexOf(forbiddenMethodName) >= 0) { - throw new Error('Ambiguous Collision settings. The `' + - forbiddenMethodName + '` is both deferred and forbidden'); - } - if (checkAllow && settings.allow.indexOf(forbiddenMethodName) >= 0) { - throw new Error('Ambiguous Collision settings. The `' + - forbiddenMethodName + '` is both allowed and forbidden'); - } - } - } - } + var finalDescriptor = opts.stamp.compose; + var allSettings = finalDescriptor.deepConfiguration && finalDescriptor.deepConfiguration.Collision; + if (!is.isObject(allSettings)) return; - if (settings.forbidAll || - isArray(settings.defer) && settings.defer.length > 0 || - isArray(settings.forbid) && settings.forbid.length > 0 - ) { - var d, j, oneMetadata; - - var methodsMetadata = {}; // methods aggregation - var composables = opts.composables; - for (i = 0; i < composables.length; i++) { - d = composables[i]; - d = isStamp(d) ? d.compose : d; - if (!isObject(d.methods)) continue; - - var methodNames = Object.keys(d.methods); - for (j = 0; j < methodNames.length; j++) { - methodName = methodNames[j]; - var method = d.methods[methodName]; - if (!method) continue; - - var existingMetadata = methodsMetadata[methodName]; - - // Checking by reference if the method is already present - if (existingMetadata && - (existingMetadata === method || - (isArray(existingMetadata) && existingMetadata.indexOf(method) >= 0))) { - continue; - } - - // Process Collision.forbid - if (existingMetadata && forbidsCollision(descriptor, methodName)) { - throw new Error('Collision of method `' + methodName + - '` is forbidden'); - } - - // Process Collision.defer - if (defersCollision(d, methodName)) { - if (existingMetadata && !isArray(existingMetadata)) { - throw new Error('Ambiguous Collision settings. The `' + - methodName + '` is both deferred and regular'); - } - var arr = existingMetadata || []; - arr.push(method); - methodsMetadata[methodName] = arr; - continue; - } - - // Process no Collision settings - if (isArray(existingMetadata)) { - throw new Error('Ambiguous Collision settings. The `' + - methodName + '` is both deferred and regular'); - } - - methodsMetadata[methodName] = method; - } - } - - var methods = opts.stamp.compose.methods; - var allMetadataMethods = Object.keys(methodsMetadata); - for (i = 0; i < allMetadataMethods.length; i++) { - methodName = allMetadataMethods[i]; - oneMetadata = methodsMetadata[methodName]; - if (isArray(oneMetadata)) { - if (oneMetadata.length === 1) { - methods[methodName] = oneMetadata[0]; - } else { - // Some collisions aggregated to a single method - // Mutating the resulting stamp - methods[methodName] = makeProxyFunction(oneMetadata, methodName); - } - } else { - methods[methodName] = oneMetadata; - } - } - } + checkAmbiguousSetup(allSettings); + var aggregatedComposables = checkCollisions(allSettings, finalDescriptor, opts.composables); + mutateFinalDescriptor(allSettings, finalDescriptor, aggregatedComposables); }] }); diff --git a/packages/core/merge.js b/packages/core/merge.js index 3edb9a5..9ea60f8 100644 --- a/packages/core/merge.js +++ b/packages/core/merge.js @@ -7,7 +7,7 @@ var isArray = require('@stamp/is/array'); * The returned values is always of the same type as the 'src'. * @param dst The object to merge into * @param src The object to merge from - * @returns {*} + * @returns {*} The mutated `dst` argument or a new object. */ function mergeOne(dst, src) { if (src === undefined) return dst; From 4f73830cf2a02a2288977e2ee8ae7abb4f7e4cc9 Mon Sep 17 00:00:00 2001 From: koresar Date: Sat, 1 Sep 2018 14:00:17 +1000 Subject: [PATCH 3/5] Commit progress 2 --- __tests__/privatize-plus-collision.js | 54 ++++----- package.json | 1 + packages/collision/__tests__/index.js | 24 ++-- packages/collision/index.js | 159 ++++++++++++-------------- 4 files changed, 111 insertions(+), 127 deletions(-) diff --git a/__tests__/privatize-plus-collision.js b/__tests__/privatize-plus-collision.js index 843ecb8..457e15d 100644 --- a/__tests__/privatize-plus-collision.js +++ b/__tests__/privatize-plus-collision.js @@ -1,9 +1,9 @@ -var compose = require('../packages/compose'); -var Collision = require('../packages/collision'); -var Privatize = require('../packages/privatize'); +var compose = require("../packages/compose"); +var Collision = require("../packages/collision"); +var Privatize = require("../packages/privatize"); -describe('Collision + Privatize', function () { - it('work together', function () { +describe("Collision + Privatize", function () { + it("work together", function () { var Stamp = compose( Collision, Privatize, @@ -13,47 +13,49 @@ describe('Collision + Privatize', function () { } } ) - .collisionProtectAnyMethod() - .privatizeMethods('privateMethod'); + .collisionSetup({ methods: "forbid" }) + .privatizeMethods("privateMethod"); var obj = Stamp(); expect(obj.privateMethod).toBeUndefined(); }); - it('privatizes deferred methods', function () { + it("privatizes deferred methods", function () { // General purpose behavior to defer "draw()" method collisions - var DeferDraw = Collision.collisionSetup({defer: ['draw']}); + var DeferDraw = Collision.collisionSetup({ methods: { draw: "defer" } }); + var draw1 = jest.fn(); // Spy function + var draw2 = jest.fn(); // Spy function var Border = compose(DeferDraw, { methods: { - draw: jest.fn() // Spy function + draw: draw1 } }); var Button = compose(DeferDraw, { methods: { - draw: jest.fn() // Spy function + draw: draw2 } }); // General purpose behavior to privatize the "draw()" method - var PrivateDraw = Privatize.privatizeMethods('draw'); + var PrivateDraw = Privatize.privatizeMethods("draw"); // General purpose behavior to forbid the "redraw()" method collision - var ForbidRedrawCollision = Collision.collisionSetup({forbid: ['redraw']}); + var ForbidRedrawCollision = Collision.collisionSetup({ methods: { redraw: "forbid" } }); // The aggregating component var ModalDialog = compose(PrivateDraw) // privatize the "draw()" method - .compose(Border, Button) // modal will consist of Border and Button - .compose(ForbidRedrawCollision) // there can be only one "redraw()" method - .compose({ - methods: { - // the public method which calls the deferred private methods - redraw: function (int) { - this.draw(int); - } + .compose(Border, Button) // modal will consist of Border and Button + .compose(ForbidRedrawCollision) // there can be only one "redraw()" method + .compose({ + methods: { + // the public method which calls the deferred private methods + redraw: function (int) { + this.draw(int); } - }); + } + }); // Creating an instance of the stamp var dialog = ModalDialog(); @@ -65,11 +67,11 @@ describe('Collision + Privatize', function () { dialog.redraw(42); // Check if the spy "draw()" deferred functions were actually invoked - expect(Border.compose.methods.draw).toBeCalledWith(42); - expect(Button.compose.methods.draw).toBeCalledWith(42); + expect(draw1).toBeCalledWith(42); + expect(draw2).toBeCalledWith(42); // Make sure the ModalDialog throws every time on the "redraw()" collisions - var HaveRedraw = compose({methods: {redraw: jest.fn()}}); - expect(function () {compose(ModalDialog, HaveRedraw)}).toThrow(); + var HaveRedraw = compose({ methods: { redraw: jest.fn() } }); + expect(function () {compose(ModalDialog, HaveRedraw);}).toThrow(); }); }); diff --git a/package.json b/package.json index 02f3b92..6bbb46d 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ } }, "jest": { + "testEnvironment": "node", "coverageReporters": [ "html", "text" diff --git a/packages/collision/__tests__/index.js b/packages/collision/__tests__/index.js index e1682e4..cbcb856 100644 --- a/packages/collision/__tests__/index.js +++ b/packages/collision/__tests__/index.js @@ -10,11 +10,11 @@ describe("@stamp/collision", function () { draw: draw1 } }, - Collision.collisionSetup({ defer: { methods: { draw: true } } }) + Collision.collisionSetup({ methods: { draw: "defer" } }) ); var draw2 = jest.fn(); draw2.mockReturnValueOnce({ b: 2 }); - var Defer2 = Collision.collisionSetup({ defer: { methods: { draw: true } } }).compose({ + var Defer2 = Collision.collisionSetup({ methods: { draw: "defer" } }).compose({ methods: { draw: draw2 } @@ -85,20 +85,16 @@ describe("@stamp/collision", function () { } ); - expect(function () {compose(Forbid, Defer);}).toThrow(); - expect(function () {compose(Defer, Forbid);}).toThrow(); - - expect(function () {compose(Regular, Forbid);}).toThrow(); - expect(function () {compose(Forbid, Regular);}).toThrow(); - }); + expect(function () {compose(Forbid, Defer);}).toThrow(/Collision/); + expect(function () {compose(Defer, Forbid);}).toThrow(/Collision/); - it("forbid + allow", function () { - expect(function () {Collision.collisionSetup({ methods: { foo: "allow" } });}).toThrow(/Collision/); + expect(function () {compose(Regular, Forbid);}).toThrow(/Collision/); + expect(function () {compose(Forbid, Regular);}).toThrow(/Collision/); }); it("can be used as a standalone function", function () { var collisionSetup = Collision.collisionSetup; - expect(function () {collisionSetup({ methods: { foo: "allow" } });}).toThrow(/Collision/); + expect(typeof collisionSetup({ methods: { foo: "allow" } })).toBe("function") }); it("forbid without conflicts", function () { @@ -120,7 +116,7 @@ describe("@stamp/collision", function () { expect(function () {compose(Regular, Forbid);}).not.toThrow(); }); - it("collisionSettingsReset", function () { + it("resetting settings", function () { var Forbid = compose({ methods: { draw: function () {} @@ -128,7 +124,7 @@ describe("@stamp/collision", function () { }, Collision.collisionSetup({ methods: { draw: "forbid" } }) ); - var Resetted1 = Forbid.collisionSettingsReset(); + var Resetted1 = Forbid.collisionSetup(null); var Defer = compose({ methods: { draw: function () {} @@ -136,7 +132,7 @@ describe("@stamp/collision", function () { }, Collision.collisionSetup({ methods: { draw: "defer" } }) ); - var Resetted2 = Defer.collisionSettingsReset(); + var Resetted2 = Defer.collisionSetup(null); var Regular = compose({ methods: { draw: function () {} diff --git a/packages/collision/index.js b/packages/collision/index.js index 77b2aff..8461f35 100644 --- a/packages/collision/index.js +++ b/packages/collision/index.js @@ -1,16 +1,25 @@ var compose = require("@stamp/compose"); -var assign = require("@stamp/core/assign"); var is = require("@stamp/is"); function forEach(maybeArray, fn) { if (!is.isArray(maybeArray) && is.isObject(maybeArray)) maybeArray = Object.keys(maybeArray); if (is.isArray(maybeArray) && maybeArray.length > 0) { for (var i = 0; i < maybeArray.length; i++) { - fn(maybeArray[i]); + if (fn(maybeArray[i]) === "BREAK") break; } } } +function forEachMetadata(maybeDescriptor, fn) { + if (!is.isObject(maybeDescriptor)) return; + forEach(maybeDescriptor, function (metadataType) { + if (!is.isPlainObject(maybeDescriptor[metadataType])) return; + forEach(maybeDescriptor[metadataType], function (metadataName) { + fn(metadataType, metadataName); + }); + }); +} + function deferredFn(functions) { return function deferredFn() { "use strict"; @@ -66,67 +75,64 @@ function makeProxyFunction(functions, name, delayType) { return fn; } -function isForbidden(allSettings, metadataType, metadataName) { - if (!allSettings || !allSettings.forbidAll) return false; - if (allSettings.forbidAll === true) return true; - if (allSettings.forbidAll[metadataType]) return true; - - if (!allSettings.forbid) return false; - if (allSettings.forbid[metadataType] && allSettings.forbid[metadataType][metadataName]) return true; - - return false; +function isForbidden(settings, metadataType, metadataName) { + return ( + settings === "forbid" || + (settings && settings[metadataType] === "forbid") || + (settings && settings[metadataType] && settings[metadataType][metadataName] === "forbid") + ); } -function checkAmbiguousSetup(allSettings) { - if (!allSettings.defer && !allSettings.pipe) return; +function getValidSettings(composables) { + var finalSettings = {}; + forEach(composables, function (composable) { + var settings = getSettings(composable); - forEach(allSettings.defer, function (metadataType) { - forEach(allSettings.defer[metadataType], function (metadataName) { - if (isForbidden(allSettings, metadataType, metadataName)) { - throw new Error("Ambiguous Collision settings. The `" + metadataName + "` " + metadataType + " are both deferred and forbidden"); + if (settings === "forbid") { + finalSettings = "forbid"; + return "BREAK"; + } + forEach(settings, function (metadataType) { + if (settings[metadataType] === "forbid") { + finalSettings[metadataType] = "forbid"; + return "BREAK"; } - }); - }); - forEach(allSettings.pipe, function (metadataType) { - forEach(allSettings.pipe[metadataType], function (metadataName) { - if (isForbidden(allSettings, metadataType, metadataName)) { - throw new Error("Ambiguous Collision settings. The `" + metadataName + "` " + metadataType + " are both piped and forbidden"); - } - }); - }); + if (!is.isPlainObject(settings[metadataType])) return; + forEach(settings[metadataType], function (metadataName) { + if (!is.isString(settings[metadataType][metadataName])) return; // malformed settings - var mergedDelayedDescriptor = assign({}, allSettings.defer, allSettings.pipe); - forEach(mergedDelayedDescriptor, function (metadataType) { - forEach(mergedDelayedDescriptor[metadataType], function (metadataName) { - if ( - allSettings.defer && allSettings.defer[metadataType] && allSettings.defer[metadataType] && - allSettings.pipe && allSettings.pipe[metadataType] && allSettings.pipe[metadataType] - ) { - throw new Error("Ambiguous Collision settings. The `" + metadataName + "` " + metadataType + " are both piped and deferred"); - } + finalSettings[metadataType] = finalSettings[metadataType] || {}; + if (settings[metadataType][metadataName] === "forbid") { + finalSettings[metadataType][metadataName] = "forbid"; + return "BREAK"; + } + + var newValue = settings[metadataType][metadataName]; + var existingValue = finalSettings[metadataType][metadataName]; + if (existingValue && existingValue !== newValue) { + throw new Error("Ambiguous Collision settings. The `" + metadataName + "` " + metadataType + " can't be both " + existingValue + " and " + newValue); + } + + finalSettings[metadataType][metadataName] = newValue; + }); }); }); + + return finalSettings; } -function checkCollisions(allSettings, finalDescriptor, composables) { +function checkCollisions(finalSettings, finalDescriptor, composables) { var aggregatedComposables = {}; forEach(composables, function (composable) { var descriptor = composable.compose || composable || {}; - forEach(descriptor, function (metadataType) { - if (!is.isPlainObject(descriptor[metadataType])) return; - forEach(descriptor[metadataType], function (metadataName) { - aggregatedComposables[metadataType] = aggregatedComposables[metadataType] || {}; - aggregatedComposables[metadataType][metadataName] = aggregatedComposables[metadataType][metadataName] || []; - aggregatedComposables[metadataType][metadataName].push(descriptor[metadataType][metadataName]); - }); - }); - }); + forEachMetadata(descriptor, function (metadataType, metadataName) { + aggregatedComposables[metadataType] = aggregatedComposables[metadataType] || {}; + var array = aggregatedComposables[metadataType][metadataName] = aggregatedComposables[metadataType][metadataName] || []; + array.push(descriptor[metadataType][metadataName]); - forEach(aggregatedComposables, function (metadataType) { - forEach(aggregatedComposables[metadataType], function (metadataName) { - if (aggregatedComposables[metadataType][metadataName].length > 1) { - if (isForbidden(allSettings, metadataType, metadataName)) { + if (array.length > 1) { + if (isForbidden(finalSettings, metadataType, metadataName)) { throw Error("Collisions of `" + metadataName + "` " + metadataType + " are forbidden."); } } @@ -136,60 +142,39 @@ function checkCollisions(allSettings, finalDescriptor, composables) { return aggregatedComposables; } -function mutateFinalDescriptor(allSettings, finalDescriptor, aggregatedComposables) { +function mutateFinalDescriptor(finalSettings, finalDescriptor, aggregatedComposables) { forEach(delayTypes, function (delayType) { - forEach(aggregatedComposables, function (metadataType) { - forEach(aggregatedComposables[metadataType], function (metadataName) { - if (allSettings && allSettings[delayType] && allSettings[delayType][metadataType] && allSettings[delayType][metadataType][metadataName]) { - finalDescriptor[metadataType][metadataName] = makeProxyFunction(aggregatedComposables[metadataType][metadataName], metadataName, delayType); - } - }); + forEachMetadata(aggregatedComposables, function (metadataType, metadataName) { + if (finalSettings[metadataType] && finalSettings[metadataType][metadataName] === delayType) { + finalDescriptor[metadataType][metadataName] = makeProxyFunction(aggregatedComposables[metadataType][metadataName], metadataName, delayType); + } }); }); } +function getSettings(composable) { + var descriptor = composable.compose || composable; + return descriptor && descriptor.deepConfiguration && descriptor.deepConfiguration.Collision; +} + var Collision = compose({ deepConfiguration: { Collision: {} }, staticProperties: { collisionSetup: function (opts) { "use strict"; - var Stamp = is.isStamp(this && this.compose) ? this : Collision; + var Stamp = is.isStamp(this) ? this : Collision; return Stamp.compose({ deepConfiguration: { Collision: opts } }); - }, - collisionForbidAll: function (metadataType) { - if (!metadataType) { - return this.collisionSetup({ forbidAll: true }); - } else { - var opts = { forbidAll: {} }; - opts[metadataType] = true; - return this.collisionSetup(opts); - } - }, - collisionForbid: function (opts) { - return this.collisionSetup({ forbid: opts }); - }, - collisionDeferMethod: function (methodName) { - var methods = {}; - methods[methodName] = true; - return this.collisionSetup({ defer: { methods: methods } }); - }, - collisionPipeMethod: function (methodName) { - var methods = {}; - methods[methodName] = true; - return this.collisionSetup({ pipe: { methods: methods } }); - }, - collisionSettingsReset: function () { - return this.collisionSetup(null); } }, composers: [function (opts) { var finalDescriptor = opts.stamp.compose; - var allSettings = finalDescriptor.deepConfiguration && finalDescriptor.deepConfiguration.Collision; - if (!is.isObject(allSettings)) return; + var allSettings = getSettings(finalDescriptor); + if (!is.isObject(allSettings) && !is.isString(allSettings)) return; - checkAmbiguousSetup(allSettings); - var aggregatedComposables = checkCollisions(allSettings, finalDescriptor, opts.composables); - mutateFinalDescriptor(allSettings, finalDescriptor, aggregatedComposables); + var finalSettings = getValidSettings(opts.composables); // recompose the settings. Also, checks for ambiguous setup + finalDescriptor.deepConfiguration.Collision = finalSettings; + var aggregatedComposables = checkCollisions(finalSettings, finalDescriptor, opts.composables); + mutateFinalDescriptor(finalSettings, finalDescriptor, aggregatedComposables); }] }); From 063f41b6866436e939e3f045c0f61ed5cc77b9e9 Mon Sep 17 00:00:00 2001 From: koresar Date: Sat, 1 Sep 2018 14:37:02 +1000 Subject: [PATCH 4/5] Piping collided methods. Tests --- packages/collision/__tests__/index.js | 115 +++++++++++++++++++++++++- packages/collision/index.js | 6 +- 2 files changed, 115 insertions(+), 6 deletions(-) diff --git a/packages/collision/__tests__/index.js b/packages/collision/__tests__/index.js index cbcb856..cde9931 100644 --- a/packages/collision/__tests__/index.js +++ b/packages/collision/__tests__/index.js @@ -1,4 +1,5 @@ var compose = require("@stamp/compose"); +var assign = require("@stamp/core").assign; var Collision = require(".."); describe("@stamp/collision", function () { @@ -63,6 +64,66 @@ describe("@stamp/collision", function () { expect(function () {compose(Regular, Defer);}).not.toThrow(); }); + it("pipe", function () { + function toJSON1(previous) { return assign({}, previous, { a: 1 });} + + var Pipe1 = compose({ + methods: { + toJSON: toJSON1 + } + }, + Collision.collisionSetup({ methods: { toJSON: "pipe" } }) + ); + + function toJSON2(previous) { return assign({}, previous, { b: 2 });} + + var Pipe2 = Collision.collisionSetup({ methods: { toJSON: "pipe" } }).compose({ + methods: { + toJSON: toJSON2 + } + }); + + var StampCombined = compose(Pipe1, Pipe2); + var obj = StampCombined(); + + var result = obj.toJSON(); + + expect(result).toEqual({ a: 1, b: 2 }); + }); + + it("pipe + regular", function () { + var Pipe = compose({ + methods: { + toJSON: function () {} + } + }, + Collision.collisionSetup({ methods: { toJSON: "pipe" } }) + ); + var Regular = compose({ methods: { toJSON: function () {} } }); + + expect(function () {compose(Pipe, Regular);}).toThrow(); + expect(function () {compose(Regular, Pipe);}).toThrow(); + }); + + + it("pipe without conflicts", function () { + var Pipe = compose({ + methods: { + toJSON: function () {} + } + }, + Collision.collisionSetup({ methods: { toJSON: "pipe" } }) + ); + var Regular = compose({ + methods: { + whatever: function () {} + } + }); + + expect(function () {compose(Pipe, Regular);}).not.toThrow(); + expect(function () {compose(Regular, Pipe);}).not.toThrow(); + }); + it("forbid", function () { var Forbid = compose({ methods: { @@ -92,9 +153,39 @@ describe("@stamp/collision", function () { expect(function () {compose(Forbid, Regular);}).toThrow(/Collision/); }); + it("forbid everything", function () { + var Forbid = compose({ + methods: { + draw: function () {} + }, + properties: { + height: 0 + } + }, + Collision.collisionSetup("forbid") + ); + var Method = compose({ + methods: { + draw: function () {} + } + } + ); + var Property = compose({ + properties: { + height: null + } + } + ); + + expect(function () {compose(Forbid, Method);}).toThrow(/Collision/); + expect(function () {compose(Method, Forbid);}).toThrow(/Collision/); + expect(function () {compose(Property, Forbid);}).toThrow(/Collision/); + expect(function () {compose(Forbid, Property);}).toThrow(/Collision/); + }); + it("can be used as a standalone function", function () { var collisionSetup = Collision.collisionSetup; - expect(typeof collisionSetup({ methods: { foo: "allow" } })).toBe("function") + expect(typeof collisionSetup({ methods: { foo: "allow" } })).toBe("function"); }); it("forbid without conflicts", function () { @@ -153,6 +244,11 @@ describe("@stamp/collision", function () { methods: { draw: null } + }) + .compose({ + methods: { + draw: "BAADFOOD" + } }); var NoDefer = compose(Collision, { @@ -161,6 +257,8 @@ describe("@stamp/collision", function () { } } ); + NoDefer.compose.deepConfiguration = {}; + NoDefer.compose.deepConfiguration.Collision = {}; NoDefer.compose.deepConfiguration.Collision.defer = null; var NoForbid = compose(Collision, { @@ -169,10 +267,25 @@ describe("@stamp/collision", function () { } } ); + NoForbid.compose.deepConfiguration = {}; + NoForbid.compose.deepConfiguration.Collision = {}; NoForbid.compose.deepConfiguration.Collision.forbid = null; + NoForbid.compose.methods.random = "not function"; + + var Malformed = compose(Collision, { + methods: { + draw: function () {} + } + } + ); + Malformed.compose.deepConfiguration = {}; + Malformed.compose.deepConfiguration.Collision = null; expect(function () {compose(UndefinedMethod, NoForbid);}).not.toThrow(); expect(function () {compose(UndefinedMethod, NoDefer);}).not.toThrow(); + + + expect(function () {compose(null, Malformed, UndefinedMethod, NoDefer, Malformed, { compose: null });}).not.toThrow(); }); }); diff --git a/packages/collision/index.js b/packages/collision/index.js index 8461f35..22a74d3 100644 --- a/packages/collision/index.js +++ b/packages/collision/index.js @@ -11,7 +11,6 @@ function forEach(maybeArray, fn) { } function forEachMetadata(maybeDescriptor, fn) { - if (!is.isObject(maybeDescriptor)) return; forEach(maybeDescriptor, function (metadataType) { if (!is.isPlainObject(maybeDescriptor[metadataType])) return; forEach(maybeDescriptor[metadataType], function (metadataName) { @@ -51,7 +50,6 @@ function makeProxyFunction(functions, name, delayType) { var haveRegularFns = false, haveProxiesFns = false; for (var i = 0; i < functions.length; i++) { var f = functions[i]; - if (!is.isFunction(f)) continue; if (is.isArray(f._proxiedFunctions)) { haveProxiesFns = true; @@ -100,7 +98,6 @@ function getValidSettings(composables) { if (!is.isPlainObject(settings[metadataType])) return; forEach(settings[metadataType], function (metadataName) { - if (!is.isString(settings[metadataType][metadataName])) return; // malformed settings finalSettings[metadataType] = finalSettings[metadataType] || {}; if (settings[metadataType][metadataName] === "forbid") { @@ -125,7 +122,7 @@ function getValidSettings(composables) { function checkCollisions(finalSettings, finalDescriptor, composables) { var aggregatedComposables = {}; forEach(composables, function (composable) { - var descriptor = composable.compose || composable || {}; + var descriptor = composable.compose || composable; forEachMetadata(descriptor, function (metadataType, metadataName) { aggregatedComposables[metadataType] = aggregatedComposables[metadataType] || {}; var array = aggregatedComposables[metadataType][metadataName] = aggregatedComposables[metadataType][metadataName] || []; @@ -158,7 +155,6 @@ function getSettings(composable) { } var Collision = compose({ - deepConfiguration: { Collision: {} }, staticProperties: { collisionSetup: function (opts) { "use strict"; From a1a1a47e2bb3b1a88ebd700328fd39f4fb2df6d7 Mon Sep 17 00:00:00 2001 From: koresar Date: Sat, 1 Sep 2018 15:14:45 +1000 Subject: [PATCH 5/5] Piping collided methods. Tests. README.md --- README.md | 2 +- packages/collision/README.md | 115 ++++++++++++++++++++++++++++------- 2 files changed, 93 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index e3e34ea..e3e70f3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ _Mono repository for stamp related packages._ -Documentation: https://stampit.js.org +Documentation: https://stampit.js.org/ecosystem * `@stamp/arg-over-prop` - Assign properties passed to the stamp factory * `@stamp/check-compose` - Command line tool to test your 'compose' function implementation diff --git a/packages/collision/README.md b/packages/collision/README.md index 2a285fb..8143bb6 100644 --- a/packages/collision/README.md +++ b/packages/collision/README.md @@ -1,6 +1,6 @@ # @stamp/collision -_Controls collision behavior: forbid or defer_ +_Controls collision behavior: forbid, defer or pipe_ This stamp (aka behavior) will check if there are any conflicts on every `compose` call. Throws an `Error` in case of a forbidden collision or ambiguous setup. @@ -10,44 +10,111 @@ Throws an `Error` in case of a forbidden collision or ambiguous setup. ```js import Collision from '@stamp/collision'; -const ForbidRedrawCollision = Collision.collisionSetup({forbid: ['redraw']}); +const ForbidRedrawCollision = Collision.collisionSetup({methods: {redraw: "forbid" }}); ``` Or if you don't want to import the stamp you can import only the method: ```js import {collisionSetup} from '@stamp/collision'; -const ForbidRedrawCollision = collisionSetup({forbid: ['redraw']}); +const ForbidRedrawCollision = collisionSetup({methods: {redraw: "forbid" }}); ``` -The `defer` collects same named methods and wraps them into a single method. +The `defer` collects same named methods and wraps them into a single method which returns an array of returned values. ```js import Collision from '@stamp/collision'; import {Border, Button, Graph} from './drawable/primitives'; -const UiComponent = Collision.collisionSetup({defer: ['draw']}) +const UiComponent = Collision.collisionSetup({methods: {draw: "defer" }}) .compose(Border, Button, Graph); const component = UiComponent(); -component.draw(); // will draw() all three primitives +const array = component.draw(42); // will draw() all three primitives +``` + + +The `pipe` collects same named methods and wraps them into a single method which pipes method returned values one into another +```js +import Collision from '@stamp/collision'; + +const UserDetails = stampit({ + props: { + firstName: "John", + lastName: "Smith" + }, + methods: { + toJSON(previous) { + return {...previous, firstName: this.firstName, lastName: this.lastName}; + } + } +}); +const UserAuth = stampit({ + props: { + email: "john@example.com", + password: "hashedpassword" + }, + methods: { + toJSON(previous) { + return {...previous, email: this.email, password: this.password}; + } + } +}); + +const User = Collision.collisionSetup({methods: {toJSON: "pipe" }}) +.compose(UserDetails, UserAuth); + +const user = User(); +const json = user.toJSON(); +/* +{ email: 'john@example.com', + password: 'hashedpassword', + firstName: 'John', + lastName: 'Smith' } +*/ ``` ## API -### Static methods +### Forbid, Defer or Pipe an exclusive method +`stamp.collisionSetup({ [META]: { [NAME]: TYPE } }) -> Stamp` +Where: +* `META` - String, one of "methods", "properties", "deepProperties", "propertyDescriptors", "staticProperties", "staticDeepProperties", "staticPropertyDescriptors", "configuration", "deepConfiguration". +* `NAME` - String, the name of your method, property, etc. +* `TYPE` - String, one of "forbid", "defer", "pipe". -#### collisionSetup -Forbid or Defer an exclusive method -`stamp.collisionSetup({forbid: ['methodName1'], defer: ['methodName2']}) -> Stamp` +Example: +```js +MyStamp = MyStamp.collisionSetup({ methods: { setPassword: "forbid" }}); +``` + +### Forbid, Defer or Pipe an all the methods +`stamp.collisionSetup({ [META]: TYPE }) -> Stamp` + +Example: +```js +MyStamp = MyStamp.collisionSetup({ methods: "forbid" }); +``` + +### Forbid, Defer or Pipe everything (NOT RECOMMENDED TO USE) +`stamp.collisionSetup(TYPE) -> Stamp` + +Example - effectively disables any further composition: +```js +MyStamp = MyStamp.collisionSetup("forbid"); +``` + +### Remove (reset) all the collision settings +```js +MyStamp = MyStamp.setupCollision(null); +``` + +### Remove (reset) some collision settings +```js +MyStamp = MyStamp.setupCollision({ methods: null }); +``` -#### collisionForbidAll -Forbid any collisions, excluding those allowed -`stamp.collisionForbidAll() -> Stamp` -#### collisionSettingsReset -Remove any Collision settings from the stamp -`stamp.collisionSettingsReset() -> Stamp` ## Example @@ -58,16 +125,18 @@ import Collision from '@stamp/collision'; import Privatize from '@stamp/privatize'; // General purpose behavior to defer "draw()" method collisions -const DeferDraw = Collision.collisionSetup({defer: ['draw']}); +const DeferDraw = Collision.collisionSetup({methods: {draw: "defer"}}); +const draw1 = jest.fn(); // Spy function const Border = compose(DeferDraw, { methods: { - draw: jest.fn() // Spy function + draw: draw1 } }); +const draw2 = jest.fn(); // Spy function const Button = compose(DeferDraw, { methods: { - draw: jest.fn() // Spy function + draw: draw2 } }); @@ -75,7 +144,7 @@ const Button = compose(DeferDraw, { const PrivateDraw = Privatize.privatizeMethods('draw'); // General purpose behavior to forbid the "redraw()" method collision -const ForbidRedrawCollision = Collision.collisionSetup({forbid: ['redraw']}); +const ForbidRedrawCollision = Collision.collisionSetup({methods: {redraw: "forbid"}}); // The aggregating component const ModalDialog = compose(PrivateDraw) // privatize the "draw()" method @@ -100,10 +169,10 @@ expect(dialog.draw).toBeUndefined(); dialog.redraw(42); // Check if the spy "draw()" deferred functions were actually invoked -expect(Border.compose.methods.draw).toBeCalledWith(42); -expect(Button.compose.methods.draw).toBeCalledWith(42); +expect(draw1).toBeCalledWith(42); +expect(draw2).toBeCalledWith(42); // Make sure the ModalDialog throws every time on the "redraw()" collisions -const HaveRedraw = compose({methods: {redraw() {}}}) +const HaveRedraw = compose({methods: {redraw() {}}}); expect(() => compose(ModalDialog, HaveRedraw)).toThrow(); ```