Skip to content

Remove lodash as a dependency #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ng2-redux lets you easily connect your Angular 2 components with Redux.
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Usage](#usage)
- [A Note about Internet Explorer](#a-note-about-internet-explorer)
- [Cookbooks](#cookbooks)
- [Using Angular 2 Services in your Action Creators](#using-angular-2-services-in-your-action-creators)
- [Using Angular 2 Services in your Middleware](#using-angular-2-services-in-your-middleware)
Expand Down Expand Up @@ -195,6 +196,14 @@ export class App {
}
```

## A Note about Internet Explorer

This library relies on the presence of `Object.assign` and `Array.forEach`.
Internet Explorer requires polyfills for these; however these same functions
are also needed for Angular 2 itself to work. Just make sure you include
[core-js](https://npmjs.com/package/core-js) or [es6-shim](https://npmjs.com/packages/es6-shim)
if you need to support IE.

## Cookbooks

### Using Angular 2 Services in your Action Creators
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
},
"homepage": "https://github.com/angular-redux/ng2-redux#readme",
"devDependencies": {
"@angular/core": "2.0.0-rc.1",
"chai": "^3.5.0",
"expect": "^1.8.0",
"mocha": "^2.4.5",
Expand All @@ -50,15 +51,15 @@
"ts-loader": "^0.8.1",
"ts-node": "^0.5.5",
"typescript": "^1.8.10",
"@angular/core": "2.0.0-rc.1",
"es6-shim": "^0.35.0",
"redux": "^3.4.0",
"reflect-metadata": "0.1.3",
"rxjs": "5.0.0-beta.6",
"typings": "^0.7.4",
"zone.js": "0.6.12"
},
"dependencies": {
"lodash": "^3.10.1",
"typings": "^0.7.4"
"peerDependencies": {
"rxjs": "5.0.0-beta.6",
"@angular/core": "2.0.0-rc.1"
}
}
11 changes: 6 additions & 5 deletions src/___tests___/components/ng-redux.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'reflect-metadata';
import {expect, use} from 'chai';
import 'es6-shim';
import { expect, use } from 'chai';
import { createStore } from 'redux';
import {NgRedux} from '../../components/ng-redux';
import { NgRedux } from '../../components/ng-redux';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';
import * as _ from 'lodash';

use(sinonChai);

function returnPojo() {
Expand Down Expand Up @@ -88,8 +89,8 @@ describe('Connector', () => {
it('Should extend target (object) with actionCreators', () => {
connector.connect(returnPojo,
{ ac1: returnPojo, ac2: () => { } })(targetObj);
expect(_.isFunction(targetObj.ac1)).to.equal(true);
expect(_.isFunction(targetObj.ac2)).to.equal(true);
expect(targetObj.ac1).to.be.a('Function');
expect(targetObj.ac2).to.be.a('Function');
});

it('Should return an unsubscribing function', () => {
Expand Down
1 change: 0 additions & 1 deletion src/___tests___/tests.entry.ts

This file was deleted.

22 changes: 22 additions & 0 deletions src/___tests___/utils/omit.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'reflect-metadata';
import { expect, use } from 'chai';

import { omit } from '../../utils/omit';

describe('omit', () => {
it('should omit the specified properties', () => {
const input = { a: 1, b: 2, c:3, d: 4 };
const output = omit(input, [ 'b', 'd' ]);

expect(output.hasOwnProperty('b')).to.be.false;
expect(output.hasOwnProperty('d')).to.be.false;
expect(output).to.eql({ a: 1, c: 3 });
});

it('should not mutate its input', () => {
const input = { a: 1, b: 2, c:3, d: 4 };
const output = omit(input, [ 'b', 'd' ]);

expect(input).to.eql({ a: 1, b: 2, c:3, d: 4 });
});
});
137 changes: 137 additions & 0 deletions src/___tests___/utils/type-checks.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { expect, use } from 'chai';

import { isObject, isFunction, isPlainObject} from '../../utils/type-checks';

describe('typeChecks', () => {
describe('isObject', () => {
it('should return true for an object literal', () => {
expect(isObject({})).to.be.true;
});

it('should return true for a new Object', () => {
expect(isObject(new Object())).to.be.true;
});

it('should return true for an instance of a class', () => {
class Foo {};
expect(isObject(new Foo())).to.be.true;
});

it('should return false for null', () => {
expect(isObject(null)).to.be.false;
});

it('should return false for undefined', () => {
expect(isObject(undefined)).to.be.false;
});

it('should return false for a function', () => {
expect(isObject(function foo() {})).to.be.false;
});

it('should return false for an arrow function', () => {
expect(isObject(() => undefined)).to.be.false;
});

it('should return false for a constructor function', () => {
class Foo {};
expect(isObject(Foo)).to.be.false;
});

it('should return false for a string', () => {
expect(isObject('foo')).to.be.false;
});

it('should return false for a number', () => {
expect(isObject(1)).to.be.false;
});
});

describe('isFunction', () => {
it('should return true for a function', () => {
expect(isFunction(function () {})).to.be.true;
});

it('should return true for an arrow function', () => {
expect(isFunction(() => undefined)).to.be.true;
});

it('should return true for a constructor function', () => {
class Foo {};
expect(isFunction(Foo)).to.be.true;
});

it('should return false for null', () => {
expect(isFunction(null)).to.be.false;
});

it('should return false for undefined', () => {
expect(isFunction(undefined)).to.be.false;
});

it('should return false for an object literal', () => {
expect(isFunction({})).to.be.false;
});

it('should return false for an instance of a class', () => {
class Foo {};
expect(isFunction(new Foo())).to.be.false;
});

it('should return false for a string', () => {
expect(isFunction('foo')).to.be.false;
});

it('should return false for a number', () => {
expect(isFunction(1)).to.be.false;
});

it('should return false for a list', () => {
expect(isFunction([])).to.be.false;
});
});

describe('isPlainObject', () => {
it('should return true for an object literal', () => {
expect(isPlainObject({})).to.be.true;
});

it('should return true for a new Object', () => {
expect(isPlainObject(new Object())).to.be.true;
});

it('should return false for an instance of a class', () => {
class Foo {};
expect(isPlainObject(new Foo())).to.be.false;
});

it('should return false for null', () => {
expect(isPlainObject(null)).to.be.false;
});

it('should return false for undefined', () => {
expect(isPlainObject(undefined)).to.be.false;
});

it('should return false for a function', () => {
expect(isPlainObject(function foo() {})).to.be.false;
});

it('should return false for an arrow function', () => {
expect(isPlainObject(() => undefined)).to.be.false;
});

it('should return false for a constructor function', () => {
class Foo {};
expect(isPlainObject(Foo)).to.be.false;
});

it('should return false for a string', () => {
expect(isPlainObject('foo')).to.be.false;
});

it('should return false for a number', () => {
expect(isObject(1)).to.be.false;
});
});
});
30 changes: 17 additions & 13 deletions src/components/ng-redux.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import shallowEqual from '../utils/shallowEqual';
import wrapActionCreators from '../utils/wrapActionCreators';
import * as Redux from 'redux';
import * as _ from 'lodash';
import { BehaviorSubject, Observable } from 'rxjs';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/distinctUntilChanged';
import { Store, Action, ActionCreator, Reducer } from 'redux';
import { Injectable } from '@angular/core';
import { invariant } from '../utils/invariant';
import { isObject, isFunction, isPlainObject} from '../utils/type-checks';
import { omit } from '../utils/omit';

const VALID_SELECTORS = ['string', 'number', 'symbol', 'function'];
const ERROR_MESSAGE = `Expected selector to be one of:
Expand All @@ -31,13 +35,13 @@ export class NgRedux<RootState> {
this._store.subscribe(() => this._store$.next(this._store.getState()));
this._defaultMapStateToTarget = () => ({});
this._defaultMapDispatchToTarget = dispatch => ({ dispatch });
const cleanedStore = _.omit(store, ['dispatch', 'getState', 'subscribe', 'replaceReducer'])
const cleanedStore = omit(store, ['dispatch', 'getState', 'subscribe', 'replaceReducer'])
Object.assign(this, cleanedStore);
}

/**
* Create an observable from a Redux store.
*
*
* @param {Store<RootState>} store Redux store to create an observable from
* @returns {BehaviorSubject<RootState>}
*/
Expand Down Expand Up @@ -96,7 +100,7 @@ export class NgRedux<RootState> {
|| this._defaultMapStateToTarget;

invariant(
_.isFunction(finalMapStateToTarget),
isFunction(finalMapStateToTarget),
'mapStateToTarget must be a Function. Instead received %s.',
finalMapStateToTarget);

Expand All @@ -108,7 +112,7 @@ export class NgRedux<RootState> {
return (target) => {

invariant(
_.isFunction(target) || _.isObject(target),
isFunction(target) || isObject(target),
'The target parameter passed to connect must be a Function or' +
'a plain object.'
);
Expand Down Expand Up @@ -174,18 +178,18 @@ export class NgRedux<RootState> {
};

private updateTarget(target, StateSlice, dispatch) {
if (_.isFunction(target)) {
if (isFunction(target)) {
target(StateSlice, dispatch);
} else {
_.assign(target, StateSlice, dispatch);
Object.assign(target, StateSlice, dispatch);
}
}

private getStateSlice(state, mapStateToScope) {
const slice = mapStateToScope(state);

invariant(
_.isPlainObject(slice),
isPlainObject(slice),
'`mapStateToScope` must return an object. Instead received %s.',
slice
);
Expand All @@ -194,16 +198,16 @@ export class NgRedux<RootState> {
}

private getBoundActions = (actions) => {
const finalMapDispatchToTarget = _.isPlainObject(actions) ?
const finalMapDispatchToTarget = isPlainObject(actions) ?
wrapActionCreators(actions) :
actions || this._defaultMapDispatchToTarget;
invariant(
_.isPlainObject(finalMapDispatchToTarget)
|| _.isFunction(finalMapDispatchToTarget),
isPlainObject(finalMapDispatchToTarget)
|| isFunction(finalMapDispatchToTarget),
'mapDispatchToTarget must be a plain Object or a Function. ' +
'Instead received % s.',
finalMapDispatchToTarget);

return finalMapDispatchToTarget(this._store.dispatch);
};
}
}
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export {provider} from './components/provider';
export { NgRedux } from './components/ng-redux';
export { provider } from './components/provider';
export { NgRedux } from './components/ng-redux';
13 changes: 13 additions & 0 deletions src/utils/omit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Creates a copy of object, but with properties matching 'props'
* omitted.
*
* @param {Object} object: the object to be copied and filtered.
* @param {string[]} props: a list of property names to be excluded
* from the filtered copy.
*/
export function omit(object: Object, props: string[]): Object {
const clone = Object.assign({}, object);
props.forEach(prop => delete clone[prop]);
return clone;
};
16 changes: 16 additions & 0 deletions src/utils/type-checks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function isFunction(thing: any): boolean {
return !!(thing &&
thing.constructor &&
thing.call &&
thing.apply);
}

export function isObject(thing: any): boolean {
return !!(thing &&
typeof thing === 'object' &&
!isFunction(thing));
}

export function isPlainObject(thing: any): boolean {
return isObject(thing) && thing.constructor === Object;
}
1 change: 0 additions & 1 deletion typings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"ambientDependencies": {
"chai": "registry:dt/chai#3.4.0+20160317120654",
"es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654",
"lodash": "github:DefinitelyTyped/DefinitelyTyped/lodash/lodash.d.ts#70bf7e2bfeb0d5b1b651ef3219bcc65c8eec117e",
"mocha": "registry:dt/mocha#2.2.5+20160317120654",
"node": "github:DefinitelyTyped/DefinitelyTyped/node/node.d.ts#20e1eb9616922d382d918cc5a21870a9dbe255f5",
"sinon": "registry:dt/sinon#1.16.0+20160317120654",
Expand Down