/
Repository.ts
138 lines (115 loc) · 4.2 KB
/
Repository.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import { Queryable } from '../../Collections/Queryable/Queryable';
import { Result } from '../../Patterns/Result/Result';
import { Validator } from '../../Patterns/Validator/Validator';
import { Strings } from '../../System/Strings';
import { ISerializer } from '../../Utility/Serialization/ISerializer';
import { IPersister } from './Persisters/IPersister';
/**
* An extension of Queryable<T> that incorporates persistence functionality
* provided by a class implementing IPersister. Previously persisted data is
* loaded on instantiation using the default persister's "Retrieve" method.
*/
export class Repository<T> extends Queryable<T> {
protected constructor() { super(); }
/**
* A set of rules the repository will check against when new elements are added
* Rules that have a severity of "Error" will not be added
*/
public get PendingChanges(): { PendingSave: Queryable<T>, PendingDeletion: Queryable<T> } {
return {
PendingSave: this.GetUnsavedElements(),
PendingDeletion: this.GetUnpurgedElements()
};
}
protected savedData = { raw: Strings.Empty, referential: [] as Array<T> };
protected persister!: IPersister<T>;
protected validator: Validator<T> = new Validator<T>([]);
protected serializer?: ISerializer;
protected serializeAs?: { new(): T; };
public static New<T>(
persister: IPersister<T>,
validator: Validator<T> = new Validator<T>([]),
serializer?: ISerializer,
serializeAs?: { new(): T; }
): Repository<T> {
const repository = Object.create(Repository.prototype) as Repository<T>;
repository.persister = persister;
repository.validator = validator;
repository.serializer = serializer;
repository.serializeAs = serializeAs;
let initialData = Queryable.From(repository.persister.Retrieve());
initialData = initialData && initialData.length >= 1 ? initialData : Queryable.From([]);
repository.push(...(repository.serializer && repository.serializeAs ?
repository.getSerializedInstancesFromInitialData(initialData) : initialData));
repository.setSavedData();
return repository;
}
/**
* Calls the underlying persister's "Persist" method saving the data currently in the list
*/
public SaveChanges(): Result<null> {
let result = new Result(null);
this.GetUnsavedElements().slice().forEach(element => {
result = result.CombineWith(this.itemIsValid(element));
});
this.GetUnpurgedElements().slice().forEach(element => {
result = result.CombineWith(this.itemIsValid(element));
});
if (result.IsSuccess) {
this.persister.Persist(this.slice());
this.setSavedData();
}
return result;
}
/**
* Override for default push that enforces validation
*/
public push(...items: T[]): number {
let result = new Result();
items.forEach(element => {
result = result.CombineWith(this.itemIsValid(element));
});
if (result.IsSuccess) {
super.push(...items);
return items.length;
} else {
return 0;
}
}
/**
* Calls the underlying persister's "Purge" method deleting any data previously persisted
*/
public PurgeData(): void {
this.persister.Purge();
this.splice(0, this.length);
}
/**
* Returns a collection of elements that have not been saved
*/
public GetUnsavedElements(): Queryable<T> {
return Queryable.From(this.filter(i => !this.savedData.raw.includes(JSON.stringify(i))));
}
/**
* Returns a collection of elements that have not been removed from persistence
*/
public GetUnpurgedElements(): Queryable<T> {
return Queryable.From(this.savedData.referential).Except(this.slice());
}
protected getSerializedInstancesFromInitialData(initialData: Array<any>): Queryable<T> {
const classInstances = new Array<T>();
initialData.forEach(element => {
classInstances.push((this.serializer as ISerializer)
.Deserialize((this.serializeAs as { new(): T; }), element));
});
return Queryable.From(classInstances);
}
protected itemIsValid(item: T): Result<null> {
return this.validator.Validate(item);
}
protected setSavedData(): void {
this.savedData = {
raw: JSON.stringify(this.slice()),
referential: this.slice()
};
}
}