Comparing version 0.0.2 to 1.0.0
162
lib/main.js
@@ -1,5 +0,5 @@ | ||
// Generated by CoffeeScript 2.4.1 | ||
(function() { | ||
'use strict'; | ||
var CND, FONTMIRROR, Fontmirror, LFT, LFT_nofreeze, MAIN, Multimix, assign, badge, copy, debug, echo, help, info, isa, jr, rpr, selector_pattern, type_of, types, urge, validate, warn, whisper; | ||
var CND, Datom, LFT, LFT_nofreeze, MAIN, Multimix, assign, badge, copy, debug, defaults, echo, help, info, isa, jr, p1, p2, rpr, type_of, types, urge, validate, warn, whisper, | ||
indexOf = [].indexOf; | ||
@@ -34,3 +34,3 @@ //########################################################################################################### | ||
({isa, validate, type_of} = types); | ||
({isa, validate, defaults, type_of} = types); | ||
@@ -43,18 +43,8 @@ LFT = require('letsfreezethat'); | ||
this._nofreeze = false; | ||
//----------------------------------------------------------------------------------------------------------- | ||
this.create_nofreeze = function() { | ||
var R; | ||
R = new this.Pipedreams(); | ||
R._nofreeze = true; | ||
return R; | ||
}; | ||
//----------------------------------------------------------------------------------------------------------- | ||
this.freeze = function(d) { | ||
if (this._nofreeze) { | ||
if (this.settings.freeze) { | ||
return LFT.freeze(d); | ||
} else { | ||
return LFT_nofreeze.freeze(d); | ||
} else { | ||
return LFT.freeze(d); | ||
} | ||
@@ -64,6 +54,6 @@ }; | ||
this.thaw = function(d) { | ||
if (this._nofreeze) { | ||
if (this.settings.freeze) { | ||
return LFT.thaw(d); | ||
} else { | ||
return LFT_nofreeze.thaw(d); | ||
} else { | ||
return LFT.thaw(d); | ||
} | ||
@@ -75,3 +65,3 @@ }; | ||
var draft; | ||
if (this._nofreeze) { | ||
if (!this.settings.freeze) { | ||
draft = this._copy(original); | ||
@@ -151,3 +141,3 @@ if (modifier != null) { | ||
/* Return whether datom is a system datom (i.e. whether its `sigil` equals `'~'`). */ | ||
return d.key.match(/^[~\[\]]/); | ||
return d.$key.match(/^[~\[\]]/); | ||
}; | ||
@@ -172,3 +162,3 @@ | ||
//----------------------------------------------------------------------------------------------------------- | ||
this.new_datom = function(key, value, ...other) { | ||
this.new_datom = function($key, $value, ...other) { | ||
var R; | ||
@@ -181,10 +171,11 @@ /* TAINT should validate key */ | ||
datom. */ | ||
validate.datom_key(key); | ||
if (value != null) { | ||
if (!isa.object(value)) { | ||
value = {value}; | ||
validate.datom_key($key); | ||
validate.datom_value($value); | ||
if ($value != null) { | ||
if ((!this.settings.merge_values) || (!isa.object($value))) { | ||
$value = {$value}; | ||
} | ||
R = assign({key}, value, ...other); | ||
R = assign({$key}, $value, ...other); | ||
} else { | ||
R = assign({key}, ...other); | ||
R = assign({$key}, ...other); | ||
} | ||
@@ -198,20 +189,20 @@ while ((isa.object(R.$)) && (isa.object(R.$.$))) { | ||
//----------------------------------------------------------------------------------------------------------- | ||
this.new_single_datom = function(key, value, ...other) { | ||
return this.new_datom(`^${key}`, value, ...other); | ||
this.new_single_datom = function($key, $value, ...other) { | ||
return this.new_datom(`^${$key}`, $value, ...other); | ||
}; | ||
this.new_open_datom = function(key, value, ...other) { | ||
return this.new_datom(`<${key}`, value, ...other); | ||
this.new_open_datom = function($key, $value, ...other) { | ||
return this.new_datom(`<${$key}`, $value, ...other); | ||
}; | ||
this.new_close_datom = function(key, value, ...other) { | ||
return this.new_datom(`>${key}`, value, ...other); | ||
this.new_close_datom = function($key, $value, ...other) { | ||
return this.new_datom(`>${$key}`, $value, ...other); | ||
}; | ||
this.new_system_datom = function(key, value, ...other) { | ||
return this.new_datom(`~${key}`, value, ...other); | ||
this.new_system_datom = function($key, $value, ...other) { | ||
return this.new_datom(`~${$key}`, $value, ...other); | ||
}; | ||
this.new_text_datom = function(value, ...other) { | ||
return this.new_single_datom('text', value, ...other); | ||
this.new_text_datom = function($value, ...other) { | ||
return this.new_single_datom('text', $value, ...other); | ||
}; | ||
@@ -233,31 +224,57 @@ | ||
//----------------------------------------------------------------------------------------------------------- | ||
selector_pattern = /^[<^>\[~\]][^<^>\[~\]]*$/; | ||
p1 = /^(?<skey>(?<sigil>[<^>\[~\]\x23])(?<key>[^<^>\[~\]\x23]*))$/u; // `\x23` used instead of `\#` which causes syntax error (???) | ||
p2 = /^(?<skey>(?<sigil>[<^>\[~\]\x23])(?<key>[^<^>\[~\]\x23]*))\x23(?<attribute>[^<^>\[~\]\x23]+):(?<value>[^<^>\[~\]\x23]+)$/u; // `\x23` used instead of `\#` which causes syntax error (???) | ||
//----------------------------------------------------------------------------------------------------------- | ||
this.select = function(d, selector) { | ||
var ref1, stamped; | ||
var g, k, match, ref1, ref2, ref3, ref4, stamped_values, v; | ||
if (selector == null) { | ||
throw new Error("µ86606 expected a selector, got none"); | ||
} | ||
if (!((isa.object(d)) && (d.key != null))) { | ||
if (!((isa.object(d)) && (d.$key != null))) { | ||
return false; | ||
} | ||
//......................................................................................................... | ||
stamped = false; | ||
if (selector.endsWith('#stamped')) { | ||
stamped = true; | ||
selector = selector.slice(0, selector.length - 8); | ||
if (selector === '') { | ||
throw new Error("µ33982 selector cannot just contain tag '#stamped'"); | ||
if ((match = (ref1 = selector.match(p2)) != null ? ref1 : selector.match(p1)) == null) { | ||
throw new Error(`µ37799 illegal selector ${rpr(selector)}`); | ||
} | ||
g = {}; | ||
ref2 = match.groups; | ||
for (k in ref2) { | ||
v = ref2[k]; | ||
if (v !== '') { | ||
g[k] = v; | ||
} | ||
} | ||
if (!selector_pattern.test(selector)) { | ||
//......................................................................................................... | ||
throw new Error(`µ37783 illegal selector ${rpr(selector)}`); | ||
if ((g.attribute != null) && (g.attribute !== 'stamped')) { | ||
throw new Error(`µ77764 unknown attribute name ${rpr(g.attribute)}`); | ||
} | ||
if ((!stamped) && ((ref1 = d.$stamped) != null ? ref1 : false)) { | ||
switch (g.value) { | ||
case void 0: | ||
stamped_values = [false]; | ||
break; | ||
case '*': | ||
stamped_values = [true, false]; | ||
break; | ||
case 'true': | ||
stamped_values = [true]; | ||
break; | ||
case 'false': | ||
stamped_values = [false]; | ||
break; | ||
default: | ||
throw new Error(`µ33366 illegal attribute or value in selector ${rpr(selector)}`); | ||
} | ||
if (ref3 = (ref4 = d.$stamped) != null ? ref4 : false, indexOf.call(stamped_values, ref3) < 0) { | ||
//......................................................................................................... | ||
return false; | ||
} | ||
return d.key === selector; | ||
if (g.key != null) { | ||
return d.$key === g.skey; | ||
} | ||
if (!d.$key.startsWith(g.sigil)) { | ||
return false; | ||
} | ||
return true; | ||
}; | ||
@@ -270,31 +287,30 @@ | ||
Fontmirror = (function() { | ||
class Fontmirror extends Multimix {}; | ||
Datom = (function() { | ||
class Datom extends Multimix { | ||
// @include ( require './outliner.mixin' ), { overwrite: false, } | ||
// @include ( require './cachewalker.mixin' ), { overwrite: false, } | ||
// @include ( require './_temp_svgttf' ), { overwrite: false, } ### !!!!!!!!!!!!!!!!!!!!!!!!!!! ### | ||
// @extend MAIN, { overwrite: false, } | ||
Fontmirror.include(MAIN, { | ||
//--------------------------------------------------------------------------------------------------------- | ||
constructor(settings = null) { | ||
super(); | ||
validate.datom_settings(settings = {...defaults.settings, ...settings}); | ||
this.settings = LFT.freeze(settings); | ||
this.Datom = Datom; | ||
return this; | ||
} | ||
}; | ||
Datom.include(MAIN, { | ||
overwrite: false | ||
}); | ||
return Fontmirror; | ||
return Datom; | ||
}).call(this); | ||
// @include ( require './outliner.mixin' ), { overwrite: false, } | ||
// @include ( require './cachewalker.mixin' ), { overwrite: false, } | ||
// @include ( require './_temp_svgttf' ), { overwrite: false, } ### !!!!!!!!!!!!!!!!!!!!!!!!!!! ### | ||
// @extend MAIN, { overwrite: false, } | ||
module.exports = new Datom(); | ||
// #--------------------------------------------------------------------------------------------------------- | ||
// constructor: ( target = null ) -> | ||
// super() | ||
// @CLI = require './cli' | ||
// @CFG = require './cfg' | ||
// @TAGS = require './tags' | ||
// @NICKS = require './texfontnamesake' | ||
// @LINKS = require './links' | ||
// @export target if target? | ||
module.exports = FONTMIRROR = new Fontmirror(); | ||
}).call(this); | ||
//# sourceMappingURL=main.js.map |
@@ -1,5 +0,4 @@ | ||
// Generated by CoffeeScript 2.4.1 | ||
(function() { | ||
'use strict'; | ||
var CND, DATOM, badge, debug, echo, help, info, isa, jr, rpr, test, type_of, types, urge, validate, warn, whisper; | ||
var CND, badge, debug, echo, help, info, isa, jr, rpr, test, type_of, types, urge, validate, warn, whisper; | ||
@@ -37,5 +36,2 @@ //########################################################################################################### | ||
//........................................................................................................... | ||
DATOM = require('../..'); | ||
// #----------------------------------------------------------------------------------------------------------- | ||
@@ -117,3 +113,6 @@ // @[ "selector keypatterns" ] = ( T, done ) -> | ||
this["select 2"] = async function(T, done) { | ||
var error, i, len, matcher, probe, probes_and_matchers; | ||
var DATOM, error, i, len, matcher, new_datom, probe, probes_and_matchers, select; | ||
DATOM = require('../..'); | ||
({new_datom, select} = DATOM.export()); | ||
//......................................................................................................... | ||
probes_and_matchers = [ | ||
@@ -123,9 +122,9 @@ [ | ||
{ | ||
key: '^number', | ||
value: 42, | ||
$key: '^number', | ||
$value: 42, | ||
$stamped: true | ||
}, | ||
'^number' | ||
'^number#stamped:*' | ||
], | ||
false | ||
true | ||
], | ||
@@ -135,8 +134,8 @@ [ | ||
{ | ||
key: '<italic', | ||
$key: '<italic', | ||
$stamped: true | ||
}, | ||
'<italic' | ||
'<italic#stamped:*' | ||
], | ||
false | ||
true | ||
], | ||
@@ -146,8 +145,8 @@ [ | ||
{ | ||
key: '<italic', | ||
$key: '<italic', | ||
$stamped: true | ||
}, | ||
'>italic' | ||
'<italic#stamped:*' | ||
], | ||
false | ||
true | ||
], | ||
@@ -157,6 +156,5 @@ [ | ||
{ | ||
key: '^number', | ||
value: 42 | ||
$key: '<italic' | ||
}, | ||
'^number' | ||
'<italic#stamped:*' | ||
], | ||
@@ -168,9 +166,8 @@ true | ||
{ | ||
key: '^number', | ||
value: 42, | ||
$key: '<italic', | ||
$stamped: true | ||
}, | ||
'^number#stamped' | ||
'>italic#stamped:*' | ||
], | ||
true | ||
false | ||
], | ||
@@ -180,8 +177,7 @@ [ | ||
{ | ||
key: '<italic', | ||
$stamped: true | ||
$key: '<italic' | ||
}, | ||
'<italic#stamped' | ||
'>italic#stamped:*' | ||
], | ||
true | ||
false | ||
], | ||
@@ -191,6 +187,7 @@ [ | ||
{ | ||
key: '<italic', | ||
$key: '^number', | ||
$value: 42, | ||
$stamped: true | ||
}, | ||
'>italic#stamped' | ||
'^number' | ||
], | ||
@@ -202,8 +199,8 @@ false | ||
{ | ||
key: '<italic', | ||
$key: '<italic', | ||
$stamped: true | ||
}, | ||
'<italic#stamped' | ||
'<italic' | ||
], | ||
true | ||
false | ||
], | ||
@@ -213,7 +210,8 @@ [ | ||
{ | ||
key: '<italic' | ||
$key: '<italic', | ||
$stamped: true | ||
}, | ||
'<italic#stamped' | ||
'>italic' | ||
], | ||
true | ||
false | ||
], | ||
@@ -223,7 +221,8 @@ [ | ||
{ | ||
key: '<italic' | ||
$key: '^number', | ||
$value: 42 | ||
}, | ||
'>italic#stamped' | ||
'^number' | ||
], | ||
false | ||
true | ||
], | ||
@@ -233,3 +232,3 @@ [ | ||
{ | ||
key: '<italic', | ||
$key: '<italic', | ||
$stamped: true | ||
@@ -244,3 +243,3 @@ }, | ||
{ | ||
key: "*data" | ||
$key: "*data" | ||
}, | ||
@@ -255,3 +254,3 @@ '*data' | ||
{ | ||
key: "data>" | ||
$key: "data>" | ||
}, | ||
@@ -266,3 +265,3 @@ 'data>' | ||
{ | ||
key: "%data" | ||
$key: "%data" | ||
}, | ||
@@ -277,3 +276,3 @@ '%data' | ||
{ | ||
key: "[data" | ||
$key: "[data" | ||
}, | ||
@@ -288,3 +287,3 @@ '[data' | ||
{ | ||
key: "data]" | ||
$key: "data]" | ||
}, | ||
@@ -299,3 +298,3 @@ 'data]' | ||
{ | ||
key: "]data" | ||
$key: "]data" | ||
}, | ||
@@ -314,3 +313,3 @@ ']data' | ||
[d, selector] = probe; | ||
return DATOM.select(d, selector); | ||
return select(d, selector); | ||
}); | ||
@@ -324,3 +323,6 @@ } | ||
this["select ignores values other than PODs"] = async function(T, done) { | ||
var error, i, len, matcher, probe, probes_and_matchers; | ||
var DATOM, error, i, len, matcher, new_datom, probe, probes_and_matchers, select; | ||
DATOM = require('../..'); | ||
({new_datom, select} = DATOM.export()); | ||
//......................................................................................................... | ||
probes_and_matchers = [[[null, '^number'], false], [[123, '^number'], false]]; | ||
@@ -335,3 +337,3 @@ //......................................................................................................... | ||
try { | ||
resolve(DATOM.select(d, selector)); | ||
resolve(select(d, selector)); | ||
} catch (error1) { | ||
@@ -350,2 +352,407 @@ error = error1; | ||
//----------------------------------------------------------------------------------------------------------- | ||
this["new_datom complains when value has `$key`"] = async function(T, done) { | ||
var DATOM, error, i, len, matcher, new_datom, probe, probes_and_matchers, select; | ||
DATOM = require('../..'); | ||
({new_datom, select} = DATOM.export()); | ||
//......................................................................................................... | ||
probes_and_matchers = [ | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"$value": 123 | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"$value": 123 | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"$value": 123, | ||
"$key": "something" | ||
} | ||
], | ||
null, | ||
"not a valid datom_value" | ||
] | ||
]; | ||
//......................................................................................................... | ||
for (i = 0, len = probes_and_matchers.length; i < len; i++) { | ||
[probe, matcher, error] = probes_and_matchers[i]; | ||
await T.perform(probe, matcher, error, function() { | ||
return new Promise(function(resolve, reject) { | ||
var key, value; | ||
[key, value] = probe; | ||
return resolve(new_datom(key, value)); | ||
}); | ||
}); | ||
} | ||
done(); | ||
return null; | ||
}; | ||
//----------------------------------------------------------------------------------------------------------- | ||
this["new_datom (default settings)"] = async function(T, done) { | ||
var DATOM, error, i, len, matcher, new_datom, probe, probes_and_matchers, select; | ||
DATOM = require('../..'); | ||
({new_datom, select} = DATOM.export()); | ||
//......................................................................................................... | ||
probes_and_matchers = [ | ||
[ | ||
["^number", | ||
null], | ||
{ | ||
"$key": "^number" | ||
}, | ||
null | ||
], | ||
[ | ||
["^number", | ||
123], | ||
{ | ||
"$key": "^number", | ||
"$value": 123 | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"$value": 123 | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"$value": 123 | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"value": 123 | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"value": 123 | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"$value": { | ||
"$value": 123 | ||
} | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"$value": { | ||
"$value": 123 | ||
} | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"value": { | ||
"$value": 123 | ||
} | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"value": { | ||
"$value": 123 | ||
} | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"$value": { | ||
"value": 123 | ||
} | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"$value": { | ||
"value": 123 | ||
} | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"value": { | ||
"value": 123 | ||
} | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"value": { | ||
"value": 123 | ||
} | ||
}, | ||
null | ||
], | ||
[ | ||
["^value", | ||
123], | ||
{ | ||
"$key": "^value", | ||
"$value": 123 | ||
}, | ||
null | ||
], | ||
[ | ||
["<start", | ||
123], | ||
{ | ||
"$key": "<start", | ||
"$value": 123 | ||
}, | ||
null | ||
], | ||
[ | ||
[">stop", | ||
123], | ||
{ | ||
"$key": ">stop", | ||
"$value": 123 | ||
}, | ||
null | ||
] | ||
]; | ||
//......................................................................................................... | ||
for (i = 0, len = probes_and_matchers.length; i < len; i++) { | ||
[probe, matcher, error] = probes_and_matchers[i]; | ||
await T.perform(probe, matcher, error, function() { | ||
return new Promise(function(resolve, reject) { | ||
var key, value; | ||
[key, value] = probe; | ||
return resolve(new_datom(key, value)); | ||
}); | ||
}); | ||
} | ||
done(); | ||
return null; | ||
}; | ||
//----------------------------------------------------------------------------------------------------------- | ||
this["new_datom (without value merging)"] = async function(T, done) { | ||
var DATOM, error, i, len, matcher, new_datom, probe, probes_and_matchers, select; | ||
DATOM = new (require('../..')).Datom({ | ||
merge_values: false | ||
}); | ||
({new_datom, select} = DATOM.export()); | ||
//......................................................................................................... | ||
probes_and_matchers = [ | ||
[ | ||
["^number", | ||
null], | ||
{ | ||
"$key": "^number" | ||
}, | ||
null | ||
], | ||
[ | ||
["^number", | ||
123], | ||
{ | ||
"$key": "^number", | ||
"$value": 123 | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"$value": 123 | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"$value": { | ||
"$value": 123 | ||
} | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"value": 123 | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"$value": { | ||
"value": 123 | ||
} | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"$value": { | ||
"$value": 123 | ||
} | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"$value": { | ||
"$value": { | ||
"$value": 123 | ||
} | ||
} | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"value": { | ||
"$value": 123 | ||
} | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"$value": { | ||
"value": { | ||
"$value": 123 | ||
} | ||
} | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"$value": { | ||
"value": 123 | ||
} | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"$value": { | ||
"$value": { | ||
"value": 123 | ||
} | ||
} | ||
}, | ||
null | ||
], | ||
[ | ||
[ | ||
"^number", | ||
{ | ||
"value": { | ||
"value": 123 | ||
} | ||
} | ||
], | ||
{ | ||
"$key": "^number", | ||
"$value": { | ||
"value": { | ||
"value": 123 | ||
} | ||
} | ||
}, | ||
null | ||
], | ||
[ | ||
["^value", | ||
123], | ||
{ | ||
"$key": "^value", | ||
"$value": 123 | ||
}, | ||
null | ||
], | ||
[ | ||
["<start", | ||
123], | ||
{ | ||
"$key": "<start", | ||
"$value": 123 | ||
}, | ||
null | ||
], | ||
[ | ||
[">stop", | ||
123], | ||
{ | ||
"$key": ">stop", | ||
"$value": 123 | ||
}, | ||
null | ||
] | ||
]; | ||
//......................................................................................................... | ||
for (i = 0, len = probes_and_matchers.length; i < len; i++) { | ||
[probe, matcher, error] = probes_and_matchers[i]; | ||
await T.perform(probe, matcher, error, function() { | ||
return new Promise(function(resolve, reject) { | ||
var key, value; | ||
[key, value] = probe; | ||
return resolve(new_datom(key, value)); | ||
}); | ||
}); | ||
} | ||
done(); | ||
return null; | ||
}; | ||
//----------------------------------------------------------------------------------------------------------- | ||
this["freezing"] = function(T, done) { | ||
var DATOM_FREEZE, DATOM_NOFREEZE, new_datom_freeze, new_datom_nofreeze; | ||
DATOM_FREEZE = new (require('../..')).Datom({ | ||
freeze: true | ||
}); | ||
({ | ||
new_datom: new_datom_freeze | ||
} = DATOM_FREEZE.export()); | ||
DATOM_NOFREEZE = new (require('../..')).Datom({ | ||
freeze: false | ||
}); | ||
({ | ||
new_datom: new_datom_nofreeze | ||
} = DATOM_NOFREEZE.export()); | ||
//......................................................................................................... | ||
T.ok(Object.isFrozen(new_datom_freeze('^mykey'))); | ||
T.ok(!Object.isFrozen(new_datom_nofreeze('^mykey'))); | ||
done(); | ||
return null; | ||
}; | ||
//----------------------------------------------------------------------------------------------------------- | ||
this["_regex performance, runaway test"] = function(T, done) {}; | ||
@@ -358,11 +765,14 @@ | ||
//########################################################################################################### | ||
if (module.parent == null) { | ||
test(this); | ||
if (require.main === module) { | ||
(() => { | ||
return test(this); | ||
})(); | ||
} | ||
// test @[ "selector keypatterns" ] | ||
// test @[ "new_datom complains when value has `$key`" ] | ||
// test @[ "selector keypatterns" ] | ||
// test @[ "select 2" ] | ||
// test @[ "new_datom (default settings)" ] | ||
// debug new_datom '^helo', 42 | ||
}).call(this); | ||
//# sourceMappingURL=select.test.js.map |
@@ -1,2 +0,1 @@ | ||
// Generated by CoffeeScript 2.4.1 | ||
(function() { | ||
@@ -35,2 +34,17 @@ 'use strict'; | ||
//----------------------------------------------------------------------------------------------------------- | ||
this.declare('datom_settings', { | ||
tests: { | ||
"x is a object": function(x) { | ||
return this.isa.object(x); | ||
}, | ||
"x.merge_values is a ?boolean": function(x) { | ||
return (x.merge_values == null) || this.isa.boolean(x.merge_values); | ||
}, | ||
"x.freeze is a ?boolean": function(x) { | ||
return (x.freeze == null) || this.isa.boolean(x.freeze); | ||
} | ||
} | ||
}); | ||
//----------------------------------------------------------------------------------------------------------- | ||
this.declare('datom_nonempty_list_of_positive_integers', function(x) { | ||
@@ -64,3 +78,3 @@ if (!this.isa.nonempty_list(x)) { | ||
"x has sigil": function(x) { | ||
return this.isa.pd_datom_sigil(x[0]); | ||
return this.isa.datom_sigil(x[0]); | ||
} | ||
@@ -79,4 +93,4 @@ } | ||
}, | ||
"x.key is a pd_datom_key": function(x) { | ||
return this.isa.pd_datom_key(x.key); | ||
"x.key is a datom_key": function(x) { | ||
return this.isa.datom_key(x.key); | ||
}, | ||
@@ -94,3 +108,3 @@ "x.$stamped is an optional boolean": function(x) { | ||
"x.$vnr is an optional nonempty list of positive integers": function(x) { | ||
return (x.$vnr == null) || this.isa.pd_nonempty_list_of_positive_integers(x.$vnr); | ||
return (x.$vnr == null) || this.isa.datom_nonempty_list_of_positive_integers(x.$vnr); | ||
} | ||
@@ -100,17 +114,18 @@ } | ||
// "?..$vnr is a ?positive": ( x ) -> ( not x.$vnr? ) or @isa.positive x.$vnr | ||
// "? has key 'vlnr_txt'": ( x ) -> @has_key x, 'vlnr_txt' | ||
// "? has key 'value'": ( x ) -> @has_key x, 'value' | ||
// "?.vlnr_txt is a nonempty text": ( x ) -> @isa.nonempty_text x.vlnr_txt | ||
// "?.vlnr_txt starts, ends with '[]'": ( x ) -> ( x.vlnr_txt.match /^\[.*\]$/ )? | ||
// "?.vlnr_txt is a JSON array of integers": ( x ) -> | ||
// # debug 'µ55589', x | ||
// ( @isa.list ( lst = JSON.parse x.vlnr_txt ) ) and \ | ||
// ( lst.every ( xx ) => ( @isa.integer xx ) and ( @isa.positive xx ) ) | ||
//----------------------------------------------------------------------------------------------------------- | ||
this.declare('datom_value', function(x) { | ||
if (!this.isa.object(x)) { | ||
return true; | ||
} | ||
return x.$key === void 0; | ||
}); | ||
// #----------------------------------------------------------------------------------------------------------- | ||
// @declare 'true', ( x ) -> x is true | ||
//----------------------------------------------------------------------------------------------------------- | ||
this.defaults = { | ||
settings: { | ||
merge_values: true, | ||
freeze: true | ||
} | ||
}; | ||
}).call(this); | ||
//# sourceMappingURL=types.js.map |
{ | ||
"name": "datom", | ||
"version": "0.0.2", | ||
"version": "1.0.0", | ||
"description": "standardized immutable objects in the spirit of datomic, especially suited for use in data pipelines", | ||
@@ -5,0 +5,0 @@ "main": "lib/main.js", |
310
README.md
# Datom ⚛ | ||
# Datom | ||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* | ||
- [Export Bound Methods](#export-bound-methods) | ||
- [Creation of Bespoke Library Instances](#creation-of-bespoke-library-instances) | ||
- [Configuration Parameters](#configuration-parameters) | ||
- [Methods](#methods) | ||
- [Freezing & Thawing](#freezing--thawing) | ||
- [Stamping](#stamping) | ||
- [Type Testing](#type-testing) | ||
- [Value Creation](#value-creation) | ||
- [Selecting](#selecting) | ||
- [System Properties](#system-properties) | ||
- [WIP](#wip) | ||
- [PipeDreams Datoms (Data Events)](#pipedreams-datoms-data-events) | ||
- [`select = ( d, selector ) ->`](#select---d-selector---) | ||
- [To Do](#to-do) | ||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
standardized immutable objects in the spirit of datomic, especially suited for use in data pipelines | ||
**NOTE: Documentation is still fragmentary. WIP.** | ||
# Export Bound Methods | ||
If you plan on using methods like `new_datom()` or `select()` a lot, consider using `.export()`: | ||
```coffee | ||
DATOM = require 'datom' | ||
{ new_datom | ||
select } = DATOM.export() | ||
``` | ||
Now `new_datom()` and `select()` are methods bound to `DATOM`. (Observe that because of the JavaScript | ||
'tear-off' effect, when you do `method = DATOM.method`, then `method()` will likely fail as its reference to | ||
`this` has been lost.) | ||
# Creation of Bespoke Library Instances | ||
In order to configure a copy of the library, pass in a settings object: | ||
```coffee | ||
_DATOM = require 'datom' | ||
settings = { merge_values: false, } | ||
DATOM = new _DATOM.Datom settings | ||
{ new_datom | ||
select } = DATOM.export() | ||
``` | ||
Or, mode idiomatically: | ||
```coffee | ||
DATOM = new ( require 'datom' ).Datom { merge_values: false, } | ||
{ new_datom | ||
select } = DATOM.export() | ||
``` | ||
The second form also helps to avoid accidental usage of the result of `require 'datom'`, which is of | ||
course the same library with a different configuration. | ||
# Configuration Parameters | ||
* **`merge_values`** (boolean, default: `true`)—Whether to merge attributes of the second argument to | ||
`new_datom()` into the resulting value. When set to `false`, `new_datom '^somekey', somevalue` will always | ||
result in a datom `{ $key: '^somekey', $value: somevalue, }`; when left to the default, and if `somevalue` | ||
is an object, then its attributes will become attributes of the datom, which may result in name clashes in | ||
case any attribute name should start with a `$` (dollar sign). | ||
* **`freeze`** (boolean, default: `true`)—Whether to freeze datoms. When set to `false`, no freezing will | ||
be performed, which may entail slightly improved performance. | ||
# Methods | ||
## Freezing & Thawing | ||
* **`@freeze = ( d ) ->`** | ||
* **`@thaw = ( d ) ->`** | ||
* **`@lets = ( original, modifier ) ->`** | ||
* **`@set = ( d, k, P... ) ->`** | ||
* **`@unset = ( d, k ) ->`** | ||
## Stamping | ||
* **`@stamp = ( d, P... ) ->`** | ||
* **`@unstamp = ( d ) ->`** | ||
## Type Testing | ||
* **`@is_system = ( d ) ->`** | ||
* **`@is_stamped = ( d ) ->`** | ||
* **`@is_fresh = ( d ) ->`** | ||
* **`@is_dirty = ( d ) ->`** | ||
## Value Creation | ||
* **`@new_datom = ( $key, $value, other... ) ->`** | ||
* **`@new_single_datom = ( $key, $value, other... ) ->`** | ||
* **`@new_open_datom = ( $key, $value, other... ) ->`** | ||
* **`@new_close_datom = ( $key, $value, other... ) ->`** | ||
* **`@new_system_datom = ( $key, $value, other... ) ->`** | ||
* **`@new_text_datom = ( $value, other... ) ->`** | ||
* **`@new_end_datom = ->`** | ||
* **`@new_warning = ( ref, message, d, other... ) ->`** | ||
## Selecting | ||
* **`@select = ( d, selector ) ->`** | ||
# System Properties | ||
* **`d.$key`**—key (i.e., type) of a datom. | ||
* **`d.$value`**—'the' proper value of a datom. This is always used in case `new_datom()` was called with a | ||
non-object in the value slot (as in `new_datom '^mykey', 123`), or when the library was configured with `{ | ||
merge_values: false, }`.—In case there is no `d.$value`, the datom's proper value is the object that would | ||
result from deleting all properties whose names start with a `$` (dollar sign). | ||
* **`d.$dirty`**—whether the object has been (thawed, then) changed (and then frozen again) since its | ||
`$dirty` property was last cleared or set to `false`. | ||
* **`d.$stamped`**—whether the object has been marked as 'stamped' (i.e., processed). | ||
------------------------------------------------------------------------------- | ||
# WIP | ||
**The below copied from PipeDreams docs, to be updated** | ||
## PipeDreams Datoms (Data Events) | ||
Data streams—of which [pull-streams](https://pull-stream.github.io/), | ||
[PipeStreams](https://github.com/loveencounterflow/pipestreams), and [NodeJS | ||
Streams](https://nodejs.org/api/stream.html) are examples—do their work by | ||
sending pieces of data (that originate from a data source) through a number of | ||
transforms (to finally end up in a data sink).<sup>*note*</sup> | ||
> (*note*) I will ignore here alternative ways of dealing with streams, especially | ||
> the [`EventEmitter` way of dealing with streamed | ||
> data](https://nodejs.org/api/stream.html#stream_api_for_stream_consumers). | ||
> When I say 'streams', I also implicitly mean 'pipelines'; when I say | ||
> 'pipelines', I also implicitly mean 'pipelines to stream data' and 'streams' | ||
> in general. | ||
When NodeJS streams started out, the thinking about those streams was pretty | ||
much confined to saying that ['a stream is a series of | ||
bytes'](http://dominictarr.com/post/145135293917/history-of-streams). Already back then, | ||
an alternative view took hold (I'm slightly paraphrasing here): | ||
> The core interpretation was that stream could be buffers or strings - but the | ||
> userland interpretation was that a stream could be anything that is | ||
> serializeable [...] it was a sequence of buffers, bytes, strings or objects. | ||
> Why not use the same api? | ||
I will no repeat here [what I've written about perceived shortcomings of NodeJS | ||
streams](https://github.com/loveencounterflow/pipestreams/blob/master/pipestreams-manual/chapter-00-comparison.md); | ||
instead, let me iterate a few observations: | ||
* In streaming, data is just data. There's no need for having [a separate | ||
'Object Mode'](https://nodejs.org/api/stream.html#stream_object_mode) or | ||
somesuch. | ||
* There's a single exception to the above rule, and that is when the data item | ||
being sent down the line is `null`. This has historically—by both NodeJS | ||
streams and pull-streams—been interpreted as a termination signal, and I'm not | ||
going to change that (although at some point I might as well). | ||
* When starting out with streams and building fairly simple-minded pipelines, | ||
sending down either raw pieces of business data or else `null` to indicate | ||
termination is enough to satisfy most needs. However, when one transitions to | ||
more complex environments, raw data is not sufficient any more: When | ||
processing text from one format to another, how could a downstream transform | ||
tell whether a given piece of text is raw data or the output of an upstream | ||
transform? | ||
Another case where raw data becomes insufficient are circular | ||
pipelines—pipelines that re-compute (some or all) output values in a recursive | ||
manner. An example which outputs the integer sequences of the [Collatz | ||
Conjecture](https://en.wikipedia.org/wiki/Collatz_conjecture) is [in the tests | ||
folder](https://github.com/loveencounterflow/pipedreams/blob/master/src/tests/circular-pipelines.test.coffee#L36). | ||
There, whenever we see an even number `n`, we send down that even number `n` | ||
alongside with half its value, `n/2`; whenever we see an odd number `n`, we | ||
send it on, followed by its value tripled plus one, `3*n+1`. No matter whether | ||
you put the transform for even numbers in front of that for odd numbers or the | ||
other way round, there will be numbers that come out at the bottom that need | ||
to be re-input into the top of the pipeline, and since there's no telling in | ||
advance how long a Collatz sequence will be for a given integer, it is, in the | ||
general case, insufficient to build a pipeline made from a (necessarily | ||
finite) repetitive sequence of copies of those individual transforms. Thus, | ||
classical streams cannot easily model this kind of processing. | ||
The idea of **datoms**—short for *data atoms*, a term borrowed from [Rich | ||
Hickey's Datomic](https://www.infoq.com/articles/Datomic-Information-Model)—is | ||
to simply to wrap each piece of raw data in a higher-level structure. This is of | ||
course an old idea, but not one that is very prevalent in NodeJS streams, the | ||
fundamental assumption (of classical stream processing) being that all stream | ||
transforms get to process each piece of data, and that all pieces of data are of | ||
equal status (with the exception of `null`). | ||
The PipeDreams sample implementation of Collatz Sequences uses datoms to (1) | ||
wrap the numerical pieces of data, which allows to mark data as processed | ||
(a.k.a. 'stamped'), to (2) mark data as 'to be recycled', and to (3) inject | ||
system-level `sync`hronization signals into the data stream to make sure that | ||
recycled data gets processed before new data is allowed into the stream. | ||
In PipeDreams datoms, **each piece of data is explicitly labelled for its | ||
type**; **each datom may have a different status**: there are **system-level | ||
datoms that serve to orchestrate the flow of data within the pipeline**; there | ||
are **user-level datoms which originate from the application**; there are | ||
**datoms to indicate the opening and closing of regions (phases) in the data | ||
stream**; there are **stream transforms that listen to and act on specific | ||
system-level events**. | ||
Datoms are JS objects that must minimally have a `key` property, a string that | ||
specifies the datom's category, namespace and name; in addition, they may have a | ||
`value` property with the payload (where desired), and any number of other | ||
attributes. The property `$` is used to carry metadata (e.g. from which line in | ||
a source file a given datom was generated from). Thus, we may give the outline | ||
of a datom as (in a rather informal notation) `d := { key, ?value, ?stamped,..., | ||
?$, }`. | ||
The `key` of a datom must be a string that consists of at least two parts, the | ||
`sigil` and the `name`. The `sigil`, a single punctuation character, indicates | ||
the 'category' of each datom; there are two levels and three elementary | ||
categories, giving six types of datoms: | ||
* Application level: | ||
* `^` for **data datoms** (a.k.a. 'singletons'), | ||
* `<` for **start-of-region datoms**, | ||
* `>` for **end-of-region datoms**. | ||
* System level: | ||
* `~` for **data datoms**, | ||
* `[` for **start-of-region datoms**, | ||
* `]` for **end-of-region datoms**. | ||
<!-- System-level events, in particular those without further payload data, are also | ||
called 'signals'; thus, `~collect` is a 'collect signal', and `[data` is a | ||
'start-of-data signal'. Aggregate transforms such as `$collect()`, `$sort()` and | ||
so on listen to the signals of the same name, `~collect` and `~sort`: In the | ||
case of `$collect()`, a collect signal will trigger the sending of the | ||
collection as it looks at that point in time; likewise, `$sort()` will react to | ||
a sort signal by sending all buffered events in the configured ordering. | ||
--> | ||
Normally, one will probably want to send around business data inside (the | ||
`value` property of) application-level data datoms (hence their name, also | ||
shortened to D-datoms); however, one can also set other properties of datom | ||
objects, or send data around using properties of start- or end-of-region datoms. | ||
Region events are intended to be used e.g. when parsing text with markup; say | ||
you want to turn a snippet of HTML like this: | ||
``` | ||
<document><div>Helo <em>world!</em></div></document> | ||
``` | ||
into another textual representation, you may want to turn that into a sequence | ||
of datoms similar to these, in the order of sending and regions symbolized by | ||
boxes:<sup>*note*</sup> | ||
``` | ||
--------------------------------------------------------+ | ||
{ key: '<document', } # d1 | | ||
------------------------------------------------------+ | | ||
{ key: '<div', } # d2 | | | ||
{ key: '^text', value: "Helo ", } # d3 | | | ||
----------------------------------------------------+ | | | ||
{ key: '<em', } # d4 | | | | ||
{ key: '^text' value: "world!", } # d5 | | | | ||
{ key: '>em', } # d6 | | | | ||
----------------------------------------------------+ | | | ||
{ key: '>div', } # d7 | | | ||
------------------------------------------------------+ | | ||
{ key: '>document', } # d8 | | ||
--------------------------------------------------------+ | ||
``` | ||
> *note* by 'in the order of sending' I mean you'd have to send datom `d1` | ||
> first, then `d2` and so on. Trivial until you imagine you write a pipeline and | ||
> then picture how the events will travel down that pipeline: | ||
> | ||
> `pipeline.push $do_this() # s1, might be processing d3 right now`<br> | ||
> `pipeline.push $do_that() # s2, might be processing d2 right now`<br> | ||
> `pipeline.push $do_something_else() # s3, might be processing d1 right now`<br> | ||
> | ||
> Although there's really no telling whether step `s3` will really process datom | ||
> `d1` at the 'same point in time' that step `s2` processes datom `d2` and so on | ||
> (in the strict sense, this is hardly possible in a single-threaded language | ||
> anyway), the visualization still holds a grain of truth: stream transforms | ||
> that come 'later' (further down) in the pipeline will see events near the top | ||
> of your to-do list first, and vice versa. This can be mildly confusing. | ||
## `select = ( d, selector ) ->` | ||
The `select` method can be used to determine whether a given event `d` matches a | ||
set of conditions; typically, one will want to use `select d, selector` to decide | ||
whether a given event is suitable for processing by the stream transform at | ||
hand, or whether it should be passed on unchanged. | ||
The current implementation of `select()` is much dumber and faster than its predecessors; where previously, | ||
it was possible to match datoms with multiple selectors that contained multiple sigils and so forth, the new | ||
version does little more than check wheter the single selector allowed equals the given datom's `key` | ||
value—that's about it, except that one can still `select d, '^somekey#stamped'` to match both unstamped and | ||
stamped datoms. | ||
------------------------------------------------------------------------ | ||
# To Do | ||
* [ ] implement piecemeal structural validation such that on repeated calls to a validator instance's | ||
`validate()` method an error will be thrown as soon as unbalanced regions (delimeted by `{ $key: '<token', | ||
..., }` and `{ $key: '>token', ..., }`) are encountered. | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1139
0
322
79389