Skip to content

Commit

Permalink
Merge pull request #63 from w11k/repository_poc
Browse files Browse the repository at this point in the history
_Tydux Repository_ adds a repository state using `createRepositoryState` with prebuild CRUD operations by extending your Tydux Commands with  
`RepositoryCommands`.
This empowers you to quickly **add/update/delete** data on a normalized state structure.

**The following operations are available:**

- **updateOrPushEntry**: add or update one entry
- **updateOrPushEntries**: add or update multiple entries
- **setPositionOfEntry**: set position of one entry to the start, the end or a specific index of the list
- **setPositionOfEntries**: set position of multiple entries to the start, the end or a specific index of the list
- **patchEntry**: patch one partial entry
- **patchEntries**: patch multiple partial entries
- **removeEntry**: removes one entry
- **removeEntries**: remove multiple entries
- **removeAllEntries**: clear all repository entries

The normalized state can be created like the following:

- **createRepositoryState(idField)**: create an empty repository state
- **createRepositoryState(idField, {})**: create a repository state with an initial value (object)
- **createRepositoryState(idField, [])**: create a repository state with an initial value (array)
  • Loading branch information
msallat authored Aug 8, 2022
2 parents 48202c7 + c7103d3 commit e3a3367
Show file tree
Hide file tree
Showing 10 changed files with 780 additions and 11,061 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ todoFacade.loadTodoListFromServer();
# Documentation

- [Installation](https://github.com/w11k/Tydux/tree/master/doc/installation.md)
- [Tydux Repository](https://github.com/w11k/Tydux/tree/master/doc/repository.md)
- [Redux comparison](https://github.com/w11k/Tydux/tree/master/doc/redux_comparison.md)
- [Angular integration](https://github.com/w11k/Tydux/blob/master/projects/w11k/tydux-angular/README.md)
- [Migration guide version 14](https://github.com/w11k/Tydux/tree/master/doc/migration_14.md)
Expand Down
55 changes: 55 additions & 0 deletions doc/repository.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Tydux Repository

_Tydux Repository_ adds a repository state using `createRepositoryState` with prebuild CRUD operations by extending your Tydux Commands with
`RepositoryCommands`.
This empowers you to quickly **add/update/delete** data on a normalized state structure.

**The following operations are available:**

- **updateOrPushEntry**: add or update one entry
- **updateOrPushEntries**: add or update multiple entries
- **setPositionOfEntry**: set position of one entry to the start, the end or a specific index of the list
- **setPositionOfEntries**: set position of multiple entries to the start, the end or a specific index of the list
- **patchEntry**: patch one partial entry
- **patchEntries**: patch multiple partial entries
- **removeEntry**: removes one entry
- **removeEntries**: remove multiple entries
- **removeAllEntries**: clear all repository entries

The normalized state can be created like the following:

- **createRepositoryState(idField)**: create an empty repository state
- **createRepositoryState(idField, {})**: create a repository state with an initial value (object)
- **createRepositoryState(idField, [])**: create a repository state with an initial value (array)


## Quick usage

```typescript
class TodoState {
todos = createRepositoryState<Todo, "id">("id");
}

class TodoCommands extends RepositoryCommands<TodoState> {
// .... other custom commands
}

class TodoFacade extends Facade<TodoCommands> {
addOne(todo: Todo) {
this.commands.updateOrPushEntry("todos", todo);
}
}

export class TodoService extends TodoFacade {

constructor() {
super("todos", new TodoCommands(), new TodoState());
}

addTodo() {
this.addOne({ id: '1', state: true, text: 'foo' });
}
}
```

(more examples see [repository.test.ts](https://github.com/w11k/Tydux/blob/master/modules/tydux/src/lib/repository.test.ts))
11,068 changes: 10 additions & 11,058 deletions modules/tydux/package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions modules/tydux/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "@w11k/tydux",
"version": "16.0.2",
"version": "16.1.0",
"author": "W11K GmbH",
"contributors": [
"Roman Roelofsen",
"Philipp Burgmer",
"Sascha Engmann"
"Sascha Engmann",
"Mario Sallat"
],
"license": "Apache-2.0",
"homepage": "https://github.com/w11k/Tydux",
Expand Down
33 changes: 32 additions & 1 deletion modules/tydux/src/lib/commands-mutator.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import {createTestFacade, createTestMount} from "../testing";
import {Commands} from "./commands";
import {arrayAppend, arrayPrepend, arrayRemoveFirst, createAssignCommand, createMutator, objectPatch} from "./commands-mutators";
import {
arrayAppend,
arrayInsertAtIndex,
arrayPrepend,
arrayRemoveFirst,
createAssignCommand,
createMutator,
objectPatch, swapPositions
} from "./commands-mutators";
import {enableTyduxDevelopmentMode} from "./development";
import {Facade} from "./Facade";

Expand Down Expand Up @@ -114,6 +122,18 @@ describe("operator functions for applyMutator", () => {
expect(arrayRemoveFirst(["a", "b"])()).toEqual(["b"]);
});

it("arrayInsertAtIndex", () => {
expect(arrayInsertAtIndex(["1", "4"], 1)(["2", "3"])).toEqual(["1", "2", "3", "4"]);

expect(() => {
arrayInsertAtIndex(["1", "4"], -1)(["2", "3"]);
}).toThrow('Index must at least be in the scope from 0 to 1');

expect(() => {
arrayInsertAtIndex(["1", "4"], 2)(["2", "3"]);
}).toThrow('Index must at least be in the scope from 0 to 1');
});

it("objectPatch", () => {
expect(objectPatch({a: 1, b: "b"})({b: "bb"})).toEqual({a: 1, b: "bb"});

Expand All @@ -132,4 +152,15 @@ describe("operator functions for applyMutator", () => {
expect(tf.state.obj).toEqual({fieldA: 123, fieldB: "bbbb"});
});

it("swapPositions", () => {
expect(swapPositions(["1", "2", "3"], 0, 2)).toEqual(["3", "2", "1"]);

expect(() => {
swapPositions(["1", "2", "3"], -1, 2);
}).toThrow('Index must at least be in the scope from 0 to 2');

expect(() => {
swapPositions(["1", "2", "3"], 0, 3);
}).toThrow('Index must at least be in the scope from 0 to 2');
});
});
28 changes: 28 additions & 0 deletions modules/tydux/src/lib/commands-mutators.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Commands, CommandsState} from "./commands";
import {getIndexNotInArrayMessage} from "./error-messages";

export function createAssignCommand<T extends Commands<any>, F extends keyof CommandsState<T>>(
commands: T,
Expand Down Expand Up @@ -59,6 +60,20 @@ export function arrayPrepend<E>(source: E[]) {
};
}

export function arrayInsertAtIndex<E>(source: E[], index: number) {
const arrLength = source.length;
if (index < 0 || index > arrLength - 1) {
throw new Error(getIndexNotInArrayMessage(arrLength));
}
return (newItems: E[]) => {
return [
...source.slice(0, index),
...newItems,
...source.slice(index)
];
};
}

export function arrayRemoveFirst<E>(source: E[]) {
return () => {
const [, ...rest] = source;
Expand All @@ -74,6 +89,19 @@ export function objectPatch<E>(source: E): (patch: Partial<E>) => E {
};
};
}

export function swapPositions<E>(source: E[], indexA: number, indexB: number) {
const arrLength = source.length;
const sourceCopy = [...source];

if (indexA < 0 || indexA > arrLength - 1 || indexB < 0 || indexB > arrLength - 1) {
throw new Error(getIndexNotInArrayMessage(arrLength));
}

[sourceCopy[indexA], sourceCopy[indexB]] = [sourceCopy[indexB], sourceCopy[indexA]];
return sourceCopy;
}

//
// type FilterFlags<Base, Condition> = {
// [Key in keyof Base]:
Expand Down
2 changes: 2 additions & 0 deletions modules/tydux/src/lib/error-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ Illegal access to 'this'. Make sure that 'this' is not accessed in positions lik
Solution: Move the asynchronous code to the store class.
`;

export const getIndexNotInArrayMessage = (arrLength: number) => `Index must at least be in the scope from 0 to ${arrLength - 1}`;
Loading

0 comments on commit e3a3367

Please sign in to comment.