metamagical-interface
Version:
An interface for attaching metadata to JavaScript objects.
265 lines (218 loc) • 6.48 kB
JavaScript
'use strict';
var _exports, _object, _forObject, _get, _getInherited, _getPropagated, _set, _update;
//----------------------------------------------------------------------
//
// This source file is part of the Meta:Magical project.
//
// See LICENCE for licence information.
// See CONTRIBUTORS for the list of contributors to the project.
//
//----------------------------------------------------------------------
// -- SHARED STATE -----------------------------------------------------
var metadata = new WeakMap();
var metaSymbol = Symbol.for('@@meta:magical');
// -- DEPENDENCIES -----------------------------------------------------
var Refinable = require('refinable');
var Maybe = require('data.maybe');
var Stability = require('./stability');
var _require = require('./assertions');
var assertObject = _require.assertObject;
// -- ALIASES ----------------------------------------------------------
var symbols = Object.getOwnPropertySymbols;
var properties = Object.getOwnPropertyNames;
var keys = Object.keys;
// -- HELPERS ----------------------------------------------------------
/*~
* Retrieves meta-data from an object.
*/
function getMeta(object) {
assertObject(object);
var data = {};
Object.assign(data, object[metaSymbol] || {});
Object.assign(data, metadata.get(object) || {});
return data;
}
/*~
* Tests if something is an object.
*/
getMeta[Symbol.for('@@meta:magical')] = {
'name': 'getMeta',
'documentation': 'Retrieves meta-data from an object.\n '
};
function isObject(x) {
return Object(x) === x;
}
/*~
* Returns an array of values in an object.
*/
isObject[Symbol.for('@@meta:magical')] = {
'name': 'isObject',
'documentation': 'Tests if something is an object.\n '
};
function values(object) {
return properties(object).map(function (key) {
return object[key];
});
}
/*~
*/
values[Symbol.for('@@meta:magical')] = {
'name': 'values',
'documentation': 'Returns an array of values in an object.\n '
};
function maybeLift2(maybeA, maybeB, fn) {
return maybeA.chain(function (a) {
return maybeB.map(function (b) {
return fn(a, b);
});
});
}
maybeLift2[Symbol.for('@@meta:magical')] = {
'name': 'maybeLift2',
'documentation': ''
};
function flatten(xss) {
return xss.reduce(function (ys, xs) {
return ys.concat(xs);
}, []);
}
function hasOwnSymbol(object, symbol) {
return symbols(object).indexOf(symbol) !== -1;
}
function unique(xs) {
return Array.from(new Set(xs));
}
// -- IMPLEMENTATION ---------------------------------------------------
/*~
*/
module.exports = (_exports = Refinable.refine({
/*~
* The object to retrieve meta-data from.
*/
object: (_object = function object() {
return this;
}, _object[Symbol.for('@@meta:magical')] = {
'name': 'object',
'documentation': 'The object to retrieve meta-data from.\n '
}, _object),
/*~
* Changes the object to retrieve meta-data from.
*/
forObject: (_forObject = function forObject(_object2) {
assertObject(_object2);
return this.refine({
object: function object() {
return _object2;
}
});
}, _forObject[Symbol.for('@@meta:magical')] = {
'name': 'forObject',
'documentation': 'Changes the object to retrieve meta-data from.\n '
}, _forObject),
mergingStrategy: {
authors: function authors(xs) {
return unique(xs);
},
stability: function stability(xs) {
return xs.reduce(function (l, r) {
var il = Stability.fromIdentifier(l).index;
var ir = Stability.fromIdentifier(r).index;
return il < ir ? l : /* else */r;
});
},
portability: function portability(xs) {
if (xs.length === 0) {
return null;
} else {
return xs.reduce(function (l, r) {
return l !== 'portable' || r !== 'portable' ? 'not portable' : /* otherwise */'portable';
});
}
}
},
/*~
*/
get: (_get = function get(field) {
var meta = getMeta(this.object());
return field in meta ? Maybe.Just(meta[field]) : /* otherwise */Maybe.Nothing();
}, _get[Symbol.for('@@meta:magical')] = {
'name': 'get',
'documentation': ''
}, _get),
/*~
*/
getInherited: (_getInherited = function getInherited(field) {
var _this = this;
return this.get(field).orElse(function (_) {
return _this.get('belongsTo').cata({
Just: function Just(parent) {
return _this.forObject(parent).getInherited(field);
},
Nothing: function Nothing() {
return Maybe.Nothing();
}
});
});
}, _getInherited[Symbol.for('@@meta:magical')] = {
'name': 'getInherited',
'documentation': ''
}, _getInherited),
/*~
*/
getPropagated: (_getPropagated = function getPropagated(field) {
var _this2 = this;
var visited = new Set();
var metaFrom = function metaFrom(meta) {
var object = meta.object();
if (!visited.has(object)) {
visited.add(object);
return flatten(values(object).filter(isObject).map(function (child) {
if (!visited.has(child)) {
var childMeta = _this2.forObject(child);
return childMeta.get(field).map(function (x) {
return [x];
}).getOrElse([]).concat(metaFrom(childMeta));
} else {
return [];
}
}));
} else {
return [];
}
};
var merge = this.mergingStrategy[field];
var collected = this.get(field).map(function (x) {
return [x];
}).getOrElse([]).concat(metaFrom(this));
return merge ? merge(collected) : /* else */collected;
}, _getPropagated[Symbol.for('@@meta:magical')] = {
'name': 'getPropagated',
'documentation': ''
}, _getPropagated),
/*~
*/
set: (_set = function set(field, value) {
var object = this.object();
var meta = metadata.get(object) || {};
meta[field] = value;
metadata.set(object, meta);
return this;
}, _set[Symbol.for('@@meta:magical')] = {
'name': 'set',
'documentation': ''
}, _set),
/*~
*/
update: (_update = function update(meta) {
var _this3 = this;
keys(meta).forEach(function (key) {
return _this3.set(key, meta[key]);
});
}, _update[Symbol.for('@@meta:magical')] = {
'name': 'update',
'documentation': ''
}, _update)
}), _exports[Symbol.for('@@meta:magical')] = {
'name': 'exports',
'documentation': ''
}, _exports);