mirror of
https://github.com/YunoHost-Apps/rocketchat_ynh.git
synced 2024-09-03 20:16:25 +02:00
174 lines
13 KiB
JavaScript
174 lines
13 KiB
JavaScript
(function () {
|
|
|
|
/* Imports */
|
|
var Meteor = Package.meteor.Meteor;
|
|
var _ = Package.underscore._;
|
|
|
|
/* Package-scope variables */
|
|
var Hook;
|
|
|
|
(function(){
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/callback-hook/hook.js //
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// XXX This pattern is under development. Do not add more callsites // 1
|
|
// using this package for now. See: // 2
|
|
// https://meteor.hackpad.com/Design-proposal-Hooks-YxvgEW06q6f // 3
|
|
// // 4
|
|
// Encapsulates the pattern of registering callbacks on a hook. // 5
|
|
// // 6
|
|
// The `each` method of the hook calls its iterator function argument // 7
|
|
// with each registered callback. This allows the hook to // 8
|
|
// conditionally decide not to call the callback (if, for example, the // 9
|
|
// observed object has been closed or terminated). // 10
|
|
// // 11
|
|
// By default, callbacks are bound with `Meteor.bindEnvironment`, so they will be
|
|
// called with the Meteor environment of the calling code that // 13
|
|
// registered the callback. Override by passing { bindEnvironment: false } // 14
|
|
// to the constructor. // 15
|
|
// // 16
|
|
// Registering a callback returns an object with a single `stop` // 17
|
|
// method which unregisters the callback. // 18
|
|
// // 19
|
|
// The code is careful to allow a callback to be safely unregistered // 20
|
|
// while the callbacks are being iterated over. // 21
|
|
// // 22
|
|
// If the hook is configured with the `exceptionHandler` option, the // 23
|
|
// handler will be called if a called callback throws an exception. // 24
|
|
// By default (if the exception handler doesn't itself throw an // 25
|
|
// exception, or if the iterator function doesn't return a falsy value // 26
|
|
// to terminate the calling of callbacks), the remaining callbacks // 27
|
|
// will still be called. // 28
|
|
// // 29
|
|
// Alternatively, the `debugPrintExceptions` option can be specified // 30
|
|
// as string describing the callback. On an exception the string and // 31
|
|
// the exception will be printed to the console log with // 32
|
|
// `Meteor._debug`, and the exception otherwise ignored. // 33
|
|
// // 34
|
|
// If an exception handler isn't specified, exceptions thrown in the // 35
|
|
// callback will propagate up to the iterator function, and will // 36
|
|
// terminate calling the remaining callbacks if not caught. // 37
|
|
// 38
|
|
Hook = function (options) { // 39
|
|
var self = this; // 40
|
|
options = options || {}; // 41
|
|
self.nextCallbackId = 0; // 42
|
|
self.callbacks = {}; // 43
|
|
// Whether to wrap callbacks with Meteor.bindEnvironment // 44
|
|
self.bindEnvironment = true; // 45
|
|
if (options.bindEnvironment === false) // 46
|
|
self.bindEnvironment = false; // 47
|
|
// 48
|
|
if (options.exceptionHandler) // 49
|
|
self.exceptionHandler = options.exceptionHandler; // 50
|
|
else if (options.debugPrintExceptions) { // 51
|
|
if (! _.isString(options.debugPrintExceptions)) // 52
|
|
throw new Error("Hook option debugPrintExceptions should be a string"); // 53
|
|
self.exceptionHandler = options.debugPrintExceptions; // 54
|
|
} // 55
|
|
}; // 56
|
|
// 57
|
|
_.extend(Hook.prototype, { // 58
|
|
register: function (callback) { // 59
|
|
var self = this; // 60
|
|
var exceptionHandler = self.exceptionHandler || function (exception) { // 61
|
|
// Note: this relies on the undocumented fact that if bindEnvironment's // 62
|
|
// onException throws, and you are invoking the callback either in the // 63
|
|
// browser or from within a Fiber in Node, the exception is propagated. // 64
|
|
throw exception; // 65
|
|
}; // 66
|
|
// 67
|
|
if (self.bindEnvironment) { // 68
|
|
callback = Meteor.bindEnvironment(callback, exceptionHandler); // 69
|
|
} else { // 70
|
|
callback = dontBindEnvironment(callback, exceptionHandler); // 71
|
|
} // 72
|
|
// 73
|
|
var id = self.nextCallbackId++; // 74
|
|
self.callbacks[id] = callback; // 75
|
|
// 76
|
|
return { // 77
|
|
stop: function () { // 78
|
|
delete self.callbacks[id]; // 79
|
|
} // 80
|
|
}; // 81
|
|
}, // 82
|
|
// 83
|
|
// For each registered callback, call the passed iterator function // 84
|
|
// with the callback. // 85
|
|
// // 86
|
|
// The iterator function can choose whether or not to call the // 87
|
|
// callback. (For example, it might not call the callback if the // 88
|
|
// observed object has been closed or terminated). // 89
|
|
// // 90
|
|
// The iteration is stopped if the iterator function returns a falsy // 91
|
|
// value or throws an exception. // 92
|
|
// // 93
|
|
each: function (iterator) { // 94
|
|
var self = this; // 95
|
|
// 96
|
|
// Invoking bindEnvironment'd callbacks outside of a Fiber in Node doesn't // 97
|
|
// run them to completion (and exceptions thrown from onException are not // 98
|
|
// propagated), so we need to be in a Fiber. // 99
|
|
Meteor._nodeCodeMustBeInFiber(); // 100
|
|
// 101
|
|
var ids = _.keys(self.callbacks); // 102
|
|
for (var i = 0; i < ids.length; ++i) { // 103
|
|
var id = ids[i]; // 104
|
|
// check to see if the callback was removed during iteration // 105
|
|
if (_.has(self.callbacks, id)) { // 106
|
|
var callback = self.callbacks[id]; // 107
|
|
// 108
|
|
if (! iterator(callback)) // 109
|
|
break; // 110
|
|
} // 111
|
|
} // 112
|
|
} // 113
|
|
}); // 114
|
|
// 115
|
|
// Copied from Meteor.bindEnvironment and removed all the env stuff. // 116
|
|
var dontBindEnvironment = function (func, onException, _this) { // 117
|
|
if (!onException || typeof(onException) === 'string') { // 118
|
|
var description = onException || "callback of async function"; // 119
|
|
onException = function (error) { // 120
|
|
Meteor._debug( // 121
|
|
"Exception in " + description + ":", // 122
|
|
error && error.stack || error // 123
|
|
); // 124
|
|
}; // 125
|
|
} // 126
|
|
// 127
|
|
return function (/* arguments */) { // 128
|
|
var args = _.toArray(arguments); // 129
|
|
// 130
|
|
var runAndHandleExceptions = function () { // 131
|
|
try { // 132
|
|
var ret = func.apply(_this, args); // 133
|
|
} catch (e) { // 134
|
|
onException(e); // 135
|
|
} // 136
|
|
return ret; // 137
|
|
}; // 138
|
|
// 139
|
|
return runAndHandleExceptions(); // 140
|
|
}; // 141
|
|
}; // 142
|
|
// 143
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
}).call(this);
|
|
|
|
|
|
/* Exports */
|
|
if (typeof Package === 'undefined') Package = {};
|
|
Package['callback-hook'] = {
|
|
Hook: Hook
|
|
};
|
|
|
|
})();
|
|
|
|
//# sourceMappingURL=callback-hook.js.map
|