persistent-clazz
v1.1.2
Published
A collection of utilities for making lightweight persistent objects in JS.
Downloads
1
Maintainers
Readme
persistent-clazz
A collection of utilities for making lightweight persistent objects (and hierarchies of such) in JS.
This library facilitates a specific style of OOP which favours composition over inheritance and immutable over mutable.
##Motivation/About
Making a user friendly persistent API is tricky, especially when dealing with nested structures. You have to explicitly construct a new object on each change. You cannot just call a method of an object you reference - you have to update the reference with the new version.
However fluent persistent API's are also a real pleasure to use and they work as a very nice alternative to functional composition. JavaScript already has facilities for creating immmutable datastructures - the Object.freeze
method so all it takes to finish up the job is to define a couple of very simple helpers.
##Example
The following example defines two types, Point
and Circle
, where both of them are persistent.
const {clazz, getter, setter, alias, lens, assign} = require('../src/main')
const number = (val) => {
if (typeof val === 'number') {
return val
} else {
throw new Error(val + ' is not a number')
}
}
//Define a class-like object using the 'clazz' helper (or with any other)
const Point = clazz({
// Declare properties and their default values
x: 0,
y: 0,
// Easily define getters and immutable setters with support for validation:
setX: setter('x', number),
setY: setter('y', number),
getX: getter('x'),
getY: getter('y'),
// Define normal methods
toString(){
return `(${this.x}, ${this.y})`
},
})
exports.getterSetter = ({throws, equal, done}) => {
// Create the object as you normally would.
point = Point({x:1, y:2})
// Apply transformations to an object to get a new one
newPoint = point
.setX(2)
.setY(3)
// The reference to the new object is updated
equal(newPoint.toString(), '(2, 3)')
// Old value remains unchanged
equal(point.toString(), '(1, 2)' )
// Validation functions passed in to the setter are automatically called
throws(()=> { point.setX('a')} )
// The properties you pass in the constructor are validated against the default values in the prototype
throws(()=> Point({x:'1', y:'2'})) // ""x" is set to a value of type "string" in the constructor, but it is a "number" in the object prototype."
done()
}
const Circle = clazz({
center:Point(),
radius: 0,
//Create aliases methods of member objects
printCenter: alias('center', 'toString'),
// Create lenses for modifying properties of member objects.
setX:lens('center', 'setX'),
setY:lens('center', 'setY'),
// Use the low level 'assign' method to define custom modification methods without also defining explicit setters
changeSize (amount) {
return this.assign({radius: this.radius + amount})
}
})
exports.hierarchies = ({throws, equal, done}) => {
// When there is no constructor defined, you just pass a plain object that you want to use:
const circle = Circle({radius:1})
// You can use methods for both the host and member objects.
biggerCircle = circle
.changeSize(1)
.setX(10)
.setY(10)
equal(biggerCircle.radius, 2)
equal(biggerCircle.printCenter(), '(10, 10)')
//
//lenses and aliases cannot be created if there are no default values for the properties
throws(() => clazz({
unexistingMethod: lens('unexistingProperty', 'method')
})) // The property "unexistingProperty" is undefined in the clazz please set a default value for the property before making an lens
//lenses and aliases cannot be created if there are no default values for the properties
throws(() => clazz({
center:Point(),
unexistingMethod: lens('center', 'unexistingMethod')
})) // The object that is stored in "center" does not have a method "unexistingMethod" You cannot create an lens for an unexisting method
done()
}
Functions
clazz(proto) ⇒ function
Creates a class-like object constructor.
Kind: global function
Returns: function - An object constructor which calls the prototype's constructor
method and then sets the prototype of
the resulting object to proto
.
| Param | Type | Description |
| --- | --- | --- |
| proto | object | The prototype. It can contain a key called constructor
with function which must return an object. If it does, this function is used as the object's constructor. If it does not, a default constructor is used. It can also contain a key called properties
with a plain object specifying all properties that the object can have: default
, lens
, alias
. See below. |
assign(source, target) ⇒ object
Applies a transformation to one or several properties of an object and returns a transformed object with the same prototype and the same values of non-altered properties.
Kind: global function
Returns: object - A new version of the instance object.
| Param | Type | Description | | --- | --- | --- | | source | object | The object which you want to transform. | | target | object | A plain object containing one or several keys which are to be changed or added to the instance, along with their new values. Multiple targets are also supported. |
remove(source, target) ⇒ object
Applies a transformation to one or several properties of an object and returns a transformed object with the same prototype and the same values of non-altered properties.
Kind: global function
Returns: object - A new version of the instance object.
| Param | Type | Description | | --- | --- | --- | | source | object | The object which you want to transform. | | target | object | A plain object containing one or several keys which are to be changed or added to the instance, along with their new values. Multiple targets are also supported. |
getter(key) ⇒ function
Creates a method that retrieves the value of a property.
Kind: global function
Returns: function - A function which when attached to an object returns the current value of the property.
| Param | Type | Description | | --- | --- | --- | | key | string | The key of the property. |
setter(key) ⇒ function
Creates a method that changes the value of a property (by creating a new version of the object).
Kind: global function
Returns: function - A function which when attached to an object returns a new version of the object to which it is attached,
where the value of key
is changed changed to the one passed as an argument.
| Param | Type | Description | | --- | --- | --- | | key | string | The key of the property. |
alias(key, methodName) ⇒ function
Creates a method that calls another method on one of the values stored in the object and returns the result.
May be used for creating shorthands for calling a getter
.
Kind: global function
Returns: function - A function which when attached to an object calls the aliased method with the arguments given to it and returns the result.
| Param | Type | Description | | --- | --- | --- | | key | string | The key where the aliased object is stored. | | methodName | string | The name of the method. |
lens(key, methodName) ⇒ function
A combination between set
and alias
. Creates a method that modifies an object's key and returns a new version
of the object with the new version of the key. Can be for creating shorthands for calling a setter
.
Kind: global function
Returns: function - A function which when attached to an object calls the aliased method with the arguments given to it and returns a new version of the object where the value of key
is replaced with the result of the method.
| Param | Type | Description | | --- | --- | --- | | key | string | The key where the aliased object is stored. | | methodName | string | The name of the method. The method should return a new version of the object. |