Skip to content

Commit

Permalink
chore: Add documentation and finalize initial API (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
daffl authored Jan 5, 2020
1 parent 4d5af76 commit 0cf6ce2
Show file tree
Hide file tree
Showing 7 changed files with 495 additions and 173 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"packages": [
"packages/*"
],
"version": "0.0.1-alpha.0",
"version": "0.0.0",
"command": {
"bootstrap": {
"hoist": true
Expand Down
121 changes: 2 additions & 119 deletions packages/hooks/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Hooks

[![Build Status](https://travis-ci.org/feathersjs/feathers.png?branch=master)](https://travis-ci.org/feathersjs/feathers)
[![CI GitHub action](https://github.com/feathersjs/hooks/workflows/Node%20CI/badge.svg)](https://github.com/feathersjs/hooks/actions?query=workflow%3A%22Node+CI%22)
[![Dependency Status](https://img.shields.io/david/feathersjs/feathers.svg?style=flat-square&path=packages/hooks)](https://david-dm.org/feathersjs/feathers?path=packages/hooks)
[![Download Status](https://img.shields.io/npm/dm/@feathersjs/hooks.svg?style=flat-square)](https://www.npmjs.com/package/@feathersjs/hooks)

Expand All @@ -12,126 +12,9 @@
npm install @feathersjs/hooks --save
```

## Quick Example

The following example logs the runtime of a function:

```js
const hooks = require('@feathersjs/hooks');
const logRuntime = async (ctx, next) => {
const start = new Date().getTime();

await next();

const end = new Date().getTime();

console.log(`Function call took ${end - start}ms`);
}

const sayHello = hooks(async message => {
return `Hello ${message}!`;
}, [ logRuntime ]);

console.log(await sayHello('David'));
```

## Documentation

### Hook functions

Hook functions take a context `ctx` and an asynchronous `next` function as their argument. The control flow is the exact same as in [KoaJS](). A hook function can do things before calling `await next()` and after all other hooks and the function call returned:

```js
const hooks = require('@feathersjs/hooks');

const sayHello = async message => {
return `Hello ${message}!`;
};

const logMethod = async (ctx, next) => {
console.log('Before calling method');
console.log('Arguments are', ctx.arguments);

await next();

console.log('Method called');
console.log('Result is', ctx.result);
}

const hookedSayHello = hooks(sayHello, [ logMethod ]);
```

### Context

- `ctx.arguments` - The arguments of the function as an array
- `ctx.method` - The name of the function (if it belongs to an object)
- `ctx.this` - The `this` context of the function being called

### Decorators

Hooks can also be attached through [ES expiremental](https://babeljs.io/docs/en/babel-plugin-proposal-decorators) or TypeScript decorators:

```js
import hooks from '@feathersjs/hooks';

class App {
@hooks([ logMethod ])
async sayHi (name) {
return `Hello ${name}`;
}
}
```

## More Examples

### Cache results

The following example is a simple hook that caches the results of a function call and uses the cached value if available. This is useful e.g. for external Ajax requests:

```js
const hooks = require('@feathersjs/hooks');
const cache = () => {
const cacheData = {};

return async (ctx, next) => {
const key = JSON.stringify(ctx);

if (cacheData[key]) {
ctx.result = cacheData[key];

return next();
}

await next();

cacheData[key] = ctx.result;
}
}

const getData = hooks(async url => {
return axios.get(url);
}, [ cache() ]);

await getData('http://url-that-takes-long-to-respond');
```

### Decorators

```js
import hooks from '@feathersjs/hooks';

class Api {
@hooks([
logRuntime,
cache()
])
async getMessages () {
const { data } = axios.get('http://myserver.com/users');

return data;
}
}
```
See [feathersjs/hooks](https://github.com/feathersjs/hooks/) for the complete documentation.

## License

Expand Down
4 changes: 3 additions & 1 deletion packages/hooks/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ export function getMiddleware (target: any): Middleware[] {
/**
* The base hook context.
*/
export class HookContext<T = any> {
export class HookContext<T = any, C = any> {
result?: T;
method?: string;
self: C;
arguments: any[];
[key: string]: any;

Expand Down
2 changes: 1 addition & 1 deletion packages/hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function hooks<F, T = any> (
export function hooks (...args: any[]) {
const [ target, _hooks, ...rest ] = args;

if (Array.isArray(_hooks)) {
if (Array.isArray(_hooks) && typeof target === 'function') {
return functionHooks(target, _hooks, ...rest);
}

Expand Down
10 changes: 8 additions & 2 deletions packages/hooks/src/object.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Middleware } from './compose';
import { functionHooks } from './function';
import { ContextUpdater, HookContext, withParams } from './base';
import { ContextUpdater, HookContext, withParams, registerMiddleware } from './base';

export interface MiddlewareMap {
[key: string]: Middleware[];
Expand All @@ -10,9 +10,15 @@ export interface ContextUpdaterMap {
[key: string]: ContextUpdater;
}

export const objectHooks = (_obj: any, hookMap: MiddlewareMap, contextMap?: ContextUpdaterMap) => {
export const objectHooks = (_obj: any, hooks: MiddlewareMap|Middleware[], contextMap?: ContextUpdaterMap) => {
const obj = typeof _obj === 'function' ? _obj.prototype : _obj;

if (Array.isArray(hooks)) {
return registerMiddleware(obj, hooks);
}

const hookMap = hooks as MiddlewareMap;

return Object.keys(hookMap).reduce((result, method) => {
const value = obj[method];
const hooks = hookMap[method];
Expand Down
20 changes: 20 additions & 0 deletions packages/hooks/test/object.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,24 @@ describe('objectHooks', () => {

assert.strictEqual(await instance.sayHi('David'), 'Hi David?!');
});

it('works with object level hooks', async () => {
hooks(obj, [
async (ctx: HookContext, next: NextFunction) => {
await next();

ctx.result += '!';
}
]);

hooks(obj, {
sayHi: [async (ctx: HookContext, next: NextFunction) => {
await next();

ctx.result += '?';
}]
});

assert.equal(await obj.sayHi('Dave'), 'Hi Dave?!');
});
});
Loading

0 comments on commit 0cf6ce2

Please sign in to comment.