reworking store API to be sync/async...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2023-02-14 16:50:35 +03:00
parent 8acb914237
commit d4a1e3f055
2 changed files with 310 additions and 58 deletions

View File

@ -541,6 +541,40 @@ object.Constructor('BasePage', {
}) }, }) },
// XXX should this be an iterator??? // XXX should this be an iterator???
// XXX EXPERIMENTAL...
// to be sync this needs:
// .energetic
// .store.isEnergetic(..)
// .resolve(..) -> .store.resolve(..)
each: function(path){
var that = this
// NOTE: we are trying to avoid resolving non-pattern paths unless
// we really have to...
path = path ?
pwpath.relative(this.path, path)
: this.location
var paths = path.includes('*') ?
Promise.awaitOrRun(
this.energetic,
this.store.isEnergetic(path),
function(a, b){
return !(a || b) ?
that.resolve(path)
: path })
: path
paths = Promise.awaitOrRun(
paths,
function(paths){
return (paths instanceof Array
|| paths instanceof Promise) ?
paths
: [paths] })
return Promise.iter(
paths,
function(path){
return that.get('/'+ path) })
.sync() },
/*/ // XXX ASYNC...
each: async function*(path){ each: async function*(path){
// NOTE: we are trying to avoid resolving non-pattern paths unless // NOTE: we are trying to avoid resolving non-pattern paths unless
// we really have to... // we really have to...
@ -561,14 +595,15 @@ object.Constructor('BasePage', {
: [paths] : [paths]
for(var path of paths){ for(var path of paths){
yield this.get('/'+ path) } }, yield this.get('/'+ path) } },
//*/
[Symbol.asyncIterator]: async function*(){ [Symbol.asyncIterator]: async function*(){
yield* this.each() }, yield* this.each() },
map: async function(func){ map: function(func){
return this.each().map(func) }, return this.each().map(func) },
filter: async function(func){ filter: function(func){
return this.each().filter(func) }, return this.each().filter(func) },
reduce: async function(func, dfl){ reduce: function(func, dfl){
return this.each().reduce(func, dfl) }, return this.each().reduce(func, dfl) },
// sorting... // sorting...
@ -1842,6 +1877,52 @@ object.Constructor('Page', BasePage, {
// actions... // actions...
// //
// XXX revise name... // XXX revise name...
/*/ XXX EXPERIMENTAL
asPages: function(path='.:$ARGS', strict=false){
// options...
var args = [...arguments]
var opts = typeof(args.at(-1)) == 'object' ?
args.pop()
: {}
var {path, strict} = {
...opts,
path: typeof(args[0]) == 'string' ?
args.shift()
: '.:$ARGS',
strict: args.shift()
?? false,
}
var page = this.get(path, strict)
// each...
if(page.isPattern){
return page.each()
// handle lists in pages (actions, ... etc.)...
} else {
return Promise.awaitOrRun(
page.data,
function(data){
data =
data instanceof types.Generator ?
// XXX
//await data()
data()
: typeof(data) == 'function' ?
data
: data && 'text' in data ?
data.text
: null
if(data instanceof Array
|| data instanceof types.Generator){
return data
.map(function(p){
return page.virtual({text: p}) }) }
// do not iterate pages/actions that are undefined...
if(data == null){
return }
return page }) } },
/*/ // XXX ASYNC...
asPages: async function*(path='.:$ARGS', strict=false){ asPages: async function*(path='.:$ARGS', strict=false){
// options... // options...
var args = [...arguments] var args = [...arguments]
@ -1883,6 +1964,7 @@ object.Constructor('Page', BasePage, {
return } return }
yield page } }, yield page } },
//*/
// expanded page text... // expanded page text...
// //

View File

@ -886,6 +886,27 @@ module.BaseStore = {
// //
// NOTE: handlers are run in order of definition. // NOTE: handlers are run in order of definition.
// //
// XXX EXPERIMENTAL
tags: function(tags){
var that = this
tags = typeof(tags) == 'string' ?
this.parseTags(tags)
: false
return Promise.awaitOrRun(
tags
&& this.tags,
function(tags){
return tags
&& function(path){
// tags -> skip untagged pages...
var t = this.tags.paths[path]
if(!t){
return false }
for(var tag of tags){
if(!t || !t.has(tag)){
return false } }
return true } }) },
/*/ // XXX ASYNC...
tags: async function(tags){ tags: async function(tags){
tags = typeof(tags) == 'string' ? tags = typeof(tags) == 'string' ?
this.parseTags(tags) this.parseTags(tags)
@ -901,6 +922,7 @@ module.BaseStore = {
if(!t || !t.has(tag)){ if(!t || !t.has(tag)){
return false } } return false } }
return true } }, return true } },
//*/
search: async function(search){ search: async function(search){
search = search search = search
&& new Set(await this.search(search)) && new Set(await this.search(search))
@ -926,6 +948,27 @@ module.BaseStore = {
count-- count--
return !!(count >= 0) } }, return !!(count >= 0) } },
}, },
// XXX EXPERIMENTAL...
__match_args: function(args){
var that = this
var predicates = []
for(var [key, gen] of Object.entries(this.__match_args__ ?? {})){
var p = gen.call(this, args[key], args)
p && predicates.push(p) }
return Promise.awaitOrRun(
...predicates,
function(...predicates){
predicates = predicates
.filter(function(p){ return p })
return predicates.length > 0 ?
function(path){
for(var p of predicates){
if(!p.call(that, path)){
return false } }
return true }
: undefined },
null) },
/*/ // XXX ASYNC...
__match_args: async function(args){ __match_args: async function(args){
var that = this var that = this
var predicates = [] var predicates = []
@ -939,7 +982,12 @@ module.BaseStore = {
return false } } return false } }
return true } return true }
: undefined }, : undefined },
match: async function(path, strict=false){ //*/
// XXX EXPERIMENTAL...
// to be sync this needs:
// .__match_args(..) -- DONE
// .metadata(..)
match: function(path, strict=false){
var that = this var that = this
// pattern match * / ** // pattern match * / **
if(path.includes('*') if(path.includes('*')
@ -954,10 +1002,14 @@ module.BaseStore = {
?? !(args.sortnewfirst ?? !(args.sortnewfirst
// default is sortnewlast... // default is sortnewlast...
?? false) ?? false)
var test = await this.__match_args(args)
var test = this.__match_args(args)
args = pwpath.joinArgs('', args) args = pwpath.joinArgs('', args)
var order = (await this.metadata(path) ?? {}).order || [] var order = Promise.awaitOrRun(
this.metadata(path),
function(metadata){
return (metadata ?? {}).order || [] })
// NOTE: we are matching full paths only here so leading and // NOTE: we are matching full paths only here so leading and
// trainling '/' are optional... // trainling '/' are optional...
@ -970,58 +1022,149 @@ module.BaseStore = {
// dir for hidden tests... // dir for hidden tests...
.replace(/(^|\\\/+)(\\\.|)([^\/]*)\\\*/g, '$1$2($3[^\\/]*)') .replace(/(^|\\\/+)(\\\.|)([^\/]*)\\\*/g, '$1$2($3[^\\/]*)')
+'(?=[\\/]|$)', 'g') +'(?=[\\/]|$)', 'g')
return [...(await this.paths) // XXX ASYNC...
// NOTE: we are not using .filter(..) here as wee return Promise.awaitOrRun(
// need to keep parts of the path only and not this.paths,
// return the whole thing... test,
.reduce(function(res, p){ order,
// skip metadata paths... function(paths, test, order){
if(p.includes('*')){ return [...paths
return res } // NOTE: we are not using .filter(..) here as wee
// check path: stage 1 // need to keep parts of the path only and not
var m = [...p.matchAll(pattern)] // return the whole thing...
var visible = m.length > 0 .reduce(function(res, p){
&& (!all ? // skip metadata paths...
// test if we need to hide things.... if(p.includes('*')){
m.reduce(function(res, m){ return res }
return res === false ? // check path: stage 1
res var m = [...p.matchAll(pattern)]
: !/(^\.|[\\\/]\.)/.test(m[1]) var visible = m.length > 0
}, true) && (!all ?
: true) // test if we need to hide things....
// args... m.reduce(function(res, m){
// NOTE: this needs to be between path checking return res === false ?
// stages as we need to skip paths depending res
// on the all argument... : !/(^\.|[\\\/]\.)/.test(m[1])
if(visible }, true)
&& test : true)
&& !test(p)){ // args...
return res } // NOTE: this needs to be between path checking
// check path: stage 2 // stages as we need to skip paths depending
visible // on the all argument...
&& (m = m[0]) if(visible
&& (!strict && test
|| m[0] == p) && !test(p)){
&& res.add( return res }
// normalize the path elements... // check path: stage 2
m[0][0] == '/' ? visible
m[0].slice(1) && (m = m[0])
: m[0]) && (!strict
return res }, new Set())] || m[0] == p)
// handle live sort... && res.add(
.run(function(){ // normalize the path elements...
return (sort && sort !== true) ? m[0][0] == '/' ?
that m[0].slice(1)
.sort(this, ...sort.split(/\s*[,\s]+/g)) : m[0])
:this return res }, new Set())]
.sortAs(order, // handle live sort...
newlast ? .run(function(){
'head' return (sort && sort !== true) ?
: 'tail') }) that
.map(function(p){ .sort(this, ...sort.split(/\s*[,\s]+/g))
return p+args })} : this
// direct search... .sortAs(order,
return this.find(path, strict) }, newlast ?
'head'
: 'tail') })
.map(function(p){
return p+args }) }) }
// direct search...
return this.find(path, strict) },
// // XXX ASYNC...
// match: async function(path, strict=false){
// var that = this
// // pattern match * / **
// if(path.includes('*')
// || path.includes('**')){
// var {path, args} = pwpath.splitArgs(path)
// path = pwpath.sanitize(path)
//
// var all = args.all
// var sort = args.sort
// var newlast =
// args.sortnewlast
// ?? !(args.sortnewfirst
// // default is sortnewlast...
// ?? false)
// var test = await this.__match_args(args)
// args = pwpath.joinArgs('', args)
//
// var order = (await this.metadata(path) ?? {}).order || []
//
// // NOTE: we are matching full paths only here so leading and
// // trainling '/' are optional...
// var pattern = new RegExp(`^\\/?`
// +RegExp.quoteRegExp(path)
// // pattern: **
// .replace(/\\\*\\\*/g, '(.*)')
// // pattern: *
// // NOTE: we are prepping the leading '.' of a pattern
// // dir for hidden tests...
// .replace(/(^|\\\/+)(\\\.|)([^\/]*)\\\*/g, '$1$2($3[^\\/]*)')
// +'(?=[\\/]|$)', 'g')
// return [...(await this.paths)
// // NOTE: we are not using .filter(..) here as wee
// // need to keep parts of the path only and not
// // return the whole thing...
// .reduce(function(res, p){
// // skip metadata paths...
// if(p.includes('*')){
// return res }
// // check path: stage 1
// var m = [...p.matchAll(pattern)]
// var visible = m.length > 0
// && (!all ?
// // test if we need to hide things....
// m.reduce(function(res, m){
// return res === false ?
// res
// : !/(^\.|[\\\/]\.)/.test(m[1])
// }, true)
// : true)
// // args...
// // NOTE: this needs to be between path checking
// // stages as we need to skip paths depending
// // on the all argument...
// if(visible
// && test
// && !test(p)){
// return res }
// // check path: stage 2
// visible
// && (m = m[0])
// && (!strict
// || m[0] == p)
// && res.add(
// // normalize the path elements...
// m[0][0] == '/' ?
// m[0].slice(1)
// : m[0])
// return res }, new Set())]
// // handle live sort...
// .run(function(){
// return (sort && sort !== true) ?
// that
// .sort(this, ...sort.split(/\s*[,\s]+/g))
// :this
// .sortAs(order,
// newlast ?
// 'head'
// : 'tail') })
// .map(function(p){
// return p+args })}
// // direct search...
// return this.find(path, strict) },
//*/
// //
// .resolve(<path>) // .resolve(<path>)
// -> <path> // -> <path>
@ -1039,6 +1182,32 @@ module.BaseStore = {
// -> ['System/tree', 'Dir/tree', ...] // -> ['System/tree', 'Dir/tree', ...]
// //
// XXX should this be used by .get(..) instead of .match(..)??? // XXX should this be used by .get(..) instead of .match(..)???
// XXX EXPERIMENTAL
// to be sync requires:
// .match(..)
resolve: function(path, strict){
// pattern match * / **
if(path.includes('*')
|| path.includes('**')){
var p = pwpath.splitArgs(path)
var args = pwpath.joinArgs('', p.args)
p = pwpath.split(p.path)
var tail = []
while(!p.at(-1).includes('*')){
tail.unshift(p.pop()) }
tail = tail.join('/')
if(tail.length > 0){
return Promise
.iter(this.match(
p.join('/') + args,
strict))
.map(function(p){
var {path, args} = pwpath.splitArgs(p)
return pwpath.joinArgs(pwpath.join(path, tail), args) })
.sync() } }
// direct...
return this.match(path, strict) },
/*/ // XXX ASYNC...
resolve: async function(path, strict){ resolve: async function(path, strict){
// pattern match * / ** // pattern match * / **
if(path.includes('*') if(path.includes('*')
@ -1059,6 +1228,7 @@ module.BaseStore = {
return pwpath.joinArgs(pwpath.join(path, tail), args) }) } } return pwpath.joinArgs(pwpath.join(path, tail), args) }) } }
// direct... // direct...
return this.match(path, strict) }, return this.match(path, strict) },
//*/
// //
// Resolve page // Resolve page
// .get(<path>) // .get(<path>)