(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 // Callbacks are bound with `Meteor.bindEnvironment`, so they will be // 12 // called with the Meteor environment of the calling code that // 13 // registered the callback. // 14 // // 15 // Registering a callback returns an object with a single `stop` // 16 // method which unregisters the callback. // 17 // // 18 // The code is careful to allow a callback to be safely unregistered // 19 // while the callbacks are being iterated over. // 20 // // 21 // If the hook is configured with the `exceptionHandler` option, the // 22 // handler will be called if a called callback throws an exception. // 23 // By default (if the exception handler doesn't itself throw an // 24 // exception, or if the iterator function doesn't return a falsy value // 25 // to terminate the calling of callbacks), the remaining callbacks // 26 // will still be called. // 27 // // 28 // Alternatively, the `debugPrintExceptions` option can be specified // 29 // as string describing the callback. On an exception the string and // 30 // the exception will be printed to the console log with // 31 // `Meteor._debug`, and the exception otherwise ignored. // 32 // // 33 // If an exception handler isn't specified, exceptions thrown in the // 34 // callback will propagate up to the iterator function, and will // 35 // terminate calling the remaining callbacks if not caught. // 36 // 37 Hook = function (options) { // 38 var self = this; // 39 options = options || {}; // 40 self.nextCallbackId = 0; // 41 self.callbacks = {}; // 42 // 43 if (options.exceptionHandler) // 44 self.exceptionHandler = options.exceptionHandler; // 45 else if (options.debugPrintExceptions) { // 46 if (! _.isString(options.debugPrintExceptions)) // 47 throw new Error("Hook option debugPrintExceptions should be a string"); // 48 self.exceptionHandler = options.debugPrintExceptions; // 49 } // 50 }; // 51 // 52 _.extend(Hook.prototype, { // 53 register: function (callback) { // 54 var self = this; // 55 // 56 callback = Meteor.bindEnvironment( // 57 callback, // 58 self.exceptionHandler || function (exception) { // 59 // Note: this relies on the undocumented fact that if bindEnvironment's // 60 // onException throws, and you are invoking the callback either in the // 61 // browser or from within a Fiber in Node, the exception is propagated. // 62 throw exception; // 63 } // 64 ); // 65 // 66 var id = self.nextCallbackId++; // 67 self.callbacks[id] = callback; // 68 // 69 return { // 70 stop: function () { // 71 delete self.callbacks[id]; // 72 } // 73 }; // 74 }, // 75 // 76 // For each registered callback, call the passed iterator function // 77 // with the callback. // 78 // // 79 // The iterator function can choose whether or not to call the // 80 // callback. (For example, it might not call the callback if the // 81 // observed object has been closed or terminated). // 82 // // 83 // The iteration is stopped if the iterator function returns a falsy // 84 // value or throws an exception. // 85 // // 86 each: function (iterator) { // 87 var self = this; // 88 // 89 // Invoking bindEnvironment'd callbacks outside of a Fiber in Node doesn't // 90 // run them to completion (and exceptions thrown from onException are not // 91 // propagated), so we need to be in a Fiber. // 92 Meteor._nodeCodeMustBeInFiber(); // 93 // 94 var ids = _.keys(self.callbacks); // 95 for (var i = 0; i < ids.length; ++i) { // 96 var id = ids[i]; // 97 // check to see if the callback was removed during iteration // 98 if (_.has(self.callbacks, id)) { // 99 var callback = self.callbacks[id]; // 100 // 101 if (! iterator(callback)) // 102 break; // 103 } // 104 } // 105 } // 106 }); // 107 // 108 ////////////////////////////////////////////////////////////////////////////////// }).call(this); /* Exports */ if (typeof Package === 'undefined') Package = {}; Package['callback-hook'] = { Hook: Hook }; })(); //# sourceMappingURL=callback-hook.js.map