yahoo/preceptor-core

View on GitHub
lib/utils.js

Summary

Maintainability
A
2 hrs
Test Coverage
// Copyright 2014-2015, Yahoo! Inc.
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.

var _ = require('underscore');

/**
 * @class utils
 */

/**
 * An extend function that applies the result of a callback to each item
 *
 * @method extendApply
 * @param {object} obj Destination object to merge into
 * @param {object[]} objects Objects that should get merged into the destination object
 * @param {function} [fn] Apply function for each item, returning the final result
 * @return {object} Destination object
 */
var extendApply = function (obj, objects, fn) {
    fn = fn || function (item /* , previousItem, obj, objectIndex, valueIndex */) {
        return item;
    };
    objects.forEach(function (currentObject, objectIndex) {
        if (currentObject) {
            _.keys(currentObject).forEach(function (key, valueIndex) {
                obj[key] = fn(currentObject[key], obj[key], {
                    key: key, currentObject: currentObject, objectIndex: objectIndex, valueIndex: valueIndex
                });
            });
        }
    });
    return obj;
};

/**
 * Deep-extend of an object
 *
 * @method deepExtend
 * @param {object} obj
 * @param {object[]} objects
 * @param {object} [options]
 * @param {boolean} [options.replace=false]
 * @return {object}
 */
var deepExtend = function (obj, objects, options) {
    options = options || {};
    objects.forEach(function (currentObject) {
        if (currentObject) {
            _.keys(currentObject).forEach(function (key) {
                var i, len;
                if ((_.isArray(obj[key]) || !obj[key]) && _.isArray(currentObject[key])) {
                    if (options.replace) {
                        obj[key] = deepExtend(obj[key] || [], [currentObject[key]]);
                    } else {
                        obj[key] = (obj[key] || []);
                        for (i = 0, len = currentObject[key].length; i < len; i++) {
                            if (_.isArray(currentObject[key][i])) {
                                obj[key].push(deepExtend([], [currentObject[key][i]]));
                            } else if (_.isObject(currentObject[key][i])) {
                                obj[key].push(deepExtend({}, [currentObject[key][i]]));
                            } else {
                                obj[key].push(currentObject[key][i]);
                            }
                        }
                    }
                } else if (_.isFunction(currentObject[key])) {
                    obj[key] = currentObject[key];
                } else if ((_.isObject(obj[key]) || !obj[key]) && _.isObject(currentObject[key])) {
                    obj[key] = deepExtend(obj[key] || {}, [currentObject[key]]);
                } else {
                    obj[key] = currentObject[key];
                }
            });
        }
    });
    return obj;
};

/**
 * Wraps a function into another function that sets another function as the __super() function
 * This is used to wrap every single function of an object so that one can call
 *
 * this.__super();
 *
 * to call its parent function that was overwritten.
 *
 * @method superWrapper
 * @param {*} currentItem
 * @param {*} previousItem
 * @return {*}
 */
var superWrapper = function (currentItem, previousItem) {

    if (_.isFunction(currentItem) && (!previousItem || _.isFunction(previousItem))) {

        previousItem = previousItem || function () {
        };

        return function () {
            var result, self = this, oldSuper = self.__super;

            self.__super = function () {
                return previousItem.apply(self, arguments);
            };
            result = currentItem.apply(self, arguments);
            self.__super = oldSuper;

            return result;
        };

    } else {
        return currentItem;
    }
};

/**
 * Combines multiple strings into one, making sure that the glue-string doesn't get applied when not required
 *
 * @method combine
 * @param {string} glue
 * @param {string} str1
 * @return {string}
 */
var combine = function (glue, str1 /*, ... */) {
    var args = Array.prototype.slice.call(arguments, 2), result = str1;

    args.forEach(function (arg) {
        if ((result.substr(-1) === glue) && (arg.substr(0, 1) === glue)) {
            result += arg.substr(1);
        } else if ((result.substr(-1) === glue) || (arg.substr(0, 1) === glue)) {
            result += arg;
        } else {
            result += glue + arg;
        }
    });

    return result;
};

/**
 * Turns a string into a filesystem safe filename
 *
 * @method fileNameSafe
 * @param {string} str
 * @return {string}
 */
var fileNameSafe = function (str) {
    return str.replace(/[^a-zA-Z\d]/g, '-');
};

/**
 * Safely requires a module
 *
 * @method require
 * @param {string} module Module name or path
 * @param {*} [defaultValue] Default value if the module cannot be found
 * @return {*}
 */
var requir = function (module, defaultValue) {
    try {
        return require(module);
    } catch (err) {
        if (defaultValue === undefined) {
            throw new Error("Cannot find module: " + module);
        }
        return defaultValue;
    }
};

module.exports = {
    superWrapper: superWrapper,
    extendApply: extendApply,
    deepExtend: deepExtend,
    combine: combine,
    fileNameSafe: fileNameSafe,
    require: requir
};