refactoring, minor tweaks and fixes...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-04-23 17:59:13 +03:00
parent e4a1139be3
commit 8f259d21fe
2 changed files with 415 additions and 380 deletions

View File

@ -249,7 +249,7 @@ module.UNDEFINED = ASIS(undefined)
// really necessary.
//
//
// 3) .__call__ action / handler
// 3) .__actioncall__ action / handler
// This action if defined is called for every action called. It behaves
// like any other action but with a fixed signature, it always receives
// the action name as first argument and a list of action arguments as
@ -257,7 +257,7 @@ module.UNDEFINED = ASIS(undefined)
//
// NOTE: it is not necessary to define the actual action, binding to a
// handler will also work.
// NOTE: one should not call actions directly from within a __call__
// NOTE: one should not call actions directly from within a __actioncall__
// handler as that will result in infinite recursion.
// XXX need a way to prevent this...
// NOTE: one should use this with extreme care as this will introduce
@ -374,6 +374,7 @@ function(func){
return func.apply(this, [handlers.pop()].concat(args)) } }
// String action parser/runner...
//
// Syntax:
// ALIAS ::=
@ -410,6 +411,7 @@ function(func){
// }
//
// NOTE: identifiers are resolved as attributes of the context...
// NOTE: this is a stateless object...
// XXX this is the same as ImageGrid's keyboard.parseActionCall(..), reuse
// in a logical manner...
@ -482,8 +484,10 @@ Object.assign(
stop_propagation: false,
code: txt,
}
},{
} },
// API and utils...
{
// atoms...
Atom: (__Atom = object.Constructor('Atom', {
__init__: function(value){
@ -501,7 +505,7 @@ Object.assign(
resolveArgs: function(context, action_args, call_args){
var that = this
var rest
var args = [].slice.call(action_args)
var args = [...action_args]
// merge args...
.map(function(arg, i){
return arg instanceof that.Argument ?
@ -660,100 +664,78 @@ module.isStringAction =
// XXX might be a good idea to add an option to return the full results...
var Action =
module.Action =
function Action(name, doc, ldoc, attrs, func){
// we got called without a 'new'...
if(this == null || this.constructor !== Action){
// XXX using something like .apply(.., arguemnts) would be more
// generel but have no time to figure out how to pass it
// to new without the later complaining...
return new Action(name, doc, ldoc, attrs, func)
}
object.Constructor('Action', {
__proto__: Function,
// prevent action overloading...
if(this[name] != null){
throw 'action "'+name+'" already exists.'
}
// Control how an action handles returned promises...
//
// Possible values:
// true - if an action returns a promise then trigger the post phase
// after that promise is resolved / rejected... (default)
// false - handle promises like any other returned value.
//
//
// NOTE: .await is only checked in the root action, thus it can not be
// overloaded by extending actions.
// This is done intentionally, as the action actually returning a
// value (and defining the signature) is the only one responsible
// for controlling how it's handled.
//
// For implmentation see: Action.prototype.chainApply(..)
//
// XXX should we be able to set this in the context???
// XXX can we use 'await'???
await: true,
// create the actual instance we will be returning...
var meth = function(){
return meth.chainApply(this, null, arguments) }
meth.__proto__ = this.__proto__
// precess args...
var args = doc instanceof Array ?
doc
: [].slice.call(arguments)
.filter(function(e){ return e !== undefined })
.slice(1)
func = args.pop()
last = args[args.length-1]
attrs = last != null && typeof(last) != typeof('str') ? args.pop() : {}
doc = typeof(args[0]) == typeof('str') ? args.shift()
: func.doc ? func.doc
: null
ldoc = typeof(args[0]) == typeof('str') ? args.shift()
: func.long_doc ? func.long_doc
: null
// populate the action attributes...
//meth.name = name
Object.defineProperty(meth, 'name', {
value: name,
})
func.doc = meth.doc = doc
func.long_doc = meth.long_doc = ldoc
meth.func = func
if(func.name == '<action-name>'){
Object.defineProperty(func, 'name', {
value: name,
})
}
// make introspection be a bit better...
meth.toString = func.toString.bind(func)
// setup attrs...
Object.assign(meth, attrs)
Object.assign(func, attrs)
return meth
}
// this will make action instances behave like real functions...
Action.prototype.__proto__ = Function
// The pre/post stage runners...
//
// .pre(context, args)
// -> data
//
// .post(context, data)
// -> result
//
//
// Call data format:
// {
// arguments: args,
//
// wrapper: call_wrapper,
// handlers: handlers,
//
// result: res,
// }
//
//
// NOTE: All the defaults should be handled by the pre stage, post will
// process data assuming that it is correct.
// NOTE: .post(..) will not wait for returned promises to resolve, use
// .chainApply(..) / ,chainCall(..) instead, or handle .result
// manually...
// (see: Action.prototype.chainApply(..))
//
// XXX revise the structure....
// ...is it a better idea to define action methods in an object
// and assign that???
Action.prototype.pre = function(context, args){
// pre/post stage runners...
//
// .pre(context, args)
// -> data
//
// .post(context, data)
// -> result
//
//
// Call data format:
// {
// arguments: args,
//
// wrapper: call_wrapper,
// handlers: handlers,
//
// result: res,
// }
//
//
// External methods (required):
// .getHandlers(..) resolved from: context, MetaActions
//
//
// External methods (optoinal):
// .__actioncall__(..) resolved from: context
// .preActionHandler(..) resolved from: context, MetaActions
//
//
// Special cases:
// - An action is referenced via a different name than is in its .name
// this can occur if:
// 1) an action is renamed but its .name is not
// 2) an action is created and stored with a different name
// var f = new Action('m', function(){ ... })
//
//
// NOTE: All the defaults should be handled by the pre stage, post will
// process data assuming that it is correct.
// NOTE: .post(..) will not wait for returned promises to resolve, use
// .chainApply(..) / ,chainCall(..) instead, or handle .result
// manually...
// (see: Action.prototype.chainApply(..))
// XXX revise the structure....
// ...is it a better idea to define action methods in an object
// and assign that???
pre: function(context, args){
var that = this
args = args || []
// prepare for after calls...
@ -771,21 +753,36 @@ Action.prototype.pre = function(context, args){
var outer = this.name
// get the handler list...
var getHandlers = context.getHandlers || MetaActions.getHandlers
var getHandlers = context.getHandlers
|| MetaActions.getHandlers
var handlers = getHandlers.call(context, outer)
// special case: see if we need to handle the call without handlers...
var preActionHandler = context.preActionHandler || MetaActions.preActionHandler
if(preActionHandler){
// XXX signature needs work...
var res = preActionHandler.call(context, outer, handlers, args)
if(res !== undefined){
return res
// handle cases where .func is not in handlers...
//
// NOTE: see Special cases in method doc above...
if(handlers.length == 0
|| handlers.filter(function(h){
return h.pre === that.func }).length == 0){
var cur = {
pre: this.func,
}
this.doc
&& (cur.doc = this.doc)
this.long_doc
&& (cur.long_doc = this.long_doc)
handlers.unshift(cur)
}
var call_wrapper = outer != '__call__' ?
getHandlers.call(context, '__call__')
// special case: see if we need to handle the call without handlers...
var preActionHandler = context.preActionHandler
|| MetaActions.preActionHandler
if(preActionHandler){
var res = preActionHandler.call(context, outer, handlers, args)
if(res !== undefined){
return res } }
var call_wrapper = outer != '__actioncall__' ?
getHandlers.call(context, '__actioncall__')
: []
try {
@ -803,8 +800,7 @@ Action.prototype.pre = function(context, args){
a.post = res
}
}
return a
})
return a })
// handlers: pre phase...
handlers
@ -828,8 +824,7 @@ Action.prototype.pre = function(context, args){
res = context
}
}
return a
})
return a })
// XXX EXPERIMENTAL (after calls)...
} catch(error){
@ -856,8 +851,8 @@ Action.prototype.pre = function(context, args){
result: res,
}
}
Action.prototype.post = function(context, data){
},
post: function(context, data){
var res = data.result
var args = data.arguments || []
@ -912,35 +907,14 @@ Action.prototype.post = function(context, data){
}
return res
}
},
// Control how an action handles returned promises...
//
// Possible values:
// true - if an action returns a promise then trigger the post phase
// after that promise is resolved / rejected... (default)
// false - handle promises like any other returned value.
//
//
// NOTE: .await is only checked in the root action, thus it can not be
// overloaded by extending actions.
// This is done intentionally, as the action actually returning a
// value (and defining the signature) is the only one responsible
// for controlling how it's handled.
//
// For implmentation see: Action.prototype.chainApply(..)
//
// XXX should we be able to set this in the context???
// XXX can we use 'await'???
Action.prototype.await = true
// Chaining...
//
// For docs see: MetaActions.chainApply(..) and the base module doc.
Action.prototype.chainApply = function(context, inner, args){
args = [].slice.call(args || [])
// chaining...
//
// For docs see: MetaActions.chainApply(..) and the base module doc.
chainApply: function(context, inner, args){
args = [...(args || [])]
var outer = this.name
var data = this.pre(context, args)
@ -972,7 +946,7 @@ Action.prototype.chainApply = function(context, inner, args){
// returned promise -> await for resolve/error...
// XXX should we be able to set this in the context???
if(data.result instanceof Promise
&& (context.getRootActionAttr || ActionSet.getRootActionAttr)
&& (context.getRootActionAttr || MetaActions.getRootActionAttr)
.call(context, this.name, 'await') ){
var that = this
return data.result
@ -983,10 +957,77 @@ Action.prototype.chainApply = function(context, inner, args){
}
return this.post(context, data)
}
Action.prototype.chainCall = function(context, inner){
return this.chainApply(context, inner, [...arguments].slice(2))
}
},
chainCall: function(context, inner){
return this.chainApply(context, inner, [...arguments].slice(2)) },
// constructor...
//
// Action(<name>, <function>)
// Action(<name>[, <doc>[, <long-doc>]][, <attrs>,] <function>)
// Action(<name>, [ [<doc>[, <long-doc>]][, <attrs>,] <function> ])
// -> <action>
//
__new__: function Action(context, name, doc, ldoc, attrs, func){
// prevent action overloading...
// XXX do we need this???
//if(context != null && context[name] != null){
// throw 'action "'+name+'" already exists.' }
// create the actual instance we will be returning...
var meth = function(){
return meth.chainApply(this, null, arguments) }
meth.__proto__ = this.__proto__
// precess args...
var args = doc instanceof Array ?
doc
: [...arguments]
.filter(function(e){ return e !== undefined })
.slice(1)
func = args.pop()
last = args[args.length-1]
attrs = (last != null && typeof(last) != typeof('str')) ?
args.pop()
: {}
doc = typeof(args[0]) == typeof('str') ?
args.shift()
: func.doc ?
func.doc
: null
ldoc = typeof(args[0]) == typeof('str') ?
args.shift()
: func.long_doc ?
func.long_doc
: null
// populate the action attributes...
//meth.name = name
Object.defineProperty(meth, 'name', {
value: name,
})
func.doc = meth.doc = doc
func.long_doc = meth.long_doc = ldoc
meth.func = func
if(func.name == '<action-name>'){
Object.defineProperty(func, 'name', {
value: name,
})
}
// make introspection be a bit better...
meth.toString = func.toString.bind(func)
// setup attrs...
Object.assign(meth, attrs)
Object.assign(func, attrs)
return meth
},
})
@ -1010,19 +1051,14 @@ Action.prototype.chainCall = function(context, inner){
// XXX should an alias return a value???
var Alias =
module.Alias =
function Alias(alias, doc, ldoc, attrs, target){
// we got called without a 'new'...
if(this == null || this.constructor !== Alias){
// XXX using something like .apply(.., arguemnts) would be more
// generel but have no time to figure out how to pass it
// to new without the later complaining...
return new Alias(alias, doc, ldoc, attrs, target)
}
object.Constructor('Alias', {
__proto__: Action.prototype,
__new__: function Alias(context, alias, doc, ldoc, attrs, target){
// precess args...
var args = doc instanceof Array ?
doc
: [].slice.call(arguments)
: [...arguments]
.filter(function(e){ return e !== undefined })
.slice(1)
target = args.pop()
@ -1045,14 +1081,15 @@ function Alias(alias, doc, ldoc, attrs, target){
return
}
var that = this
var in_args = [].slice.call(arguments)
var in_args = [...arguments]
var p = parsed || this.parseStringAction(target)
var p = parsed
|| (this.parseStringAction || parseStringAction)(target)
// XXX should an alias return a value???
//p.action in this ?
return p.action in this ?
this.parseStringAction.callAction(this, p, ...in_args)
(this.parseStringAction || parseStringAction).callAction(this, p, ...in_args)
// error...
: console.error(`${alias}: Unknown alias target action: ${p.action}`)
}
@ -1060,15 +1097,15 @@ function Alias(alias, doc, ldoc, attrs, target){
return meth.alias.code || meth.alias }
// make the action...
var meth = Action(alias, doc, ldoc, attrs, func)
// XXX not sure this is the correct way to go...
var meth = this.__proto__.__new__.call(this, context, alias, doc, ldoc, attrs, func)
meth.__proto__ = this.__proto__
meth.func.alias = target
return meth
}
// inherit from Action...
Alias.prototype.__proto__ = Action.prototype
},
})
@ -1180,7 +1217,7 @@ module.MetaActions = {
// - Local action function (.func)
// - if an alias look in the target...
// - repeat for .__proto__ (until top of MRO)
// - repeat for '__call__' special action (XXX EXPERIMENTAL)
// - repeat for '__actioncall__' special action (XXX EXPERIMENTAL)
//
//
// NOTE: this will get attribute set both on the action object and
@ -1201,7 +1238,7 @@ module.MetaActions = {
// modifyAction(function(){ ... })],
//
// XXX document...
// XXX add option to to enable/disable look in .__call__...
// XXX add option to to enable/disable look in .__actioncall__...
getActionAttr: function(action, attr){
var cur = this
@ -1230,9 +1267,9 @@ module.MetaActions = {
cur = cur.__proto__
}
// search .__call__ action...
if(cur[action] != null && action != '__call__'){
return this.getActionAttr('__call__', attr)
// search .__actioncall__ action...
if(cur[action] != null && action != '__actioncall__'){
return this.getActionAttr('__actioncall__', attr)
}
},
@ -1535,9 +1572,7 @@ module.MetaActions = {
a.long_doc
&& (res.long_doc = a.long_doc)
return res
})
},
return res }) },
// Handler for cases when we need to avoid the pre/post handlers...
//
@ -2283,7 +2318,7 @@ object.Constructor('ActionSet', MetaActions)
// An action set...
// An action set constructor...
//
// Actions(<object>)
// Actions(<prototype>, <object>)
@ -2387,7 +2422,7 @@ function Actions(a, b){
var mix =
module.mix =
function(){
var args = [].slice.call(arguments)
var args = [...arguments]
var res = {}
// special case: if MetaActions is in the args then inherit the root

View File

@ -1,6 +1,6 @@
{
"name": "ig-actions",
"version": "3.24.4",
"version": "3.24.5",
"description": "",
"main": "actions.js",
"scripts": {