forked from 418sec/js-data
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLinkedCollection.ts
159 lines (139 loc) · 4.5 KB
/
LinkedCollection.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import utils from './utils'
import './decorators'
import Collection from './Collection'
const DOMAIN = 'LinkedCollection'
/**
* Extends {@link Collection}. Used by a {@link DataStore} to implement an
* Identity Map.
*
* @example
* import {LinkedCollection} from 'js-data';
*
* // Extend the class using ES2015 class syntax.
* class CustomLinkedCollectionClass extends LinkedCollection {
* foo () { return 'bar'; }
* static beep () { return 'boop'; }
* }
* const customLinkedCollection = new CustomLinkedCollectionClass();
* console.log(customLinkedCollection.foo());
* console.log(CustomLinkedCollectionClass.beep());
*
* @class LinkedCollection
* @extends Collection
* @param {array} [records] Initial set of records to insert into the
* collection. See {@link Collection}.
* @param {object} [opts] Configuration options. See {@link Collection}.
* @returns {Mapper}
*/
export default class LinkedCollection extends Collection {
datastore: any;
constructor (records, opts) {
super(records, opts)
// Make sure this collection has a reference to a datastore
if (!this.datastore) {
throw utils.err(`new ${DOMAIN}`, 'opts.datastore')(400, 'DataStore', this.datastore)
}
}
_addMeta (record, timestamp) {
// Track when this record was added
this._added[this.recordId(record)] = timestamp
if (utils.isFunction(record._set)) {
record._set('$', timestamp)
}
}
_clearMeta (record) {
delete this._added[this.recordId(record)]
if (utils.isFunction(record._set)) {
record._set('$') // unset
}
}
_onRecordEvent (...args) {
Collection.prototype._onRecordEvent.apply(this, args)
const event = args[0]
// This is a very brute force method
// Lots of room for optimization
if (utils.isString(event) && event.indexOf('change') === 0) {
this.updateIndexes(args[1])
}
}
add (records, opts) {
const mapper = this.mapper
const timestamp = new Date().getTime()
const singular = utils.isObject(records) && !utils.isArray(records)
if (singular) {
records = [records]
}
records = super.add(records, opts)
if (mapper.relationList.length && records.length) {
// Check the currently visited record for relations that need to be
// inserted into their respective collections.
mapper.relationList.forEach(def => {
def.addLinkedRecords(records)
})
}
records.forEach(record => this._addMeta(record, timestamp))
return singular ? records[0] : records
}
remove (idOrRecord, opts) {
const mapper = this.mapper
const record = super.remove(idOrRecord, opts)
if (record) {
this._clearMeta(record)
}
if (mapper.relationList.length && record) {
mapper.relationList.forEach(def => {
def.removeLinkedRecords(mapper, [record])
})
}
return record
}
removeAll (query, opts) {
const mapper = this.mapper
const records = super.removeAll(query, opts)
records.forEach(this._clearMeta, this)
if (mapper.relationList.length && records.length) {
mapper.relationList.forEach(def => {
def.removeLinkedRecords(mapper, records)
})
}
return records
}
}
/**
* Create a subclass of this LinkedCollection:
*
* // Extend the class using alternate method.
* const OtherLinkedCollectionClass = LinkedCollection.extend({
* foo () { return 'bar'; }
* }, {
* beep () { return 'boop'; }
* });
* const otherLinkedCollection = new OtherLinkedCollectionClass();
* console.log(otherLinkedCollection.foo());
* console.log(OtherLinkedCollectionClass.beep());
*
* // Extend the class, providing a custom constructor.
* function AnotherLinkedCollectionClass () {
* LinkedCollection.call(this);
* this.created_at = new Date().getTime();
* }
* LinkedCollection.extend({
* constructor: AnotherLinkedCollectionClass,
* foo () { return 'bar'; }
* }, {
* beep () { return 'boop'; }
* });
* const anotherLinkedCollection = new AnotherLinkedCollectionClass();
* console.log(anotherLinkedCollection.created_at);
* console.log(anotherLinkedCollection.foo());
* console.log(AnotherLinkedCollectionClass.beep());
*
* @method LinkedCollection.extend
* @param {object} [props={}] Properties to add to the prototype of the
* subclass.
* @param {object} [props.constructor] Provide a custom constructor function
* to be used as the subclass itself.
* @param {object} [classProps={}] Static properties to add to the subclass.
* @returns {Constructor} Subclass of this LinkedCollection class.
* @since 3.0.0
*/