Skip to content

Commit

Permalink
update engine prop according to new npm format
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewKovalenko committed Nov 12, 2018
2 parents 9b75df5 + 7645a0e commit e86fee1
Show file tree
Hide file tree
Showing 7 changed files with 548 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
language: node_js,
language: node_js
install: npm install
script: npm run lint && npm test && npm run build
after_script:
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Minimalistic JavaScript utils library intended to replace *underscore* / *lodash

## Why?

Most methods people usually use from *underscore* or *lodash* already implemented natively in ES5 / ES6, but lots of developers keep pulling those monsters increasing size of their output bundles, polluting their code with purely readable `_` characters and slowing down their project srarting time.
Most methods people usually use from *underscore* or *lodash* already implemented natively in ES5 / ES6, but lots of developers keep pulling those monsters increasing size of their output bundles, polluting their code with purely readable `_` characters and slowing down their project starting time.

As for those methods which are still missing in "native" JS API - *underscore* / *lodash* provides pretty ugly API. For example, which code does look better / more readable: `const lone = _.clone(obj)` or `const clone = obj.deepCopy()`?

Expand All @@ -53,7 +53,7 @@ So my intention is to extend "native" JS API with methods we often find missing.

`<array>.last` - is a readonly property which returns last element of an array or `undefined` if array is empty

`<array>.empty()` - returns Bool `true` if array is empty, otherwice returns `false`
`<array>.empty()` - returns Bool `true` if array is empty, otherwise returns `false`

`<array>.deepCopy()` - returns a [deep copy](https://en.wikipedia.org/wiki/Object_copying) of an array

Expand All @@ -78,3 +78,8 @@ So my intention is to extend "native" JS API with methods we often find missing.

### Math
`Math.roundTo(number, decimals)` - rounds float point number to specified decimals using common mathematical rounding rules

### Function
`<any function>.throttle(rate, [optionalContext])` - [throttles](https://en.wikipedia.org/wiki/Throttling_process_(computing)) function at `rate`. Other words, prevent function to be called more frequent then `rate` parameter specifies. `optionalContext` - may be used to pass `this` to throttled function.

`<any function>.debounce(wait, [optionalContext])` - "debounces" function call. Other words, it makes sure that actual call is made at least after `wait` milliseconds after last call attempt, preventing multiple frequent calls. `optionalContext` - may be used to pass `this` to debounced function.
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vertibar",
"version": "0.0.16",
"version": "0.0.23",
"description": "Minimalistic JS utils library with a human face",
"scripts": {
"lint": "eslint ./src && eslint ./tests",
Expand All @@ -12,7 +12,7 @@
"dev": "webpack --progress --colors --watch",
"test": "nyc ava",
"prepublishOnly": "npm run build && standard-version",
"postpublish": "git remote add origin-pages https://${GITHUB_TOKEN}@github.com/AndrewKovalenko/vertibar.git > /dev/null 2>&1 && git push --follow-tags --set-upstream origin-pages master",
"postpublish": "git remote add origin-pages https://${GITHUB_TOKEN}@github.com/AndrewKovalenko/vertibar.git > /dev/null 2>&1 && git push --tags --set-upstream origin-pages master",
"install": "npm build",
"run-dev": "webpack --progress --colors --watch"
},
Expand All @@ -29,9 +29,11 @@
"object extensions",
"tail",
"head",
"deepCopy"
"deepCopy",
"throttle",
"debounce"
],
"engine": "node >= 6.9.2",
"engines": { "node" : ">= 6.9.2" },
"ava": {
"require": "babel-register",
"failFast": true,
Expand All @@ -57,7 +59,7 @@
"eslint-plugin-react": "^7.2.1",
"npm-run-all": "^4.1.2",
"nyc": "10.3.2",
"sinon": "^4.1.3",
"sinon": "^4.2.2",
"standard-version": "^4.3.0",
"uglify-js": "^2.8.0",
"uglifyjs-webpack-plugin": "^0.4.3",
Expand Down
38 changes: 38 additions & 0 deletions src/extensions/function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Function.prototype.debounce =
Function.prototype.debounce || function debounce(wait, context) {
if (typeof wait === 'undefined') {
throw new Error('Missing debounce waiting period.');
}

let timeoutId;
const self = this;

return (...callArguments) => {
const callContext = context || this;
if (timeoutId) {
clearTimeout(timeoutId);
}

timeoutId = setTimeout(() => self.apply(callContext, callArguments), wait);
};
};

Function.prototype.throttle =
Function.prototype.throttle || function throttle(rate, context) {
if (typeof rate === 'undefined') {
throw new Error('Missing throttle rate.');
}

let throttled;
const self = this;

return (...callArguments) => {
const callContext = context || this;

if (!throttled) {
self.apply(callContext, callArguments);
throttled = true;
setTimeout(() => { throttled = false; }, rate);
}
};
};
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ import './extensions/array';
import './extensions/string';
import './extensions/object';
import './extensions/math';
import './extensions/function';
113 changes: 113 additions & 0 deletions tests/function.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import test from 'ava';
import { spy, useFakeTimers } from 'sinon';
import '../src';

const THROTTLE_DELAY = 200;
const CALL_REPEATING_INTERVAL = 100;
const LITTLE_BIT_LATER = 10;
const NUMBER_OF_DESIRED_CALLS = 3;

let fakeClock;

test.before(() => {
fakeClock = useFakeTimers();
});


const makeCalls = async (mockFunction, mockCallArguments) => {
const result = new Promise((resolve) => {
let callsAttempts = 0;

const intervalId = setInterval(() => {
callsAttempts++;
mockFunction(...mockCallArguments);

if (callsAttempts >= NUMBER_OF_DESIRED_CALLS) {
clearInterval(intervalId);
fakeClock.tick(THROTTLE_DELAY + LITTLE_BIT_LATER);
resolve();
}
}, CALL_REPEATING_INTERVAL);
});

fakeClock.tick((NUMBER_OF_DESIRED_CALLS * CALL_REPEATING_INTERVAL) + LITTLE_BIT_LATER);

return result;
};

test('Function debounce', async (t) => {
const mockFunction = spy();
const mockContext = {};
const mockCallArguments = [1, 2, 3];

const throtteledMock = mockFunction.debounce(THROTTLE_DELAY, mockContext);
await makeCalls(throtteledMock, mockCallArguments);

t.true(mockFunction.calledOnce);
t.true(mockFunction.calledWith(...mockCallArguments));
t.true(mockFunction.calledOn(mockContext));
});

test('Function debounce uses initial caller context by default', async (t) => {
const mockFunction = spy();
const mockContext = {};
const mockCallArguments = [1, 2, 3];

const throtteledMock = mockFunction.debounce(THROTTLE_DELAY);
const artificialContextCall = makeCalls.bind(mockContext);
await artificialContextCall(throtteledMock, mockCallArguments);

t.true(mockFunction.calledOnce);
t.true(mockFunction.calledWith(...mockCallArguments));
t.true(mockFunction.calledOn(mockFunction));
});

test('Function debounce throws an error if there is no waiting parameter passed', async (t) => {
const mockFunction = spy();
const error = t.throws(() => {
mockFunction.debounce();
}, Error);

t.is(error.message, 'Missing debounce waiting period.');
});


test('Function throttle', async (t) => {
const mockFunction = spy();
const mockContext = {};
const mockCallArguments = [1, 2, 3];
const DELAY_FOR_TWO_CALLS = THROTTLE_DELAY / 2;
const throttleMock = mockFunction.throttle(DELAY_FOR_TWO_CALLS, mockContext);
await makeCalls(throttleMock, mockCallArguments, CALL_REPEATING_INTERVAL);

t.is(mockFunction.callCount, 2, 'Function was not throtteled as expected');
t.true(mockFunction.calledWith(...mockCallArguments));
t.true(mockFunction.calledOn(mockContext));
});


test('Function throttle uses initial caller context by default', async (t) => {
const mockFunction = spy();
const mockContext = {};
const mockCallArguments = [1, 2, 3];
const throttleMock = mockFunction.throttle(THROTTLE_DELAY);
const artificialContextCall = makeCalls.bind(mockContext);
await artificialContextCall(throttleMock, mockCallArguments, CALL_REPEATING_INTERVAL);

t.true(mockFunction.calledOnce);
t.true(mockFunction.calledWith(...mockCallArguments));
t.true(mockFunction.calledOn(mockFunction));
});

test('Function throttle throws an error if there is no waiting parameter passed', async (t) => {
const mockFunction = spy();
const error = t.throws(() => {
mockFunction.throttle();
}, Error);

t.is(error.message, 'Missing throttle rate.');
});

test.after(() => {
fakeClock.restore();
});

0 comments on commit e86fee1

Please sign in to comment.