Skip to content

Commit

Permalink
v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio Daniel Xalambrí committed Apr 20, 2016
1 parent 4917031 commit ebf1573
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 1 deletion.
11 changes: 11 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"plugins": [
"transform-es2015-modules-commonjs",
"transform-es2015-arrow-functions",
"transform-es2015-parameters",
"transform-es2015-block-scoping",
"transform-es2015-computed-properties",
"transform-object-rest-spread",
"transform-es2015-shorthand-properties"
]
}
4 changes: 4 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "airbnb",
"parser": "babel-eslint"
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ node_modules

# Optional REPL history
.node_repl_history

build
7 changes: 7 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
lib
test
logs
*.log
.babelrc
.eslintrc
56 changes: 55 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,56 @@
# redux-duck
Helper function to create Redux modules easy using `ducks-modular-redux` proposal
Helper function to create Redux modules using the `ducks-modular-redux` proposal.

## Installation
```bash
npm i -S redux-duck
```

## API
### Create duck
```javascript
import { createDuck } from 'redux-duck';

const myDuck = createDuck('duck-name', 'application-name');
```
* `createDuck` receive 2 arguments, the second argument is optional.
* The first argument is the duck name.
* The second, and optional, argument is the application or module name.

### Define action types
```javascript
const ACTION_TYPE = myDuck.defineType('ACTION_TYPE');
```
* `defineType` receive just one argument.
* The argument is the name of the action.
* The result should be an string like `application-name/duck-name/ACTION_TYPE` or `duck-name/ACTION_TYPE` if the application or module name was not defined.

### Create action creators
```javascript
const actionType = myDuck.createAction(ACTION_TYPE);
```
* `createAction` receive just one argument.
* This argument should be the defined action type string.
* It should return a function who will receive the action payload and return a valid (FSA compilant) action object.
* The action creator will receive an optional argument with the action payload.

### Create reducer
```javascript
const initialState = {
list: Immutable.List(),
data: Immutable.Map(),
};

const reducer = myDuck.createReducer({
[ACTION_TYPE]: (state, action) => ({
...state,
list: state.list.push(action.payload.id),
data: state.map.set(action.payload.id+'', action.payload),
}),
}, initialState);
```
* `createReducer` receive two arguments, both required.
* The first argument is an object with the possible action cases.
* The second argument is the reducer initial state.
* The first argument should use the previously defined *action types* as keys.
* Each key in the first argument object should be a function who will receive the current state and the dispatched action as arguments and return the updated state.
36 changes: 36 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export function createDuck(name, app) {
function defineType(type) {
if (app) {
return `${app}/${name}/${type}`;
}
return `${name}/${type}`;
}

function createReducer(cases, defaultState = {}) {
return function reducer(state = defaultState, action = {}) {
if (state === undefined) return defaultState;
for (const caseName in cases) {
if (action.type === caseName) return cases[caseName](state, action);
}
return state;
};
}

function createAction(type) {
return function actionCreator(payload) {
const action = {
type,
};

if (payload) action.payload = payload;

return action;
};
}

return {
defineType,
createReducer,
createAction,
};
}
52 changes: 52 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "redux-duck",
"version": "1.0.0",
"description": "Helper function to create Redux modules using the ducks-modular-redux proposal.",
"main": "build/index.js",
"directories": {
"test": "test"
},
"scripts": {
"lint": "eslint lib/index.js",
"prebuild": "npm run lint",
"build": "babel lib --out-dir build",
"pretest": "npm run build",
"test": "babel-node test/index.js | tap-spec",
"prepublish": "npm run test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sergiodxa/redux-duck.git"
},
"keywords": [
"redux",
"duck",
"module",
"helper"
],
"author": "Sergio Daniel Xalambrí <[email protected]> (http://sergio.xalambri.com.ar/)",
"license": "MIT",
"bugs": {
"url": "https://github.com/sergiodxa/redux-duck/issues"
},
"homepage": "http://sergio.xalambri.com.ar/redux-duck/",
"devDependencies": {
"babel": "6.5.2",
"babel-cli": "6.7.5",
"babel-core": "6.7.6",
"babel-eslint": "6.0.3",
"babel-plugin-transform-es2015-arrow-functions": "6.5.2",
"babel-plugin-transform-es2015-block-scoping": "6.7.1",
"babel-plugin-transform-es2015-computed-properties": "6.6.5",
"babel-plugin-transform-es2015-modules-commonjs": "6.7.4",
"babel-plugin-transform-es2015-parameters": "6.7.0",
"babel-plugin-transform-es2015-shorthand-properties": "6.5.0",
"babel-plugin-transform-object-rest-spread": "6.6.5",
"eslint": "2.8.0",
"eslint-config-airbnb": "7.0.0",
"eslint-plugin-jsx-a11y": "0.6.2",
"eslint-plugin-react": "4.3.0",
"tap-spec": "4.1.1",
"tape": "4.5.1"
}
}
140 changes: 140 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import test from 'tape';
import { createDuck } from '../build/index.js';

test('create duck', t => {
t.plan(6);

const duck = createDuck('test', 'redux-duck');

t.ok(duck.defineType, 'it should had a key `defineType`');
t.equals(
typeof duck.defineType,
'function',
'it should be a function'
);

t.ok(duck.createReducer, 'it should had a key `createReducer`');
t.equals(
typeof duck.createReducer,
'function',
'it should be a function'
);

t.ok(duck.createAction, 'it should had a key `createAction`');
t.equals(
typeof duck.createAction,
'function',
'it should be a function'
);
});

test('define type', t => {
t.plan(2);

const duck1 = createDuck('test1', 'redux-duck');
const duck2 = createDuck('test2', 'redux-duck');

const type1 = duck1.defineType('TYPE');
const type2 = duck2.defineType('TYPE');

t.equals(
type1,
'redux-duck/test1/TYPE',
'it should be the expected action type string'
);

t.equals(
type2,
'redux-duck/test2/TYPE',
'it should be the expected action type string'
);
});

test('create action creator', t => {
t.plan(3);

const duck = createDuck('test', 'redux-duck');

const type = duck.defineType('TYPE');

const createType = duck.createAction(type);

const testData = {
id: 123,
message: 'hello world',
};

const action = createType(testData);

const emptyActon = createType();

t.equals(
typeof createType,
'function',
'it should create a valid function'
);

t.deepEquals(
action,
{
type,
payload: testData,
},
'it should create a valid action object'
);

t.deepEquals(
emptyActon,
{
type,
},
'it should be able to create an action without payload'
);
});

test('create reducer', t => {
t.plan(3);

const duck = createDuck('test', 'redux-duck');

const type = duck.defineType('TYPE');

const reducer = duck.createReducer({
[type]: (state, action) => ({
...state,
[action.payload.id]: action.payload,
}),
}, {});

const testData = {
id: 123,
message: 'hello world',
};

const testAction = {
type,
payload: testData,
};

const state = reducer({}, testAction);

t.equals(
typeof reducer,
'function',
'the reducer should be a function'
);

t.deepEquals(
reducer(),
{},
'the reducer should be able to return the default state'
);

t.deepEquals(
state,
{
[testData.id]: testData,
},
'the reducer should work with the defined cases'
);
});

0 comments on commit ebf1573

Please sign in to comment.