NOTE! THE NEW HOME FOR THIS PAGE IS https://stampit.js.org
NOTE! This page is not maintained since May 2018
- Example
- stampit(...args)
- The stamp object
- stamp.methods(...args)
- stamp.props(...args) and stamp.properties(...args)
- stamp.init(...args) and stamp.initializers(...args)
- stamp.composers(...args)
- stamp.deepProps(...args) and stamp.deepProperties(...args)
- stamp.statics(...args) and stamp.staticProperties(...args)
- stamp.deepStatics(...args) and stamp.staticDeepProperties(...args)
- stamp.conf(...args) and stamp.configuration(...args)
- stamp.deepConf(...args) and stamp.deepConfiguration(...args)
- stamp.propertyDescriptors(...args)
- stamp.staticPropertyDescriptors(...args)
- stamp.compose(...args)
- stamp.create(...args)
- Shortcut methods
- Chaining methods
- Breaking changes
// Adds .log() method to factory instantiated objects.
const Logger = stampit()
.methods({
log: console.log
});
// Assigns the default connection string.
const DefaultConnectionConfig = stampit()
.init(function ({connectionConfig}) { // can pass the string as factory argument
this.connectionConfig = connectionConfig;
})
.props({ // if nothing was passed this value will be used
connectionConfig: require('./config.json').db.connection
});
// Allow dbConnection to be passed from outside.
const AcceptsDbConnection = stampit()
.init(function ({dbConnection}) {
this.dbConnection = dbConnection;
});
// The connection object.
const DbConnection = stampit()
.props({ // Assigns the mongoose connection object.
dbConnection: mongoose.connection
})
.compose(
Logger, // add logging capability via this.log()
DefaultConnectionConfig, // add the default this.connectionConfig value
AcceptsDbConnection // allow passing dbConnection as argument
)
.init(function () { // Connecting to the DB upon creating an object.
if (!this.dbConnection.readyState) {
this.dbConnection.open(this.connectionConfig);
this.log('Opening a DB connection');
}
})
.methods({ // A method to close the connection.
close() {
if (this.dbConnection.readyState) {
this.dbConnection.close();
this.log('Closing the DB connection');
}
}
});
const conn = DbConnection(); // Open a default DB connection
const conn2 = DbConnection({ dbConnection: conn.dbConnection }); // reusing existing
conn.close(); // Close the conneciton.
// Connect to a different DB.
const localConn = DbConnection({ connectionConfig: 'mongodb://localhost' });
The arguments can be either another stamps, or the following structure:
@param {Object} [options]
Options to build stamp from@param {Object} [options.methods]
A map of method names and bodies for delegation@param {Object} [options.props]
A map of property names and values to be mixed into each new object@param {Object} [options.properties]
Same asoptions.props
@param {Object} [options.init]
A closure (function) used to create private data and privileged methods@param {Object} [options.initializers]
Same asoptions.init
@param {Object} [options.composers]
Similar to initializers, but executed at the composition time@param {Object} [options.deepProps]
An object to be deeply cloned into each newly stamped object@param {Object} [options.deepProperties]
Same asoptions.deepProps
@param {Object} [options.statics]
A map of property names and values to be mixed onto stamp itself@param {Object} [options.staticProperties]
Same asoptions.statics
@param {Object} [options.deepStatics]
An object to be deeply cloned onto stamp itself@param {Object} [options.staticDeepProperties]
Same asoptions.statics
@param {Object} [options.conf]
Arbitrary data assigned to the stamp metadata. Not used by stampit@param {Object} [options.configuration]
Same asoptions.conf
@param {Object} [options.deepConf]
Deeply merged arbitrary data assigned to the stamp metadata. Not used by stampit@param {Object} [options.deepConfiguration]
Same asoptions.conf
@param {Object} [options.propertyDescriptors]
Property descriptors applied to objects. See MDN@param {Object} [options.staticPropertyDescriptors]
Property descriptors applied to stamps. See MDN
Returns a new factory function (called a stamp) that will produce new objects.
@return {Function} stamp
A factory function to produce objects@return {Function} stamp.create
Just like calling the factory function itself@return {Function} stamp.compose
An object map containing the stamp metadata, also composes arguments and creates new stamps based on the current@return {Function} stamp.methods
Add methods to the stamp. Returns a new stamp@return {Function} stamp.props
Add properties by assignment to the stamp. Returns a new stamp@return {Function} stamp.properties
Same asstamp.props
@return {Function} stamp.init
Add an initializer which called on object instantiation. Returns a new stamp@return {Function} stamp.initializers
Add an initializer which called on object instantiation. Returns a new stamp@return {Function} stamp.composers
Add a composer function which is called on stamp composition. Returns a new stamp@return {Function} stamp.deepProps
Add deeply cloned properties to the produced objects. Returns a new stamp@return {Function} stamp.deepProperties
Same asstamp.deepProps
@return {Function} stamp.statics
Add properties to the factory object. Returns a new stamp@return {Function} stamp.staticProperties
Same asstamp.statics
@return {Function} stamp.deepStatics
Add deeply cloned properties to the factory object. Returns a new stamp@return {Function} stamp.staticDeepProperties
Same asstamp.deepStatics
@return {Function} stamp.conf
Assign arbitrary data to the stamp metadata. Not used by stampit. Returns a new stamp@return {Function} stamp.configuration
Same asstamp.conf
@return {Function} stamp.deepConf
Deeply merge and clone arbitrary data to the stamp metadata. Not used by stampit. Returns a new stamp@return {Function} stamp.deepConfiguration
Same asstamp.deepConf
@return {Function} stamp.propertyDescriptors
Property descriptors applied to objects. See MDN. Returns a new stamp@return {Function} stamp.staticPropertyDescriptors
Property descriptors applied to stamps. See MDN. Returns a new stamp
const stamp = stampit({
methods: {
amplify(value) {
return this.factor * value;
}
},
props: {
defaultFactor: 1
},
init({factor}) {
this.factor = factor >= 0 ? factor : this.defaultFactor;
}
});
const objectInstance = stamp({factor: 1.1});
Take n objects and add them to the methods list of a new stamp. Creates new stamp.
@return {Object} stamp
The new stamp is based on the originalthis
stamp.
const stamp = stampit().methods({
error: console.error,
amplify(value) {
if (!isFinite(value) || value < 0) { this.error(`value ${value} is incorrect`); }
return this.factor * value;
}
});
stamp().amplify('BADF00D'); // value BADF00D is incorrect
Take n objects and add them to the references list of a new stamp. Creates new stamp.
@return {Object} stamp
The new stamp is based on the originalthis
stamp.
const stamp = stampit().props({
factor: 1
});
console.log(stamp().factor); // 1
console.log(stamp.props({factor: 5})().factor); // 5
It's really important to know the difference between a reference and an actual value. Primitive
types can be used in props
without limititation. However the following might be source of confusion and bugs.
const stamp = stampit().props({
salaries: []
});
const firstInstance = stamp();
firstInstance.salaries.push(100);
console.log(firstInstance.salaries); // [100]
const secondInstance = stamp();
secondInstance.salaries.push(200);
console.log(firstInstance.salaries); // [100, 200]
console.log(secondInstance.salaries); // [100, 200]
What happened? The salaries
property was kept as reference inside the stamp metadata. Every
instance made from that stamp would share the same reference. To solve this, you can either
create a new array for every instance or simply use deepProps
instead which would make
a copy of everything.
const stamp = stampit().props({
salaries: null
}).init((opts, {instance}) => {
instance.salaries = [];
});
// just last line from previous example, now it works correctly
secondInstance.salaries.push(200); // [200]
Take n functions or array(s) of functions and add the functions to the initializers list of a new stamp. Creates new stamp.
@return {Object} stamp
The new stamp is based on the originalthis
stamp.
Functions passed into .init()
are called any time an
object is instantiated. That happens when the stamp function
is invoked, or when the .create()
method is called.
If an initializer returns a non-undefined value then it becomes the factory instantiated value.
Each function receives the first argument of the called factory function and second object argument is like this:
{
instance, // The same as `this`. Handy when using with the ES6 fat arrows
stamp, // The factory being executed at the moment
args // The arguments passed to the factory
}
Private state.
const stamp = stampit().init((opts, {instance}) => {
const factor = opts.factor || 1;
instance.getFactor = () => factor;
});
console.log(stamp().getFactor()); // 1
console.log(stamp({factor: 2.5}).getFactor()); // 2.5
Make any stamp cloneable.
const Cloneable = stampit().init((opts, {instance, stamp, args}) => {
instance.clone = () => stamp.apply(undefined, args);
});
const MyStamp = stampit().props({x: 42}).compose(Cloneable); // composing with the "Cloneable" behavior
MyStamp().clone().clone().clone().x === 42; // true
Take n functions or array(s) of functions and add the functions to the deepConfiguration.composers
list of a new stamp. Creates new stamp.
@return {Object} stamp
The new stamp is based on the originalthis
stamp.
Functions passed into .composers()
are executed on the spot and every other time the new stamp or its derivatives are being composed. They are sort of a after-composition callbacks.
If a composer function returns a stamp then it becomes the resulting stamp.
Each function receives an options object with two properties:
{
composables, // The list of composables being merged
stamp // The resulting stamp
}
Collect every composable the stamp was created from.
const ComponentsMonitoring = stampit().composers(({stamp, composables}) => {
const conf = stamp.compose.configuration || {};
conf._wasComposedOf = _.uniq(composables.concat(conf._wasComposedOf));
stamp.compose.configuration = conf;
});
const stamp = stampit().compose(ComponentsMonitoring)
.init(() => {})
.methods({foo() {}})
.props({a: 1});
console.log(stamp.compose.configuration._wasComposedOf);
NOTE: The example below can be implemented using plain old higher order functions.
Same time, make your stamps to produce functions instead of plain objects:
const ProduceFunction = stampit({
statics: {
produceCloneOf(func) {
function FirstInitializer() {
function theClone() { return func.apply(this, arguments); };
Object.assign(theClone, Object.getPrototypeOf(this), this);
return theClone;
}
return this.conf({FirstInitializer});
}
},
composers({stamp}) {
const conf = stamp.compose.configuration || {};
if (conf.FirstInitializer) {
let inits = stamp.compose.initializers || [];
// Make my initializer the first.
inits = _.uniq([conf.FirstInitializer].concat(inits));
stamp.compose.initializers = inits;
}
}
});
const stamp = stampit().compose(ProduceFunction, ComponentsMonitoring)
.produceCloneOf(function () {
console.log('A clone of this function was returned as a stamp result');
});
console.log(stamp.compose.configuration.FirstInitializer);
const producedFunction = stamp();
producedFunction(); // prints "A clone of this function was returned as a stamp result"
Take n objects and deep merge them safely to the properties. Creates new stamp.
Note: the merge algorithm will not change any existing props
data of a resulting object instance.
@return {Object} stamp
The new stamp is based on the originalthis
stamp.
const stamp = stampit().deepProps({
effects: {
amplification: 1,
cutoff: {min: 0, max:255}
}
});
console.log(stamp().effects.cutoff.min); // 0
const effectMashup = stamp.deepProps({effects: {cutoff: {min: 42}}})();
console.log(effectMashup.effects.cutoff.min); // 42
console.log(effectMashup.effects.cutoff.max); // 255
Take n objects and add all its properties to the stamp (aka factory object).
@return {Object} stamp
The new stamp is based on the originalthis
stamp.
const stamp = stampit().statics({
printMe() { console.log(this); }
});
stamp.printMe();
It used to be like that:
Object.assign(stamp, {
foo: 'foo'
});
But can be short written as:
stamp = stamp.statics({
foo: 'foo'
});
Same as stamp.statics()
and stamp.staticProperties()
but deeply merges the
provided objects.
Take n objects and add all its properties to the stamp's metadata. This arbitrary data could be used in initializers and static methods for your needs. Not used by stampit.
@return {Object} stamp
The new stamp is based on the originalthis
stamp.
NOTE: Accessible as stamp.compose.configuration
. Do not confuse with the stamp.compose.deepConfiguration
.
Use metadata in initializers:
const stamp = stampit()
.conf({addFactorSetter: false})
.init((opts, {stamp, instance}) => {
let factor = opts.factor || 1;
instance.getFactor = () => factor;
const {configuration} = stamp.compose;
if (configuration && configuration.addFactorSetter) {
instance.setFactor = f => factor = f;
}
});
console.log(stamp().setFactor); // undefined
const stamp2 = stamp.conf({addFactorSetter: false});
console.log(stamp2(5).getFactor()); // 5
Use metadata in static functions:
const stamp = stampit()
.statics({
allowFactorSetter(allow) {
return this.conf({addFactorSetter: !!allow});
}
})
.init((opts, {instance, stamp}) => {
let factor = opts.factor || 1;
instance.getFactor = () => factor;
const {configuration} = stamp.compose;
if (configuration && configuration.addFactorSetter) {
instance.setFactor = f => factor = f;
}
});
console.log(stamp().setFactor); // undefined
const stamp2 = stamp.allowFactorSetter(true);
const instance2 = stamp2()
instance2.setFactor(5);
console.log(instance2.getFactor()); // 5
Same as stamp.conf()
and stamp.configuration()
but deeply merges the
provided objects. This arbitrary data could be used in initializers and static methods for your needs. Not used by stampit.
@return {Object} stamp
The new stamp is based on the originalthis
stamp.
NOTE: Accessible as stamp.compose.deepConfiguration
. Do not confuse with the stamp.compose.configuration
.
Property descriptors applied to the instantiated objects. See MDN
Property descriptors applied to the stamps. See MDN
Take one or more stamp or stamp descriptors and
combine them with this
stamp to produce and return a new stamp.
Combining overrides properties with last-in priority.
@return {Function}
A new stampit factory composed from arguments.
const stamp1 = stampit({ methods: { log: console.log } });
const stamp2 = stampit({ props: { theAnswer: 42 } });
const composedStamp = stamp1.compose(stamp2);
Alias to stamp(...args)
.
Just like calling stamp()
, stamp.create()
invokes the stamp
and returns a new object instance.
const stamp = stampit().init((opts, {args}) => { console.log(...args); });
stamp.create(null, 42); // null, 42
stamp(null, 42); // null, 42
See more useful tips in the advanced examples. (NEED AN OVERHAUL FOR STAMPIT V3)
Also available as import {FUNCTION} from 'stampit'
or const {FUNCTION} = require('stampit')
.
All return a new stamp exactly as the stamp.*
methods above.
- stampit.methods()
- stampit.props()
- stampit.properties()
- stampit.init()
- stampit.initializers()
- stampit.composers()
- stampit.deepProps()
- stampit.deepProperties()
- stampit.statics()
- stampit.staticProperties()
- stampit.deepStatics()
- stampit.staticDeepProperties()
- stampit.conf()
- stampit.configuration()
- stampit.deepConf()
- stampit.deepConfiguration()
- stampit.propertyDescriptors()
- stampit.deepPropertyDescriptors()
- stampit.compose()
All the methods always create new stamps.
const MyStamp = stampit() // creates new stamp
.methods({ // creates new stamp
methodOverride() {
return false;
}
})
.methods({ // creates new stamp
methodOverride() {
return true;
}
})
.props({ // creates new stamp
stateOverride: false
})
.props({ // creates new stamp
stateOverride: true
})
.props({ // creates new stamp
name: { first: 'John' }
})
.props({ // creates new stamp
name: { last: 'Doe' }
})
.statics({ // creates new stamp
staticOverride: false
})
.statics({ // creates new stamp
staticOverride: true
})
.init(function () { // creates new stamp
const secret = 'foo';
this.getSecret = function () {
return secret;
};
})
.init(function bar() { // creates new stamp
this.a = true;
}, function baz() {
this.b = true;
})
.compose(AnotherStamp); // creates new stamp
MyStamp.staticOverride; // true
const obj = MyStamp();
obj.methodOverride; // true
obj.stateOverride; // true
obj.name.first && obj.name.last; // true
obj.getSecret && obj.a && obj.b; // true
Every method of stampit can receive multiple arguments. The properties from later arguments in the list will override the same named properties of previously passed in objects.
const obj = stampit()
.methods({
a() { return 'a'; }
}, {
b() { return 'b'; }
})
.props({
a: 'a'
}, {
b: 'b'
})
.init(function ({x}) {
this.value = x;
}, function ({y}) {
this.value = y; // overwrites previous init() effort
})
.deepProps({
name: { first: 'John' }
}, {
name: { last: 'Doe' }
})
.statics({
foo: 'foo'
}, {
bar: 'bar'
})
.compose(concreteStamp, additionalStamp, utilityStamp)
.create({x: 5, y: 10});
Differences with Stampit v1.
stampit()
now receives options object ({methods,refs,init,props,static}
) instead of multiple arguments.- All chaining methods return new stamps instead of self-mutating this stamp.
state()
always shallow merge properties. It was not doing so in a single rare case.- Instead of factory arguments the
enclose()
functions now receive the following object{ instance, stamp, args }
.
New features
stampit()
now receives options object ({methods,refs,init,props,static}
) instead of multiple arguments.- Instead of factory arguments the
enclose()
functions now receive the following object{ instance, stamp, args }
. - New
stamp.props()
method for deeply merged state. - New
stamp.statics()
method which add properties to stamp, not an object. state
deprecated.refs
must be used instead.enclose
deprecated.init
must be used instead.- All API functions have shortcuts now. I.e. you can write
stampit.init()
instead ofstampit().init()
. Same for methods, refs, props, static. - All unit tests are now on
tape
instead of mocha+should.
Differences with Stampit v2.
- node.js v0.10 is not supported any more because it's maintenance period has ended.
- Stamps from stampit v2 and stampit v3 and not compatible. You should not compose them together.
- Initializers now receive two arguments instead of just one.
First is the factory first argument (i.e.
arguments[0]
), second is the same options object as before -{ instance, stamp, args }
.
Stampit v2:
const Stamp = stampit({ init({instance, stamp, args}) {
// ...
}});
Stampit v3:
const Stamp = stampit({ init(arg, {instance, stamp, args}) {
console.log(arg); // 42
}});
Stamp(42);
- The factory first argument properties are no longer automatically assigned to the instance.
Stampit v2:
const Stamp = stampit({ init({instance, stamp, args}) {
console.log(this);
}});
Stamp({foo: 'bar'}); // {foo: "bar"}
Stampit v3:
const Stamp = stampit({init(arg, {instance, stamp, args}) {
console.log(this);
}});
Stamp({foo: 'bar'}); // {}
A workaround can be implemented as a separate behavior (stamp).
const AssignFirstArgument = stampit({ init(opts) {
Object.assign(this, opts);
}});
Stamp = AssignFirstArgument.compose(Stamp);
Stamp({foo: 'bar'}); // {foo: "bar"}
- A stamp's metadata is now stored in the
stamp.compose
object. Previously it was stored instamp.fixed
object. - Removed
convertConstructor()
. We plan to revive it and support the ES6 classes. - The
.props()
does not deeply merge objects any more, but shallow assigns properties. Just like.properties()
and.refs()
. Use.deepProps()
instead. - Removed
state()
. Useprops()
instead. stampit.mixin()
,.extend()
,.mixIn()
,.assign()
are all gone too. Use ES6Object.assign()
static()
got renamed tostatics()
- The
stampit.isStamp
was moved. You should import it separately now:require('stampit/isStamp')
. - Initializers do not support Promises anymore. Meaning that "thenables" are not automatically unwrapped by initializers.
- The
stamp.init()
andstampit.init()
do not support objects as incoming arguments anymore. Use ES6.init(Object.values(obj))
instead.
New features
- Stampit is compatible with the Stamp Specification.
- You can import shortcuts and utility functions in various ways:
import {statics} from 'stampit'
const {statics} = require('stampit')
- New utility function
isComposable
. Can be imported separately:require('stampit/isComposable')
. - New utility function
compose
. It is the pure standardcompose
function implementation. Can be imported separately:require('stampit/compose')
. - New methods on stamps (
stamp.METHOD
), as well as new shortcut methods on stampit (stampit.METHOD
), as well as new options to stampit (stampit({OPTION: *})
). They are:initializers
,init
,composers
,props
,properties
,deepProps
,deepProperties
,statics
,staticProperties
,deepStatics
,staticDeepProperties
,conf
,configuration
,deepConf
,deepConfiguration
,propertyDescriptors
,staticPropertyDescriptors
Other notable changes
- The
refs
are deprecated now.
Differences with Stampit v3.
- Removed utility functions
stampit/isStamp
,stampit/isComposable
, andstampit/compose
. Use@stamp/is/stamp
,@stamp/is/composable
,@stamp/compose
modules instead. - The composers array is now stored in
stamp.compose.composers
instead of the (temporary) experimental placestamp.compose.deepConfiguration.composers
. - Removed the earlier deprecated
refs
from the API entirely.
New features
- The composers feature is not experimental any more.
- Stampit is now fully compatible with the stamp modules ecosystem.
Other notable changes
- The
.min.js
bundle size got 50% smaller: 5.3 KB -> 2.7 KB. The gzipped size was reduced by 40%: 1.84 KB -> 1.29 KB