-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathvermongo.js
160 lines (134 loc) · 4.46 KB
/
vermongo.js
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
160
// [1,2,3,4,5,6].diff( [3,4,5] ); => [1, 2, 6]
const diff = function(a, b) {
return a.filter(function(i) {
return b.indexOf(i) < 0;
});
};
// [1, 2, [3, 4]].sameAs([1, 2, [3, 2]]) === false;
// attach the .sameAs method to Array's prototype to call it on any array
const sameAs = function(a, b) {
// if the other array is a falsy value, return
if(!a || !b) return false;
// compare lengths - can save a lot of time
if(a.length !== b.length) return false;
for(let i = 0, l = a.length; i < l; i++) {
// Check if we have nested arrays
if(a[i] instanceof Array && b[i] instanceof Array) {
// recurse into the nested arrays
if(!sameAs(a[i], b[i])) return false;
}
else if(a[i] !== b[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
};
Mongo.Collection.prototype.vermongo = function(op) {
const collection = this;
//console.log('[Vermongo]', collection._name, op);
const options = op || {};
options.userId = options.userId || false;
options.createdAt = options.createdAt || 'createdAt';
options.modifiedAt = options.modifiedAt || 'modifiedAt';
options.ignoredFields = options.ignoredFields || [];
let offOnce = false;
let _versions_collection = null;
// Setting hooks for a collection
const add = function(collection) {
const name = collection._name;
// create a new collection if not already existing
_versions_collection = new Mongo.Collection(name + '.vermongo');
/*
* insert hook
* Beware that collection2 validation occurs *before* this callback
* */
collection.before.insert(function(userId, doc) {
// do nothing if special option is set
if(offOnce) {
offOnce = false;
return;
}
// add vermongo fields
doc._version = 1;
if(options['timestamps']) {
const now = new Date();
if(!doc[options.createdAt]) doc[options.createdAt] = now;
if(!doc[options.modifiedAt]) doc[options.modifiedAt] = now;
}
if(!doc[options.userId] && options.userId && userId)
doc[options.userId] = userId;
});
// copy Doc in vermondo collection
const copyDoc = function(doc) {
if(Meteor.isServer) { // avoid duplicated insertion
// copy doc to versions collection
const savedDoc = Object.assign({}, doc); // shallow copy
if(typeof(savedDoc._id) !== 'undefined') delete savedDoc._id;
savedDoc.ref = doc._id;
_versions_collection.insert(savedDoc);
}
};
/*
* update hook
* */
collection.before.update(function(userId, doc, fieldNames, modifier, hook_options) {
// do nothing if special option is set
if(offOnce) {
offOnce = false;
return;
}
// do nothing if only ignored fields are modified
if(sameAs(diff(fieldNames, options.ignoredFields), [])) return;
// in case of doc not already versionned
if(!doc._version) doc._version = 1;
copyDoc(doc);
// incrementing version
modifier.$set = modifier.$set || {};
modifier.$set._version = doc._version + 1;
if(options['timestamps'])
modifier.$set[options.modifiedAt] = new Date();
if(!doc[options.userId] && options.userId && userId)
modifier.$set[options.userId] = userId;
});
/*
* remove hook
* */
collection.before.remove(function(userId, doc) {
// do nothing if special option is set
if(offOnce) {
offOnce = false;
return;
}
// in case of doc not already versioned
if(!doc._version) doc._version = 1;
copyDoc(doc); // put last known version in vermongo collection
// put a dummy version with deleted flag
doc._version = doc._version + 1;
if(options['timestamps'])
doc[options.modifiedAt] = new Date();
if(!doc[options.userId] && options.userId && userId)
doc[options.userId] = userId;
doc._deleted = true;
copyDoc(doc);
});
/*
* collection helpers
* */
collection.helpers({
versions: function() {
return _versions_collection.find({ref: this._id}, {sort: {_version: -1}});
}
});
collection.vermongoOffOnce = function() {
offOnce = true;
};
return collection;
};
this.getVersionCollection = function() {
return _versions_collection;
};
if(typeof(collection) !== 'undefined' && collection !== null)
add(collection);
return collection;
};