<commit_before>'use strict';

/**
 *  A static library.
 *  Check (library) is designed to provide foolproof, simple validation for
 *  aspects of variables and arguments.  A Check routine, by definition will
 *  throw a CheckException if the check fails.  Thus, a CheckException may
 *  be thought of as a kind of true/false value.  It's useful to throw
 *  an exception when this is false, to program in the "fail fast" mode,
 *  which catches errors early.
 *
 *  Other exception types:
 *  ----------------------
 *  The other type of exception, besides CheckException, thrown by this
 *  library is ArgTypeException, which means that a programming error
 *  was made in calling this library (or there could be a bug in this library).
 *  ArgTypeException means the true/false value is not legitimate because
 *  the test couldn't be conducted properly.  For example "inArray" test
 *  will throw ArgTypeException if the second argument is not an array.
 *  This is not a bona fide true/false value (CheckException) because
 *  the second arg was not an array, and the test can't be carried out
 *  (and this is, of course, a programming error).
 *
 *  It is recommended that you convert CheckExceptions to your own
 *  type.  For example if you are checking arguments at the start of
 *  a function call, you could convert the CheckException to
 *  ArgTypeException.  The following code does that, as well as
 *  provides a better message in the exception.
 *
 *      Check.isMap (mapArgument1, {
 *          exception : ArgTypeException,
 *          message : "Params.copy 3rd arg (options) must be a map."
 *      });
 *
 *  Thus, if you follow this convention, a CheckException is really just
 *  an "unwrapped failure".  This is fine, but you may want to examine
 *  the failure, and change exception and message to be more helpful.
 *
 *  About the checks:
 *  -----------------
 *  Some of the qualities we are checking for are defined in ways to make
 *  coding less ambiguous.  Thus, we take some liberties to slightly
 *  change what might be considered a "native Javascript" check.  A
 *  perfect example of this is "null" being an Object (Check doesn't
 *  consider this to be true).
 *
 *  Note for the purpose of OOP, the prototype is considered the "class".
 *  In calls with objects that have an argument named "constructor",
 *  the intent is to pass the constructor, since that has a name that
 *  can be used.  Passing an object with a prototype will generate a
 *  ArgTypeException.
 *
 *  Making new checks:
 *  ------------------
 *  Can be done by writing a truthFunction.  This returns no value, but
 *  will throw a CheckException on a valid "false" value, and other
 *  exceptions on some type of programming error (e.g. ArgTypeException).
 *  The wrapper function Check.twoArgTruth () will examine the type of
 *  the exception to decide what to do.
 */

var Options = require ('Options');
var Describe = require ('Describe');
var CheckException = require ('CheckException');
var ArgTypeException = require ('ArgTypeException');

var Check = {};

/**
 *  Generic wrapper function for Check functions.  truthFunction must
 *  throw an exception if the test fails.  If it fails on testing the
 *  value, it must throw a CheckException, otherwise any other
 *  exception thrown is considered a programming error and is passed out.
 *  Thus, even if options = { throws : false }, we will always throw
 *  programming exceptions out, but true/false results (which are
 *  expressed as CheckExceptions), do not throw if throws = false.
 */
Check.twoArgTruth = function (obj, obj2, truthFunction, options) {
    if (options === undefined)
        options = {};

    // Is options a Map?
    try {
        Check._instanceOf (options, Object);
    } catch (e) {
        throw new ArgTypeException ("Fourth arg (options) must be a map.  " +
            "It is a " + Describe.get (options) + ".");
    }

    try {
        Check._keysInArray (options, ['throws', 'exception', 'message']);
    } catch (e) {
        throw new ArgTypeException ("Fourth arg (options) must be a map.  " +
            "It is a " + Describe.get (options) + ".");
    }

    var throws = Options.get ('throws', options, true);
    var exceptionCtor = Options.get ('exception', options);
    var message = Options.get ('message', options, "");

    var exc;
    try {
        truthFunction (obj, obj2);
    } catch (e) {
        exc = e;
    }

    // truthFunctions are expected to throw on false or error

    if (exc) {
        // We will still throw if this is a programming error (i.e.
        // truthFunction () throws something that is not a CheckException

        var excIsCheckException = Object.getPrototypeOf (exc) ===
            CheckException.prototype;

        if (throws || ! excIsCheckException) {
            message = message + " " + exc;
            message = message + "  ArgType=" + Describe.get (obj) + ".";

            // If truthFunction throws a CheckException, we need to take
            // its message and enhance it (i.e. with the Arg description),
            // otherwise we rethrow.

            if (excIsCheckException) {
                if (exceptionCtor)
                    throw new exceptionCtor (message);
                throw new CheckException (message);
            }

            // else rethrow
            throw exc;
        }

        return false;
    }

    return true;
}

/**
 * This is equivalent to saying something is the same "class"
 */
Check._hasSamePrototype = function (value, constructor) {
    if (value === undefined || value === null)
        throw new CheckException ("First arg (value) is undefined or null");

    if (constructor === undefined || constructor === null)
        throw new ArgTypeException ("Second arg (constructor) is " +
            "undefined or null");

    try {
        Check._isFunction (constructor);
    } catch (e) {
        throw new ArgTypeException ("Second arg (constructor) is not a " +
            "function");
    }

    if (constructor.prototype === null)
        throw new ArgTypeException ("Second arg (constructor), prototype " +
            "property is null");

    if (constructor.prototype === undefined)
        throw new ArgTypeException ("Second arg (constructor), prototype " +
            "property is undefined");

    var name = "(with no name)";
    if ( ! (constructor.name === undefined))
        name = constructor.name;

    var prototype1;

    try {
        prototype1 = Object.getPrototypeOf (value);
    } catch (e) {
        throw new CheckException (Describe.get (value) +
            " has no prototype (not an object) compared to constructor '" +
            name + "'");
    }

    if (prototype1 != constructor.prototype)
        throw new CheckException (Describe.get (value) +
            " has a different prototype than constructor '" + name + "'");
}

Check.hasSamePrototype = function (value, constructor, options) {
    return Check.twoArgTruth (value, constructor, Check._hasSamePrototype,
        options);
}

Check._isArray = function (value) {
    if ( ! Array.isArray (value))
        throw new CheckException ("The value is not an array.");
}

Check.isArray = function (value, options) {
    return Check.twoArgTruth (value, null, Check._isArray, options);
}

Check._inArray = function (value, arr) {
    if (arr.indexOf (value) < 0)
        throw new CheckException ("The value '" + value + "' is not " +
            "in the array.");
}

Check.inArray = function (value, arr, options) {
    return Check.twoArgTruth (value, arr, Check._inArray, options);
}

Check._keysInArray = function (value, arr) {
    var keys = Object.keys (value),
        len = keys.length;
    for (var i = 0 ; i < len ; i++) {
        var k = keys [i];
        if (arr.indexOf (k) < 0)
            throw new CheckException ("Key '" + k + "' not found in array.");
    }
}

Check.keysInArray = function (value, arr, options) {
    return Check.twoArgTruth (value, arr, Check._keysInArray, options);
}

Check._inMapAndNotEmpty = function (value, map) {
    try {
        Check._isMap (map);
    } catch (e) {
        throw new ArgTypeException ("Second arg is not a map.");
    }

    if ( ! (value in map))
        throw new CheckException ("Value (key) '" + value + "' is not in " +
            "map.");

    var v = map [value];
    if (v === undefined || v === null)
        throw new CheckException ("Value (key) '" + value + "' is in " +
            "map, but is empty (null or undefined).");
}

Check.inMapAndNotEmpty = function (value, map, options) {
    return Check.twoArgTruth (value, map, Check._inMapAndNotEmpty, options);
}

Check._isInteger = function (value) {
    if ((undefined === value) || (null === value))
        throw new CheckException ("Value '" + value + "' is not an " +
            "integer.");

    if ( ! (value % 1 == 0))
        throw new CheckException ("Value '" + value + "' is not an " +
            "integer.");
}

Check.isInteger = function (value, options) {
    return Check.twoArgTruth (value, undefined, Check._isInteger, options);
}

Check._isUndefined = function (value) {
    if ( ! (undefined === value))
        throw new CheckException ("Value '" + value + "' is not undefined.");
}

Check.isUndefined = function (value, options) {
    return Check.twoArgTruth (value, undefined, Check._isUndefined, options);
}

Check._isDefined = function (value) {
    if (undefined === value)
        throw new CheckException ("Value '" + value + "' must be defined.");
}

Check.isDefined = function (value, options) {
    return Check.twoArgTruth (value, undefined, Check._isUndefined, options);
}

/**
 * Synonym for hasSamePrototype
 */
Check.hasClass = function (value, constructor, options) {
    return Check.hasSamePrototype (value, constructor, options);
}

/**
 * Determines whether the class is "Object" (e.g. {})
 */
Check.hasClassOfObject = function (value, options) {
    return Check.hasSamePrototype (value, Object, options);
}

Check._isFunction = function (value) {
    var isFunc = !! (value && value.constructor && value.call && value.apply);

    if ( ! isFunc)
        throw new CheckException ("Value is not a function.");
}

Check.isFunction = function (value, options) {
    return Check.twoArgTruth (value, undefined, Check._isFunction, options);
}

/**
 * Determines whether the class is "Object" (e.g. {})
 */

Check._instanceOf = function (value, constructor) {
    try {
        Check._isFunction (constructor);
    } catch (e) {
        throw new ArgTypeException ("Second arg (constructor) must be " +
            "a function");
    }

    if ( ! (value instanceof constructor))
        throw new CheckException ("Value '" + Describe.get (value) + "' is " +
            "not an instanceof constructor '" + Describe.get (constructor) +
            "'.");
}

Check.instanceOf = function (value, constructor, options) {
    return Check.twoArgTruth (value, constructor, Check._instanceOf, options);
}

/**
 *  Synonym for instanceOf
 */
Check.isA = function (value, constructor, options) {
    return Check.instanceOf (value, constructor, options);
}

/**
 * Determines whether the class is "Object" (e.g. {})
 */
Check.isObject = function (value, options) {
    return Check.instanceOf (value, Object, options);
}

/**
 * A map is an object in Javascript.  The intent here is to determine
 * whether something is an object literal.
 */
Check._isMap = function (value) {
    Check._instanceOf (value, Object);
}

Check.isMap = function (value, options) {
    return Check.twoArgTruth (value, Object, Check._isMap, options);
}

module.exports = Check;
<commit_msg>Improved message for exception ctor override in Check<commit_after>'use strict';

/**
 *  A static library.
 *  Check (library) is designed to provide foolproof, simple validation for
 *  aspects of variables and arguments.  A Check routine, by definition will
 *  throw a CheckException if the check fails.  Thus, a CheckException may
 *  be thought of as a kind of true/false value.  It's useful to throw
 *  an exception when this is false, to program in the "fail fast" mode,
 *  which catches errors early.
 *
 *  Other exception types:
 *  ----------------------
 *  The other type of exception, besides CheckException, thrown by this
 *  library is ArgTypeException, which means that a programming error
 *  was made in calling this library (or there could be a bug in this library).
 *  ArgTypeException means the true/false value is not legitimate because
 *  the test couldn't be conducted properly.  For example "inArray" test
 *  will throw ArgTypeException if the second argument is not an array.
 *  This is not a bona fide true/false value (CheckException) because
 *  the second arg was not an array, and the test can't be carried out
 *  (and this is, of course, a programming error).
 *
 *  It is recommended that you convert CheckExceptions to your own
 *  type.  For example if you are checking arguments at the start of
 *  a function call, you could convert the CheckException to
 *  ArgTypeException.  The following code does that, as well as
 *  provides a better message in the exception.
 *
 *      Check.isMap (mapArgument1, {
 *          exception : ArgTypeException,
 *          message : "Params.copy 3rd arg (options) must be a map."
 *      });
 *
 *  Thus, if you follow this convention, a CheckException is really just
 *  an "unwrapped failure".  This is fine, but you may want to examine
 *  the failure, and change exception and message to be more helpful.
 *
 *  About the checks:
 *  -----------------
 *  Some of the qualities we are checking for are defined in ways to make
 *  coding less ambiguous.  Thus, we take some liberties to slightly
 *  change what might be considered a "native Javascript" check.  A
 *  perfect example of this is "null" being an Object (Check doesn't
 *  consider this to be true).
 *
 *  Note for the purpose of OOP, the prototype is considered the "class".
 *  In calls with objects that have an argument named "constructor",
 *  the intent is to pass the constructor, since that has a name that
 *  can be used.  Passing an object with a prototype will generate a
 *  ArgTypeException.
 *
 *  Making new checks:
 *  ------------------
 *  Can be done by writing a truthFunction.  This returns no value, but
 *  will throw a CheckException on a valid "false" value, and other
 *  exceptions on some type of programming error (e.g. ArgTypeException).
 *  The wrapper function Check.twoArgTruth () will examine the type of
 *  the exception to decide what to do.
 */

var Options = require ('Options');
var Describe = require ('Describe');
var CheckException = require ('CheckException');
var ArgTypeException = require ('ArgTypeException');

var Check = {};

/**
 *  Generic wrapper function for Check functions.  truthFunction must
 *  throw an exception if the test fails.  If it fails on testing the
 *  value, it must throw a CheckException, otherwise any other
 *  exception thrown is considered a programming error and is passed out.
 *  Thus, even if options = { throws : false }, we will always throw
 *  programming exceptions out, but true/false results (which are
 *  expressed as CheckExceptions), do not throw if throws = false.
 */
Check.twoArgTruth = function (obj, obj2, truthFunction, options) {
    if (options === undefined)
        options = {};

    // Is options a Map?
    try {
        Check._instanceOf (options, Object);
    } catch (e) {
        throw new ArgTypeException ("Fourth arg (options) must be a map.  " +
            "It is a " + Describe.get (options) + ".");
    }

    try {
        Check._keysInArray (options, ['throws', 'exception', 'message']);
    } catch (e) {
        throw new ArgTypeException ("Fourth arg (options) must be a map.  " +
            "It is a " + Describe.get (options) + ".");
    }

    var throws = Options.get ('throws', options, true);
    var exceptionCtor = Options.get ('exception', options);
    var message = Options.get ('message', options, "");

    var exc;
    try {
        truthFunction (obj, obj2);
    } catch (e) {
        exc = e;
    }

    // truthFunctions are expected to throw on false or error

    if (exc) {
        // We will still throw if this is a programming error (i.e.
        // truthFunction () throws something that is not a CheckException

        var excIsCheckException = Object.getPrototypeOf (exc) ===
            CheckException.prototype;

        if (throws || ! excIsCheckException) {
	    if (message.length)
		message = message + " ";

	    message += exc;
            message += "  ArgType=" + Describe.get (obj) + ".";

	    // If this starts with "CheckException:', remove this part

	    var pattern = "CheckException: ";
	    if (message.indexOf (pattern) == 0)
		message = message.substring (pattern.length);

            // If truthFunction throws a CheckException, we need to take
            // its message and enhance it (i.e. with the Arg description),
            // otherwise we rethrow.

            if (excIsCheckException) {
                if (typeof (exceptionCtor) === "function")
                    throw new exceptionCtor (message);
                throw new CheckException (message);
            }

            // else rethrow
            throw exc;
        }

        return false;
    }

    return true;
}

/**
 * This is equivalent to saying something is the same "class"
 */
Check._hasSamePrototype = function (value, constructor) {
    if (value === undefined || value === null)
        throw new CheckException ("First arg (value) is undefined or null");

    if (constructor === undefined || constructor === null)
        throw new ArgTypeException ("Second arg (constructor) is " +
            "undefined or null");

    try {
        Check._isFunction (constructor);
    } catch (e) {
        throw new ArgTypeException ("Second arg (constructor) is not a " +
            "function");
    }

    if (constructor.prototype === null)
        throw new ArgTypeException ("Second arg (constructor), prototype " +
            "property is null");

    if (constructor.prototype === undefined)
        throw new ArgTypeException ("Second arg (constructor), prototype " +
            "property is undefined");

    var name = "(with no name)";
    if ( ! (constructor.name === undefined))
        name = constructor.name;

    var prototype1;

    try {
        prototype1 = Object.getPrototypeOf (value);
    } catch (e) {
        throw new CheckException (Describe.get (value) +
            " has no prototype (not an object) compared to constructor '" +
            name + "'");
    }

    if (prototype1 != constructor.prototype)
        throw new CheckException (Describe.get (value) +
            " has a different prototype than constructor '" + name + "'");
}

Check.hasSamePrototype = function (value, constructor, options) {
    return Check.twoArgTruth (value, constructor, Check._hasSamePrototype,
        options);
}

Check._isArray = function (value) {
    if ( ! Array.isArray (value))
        throw new CheckException ("The value is not an array.");
}

Check.isArray = function (value, options) {
    return Check.twoArgTruth (value, null, Check._isArray, options);
}

Check._inArray = function (value, arr) {
    if (arr.indexOf (value) < 0)
        throw new CheckException ("The value '" + value + "' is not " +
            "in the array.");
}

Check.inArray = function (value, arr, options) {
    return Check.twoArgTruth (value, arr, Check._inArray, options);
}

Check._keysInArray = function (value, arr) {
    var keys = Object.keys (value),
        len = keys.length;
    for (var i = 0 ; i < len ; i++) {
        var k = keys [i];
        if (arr.indexOf (k) < 0)
            throw new CheckException ("Key '" + k + "' not found in array.");
    }
}

Check.keysInArray = function (value, arr, options) {
    return Check.twoArgTruth (value, arr, Check._keysInArray, options);
}

Check._inMapAndNotEmpty = function (value, map) {
    try {
        Check._isMap (map);
    } catch (e) {
        throw new ArgTypeException ("Second arg is not a map.");
    }

    if ( ! (value in map))
        throw new CheckException ("Value (key) '" + value + "' is not in " +
            "map.");

    var v = map [value];
    if (v === undefined || v === null)
        throw new CheckException ("Value (key) '" + value + "' is in " +
            "map, but is empty (null or undefined).");
}

Check.inMapAndNotEmpty = function (value, map, options) {
    return Check.twoArgTruth (value, map, Check._inMapAndNotEmpty, options);
}

Check._isInteger = function (value) {
    if ((undefined === value) || (null === value))
        throw new CheckException ("Value '" + value + "' is not an " +
            "integer.");

    if ( ! (value % 1 == 0))
        throw new CheckException ("Value '" + value + "' is not an " +
            "integer.");
}

Check.isInteger = function (value, options) {
    return Check.twoArgTruth (value, undefined, Check._isInteger, options);
}

Check._isUndefined = function (value) {
    if ( ! (undefined === value))
        throw new CheckException ("Value '" + value + "' is not undefined.");
}

Check.isUndefined = function (value, options) {
    return Check.twoArgTruth (value, undefined, Check._isUndefined, options);
}

Check._isDefined = function (value) {
    if (undefined === value)
        throw new CheckException ("Value '" + value + "' must be defined.");
}

Check.isDefined = function (value, options) {
    return Check.twoArgTruth (value, undefined, Check._isUndefined, options);
}

/**
 * Synonym for hasSamePrototype
 */
Check.hasClass = function (value, constructor, options) {
    return Check.hasSamePrototype (value, constructor, options);
}

/**
 * Determines whether the class is "Object" (e.g. {})
 */
Check.hasClassOfObject = function (value, options) {
    return Check.hasSamePrototype (value, Object, options);
}

Check._isFunction = function (value) {
    var isFunc = !! (value && value.constructor && value.call && value.apply);

    if ( ! isFunc)
        throw new CheckException ("Value is not a function.");
}

Check.isFunction = function (value, options) {
    return Check.twoArgTruth (value, undefined, Check._isFunction, options);
}

/**
 * Determines whether the class is "Object" (e.g. {})
 */

Check._instanceOf = function (value, constructor) {
    try {
        Check._isFunction (constructor);
    } catch (e) {
        throw new ArgTypeException ("Second arg (constructor) must be " +
            "a function");
    }

    if ( ! (value instanceof constructor))
        throw new CheckException ("Value '" + Describe.get (value) + "' is " +
            "not an instanceof constructor '" + Describe.get (constructor) +
            "'.");
}

Check.instanceOf = function (value, constructor, options) {
    return Check.twoArgTruth (value, constructor, Check._instanceOf, options);
}

/**
 *  Synonym for instanceOf
 */
Check.isA = function (value, constructor, options) {
    return Check.instanceOf (value, constructor, options);
}

/**
 * Determines whether the class is "Object" (e.g. {})
 */
Check.isObject = function (value, options) {
    return Check.instanceOf (value, Object, options);
}

/**
 * A map is an object in Javascript.  The intent here is to determine
 * whether something is an object literal.
 */
Check._isMap = function (value) {
    Check._instanceOf (value, Object);
}

Check.isMap = function (value, options) {
    return Check.twoArgTruth (value, Object, Check._isMap, options);
}

module.exports = Check;
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
function Chunk(name, module, loc) {
	this.id = null;
	this.ids = null;
	this.name = name;
	this.modules = [];
	this.chunks = [];
	this.parents = [];
	this.blocks = [];
	this.origins = [];
	this.files = [];
	this.rendered = false;
	this.entry = false;
	this.initial = false;
	if(module) {
		this.origins.push({
			module: module,
			loc: loc,
			name: name
		});
	}
}
module.exports = Chunk;

Chunk.prototype.addModule = function(module) {
	if(this.modules.indexOf(module) >= 0) {
		return false;
	}
	this.modules.push(module);
	return true;
};

Chunk.prototype._removeAndDo = require("./removeAndDo");

Chunk.prototype.removeModule = function(module) {
	this._removeAndDo("modules", module, "removeChunk");
};

Chunk.prototype.removeChunk = function(chunk) {
	this._removeAndDo("chunks", chunk, "removeParent");
};

Chunk.prototype.removeParent = function(chunk) {
	this._removeAndDo("parents", chunk, "removeChunk");
};

function createAdder(collection) {
	return function(chunk) {
		if(chunk === this) {
			return false;
		}
		if(this[collection].indexOf(chunk) >= 0) {
			return false;
		}
		this[collection].push(chunk);
		return true;
	};
}

Chunk.prototype.addChunk = createAdder("chunks");

Chunk.prototype.addParent = createAdder("parents");

Chunk.prototype.addBlock = function(block) {
	if(this.blocks.indexOf(block) >= 0) {
		return false;
	}
	this.blocks.push(block);
	return true;
};

Chunk.prototype.addOrigin = function(module, loc) {
	this.origins.push({module: module, loc: loc, name: this.name});
};

Chunk.prototype.remove = function(reason) {
	this.modules.slice().forEach(function(m) {
		m.removeChunk(this);
	}, this);
	this.parents.forEach(function(c) {
		var idx = c.chunks.indexOf(this);
		if(idx >= 0) {
			c.chunks.splice(idx, 1);
		}
		this.chunks.forEach(function(cc) {
			cc.addParent(c);
		});
	}, this);
	this.chunks.forEach(function(c) {
		var idx = c.parents.indexOf(this);
		if(idx >= 0) {
			c.parents.splice(idx, 1);
		}
		this.parents.forEach(function(cc) {
			cc.addChunk(c);
		});
	}, this);
	this.blocks.forEach(function(b) {
		var idx = b.chunks.indexOf(this);
		if(idx >= 0) {
			b.chunks.splice(idx, 1);
			if(b.chunks.length === 0) {
				b.chunks = null;
				b.chunkReason = reason;
			}
		}
	}, this);
};

Chunk.prototype.integrate = function(other, reason) {
	if(!this.canBeIntegrated(other)) {
		return false;
	}

	var otherModules = other.modules.slice();
	otherModules.forEach(function(m) {
		m.removeChunk(other);
		m.addChunk(this);
		this.addModule(m);
		m.rewriteChunkInReasons(other, [this]);
	}, this);
	other.modules.length = 0;
	function moveChunks(chunks, kind, onChunk) {
		chunks.forEach(function(c) {
			var idx = c[kind].indexOf(other);
			if(idx >= 0) {
				c[kind].splice(idx, 1);
			}
			onChunk(c);
		});
	}
	moveChunks(other.parents, "chunks", function(c) {
		if(c !== this && this.addParent(c)) {
			c.addChunk(this);
		}
	}.bind(this));
	other.parents.length = 0;
	moveChunks(other.chunks, "parents", function(c) {
		if(c !== this && this.addChunk(c)) {
			c.addParent(this);
		}
	}.bind(this));
	other.chunks.length = 0;
	other.blocks.forEach(function(b) {
		b.chunks = (b.chunks || [this]).map(function(c) {
			return c === other ? this : c;
		}, this);
		b.chunkReason = reason;
		this.addBlock(b);
	}, this);
	other.blocks.length = 0;
	other.origins.forEach(function(origin) {
		if(!origin.reasons) {
			origin.reasons = [reason];
		} else if(origin.reasons[0] !== reason) {
			origin.reasons.unshift(reason);
		}
		this.origins.push(origin);
	}, this);
	return true;
};

Chunk.prototype.isEmpty = function() {
	return (this.modules.length === 0);
};

Chunk.prototype.updateHash = function(hash) {
	hash.update(this.id + " ");
	hash.update(this.ids ? this.ids.join(",") : "");
	hash.update(this.name + "");
	this.modules.forEach(function(m) {
		m.updateHash(hash);
	});
};

Chunk.prototype.size = function(options) {
	var CHUNK_OVERHEAD = options.chunkOverhead || 10000;
	var ENTRY_CHUNK_MULTIPLICATOR = options.entryChunkMultiplicator || 10;

	var modulesSize = this.modules.map(function(m) {
		return m.size();
	}).reduce(function(a, b) {
		return a + b;
	}, 0);
	return modulesSize * (this.initial ? ENTRY_CHUNK_MULTIPLICATOR : 1) + CHUNK_OVERHEAD;
};

Chunk.prototype.canBeIntegrated = function(other) {
	if(other.initial) {
		return false;
	}
	if(this.initial) {
		if(other.parents.length !== 1 || other.parents[0] !== this) {
			return false;
		}
	}
	return true;
};

Chunk.prototype.integratedSize = function(other, options) {
	// Chunk if it's possible to integrate this chunks
	if(!this.canBeIntegrated(other)) {
		return false;
	}

	var CHUNK_OVERHEAD = options.chunkOverhead || 10000;
	var ENTRY_CHUNK_MULTIPLICATOR = options.entryChunkMultiplicator || 10;

	var mergedModules = this.modules.slice();
	other.modules.forEach(function(m) {
		if(this.modules.indexOf(m) < 0) {
			mergedModules.push(m);
		}
	}, this);

	var modulesSize = mergedModules.map(function(m) {
		return m.size();
	}).reduce(function(a, b) {
		return a + b;
	}, 0);
	return modulesSize * (this.initial || other.initial ? ENTRY_CHUNK_MULTIPLICATOR : 1) + CHUNK_OVERHEAD;
};

Chunk.prototype.getChunkMaps = function(includeEntries) {
	var chunksProcessed = [];
	var chunkHashMap = {};
	var chunkNameMap = {};
	(function addChunk(c) {
		if(chunksProcessed.indexOf(c) >= 0) return;
		chunksProcessed.push(c);
		if(!c.entry || includeEntries) {
			chunkHashMap[c.id] = c.renderedHash;
			if(c.name)
				chunkNameMap[c.id] = c.name;
		}
		c.chunks.forEach(addChunk);
	}(this));
	return {
		hash: chunkHashMap,
		name: chunkNameMap
	};
};

Chunk.prototype.toString = function() {
	return "Chunk[" + this.modules.join() + "]";
};
<commit_msg>Fix typo in Chunk.js<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
function Chunk(name, module, loc) {
	this.id = null;
	this.ids = null;
	this.name = name;
	this.modules = [];
	this.chunks = [];
	this.parents = [];
	this.blocks = [];
	this.origins = [];
	this.files = [];
	this.rendered = false;
	this.entry = false;
	this.initial = false;
	if(module) {
		this.origins.push({
			module: module,
			loc: loc,
			name: name
		});
	}
}
module.exports = Chunk;

Chunk.prototype.addModule = function(module) {
	if(this.modules.indexOf(module) >= 0) {
		return false;
	}
	this.modules.push(module);
	return true;
};

Chunk.prototype._removeAndDo = require("./removeAndDo");

Chunk.prototype.removeModule = function(module) {
	this._removeAndDo("modules", module, "removeChunk");
};

Chunk.prototype.removeChunk = function(chunk) {
	this._removeAndDo("chunks", chunk, "removeParent");
};

Chunk.prototype.removeParent = function(chunk) {
	this._removeAndDo("parents", chunk, "removeChunk");
};

function createAdder(collection) {
	return function(chunk) {
		if(chunk === this) {
			return false;
		}
		if(this[collection].indexOf(chunk) >= 0) {
			return false;
		}
		this[collection].push(chunk);
		return true;
	};
}

Chunk.prototype.addChunk = createAdder("chunks");

Chunk.prototype.addParent = createAdder("parents");

Chunk.prototype.addBlock = function(block) {
	if(this.blocks.indexOf(block) >= 0) {
		return false;
	}
	this.blocks.push(block);
	return true;
};

Chunk.prototype.addOrigin = function(module, loc) {
	this.origins.push({module: module, loc: loc, name: this.name});
};

Chunk.prototype.remove = function(reason) {
	this.modules.slice().forEach(function(m) {
		m.removeChunk(this);
	}, this);
	this.parents.forEach(function(c) {
		var idx = c.chunks.indexOf(this);
		if(idx >= 0) {
			c.chunks.splice(idx, 1);
		}
		this.chunks.forEach(function(cc) {
			cc.addParent(c);
		});
	}, this);
	this.chunks.forEach(function(c) {
		var idx = c.parents.indexOf(this);
		if(idx >= 0) {
			c.parents.splice(idx, 1);
		}
		this.parents.forEach(function(cc) {
			cc.addChunk(c);
		});
	}, this);
	this.blocks.forEach(function(b) {
		var idx = b.chunks.indexOf(this);
		if(idx >= 0) {
			b.chunks.splice(idx, 1);
			if(b.chunks.length === 0) {
				b.chunks = null;
				b.chunkReason = reason;
			}
		}
	}, this);
};

Chunk.prototype.integrate = function(other, reason) {
	if(!this.canBeIntegrated(other)) {
		return false;
	}

	var otherModules = other.modules.slice();
	otherModules.forEach(function(m) {
		m.removeChunk(other);
		m.addChunk(this);
		this.addModule(m);
		m.rewriteChunkInReasons(other, [this]);
	}, this);
	other.modules.length = 0;
	function moveChunks(chunks, kind, onChunk) {
		chunks.forEach(function(c) {
			var idx = c[kind].indexOf(other);
			if(idx >= 0) {
				c[kind].splice(idx, 1);
			}
			onChunk(c);
		});
	}
	moveChunks(other.parents, "chunks", function(c) {
		if(c !== this && this.addParent(c)) {
			c.addChunk(this);
		}
	}.bind(this));
	other.parents.length = 0;
	moveChunks(other.chunks, "parents", function(c) {
		if(c !== this && this.addChunk(c)) {
			c.addParent(this);
		}
	}.bind(this));
	other.chunks.length = 0;
	other.blocks.forEach(function(b) {
		b.chunks = (b.chunks || [this]).map(function(c) {
			return c === other ? this : c;
		}, this);
		b.chunkReason = reason;
		this.addBlock(b);
	}, this);
	other.blocks.length = 0;
	other.origins.forEach(function(origin) {
		if(!origin.reasons) {
			origin.reasons = [reason];
		} else if(origin.reasons[0] !== reason) {
			origin.reasons.unshift(reason);
		}
		this.origins.push(origin);
	}, this);
	return true;
};

Chunk.prototype.isEmpty = function() {
	return (this.modules.length === 0);
};

Chunk.prototype.updateHash = function(hash) {
	hash.update(this.id + " ");
	hash.update(this.ids ? this.ids.join(",") : "");
	hash.update(this.name + "");
	this.modules.forEach(function(m) {
		m.updateHash(hash);
	});
};

Chunk.prototype.size = function(options) {
	var CHUNK_OVERHEAD = options.chunkOverhead || 10000;
	var ENTRY_CHUNK_MULTIPLICATOR = options.entryChunkMultiplicator || 10;

	var modulesSize = this.modules.map(function(m) {
		return m.size();
	}).reduce(function(a, b) {
		return a + b;
	}, 0);
	return modulesSize * (this.initial ? ENTRY_CHUNK_MULTIPLICATOR : 1) + CHUNK_OVERHEAD;
};

Chunk.prototype.canBeIntegrated = function(other) {
	if(other.initial) {
		return false;
	}
	if(this.initial) {
		if(other.parents.length !== 1 || other.parents[0] !== this) {
			return false;
		}
	}
	return true;
};

Chunk.prototype.integratedSize = function(other, options) {
	// Chunk if it's possible to integrate this chunk
	if(!this.canBeIntegrated(other)) {
		return false;
	}

	var CHUNK_OVERHEAD = options.chunkOverhead || 10000;
	var ENTRY_CHUNK_MULTIPLICATOR = options.entryChunkMultiplicator || 10;

	var mergedModules = this.modules.slice();
	other.modules.forEach(function(m) {
		if(this.modules.indexOf(m) < 0) {
			mergedModules.push(m);
		}
	}, this);

	var modulesSize = mergedModules.map(function(m) {
		return m.size();
	}).reduce(function(a, b) {
		return a + b;
	}, 0);
	return modulesSize * (this.initial || other.initial ? ENTRY_CHUNK_MULTIPLICATOR : 1) + CHUNK_OVERHEAD;
};

Chunk.prototype.getChunkMaps = function(includeEntries) {
	var chunksProcessed = [];
	var chunkHashMap = {};
	var chunkNameMap = {};
	(function addChunk(c) {
		if(chunksProcessed.indexOf(c) >= 0) return;
		chunksProcessed.push(c);
		if(!c.entry || includeEntries) {
			chunkHashMap[c.id] = c.renderedHash;
			if(c.name)
				chunkNameMap[c.id] = c.name;
		}
		c.chunks.forEach(addChunk);
	}(this));
	return {
		hash: chunkHashMap,
		name: chunkNameMap
	};
};

Chunk.prototype.toString = function() {
	return "Chunk[" + this.modules.join() + "]";
};
<|endoftext|>


<commit_before>var fs = require( 'fs' ),
	util = require( 'util' ),
	PATH = require( 'path' ),
	EventEmitter = require( 'events' ).EventEmitter,
	Slice = Array.prototype.slice,
	toString = Object.prototype.toString,
	rhome = /^\~\//,
	rroot = /^\//;

// Root just creates assertion module
function MUnit( name, options, callback ) {
	return MUnit.Module( name, options, callback );
}

// Type tests
"Boolean Number String Function Array Date RegExp Object Error".split(' ').forEach(function( method ) {
	if ( method == 'Array' ) {
		return ( MUnit.isArray = Array.isArray );
	}
	else if ( method == 'Error' ) {
		MUnit.isError = function( object ) {
			return object && ( object instanceof Error );
		};

		return;
	}

	var match = '[object ' + method + ']';
	MUnit[ 'is' + method ] = function( object ) {
		return object !== undefined && object !== null && toString.call( object ) == match;
	};
});

// Object extension utility
MUnit.extend = function(){
	var args = Slice.call( arguments ), i = -1, l = args.length, deep = false, target = this, name, copy;

	// Check for deep copy
	if ( MUnit.isBoolean( args[ 0 ] ) ) {
		deep = args.shift();
		l = args.length;
	}

	// Check for multi object extension
	if ( l > 1 ) {
		target = args.shift();
		l = args.length;
	}

	for ( ; ++i < l; ) {
		copy = args[ i ];
		for ( name in copy ) {
			if ( deep && copy[ name ] && MUnit.isArray( copy[ name ] ) ) {
				target[ name ] = MUnit.extend( deep, target[ name ] || [], copy[ name ] );
			}
			else if ( deep && MUnit.isObject( copy[ name ] ) ) {
				target[ name ] = MUnit.extend( deep, target[ name ] || {}, copy[ name ] );
			}
			else {
				target[ name ] = copy[ name ];
			}
		}
	}

	return target;
};

// Attach properties
MUnit.extend({

	// Setup
	version: '0.0.1',
	noop: function(){},
	ns: {},
	passed: 0,
	failed: 0,

	// These options don't inherit from parent modules
	_optionReset: [ 'expect', 'queue' ],

	// Iteraction utility
	each: function( items, fn ) {
		var i, l;

		if ( MUnit.isArray( items ) ) {
			for ( i = -1, l = items.length; ++i < l; ) {
				if ( fn.call( items[ i ], items[ i ], i ) === false ) {
					break;
				}
			}
		}
		else {
			for ( i in items ) {
				if ( fn.call( items[ i ], items[ i ], i ) === false ) {
					break;
				}
			}
		}

		return items;
	},

	// Attach custom events module to object
	Event: function( object ) {
		if ( ! object ) {
			return object;
		}

		var emitter = object.__EventEmitter = new EventEmitter();
		emitter.on( 'error', MUnit.noop );
		[ 'on', 'once', 'emit', 'removeListener', 'removeAllListeners' ].forEach(function( name ) {
			if ( ! object.hasOwnProperty( name ) ) {
				object[ name ] = emitter[ name ].bind( emitter );
			}
		});
	},

	// Attaches custom tests to each submodule
	_attachCustom: function( ns, name, handle ) {
		MUnit.each( ns, function( mod ) {
			mod[ name ] = handle;

			MUnit._attachCustom( mod.ns, name, handle );
		});
	},

	// Adding custom test comparisons
	Custom: function( name, handle ) {
		if ( MUnit.isObject( name ) ) {
			return MUnit.each( name, function( fn, method ) {
				MUnit.Custom( method, fn );
			});
		}

		// Attach to assert proto and each existing instance
		MUnit.Assert.prototype[ name ] = handle;
		MUnit._attachCustom( MUnit.ns, name, handle );
	},

	// Module creation
	Module: function( name, options, callback ) {
		if ( MUnit.isArray( name ) ) {
			return MUnit.each( name, function( object ) {
				MUnit.Modlue( object.name, object.options, object.callback );
			});
		}
		else if ( MUnit.isObject( name ) ) {
			return MUnit.each( name, function( callback, name ) {
				MUnit.Modlue( name, options, callback );
			});
		}

		// Setup
		var ns = MUnit.ns,
			parts = name.split( '.' ),
			finalPart = parts.pop(),
			opts = MUnit.extend( true, {}, MUnit.Defaults.Settings ),
			reset = {},
			assert = null,
			path = '';

		// Setup option reset based on default values
		MUnit._optionReset.forEach(function( key ) {
			reset[ key ] = MUnit.Defaults.Settings[ key ];
		});

		// Filter through all parents
		parts.forEach(function( mod ) {
			// Update the path
			if ( path.length ) {
				path += '.';
			}
			path += mod;

			// Assign assert module
			if ( ! ns[ mod ] ) {
				assert = ns[ mod ] = new MUnit.Assert( path, assert, opts );
			}

			// Trickle down the path
			assert = ns[ mod ];
			ns = assert.ns;
			opts = MUnit.extend( true, {}, opts, reset, assert.options );
		});

		// Getter
		if ( options === undefined ) {
			if ( ! ns[ finalPart ] ) {
				ns[ finalPart ] = new MUnit.Assert( name, assert );
			}

			return ns[ finalPart ];
		}

		// Variable arguments
		if ( callback === undefined && MUnit.isFunction( options ) ) {
			callback = options;
			options = {};
		}
		// Handle only passing of expected tests to options
		else if ( MUnit.isNumber( options ) ) {
			options = { expect: options };
		}

		// Module already exists, update it
		if ( ns[ finalPart ] ) {
			assert = ns[ finalPart ];

			// Test module is already in use, code setup is wrong
			if ( assert.callback ) {
				throw new MUnit.AssertionError( name + " module has already been created", MUnit.Module );
			}

			assert.options = MUnit.extend( true, {}, opts, reset, options || {} );
			assert.callback = callback;
		}
		else {
			options = MUnit.extend( true, {}, opts, reset, options );
			assert = ns[ finalPart ] = new MUnit.Assert( name, assert, options, callback );
		}

		// Stack assert module into queue stash when defined
		if ( assert.options.queue && MUnit.Queue.modules.indexOf( assert ) === -1 ) {
			MUnit.Queue.modules.push( assert );
		}

		// Return the assertion module for use
		return assert;
	},

	// Loads up each possible test file
	_render: function( path ) {
		path += '/';
		fs.readdirSync( path ).forEach(function( file ) {
			if ( ! fs.statSync( path + file ).isDirectory() ) {
				return;
			}
			else if ( PATH.existsSync( path + file + '/' + file + '.js' ) ) {
				require( path + file + '/' + file + '.js' );
			}

			MUnit._render( path + file );
		});
	},

	// Renders each module
	_renderNS: function( ns ) {
		MUnit.each( ns, function( assert, name ) {
			// Root object, close off as no tests are needed
			if ( ! MUnit.isFunction( assert.callback ) ) {
				assert.close();
			}
			// Queued modules are run through the queuer
			else if ( ! assert.options.queue ) {
				assert.callback( assert );

				// Make sure module is really supposed to be asynchronous
				if ( ! assert._closed ) {
					// No limit on expected tests, assume synchronous
					// And if timeout is explicetely null'd out, assume synchronous
					if ( ! assert.options.expect || ! assert.options.timeout ) {
						assert.close();
					}
					// Attach timeout while the module is running
					else {
						assert._timeid = setTimeout(function(){
							if ( ! assert._closed ) {
								assert.close();
							}
						}, assert.options.timeout);
					}
				}
			}

			// Traverse down the module tree
			MUnit._renderNS( assert.ns );
		});
	},

	// Checks all modules to see if they are finished
	_renderCheck: function(){
		var finished = true,
			color = MUnit.failed > 0 ? MUnit.Color.get.red : MUnit.Color.get.green;

		// Check each module
		MUnit.each( MUnit.ns, function( mod, name ) {
			if ( ! mod._finished ) {
				return ( finished = false );
			}
		});

		// Only flush full results once all modules have completed
		if ( finished ) {
			console.log([
				"\n",
				color( "Tests Passed: " + MUnit.passed ),
				color( "Tests Failed: " + MUnit.failed ),
				"\n"
			].join( "\n" ));

			process.exit( MUnit.failed > 0 ? 1 : 0 );
		}
	},

	// Starts the rendering process
	render: function( path ) {
		var ns = MUnit.ns;

		// Handle directory rendering
		if ( MUnit.isString( path ) && path.length ) {
			// Normalize path
			if ( rhome.exec( path ) ) {
				path = path.replace( rhome, process.env.HOME + '/' );
			}
			else if ( ! rroot.exec( path ) ) {
				path = process.cwd() + '/' + path;
			}

			// Quick checks on path to render
			if ( ! PATH.existsSync( path ) || ! fs.statSync( path ).isDirectory() ) {
				MUnit.Color.red( "\n'" + path + "' is not a directory\n" );
				process.exit( 1 );
			}
			else if ( PATH.existsSync( path + '/munit.js' ) ) {
				require( path + '/munit.js' );
			}

			// Setup all submodules
			MUnit._render( path );
		}

		// Trigger each module independently
		MUnit._renderNS( MUnit.ns );

		// Have the queue check it's modules
		MUnit.Queue.check();
	},

	// AssertionError stolen from node src for throwing
	AssertionError: function( message, startFunc ) {
		var self = this;

		self.name = 'Assertion Error';
		self.message = message + "\n";

		if ( Error.captureStackTrace ) {
			Error.captureStackTrace( self, startFunc );
		}
	}

});

// Attach error to assertation
util.inherits( MUnit.AssertionError, Error );

// Expose
module.exports = MUnit;
<commit_msg>-Refactoring path rendering to look for js files with test-* prefix<commit_after>var fs = require( 'fs' ),
	util = require( 'util' ),
	EventEmitter = require( 'events' ).EventEmitter,
	Slice = Array.prototype.slice,
	toString = Object.prototype.toString,
	rtestfile = /^test\-[A-Za-z0-9_\-]+\.js$/,
	rhome = /^\~\//,
	rroot = /^\//;

// Root just creates assertion module
function MUnit( name, options, callback ) {
	return MUnit.Module( name, options, callback );
}

// Type tests
"Boolean Number String Function Array Date RegExp Object Error".split(' ').forEach(function( method ) {
	if ( method == 'Array' ) {
		return ( MUnit.isArray = Array.isArray );
	}
	else if ( method == 'Error' ) {
		MUnit.isError = function( object ) {
			return object && ( object instanceof Error );
		};

		return;
	}

	var match = '[object ' + method + ']';
	MUnit[ 'is' + method ] = function( object ) {
		return object !== undefined && object !== null && toString.call( object ) == match;
	};
});

// Object extension utility
MUnit.extend = function(){
	var args = Slice.call( arguments ), i = -1, l = args.length, deep = false, target = this, name, copy;

	// Check for deep copy
	if ( MUnit.isBoolean( args[ 0 ] ) ) {
		deep = args.shift();
		l = args.length;
	}

	// Check for multi object extension
	if ( l > 1 ) {
		target = args.shift();
		l = args.length;
	}

	for ( ; ++i < l; ) {
		copy = args[ i ];
		for ( name in copy ) {
			if ( deep && copy[ name ] && MUnit.isArray( copy[ name ] ) ) {
				target[ name ] = MUnit.extend( deep, target[ name ] || [], copy[ name ] );
			}
			else if ( deep && MUnit.isObject( copy[ name ] ) ) {
				target[ name ] = MUnit.extend( deep, target[ name ] || {}, copy[ name ] );
			}
			else {
				target[ name ] = copy[ name ];
			}
		}
	}

	return target;
};

// Attach properties
MUnit.extend({

	// Setup
	version: '0.0.1',
	noop: function(){},
	ns: {},
	passed: 0,
	failed: 0,

	// These options don't inherit from parent modules
	_optionReset: [ 'expect', 'queue' ],

	// Iteraction utility
	each: function( items, fn ) {
		var i, l;

		if ( MUnit.isArray( items ) ) {
			for ( i = -1, l = items.length; ++i < l; ) {
				if ( fn.call( items[ i ], items[ i ], i ) === false ) {
					break;
				}
			}
		}
		else {
			for ( i in items ) {
				if ( fn.call( items[ i ], items[ i ], i ) === false ) {
					break;
				}
			}
		}

		return items;
	},

	// Attach custom events module to object
	Event: function( object ) {
		if ( ! object ) {
			return object;
		}

		var emitter = object.__EventEmitter = new EventEmitter();
		emitter.on( 'error', MUnit.noop );
		[ 'on', 'once', 'emit', 'removeListener', 'removeAllListeners' ].forEach(function( name ) {
			if ( ! object.hasOwnProperty( name ) ) {
				object[ name ] = emitter[ name ].bind( emitter );
			}
		});
	},

	// Attaches custom tests to each submodule
	_attachCustom: function( ns, name, handle ) {
		MUnit.each( ns, function( mod ) {
			mod[ name ] = handle;

			MUnit._attachCustom( mod.ns, name, handle );
		});
	},

	// Adding custom test comparisons
	Custom: function( name, handle ) {
		if ( MUnit.isObject( name ) ) {
			return MUnit.each( name, function( fn, method ) {
				MUnit.Custom( method, fn );
			});
		}

		// Attach to assert proto and each existing instance
		MUnit.Assert.prototype[ name ] = handle;
		MUnit._attachCustom( MUnit.ns, name, handle );
	},

	// Module creation
	Module: function( name, options, callback ) {
		if ( MUnit.isArray( name ) ) {
			return MUnit.each( name, function( object ) {
				MUnit.Modlue( object.name, object.options, object.callback );
			});
		}
		else if ( MUnit.isObject( name ) ) {
			return MUnit.each( name, function( callback, name ) {
				MUnit.Modlue( name, options, callback );
			});
		}

		// Setup
		var ns = MUnit.ns,
			parts = name.split( '.' ),
			finalPart = parts.pop(),
			opts = MUnit.extend( true, {}, MUnit.Defaults.Settings ),
			reset = {},
			assert = null,
			path = '';

		// Setup option reset based on default values
		MUnit._optionReset.forEach(function( key ) {
			reset[ key ] = MUnit.Defaults.Settings[ key ];
		});

		// Filter through all parents
		parts.forEach(function( mod ) {
			// Update the path
			if ( path.length ) {
				path += '.';
			}
			path += mod;

			// Assign assert module
			if ( ! ns[ mod ] ) {
				assert = ns[ mod ] = new MUnit.Assert( path, assert, opts );
			}

			// Trickle down the path
			assert = ns[ mod ];
			ns = assert.ns;
			opts = MUnit.extend( true, {}, opts, reset, assert.options );
		});

		// Getter
		if ( options === undefined ) {
			if ( ! ns[ finalPart ] ) {
				ns[ finalPart ] = new MUnit.Assert( name, assert );
			}

			return ns[ finalPart ];
		}

		// Variable arguments
		if ( callback === undefined && MUnit.isFunction( options ) ) {
			callback = options;
			options = {};
		}
		// Handle only passing of expected tests to options
		else if ( MUnit.isNumber( options ) ) {
			options = { expect: options };
		}

		// Module already exists, update it
		if ( ns[ finalPart ] ) {
			assert = ns[ finalPart ];

			// Test module is already in use, code setup is wrong
			if ( assert.callback ) {
				throw new MUnit.AssertionError( name + " module has already been created", MUnit.Module );
			}

			assert.options = MUnit.extend( true, {}, opts, reset, options || {} );
			assert.callback = callback;
		}
		else {
			options = MUnit.extend( true, {}, opts, reset, options );
			assert = ns[ finalPart ] = new MUnit.Assert( name, assert, options, callback );
		}

		// Stack assert module into queue stash when defined
		if ( assert.options.queue && MUnit.Queue.modules.indexOf( assert ) === -1 ) {
			MUnit.Queue.modules.push( assert );
		}

		// Return the assertion module for use
		return assert;
	},

	// Loads up each possible test file
	_render: function( path ) {
		path += '/';
		fs.readdirSync( path ).forEach(function( file ) {
			var fullpath = path + file;

			if ( fs.statSync( fullpath ).isDirectory() ) {
				MUnit._render( fullpath );
			}
			else if ( rtestfile.exec( file ) ) {
				require( path + file );
			}
		});
	},

	// Renders each module
	_renderNS: function( ns ) {
		MUnit.each( ns, function( assert, name ) {
			// Root object, close off as no tests are needed
			if ( ! MUnit.isFunction( assert.callback ) ) {
				assert.close();
			}
			// Queued modules are run through the queuer
			else if ( ! assert.options.queue ) {
				assert.callback( assert );

				// Make sure module is really supposed to be asynchronous
				if ( ! assert._closed ) {
					// No limit on expected tests, assume synchronous
					// And if timeout is explicetely null'd out, assume synchronous
					if ( ! assert.options.expect || ! assert.options.timeout ) {
						assert.close();
					}
					// Attach timeout while the module is running
					else {
						assert._timeid = setTimeout(function(){
							if ( ! assert._closed ) {
								assert.close();
							}
						}, assert.options.timeout);
					}
				}
			}

			// Traverse down the module tree
			MUnit._renderNS( assert.ns );
		});
	},

	// Checks all modules to see if they are finished
	_renderCheck: function(){
		var finished = true,
			color = MUnit.failed > 0 ? MUnit.Color.get.red : MUnit.Color.get.green;

		// Check each module
		MUnit.each( MUnit.ns, function( mod, name ) {
			if ( ! mod._finished ) {
				return ( finished = false );
			}
		});

		// Only flush full results once all modules have completed
		if ( finished ) {
			console.log([
				"\n",
				color( "Tests Passed: " + MUnit.passed ),
				color( "Tests Failed: " + MUnit.failed ),
				"\n"
			].join( "\n" ));

			process.exit( MUnit.failed > 0 ? 1 : 0 );
		}
	},

	// Starts the rendering process
	render: function( path ) {
		var ns = MUnit.ns;

		// Handle directory rendering
		if ( MUnit.isString( path ) && path.length ) {
			// Normalize path
			if ( rhome.exec( path ) ) {
				path = path.replace( rhome, process.env.HOME + '/' );
			}
			else if ( ! rroot.exec( path ) ) {
				path = process.cwd() + '/' + path;
			}

			// Quick checks on path to render
			if ( ! fs.existsSync( path ) || ! fs.statSync( path ).isDirectory() ) {
				MUnit.Color.red( "\n'" + path + "' is not a directory\n" );
				process.exit( 1 );
			}
			else if ( fs.existsSync( path + '/munit.js' ) ) {
				require( path + '/munit.js' );
			}

			// Setup all submodules
			MUnit._render( path );
		}

		// Trigger each module independently
		MUnit._renderNS( MUnit.ns );

		// Have the queue check it's modules
		MUnit.Queue.check();
	},

	// AssertionError stolen from node src for throwing
	AssertionError: function( message, startFunc ) {
		var self = this;

		self.name = 'Assertion Error';
		self.message = message + "\n";

		if ( Error.captureStackTrace ) {
			Error.captureStackTrace( self, startFunc );
		}
	}

});

// Attach error to assertation
util.inherits( MUnit.AssertionError, Error );

// Expose
module.exports = MUnit;
<|endoftext|>


<commit_before>'use babel'

import {
  RtmClient,
  WebClient,
  MemoryDataStore,
  RTM_EVENTS,
  CLIENT_EVENTS,
} from '@slack/client'

import TeamObject from './TeamObject'
import UserObject from './UserObject'
import ChannelObject from './ChannelObject'
import MessageObject from './MessageObject'

import {
 updateTeam,
 setAllUsers,
 addNewUser,
 addNewChannel,
 setAllChannels,
 updateChannel,
 addChannels,
 sendMessage,
 replaceMessage,
 } from './actions'
import store from './store'

const RTM_CLIENT_EVENTS = CLIENT_EVENTS.RTM

export default class Slack {
  constructor(options) {
    this.id = options.team_id
    this.userId = options.user_id
    this.name = options.team_name
    this.token = options.access_token

    this.rtm = new RtmClient(this.token, {
      logLevel: 'debug',
      dataStore: new MemoryDataStore(),
    })

    this.web = new WebClient(this.token)
    this.bind()
  }

  bind() {
    const { rtm, id, userId, name } = this

    rtm.on(RTM_EVENTS.MESSAGE,  (msg) => {
      const message = new MessageObject({
        id: msg.ts,
        senderId: msg.user,
        channelId: msg.channel,
        teamId: this.id,
        text: msg.text,
        createdAt: Date(msg.ts),
        state: 'recived',
      })

      store.dispatch(sendMessage(message))
      if (this.getDisplayedChannel().id !== message.channelId) {
        this.incrementUnread(message)
      }
    });

    rtm.on(RTM_CLIENT_EVENTS.AUTHENTICATED, (rtmStartData) => {
      this.fullFillTeamInfo(rtmStartData.team)
      this.fullFillUsers(rtmStartData.users)
      this.fullFillChannels(rtmStartData.channels)
      this.fullFillDirectMessages(rtmStartData.ims)
      this.didConnect()
    })

    return this
  }

  connect() {
    this.rtm.start()
  }

  getDisplayedChannel() {
    const { currentTeam, activeChannels } = store.getState()
    return activeChannels[currentTeam.id] || {}
  }

  incrementUnread(msg) {
    const { channels } = store.getState()
    let channel = channels[this.id].find((ch) => ch.id === msg.channelId)
    channel.unreadCount += 1
    store.dispatch(updateChannel(channel))
  }

  send(message) {
    store.dispatch(sendMessage(message))
    this.rtm.sendMessage(message.text, message.channelId, (err, msg) => {
      const newMessage = new MessageObject({
        ...message.serialize(),
        id: msg.ts,
        state: 'sent',
      })
      store.dispatch(replaceMessage(message, newMessage))
    })
  }

  history(channelId, options = {}) {
    return this.web.channels.history(channelId, options)

    // this.web.channels.history(channelId, options).then((data) => {
    //
    //   const mydata = {
    //     ok: true,
    //     messages: [
    //       {
    //         type: 'message',
    //         user: 'U02CTSJJS',
    //         text: 'aslkdaldjalsjdasld',
    //         ts: '1475794796.000011',
    //       },
    //     ],
    //     has_more: true,
    //   }
    //
    // })
  }

  didConnect() {
    const teamObject = new TeamObject({
      ...this.serialize(),
      status: 'online',
    })
    store.dispatch(updateTeam(teamObject))
  }

  fullFillTeamInfo(team) {
    if (!team.icon.image_default) {
      this.icon = team.icon.image_230
    }
  }

  fullFillUsers(rawUsers) {
    const users = rawUsers.map((user) => {
      return new UserObject({
        id: user.id,
        teamId: this.id,
        username: user.name,
        displayName: user.profile.real_name,
        avatar: user.profile.image_192,
      })
    })
    store.dispatch(setAllUsers({ users, teamId: this.id }))
  }

  fullFillChannels(rawChannels) {
    const channels = rawChannels.map((channel) => {
      return new ChannelObject({
        id: channel.id,
        teamId: this.id,
        name: channel.name,
        type: 'group',
        topic: channel.topic,
        memberIds: channel.members,
        isMember: channel.is_member,
        unreadCount: channel.unread_count,
        lastRead: channel.last_read,
      })
    })
    store.dispatch(addChannels({ channels, teamId: this.id }))
  }

  fullFillDirectMessages(dms) {
    const channels = dms.map((channel) => {
      return new ChannelObject({
        id: channel.id,
        teamId: this.id,
        name: this.rtm.dataStore.getUserById(channel.user).name,
        type: 'dm',
        memberIds: [channel.user, this.userId],
        isMember: true,
        unreadCount: channel.unread_count,
        lastRead: channel.last_read,
      })
    })
    store.dispatch(addChannels({ channels, teamId: this.id }))
  }

  serialize() {
    const { id, name, userId, icon } = this

    return {
      id,
      name,
      userId,
      icon,
    }
  }
}
<commit_msg>fix bot messages<commit_after>'use babel'

import {
  RtmClient,
  WebClient,
  MemoryDataStore,
  RTM_EVENTS,
  CLIENT_EVENTS,
} from '@slack/client'

import TeamObject from './TeamObject'
import UserObject from './UserObject'
import ChannelObject from './ChannelObject'
import MessageObject from './MessageObject'

import {
 updateTeam,
 setAllUsers,
 addNewUser,
 addNewChannel,
 setAllChannels,
 updateChannel,
 addChannels,
 sendMessage,
 replaceMessage,
 } from './actions'
import store from './store'

const RTM_CLIENT_EVENTS = CLIENT_EVENTS.RTM

export default class Slack {
  constructor(options) {
    this.id = options.team_id
    this.userId = options.user_id
    this.name = options.team_name
    this.token = options.access_token

    this.rtm = new RtmClient(this.token, {
      logLevel: 'debug',
      dataStore: new MemoryDataStore(),
    })

    this.web = new WebClient(this.token)
    this.bind()
  }

  bind() {
    const { rtm, id, userId, name } = this

    rtm.on(RTM_EVENTS.MESSAGE, (msg) => {
      if (msg.type !== 'message' || msg.subtype) {
        return
      }

      const message = new MessageObject({
        id: msg.ts,
        senderId: msg.user,
        channelId: msg.channel,
        teamId: this.id,
        text: msg.text,
        createdAt: Date(msg.ts),
        state: 'recived',
      })

      store.dispatch(sendMessage(message))
      if (this.getDisplayedChannel().id !== message.channelId) {
        this.incrementUnread(message)
      }
    });

    rtm.on(RTM_CLIENT_EVENTS.AUTHENTICATED, (rtmStartData) => {
      this.fullFillTeamInfo(rtmStartData.team)
      this.fullFillUsers(rtmStartData.users)
      this.fullFillChannels(rtmStartData.channels)
      this.fullFillDirectMessages(rtmStartData.ims)
      this.didConnect()
    })

    return this
  }

  connect() {
    this.rtm.start()
  }

  getDisplayedChannel() {
    const { currentTeam, activeChannels } = store.getState()
    return activeChannels[currentTeam.id] || {}
  }

  incrementUnread(msg) {
    const { channels } = store.getState()
    let channel = channels[this.id].find((ch) => ch.id === msg.channelId)
    channel.unreadCount += 1
    store.dispatch(updateChannel(channel))
  }

  send(message) {
    store.dispatch(sendMessage(message))
    this.rtm.sendMessage(message.text, message.channelId, (err, msg) => {
      const newMessage = new MessageObject({
        ...message.serialize(),
        id: msg.ts,
        state: 'sent',
      })
      store.dispatch(replaceMessage(message, newMessage))
    })
  }

  history(channelId, options = {}) {
    return this.web.channels.history(channelId, options)

    // this.web.channels.history(channelId, options).then((data) => {
    //
    //   const mydata = {
    //     ok: true,
    //     messages: [
    //       {
    //         type: 'message',
    //         user: 'U02CTSJJS',
    //         text: 'aslkdaldjalsjdasld',
    //         ts: '1475794796.000011',
    //       },
    //     ],
    //     has_more: true,
    //   }
    //
    // })
  }

  didConnect() {
    const teamObject = new TeamObject({
      ...this.serialize(),
      status: 'online',
    })
    store.dispatch(updateTeam(teamObject))
  }

  fullFillTeamInfo(team) {
    if (!team.icon.image_default) {
      this.icon = team.icon.image_230
    }
  }

  fullFillUsers(rawUsers) {
    const users = rawUsers.map((user) => {
      return new UserObject({
        id: user.id,
        teamId: this.id,
        username: user.name,
        displayName: user.profile.real_name,
        avatar: user.profile.image_192,
      })
    })
    store.dispatch(setAllUsers({ users, teamId: this.id }))
  }

  fullFillChannels(rawChannels) {
    const channels = rawChannels.map((channel) => {
      return new ChannelObject({
        id: channel.id,
        teamId: this.id,
        name: channel.name,
        type: 'group',
        topic: channel.topic,
        memberIds: channel.members,
        isMember: channel.is_member,
        unreadCount: channel.unread_count,
        lastRead: channel.last_read,
      })
    })
    store.dispatch(addChannels({ channels, teamId: this.id }))
  }

  fullFillDirectMessages(dms) {
    const channels = dms.map((channel) => {
      return new ChannelObject({
        id: channel.id,
        teamId: this.id,
        name: this.rtm.dataStore.getUserById(channel.user).name,
        type: 'dm',
        memberIds: [channel.user, this.userId],
        isMember: true,
        unreadCount: channel.unread_count,
        lastRead: channel.last_read,
      })
    })
    store.dispatch(addChannels({ channels, teamId: this.id }))
  }

  serialize() {
    const { id, name, userId, icon } = this

    return {
      id,
      name,
      userId,
      icon,
    }
  }
}
<|endoftext|>


<commit_before>'use strict';
var Q = require('q');
var debug = require('debug')('dynamoose:table');
var util = require('util');
var _ = require('underscore');

function Table(name, schema, options, base) {
  debug('new Table (%s)', name, schema);
  this.name = name;
  this.schema = schema;
  this.options = options || {};
  this.base = base;

  if (this.options.create === undefined || this.options.create === null) {
    this.options.create = true;
  }
}

var compareIndexes = function compareIndexes(local, remote) {
  var indexes = {
    delete: [],
    create: [],
    both: []
  };
  var localTableReq = local;
  var remoteTableReq = remote;
  var i;
  var j;

  var localIndexes = localTableReq.GlobalSecondaryIndexes || [];
  var remoteIndexes = remoteTableReq.GlobalSecondaryIndexes || [];

  debug('compareIndexes');
  // let's see what remote indexes we need to sync or create
  for (i = 0; i < localIndexes.length; i++) {
    var remoteIndexFound = false;
    for (j = 0; j < remoteIndexes.length; j++) {
      if (remoteIndexes[j].IndexName === localIndexes[i].IndexName) {
        // let's see if the core data matches. if it doesn't,
        // we may need to delete the remote GSI and rebuild.
        var localIndex = _.pick(localIndexes[i], 'IndexName', 'KeySchema', 'Projection');
        var remoteIndex = _.pick(remoteIndexes[j], 'IndexName', 'KeySchema', 'Projection');

        if (!_.isEqual(remoteIndex, localIndex)) {
          indexes.both.push(localIndex);
          remoteIndexFound = true;
        } else {
          remoteIndexFound = true;
        }
      }
    }
    if (!remoteIndexFound) {
      indexes.create.push(localIndexes[i]);
    }
  }
  for (j = 0; j < remoteIndexes.length; j++) {
    var localExists = false;
    for (i = 0; i < localIndexes.length; i++) {
      if (remoteIndexes[j].IndexName === localIndexes[i].IndexName) {
        localExists = true;
      }
    }
    if (!localExists) {
      indexes.delete.push(remoteIndexes[j]);
    }
  }
  // now let's see what remote indexes exist that shouldn't exist

  return indexes;
};

Table.prototype.deleteIndex = function deleteIndex(indexName) {
  var deferred = Q.defer();
  var table = this;
  table.active = false;
  var params = {
    TableName: table.name,
    GlobalSecondaryIndexUpdates: [
      {
        Delete: {
          IndexName: indexName
        }
      }
    ]
  };
  table.base.ddb().updateTable(params, function (err, data) {
    debug('deleteIndex handler running');
    if (err) {
      deferred.reject(err);
    }
    else {
      setTimeout(function () {
        table.waitForActive()
          .then(function () {
            deferred.resolve(data);
          });
      }, 300);
    }
  });
  return deferred.promise;
};

Table.prototype.createIndex = function createIndex(attributes, indexSpec) {
  var deferred = Q.defer();
  var table = this;
  table.active = false;
  var params = {
    TableName: this.name,
    AttributeDefinitions: attributes,
    GlobalSecondaryIndexUpdates: [
      {
        Create: indexSpec
      }
    ]
  };
  this.base.ddb().updateTable(params, function (err, data) {
    if (err) {
      deferred.reject(err);
    }
    else {
      setTimeout(function () {
        table.waitForActive()
          .then(function () {
            deferred.resolve(data);
          });
      }, 300);
    }
  });
  return deferred.promise;
};

Table.prototype.init = function (next) {
  debug('initializing table, %s, %j', this.name, this.options);
  var deferred = Q.defer();

  var table = this;
  var localTableReq;

  if (this.options.create) {
    this.describe()
      .then(function (data) {
        debug('table exist -- initialization done');
        localTableReq = buildTableReq(table.name, table.schema);
        var indexes = compareIndexes(localTableReq, data.Table);

        debug('%s', JSON.stringify(indexes, null, 2));
        if (table.options.update) {
          debug('checking indexes');
          for (var deleteIdx in indexes.delete) {
            table.deleteIndex(indexes.delete[deleteIdx].IndexName);
          }
          for (var bothIdx in indexes.both) {
            /*jshint loopfunc: true */
            table.deleteIndex(indexes.both[bothIdx].IndexName)
              .then(function () {
                table.createIndex(localTableReq.AttributeDefinitions, indexes.both[bothIdx]);
              });
          }
          for (var createIdx in indexes.create) {
            table.createIndex(localTableReq.AttributeDefinitions, indexes.create[createIdx]);
          }
        } else {
          if (indexes.delete.length > 0 || indexes.create.length > 0) {
            debug('indexes are not synchronized and update flag is set to false');
            deferred.reject(new Error('indexes are not synchronized and update flag is set to false'));
          }
        }
        table.initialized = true;
        return table.waitForActive()
          .then(function () {
            //table.active = data.Table.TableStatus === 'ACTIVE';

            return deferred.resolve();
          });
      })
      .catch(function (err) {
        if (err && err.code === 'ResourceNotFoundException') {
          debug('table does not exist -- creating');
          return deferred.resolve(
            table.create()
              .then(function () {
                table.initialized = true;
              })
              .then(function() {
                if(table.options.waitForActive) {
                  return table.waitForActive();
                }
              })
          );
        }
        if (err) {
          debug('error initializing', err.stack);
          return deferred.reject(err);
        }
      });
  } else {
    table.initialized = true;
    return deferred.resolve();
  }
  return deferred.promise.nodeify(next);
};

Table.prototype.waitForActive = function (timeout, next) {
  debug('Waiting for Active table, %s, %j', this.name, this.options);
  var deferred = Q.defer();

  if (typeof timeout === 'function') {
    next = timeout;
    timeout = null;
  }

  if (!timeout) {
    timeout = this.options.waitForActiveTimeout;
  }

  var table = this;

  var timeoutAt = Date.now() + timeout;

  function waitForActive() {
    debug('Waiting...');
    /*
     if (table.active) {
     debug('Table flag is set to Active - %s', table.name);
     return deferred.resolve();
     }*/
    if (Date.now() > timeoutAt) {
      return deferred.reject(
        new Error('Wait for Active timed out after ' + timeout + ' ms.')
      );
    }
    if (!table.initialized) {
      return setTimeout(waitForActive, 10);
    }
    table.describe()
      .then(function (data) {
        var active = (data.Table.TableStatus === 'ACTIVE');
        var indexes = data.Table.GlobalSecondaryIndexes || [];
        indexes.forEach(function (gsi) {
          //debug('waitForActive Index Check: %s', JSON.stringify(gsi, null, 2));
          debug('index %s.IndexStatus is %s', gsi.IndexName, gsi.IndexStatus);
          if (gsi.IndexStatus !== 'ACTIVE') {
            active = false;
          }
        });
        if (!active) {
          debug('Waiting for Active again - %s', table.name);
          setTimeout(waitForActive, 500);
        } else {
          table.active = true;
          deferred.resolve();
        }
      })
      .catch(function (err) {
        if (err && err.code === 'ResourceNotFoundException') {
          return setTimeout(waitForActive, 10);
        }
        debug('Error waiting for active', err.stack);
        return deferred.reject(err);
      });
  }

  waitForActive();

  return deferred.promise.nodeify(next);
};

Table.prototype.describe = function (next) {
  var describeTableReq = {
    TableName: this.name
  };

  var deferred = Q.defer();

  var ddb = this.base.ddb();
  ddb.describeTable(describeTableReq, function (err, data) {
    if (err) {
      debug('error describing table', err);
      return deferred.reject(err);
    }
    deferred.resolve(data);
  });

  return deferred.promise.nodeify(next);
};

var buildTableReq = function buildTableReq(name, schema) {
  var attrDefs = [];

  var keyAttr = {};

  function addKeyAttr(attr) {
    if (attr) {
      keyAttr[attr.name] = attr;
    }
  }

  addKeyAttr(schema.hashKey);
  addKeyAttr(schema.rangeKey);
  for (var globalIndexName in schema.indexes.global) {
    addKeyAttr(schema.indexes.global[globalIndexName]);

    // add the range key to the attribute definitions if specified
    var rangeKeyName = schema.indexes.global[globalIndexName].indexes[globalIndexName].rangeKey;
    addKeyAttr(schema.attributes[rangeKeyName]);
  }
  for (var indexName in schema.indexes.local) {
    addKeyAttr(schema.indexes.local[indexName]);
  }

  for (var keyAttrName in keyAttr) {
    attrDefs.push({
      AttributeName: keyAttrName,
      AttributeType: keyAttr[keyAttrName].type.dynamo
    });
  }

  var keySchema = [{
    AttributeName: schema.hashKey.name,
    KeyType: 'HASH'
  }];
  if (schema.rangeKey) {
    keySchema.push({
      AttributeName: schema.rangeKey.name,
      KeyType: 'RANGE'
    });
  }

  var provThroughput = {
    ReadCapacityUnits: schema.throughput.read,
    WriteCapacityUnits: schema.throughput.write
  };

  var createTableReq = {
    AttributeDefinitions: attrDefs,
    TableName: name,
    KeySchema: keySchema,
    ProvisionedThroughput: provThroughput
  };

  debug('Creating table local indexes', schema.indexes.local);
  var localSecIndexes, index;
  for (var localSecIndexName in schema.indexes.local) {
    localSecIndexes = localSecIndexes || [];

    var indexAttr = schema.indexes.local[localSecIndexName];
    index = indexAttr.indexes[localSecIndexName];
    var localSecIndex = {
      IndexName: localSecIndexName,
      KeySchema: [{
        AttributeName: schema.hashKey.name,
        KeyType: 'HASH'
      }, {
        AttributeName: indexAttr.name,
        KeyType: 'RANGE'
      }]
    };

    if (index.project) {
      if (util.isArray(index.project)) {
        localSecIndex.Projection = {
          ProjectionType: 'INCLUDE',
          NonKeyAttributes: index.project
        };
      } else {
        localSecIndex.Projection = {
          ProjectionType: 'ALL'
        };
      }
    } else {
      localSecIndex.Projection = {
        ProjectionType: 'KEYS_ONLY'
      };
    }

    localSecIndexes.push(localSecIndex);
  }

  var globalSecIndexes;
  for (var globalSecIndexName in schema.indexes.global) {
    globalSecIndexes = globalSecIndexes || [];

    var globalIndexAttr = schema.indexes.global[globalSecIndexName];
    index = globalIndexAttr.indexes[globalSecIndexName];

    var globalSecIndex = {
      IndexName: globalSecIndexName,
      KeySchema: [{
        AttributeName: globalIndexAttr.name,
        KeyType: 'HASH'
      }],
      ProvisionedThroughput: {
        ReadCapacityUnits: index.throughput.read,
        WriteCapacityUnits: index.throughput.write
      }
    };

    if (index.rangeKey) {
      globalSecIndex.KeySchema.push({
        AttributeName: index.rangeKey,
        KeyType: 'RANGE'
      });
    }

    if (index.project) {
      if (util.isArray(index.project)) {
        globalSecIndex.Projection = {
          ProjectionType: 'INCLUDE',
          NonKeyAttributes: index.project
        };
      } else {
        globalSecIndex.Projection = {
          ProjectionType: 'ALL'
        };
      }
    } else {
      globalSecIndex.Projection = {
        ProjectionType: 'KEYS_ONLY'
      };
    }

    globalSecIndexes.push(globalSecIndex);
  }

  if (localSecIndexes) {
    createTableReq.LocalSecondaryIndexes = localSecIndexes;
  }

  if (globalSecIndexes) {
    createTableReq.GlobalSecondaryIndexes = globalSecIndexes;
  }

  return createTableReq;
};

Table.prototype.create = function (next) {
  var ddb = this.base.ddb();
  var schema = this.schema;
  var createTableReq = buildTableReq(this.name, schema);

  debug('ddb.createTable request:', createTableReq);

  var deferred = Q.defer();

  ddb.createTable(createTableReq, function (err, data) {
    if (err) {
      debug('error creating table', err);
      return deferred.reject(err);
    }
    debug('table created', data);
    deferred.resolve(data);
  });
  return deferred.promise.nodeify(next);

};

Table.prototype.delete = function (next) {
  var deleteTableReq = {
    TableName: this.name
  };

  debug('ddb.deleteTable request:', deleteTableReq);

  var ddb = this.base.ddb();

  var deferred = Q.defer();

  ddb.deleteTable(deleteTableReq, function (err, data) {
    if (err) {
      debug('error deleting table', err);
      return deferred.reject(err);
    }
    debug('deleted table', data);
    deferred.resolve(data);
  });

  return deferred.promise.nodeify(next);

};

Table.prototype.update = function (next) {
  // var ddb = this.base.ddb();
  // ddb.updateTable();
  var deferred = Q.defer();
  deferred.reject(new Error('TODO'));
  return deferred.promise.nodeify(next);
};

module.exports = Table;
<commit_msg>[fix] missing ProvisionedThroughput<commit_after>'use strict';
var Q = require('q');
var debug = require('debug')('dynamoose:table');
var util = require('util');
var _ = require('underscore');

function Table(name, schema, options, base) {
  debug('new Table (%s)', name, schema);
  this.name = name;
  this.schema = schema;
  this.options = options || {};
  this.base = base;

  if (this.options.create === undefined || this.options.create === null) {
    this.options.create = true;
  }
}

var compareIndexes = function compareIndexes(local, remote) {
  var indexes = {
    delete: [],
    create: [],
    both: []
  };
  var localTableReq = local;
  var remoteTableReq = remote;
  var i;
  var j;

  var localIndexes = localTableReq.GlobalSecondaryIndexes || [];
  var remoteIndexes = remoteTableReq.GlobalSecondaryIndexes || [];

  debug('compareIndexes');
  // let's see what remote indexes we need to sync or create
  for (i = 0; i < localIndexes.length; i++) {
    var remoteIndexFound = false;
    for (j = 0; j < remoteIndexes.length; j++) {
      if (remoteIndexes[j].IndexName === localIndexes[i].IndexName) {
        // let's see if the core data matches. if it doesn't,
        // we may need to delete the remote GSI and rebuild.
        var localIndex = _.pick(localIndexes[i], 'IndexName', 'KeySchema', 'Projection');
        var remoteIndex = _.pick(remoteIndexes[j], 'IndexName', 'KeySchema', 'Projection');

        if (!_.isEqual(remoteIndex, localIndex)) {
          indexes.both.push(localIndexes[i]);
          remoteIndexFound = true;
        } else {
          remoteIndexFound = true;
        }
      }
    }
    if (!remoteIndexFound) {
      indexes.create.push(localIndexes[i]);
    }
  }
  for (j = 0; j < remoteIndexes.length; j++) {
    var localExists = false;
    for (i = 0; i < localIndexes.length; i++) {
      if (remoteIndexes[j].IndexName === localIndexes[i].IndexName) {
        localExists = true;
      }
    }
    if (!localExists) {
      indexes.delete.push(remoteIndexes[j]);
    }
  }
  // now let's see what remote indexes exist that shouldn't exist

  return indexes;
};

Table.prototype.deleteIndex = function deleteIndex(indexName) {
  var deferred = Q.defer();
  var table = this;
  table.active = false;
  var params = {
    TableName: table.name,
    GlobalSecondaryIndexUpdates: [
      {
        Delete: {
          IndexName: indexName
        }
      }
    ]
  };
  table.base.ddb().updateTable(params, function (err, data) {
    debug('deleteIndex handler running');
    if (err) {
      deferred.reject(err);
    }
    else {
      setTimeout(function () {
        table.waitForActive()
          .then(function () {
            deferred.resolve(data);
          });
      }, 300);
    }
  });
  return deferred.promise;
};

Table.prototype.createIndex = function createIndex(attributes, indexSpec) {
  var deferred = Q.defer();
  var table = this;
  table.active = false;
  var params = {
    TableName: this.name,
    AttributeDefinitions: attributes,
    GlobalSecondaryIndexUpdates: [
      {
        Create: indexSpec
      }
    ]
  };
  this.base.ddb().updateTable(params, function (err, data) {
    if (err) {
      deferred.reject(err);
    }
    else {
      setTimeout(function () {
        table.waitForActive()
          .then(function () {
            deferred.resolve(data);
          });
      }, 300);
    }
  });
  return deferred.promise;
};

Table.prototype.init = function (next) {
  debug('initializing table, %s, %j', this.name, this.options);
  var deferred = Q.defer();

  var table = this;
  var localTableReq;

  if (this.options.create) {
    this.describe()
      .then(function (data) {
        debug('table exist -- initialization done');
        localTableReq = buildTableReq(table.name, table.schema);
        var indexes = compareIndexes(localTableReq, data.Table);

        debug('%s', JSON.stringify(indexes, null, 2));
        if (table.options.update) {
          debug('checking indexes');
          for (var deleteIdx in indexes.delete) {
            table.deleteIndex(indexes.delete[deleteIdx].IndexName);
          }
          for (var bothIdx in indexes.both) {
            /*jshint loopfunc: true */
            table.deleteIndex(indexes.both[bothIdx].IndexName)
              .then(function () {
                table.createIndex(localTableReq.AttributeDefinitions, indexes.both[bothIdx]);
              });
          }
          for (var createIdx in indexes.create) {
            table.createIndex(localTableReq.AttributeDefinitions, indexes.create[createIdx]);
          }
        } else {
          if (indexes.delete.length > 0 || indexes.create.length > 0) {
            debug('indexes are not synchronized and update flag is set to false');
            deferred.reject(new Error('indexes are not synchronized and update flag is set to false'));
          }
        }
        table.initialized = true;
        return table.waitForActive()
          .then(function () {
            //table.active = data.Table.TableStatus === 'ACTIVE';

            return deferred.resolve();
          });
      })
      .catch(function (err) {
        if (err && err.code === 'ResourceNotFoundException') {
          debug('table does not exist -- creating');
          return deferred.resolve(
            table.create()
              .then(function () {
                table.initialized = true;
              })
              .then(function() {
                if(table.options.waitForActive) {
                  return table.waitForActive();
                }
              })
          );
        }
        if (err) {
          debug('error initializing', err.stack);
          return deferred.reject(err);
        }
      });
  } else {
    table.initialized = true;
    return deferred.resolve();
  }
  return deferred.promise.nodeify(next);
};

Table.prototype.waitForActive = function (timeout, next) {
  debug('Waiting for Active table, %s, %j', this.name, this.options);
  var deferred = Q.defer();

  if (typeof timeout === 'function') {
    next = timeout;
    timeout = null;
  }

  if (!timeout) {
    timeout = this.options.waitForActiveTimeout;
  }

  var table = this;

  var timeoutAt = Date.now() + timeout;

  function waitForActive() {
    debug('Waiting...');
    /*
     if (table.active) {
     debug('Table flag is set to Active - %s', table.name);
     return deferred.resolve();
     }*/
    if (Date.now() > timeoutAt) {
      return deferred.reject(
        new Error('Wait for Active timed out after ' + timeout + ' ms.')
      );
    }
    if (!table.initialized) {
      return setTimeout(waitForActive, 10);
    }
    table.describe()
      .then(function (data) {
        var active = (data.Table.TableStatus === 'ACTIVE');
        var indexes = data.Table.GlobalSecondaryIndexes || [];
        indexes.forEach(function (gsi) {
          //debug('waitForActive Index Check: %s', JSON.stringify(gsi, null, 2));
          debug('index %s.IndexStatus is %s', gsi.IndexName, gsi.IndexStatus);
          if (gsi.IndexStatus !== 'ACTIVE') {
            active = false;
          }
        });
        if (!active) {
          debug('Waiting for Active again - %s', table.name);
          setTimeout(waitForActive, 500);
        } else {
          table.active = true;
          deferred.resolve();
        }
      })
      .catch(function (err) {
        if (err && err.code === 'ResourceNotFoundException') {
          return setTimeout(waitForActive, 10);
        }
        debug('Error waiting for active', err.stack);
        return deferred.reject(err);
      });
  }

  waitForActive();

  return deferred.promise.nodeify(next);
};

Table.prototype.describe = function (next) {
  var describeTableReq = {
    TableName: this.name
  };

  var deferred = Q.defer();

  var ddb = this.base.ddb();
  ddb.describeTable(describeTableReq, function (err, data) {
    if (err) {
      debug('error describing table', err);
      return deferred.reject(err);
    }
    deferred.resolve(data);
  });

  return deferred.promise.nodeify(next);
};

var buildTableReq = function buildTableReq(name, schema) {
  var attrDefs = [];

  var keyAttr = {};

  function addKeyAttr(attr) {
    if (attr) {
      keyAttr[attr.name] = attr;
    }
  }

  addKeyAttr(schema.hashKey);
  addKeyAttr(schema.rangeKey);
  for (var globalIndexName in schema.indexes.global) {
    addKeyAttr(schema.indexes.global[globalIndexName]);

    // add the range key to the attribute definitions if specified
    var rangeKeyName = schema.indexes.global[globalIndexName].indexes[globalIndexName].rangeKey;
    addKeyAttr(schema.attributes[rangeKeyName]);
  }
  for (var indexName in schema.indexes.local) {
    addKeyAttr(schema.indexes.local[indexName]);
  }

  for (var keyAttrName in keyAttr) {
    attrDefs.push({
      AttributeName: keyAttrName,
      AttributeType: keyAttr[keyAttrName].type.dynamo
    });
  }

  var keySchema = [{
    AttributeName: schema.hashKey.name,
    KeyType: 'HASH'
  }];
  if (schema.rangeKey) {
    keySchema.push({
      AttributeName: schema.rangeKey.name,
      KeyType: 'RANGE'
    });
  }

  var provThroughput = {
    ReadCapacityUnits: schema.throughput.read,
    WriteCapacityUnits: schema.throughput.write
  };

  var createTableReq = {
    AttributeDefinitions: attrDefs,
    TableName: name,
    KeySchema: keySchema,
    ProvisionedThroughput: provThroughput
  };

  debug('Creating table local indexes', schema.indexes.local);
  var localSecIndexes, index;
  for (var localSecIndexName in schema.indexes.local) {
    localSecIndexes = localSecIndexes || [];

    var indexAttr = schema.indexes.local[localSecIndexName];
    index = indexAttr.indexes[localSecIndexName];
    var localSecIndex = {
      IndexName: localSecIndexName,
      KeySchema: [{
        AttributeName: schema.hashKey.name,
        KeyType: 'HASH'
      }, {
        AttributeName: indexAttr.name,
        KeyType: 'RANGE'
      }]
    };

    if (index.project) {
      if (util.isArray(index.project)) {
        localSecIndex.Projection = {
          ProjectionType: 'INCLUDE',
          NonKeyAttributes: index.project
        };
      } else {
        localSecIndex.Projection = {
          ProjectionType: 'ALL'
        };
      }
    } else {
      localSecIndex.Projection = {
        ProjectionType: 'KEYS_ONLY'
      };
    }

    localSecIndexes.push(localSecIndex);
  }

  var globalSecIndexes;
  for (var globalSecIndexName in schema.indexes.global) {
    globalSecIndexes = globalSecIndexes || [];

    var globalIndexAttr = schema.indexes.global[globalSecIndexName];
    index = globalIndexAttr.indexes[globalSecIndexName];

    var globalSecIndex = {
      IndexName: globalSecIndexName,
      KeySchema: [{
        AttributeName: globalIndexAttr.name,
        KeyType: 'HASH'
      }],
      ProvisionedThroughput: {
        ReadCapacityUnits: index.throughput.read,
        WriteCapacityUnits: index.throughput.write
      }
    };

    if (index.rangeKey) {
      globalSecIndex.KeySchema.push({
        AttributeName: index.rangeKey,
        KeyType: 'RANGE'
      });
    }

    if (index.project) {
      if (util.isArray(index.project)) {
        globalSecIndex.Projection = {
          ProjectionType: 'INCLUDE',
          NonKeyAttributes: index.project
        };
      } else {
        globalSecIndex.Projection = {
          ProjectionType: 'ALL'
        };
      }
    } else {
      globalSecIndex.Projection = {
        ProjectionType: 'KEYS_ONLY'
      };
    }

    globalSecIndexes.push(globalSecIndex);
  }

  if (localSecIndexes) {
    createTableReq.LocalSecondaryIndexes = localSecIndexes;
  }

  if (globalSecIndexes) {
    createTableReq.GlobalSecondaryIndexes = globalSecIndexes;
  }

  return createTableReq;
};

Table.prototype.create = function (next) {
  var ddb = this.base.ddb();
  var schema = this.schema;
  var createTableReq = buildTableReq(this.name, schema);

  debug('ddb.createTable request:', createTableReq);

  var deferred = Q.defer();

  ddb.createTable(createTableReq, function (err, data) {
    if (err) {
      debug('error creating table', err);
      return deferred.reject(err);
    }
    debug('table created', data);
    deferred.resolve(data);
  });
  return deferred.promise.nodeify(next);

};

Table.prototype.delete = function (next) {
  var deleteTableReq = {
    TableName: this.name
  };

  debug('ddb.deleteTable request:', deleteTableReq);

  var ddb = this.base.ddb();

  var deferred = Q.defer();

  ddb.deleteTable(deleteTableReq, function (err, data) {
    if (err) {
      debug('error deleting table', err);
      return deferred.reject(err);
    }
    debug('deleted table', data);
    deferred.resolve(data);
  });

  return deferred.promise.nodeify(next);

};

Table.prototype.update = function (next) {
  // var ddb = this.base.ddb();
  // ddb.updateTable();
  var deferred = Q.defer();
  deferred.reject(new Error('TODO'));
  return deferred.promise.nodeify(next);
};

module.exports = Table;
<|endoftext|>