What is a Stamp
Stamp is a composable factory function.
NOTE
Factory functions create and return new objects.
Go to API Quick start to learn how to create a Stamp.
const object = Stamp() // creating a new object from a stampStamp's metadata (descriptor)
But unlike plain functions, Stamps carry around the metadata about the object they are going to produce. The metadata is attached to the Stamp.compose object. Another name for that metadata is "stamp descriptor" or just "descriptor".
const descriptor = Stamp.compose // retrieving a stamp's metadataThere are 11 standardized types of metadata:
interface Descriptor {
methods: Object,
properties: Object,
deepProperties: Object,
propertyDescriptors: Object,
initializers: Function[],
staticProperties: Object,
staticDeepProperties: Object,
staticPropertyDescriptors: Object,
composers: Function[],
configuration: Object,
deepConfiguration: Object
}You, the developer, is allowed and encouraged to operate that metadata manually if needed.
For example, you can always check which methods an object instance will have:
console.log('Object will have these methods:', Object.keys(Stamp.compose.methods))Ducktyping a stamp
To understand if your object is a stamp you need to check its property .compose like this:
function isStamp (object) {
return typeof object === 'function' && typeof object.compose === 'function'
}Composing stamps
The main reason why stamps exist is the ability to freely compose stamps together.
const ComposedStamp = stampit(Stamp1, Stamp2, Stamp3)The line above is identical to these:
const ComposedStamp = Stamp1.compose(Stamp2, Stamp3)
const ComposedStamp = Stamp1.compose(Stamp2).compose(Stamp3)
const ComposedStamp = Stamp1.compose(Stamp2.compose(Stamp3))
const ComposedStamp = stampit().compose(Stamp1, Stamp2, Stamp3)The stampit and .compose functions are doing only one thing: merge stamp descriptors according to the stamp specification.
NOTE
Every time you call
stampitor.composeyou create a NEW stamp.
Naming conflict resolution
There is no naming conflict resolution in stamps by default. This means that in case of conflicting properties/methods the last composed overwrites all previous.
const Stamp1 = stampit({ prop: { conflicting: 'foo' } })
const Stamp2 = stampit({ prop: { conflicting: 'oops!!!' } })
const ComposedStamp = compose(Stamp1, Stamp2)
ComposedStamp.compose.properties.conflicting === 'oops!!!'
const ReverseComposedStamp = compose(Stamp2, Stamp1)
ReverseComposedStamp.compose.properties.conflicting === 'foo'This behaviour is by design.
NOTE
If you really need to make sure none overwrites your method you can use @stamp/collision stamp. Compose it into your stamp. However, in our practice stamps tend to stay quite small, so that conflict resolution is never needed.
Creating stamps
To create a new stamp from scratch you would need to use one of the JavaScript modules available:
@stamp/it- the same as the compose function above, but provides some additional nicer APIstampit- the same as the@stamp/itabove, but optimized for browsers
You can use both stampits same way as if it was the compose function. But you cannot use compose function same way as stampit. For example, these lines generate same stamp:
const StampFromCompose = compose(Stamp1, Stamp2, Stamp3)
const StampFromStampit = stampit(Stamp1, Stamp2, Stamp3)However, stampit adds few more handy APIs to your stamp (see comments):
const NewStamp1 = StampFromStampit.methods({ myMethod () {} }) // add a method metadata using chaining API
const NewStamp2 = stampit({ props: { myProperty: 'my value' } }) // using "props" metadata shortcut
// etcWhereas compose does not have the handy API (see comments):
StampFromCompose.methods === undefined // THERE IS NO .methods CHAINING API
compose({ props: { myProperty: 'my value' } }) // WRONG! YOU MUST USE FULL `properties` KEYNOTE
The stampit nicer API is nothing else but few additional static methods in your stamps. That's it.
Last updated