- Example
- stampit(...args)
- The stamp object
- stamp.methods(...args)
- stamp.props(...args) and stamp.properties(...args)
- stamp.init(...args)
- stamp.deepProps(...args) and stamp.deepProperties(...args)
- stamp.statics(...args) and stamp.staticProperties(...args)
- stamp.deepStatics(...args) and stamp.deepStaticProperties(...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
- Utility functions
- 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.refs]Same asoptions.props. DEPRECATED@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.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} stampA factory function to produce objects@return {Function} stamp.createJust like calling the factory function itself@return {Function} stamp.composeAn object map containing the stamp metadata, also composes arguments and creates new stamps based on the current@return {Function} stamp.methodsAdd methods to the stamp. Returns a new stamp@return {Function} stamp.propsAdd properties by assignment to the stamp. Returns a new stamp@return {Function} stamp.refsSame asstamp.props. DEPRECATED@return {Function} stamp.propertiesSame asstamp.props@return {Function} stamp.initAdd an initializer which called on object instantiation. Returns a new stamp@return {Function} stamp.initializersAdd an initializer which called on object instantiation. Returns a new stamp@return {Function} stamp.deepPropsAdd deeply cloned properties to the produced objects. Returns a new stamp@return {Function} stamp.deepPropertiesSame asstamp.deepProps@return {Function} stamp.staticAdd properties to the factory object. Returns a new stamp@return {Function} stamp.staticPropertiesSame asstamp.statics@return {Function} stamp.deepStaticsAdd deeply cloned properties to the factory object. Returns a new stamp@return {Function} stamp.staticDeepPropertiesSame asstamp.deepStatics@return {Function} stamp.confAssign arbitrary data to the stamp metadata. Not used by stampit. Returns a new stamp@return {Function} stamp.configurationSame asstamp.conf@return {Function} stamp.deepConfDeeply merge and clone arbitrary data to the stamp metadata. Not used by stampit. Returns a new stamp@return {Function} stamp.deepConfigurationSame asstamp.deepConf@return {Function} stamp.propertyDescriptorsProperty descriptors applied to objects. See MDN. Returns a new stamp@return {Function} stamp.staticPropertyDescriptorsProperty 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} stampThe new stamp based on the originalthisstamp.
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 incorrectTake n objects and add them to the references list of a new stamp. Creates new stamp.
@return {Object} stampThe new stamp based on the originalthisstamp.
const stamp = stampit().props({
factor: 1
});
console.log(stamp().factor); // 1
console.log(stamp({factor: 5}).factor); // 5It'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} stampThe new stamp based on the originalthisstamp.
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.5Make 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; // trueTake 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} stampThe new stamp based on the originalthisstamp.
const stamp = stampit().deepProps({
effects: {
amplification: 1,
cutoff: {min: 0, max:255}
}
});
console.log(stamp().effects.cutoff.min); // 0
const effectMashup = stamp({effects: {cutoff: {min: 42}}});
console.log(effectMashup.effects.cutoff.min); // 42
console.log(effectMashup.effects.cutoff.max); // 255Take n objects and add all its properties to the stamp (aka factory object).
@return {Object} stampThe new stamp based on the originalthisstamp.
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} stampThe new stamp based on the originalthisstamp.
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;
if (stamp.compose.configuration.addFactorSetter) {
instance.setFactor = f => factor = f;
}
});
console.log(stamp().setFactor); // undefined
const stamp2 = stamp.conf({addFactorSetter: false});
console.log(stamp2(5).getFactor()); // 5Use 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;
if (stamp.compose.configuration.addFactorSetter) {
instance.setFactor = f => factor = f;
}
});
console.log(stamp().setFactor); // undefined
const stamp2 = stamp.allowFactorSetter(true);
console.log(stamp2().setFactor(5).getFactor()); // 5Same 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} stampThe new stamp based on the originalthisstamp.
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, 42See 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.deepProps()
- stampit.deepProperties()
- stampit.statics()
- stampit.staticProperties()
- stampit.deepStatics()
- stampit.deepStaticProperties()
- stampit.conf()
- stampit.configuration()
- stampit.deepConf()
- stampit.deepConfiguration()
- stampit.propertyDescriptors()
- stampit.deepPropertyDescriptors()
- stampit.compose()
import compose from 'stampit/compose'; // or
const compose = require('stampit/compose');It's a pure clean standard compose function implementation. See specification.
import isStamp from 'stampit/isStamp'; // or
const isStamp = require('stampit/isStamp');Take an object and return true if it's a stamp, false otherwise.
import isComposable from 'stampit/isComposable'; // or
const isComposable = require('stampit/isComposable');Take an object and return true if it's a stamp or a stamp descriptor.
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; // trueEvery 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. statedeprecated.refsmust be used instead.enclosedeprecated.initmust 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
tapeinstead 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.composeobject. Previously it was stored instamp.fixedobject. - 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.isStampwas 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 standardcomposefunction 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,props,properties,deepProps,deepProperties,statics,staticProperties,deepStatics,staticDeepProperties,conf,configuration,deepConf,deepConfiguration,propertyDescriptors,staticPropertyDescriptors
Other notable changes
- The
refsare deprecated now.