Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

hook-up

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hook-up - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

267

lib/hook-up.js

@@ -1,121 +0,168 @@

var arrayize, getHook, isFunction;
(function() {
var arrayize, defaultPosition, isArray, isFunction, isString, positions, setupAction;
isFunction = (fn) => {
return typeof fn === "function";
};
isFunction = function(fn) {
return typeof fn === "function";
};
arrayize = (obj) => {
if (Array.isArray(obj)) {
return obj;
} else if (obj != null) {
return [obj];
}
return [];
};
isArray = Array.isArray.bind(Array);
getHook = (obj, options) => {
var arr, exec, execute, getExec, hook, register;
arr = [];
exec = null;
execute = async(o) => {
if (exec == null) {
exec = getExec();
arrayize = function(obj) {
if (isArray(obj)) {
return obj;
} else if (obj != null) {
return [obj];
}
return (await exec(o));
return [];
};
register = (o) => {
if (isFunction(o)) {
o = {
hook: o,
prio: 0
};
isString = function(str) {
return typeof str === "string" || str instanceof String;
};
positions = ["init", "before", "during", "after", "end"];
defaultPosition = "during";
setupAction = function(actionName, obj, arg) {
var Promise, action, call, catcher, def, hookIn, names;
catcher = arg["catch"], names = arg.names, def = arg["default"], Promise = arg.Promise;
if ((catcher != null) && !isFunction(catcher)) {
catcher = catcher[actionName];
}
if (!(((o != null ? o.hook : void 0) != null) && (o.prio != null))) {
throw new Error("a hook needs a 'hook' and a 'prio' property");
}
arr.push(o);
return exec = null;
};
getExec = () => {
var reduced;
reduced = (arr.reduce(((acc, current) => {
var name1, tmp;
tmp = acc[name1 = current.prio] != null ? acc[name1] : acc[name1] = [];
tmp.push(current.hook.bind(obj));
return acc;
}), [])).map((hooks) => {
if (hooks.length === 1) {
return hooks[0];
} else {
return (o) => {
return Promise.all(hooks.map((hook) => {
return hook(o);
}));
};
call = function(o) {
var promise;
if (o == null) {
o = obj;
}
});
return exec = async(o) => {
var callHooks, i, len, results;
results = [];
for (i = 0, len = reduced.length; i < len; i++) {
callHooks = reduced[i];
results.push((await callHooks(o)));
promise = (action._chain.reduce((function(lastPromise, hooks) {
if (hooks.length === 1) {
return lastPromise.then(hooks[0].bind(obj, o, obj));
} else {
return lastPromise.then(function() {
return Promise.all(hooks.map(function(hook) {
return hook.call(obj, o, obj);
}));
});
}
}), Promise.resolve())).then(function() {
return o;
});
if (catcher != null) {
return promise["catch"](catcher);
}
return results;
return promise;
};
hookIn = function(index, cb) {
var base, hookInName, ref, tmp;
if (isFunction(index)) {
cb = index;
index = def;
} else if (!isFunction(cb)) {
ref = index, cb = ref.cb, index = ref.index;
if (index == null) {
index = def;
}
}
if (!((cb != null) && (index != null))) {
hookInName = names.hookIn || "hooking-in";
throw new Error(hookInName + " needs a 'cb' and a 'index'");
}
tmp = (base = action._chain)[index] != null ? base[index] : base[index] = [];
tmp.push(cb);
return function() {
var i;
if (~(i = tmp.indexOf(cb))) {
return tmp = tmp.splice(i, 1);
}
};
};
if (names.hookIn === "") {
action = hookIn;
action[names.call] = call;
} else if (names.call === "") {
action = call;
action[names.hookIn] = hookIn;
} else {
action = {};
action[names.call] = call;
action[names.hookIn] = hookIn;
}
action._chain = [];
action[names.reset] = function() {
return action._chain = [];
};
return action;
};
if (options.register === "") {
hook = register;
hook[options.execute] = execute;
} else if (options.execute === "") {
hook = execute;
hook[options.register] = register;
} else {
hook = {};
hook[options.execute] = execute;
hook[options.register] = register;
}
hook[options.clear] = () => {
hook._hooks = arr = [];
return exec = null;
};
hook._hooks = arr;
return hook;
};
module.exports = (obj, options) => {
var prefix;
if (options == null) {
options = {};
}
if (options.prefix != null) {
prefix = arrayize(options.prefix);
}
if (options.register == null) {
options.register = "register";
}
if (options.execute == null) {
options.execute = "execute";
}
if (options.clear == null) {
options.clear = "clear";
}
return obj.registerHooks = (names) => {
var i, j, k, len, len1, len2, name, prop, results, results1, tmp;
names = arrayize(names);
if (prefix) {
for (i = 0, len = prefix.length; i < len; i++) {
prop = prefix[i];
obj[prop] = {};
module.exports = function(obj, options) {
var action, actionName, actions, def, i, j, k, len, name, names, position, ref, results, spread, tmp, v;
if (options != null) {
if (isString(options) || isArray(options)) {
options = {
actions: options
};
}
if (options.Promise == null) {
options.Promise = Promise;
}
names = options.names != null ? options.names : options.names = {};
if (names.hookIn == null) {
names.hookIn = "hookIn";
}
if (names.call == null) {
names.call = "";
}
if (names.reset == null) {
names.reset = "reset";
}
if (names.position == null) {
names.position = "position";
}
if (options.position != null) {
position = options.position;
} else {
spread = options.spread || 8;
position = {};
ref = options.positions || positions;
for (i = j = 0, len = ref.length; j < len; i = ++j) {
name = ref[i];
position[name] = i * spread;
}
}
if ((def = options["default"]) != null) {
if (isString(def)) {
options["default"] = position[def];
} else {
options["default"] = def;
}
} else {
options["default"] = position[defaultPosition];
}
obj[names.position] = position;
actions = options.actions;
if (isString(actions)) {
actions = [actions];
}
if (isArray(actions)) {
actions = {
"": actions
};
}
results = [];
for (j = 0, len1 = prefix.length; j < len1; j++) {
prop = prefix[j];
tmp = obj[prop];
for (k in actions) {
v = actions[k];
if (k) {
tmp = obj[k] = {};
} else {
tmp = obj;
}
results.push((function() {
var k, len2, results1;
var l, len1, ref1, results1;
ref1 = arrayize(v);
results1 = [];
for (k = 0, len2 = names.length; k < len2; k++) {
name = names[k];
results1.push(tmp[name] = getHook(obj, options));
for (l = 0, len1 = ref1.length; l < len1; l++) {
action = ref1[l];
actionName = k ? k + "." + action : action;
results1.push(tmp[action] = setupAction(actionName, obj, options));
}

@@ -126,11 +173,5 @@ return results1;

return results;
} else {
results1 = [];
for (k = 0, len2 = names.length; k < len2; k++) {
name = names[k];
results1.push(obj[name] = getHook(obj, options));
}
return results1;
}
};
};
}).call(this);
{
"name": "hook-up",
"description": "Create your own hook api with 2-dimensional hooks - better than events or simple hooks",
"version": "0.1.0",
"version": "0.2.0",
"homepage": "https://github.com/paulpflug/",

@@ -22,8 +22,8 @@ "author": {

],
"dependencies": {},
"devDependencies": {
"coffeescript": "^2.0.2",
"script-runner": "^0.1.7",
"mocha": "^4.0.1",
"chai": "^4.1.2"
"chai": "^4.1.2",
"coffeescript": "^2.2.1",
"coffee-cli": "^0.2.1",
"mocha": "^5.0.0",
"script-runner": "^0.1.7"
},

@@ -33,3 +33,4 @@ "keywords": [],

"scripts": {
"build": "coffee --bare --no-header --compile --output lib/ src/*.coffee",
"build": "run-npm build:*",
"build:node": "coffee-cli --no-header --compile --output lib/ src/*.coffee",
"test": "mocha",

@@ -36,0 +37,0 @@ "preversion": "npm run test",

# hook-up
Create your own hook api with 2-dimensional hooks - better than events or simple hooks.
**Speed, functionality, simplicity - choose two.**
This fundamental tradeoff in programming is as true as always.
We are just better at hiding complexity - regular severe security flaws are a reminder of what we already hid away.
The only way we can improve this tradeoff is *clever program design*.
Over the last few years of programming I produced two very helpful guidelines
- aim for declarative programming
- separate by functionality
(please note the absence of object orientated programming - which is *often* increasing complexity without any gain)
## Aim for declarative programming
Make your programs work with configuration files - the most common type of declarative programming.
They can be easily read, merged, diffed and shared.
Common settings of different projects can be easily extracted and maintained in one place.
I created [read-conf](https://github.com/paulpflug/read-conf) as a powerful configuration reader with watching and plugin functionality.
## Separation by functionality
Separation by functionality greatly improves extendability and understandability - thus maintainability.
You probably experienced the need for a major refactoring or even a complete rewrite at least once. And you will remember the large impact this had on your project - This happens when functionality isn't separated properly.
I'm using two design pattern for different types of programms:
- user interface: mixins (used in [cerijs](https://github.com/cerijs/ceri))
Each functionality is encapsulated in one mixin.
A user interface component and each mixin can depend on other mixins.
You need one main merging algorithm to resolve the dependency tree and merge all functionality into your ui-component.
- processing programs: plugins and actions (used in [leajs](https://github.com/leajs/leajs) or [snapy](https://github.com/snapy/snapy))
Think of an action as an 2d array of callbacks where a state can progress through.
Each callback only interacts with the current (action and/or program) state.
```
# cb2 and cb3 will be called simultaneously but only after cb1 is finished
# there is empty space where plugins could hook in more cbs
{actionState, programState} -> [cb1, , , , [cb2, cb3], , ,] -> {actionState}
```
A plugin can hook in in those actions on any position.
This package is an action builder:
### Install

@@ -13,24 +61,45 @@ ```sh

hookUp = require("hook-up")
program = {
config: {}
}
// hookUp(obj:Object, options:Object)
someObj = {}
hookUp(someObj)
// adds a function:
// registerHooks(names:String | Array of Strings)
someObj.registerHooks("start")
// adds functions
// to add a hook
// [name].register({prio: (optional) Number, cb: Function})
// to call all hooks
// [name].execute(obj)
// to delete all hooks
// [name].clear()
someArg = {}
someHook = async (arg) =>
// this == someObj
// arg == someArg
// do something async
someObj.start.register(someHook)
someObj.start.register({prio: 2, cb: someHook}) // bigger prio gets called first
// hooks with the same prio get called simultaneously
await someObj.start.execute(someArg)
hookUp(program,{
actions: {
{"": "run"},
{"cache": ["get", "set"]}
},
catch: {
"cache.get": (e) => { console.error(e) }
}
})
// hookIn([position:Number], cb:Function)
// position defaults to program.position.during
// (see below)
program.run.hookIn((state,{config}) => {
// config equals program.config
// it is recommend to test the state if you depend on it
// as you have no idea what the previous cbs did
if (// is in correct state) {
// doSomething with state
}
// you don't need to return anything, each cb will be
// called with the current action and program state
// if you return a promise or the cb is async
// the next cb will only get called afterwards
})
result = await program.run({})
// remove all cbs
program.run.reset()
// program.position
// with a default spread of 8
// contains the predefined positions:
// {init: 0,before: 8, during: 16, after: 24, end: 32}
program.run.hookIn(program.position.init, (state) => {
// init state somehow
})
```

@@ -40,25 +109,31 @@ #### Options

---:| --- | ---| ---
prefix | Array or String | - | to add a namespace for all hooks
register | String | `register` | name of the register prop, can be empty
execute | String | `execute` | name of the execute prop, can be empty
actions | String or Array or Object | - | name of the actions - maximum 1 depth
catch | Object | - | lookup object to apply default catch functions to actions
spread | Number | 8 | distance between the predefined positions
position | Object | - | lookup object to use as predefined positions
Promise | Object | native Promise | Promise lib to use
names | Object | - | see below
Note, that only one of register or execute can be empty
you can change the default names:
```js
// available: "hookIn", "reset", "position", "call"
hookUp(program = {},{
actions: "run",
names:{
hookIn: "", // only one of hookIn or call can be empty
reset: "clear",
position: "pos",
call: "call"
}
})
### Example for custom API
```js
hookUp = require("hook-up")
someObj = {}
hookUp(someObj,{prefix:["before","after"], register: "call", execute:""})
someObj.registerHooks("start")
// will result in the following api for hooks:
someObj.before.start.call(someHook)
someObj.after.start.call(someOtherHook)
// to execute
await someObj.before.start(someArg)
program.run(program.pos.init,(state)=>{
// do something
})
result = await program.run.call({})
program.run.clear()
```
## License
Copyright (c) 2017 Paul Pflugradt
Licensed under the MIT license.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc