UNPKG

useless

Version:

Use Less. Do More. JavaScript on steroids.

196 lines (147 loc) 8.3 kB
"use strict"; const _ = require ('underscore') /* Interceptable/observable methods ======================================================================== */ /* TODO: rewrite test */ _.deferTest ('bindable', function () { /* Test subject */ var obj = { plusOne: function (x) { return x + 1 }, innocentMethod: function (x) { return x } } $assertEveryCalled (function (before__1, after__1, intercept__2, secondIntercept__1, bindable__1, infixBefore__1) { /* That's how you observe method calls */ _.onBefore (obj, 'plusOne', function (x) { before__1 (); $assert (x === 7) }) _.onAfter (obj, 'plusOne', function (x, result) { after__1 (); $assert ([x, result], [7, 8]) }) $assert (obj.plusOne (7), 8) /* That's how you intercept method calls */ _.intercept (obj, 'innocentMethod', function (x, method) { intercept__2 () return method (x + 1) * 2 }) $assert (obj.innocentMethod (42), (42 + 1) * 2) /* Consequent interceptors wrap-up previous ones */ _.intercept (obj, 'innocentMethod', function (x, method) { secondIntercept__1 () $assert (method (x), (42 + 1) * 2) return 'hard boiled shit' }) $assert (obj.innocentMethod (42), 'hard boiled shit') /* Test infix calls */ var method = _.bindable (function (x) { bindable__1 (); $assert (x === 42) }) method.onBefore (function (x) { infixBefore__1 (); $assert (x === 42) }) method (42) }) /* Test 'once' semantics */ var obj2 = { plusOne: function (x) { return x + 1 } } $assertEveryCalledOnce (function (beforeCalled, afterCalled) { var before = function (x) { beforeCalled (); $assert (x === 7) } var after = function (x, result) { afterCalled (); $assert ([x, result], [7, 8]) } _.times (2, function () { _.onceBefore (obj, 'plusOne', before) _.onceAfter (obj, 'plusOne', after) }) $assert (obj.plusOne (7), 8) $assert (obj.plusOne (7), 8) }) /* Test unbinding */ $assertEveryCalled (function (afterCalled__1, shouldNotCall__0) { var method = _.bindable (function () {}) /* Unbind specific delegate */ method.onBefore (shouldNotCall__0) method.onAfter (afterCalled__1) method.off (shouldNotCall__0) method () /* Unbind everything */ method.onBefore (shouldNotCall__0) method.onAfter (shouldNotCall__0) method.off () method () }) }, function () { /* Internal impl */ var hooks = ['onceBefore', 'onceAfter', 'onBefore', 'onAfter', 'intercept'] var hooksShort = ['onceBefore', 'onceAfter', 'before', 'after', 'intercept'] var copyHooks = function (from, to) { _.extend (to, _.map2 (_.pick (from, hooks), _.clone)) } var makeBindable = function (obj, targetMethod) { var method = obj[targetMethod] return _.isBindable (method) ? method : (obj[targetMethod] = _.bindable (method)) } var hookProc = function (name) { return function (obj, targetMethod, delegate) { var bindable = makeBindable (obj, targetMethod) return bindable[name].call (bindable, delegate) } } var mixin = function (method, context) { if (typeof method !== 'function') { throw new Error ('method should be a function') } return _.extend ({}, method, { _bindable: true, impl: method, _wrapped: method, context: context, off: function (delegate) { _.each (hooks, function (hook) { if (delegate) { this['_' + hook].remove (delegate) } else { this['_' + hook].removeAll () } }, this); return this } }, /* .onBefore, .onAfter, .intercept (API methods) */ _.fromPairs (_.map (hooks, function (name) { var queueName = ('_' + name) var once = (name.indexOf ('once') >= 0) return [name, function (fn) { if (!_.isBindable (this)) { throw new Error ('wrong this') } var queue = this[queueName] if (!once || queue.indexOf (fn) < 0) { this[queueName].push (fn) } return this }] })), /* ._onBefore, ._onAfter, ._intercept (queues) */ _.fromPairs (_.map (hooks, function (name) { return ['_' + name, []] }))) } /* Public API */ _.extend (_, _.mapValues (_.invert (hooks), hookProc.flip2), { unbind: function (obj, targetMethod, delegate) { var method = obj[targetMethod] if (method && method.off) { method.off (delegate) } }, isBindable: function (fn) { return (fn && fn._bindable) ? true : false }, bindable: _.extendWith ({ hooks: hooks, hooksShort: hooksShort }, function (method, context) { return _.withSameArgs (method, _.extendWith (mixin (method, context), function wrapper (...args) { var onceBefore = wrapper._onceBefore var onceAfter = wrapper._onceAfter var before = wrapper._onBefore var after = wrapper._onAfter var intercept = wrapper._intercept var this_ = context || this var i, ni = undefined /* Call onceBefore */ if (onceBefore.length) { for (i = 0, ni = onceBefore.length; i < ni; i++) { onceBefore[i].apply (this_, args) } onceBefore.removeAll () } /* Call before */ for (i = 0, ni = before.length; i < ni; i++) { before[i].apply (this_, args) } /* Call intercept */ var result = (intercept.length ? _.cps.compose ([method].concat (intercept)) : method).apply (this_, args) // @hide if (after.length || onceAfter.length) { var newArgs = args.concat (result) /* Call after */ for (i = 0, ni = after.length; i < ni; i++) { after[i].apply (this_, newArgs) } /* Call onceAfter */ if (onceAfter.length) { var arr = onceAfter.copy onceAfter.removeAll () for (i = 0, ni = arr.length; i < ni; i++) { arr[i].apply (this_, newArgs) } } } return result } )) }) }) })