Fun with Stamps. Episode 9. Detaching compose()

Vasyl Boroviak
3 min readJun 23, 2016

Hello. I’m developer Vasyl Boroviak and welcome to the ninth episode of Vasyl Boroviak presents Fun with Stamps.

TL;DR

You can detach a .compose() method to become the compose() standalone function because the .compose() method is not bound.

import ThirdPartyStamp from 'somewhere';const compose = ThirdPartyStamp.compose;// use it as regular compose function
compose({properties, method, initializers});

The problem

Have you ever had this problem when your code was using several implementations of promises in a single JavaScript application? Once I had Bluebird, Q, and native promises loaded and running in the same node.js process.

Here is how it’s typically happens:

import {readUser, readGroup} from ‘whatever’;
import Promise from 'q';
export function getGroups(userId) {
const userPromise = readUser(userId); // returns bluebird
return userPromise.then(user => {
const allGroups = user.groups.map(readGroup); // all bluebird
return
Promise.all(allGroups); // Q promise!
});
}

I wish Promises specification would allow detaching the .then() method to a Promise constructor. Like this:

import {readUser, readGroup} from ‘whatever’;export function getGroups(userId) {
const userPromise = readUser(userId);
const Promise = userPromise.then; // detaching bluebird promise
return userPromise.then(user => {
const allGroups = user.groups.map(readGroup);
return Promise.all(allGroups); // still bluebird!!!
});
}

Stamps’ solution

You can reuse compose() implementation by detaching the .compose() method from any stamp.

import ThirdPartyStamp from 'somewhere';const compose = ThirdPartyStamp.compose; // detaching

And use is as a regular compose() standalone function.

compose({properties, method, initializers, staticProperties, ...});

But for purity (and possibly Garbage Collection issues) it’s recommended cloning that function using JavaScript .bind(). You would strip off any attached properties as well.

const Stamp = ThirdPartyStamp.compose({methods: {foo() {}}});let compose = Stamp.compose;
console.log(compose.methods.foo); // [Function: foo]
compose = compose.bind();
console.log(compose.methods); // undefined

Detaching compose() from an infected stamp

From the previous episode you learned that stamps can be infected with additional functionality. When you detach the .compose() method from an infected stamp the compose() function will still be infected.

For example we have this infected compose() which prints its all arguments:

function debuggerCompose(...args) {
console.log('Composing these:', args);
args.push({staticProperties: {
compose: debuggerCompose
}});
return compose.apply(this, args);
}
const InfectedStamp = debuggerCompose();

Let’s detach the .compose() form the InfectedStamp:

const compose = InfectedStamp.compose; // detaching

And try creating a stamp from it to see if it’s still infected or not:

const NewStamp = compose();

The code above will print:

Composing these: []

This means that the NewStamp is still infected. New stamps created from it will also be infected:

const NewNewStamp = NewStamp.compose({properties: {bar: 2}});

Will print this:

Composing these: [ { properties: { bar: 2 } } ]

As you can see detaching doesn’t help to disinfect a stamp.

Disinfecting a detached compose()

It depends on how the infection is implemented. But generally, you can’t disinfect an infected compose().

--

--