JSPM

  • Created
  • Published
  • Downloads 1480
  • Score
    100M100P100Q110424F

Collection of Node.js / ECMAScript Gists

Package Exports

  • jinang
  • jinang/Directory
  • jinang/JsonFile
  • jinang/PoC
  • jinang/Progress
  • jinang/absorb
  • jinang/cloneObject
  • jinang/defineError
  • jinang/forInObject
  • jinang/jointString
  • jinang/modifyUrl
  • jinang/ott
  • jinang/papply
  • jinang/parseOptions
  • jinang/safeClone
  • jinang/sleep
  • jinang/split

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (jinang) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

jinang

Collection of Node.js / ECMAScript Mini Modules

coverage status of github.com/YounGoat/jinang build status of github.com/YounGoat/jinang total downloads of jinang jinang's License latest version of jinang

The name jinang is abbreviation of "Jin-Nang", which in chinese means a magic box. The modules in jinang are independent for each other, and are for different usage.

jinang is an incubator. Successful sub modules may be encouraged to be published as independent NPM packages.

Table of contents

Get Started

// Modules are independent for each other and are suggested to be required independently.
const defineError = require('jinang/defineError');

const MyError = defineError('MyError', Error, function(message) {
    this.code = 'SOMETHING_IS_WRONG';
    this.message = message;
});

// ...
throw new MyError('Helo word!');

API

All sub-modules in jinang are independent from each other. Use require('jinang/<subModuleName>') to require the sub-modules.

For your convenience, avaiable modules included in jinang are listed here,

absorb

  • number absorb( Array foo, Array bar )

cloneObject

  • Object cloneObject( object source )

  • Object cloneObject( object source, Array keynames )
    Item of keynames may be string or RegExp instance. If item is a string, key in source which strictly equals to the keyname will be cloned. However, if it is a RegExp instance, all keys in source will be iterated and those matched will be cloned.

  • Object cloneObject( object source, Function mapper )
    The mapper SHOULD be a function who accepts two arguments (key, value) and return an array [ newKey, newValue ].

currying

Transform a normal function to a curried one.

  • curried Function currying( Function fn, number paramsLength )
const currying = require('jinang/currying');

function plus(a, b, c) {
    return a + b + c;
}

// Create a curried function.
// 3 means that the original function plus() should be invoked with 3 arguments.
var curriedPlus = currying(plus, 3);

// Curried function accepts one and only one argument.
// It may return a new curried function, or the result returned by the original function.
curriedPlus(1);       // RETURN a new function.
curriedPlus(1)(2);    // RETURN a new function.
curriedPlus(1)(2)(3); // RETURN 6.

ATTENTION: Although Currying and Partial Application are mutually related, they are DIFFERENT. If you wanna create a new function which is based on an existing function and with some arguments predefined, use papply.

Read more about currying:

defineError

  • Function defineError( string ErrorName, Function ParentErrorClass, Function constructor )

E.g.

// Modules are independent for each other and are suggested to be required independently.
const defineError = require('jinang/defineError');

const MyError = defineError('MyError', Error, function(message) {
    this.code = 'SOMETHING_IS_WRONG';
    this.message = message;
});

// ...
throw new MyError('Helo word!');

Directory

  • class Directory( string homepath )
  • void <instance>.append ( string filename, string | Buffer data )
  • ReadStream <instance>.createReadStream ( string filename [, object options ] )
  • WriteStream <instance>.createWriteStream ( string filename [, object options ] )
  • boolean <instance>.exists ( string filename )
  • void <instance>.mkdir ( string pathname )
  • number <instance>.open ( string pathname, string flags )
  • void <instance>.read ( string filename, string | Buffer encoding )
  • string <instance>.resolve( string filename )
  • void <instance>.rmfr ( string filename )
  • void <instance>.write ( string filename, string | Buffer data )

forInObject

  • void forInObject( object foo, Function iterator )
    Function iterator SHOULD accept two arguments.

E.g.

const forInObject = require('jinang/forInObject');
forInObject({ name: 'YounGoat' }, (name, value) {
    console.log(name);
    console.log(value);
});

jointString

  • string jointString( string joint, string str_1, string str_2, ...)
    Concatenate strings with joint. If the end of the front string or the beginning of the rear one equals to joint, it will be trimmed before being concatenated.

E.g.

const jointString = require('jinang/jointString');
jointString('/', 'a/', '/b');
jointString('/', 'a' , '/b');
jointString('/', 'a/',  'b');
jointString('/', 'a' ,  'b');
// All return 'a/b'.

JsonFile

  • class JsonFile( string pathname )
  • object <instance>.json
    Handle to read/write JSON data.
  • void <instance>.save()
  • void <instance>.remove()

modifyUrl

  • string modifyUrl( string urlname, Object options )
  • string modifyUrl.pathname( string urlname, string pathname, char flag )
  • string modifyUrl.protocol( string urlname, string protocol )
  • string modifyUrl.query( string urlname, string|Object query, char flag )

ordinal

Acquire the ordinal form of a natural number, e.g. 1st for 1, 22nd for 22.

const ordinal = require('jinang/ordinal');

ordinal(1);
// RETURN 1st

ordinal(101);
// RETURN 101st

ordinal.suffix(22);
// RETURN nd

ordinal(0);
// throws Error
  • string ordinal(number | string n) throws
    Return the ordinal form of a natural number.
    If n is not a natural number, an Error will be thrown.

  • string oridnal.suffix(number | string n) throws
    Return only the ordinal suffix.
    If n is not a natural number, an Error will be thrown.

ott

ott is abbreviation of Once, Twice, Thrice.

const ott = require('jinang/ott');

let n = 0;
function fn() {
    return n++;
};

let fn3 = ott(fn, 3);

fn3(); // n == 1; RETURN 0
fn3(); // n == 2; RETURN 1
fn3(); // n == 3; RETURN 2

// n will not change more by fn3
fn3(); // n == 3
fn3(); // n == 3

fn3.runtimes(); // RETURN 5
  • Function ott(Function fn, number n)
  • Function ott(Function fn, number n, any returnOnExceeding)
  • Symbol ott.FIRST
  • Symbol ott.LAST
  • Function ott.once(Function fn)
  • Function ott.twice(Function fn)
  • Function ott.thrice(Function fn)

ott() returns a wrapper of the provided function. When the returned function has been invoked for n times, what it returns on next calls depends on the parameter returnOnExceeding:

  • If ott.FIRST used, it will return what it firstly returned.
  • If ott.LAST used, it will return what it returned on n-th call.
  • Otherwise, it will return just returnOnExceeding.

papply

Word "papply" is abbreviation of Partial Application, which means to apply a function to some of its arguments and return a new function with fewer parameters.

papply offers three styles:

  • Function papply(Function fn, any leading_argument, ...)
    Create a new function based on fn with leading arguments predefined by any number of leading_argument.

  • Function papply.tail(Function fn, any tailing_argument, ...)
    Create a new function based on fn with tailing arguments predefined by any number of tailing_argument. The last tailing_argument will also be the last argument finally passed into fn.

  • Function papply.position(Function fn, [ number position, any value ] positioned_argument, ...)
    Create a new function based on fn with some position(s) of argument list predefined by any number of positioned_argument.
    The final arguments passed into fn will be the shortest list which contains all positioned arguments and real arguments. Sometimes the final arguments will be tailed with one or more undefined.
    If position is 0 or positive integer, it indicates the position from left (starts with 0). Otherwise, if position is negative integer, it indicates the position from right (starts with -1).

const papply = require('jinang/papply');

// -- papply --

// Suppose a function with three parameters.
function add(a, b, c) {
    return a + b + c;
}

// Run partial application and return a new function.
const add2 = papply(add, 2);
add2(3, 4); // RETURN 2 + 3 + 4

const add2_3 = papply(add, 2, 3);
add2_3(4); // RETURN 2 + 3 + 4

// -- papply.tail --

function minus(a, b, c) {
    return a - b*1 - c*2;
}

const minus2 = papply.tail(minus, 2);
minus2(9, 3); // RETURN 9 - 3 - 2*2 = 2

const minus2_3 = papply.tail(minus, 2, 3);
minus2_3(9); // RETURN 9 - 2 - 3*2 = 1

// -- papply.position --

function join() {
    let args = Array.from(arguments);
    let chars = args.map(n => typeof n == 'undefined' ? '-' : n);
    return chars.join('');
}

const join_z = papply.position(join, [ -1, 'z' ]);
join_z('a');

Read more about partial application:

parseOptions

To normalise options argument.

const parseOptions = requrie('jinang/parseOptions');

const options = {
    Domain: 'www.example.com',
    Port: 8080,
    path: '/index.html',
};

const def = {
    // Whether the property names of *options* are case sensitive.
    // DEFAULT false
    caseSensitive: false,

    // Whether the property names of returned (parsed) object may keep the case 
    // as what column names look like, even *def.caseSensitive* is false.
    // DEFAULT false
    keepNameCase: false,

    // If set true, properties not explicitly declared will be ignored.
    // DEFAULT false
    explicit: true,

    // Declare properties in *options*.
    columns: [ 
        'port', 
        { name: 'Hostname', alias: 'domain' } 
    ],
};

const po = parseOptions(options, def);
// RETURN {
//     Hostname: 'www.example.com',
//     port: 8080
// }

parseOptions is an easy way to make your function paramemters more friendly.

function formatUrl(options) {
    
    const optionsDef = {
        caseSensitive: false, 
        explicit: true,

        // Property *columns* of definition may be an array or object.
        columns: [
            // Use object to define/declare a column.
            { name: 'protocol', default: 'http' },
            { name: 'query', alias: [ 'querystring', 'queries' ] },

            // Use description string to define/declare a column.
            'hostname required alias(domain, domainName)',

            // Only column name is also OK.
            'port',
            'path',
            'search',
            'hash'
        ]
    };

    options = parseOptions(options, optionsDef);

    // ...
}

A column definition may be an object with following properties:

  • name string
  • alias string | string[]
  • default any OPTIONAL
  • parser Function OPTIONAL
  • required boolean DEFAULT false

PoC

PoC means Promise or Callback. It may be invoked in an asychronuous function to create an return an instance of Promise or to invoke the callback function if passed.

const PoC = require('jinang/PoC');

function asyncDemo(params, callback) {
    return PoC((done) => {
        // ...
        done(error, data);
    }, callback);
};

asyncDemo(params)
    .then((data) => { /* ... */ })
    .catch((error) => { /* ... */ })
    ;

// When callback passed, PoC will return void.
asyncDemo(params, (error, data) => { /* ... */ });

Progress

const Progress = require('jinang/Progress');

const progress = new Progress();

// Progress inherits class events.EventEmitter.
progress.on('error', function(ex) {
    // ...
});
progress.emit('error', ex);

// Progress may send/change its signal via homonymous methods.
progress.on('signal', function(signal) {
    // ...
});
progress.abort();
  • class Progress

  • number Progress.SIGABRT

  • number Progress.SIGHUP

  • number Progress.SIGINT

  • number Progress.SIGKILL

  • number Progress.SIGQUIT

  • number Progress.SIGTERM

  • void <instance>.raise(number signal )

  • void <instance>.signal(number signal, Function catcher)

  • void <instance>.abort()
    Send signal SIGABRT to progress and emit a signal event.

  • void <instance>.hangup()
    Send signal SIGHUP to progress and emit a signal event.

  • void <instance>.interrupt()
    Send signal SIGINT to progress and emit a signal event.

  • void <instance>.kill()
    Send signal SIGKILL to progress and emit a signal event.

  • void <instance>.quit()
    Send signal SIGQUIT to progress and emit a signal event.

  • void <instance>.terminate()
    Send signal SIGTERM to progress and emit a signal event.

promiseRejectionAutoHandle

By default, an instance of Promise without .catch(onReject) will throw an exception while it is rejected. promiseRejectionAutoHandle will catch the exception automatically in such situations.

const handler = (err) => console.log(err);
require('jinang/promiseRejectionAutoHandle')(handler);
// The *handler* is not required

new Promise((resolve, reject) => { /* ... */ })
    .then(data => {
        /* ... */
    })
    ;

Read unit test code for more examples.

safeClone

safeClone is deep and partial clone method. The skeleton of the original argument will be kept and the primitive items or properties will be copied, while the complex values will be ignored. Whatever you do with the returned value, the original will not be affected.

const safeClone = require('jinang/safeClone');

const foo = {
    a: undefined,
    b: null,
    c: 1,              // number
    d: 'foobar',       // string
    e: true,           // boolean
    f: function() {},  // function
    g: [ , , 2, ],     // Array
}

let bar = safeClone(foo);
bar.a === undefined;
bar.b === null;
bar.c === 1;
bar.d === 'foobar';
bar.e === true;
bar.hasOwnProperty('f') === false;
bar.g.length === 3;

// The original will not change when the cloned changed.
bar.g.pop();
foo.g.length === 3;

Read unit test code for more examples.

sleep

Make current process to "sleep" for a while.

const sleep = require('jinang/sleep');

// Block the current process for 2000 milliseconds (2 seconds).
sleep(2000);

In co(function*() { /* ... */ }) code block, sleep.promise() is recommended because it is more precise.

const sleep = require('jinang/sleep');
const co = require('co');

co(function*() {
    yield sleep.promise(2000);
});

split

Split string in different ways.

  • Array split(string s, string | RegExp seperator)
    The first parameter is the string to be split.
    seperator may be a string or a regular expression object.

  • Array split(string s, string | RegExp seperator, string | Array delimiter)
    Substring enbraced by SAME delimiter will not be split. Each delimiter SHOULD be a character (1-length string).

  • Array split(string s, string | RegExp seperator, string | Array delimiter, string escaper)

const split = require('jinang/split');

split('foo bar baz', ' ');
// RETURN: [ "foo", "bar", "baz" ]

split('foo==bar==baz', '==');
// RETURN: [ "foo", "bar", "baz" ]

split('foo "bar baz"', ' ', '"');
// RETURN: [ "foo", "bar baz" ]

split('foo "bar\\"baz"', ' ', '"');
// RETURN: [ "foo", "bar\\"baz" ]

Read unit test code for more examples.

Why jinang

jinang is an incubator for creatives which will make programming with Node.js much easier.

Honorable Dependents

About

References