UNPKG

metamagical-interface

Version:

An interface for attaching metadata to JavaScript objects.

265 lines (218 loc) 6.48 kB
'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);