UNPKG

json-prism

Version:

Immutable Prisms, i.e. Taking a Multifoci Json Path to Immutability, or A sentient item has some ability to communicate, either by sharing its emotions, broadcasting its thoughts telepathically, or speaking aloud.

573 lines (535 loc) 20.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _defineEnumerableProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/defineEnumerableProperties")); var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread")); // const {has, returns} = require('shades'); var F = require('functional-pipelines'); var _require = require('../../utils/sortByPath'), sortByPath = _require.sortByPath; var _require2 = require('./SentientBatch'), MultiWriter = _require2.MultiWriter; var _require3 = require('rxjs'), BehaviorSubject = _require3.BehaviorSubject; var _require4 = require('rxjs/operators'), take = _require4.take, toArray = _require4.toArray; var _require5 = require('../../utils/trace'), show = _require5.show, aLine = _require5.aLine; describe('It should collect deep paths from existing stable-shaped-document (by design)', function () { it('works', function (done) { var MiniSeries = require('./mini-series.data'); // const nodes = jp.nodes(MiniSeries, '$..*'); // const pathSheet = nodes.map(({path, value}) => ([jp.stringify(path), value])); var pathSheet = require('./path-sheet.data'); var pathList = [// '$.sequence', // '$.episodes', // '$.sequence[0]', '$.sequence[1]', // '$.sequence[2]', // '$.episodes.EP1', // '$.episodes.EP2', // '$.episodes.EP3', // '$.episodes.EP1.sequence', // '$.episodes.EP1.EP11', // '$.episodes.EP1.EP12', // '$.episodes.EP1.EP13', // '$.episodes.EP1.sequence[0]', '$.episodes.EP1.sequence[1]', // '$.episodes.EP1.sequence[2]', // '$.episodes.EP1.EP11.start', '$.episodes.EP1.EP11.start.place', '$.episodes.EP1.EP11.start.time', // '$.episodes.EP1.EP12.start', // '$.episodes.EP1.EP12.start.place', // '$.episodes.EP1.EP12.start.time', // '$.episodes.EP1.EP13.start', // '$.episodes.EP1.EP13.start.place', // '$.episodes.EP1.EP13.start.time', // '$.episodes.EP2.sequence', // '$.episodes.EP2.EP21', // '$.episodes.EP2.EP22', // '$.episodes.EP2.EP23', // '$.episodes.EP2.sequence[0]', // '$.episodes.EP2.sequence[1]', // '$.episodes.EP2.sequence[2]', // '$.episodes.EP2.EP21.start', // '$.episodes.EP2.EP21.start.place', // '$.episodes.EP2.EP21.start.time', // '$.episodes.EP2.EP22.start', // '$.episodes.EP2.EP22.start.place', // '$.episodes.EP2.EP22.start.time', // '$.episodes.EP2.EP23.start', // '$.episodes.EP2.EP23.start.place', // '$.episodes.EP2.EP23.start.time', // '$.episodes.EP3.sequence', // '$.episodes.EP3.EP31', // '$.episodes.EP3.EP32', // '$.episodes.EP3.EP33', // '$.episodes.EP3.sequence[0]', // '$.episodes.EP3.sequence[1]', // '$.episodes.EP3.sequence[2]', // '$.episodes.EP3.EP31.start', '$.episodes.EP3.EP31.start.place', '$.episodes.EP3.EP31.start.time', // '$.episodes.EP3.EP32.start', '$.episodes.EP3.EP32.start.place', '$.episodes.EP3.EP32.start.time', // '$.episodes.EP3.EP33.start', '$.episodes.EP3.EP33.start.place', '$.episodes.EP3.EP33.start.time']; var journal = new BehaviorSubject({ value: MiniSeries }); var clicks = 0; var fs = require('fs'); // const journalLog = fs.createWriteStream('./journal-expected-log.json'); journal.subscribe(function (entry) { show('+JournalEntry', ++clicks, entry, entry.gets ? entry.gets('$.___marker.version') : 'NO .gets'); }, function (error) { return console.error(error); }); journal.pipe(take(21), toArray()).subscribe(function (journal) { try { // expect(journal.map(({gets, sets, ...rest}) => rest)).toEqual(require('./journal-expected-stringified')); // @NOTICE we strip out the gets/sets attrs before we compare with the JSON.stringified sanitized version of the json-tree // journalLog.write(JSON.stringify(journal, null, 0) + '\n', undefined, done); done(); } catch (error) { done.fail(error); } }, function (error) { return done.fail(error); }, function () { return ( /*journalLog.end(`@clicks: ${clicks}`),*/ show('ALL-DONE') ); }); var multiCatcher = MultiWriter(journal, { labels: ['BatchSentient-007'] })(pathList); var v1Values = ["EP2", "EP12", "EP11-Place", "EP11 @ Wed May 15 2019 23:48:27 GMT+0200 (EET)", "EP31-Place", "EP31 @ Wed May 15 2019 23:48:27 GMT+0200 (EET)", "EP32-Place", "EP32 @ Wed May 15 2019 23:48:27 GMT+0200 (EET)", "EP33-Place", "EP33 @ Wed May 15 2019 23:48:27 GMT+0200 (EET)"]; expect(multiCatcher.updates()).toEqual(v1Values); var notAvailables = []; notAvailables.length = pathList.length; notAvailables.fill('NOT-AVAILABLE'); var revisions = multiCatcher.updates(notAvailables); expect(revisions.version).toEqual(notAvailables.length); expect(revisions).toEqual({ "action": "set", "gets": expect.any(Function), "labels": ["BatchSentient-007"], "path": "$.episodes.EP3.EP33.start.time", "sets": expect.any(Function), "value": { "episodes": { "EP1": { "EP11": { "start": { "place": "NOT-AVAILABLE", "time": "NOT-AVAILABLE" } }, "EP12": { "start": { "place": "EP12-Place", "time": "EP12 @ Wed May 15 2019 23:48:27 GMT+0200 (EET)" } }, "EP13": { "start": { "place": "EP13-Place", "time": "EP13 @ Wed May 15 2019 23:48:27 GMT+0200 (EET)" } }, "sequence": ["EP11", "NOT-AVAILABLE", "EP13"] }, "EP2": { "EP21": { "start": { "place": "EP21-Place", "time": "EP21 @ Wed May 15 2019 23:48:27 GMT+0200 (EET)" } }, "EP22": { "start": { "place": "EP22-Place", "time": "EP22 @ Wed May 15 2019 23:48:27 GMT+0200 (EET)" } }, "EP23": { "start": { "place": "EP23-Place", "time": "EP23 @ Wed May 15 2019 23:48:27 GMT+0200 (EET)" } }, "sequence": ["EP21", "EP22", "EP23"] }, "EP3": { "EP31": { "start": { "place": "NOT-AVAILABLE", "time": "NOT-AVAILABLE" } }, "EP32": { "start": { "place": "NOT-AVAILABLE", "time": "NOT-AVAILABLE" } }, "EP33": { "start": { "place": "NOT-AVAILABLE", "time": "NOT-AVAILABLE" } }, "sequence": ["EP31", "EP32", "EP33"] } }, "sequence": ["EP1", "NOT-AVAILABLE", "EP3"] }, "version": 10 }); multiCatcher.updates(v1Values); expect(multiCatcher.updates()).toEqual(v1Values); }); }); ///// @TODO: LEGACY CODE AHEAD ////// describe.skip('scenario: shades', function () { it('inspiration:: should handle binding', function () { var base = { IDTag: function IDTag() { return this.tag; } }; var extended = (0, _objectSpread2["default"])({}, base, { tag: 'hi' }); expect(has({ IDTag: returns('hi') })).toBeTruthy(); }); it('property accessors get/set do not survive shallow copying and turn into normal attributes', function () { var _virtual, _base, _mutatorMap; var base = (_base = {}, _virtual = 'virtual', _mutatorMap = {}, _mutatorMap[_virtual] = _mutatorMap[_virtual] || {}, _mutatorMap[_virtual].get = function () { console.log('GET-ATTR'); return "Oh - ".concat(this.tag); }, (0, _defineEnumerableProperties2["default"])(_base, _mutatorMap), _base); var extended = (0, _objectSpread2["default"])({}, base, { tag: 'hi' }); expect(base.virtual).toEqual('Oh - undefined'); expect(extended.virtual).toEqual('Oh - undefined'); base.tag = 'Bye'; // old school patching expect(base.virtual).toEqual('Oh - Bye'); expect(extended.virtual).toEqual('Oh - undefined'); // getter and setters don't survive the ...spread operator }); it('should handle first-level (shallow) virtual accessors with context this', function () { var forKey = function forKey(key) { return (0, _defineProperty2["default"])({}, "".concat(key), function _(newValue) { if (newValue === undefined) { console.log('GET-ATTR'); return "Oh - ".concat(this.tag); } else { this.tag = newValue; console.log('SET-ATTR'); return newValue; } }); }; var base = forKey('virtual'); expect(base.virtual()).toEqual('Oh - undefined'); var extended = (0, _objectSpread2["default"])({}, base, { tag: 'hi', childAttr: 'protected against access from base references' }); expect(base.virtual()).toEqual('Oh - undefined'); expect(extended.virtual()).toEqual('Oh - hi'); base.virtual('Bye'); expect(base.virtual()).toEqual('Oh - Bye'); expect(extended.virtual()).toEqual('Oh - hi'); }); it('should handle first-level (shallow) virtual accessors with caller reference this', function () { var forKey = function forKey(key) { return (0, _defineProperty2["default"])({}, "".concat(key), function _(newValue) { if (newValue === undefined) { console.log('GET-ATTR'); return "Oh - ".concat(this.tag); } else { this.tag = newValue; console.log('SET-ATTR'); return newValue; } }); }; var base = forKey('virtual'); expect(base.virtual()).toEqual('Oh - undefined'); var extended = (0, _objectSpread2["default"])({}, base, { tag: 'hi', childAttr: 'protected against access from base references' }); expect(base.virtual()).toEqual('Oh - undefined'); expect(extended.virtual()).toEqual('Oh - hi'); base.virtual('Bye'); expect(base.virtual()).toEqual('Oh - Bye'); expect(extended.virtual()).toEqual('Oh - hi'); extended.virtual('Welcome'); expect(base.virtual()).toEqual('Oh - Bye'); expect(extended.virtual()).toEqual('Oh - Welcome'); }); it('should handle dynamic first-level (shallow) virtual accessors', function () { var forKey = function forKey(key) { return (0, _defineProperty2["default"])({}, "get".concat(key[0].toUpperCase()).concat(key.slice(1)), function get(newValue) { if (newValue === undefined) { console.log("GET-ATTR::".concat(key)); return this[key]; } else { this[key] = newValue; console.log("SET-ATTR::".concat(key)); return newValue; } }); }; var attrName = 'childAttr'; var expectedAccessorName = "get".concat(attrName[0].toUpperCase()).concat(attrName.slice(1)); var base = forKey('childAttr'); expect(base[expectedAccessorName]()).toEqual(); var theGist = 'protected against access from base references'; var extended = (0, _objectSpread2["default"])({}, base, { tag: 'hi', childAttr: theGist }); expect(base[expectedAccessorName]()).toEqual(); expect(extended[expectedAccessorName]()).toEqual(theGist); base[expectedAccessorName]('Bye'); expect(base[expectedAccessorName]()).toEqual('Bye'); expect(extended[expectedAccessorName]()).toEqual(theGist); extended[expectedAccessorName]('Welcome'); expect(base[expectedAccessorName]()).toEqual('Bye'); expect(extended[expectedAccessorName]()).toEqual('Welcome'); }); }); describe.skip('Catchers', function () { it('should handle dynamic first-level (shallow) virtual accessors', function () { var catcher = function catcher(keys) { var _ref6; var setInto = function setInto(self) { return function (name) { return function (value) { return self[name] = value; }; }; }; var getFrom = function getFrom(self) { return function (name) { return self[name]; }; }; return _ref6 = {}, (0, _defineProperty2["default"])(_ref6, '___included', function ___included() { return keys; }), (0, _defineProperty2["default"])(_ref6, "get___", function get___(newValues) { if (newValues === undefined) { var getter = getFrom(this); var ___included = this.___included(); console.log("GET-ATTR::".concat(___included)); return ___included.map(function (k) { return getter(k); }); } else { var setter = setInto(this); var _included = this.___included(); F.map(function (_ref4) { var _ref5 = (0, _slicedToArray2["default"])(_ref4, 2), k = _ref5[0], nV = _ref5[1]; return setter(k)(nV); }, F.zip(_included, newValues)); console.log("SET-ATTR::".concat(_included, " ---> ").concat(newValues)); return this; // mutating catcher } }), _ref6; }; var attrName = 'childAttr'; var expectedAccessorName = "get___"; // const base = forKey('childAttr'); // expect(base[expectedAccessorName]()).toEqual(); var theGist = 'protected against access from base references'; var theList = ['tag', attrName, 'notThere', 'not-there', 'catchMeIYCe', 'catchMeiyce', 'catch[Mice]', 'catchMeIntoYourContainer', 'catchMeIntoYourContainerEventually']; var shallowCatcher = catcher(theList); var extended = (0, _objectSpread2["default"])({}, shallowCatcher, { tag: 'hi', childAttr: theGist, catchMeIYC: {}, catchMeIntoYourContainer: [] }); var lotsOfUndefind = []; lotsOfUndefind.length = theList.length; expect(shallowCatcher[expectedAccessorName]()).toEqual(lotsOfUndefind); var catcherResults = ['hi', theGist,,,,,, [], undefined]; expect(extended[expectedAccessorName]()).toEqual(catcherResults); shallowCatcher[expectedAccessorName]('Bye'); var stringIteratorResults = ["B", "y", "e", undefined, undefined, undefined, undefined, undefined, undefined]; expect(shallowCatcher[expectedAccessorName]()).toEqual(stringIteratorResults); expect(extended[expectedAccessorName]()).toEqual(catcherResults); expect(extended).toEqual({ "___included": expect.any(Function), "catchMeIYC": {}, "catchMeIntoYourContainer": [], "childAttr": "protected against access from base references", "get___": expect.any(Function), "tag": "hi" }); extended[expectedAccessorName]('Welcome'); expect(shallowCatcher[expectedAccessorName]()).toEqual(stringIteratorResults); expect(extended[expectedAccessorName]()).toEqual(["W", "e", "l", "c", "o", "m", "e", [], undefined]); expect(extended).toEqual({ "___included": expect.any(Function), "catchMeIYC": {}, "catchMeIYCe": "o", "catchMeIntoYourContainer": [], "catchMeiyce": "m", "catch[Mice]": "e", "childAttr": "e", "get___": expect.any(Function), "not-there": "c", "notThere": "l", "tag": "W" }); }); it('should handle dynamic json-atom immutable virtual accessors', function () { // const catcher = function (keys) { // const setInto = self => path => value => set(`$.${path}`)(value)(self); // const getFrom = self => path => get(`$.${path}`)(self); // // return { // ['___included']() { // return keys; // }, // // // [`get___`](newValues) { // if (newValues === undefined) { // const getter = getFrom(this); // const ___included = this.___included(); // console.log(`GET-ATTR::${___included}`); // return ___included.map(k => (console.log('<< K::', k), console.log('<< V::', getter(k)), getter(k))); // } else { // const setter = setInto(this); // const ___included = this.___included(); // // const reducingFn = ({revisionSetter, value}, [k, nV]) => { // const newRevision = revisionSetter(k)(nV); // return {revisionSetter: setInto(newRevision), value: newRevision}; // }; // const initFn = () => ({revisionSetter: setInto(this)}); // const enumerable = F.zip(___included, newValues); // const resultFn = x => x.value; // // // const revisions = F.reduce(reducingFn, initFn, enumerable, resultFn); // // // // const revisions = F.map( // // ([k, nV]) => (console.log('>> K::', k), console.log('>> V::', nV), setter(k)(nV)), // // F.zip(___included, newValues)); // console.log(`SET-ATTR::${___included} ---> ${JSON.stringify(newValues, null, 0)}`); // return revisions; // } // } // }; // }; var attrName = 'childAttr'; var expectedAccessorName = "get___"; // const base = forKey('childAttr'); // expect(base[expectedAccessorName]()).toEqual(); var theGist = 'protected against access from base references'; var theList = ['a1', 'a1.b1', 'a1.b1.c1', 'a1.b1.c1.name', 'a1.b1.c1.id', 'a2', 'a2.b2', 'a2.b2[0]', 'a2.b2[0].value']; var theProvided = [{}, {}, { id: '007' }, 'Bond ... James Bond', 'SLYD', {}, [], { value: 'Gothca' }, { value: 'NO!' }]; expect(theList.length).toEqual(theProvided.length); var accessors = require('./accessors'); var deepCatcher = accessors.catcher(theList); var extended = (0, _objectSpread2["default"])({}, deepCatcher, { a1: { some: 'a1-stale-value', b1: { value: 'b1-stale-value' } }, catchMeIntoYourContainer: [] }); var lotsOfUndefind = []; lotsOfUndefind.length = theList.length; expect(function () { return deepCatcher[expectedAccessorName](); }).toThrow(/Cannot read property 'b1' of undefined/); // Cannot read property 'c1' of undefined var catcherResults = ['hi', theGist,,,,,, [], undefined]; expect(function () { return extended[expectedAccessorName](); }).toThrow(/Cannot read property 'name' of undefined/); var newRevision = deepCatcher[expectedAccessorName](theProvided); expect(newRevision).toEqual({ "___included": expect.any(Function), "a1": { "b1": { "c1": { "id": "SLYD", "name": "Bond ... James Bond" } } }, "a2": { "b2": [{ "value": { "value": "NO!" } }] }, "get___": expect.any(Function) }); expect(function () { return deepCatcher[expectedAccessorName](); }).toThrow(/Cannot read property 'b1' of undefined/); expect(function () { return extended[expectedAccessorName](); }).toThrow(/Cannot read property 'name' of undefined/); expect(extended).toEqual({ "___included": expect.any(Function), "a1": { "b1": { "value": "b1-stale-value" }, "some": "a1-stale-value" }, "catchMeIntoYourContainer": [], "get___": expect.any(Function) }); var extendedNewRevision = (0, _objectSpread2["default"])({}, deepCatcher, newRevision); expect(extendedNewRevision.___included()).toEqual(["a1", "a1.b1", "a1.b1.c1", "a1.b1.c1.name", "a1.b1.c1.id", "a2", "a2.b2", "a2.b2[0]", "a2.b2[0].value"]); expect(extendedNewRevision[expectedAccessorName]()).toEqual([{ "b1": { "c1": { "id": "SLYD", "name": "Bond ... James Bond" } } }, { "c1": { "id": "SLYD", "name": "Bond ... James Bond" } }, { "id": "SLYD", "name": "Bond ... James Bond" }, "Bond ... James Bond", "SLYD", { "b2": [{ "value": { "value": "NO!" } }] }, [{ "value": { "value": "NO!" } }], { "value": { "value": "NO!" } }, { "value": "NO!" }]); }); });