wrapper.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. /**
  6. * Utility methods used to wrap and extend functions.
  7. *
  8. * @example Usage of a 'wrap' method with arguments delegation.
  9. * var multiply = function (a, b) {
  10. * return a * b;
  11. * };
  12. *
  13. * multiply = module.wrap(multiply, function (orig) {
  14. * return 'Result is: ' + orig();
  15. * });
  16. *
  17. * multiply(2, 2);
  18. * => 'Result is: 4'
  19. *
  20. * @example Usage of 'wrapSuper' method.
  21. * var multiply = function (a, b) {
  22. * return a * b;
  23. * };
  24. *
  25. * var obj = {
  26. * multiply: module.wrapSuper(multiply, function () {
  27. * return 'Result is: ' + this._super();
  28. * });
  29. * };
  30. *
  31. * obj.multiply(2, 2);
  32. * => 'Result is: 4'
  33. */
  34. define([
  35. 'underscore'
  36. ], function (_) {
  37. 'use strict';
  38. /**
  39. * Checks if string has a '_super' substring.
  40. */
  41. var superReg = /\b_super\b/;
  42. return {
  43. /**
  44. * Wraps target function with a specified wrapper, which will receive
  45. * reference to the original function as a first argument.
  46. *
  47. * @param {Function} target - Function to be wrapped.
  48. * @param {Function} wrapper - Wrapper function.
  49. * @returns {Function} Wrapper function.
  50. */
  51. wrap: function (target, wrapper) {
  52. if (!_.isFunction(target) || !_.isFunction(wrapper)) {
  53. return wrapper;
  54. }
  55. return function () {
  56. var args = _.toArray(arguments),
  57. ctx = this,
  58. _super;
  59. /**
  60. * Function that will be passed to the wrapper.
  61. * If no arguments will be passed to it, then the original
  62. * function will be called with an arguments of a wrapper function.
  63. */
  64. _super = function () {
  65. var superArgs = arguments.length ? arguments : args.slice(1);
  66. return target.apply(ctx, superArgs);
  67. };
  68. args.unshift(_super);
  69. return wrapper.apply(ctx, args);
  70. };
  71. },
  72. /**
  73. * Wraps the incoming function to implement support of the '_super' method.
  74. *
  75. * @param {Function} target - Function to be wrapped.
  76. * @param {Function} wrapper - Wrapper function.
  77. * @returns {Function} Wrapped function.
  78. */
  79. wrapSuper: function (target, wrapper) {
  80. if (!this.hasSuper(wrapper) || !_.isFunction(target)) {
  81. return wrapper;
  82. }
  83. return function () {
  84. var _super = this._super,
  85. args = arguments,
  86. result;
  87. /**
  88. * Temporary define '_super' method which
  89. * contains call to the original function.
  90. */
  91. this._super = function () {
  92. var superArgs = arguments.length ? arguments : args;
  93. return target.apply(this, superArgs);
  94. };
  95. result = wrapper.apply(this, args);
  96. this._super = _super;
  97. return result;
  98. };
  99. },
  100. /**
  101. * Checks wether the incoming method contains calls of the '_super' method.
  102. *
  103. * @param {Function} fn - Function to be checked.
  104. * @returns {Boolean}
  105. */
  106. hasSuper: function (fn) {
  107. return _.isFunction(fn) && superReg.test(fn);
  108. },
  109. /**
  110. * Extends target object with provided extenders.
  111. * If property in target and extender objects is a function,
  112. * then it will be wrapped using 'wrap' method.
  113. *
  114. * @param {Object} target - Object to be extended.
  115. * @param {...Object} extenders - Multiple extenders objects.
  116. * @returns {Object} Modified target object.
  117. */
  118. extend: function (target) {
  119. var extenders = _.toArray(arguments).slice(1),
  120. iterator = this._extend.bind(this, target);
  121. extenders.forEach(iterator);
  122. return target;
  123. },
  124. /**
  125. * Same as the 'extend' method, but operates only on one extender object.
  126. *
  127. * @private
  128. * @param {Object} target
  129. * @param {Object} extender
  130. */
  131. _extend: function (target, extender) {
  132. _.each(extender, function (value, key) {
  133. target[key] = this.wrap(target[key], extender[key]);
  134. }, this);
  135. }
  136. };
  137. });