npm package: @keepzen/pipe.js
Description: pipeline for Js
Author: Keep Zen
Latest version: 2.1.0

![img](//img.shields.io/npm/v/@keepzen/pipe.js.svg)
![img](//img.shields.io/npm/dm/@keepzen/pipe.js.svg)
![img](//img.shields.io/npm/dt/@keepzen/pipe.js.svg)

<div id="table-of-contents">
<h2>Table of Contents</h2>
<div id="text-table-of-contents">
<ul>
<li><a href="#sec-1">1. Pipe</a>
<ul>
<li><a href="#sec-1-1">1.1. Make a pipable</a></li>
<li><a href="#sec-1-2">1.2. Use placeholder set which argument of <code>fun</code> take the piped value</a></li>
<li><a href="#sec-1-3">1.3. <code>pipable.pipe(fun,...args)</code> keep <code>pipable</code> object immutable.</a></li>
<li><a href="#sec-1-4">1.4. <code>pipable.pipe(fn,...args)</code> is lazy</a></li>
<li><a href="#sec-1-5">1.5. <code>pipable&lt;Promise&gt;.pipe(fn,...args)</code></a></li>
<li><a href="#sec-1-6">1.6. <code>pipable&lt;Promise&gt;.promise_rescue</code>:</a></li>
<li><a href="#sec-1-7">1.7. <code>pipable.inspect(fn, ...args)</code></a></li>
</ul>
</li>
</ul>
</div>
</div>

# Pipe

## Make a pipable

Pipable is a object, which have a method named `.pipe(fun,...args)`.

You can get a new pipable object as fellow ways:

    const {
      Pipe,
      pipe,
      from,
      _
    } = require('./pipe.js')
    
    test('make pipable', () => {
      const properties = ['valueOf', 'pipe', 'map', "promise_rescue"].sort();
      const keys = Object.keys.bind(Object);
      expect(keys(Pipe(1)).sort()).toMatchObject(properties);
      expect(keys(pipe(1)).sort()).toMatchObject(properties);
      expect(keys(from(1)).sort()).toMatchObject(properties);
      expect(keys(from(1).pipe(a => a + 1)).sort()).toMatchObject(properties);
    })

As then snippet showed, a `pipable` object have methods as fellow:
-   `pipable.valueOf()` : 
    
    Get the `v` you want to pipe to other function.
    
    So `pipe(a).valueOf() === a` is always `true`:

    test('pipe(any).valueOf() == any', () => {
      expect(pipe(1).valueOf() == 1).toBe(true)
      expect(pipe("hello").valueOf() == "hello").toBe(true);
      expect(pipe(true).valueOf() == true).toBe(true);
      expect(pipe(false).valueOf() == false).toBe(true);
      let str = new String('Hello');
      expect(pipe(str).valueOf() == str).toBe(true);
      expect(pipe(null).valueOf() == null).toBe(true);
      expect(pipe(undefined).valueOf() == undefined).toBe(true);
      let a = { hello: "a" };
      expect(pipe(a).valueOf() == a).toBe(true)
    })

</code>

If `a` is primary type but neither `null` nor `undefined` then `pipe(a) == a`
is true.

    test('pipe(primary) == primary', () => {
      expect(pipe(1) == 1).toBe(true)
      expect(pipe("hello") == "hello").toBe(true);
      expect(pipe(true) == true).toBe(true);
      expect(pipe(false) == false).toBe(true);
      let str = new String('Hello')
      expect(pipe(str) == str).toBe(false);
      expect(pipe(null) == null).toBe(false);
      expect(pipe(undefined) == undefined).toBe(false);
      let a = { hello: "a" };
      expect(pipe(a) == a).toBe(false);
    })

-   `pipable.pipe(fun,...args)` : Return a pipable object.
    
    `fun` is a function, `args` are the rest arguments you want pipe to `fun`.
    
    -   If `args` do not included placeholder `_`, `fun` will be called as
        `fun(...[v,...args])`.
    
    -   If `args` included one and only one placeholder `_`, `pipable.valueOf()` will 
        instead of the placehoder, and `fun` will be called as 
        `fun(...args)`.

-   `pipable.map(fun,...args)` : `pipable.map` is the alias of `pipable.pipe`.
-   `pipable.promise_rescue(fun,...args)`: return a new pipable object.
    -   when the `pipable.vlaueOf()` is a promise and the promise is reject, 
        the reject reason will pipe to `fun`.
    -   when `pipeable.valueOf()` is not a promise, `fun` will been skiped and return 
        copy of `pipable`.

## Use placeholder set which argument of `fun` take the piped value

    test('pipe with placeholder', () => {
      let fn = jest.fn((a, b, c) => a + b + c);
      let z = pipe('a').pipe(fn, 'b', 'c');
      expect(fn).not.toHaveBeenCalled();
      expect(z == 'abc').toBe(true);
      expect(fn).toBeCalledWith('a', 'b', 'c');
      z = pipe("2").pipe(fn, "1", _, "3");
      expect(z == '123').toBe(true);
      expect(fn).toBeCalledWith('1', '2', '3');
      z = pipe('e').pipe(fn, 'a', 'b', 'c', 'd', _);
      expect(z == 'abc');
      expect(fn).toHaveBeenLastCalledWith('a', 'b', 'c', 'd', 'e');
    })

## `pipable.pipe(fun,...args)` keep `pipable` object immutable.

`pipable.pipe(fn,...args)` will return a new pipable object,don't change the old one.

    test('pipable is independ', () => {
      let a = 1;
      let fn1 = jest.fn(a => a + 1);
      let fn2 = jest.fn((b, c) => b + c);
      let pipable0 = pipe(a);
      let pipable1 = pipable0.pipe(fn1);
      expect(pipable1 == a + 1).toBe(true);
      expect(pipable0.valueOf()).toBe(a);
      let pipable2 = pipable1.pipe(fn2, 'c');
      expect(pipable2 == '2c').toBe(true);
      expect(pipable1.valueOf()).toBe(a + 1);
    })

## `pipable.pipe(fn,...args)` is lazy

Call `pipeable.pipe(fn,...args)` do not call `fn`,
but `pipable.valueOf()`  will call the `fn` if there have one.
As fellow show:

    test(`pipable is layze`, () => {
      const fn = jest.fn(a => a + 1);
      let pipable0 = pipe(1);
      let ret = pipable0.pipe(fn).pipe(fn).pipe(fn);
      expect(fn).not.toHaveBeenCalled();
      expect(ret.valueOf()).toBe(1 + 1 + 1 + 1);
      expect(fn.mock.calls).toMatchObject([[1], [2], [3]]);
    })

## `pipable<Promise>.pipe(fn,...args)`

If the value of a pipable is a `Promise`, 
`pipable.pipe(fn)` pipe the resolve value of the promise to `fn`,
and return a new promise pipable.

### Examaples

    test('pipe promise resolve ', (done) => {
      let a = from(Promise.resolve(1));
      expect(a.valueOf()).toBeInstanceOf(Promise);
      let fn = jest.fn(a => a + 1);
      let ret = a.pipe(fn).
        promise_rescue(fn).//skip
        pipe(fn).valueOf();
      expect(ret).toBeInstanceOf(Promise);
      ret.then(() => {
        expect(fn.mock.calls).toMatchObject([[1], [2]]);
        done();
      });
    })

## `pipable<Promise>.promise_rescue`:

Return a new pipable<Promise>.

If value of `pipable<Promise>` is reject, this method can rescue from the error.

    test('pipe promise reject', (done) => {
      let a = from(Promise.reject('hello'));
      let fn1 = jest.fn(a => 'fn1');
      let fn2 = jest.fn(a => 'fn2');
      let fn3 = jest.fn(a => "fn3");
      let promise = a.pipe(fn1).//skip
        promise_rescue(fn2).//call
        pipe(fn3).valueOf();
      expect(promise).toBeInstanceOf(Promise);
    
      promise.then(
        (a) => {
          expect(fn1).not.toHaveBeenCalled();
          expect(fn2).toHaveBeenCalledWith('hello');
          expect(fn3).toHaveBeenCalledWith('fn2');
          expect(a).toBe('fn3');
          done();
        },
        (error) => {
          console.error(error);
          expect('not run to here').toBe('but it is here');
        }
      )
    })

## `pipable.inspect(fn, ...args)`

Return a new `pipable`.

Run then function `fn`, ignor the return value of `fn`, so the value of the pipable will keep not change.

The purpose of this method is helpfully debuging the pipeable chain.