Plato on Github
Report Home
src/define/class/super.js
Maintainability
77.70
Lines of code
234
Difficulty
16.85
Estimated Errors
0.66
Function weight
By Complexity
By SLOC
/** * @file Class method superify * @since 0.1.6 */ /*#ifndef(UMD)*/ "use strict"; /*global _GpfClassDefinition*/ // Class definition /*global _gpfErrorDeclare*/ // Declare new gpf.Error names /*global _gpfFunctionBuild*/ // Build function from description and context /*global _gpfFunctionDescribe*/ // Extract function description /*global _gpfRegExpForEach*/ // Executes the callback for each match of the regular expression /*exported _gpfClassMethodSuperify*/ // Create a method that can use this.$super /*#endif*/ _gpfErrorDeclare("define/class/super", { /** * ### Summary * * $super used in a member that doesn't override a method * * ### Description * * $super can't be used if the method does not override an inherited one * @since 0.1.7 */ invalidClassSuper: "Invalid class super", /** * ### Summary * * An invalid member of $super was used * * ### Description * * $super members must point to a method exposed by the inherited prototype. * @since 0.1.7 */ invalidClassSuperMember: "Invalid class super member" }); /** * Used when $super points to a non existent member * * @throws {gpf.Error.InvalidClassSuper} * @since 0.1.7 */ function _gpfClassNoSuper () { gpf.Error.invalidClassSuper(); } /** * Copy super method signature and invokes it. * NOTE: it is required to create a new function as it will receive additional members * * @param {Function} superMethod Super method to copy * @return {Function} New function that wraps the super method * @since 0.1.7 */ function _gpfClassSuperCreateWithSameSignature (superMethod) { var definition = _gpfFunctionDescribe(superMethod); definition.body = "return _superMethod_.apply(this, arguments);"; return _gpfFunctionBuild(definition, { _superMethod_: superMethod }); } /** * Create $super function, either based on super method or triggering an error * * @param {*} superMember Member extracted from inherited prototype * @return {Function} $super function * @since 0.1.7 */ function _gpfClassSuperCreate (superMember) { var superMethod; if (typeof superMember === "function") { superMethod = superMember; } else { superMethod = _gpfClassNoSuper; } return _gpfClassSuperCreateWithSameSignature(superMethod); } /** * Copy super method signature and apply weak binding. * * @param {Object} that Object instance * @param {Function} $super $super member * @param {*} superMethod superMember Member extracted from inherited prototype * @return {Function} $super method * @since 0.1.7 */ function _gpfClassSuperCreateWeakBoundWithSameSignature (that, $super, superMethod) { var definition = _gpfFunctionDescribe(superMethod); definition.body = "return _superMethod_.apply(this === _$super_ ? _that_ : this, arguments);"; return _gpfFunctionBuild(definition, { _that_: that, _$super_: $super, _superMethod_: superMethod }); } /** * Create $super method * NOTE: if the super method is not a function, an exception is thrown * * @param {Object} that Object instance * @param {Function} $super $super member * @param {*} superMethod superMember Member extracted from inherited prototype * @return {Function} $super method * @throws {gpf.Error.InvalidClassSuperMember} * @since 0.1.7 */ function _gpfClassSuperCreateMember (that, $super, superMethod) { if (typeof superMethod !== "function") { gpf.Error.invalidClassSuperMember(); } return _gpfClassSuperCreateWeakBoundWithSameSignature(that, $super, superMethod); } /** * Regular expression detecting .$super use * * @type {RegExp} * @since 0.2.1 */ var _gpfClassSuperRegExp = new RegExp("\\.\\$super\\.(\\w+)\\b", "g"), _GPF_CLASS_SUPER_MATCH_MEMBER = 1; /** * Extract all 'members' that are used on $super * * @param {Function} method Method to analyze * @return {String[]} Member names that are used * @since 0.1.7 */ function _gpfClassMethodExtractSuperMembers (method) { return _gpfRegExpForEach(_gpfClassSuperRegExp, method) .map(function (match) { return match[_GPF_CLASS_SUPER_MATCH_MEMBER]; }); } Object.assign(_GpfClassDefinition.prototype, { /** * Called before invoking a that contains $super method, it is responsible of allocating the $super object * * @param {Object} that Object instance * @param {String} methodName Name of the method that uses $super * @param {String[]} superMembers Expected member names on $super * @return {Function} $super method * @since 0.1.7 */ _get$Super: function (that, methodName, superMembers) { var superProto = this._extend.prototype, $super = _gpfClassSuperCreate(superProto[methodName]); superMembers.forEach(function (memberName) { $super[memberName] = _gpfClassSuperCreateMember(that, $super, superProto[memberName]); }); return $super; }, /** * Body of superified method * @since 0.1.7 */ _superifiedBody: "var _super_;\n" + "if (Object.prototype.hasOwnProperty.call(this, \"$super\")) {\n" + " _super_ = this.$super;\n" + "}\n" + "this.$super = _classDef_._get$Super(this, _methodName_, _superMembers_);\n" + "try{\n" + " var _result_ = _method_.apply(this, arguments);\n" + "} finally {\n" + " if (undefined === _super_) {\n" + " delete this.$super;\n" + " } else {\n" + " this.$super = _super_;\n" + " }\n" + "}\n" + "return _result_;", /** * Generates context for the superified method * * @param {Function} method Method to superify * @param {String} methodName Name of the method (used to search in object prototype) * @param {String[]} superMembers Detected $super members used in the method * @return {Object} Context of superified method * @since 0.1.7 */ _getSuperifiedContext: function (method, methodName, superMembers) { return { _method_: method, _methodName_: methodName, _superMembers_: superMembers, _classDef_: this }; }, /** * Generates the superified version of the method * * @param {Function} method Method to superify * @param {String} methodName Name of the method (used to search in object prototype) * @param {String[]} superMembers Detected $super members used in the method * @return {Function} Superified method * @since 0.1.7 */ _createSuperified: function (method, methodName, superMembers) { // Keep signature var description = _gpfFunctionDescribe(method); description.body = this._superifiedBody; return _gpfFunctionBuild(description, this._getSuperifiedContext(method, methodName, superMembers)); }, /** * Create a method that can use this.$super * * @param {Function} method Method to superify * @param {String} methodName Name of the method (used to search in object prototype) * @return {Function} Superified method * @since 0.1.7 */ _superify: function (method, methodName) { if (new RegExp("\\.\\$super\\b").exec(method)) { return this._createSuperified(method, methodName, _gpfClassMethodExtractSuperMembers(method)); } return method; } });