Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 631ead2

Browse files
committedJun 2, 2018
PoC implementation of **item** actions and reducer
1 parent 5573f19 commit 631ead2

File tree

16 files changed

+555
-2
lines changed

16 files changed

+555
-2
lines changed
 

‎CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Topcoder React Utils Changelog
22

3+
### v0.4.4
4+
PoC implementation of **item** actions and reducer.
5+
36
### v0.4.3
47
- Removes `adopt-dev-deps` script (use
58
[`topcoder-lib-setup`](docs/topcoder-lib-setup-script.md) instead).

‎__tests__/__snapshots__/index.js.snap

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ Object {
2020
"loadItemDone": [Function],
2121
"loadItemInit": [Function],
2222
},
23+
"item": Object {
24+
"dropData": [Function],
25+
"loadDataDone": [Function],
26+
"loadDataInit": [Function],
27+
"setData": [Function],
28+
"updateReferenceCounter": [Function],
29+
},
2330
},
2431
"client": [Function],
2532
"config": undefined,
@@ -32,6 +39,7 @@ Object {
3239
},
3340
"reducers": Object {
3441
"collection": [Function],
42+
"item": [Function],
3543
},
3644
"redux": Object {
3745
"combineReducers": [Function],
@@ -162,6 +170,13 @@ Object {
162170
"loadItemDone": [Function],
163171
"loadItemInit": [Function],
164172
},
173+
"item": Object {
174+
"dropData": [Function],
175+
"loadDataDone": [Function],
176+
"loadDataInit": [Function],
177+
"setData": [Function],
178+
"updateReferenceCounter": [Function],
179+
},
165180
},
166181
"client": [Function],
167182
"config": Config {
@@ -179,6 +194,7 @@ Object {
179194
},
180195
"reducers": Object {
181196
"collection": [Function],
197+
"item": [Function],
182198
},
183199
"redux": Object {
184200
"combineReducers": [Function],

‎__tests__/shared/actions/__snapshots__/index.js.snap

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,12 @@ Object {
1010
"loadItemDone": [Function],
1111
"loadItemInit": [Function],
1212
},
13+
"item": Object {
14+
"dropData": [Function],
15+
"loadDataDone": [Function],
16+
"loadDataInit": [Function],
17+
"setData": [Function],
18+
"updateReferenceCounter": [Function],
19+
},
1320
}
1421
`;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Module exports 1`] = `
4+
Object {
5+
"item": Object {
6+
"dropData": [Function],
7+
"loadDataDone": [Function],
8+
"loadDataInit": [Function],
9+
"setData": [Function],
10+
"updateReferenceCounter": [Function],
11+
},
12+
}
13+
`;
14+
15+
exports[`dropData 1`] = `
16+
Object {
17+
"payload": 1.7976931348623157e+308,
18+
"type": "ITEM/DROP_DATA",
19+
}
20+
`;
21+
22+
exports[`dropData 2`] = `
23+
Object {
24+
"payload": 12345,
25+
"type": "ITEM/DROP_DATA",
26+
}
27+
`;
28+
29+
exports[`getDataDone 1`] = `
30+
Object {
31+
"payload": Object {
32+
"data": "Dummy Data",
33+
"loadingOperationId": "12345",
34+
"timestamp": 1527961703000,
35+
},
36+
"type": "ITEM/LOAD_DATA_DONE",
37+
}
38+
`;
39+
40+
exports[`getDataInit 1`] = `
41+
Object {
42+
"payload": "12345",
43+
"type": "ITEM/LOAD_DATA_INIT",
44+
}
45+
`;
46+
47+
exports[`setData 1`] = `
48+
Object {
49+
"payload": Object {
50+
"data": "Dummy Data",
51+
"timestamp": 1527961703000,
52+
},
53+
"type": "ITEM/SET_DATA",
54+
}
55+
`;
56+
57+
exports[`updateReferenceCounter 1`] = `
58+
Object {
59+
"payload": 123,
60+
"type": "ITEM/UPDATE_REFERENCE_COUNTER",
61+
}
62+
`;
63+
64+
exports[`updateReferenceCounter 2`] = `
65+
Object {
66+
"payload": -123,
67+
"type": "ITEM/UPDATE_REFERENCE_COUNTER",
68+
}
69+
`;

‎__tests__/shared/actions/item.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Testing of payload creators for item actions.
3+
*/
4+
5+
import actions from 'actions/item';
6+
import MockDate from 'mockdate';
7+
8+
const a = actions.item;
9+
10+
beforeAll(() => MockDate.set('2018-06-02T19:11:23+01:23'));
11+
afterAll(() => MockDate.reset());
12+
13+
test('Module exports', () => expect(actions).toMatchSnapshot());
14+
15+
test('dropData', () => {
16+
expect(a.dropData()).toMatchSnapshot();
17+
expect(a.dropData(12345)).toMatchSnapshot();
18+
});
19+
20+
test('getDataInit', () => {
21+
expect(a.loadDataInit('12345')).toMatchSnapshot();
22+
});
23+
24+
test('getDataDone', () => {
25+
expect(a.loadDataDone('12345', 'Dummy Data')).toMatchSnapshot();
26+
});
27+
28+
test('setData', () => {
29+
expect(a.setData('Dummy Data')).toMatchSnapshot();
30+
});
31+
32+
test('updateReferenceCounter', () => {
33+
expect(a.updateReferenceCounter(123)).toMatchSnapshot();
34+
expect(a.updateReferenceCounter(-123)).toMatchSnapshot();
35+
});
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Snapshot testing case #1 1`] = `
4+
Object {
5+
"data": null,
6+
"loadingOperationId": null,
7+
"numRefs": 0,
8+
"timestamp": 0,
9+
}
10+
`;
11+
12+
exports[`Snapshot testing case #1 2`] = `
13+
Object {
14+
"data": null,
15+
"loadingOperationId": null,
16+
"numRefs": 1,
17+
"timestamp": 0,
18+
}
19+
`;
20+
21+
exports[`Snapshot testing case #1 3`] = `
22+
Object {
23+
"data": "Test data",
24+
"loadingOperationId": null,
25+
"numRefs": 1,
26+
"timestamp": 1527945685468,
27+
}
28+
`;
29+
30+
exports[`Snapshot testing case #1 4`] = `
31+
Object {
32+
"data": "Test data",
33+
"loadingOperationId": null,
34+
"numRefs": 1,
35+
"timestamp": 1527945685468,
36+
}
37+
`;
38+
39+
exports[`Snapshot testing case #1 5`] = `
40+
Object {
41+
"data": "Test data",
42+
"loadingOperationId": null,
43+
"numRefs": 0,
44+
"timestamp": 1527945685468,
45+
}
46+
`;
47+
48+
exports[`Snapshot testing case #1 6`] = `
49+
Object {
50+
"data": null,
51+
"loadingOperationId": null,
52+
"numRefs": 0,
53+
"timestamp": 0,
54+
}
55+
`;
56+
57+
exports[`Snapshot testing case #2 1`] = `
58+
Object {
59+
"data": null,
60+
"loadingOperationId": null,
61+
"numRefs": 0,
62+
"timestamp": 0,
63+
}
64+
`;
65+
66+
exports[`Snapshot testing case #2 2`] = `
67+
Object {
68+
"data": null,
69+
"loadingOperationId": 12345,
70+
"numRefs": 0,
71+
"timestamp": 0,
72+
}
73+
`;
74+
75+
exports[`Snapshot testing case #2 3`] = `
76+
Object {
77+
"data": null,
78+
"loadingOperationId": 12345,
79+
"numRefs": 0,
80+
"timestamp": 0,
81+
}
82+
`;
83+
84+
exports[`Snapshot testing case #2 4`] = `
85+
Object {
86+
"data": "Temporary Data",
87+
"loadingOperationId": 12345,
88+
"numRefs": 0,
89+
"timestamp": 1527945694106,
90+
}
91+
`;
92+
93+
exports[`Snapshot testing case #2 5`] = `
94+
Object {
95+
"data": "Temporary Data",
96+
"loadingOperationId": 12345,
97+
"numRefs": 0,
98+
"timestamp": 1527945694106,
99+
}
100+
`;
101+
102+
exports[`Snapshot testing case #2 6`] = `
103+
Object {
104+
"data": "Correct data",
105+
"loadingOperationId": null,
106+
"numRefs": 0,
107+
"timestamp": 1527945696574,
108+
}
109+
`;
110+
111+
exports[`Snapshot testing case #2 7`] = `
112+
Object {
113+
"data": "Correct data",
114+
"loadingOperationId": null,
115+
"numRefs": 0,
116+
"timestamp": 1527945696574,
117+
}
118+
`;
119+
120+
exports[`Snapshot testing case #2 8`] = `
121+
Object {
122+
"data": "Correct data",
123+
"loadingOperationId": null,
124+
"numRefs": -3,
125+
"timestamp": 1527945696574,
126+
}
127+
`;
128+
129+
exports[`Snapshot testing case #2 9`] = `
130+
Object {
131+
"data": null,
132+
"loadingOperationId": null,
133+
"numRefs": -3,
134+
"timestamp": 0,
135+
}
136+
`;

‎__tests__/shared/reducers/item.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import actions from 'actions/item';
2+
import reducer from 'reducers/item';
3+
import mockDate from 'mockdate';
4+
5+
beforeAll(() => mockDate.set('2018-06-02T18:31:23+05:10'));
6+
afterAll(() => mockDate.reset());
7+
8+
describe('Snapshot testing', () => {
9+
const a = actions.item;
10+
let state;
11+
12+
function check(action) {
13+
state = reducer(state, action);
14+
expect(state).toMatchSnapshot();
15+
mockDate.set(Date.now() + 1234);
16+
}
17+
18+
beforeEach(() => {
19+
state = undefined;
20+
});
21+
22+
test('case #1', () => {
23+
check('@@INIT');
24+
check(a.updateReferenceCounter(1));
25+
check(a.setData('Test data'));
26+
check(a.dropData());
27+
check(a.updateReferenceCounter(-1));
28+
check(a.dropData());
29+
});
30+
31+
test('case #2', () => {
32+
check('@@INIT');
33+
check(a.loadDataInit(12345));
34+
check(a.dropData());
35+
check(a.setData('Temporary Data'));
36+
check(a.loadDataDone(0, 'Wrong data'));
37+
check(a.loadDataDone(12345, 'Correct data'));
38+
check(a.dropData(Date.now() - 10000));
39+
check(a.updateReferenceCounter(-3));
40+
check(a.dropData());
41+
});
42+
});

‎docs/auto/actions.item.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<a name="module_actions.item"></a>
2+
3+
## actions.item
4+
Actions for management of item data in Redux store.
5+
6+
7+
* [actions.item](#module_actions.item)
8+
* [.dropData(olderThan)](#module_actions.item.dropData) ⇒ <code>Action</code>
9+
* [.loadDataInit(loadingOperationId)](#module_actions.item.loadDataInit) ⇒ <code>Action</code>
10+
* [.loadDataDone(loadingOperationId, data)](#module_actions.item.loadDataDone) ⇒ <code>Action</code>
11+
* [.setData(data)](#module_actions.item.setData) ⇒ <code>Action</code>
12+
* [.updateReferenceCounter(shift)](#module_actions.item.updateReferenceCounter) ⇒ <code>Action</code>
13+
14+
<a name="module_actions.item.dropData"></a>
15+
16+
### actions.item.dropData(olderThan) ⇒ <code>Action</code>
17+
Creates an action that drops item's data if (i) the value of item's
18+
reference counter is not positive; (ii) item's timestamp is older than
19+
`olderThan` value.
20+
21+
**Kind**: static method of [<code>actions.item</code>](#module_actions.item)
22+
23+
| Param | Type | Default | Description |
24+
| --- | --- | --- | --- |
25+
| olderThan | <code>Number</code> | <code>Number.MAX_VALUE</code> | Optional. Timestamp [ms]. If provided, item's data will be dropped only if they were received / set prior to this moment in time. |
26+
27+
<a name="module_actions.item.loadDataInit"></a>
28+
29+
### actions.item.loadDataInit(loadingOperationId) ⇒ <code>Action</code>
30+
Creates an action that signals the beginning of async loading of
31+
item's data.
32+
33+
**Kind**: static method of [<code>actions.item</code>](#module_actions.item)
34+
35+
| Param | Type | Description |
36+
| --- | --- | --- |
37+
| loadingOperationId | <code>Number</code> \| <code>String</code> | The unique identifier of the loading operation. It will be written into item's segment of Redux store with the following efffects: - We consider that item's data are being loaded if, and only if, the item's `loadingOperationId` has a truly value; - `loadingOperationId` is used to match the corresponding [loadDataDone](#module_actions.item.loadDataDone) operation. |
38+
39+
<a name="module_actions.item.loadDataDone"></a>
40+
41+
### actions.item.loadDataDone(loadingOperationId, data) ⇒ <code>Action</code>
42+
Creates an action that handles the result of async loading of item's
43+
data.
44+
45+
**Kind**: static method of [<code>actions.item</code>](#module_actions.item)
46+
47+
| Param | Type | Description |
48+
| --- | --- | --- |
49+
| loadingOperationId | <code>Number</code> \| <code>String</code> | The unique identifier of the loading operation. If it does not match the value currently written for the item in the Redux store (see [loadDataInit](#module_actions.item.loadDataInit)), the action will be silently ignored. |
50+
| data | <code>Any</code> | Any value to store as the item's data. |
51+
52+
<a name="module_actions.item.setData"></a>
53+
54+
### actions.item.setData(data) ⇒ <code>Action</code>
55+
Creates an action that stores given data into the item.
56+
57+
**Kind**: static method of [<code>actions.item</code>](#module_actions.item)
58+
59+
| Param | Type | Description |
60+
| --- | --- | --- |
61+
| data | <code>Any</code> | Any value to store as the item's data. |
62+
63+
<a name="module_actions.item.updateReferenceCounter"></a>
64+
65+
### actions.item.updateReferenceCounter(shift) ⇒ <code>Action</code>
66+
Creates an action that updates the item's reference counter.
67+
68+
**Kind**: static method of [<code>actions.item</code>](#module_actions.item)
69+
70+
| Param | Type | Description |
71+
| --- | --- | --- |
72+
| shift | <code>Number</code> | The value to add to the item's reference counter. Should be `1` or `-1` in majority of cases, although any other values are permitted. |
73+

‎docs/auto/index.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,12 @@
44
<dt><a href="actions.collection.md">actions.collection</a></dt>
55
<dd><p>Actions for management of <em>Collection</em> segment of Redux store.</p>
66
</dd>
7+
<dt><a href="actions.item.md">actions.item</a></dt>
8+
<dd><p>Actions for management of item data in Redux store.</p>
9+
</dd>
10+
<dt><a href="reducers.item.md">reducers.item</a></dt>
11+
<dd><p>Reducer for <a href="#module_actions.item">actions.item</a> actions.</p>
12+
<p>State segment managed by this reducer has the following structure:</p>
13+
</dd>
714
</dl>
815

‎docs/auto/reducers.item.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<a name="module_reducers.item"></a>
2+
3+
## reducers.item
4+
Reducer for [actions.item](#module_actions.item) actions.
5+
6+
State segment managed by this reducer has the following structure:
7+
8+
9+
| Param | Type | Default | Description |
10+
| --- | --- | --- | --- |
11+
| data | <code>Any</code> | <code></code> | Actual item data. |
12+
| loadingOperationId | <code>Number</code> \| <code>String</code> | <code></code> | `null` when no loading operation is happening; unique identifier of the ongoing loading operation otherwise (expected to be a truly value). |
13+
| numRefs | <code>Number</code> | <code>0</code> | Number of references to this item from the code. |
14+
| timestamp | <code>Number</code> | <code>0</code> | Timestamp of the most recent moment when item data were loaded or set. |
15+

‎package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,5 @@
116116
"lint:scss": "./node_modules/.bin/stylelint **/*.scss --syntax scss",
117117
"test": "npm run lint && npm run jest"
118118
},
119-
"version": "0.4.3"
119+
"version": "0.4.4"
120120
}

‎src/shared/actions/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import collection from './collection';
2+
import item from './item';
23

34
export default {
45
...collection,
6+
...item,
57
};

‎src/shared/actions/item.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @module "actions.item"
3+
* @desc Actions for management of item data in Redux store.
4+
*/
5+
6+
import * as redux from 'utils/redux';
7+
8+
/**
9+
* @static
10+
* @desc Creates an action that drops item's data if (i) the value of item's
11+
* reference counter is not positive; (ii) item's timestamp is older than
12+
* `olderThan` value.
13+
* @param {Number} olderThan=Number.MAX_VALUE Optional. Timestamp [ms].
14+
* If provided, item's data will be dropped only if they were received / set
15+
* prior to this moment in time.
16+
* @return {Action}
17+
*/
18+
function dropData(olderThan = Number.MAX_VALUE) {
19+
return olderThan;
20+
}
21+
22+
/**
23+
* @static
24+
* @desc Creates an action that signals the beginning of async loading of
25+
* item's data.
26+
* @param {Number|String} loadingOperationId The unique identifier of the
27+
* loading operation. It will be written into item's segment of Redux store
28+
* with the following efffects:
29+
*
30+
* - We consider that item's data are being loaded if, and only if,
31+
* the item's `loadingOperationId` has a truly value;
32+
*
33+
* - `loadingOperationId` is used to match the corresponding
34+
* {@link module:actions.item.loadDataDone} operation.
35+
*
36+
* @return {Action}
37+
*/
38+
function loadDataInit(loadingOperationId) {
39+
return loadingOperationId;
40+
}
41+
42+
/**
43+
* @static
44+
* @desc Creates an action that handles the result of async loading of item's
45+
* data.
46+
* @param {Number|String} loadingOperationId The unique identifier of the
47+
* loading operation. If it does not match the value currently written for
48+
* the item in the Redux store (see {@link module:actions.item.loadDataInit}),
49+
* the action will be silently ignored.
50+
* @param {Any} data Any value to store as the item's data.
51+
* @return {Action}
52+
*/
53+
function loadDataDone(loadingOperationId, data) {
54+
return { data, loadingOperationId, timestamp: Date.now() };
55+
}
56+
57+
/**
58+
* @static
59+
* @desc Creates an action that stores given data into the item.
60+
* @param {Any} data Any value to store as the item's data.
61+
* @return {Action}
62+
*/
63+
function setData(data) {
64+
return { data, timestamp: Date.now() };
65+
}
66+
67+
/**
68+
* @static
69+
* @desc Creates an action that updates the item's reference counter.
70+
* @param {Number} shift The value to add to the item's reference counter.
71+
* Should be `1` or `-1` in majority of cases, although any other values
72+
* are permitted.
73+
* @return {Action}
74+
*/
75+
function updateReferenceCounter(shift) {
76+
return shift;
77+
}
78+
79+
export default redux.createActions({
80+
ITEM: {
81+
DROP_DATA: dropData,
82+
LOAD_DATA_INIT: loadDataInit,
83+
LOAD_DATA_DONE: loadDataDone,
84+
SET_DATA: setData,
85+
UPDATE_REFERENCE_COUNTER: updateReferenceCounter,
86+
},
87+
});

‎src/shared/reducers/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import collection from './collection';
2+
import item from './item';
23

34
export default {
45
collection,
6+
item,
57
};

‎src/shared/reducers/item.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* @module "reducers.item"
3+
* @desc Reducer for {@link module:actions.item} actions.
4+
*
5+
* State segment managed by this reducer has the following structure:
6+
* @param {Any} data=null Actual item data.
7+
* @param {Number|String} loadingOperationId=null `null` when no loading
8+
* operation is happening; unique identifier of the ongoing loading operation
9+
* otherwise (expected to be a truly value).
10+
* @param {Number} numRefs=0 Number of references to this item from the code.
11+
* @param {Number} timestamp=0 Timestamp of the most recent moment when item
12+
* data were loaded or set.
13+
*/
14+
15+
import actions from 'actions/item';
16+
import * as redux from 'utils/redux';
17+
18+
function onDropData(state, { payload: olderThan }) {
19+
if (state.numRefs > 0 || state.timestamp > olderThan) return state;
20+
return { ...state, data: null, timestamp: 0 };
21+
}
22+
23+
function onLoadDataInit(state, { payload: loadingOperationId }) {
24+
return { ...state, loadingOperationId };
25+
}
26+
27+
function onLoadDataDone(state, action) {
28+
const { data, loadingOperationId, timestamp } = action.payload;
29+
if (loadingOperationId !== state.loadingOperationId) return state;
30+
return {
31+
...state,
32+
data,
33+
loadingOperationId: null,
34+
timestamp,
35+
};
36+
}
37+
38+
function onSetData(state, { payload }) {
39+
return { ...state, ...payload };
40+
}
41+
42+
function onUpdateReferenceCounter(state, { payload: shift }) {
43+
const numRefs = state.numRefs + shift;
44+
return { ...state, numRefs };
45+
}
46+
47+
const a = actions.item;
48+
export default redux.handleActions({
49+
[a.dropData]: onDropData,
50+
[a.loadDataInit]: onLoadDataInit,
51+
[a.loadDataDone]: onLoadDataDone,
52+
[a.setData]: onSetData,
53+
[a.updateReferenceCounter]: onUpdateReferenceCounter,
54+
}, {
55+
data: null,
56+
loadingOperationId: null,
57+
numRefs: 0,
58+
timestamp: 0,
59+
});

0 commit comments

Comments
 (0)
Please sign in to comment.