Pocket.js (P/p)
Developed by Eaglex
Easy to use, Pocket.js redistribution utility framework, allowing you to probe data and managing state, well documented, clean beautified code. You can probe task assignments and control its outcome anywhere in your code.
Why use it
- It uses Require.js/AMD for compatibility and encapsulation.
- Project is status driven and pattern driven, requires tasks to complete
- Specify
tasks, and receive results by assignment - Manage task ( Probe/task ) states and resolutions
- Re/distribution of data/scheduled assignments to different areas of your code
- Flexibility - make your work easier and justifiable, simply more fun
- Easy to use, with chaining mythology
- Consider each Probe task of Pocket.js a campaign you are running, once all campaigns
complete to sendPocket will resolve(). your assignment - All tests passing
- Production ready
Pocket code documentation
Project docs are available on github pages at: Pocket.js
About the code:
-
PocketModulemanages eachnew Probe(), when status=completeis set,sq.resolve()and dispatch is initiated. Final results are returned inawait $ready(payloadID) -
Well documented with debug logging
-
Good catch exceptions
-
In depth
Mochatests with good nyc/Istanbul coverage.
What is Pocket
Pocket > Probe: Main/parent module managing each new payload pushed thru theProbe, it watches the status of its tasks, once all are complete the $ready event is called.- You can consider the task list your campaign list.
- refer to example:
node /samples/pocket.simple.js
What is a Probe
Probe < Pocket: Child module doesnt know about Pocket, its status managed, whencomplete, Pocket intercepts, and waits until all Probe taskscomplete. Can be used independently if needed.- You can consider each probe a todo item, part of a bigger assignment.
- refer to example:
node /samples/simple.probe.js - Becomes immutable once the status is set to complete
Playground
ps: login to get the latest code
you can play around with /samples
## first install
npm i
## then play :)
node /samples/pocket.simple.js
node /samples/pocket.advance.js
node /samples/simple.probe.js
## or
npm run example
Stack
- Node.js, ES6, JavaScript, Require.js/AMD, data-management, state/management, Promise (sync/async), prototyping, class chaining, OOP (SETTERS/GETTER), Istanbul/nyc, Eslint, Mocha/Chai, Custom Utils, debug/error exception handling, user/friendly logging, jsdocs, task runners
How to build
You can you the production branch or build it your self :)
Building project for production: common.js and Windows/browser are available, take a look inside package.json.
-
To run build from source, run
npm run build:umd, this will create universal production bundle available to use in browser and Node.js backend. -
there is already an available build you can use,
require("./build"), if importing as packagerequire("pocket/build") -
-
To use in browser just include script source
<script src="build/js/pocket_umd.js"/>- then access via:
new Window.Pocket({...}) -
- then access via:
- it uses Require.js/AMD in the build to support client with own encapsulation.
PocketModule config/opts and status logic:
-
PocketModule.opts{}: available options on constructor to be set with instance creation.opts.onChange:boolean: enables property watch on each Probe, and lets you use$onChangemethodopts.completeOnNull:boolean: when set will allow completion of any probe even it data is still in initial null phaseopts.withDataBank: enables Probe{} .data to provide dataBank on every return for data history changesopts.deleteWithDelay:number: remove all project data with given delay after $ready has been resolved, defaults to1000msopts.async:Boolean: when set will handleawait payload(asyncData)as Promise.opts.dispatcher:Boolean: when set will loadDispatcher module, in to Pocket instance, and allow additional live on-change logging and direct communication with each Probe, currently this feature is limited to only logging, must setdebug:trueto see it in action.[dispatcher]....debug:Boolean: will log additional messages on what is happening, good for debugging :)!
-
Probe status logic: each Probe hasstatus, which gives it the required interaction behavior through out each payload. Status list, runs in forward motion, once the status is set tocompletenothing can be changed:open: When new Probe is created, first status is created automatically, its status will remain the same untilProbe.dataproperty is updated, it can never be set back toopen.updated: status is set automatically once firstProbe.datais updated to any true value, it can be re updated, once initialdatawas previously setcomplete: when Probe/task is done,Probe.sq.resolve()is called, and nothing more can be done.send: automatically set afterProbe.sq.resolve()is called, cannot be set manually, it is used to identify last statuserrorworks same ascomplete, with small difference... it is set last instatusStackOrder
PocketModule methods:
-
$payload( data, async,type ).d:Boolean : top level method to initiate requested tasks, returns true when successful, and false on error.
data.id:String: payload.id that identifies this jobdata.tasks:Array[]: specifies required format/data of tasks to perform. Specifications for this can be found in./samples/**async: when passing data is a defer you need to specify data type output as async:true, or set it in global options on Pocket instancetype: if our payload is in the loop, for example: we do not want to override existing values, we can set it to type='update' (default isnew)
-
$probeStatusAsync( probeID ).d:promise : returns last status changed via async method, the promise is reset overtime new status is updated, so it can be called many times, returns status name
probeID: must provide probeID example:${payloadID}::${task}
-
Probe.getStatusAsync : same as ^^above^^, method can be called on each Probe/task instance, good for checking latest status in question, where its needed :)
-
$get( probeID ):Probe: returns an active instance of your Probe/task =>
$get(probeID).status='complete', you can also use it instead of$update()probeID:String: provided format must be, example:${payloadID}::${task}
-
$update( dataFrom, mergeData,probeID).d:Boolean : will select currently available Probe/task by
id, and update its data, only available fields found on Probe can be updated according to setter/getter requirementsprobeID:String: required probeID, each probeprobeIDmakes up:${payload.id}::${task}, dynamically created upon$payload(..)===truedataFrom:{}: available fields example:dataFrom:{data:'some cola', campaign:'cocacola',status:'complete'}, will perform an update on Probe[id][data], Probe[id][campaign], etc. Validation is sensitive.mergeData: when specified anddataFrom.datafield is set, will merge both
-
$activeTasks( payloadID ).d:Array: returns an
array['taskA','taskB']from current job payload, will only be available before$ready(..)is resolved, and before PocketSet tasks are completed. -
$ready(payloadID, allowMultiple, strict=false).d:Promise: last calling method, when your
Pockettasks are completed, example:Probe[id][status]='complete'only then, will it resolve(), otherwise pending Probe's will remain and$ready()will expire, this is the desired effect, most logical behavior.allowMultiple:boolean = false: if you want to allow calling same project $ready(..) multiple times then setallowMultiple=true, otherwise it will give you warning. By default is set tofalse.strict=true: will check if provided id exists on the project
-
Final note: All user/interaction methods are prefixed with '$'
-
Note: Most user $methods require
...).dfor access to values, to allows chaining
PocketModule selectors:
Probe{} selectors list:- $removeProject(projectID): removes the project in case not already removed
- $projectSet(projectID): tells if project already created
- $of(probeID = ''):self: selects pointer to Probe{}
- $probeStatusAsync(probeID).d:Promise: resolves promise when status changes
- $project(data)/$payload(data,async, type = 'new'): alias of $payload
- $getByRef(probeRef):[Array]: returns an array of probes that match the Probe.ref
- $probe(probeID):Probe{}: returns Probe{} by id
- $select(projectID):self: sets pointer to the project
- $filter(cb, projectID):self: filters probes that match condition true, in a callback
- $compute(cb, projectID = ''):self: loops thru each probe (if previously filtered/or all) that can be manipulated
- $projectID: getter/ return last projectID
- $list(projectID = '', cb = null, type = 'self'):Array[]: list active Probes{} by project id, should return all assigned probe/tasks regardless of status
- $transfer(fromProbeID = ''):self: select data from
fromProbeIDand hold it in_transferCache, until$to(probeID)is called - $to(toProbeID = '', pointToThisProbe = true, maxDelay = 100):self: works together with
$transfer, will transferdatafrom one Probe{} to another - $data(dataProp:{}||[] , probeID = '', self = false): returns Object copy of
Probe['data'] - $cached(dataProp = {}, probeID = ''):{}: Grabs last cached
$data(...)from Probe{} - $campaign(probeID):string: Returns Object copy of
Probe['campaign'] - $ref(probeID): Returns Probe{}.ref
- $status(probeID):String: Returns Object copy of
Probe['status'] - $task(probeID):string: Returns Object copy of
Probe['task'] - $error(probeID):[]: Returns Object copy of
Probe['task'] - $all(probeID):probeGetters: Return object copy of all setters:
{id,status,campaign,task,data} - $architect(cb, projectID): more construct way of setting up a project and allowing few external assets to be used. This method uses $payload inheritance with access to
typeandasync. Things to remember consecutive call to $architect thru .$condition() method, can only update existing items.cb(()=>({project:payloadData, type,async})): return callback must return {project:payload} as minimum requirement, when running in a loop or repetitive actions, it is best to set type='update' so that concurrent call to the same task wont wont be ignored,typeis state base, so last setting is kept
- $asset(assetName, projectID): can access the asset declared in
$architect - $condition(cb,id): declare arguments within callback without exiting Pocket block chain. The
idyou pass: probe::idorprojectID, will expose access to self of either Probe or Pocket instance. The return of callback is sensitive, if no value is passed the Pocket/self is returned. For example if accessing Probe then probe id can be returned but the chaining will refer to its instance, you may want to return pocket self instead. - $exists(probeID):boolean: check if probe exists, specify
probeID, returns boolean. - $onChange(cb, watchProp, ProbeID):self: when
opts.onChange:booleanis set, watches changes of ProbewatchProp:String: provide property name to watch, defaults toall
- $onProbeComplete(cb, probeID):self: listen to callback on one, or all probes that completed due process
cb((allData,id))probeID: optionally select if wishing on only listen for one probe by id
Code/extensions
for comments/and linting use:
Document ThisAdd jsdoc comments
Examples
// pocket.simple.js
const Pocket = require("../index").Pocket
const pock = new Pocket({ async: false, dispatcher: true, withDataBank: true, onChange: true, deleteWithDelay: 0, completeOnNull: true }, true)
const data = {
// source: `https://en.wikipedia.org/wiki/List_of_projects_of_the_Belt_and_Road_Initiative`
id: "pocket-1", // Belt and Road Initiative
tasks: [
{
task: "china",
ref: "abc",
data: { assets: 10, type: "billions", info: "benefactor" },
campaign: "Belt_and_Road_Initiative"
},
{
task: "srilanka",
ref: "efg",
data: { budget: 1.4, type: "billions", project: "naval port" },
campaign: "Belt_and_Road_Initiative"
}
]
}
// this will check data assignments follow format principles then assign it to Pocket
const onSet = pock.$project(data, false, "update").d
if (onSet) {
pock.$filter(function (probe) {
// filter by probe data attributes
return true
}).d // access computed data , or continue chaining
/** we only have initiated single Pocket so dont need to select it, refer to pocket.advance.js */
pock.$filter((probe) => {
// select all probe tasks
return true
})
.$compute(function (probe, id) {
// run thru all probe tasks based on filter condition
console.log("probe.task", probe.task)
this.data = "any new data type 1"
if (probe.task === "china") this.status = "complete"
})
.$filter((probe) => {
return probe.task === "srilanka"
})
.$compute(function (probe, id) {
// run thru all probe tasks based on filter condition
console.log("probe.task", probe.task)
this.data = "any new data type 2"
this.status = "complete"
})
}
// observe all changes of china pocket
pock.$get(`pocket-1::china`).onChange(function (data, id) {
console.log("pocket-1::china onChange", data.status, id)
}, "all")
// observe all pocket changes
pock.$get(`pocket-1`).onChange(function (data, id) {
console.log("pocket-1 onChange", data.status.id)
}, "all")
// pocket-1 will only resolve if all Probe tasks have been set as complete
pock.$ready(`pocket-1`, true)
.d.then((z) => {
console.log("pocket-1 ready", z)
})
.catch((err) => {
console.log("ups", err)
})
// accessors
// console.log("[pock.$get][data]", pock.$get("pocket-1::china").data)
// console.log("[pock.$get][dataBank]", "pocket-1::china", pock.$get("pocket-1::china").dataBank)
Test / Mocha and coverage
- To run a Mocha test:
npm run mocha - To run full coverage test with Instanbul:
npm run test - Coverage can be found in
./coverage/index.html
TODO on premium release:
- (add) typescript support in later version.
- (add) browser version support.
- (add) history state management.
Contact
- Have questions, or would like to submit feedback,
contact me at: (https://michaelworks.eaglex.net/connect
LICENSE
- LICENSE: CC BY-NC-ND
- SOURCE: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode