adde action pre-call test and debounce test generator...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-12-03 03:33:51 +03:00
parent 03b52f6f9f
commit 7b23e84dcc
3 changed files with 151 additions and 25 deletions

View File

@ -158,6 +158,29 @@ n
languages such as Python where the order is reversed. languages such as Python where the order is reversed.
## Index
- [Actions](#actions)
- [The problem:](#the-problem)
- [The solution:](#the-solution)
- [What we get:](#what-we-get)
- [Restrictions comparing to native JavaScript:](#restrictions-comparing-to-native-javascript)
- [Index](#index)
- [The main entities:](#the-main-entities)
- [The action system main protocols:](#the-action-system-main-protocols)
- [1. Documentation generation and introspection (`MetaActions`)](#1-documentation-generation-and-introspection-metaactions)
- [2. Event-like callbacks for actions (`MetaActions`, `Action`)](#2-event-like-callbacks-for-actions-metaactions-action)
- [3. A mechanism to define and extend already defined actions](#3-a-mechanism-to-define-and-extend-already-defined-actions)
- [Secondary action protocols:](#secondary-action-protocols)
- [1. A mechanism to manually call the pre/post stages of an action](#1-a-mechanism-to-manually-call-the-prepost-stages-of-an-action)
- [2. A mechanism to chain/wrap actions or an action and a function.](#2-a-mechanism-to-chainwrap-actions-or-an-action-and-a-function)
- [3. `.__actioncall__` action / handler](#3-__actioncall__-action--handler)
- [4. Action attributes](#4-action-attributes)
- [5. Pre-call testing if an action can be called](#5-pre-call-testing-if-an-action-can-be-called)
- [6. Scheduling a call after the running action](#6-scheduling-a-call-after-the-running-action)
- [7. Calling action handlers sorted independently of the prototype chain](#7-calling-action-handlers-sorted-independently-of-the-prototype-chain)
- [Alias protocols:](#alias-protocols)
- [License](#license)
### The main entities: ### The main entities:
@ -479,6 +502,17 @@ the second arguments, and as normal a result on the post phase.
#### 4. Action attributes #### 4. Action attributes
Setting action attributes:
```javascript
someAction: [
{attr: 'value', .. },
function(){
...
}],
```
Attribute access:
``` ```
<action-set>.getActionAttr('action', 'attr') <action-set>.getActionAttr('action', 'attr')
-> <value> -> <value>
@ -487,7 +521,35 @@ the second arguments, and as normal a result on the post phase.
-> <value> -> <value>
``` ```
#### 5. Scheduling a call after the running action
#### 5. Pre-call testing if an action can be called
A pre call test is called before the action's pre handlers are called and if
it returns anything truthy the action is not called and that return value is
returned instead.
To return a falsey value wrap it in `actions.ASIS(..)`
Only the top-most pre call test is called.
Defining a pre call test:
```javascript
someAction: [
{precall: actions.debounce(200, true)},
function(){
...
}],
```
The test is called in the context of the `<action-set>`
```
<pre-call-test>(<action>, ...)
-> undefined
-> <value>
```
#### 6. Scheduling a call after the running action
This enables the action code to schedule a call after the current This enables the action code to schedule a call after the current
action level or the root action is done. action level or the root action is done.
@ -520,7 +582,7 @@ Example:
- This is pointless outside of an action call, thus an exception will be thrown. - This is pointless outside of an action call, thus an exception will be thrown.
#### 6. Calling action handlers sorted independently of the prototype chain #### 7. Calling action handlers sorted independently of the prototype chain
This sorts action handlers by priority `.sortedActionPriority` then This sorts action handlers by priority `.sortedActionPriority` then
order and calls them. order and calls them.

View File

@ -58,6 +58,46 @@ function(func){
return func.apply(this, [handlers.pop()].concat(args)) } } return func.apply(this, [handlers.pop()].concat(args)) } }
//---------------------------------------------------------------------
// pre-call tests...
// Debounce action call...
//
// debounce()
// debounce(timeout[, postcall])
// -> this
// -> false
//
//
// XXX would be good to add a version of this that would debounce taking
// into acoount args...
// XXX EXPERIMENTAL (precall)...
var debounce =
module.debounce =
function(timeout=200, postcall=true){
var debounced = false
var last_args
return function(action, ...args){
// call...
if(!debounced){
debounced = setTimeout(
function(){
debounced = false
// post call...
postcall
&& last_args !== undefined
&& action.call(this, ...last_args) }.bind(this),
timeout)
// cleanup...
last_args = undefined
return false
// skip...
} else {
last_args = args
return this } } }
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// String action parser/runner... // String action parser/runner...
@ -483,6 +523,29 @@ object.Constructor('Action', Function, {
|| MetaActions.getHandlers || MetaActions.getHandlers
var handlers = getHandlers.call(context, outer) var handlers = getHandlers.call(context, outer)
// precall test...
// NOTE: we are calling only the top-most precall method, the
// rest are ignored...
// XXX EXPERIMENTAL (precall)...
var precall = handlers
.map(function(h){
return (h.pre || {}).precall ?
[h.pre.precall]
: [] })
.flat()
.pop()
if(typeof(precall) == 'function'){
// XXX revise args...
precall = precall.call(context, this, ...args)
if(precall){
return {
rejected: true,
// XXX revise how default value is returned...
result: precall instanceof ASIS ?
precall.value
: precall,
} } }
// handle cases where .func is not in handlers... // handle cases where .func is not in handlers...
// //
// NOTE: see Special cases in method doc above... // NOTE: see Special cases in method doc above...
@ -554,13 +617,10 @@ object.Constructor('Action', Function, {
throw error } throw error }
// return context if nothing specific is returned... // return context if nothing specific is returned...
res = res === undefined ? context res = res === undefined ?
: res instanceof ASIS ? res.value context
// XXX returning an explicit [undefined]... : res instanceof ASIS ?
//: res instanceof Array res.value
// && res.length == 1
// && res.indexOf(undefined) == 0 ?
// undefined
: res : res
return { return {
@ -626,12 +686,17 @@ object.Constructor('Action', Function, {
// chaining... // chaining...
// //
// For docs see: MetaActions.chainApply(..) and the base module doc. // For docs see: MetaActions.chainApply(..) and the base module doc.
chainApply: function(context, inner, args){ chainCall: function(context, inner, ...args){
args = [...(args || [])] args = args || []
var outer = this.name var outer = this.name
var data = this.pre(context, args) var data = this.pre(context, args)
// precall test...
// XXX EXPERIMENTAL (precall)...
if(data.rejected == true){
return data.result }
// call the inner action/function if preset.... // call the inner action/function if preset....
// NOTE: this is slightly different (see docs) to what happens in // NOTE: this is slightly different (see docs) to what happens in
// .pre(..)/.post(..), thus we are doing this separately and // .pre(..)/.post(..), thus we are doing this separately and
@ -640,9 +705,9 @@ object.Constructor('Action', Function, {
var res = inner instanceof Function ? var res = inner instanceof Function ?
inner.apply(context, args) inner.apply(context, args)
: inner instanceof Array && inner.length > 0 ? : inner instanceof Array && inner.length > 0 ?
context[inner.pop()].chainApply(context, inner, args) context[inner.pop()].chainCall(context, inner, ...args)
: typeof(inner) == typeof('str') ? : typeof(inner) == typeof('str') ?
context[inner].chainApply(context, null, args) context[inner].chainCall(context, null, ...args)
: undefined : undefined
// call the resulting function... // call the resulting function...
@ -667,8 +732,8 @@ object.Constructor('Action', Function, {
return that.post(context, data) }) } return that.post(context, data) }) }
return this.post(context, data) }, return this.post(context, data) },
chainCall: function(context, inner){ chainApply: function(context, inner, args){
return this.chainApply(context, inner, [...arguments].slice(2)) }, return this.chainCall(context, inner, ...args) },
// constructor... // constructor...
@ -686,7 +751,7 @@ object.Constructor('Action', Function, {
// create the actual instance we will be returning... // create the actual instance we will be returning...
var meth = function(){ var meth = function(){
return meth.chainApply(this, null, arguments) } return meth.chainCall(this, null, ...arguments) }
meth.__proto__ = this.__proto__ meth.__proto__ = this.__proto__
// precess args... // precess args...
@ -1481,14 +1546,13 @@ module.MetaActions = {
// Apply/call a function/action "inside" an action... // Apply/call a function/action "inside" an action...
// //
// .chainApply(outer, inner)
// .chainApply(outer, inner, arguments)
// -> result
//
// .chainCall(outer, inner) // .chainCall(outer, inner)
// .chainCall(outer, inner, ..) // .chainCall(outer, inner, ..)
// -> result // -> result
// //
// .chainApply(outer, inner)
// .chainApply(outer, inner, arguments)
// -> result
// //
// The inner action call is completely nested as base of the outer // The inner action call is completely nested as base of the outer
// action. // action.
@ -1514,10 +1578,10 @@ module.MetaActions = {
// NOTE: these call the action's .chainApply(..) and .chainCall(..) // NOTE: these call the action's .chainApply(..) and .chainCall(..)
// methods, thus is not compatible with non-action methods... // methods, thus is not compatible with non-action methods...
// NOTE: .chainCall('action', ..) is equivalent to .action.chainCall(..) // NOTE: .chainCall('action', ..) is equivalent to .action.chainCall(..)
chainApply: function(outer, inner, args){ chainCall: function(outer, inner, ...args){
return this[outer].chainApply(this, inner, args) }, return this[outer].chainCall(this, inner, ...args) },
chainCall: function(outer, inner){ chainApply: function(outer, inner, ...args){
return this[outer].chainApply(this, inner, [...arguments].slice(2)) }, return this[outer].chainCall(this, inner, args) },
// Call action handlers serted by .sortedActionPriority... // Call action handlers serted by .sortedActionPriority...

View File

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