Skip to content

Instantly share code, notes, and snippets.

@danielkcz
Last active June 18, 2016 03:40
Show Gist options
  • Save danielkcz/3a1bac834c971f604b8bfac4a6f813c4 to your computer and use it in GitHub Desktop.
Save danielkcz/3a1bac834c971f604b8bfac4a6f813c4 to your computer and use it in GitHub Desktop.
Simple example of use of the stampit library to create easy to define models with getters/setters and true private values.
import stampit from 'stampit';
import EventEmitter from 'events';
const EventEmittable = stampit({
initializers: function initEventEmitter() {
Reflect.apply(EventEmitter, this, []);
},
methods: ['emit', 'on', 'once', 'removeListener'].reduce((methods, methodName) => {
methods[methodName] = EventEmitter.prototype[methodName];
return methods;
}, {}),
});
export default EventEmittable;
import { upperFirst, isFunction } from 'lodash';
import stampit from 'stampit';
import EventEmittable from './EventEmittable';
import Private from './Private';
export const ModelPrivate = Private.methods({
getProperty(owner, propertyName) {
return this.get(owner, 'props')[propertyName];
},
setProperty(owner, propertyName, propertyValue) {
const props = this.get(owner, 'props');
props[propertyName] = propertyValue;
},
});
function Model(modelName, privates = ModelPrivate.create()) {
function initializeModelPrivateArea() {
privates.init(this);
privates.set(this, 'props', {});
}
const getter = setupPropertyAccessor(privates, true);
const property = setupPropertyAccessor(privates);
function valueOf() {
return Object.assign({}, privates.get(this, 'props'));
}
const data = { get: valueOf };
function toString() {
return `model of ${modelName}`;
}
function getModelName() {
return modelName;
}
return stampit.compose(EventEmittable, {
initializers: [initializeModelPrivateArea],
methods: { getModelName, toString, valueOf },
propertyDescriptors: { data },
staticProperties: { getter, property },
});
}
function setupPropertyAccessor(privates, readonly = false) {
return function accessProperty(propertyName, defaultValue = null) {
const methodSuffix = upperFirst(propertyName);
const methods = {};
methods[`get${methodSuffix}`] = function getPropertyValue() {
return privates.getProperty(this, propertyName);
};
function setPropertyValue(newValue) {
const oldValue = privates.getProperty(this, propertyName);
privates.setProperty(this, propertyName, newValue);
this.emit('change', propertyName, newValue, oldValue);
}
if (readonly !== true) {
methods[`set${methodSuffix}`] = setPropertyValue;
}
function initializePropertyValue(data) {
let initValue = defaultValue;
if (isFunction(defaultValue)) {
const obtainedValue = Reflect.apply(defaultValue, this, [data]);
initValue = obtainedValue;
} else if (data && data.hasOwnProperty(propertyName)) {
initValue = data[propertyName];
}
privates.setProperty(this, propertyName, initValue);
}
return this.compose({
methods, initializers: [initializePropertyValue],
});
};
}
export default Model;
import Model from './Model';
import Private from './Private';
const privates = Private.create();
export default Model('Person', privates)
.property('firstname')
.property('surname')
.getter('sex', 'male')
).methods({
getFullName() {
return `${this.getFirstname()} ${this.getSurname} is ${this.getSex()}`.trim();
}
});
import stampit from 'stampit';
const Private = stampit()
.init((options, { instance }) => {
instance.map = new WeakMap();
})
.methods({
init(owner) {
const ownerMap = new Map();
this.map.set(owner, ownerMap);
return ownerMap;
},
for(owner) {
return this.map.get(owner);
},
get(owner, key) {
const ownerMap = this.for(owner);
return ownerMap.has(key) ? ownerMap.get(key) : null;
},
set(owner, key, value) {
this.for(owner).set(key, value);
return value;
},
})
;
export default Private;
@danielkcz
Copy link
Author

danielkcz commented May 17, 2016

The PersonModel can be extended in any way you like, composing other stamps into it. This can be used simply like this.

const person = PersonModel.create({ firstname: 'Daniel' });
console.log(person.getFullName()); // Daniel is male
person.setSurname('K.');
console.log(person.getFullName()); // Daniel K. is male
person.setSex('female') // TypeError: person.setSex is not a function

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment