Skip to content

Commit

Permalink
feat: full info about selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed May 2, 2020
1 parent 088fb4a commit 1e887cb
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 54 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,9 +473,22 @@ store.select(selectUser, 1).subsribe(user => {
});
```

### Gathering information of a selector

Besides the `release` function every selector provides information about itself.

* `ngrxEntityRelationship` - name of its function: `rootEntity`, `rootEntities`, `relatedEntity`, `childEntity` and `childrenEntities`.
* `collectionSelector` - a function that returns a collection of its entity.
* `idSelector` - a function that returns the id of the related entity.
* `relationships` - an array of passed relationships.

In case of relationship functions there are two more keys
* `keyId` - a name of the keyId field.
* `keyValue` - a name of the keyValue field.

## Additional examples

Of course we can select as many relationships as we want until we have a field with a related id.
Of course, we can select as many relationships as we want until we have a field with a related id.
Check how `childrenEntities` works. It gathers entities based on a parent field.

```typescript
Expand Down
12 changes: 8 additions & 4 deletions src/childEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function childEntity<
let relationships: Array<HANDLER_RELATED_ENTITY<STORE, RELATED_ENTITY>> = [...arguments];
relationships = relationships.slice(3);

const {collection: funcSelector, id: idSelector} = normalizeSelector(featureSelector);
const {collection: collectionSelector, id: idSelector} = normalizeSelector(featureSelector);

const callback = (
cacheLevel: string,
Expand All @@ -47,7 +47,7 @@ export function childEntity<
source: PARENT_ENTITY,
idParentSelector: ID_SELECTOR<PARENT_ENTITY>,
) => {
const featureState = funcSelector(state);
const featureState = collectionSelector(state);
const parentId = idParentSelector(source);

let cacheDataLevel = cache.get(cacheLevel);
Expand Down Expand Up @@ -75,7 +75,7 @@ export function childEntity<
}
idChecks = new Map();
const idChecksEntities = new Map();
idChecks.set(funcSelector, idChecksEntities);
idChecks.set(collectionSelector, idChecksEntities);
idChecksEntities.set(null, featureState.entities);
cacheDataLevel.set(`!${parentId}`, [idChecks, id]);
}
Expand All @@ -97,7 +97,7 @@ export function childEntity<
value = undefined;
checks = new Map();
const checksEntities = new Map();
checks.set(funcSelector, checksEntities);
checks.set(collectionSelector, checksEntities);
checksEntities.set(null, featureState.entities);
checksEntities.set(id, featureState.entities[id]);

Expand All @@ -118,7 +118,11 @@ export function childEntity<
return cacheHash;
};
callback.ngrxEntityRelationship = 'childEntity';
callback.collectionSelector = collectionSelector;
callback.idSelector = idSelector;
callback.relationships = relationships;
callback.keyId = keyId;
callback.keyValue = keyValue;
callback.release = () => {
for (const relationship of relationships) {
relationship.release();
Expand Down
14 changes: 9 additions & 5 deletions src/childrenEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function childrenEntities<
let relationships: Array<HANDLER_RELATED_ENTITY<STORE, RELATED_ENTITY>> = [...arguments];
relationships = relationships.slice(3);

const {collection: funcSelector, id: idSelector} = normalizeSelector(featureSelector);
const {collection: collectionSelector, id: idSelector} = normalizeSelector(featureSelector);
const emptyResult: Map<UNKNOWN, UNKNOWN> = new Map();

const callback = (
Expand All @@ -48,7 +48,7 @@ export function childrenEntities<
source: PARENT_ENTITY,
idParentSelector: ID_SELECTOR<PARENT_ENTITY>,
) => {
const featureState = funcSelector(state);
const featureState = collectionSelector(state);
const parentId = idParentSelector(source);

let cacheDataLevel = cache.get(cacheLevel);
Expand Down Expand Up @@ -78,7 +78,7 @@ export function childrenEntities<
}
idsChecks = new Map();
const idsChecksEntities = new Map();
idsChecks.set(funcSelector, idsChecksEntities);
idsChecks.set(collectionSelector, idsChecksEntities);
idsChecksEntities.set(null, featureState.entities);
cacheDataLevel.set(`!${parentId}`, [idsChecks, ids]);
}
Expand All @@ -105,7 +105,7 @@ export function childrenEntities<
value = [];
checks = new Map();
const checksEntities = new Map();
checks.set(funcSelector, checksEntities);
checks.set(collectionSelector, checksEntities);
checksEntities.set(null, featureState.entities);
for (const id of ids) {
checksEntities.set(id, featureState.entities[id]);
Expand All @@ -125,7 +125,7 @@ export function childrenEntities<
entityValue = {...featureState.entities[id]} as RELATED_ENTITY;
entityChecks = new Map();
const entityChecksEntities = new Map();
entityChecks.set(funcSelector, entityChecksEntities);
entityChecks.set(collectionSelector, entityChecksEntities);
entityChecksEntities.set(null, featureState.entities);
entityChecksEntities.set(id, featureState.entities[id]);

Expand All @@ -147,7 +147,11 @@ export function childrenEntities<
return cacheHash;
};
callback.ngrxEntityRelationship = 'childrenEntities';
callback.collectionSelector = collectionSelector;
callback.idSelector = idSelector;
callback.relationships = relationships;
callback.keyId = keyId;
callback.keyValue = keyValue;
callback.release = () => {
emptyResult.clear();
for (const relationship of relationships) {
Expand Down
12 changes: 8 additions & 4 deletions src/relatedEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function relatedEntity<
let relationships: Array<HANDLER_RELATED_ENTITY<STORE, RELATED_ENTITY>> = [...arguments];
relationships = relationships.slice(3);

const {collection: funcSelector, id: idSelector} = normalizeSelector(featureSelector);
const {collection: collectionSelector, id: idSelector} = normalizeSelector(featureSelector);
const emptyResult: Map<UNKNOWN, UNKNOWN> = new Map();

const callback = (cacheLevel: string, state: STORE, cache: CACHE<STORE>, source: PARENT_ENTITY) => {
Expand All @@ -51,7 +51,7 @@ export function relatedEntity<
return;
}

const featureState = funcSelector(state);
const featureState = collectionSelector(state);

let cacheDataLevel = cache.get(cacheLevel);
if (!cacheDataLevel) {
Expand Down Expand Up @@ -95,7 +95,7 @@ export function relatedEntity<
value = undefined;
checks = new Map();
const checkIds = new Map();
checks.set(funcSelector, checkIds);
checks.set(collectionSelector, checkIds);
checkIds.set(null, featureState.entities);
for (const id of ids) {
checkIds.set(id, featureState.entities[id]);
Expand All @@ -119,7 +119,7 @@ export function relatedEntity<
entityValue = {...featureState.entities[id]} as RELATED_ENTITY;
entityChecks = new Map();
const entityCheckIds = new Map();
entityChecks.set(funcSelector, entityCheckIds);
entityChecks.set(collectionSelector, entityCheckIds);
entityCheckIds.set(null, featureState.entities);
entityCheckIds.set(id, featureState.entities[id]);

Expand All @@ -142,7 +142,11 @@ export function relatedEntity<
return cacheHash;
};
callback.ngrxEntityRelationship = 'relatedEntity';
callback.collectionSelector = collectionSelector;
callback.idSelector = idSelector;
callback.relationships = relationships;
callback.keyId = keyId;
callback.keyValue = keyValue;
callback.release = () => {
emptyResult.clear();
for (const relationship of relationships) {
Expand Down
2 changes: 2 additions & 0 deletions src/rootEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ export function rootEntities<STORE, ENTITY, TRANSFORMED>(
return value;
};
callback.ngrxEntityRelationship = 'rootEntities';
callback.collectionSelector = rootSelector.collectionSelector;
callback.idSelector = rootSelector.idSelector;
callback.relationships = rootSelector.relationships;
callback.release = () => {
cacheMap.clear();
rootSelector.release();
Expand Down
8 changes: 5 additions & 3 deletions src/rootEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function rootEntity<STORE, ENTITY, TRANSFORMED>(
transformer = deside;
relationships = relationships.slice(1);
}
const {collection: funcSelector, id: idSelector} = normalizeSelector(featureSelector);
const {collection: collectionSelector, id: idSelector} = normalizeSelector(featureSelector);

const cacheLevel = '0';
const cache: CACHE<STORE> = new Map();
Expand All @@ -43,7 +43,7 @@ export function rootEntity<STORE, ENTITY, TRANSFORMED>(
return;
}

const featureState = funcSelector(state);
const featureState = collectionSelector(state);
let cacheDataLevel = cache.get(cacheLevel);
if (!cacheDataLevel) {
cacheDataLevel = new Map();
Expand All @@ -66,7 +66,7 @@ export function rootEntity<STORE, ENTITY, TRANSFORMED>(
value = undefined;
checks = new Map();
const checkIds = new Map();
checks.set(funcSelector, checkIds);
checks.set(collectionSelector, checkIds);
checkIds.set(null, featureState.entities);
checkIds.set(id, featureState.entities[id]);

Expand Down Expand Up @@ -94,7 +94,9 @@ export function rootEntity<STORE, ENTITY, TRANSFORMED>(
return value;
};
callback.ngrxEntityRelationship = 'rootEntity';
callback.collectionSelector = collectionSelector;
callback.idSelector = idSelector;
callback.relationships = relationships;
callback.release = () => {
cache.clear();
for (const relationship of relationships) {
Expand Down
16 changes: 12 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,21 @@ export type CACHE_CHECKS = Map<ID_TYPES | null, UNKNOWN>;
export type CACHE_CHECKS_SET<S> = Map<STORE_SELECTOR<S, ENTITY_STATE<UNKNOWN>>, CACHE_CHECKS>;
export type CACHE<S> = Map<string, Map<string | null, [CACHE_CHECKS_SET<S>, UNKNOWN]>>;

export type HANDLER_ROOT_ENTITY<S, F, T, I> = {
export type HANDLER_ROOT_ENTITY<S, E, T, I> = {
(state: S, id: I): undefined | T;
ngrxEntityRelationship: string;
idSelector: ID_SELECTOR<F>;
collectionSelector: STORE_SELECTOR<S, ENTITY_STATE<E>>;
idSelector: ID_SELECTOR<E>;
relationships: Array<HANDLER_RELATED_ENTITY<S, E>>;
release(): void;
};

export type HANDLER_ROOT_ENTITIES<S, F, T, I> = {
export type HANDLER_ROOT_ENTITIES<S, E, T, I> = {
(state: S, id: Array<I>): Array<T>;
ngrxEntityRelationship: string;
idSelector: ID_SELECTOR<F>;
collectionSelector: STORE_SELECTOR<S, ENTITY_STATE<E>>;
idSelector: ID_SELECTOR<E>;
relationships: Array<HANDLER_RELATED_ENTITY<S, E>>;
release(): void;
};

Expand All @@ -64,7 +68,11 @@ export type HANDLER_RELATED_ENTITY<S, E> = {
sourceIdSelector: ID_SELECTOR<any /* TODO has to be a related entity */>,
): string | undefined;
ngrxEntityRelationship: string;
collectionSelector: STORE_SELECTOR<S, ENTITY_STATE<any /* TODO has to be a related entity */>>;
idSelector: ID_SELECTOR<any /* TODO has to be a related entity */>;
relationships: Array<HANDLER_RELATED_ENTITY<S, any /* TODO has to be a related entity */>>;
keyId: keyof any;
keyValue: keyof any;
release(): void;
};

Expand Down
17 changes: 10 additions & 7 deletions test/childEntity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ describe('childEntity', () => {
child?: Entity;
};

it('marks callback with ngrxEntityRelationship key', () => {
const actual = childEntity<any, any, any, any, any>(jasmine.createSpy(), '', '');
it('marks callback with ngrxEntityRelationship key and passed args', () => {
const rel1: any = Symbol();
const rel2: any = Symbol();
const featureSelector = jasmine.createSpy();
const actual = childEntity<any, any, any, any, any>(featureSelector, 'myKeyId', 'myKeyValue', rel1, rel2);
expect(actual).toEqual(jasmine.any(Function));
expect(actual).toEqual(
jasmine.objectContaining({
ngrxEntityRelationship: 'childEntity',
}),
);
expect(actual.ngrxEntityRelationship).toEqual('childEntity');
expect(actual.collectionSelector).toBe(featureSelector);
expect(actual.keyId).toEqual('myKeyId');
expect(actual.keyValue).toEqual('myKeyValue');
expect(actual.relationships).toEqual([rel1, rel2]);
});

it('does not set anything if there is no child entity', () => {
Expand Down
17 changes: 10 additions & 7 deletions test/childrenEntities.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ describe('childrenEntities', () => {
child?: Array<Entity>;
};

it('marks callback with ngrxEntityRelationship key', () => {
const actual = childrenEntities<any, any, any, any, any>(jasmine.createSpy(), '', '');
it('marks callback with ngrxEntityRelationship key and passed args', () => {
const rel1: any = Symbol();
const rel2: any = Symbol();
const featureSelector = jasmine.createSpy();
const actual = childrenEntities<any, any, any, any, any>(featureSelector, 'myKeyId', 'myKeyValue', rel1, rel2);
expect(actual).toEqual(jasmine.any(Function));
expect(actual).toEqual(
jasmine.objectContaining({
ngrxEntityRelationship: 'childrenEntities',
}),
);
expect(actual.ngrxEntityRelationship).toEqual('childrenEntities');
expect(actual.collectionSelector).toBe(featureSelector);
expect(actual.keyId).toEqual('myKeyId');
expect(actual.keyValue).toEqual('myKeyValue');
expect(actual.relationships).toEqual([rel1, rel2]);
});

it('set an empty array if there are no child entities', () => {
Expand Down
23 changes: 16 additions & 7 deletions test/relatedEntity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,23 @@ describe('relatedEntity', () => {
parentsId?: Array<string | number>;
};

it('marks callback with ngrxEntityRelationship key', () => {
const actual = relatedEntity<any, any, any, any, any, any, any>(jasmine.createSpy(), '', '');
expect(actual).toEqual(jasmine.any(Function));
expect(actual).toEqual(
jasmine.objectContaining({
ngrxEntityRelationship: 'relatedEntity',
}),
it('marks callback with ngrxEntityRelationship key and passed args', () => {
const rel1: any = Symbol();
const rel2: any = Symbol();
const featureSelector = jasmine.createSpy();
const actual = relatedEntity<any, any, any, any, any, any, any>(
featureSelector,
'myKeyId',
'myKeyValue',
rel1,
rel2,
);
expect(actual).toEqual(jasmine.any(Function));
expect(actual.ngrxEntityRelationship).toEqual('relatedEntity');
expect(actual.collectionSelector).toBe(featureSelector);
expect(actual.keyId).toEqual('myKeyId');
expect(actual.keyValue).toEqual('myKeyValue');
expect(actual.relationships).toEqual([rel1, rel2]);
});

it('does not set anything if the related id is falsy', () => {
Expand Down
15 changes: 9 additions & 6 deletions test/rootEntities.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ describe('rootEntities', () => {
});

it('marks callback with ngrxEntityRelationship key', () => {
const actual = rootEntities(jasmine.createSpy() as any);
const rootSelector: any = jasmine.createSpy();
rootSelector.relationships = Symbol();
rootSelector.collectionSelector = Symbol();
rootSelector.idSelector = Symbol();
const actual = rootEntities(rootSelector);
expect(actual).toEqual(jasmine.any(Function));
expect(actual).toEqual(
jasmine.objectContaining({
ngrxEntityRelationship: 'rootEntities',
}),
);
expect(actual.ngrxEntityRelationship).toEqual('rootEntities');
expect(actual.collectionSelector).toBe(rootSelector.collectionSelector);
expect(actual.idSelector).toBe(rootSelector.idSelector);
expect(actual.relationships).toBe(rootSelector.relationships);
});

it('returns the same empty array on no ids', () => {
Expand Down
14 changes: 8 additions & 6 deletions test/rootEntity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ describe('rootEntity', () => {
});

it('marks callback with ngrxEntityRelationship key', () => {
const actual = rootEntity(jasmine.createSpy());
const rel1: any = Symbol();
const rel2: any = Symbol();
const featureSelector = jasmine.createSpy();
// because rels aren't real rels we need to pass a transformer.
const actual = rootEntity(featureSelector, () => null, rel1, rel2);
expect(actual).toEqual(jasmine.any(Function));
expect(actual).toEqual(
jasmine.objectContaining({
ngrxEntityRelationship: 'rootEntity',
}),
);
expect(actual.collectionSelector).toBe(featureSelector);
expect(actual.ngrxEntityRelationship).toEqual('rootEntity');
expect(actual.relationships).toEqual([rel1, rel2]);
});

it('returns undefined when the id is falsy', () => {
Expand Down

0 comments on commit 1e887cb

Please sign in to comment.