From 7b23e84dcc8dd2d6b4f7dc283170f5d66d1a4802 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Thu, 3 Dec 2020 03:33:51 +0300 Subject: [PATCH] adde action pre-call test and debounce test generator... Signed-off-by: Alex A. Naanou --- README.md | 66 ++++++++++++++++++++++++++++++- actions.js | 108 ++++++++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 3 files changed, 151 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 9d4c143..1f271d8 100755 --- a/README.md +++ b/README.md @@ -158,6 +158,29 @@ n 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: @@ -479,6 +502,17 @@ the second arguments, and as normal a result on the post phase. #### 4. Action attributes +Setting action attributes: +```javascript + someAction: [ + {attr: 'value', .. }, + function(){ + ... + }], + +``` + +Attribute access: ``` .getActionAttr('action', 'attr') -> @@ -487,7 +521,35 @@ the second arguments, and as normal a result on the post phase. -> ``` -#### 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 `` +``` +(, ...) + -> undefined + -> +``` + + +#### 6. Scheduling a call after the running action This enables the action code to schedule a call after the current 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. -#### 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 order and calls them. diff --git a/actions.js b/actions.js index 0eb20f7..c271f2b 100755 --- a/actions.js +++ b/actions.js @@ -58,6 +58,46 @@ function(func){ 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... @@ -483,6 +523,29 @@ object.Constructor('Action', Function, { || MetaActions.getHandlers 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... // // NOTE: see Special cases in method doc above... @@ -554,13 +617,10 @@ object.Constructor('Action', Function, { throw error } // return context if nothing specific is returned... - res = res === undefined ? context - : res instanceof ASIS ? res.value - // XXX returning an explicit [undefined]... - //: res instanceof Array - // && res.length == 1 - // && res.indexOf(undefined) == 0 ? - // undefined + res = res === undefined ? + context + : res instanceof ASIS ? + res.value : res return { @@ -626,12 +686,17 @@ object.Constructor('Action', Function, { // chaining... // // For docs see: MetaActions.chainApply(..) and the base module doc. - chainApply: function(context, inner, args){ - args = [...(args || [])] + chainCall: function(context, inner, ...args){ + args = args || [] var outer = this.name 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.... // NOTE: this is slightly different (see docs) to what happens in // .pre(..)/.post(..), thus we are doing this separately and @@ -640,9 +705,9 @@ object.Constructor('Action', Function, { var res = inner instanceof Function ? inner.apply(context, args) : inner instanceof Array && inner.length > 0 ? - context[inner.pop()].chainApply(context, inner, args) + context[inner.pop()].chainCall(context, inner, ...args) : typeof(inner) == typeof('str') ? - context[inner].chainApply(context, null, args) + context[inner].chainCall(context, null, ...args) : undefined // call the resulting function... @@ -667,8 +732,8 @@ object.Constructor('Action', Function, { return that.post(context, data) }) } return this.post(context, data) }, - chainCall: function(context, inner){ - return this.chainApply(context, inner, [...arguments].slice(2)) }, + chainApply: function(context, inner, args){ + return this.chainCall(context, inner, ...args) }, // constructor... @@ -686,7 +751,7 @@ object.Constructor('Action', Function, { // create the actual instance we will be returning... var meth = function(){ - return meth.chainApply(this, null, arguments) } + return meth.chainCall(this, null, ...arguments) } meth.__proto__ = this.__proto__ // precess args... @@ -1481,14 +1546,13 @@ module.MetaActions = { // Apply/call a function/action "inside" an action... // - // .chainApply(outer, inner) - // .chainApply(outer, inner, arguments) - // -> result - // // .chainCall(outer, inner) // .chainCall(outer, inner, ..) // -> result // + // .chainApply(outer, inner) + // .chainApply(outer, inner, arguments) + // -> result // // The inner action call is completely nested as base of the outer // action. @@ -1514,10 +1578,10 @@ module.MetaActions = { // NOTE: these call the action's .chainApply(..) and .chainCall(..) // methods, thus is not compatible with non-action methods... // NOTE: .chainCall('action', ..) is equivalent to .action.chainCall(..) - chainApply: function(outer, inner, args){ - return this[outer].chainApply(this, inner, args) }, - chainCall: function(outer, inner){ - return this[outer].chainApply(this, inner, [...arguments].slice(2)) }, + chainCall: function(outer, inner, ...args){ + return this[outer].chainCall(this, inner, ...args) }, + chainApply: function(outer, inner, ...args){ + return this[outer].chainCall(this, inner, args) }, // Call action handlers serted by .sortedActionPriority... diff --git a/package.json b/package.json index d2c5417..77722f8 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ig-actions", - "version": "3.24.19", + "version": "3.24.20", "description": "", "main": "actions.js", "scripts": {