Переглянути джерело

Refs #1661: Fixes to misc docs in `munge`

http://source.rd.rcg.local/trac/eagle6/changeset/1415/Eagle6_SVN
Kyle Davis 13 роки тому
батько
коміт
ff8fd52b8b

+ 23 - 18
README.md

@@ -16,24 +16,29 @@ Deviations
 ----------
 Here is a list of the major items where we have deviated from the MongoDB code and a little bit about why:
 
-  * Pipeline classes
-    * the `Document` class
+  * **General**
+    * DESIGN: A lot of these things are packed into a single `.cpp` file in the MongoDB code but to keep things clean and separate they have been broken out into files named the same and only rarely is there more than one class within a single file
+    * `BSON` vs `JSON`
+      * DESIGN: Basically all of the `BSON`-specific code has become equivalent `JSON`-specific code since that's what we're working with (no need for needless conversions)
+      * DESIGN: A lot of these have a `addToBson...` and other `BSONObjBuilder`-related methods that take in an instance to be modified but it's owned by the caller; in `munge` we build a new `Object` and return it because it's simpler and that's how they're generally used anyhow
+    * TESTING: Many of the tests have been written without the use of the testing base classes as they are in the MongoDB code to try and simplify and make things more clear (but never less complete)
+  * **Pipeline components**
+    * `Document` class
       * DESIGN: `Document` now provides static helpers rather than instance helpers to avoid unecessary boxing/unboxing since that seems to make more sense here (we treat any `Object` like a `Document`)
-    * the `Expression` class
-      * DESIGN: the nested `ObjectCtx` class no longer uses contants and a bit flags but instead uses similarly named boolean; e.g., `isDocumentOk` rather than `DOCUMENT_OK`
-    * the `Value` class
+    * `Value` class
       * DESIGN: `Value` now provides static helpers rather than instance helpers to avoid unecessary boxing/unboxing since that seems to make more sense here (we treat any `Object` like a `Value)
       * NAMING: `Value#get{TYPE}` methods have been renamed to `Value.verify{TYPE}` since that seemed to make more sense given what they're really doing for us as statics
       * DESIGN: `Value.coerceToDate` static returns a JavaScript `Date` object rather than milliseconds since that seems to make more sense where possible
-    * NAMING: The `Expression{FOO}` classes have all been renamed to `{FOO}Expression` to satisfy my naming OCD.
-    * DESIGN: The `{FOO}Expression` classes do not provide `create` statics since calling new is easy enough
-      * DESIGN: To further this, the `CompareExpression` class doesn't provide any of it's additional `create{FOO}` helpers so instead I'm binding the appropriate args to the ctor
-    * TESTING: Many of the expression tests have been written without the use of the testing base classes to make things a little more clear (but not less complete)
-  * BSON vs JSON
-    * DESIGN: Basically all of the `BSON`-specific code has become equivalent `JSON`-specific code since that's what we're working with (no need for needless conversions)
-    * DESIGN: A lot of these have a `addToBson...` and other `BSONObjBuilder`-related methods that take in an instance to be modified but it's owned by the caller; in `munge` we build a new `Object` and return it because it's simpler and that's how they're generally used anyhow
-  * Document sources
-  	* we have implemented a 'reset' method for all document sources so that we can reuse them against different streams of data
+    * `Expression` classes
+      * `Expression` base class
+        * DESIGN: The nested `ObjectCtx` class no longer uses contants and bitmask flags, instead it takes an `Object` with similarly named keys that are expected to be `Boolean`s; e.g., `{isDocumentOk:true}` rather than `DOCUMENT_OK`
+      * NAMING: The `Expression{FOO}` classes have all been renamed to `{FOO}Expression` to satisfy my naming OCD.
+      * DESIGN: The `{FOO}Expression` classes do not provide `create` statics since calling new is easy enough
+        * DESIGN: To further this, the `CompareExpression` class doesn't provide any of it's additional `create{FOO}` helpers so instead I'm binding the appropriate args to the `constructor`
+      * EXTENSIONS: The following are extended `munge`-only expressions that have not been ported back to MongoDB yet
+        * `IndexOfExpression` or `$indexOf` - A new `Expression` that returns the index of an item in an `Array` or `String`
+    * `DocumentSource` classes
+      * DESIGN: We have implemented a `reset` method for all document sources so that we can reuse them against different streams of data
 
 
 TODO
@@ -41,9 +46,8 @@ TODO
 Here is a list of global items that I know about that may need to be done in the future:
 
   * Go through the TODOs....
-  * `getOpName` should be static!
   * Need a method by which consumers can provide their own extensions
-  * Move expression name to the ctor? or at least a const prototype property or something
+  * Move expression name to the `constructor`? or at least a const prototype property or something
   * NAMING: need to go back through and make sure that places referencing <Document> in the C++ code are represented here by referencing a var called "doc" or similar
   * Currently using JS types but may need to support `BSON` types to do everything properly; affects handling of `ObjectId`, `ISODate`, and `Timestamp`
   * Go through test cases and try to turn `assert.equal()` calls into `assert.strictEqual()` calls
@@ -51,5 +55,6 @@ Here is a list of global items that I know about that may need to be done in the
   * Go through uses of `throw` and make them actually use `UserException` vs `SystemException` (or whatever they're called)
   * Go through and fix the `/** documentation **/` to ensure that they are YUIDoc-fiendly and exist on multiple lines
   * Go through and modify classes to use advanced OO property settings properly (`seal`, `freeze`, etc.) where appropriate
-  * Make sure that nobody is using private (underscored) variables that they shouldn't be...might have broken encapsulation somewhere along the way...
-  * Make sure  that all of the pure `virtual`s (i.e., /virtual .* = 0;$/) are implemented as a proto with a throw new Error("NOT IMPLEMENTED BY INHERITOR") or similar
+  * Make sure that nobody is using private (underscored) variables that they shouldn't be ...might have broken encapsulation somewhere along the way...
+  * Make sure  that all of the pure `virtual`s (i.e., `/virtual .* = 0;$/`) are implemented as a proto with a throw new Error("NOT IMPLEMENTED BY INHERITOR") or similar
+  * Need to make real exceptions to emulate the MongoDB assertions (e.g., `uassert` might be `munge.UserException`) so they can be caught properly

+ 4 - 1
lib/pipeline/expressions/AddExpression.js

@@ -25,7 +25,10 @@ var AddExpression = module.exports = (function(){
 		return klass;	// using the ctor rather than a separate .create() method
 	};
 
-	/** Takes an array of one or more numbers and adds them together, returning the sum. **/
+	/**
+	* Takes an array of one or more numbers and adds them together, returning the sum.
+	* @method @evaluate
+	**/
 	proto.evaluate = function evaluate(doc) {
 		var total = 0;
 		for (var i = 0, n = this.operands.length; i < n; ++i) {

+ 4 - 1
lib/pipeline/expressions/AndExpression.js

@@ -30,7 +30,10 @@ var AndExpression = module.exports = (function(){
 		return klass;	// using the ctor rather than a separate .create() method
 	};
 
-	/** Takes an array one or more values and returns true if all of the values in the array are true. Otherwise $and returns false. **/
+	/**
+	* Takes an array one or more values and returns true if all of the values in the array are true. Otherwise $and returns false.
+	* @method evaluate
+	**/
 	proto.evaluate = function evaluate(doc) {
 		for (var i = 0, n = this.operands.length; i < n; ++i) {
 			var value = this.operands[i].evaluate(doc);

+ 4 - 1
lib/pipeline/expressions/CompareExpression.js

@@ -41,7 +41,10 @@ var CompareExpression = module.exports = (function(){
 	})();
 
 	// PRIVATE STATIC MEMBERS
-	/** a table of cmp type lookups to truth values **/
+	/**
+	* a table of cmp type lookups to truth values
+	* @private
+	**/
 	var cmpLookupMap = [	//NOTE: converted from this Array to a Dict/Object below using CmpLookup#name as the key
 		//              -1      0      1      reverse             name     (taking advantage of the fact that our 'enums' are strings below)
 		new CmpLookup([false, true, false], Expression.CmpOp.EQ, Expression.CmpOp.EQ),

+ 4 - 1
lib/pipeline/expressions/ConstantExpression.js

@@ -32,7 +32,10 @@ var ConstantExpression = module.exports = (function(){
 		// nothing to do
 	};
 
-	/** Get the constant value represented by this Expression. **/
+	/**
+	* Get the constant value represented by this Expression.
+	* @method evaluate
+	**/
 	proto.evaluate = function evaluate(doc){
 		return this.value;
 	};

+ 4 - 1
lib/pipeline/expressions/DayOfMonthExpression.js

@@ -23,7 +23,10 @@ var DayOfMonthExpression = module.exports = (function(){
 		base.prototype.addOperand.call(this, expr);
 	};
 
-	/** Takes a date and returns the day of the month as a number between 1 and 31. **/
+	/**
+	* Takes a date and returns the day of the month as a number between 1 and 31.
+	* @method evaluate
+	**/
 	proto.evaluate = function evaluate(doc){
 		this.checkArgCount(1);
 		var date = this.operands[0].evaluate(doc);

+ 4 - 1
lib/pipeline/expressions/DayOfWeekExpression.js

@@ -23,7 +23,10 @@ var DayOfWeekExpression = module.exports = (function(){
 		base.prototype.addOperand.call(this, expr);
 	};
 
-	/** Takes a date and returns the day of the week as a number between 1 (Sunday) and 7 (Saturday.) **/
+	/**
+	* Takes a date and returns the day of the week as a number between 1 (Sunday) and 7 (Saturday.)
+	* @method evaluate
+	**/
 	proto.evaluate = function evaluate(doc){
 		this.checkArgCount(1);
 		var date = this.operands[0].evaluate(doc);

+ 4 - 1
lib/pipeline/expressions/DayOfYearExpression.js

@@ -23,7 +23,10 @@ var DayOfYearExpression = module.exports = (function(){
 		base.prototype.addOperand.call(this, expr);
 	};
 
-	/** Takes a date and returns the day of the year as a number between 1 and 366. **/
+	/**
+	* Takes a date and returns the day of the year as a number between 1 and 366.
+	* @method evaluate
+	**/
 	proto.evaluate = function evaluate(doc){
 		//NOTE: the below silliness is to deal with the leap year scenario when we should be returning 366
 		this.checkArgCount(1);

+ 4 - 1
lib/pipeline/expressions/DivideExpression.js

@@ -27,7 +27,10 @@ var DivideExpression = module.exports = (function(){
 		base.addOperand.call(this, expr);
 	};
 
-	/** Takes an array that contains a pair of numbers and returns the value of the first number divided by the second number. **/
+	/**
+	* Takes an array that contains a pair of numbers and returns the value of the first number divided by the second number.
+	* @method evaluate
+	**/
 	proto.evaluate = function evaluate(doc) {
 		this.checkArgCount(2);
 		var left = this.operands[0].evaluate(doc),

+ 64 - 21
lib/pipeline/expressions/Expression.js

@@ -13,7 +13,7 @@ var Expression = module.exports = (function(){
 	* @module munge
 	* @constructor
 	**/
-	var klass = module.exports = Expression = function Expression(opts){
+	var klass = module.exports = Expression = function Expression(){
 		if(arguments.length !== 0) throw new Error("zero args expected");
 	}, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
@@ -22,16 +22,26 @@ var Expression = module.exports = (function(){
 
 	// NESTED CLASSES
 	/**
-	* Utility class for parseObject() below. isDocumentOk indicates that it is OK to use a Document in the current context.
-	*
-	* NOTE: deviation from Mongo code: accepts an object of settings rather than a bitmask to help cleanup the interface a little bit
-	*
-	* @class ObjectCtx
-	* @namespace munge.pipeline.expressions.Expression
-	* @module munge
+	* Reference to the `munge.pipeline.expressions.Expression.ObjectCtx` class
+	* @static
+	* @property ObjectCtx
 	**/
 	var ObjectCtx = Expression.ObjectCtx = (function(){
 		// CONSTRUCTOR
+		/**
+		* Utility class for parseObject() below. isDocumentOk indicates that it is OK to use a Document in the current context.
+		*
+		* NOTE: deviation from Mongo code: accepts an `Object` of settings rather than a bitmask to help simplify the interface a little bit
+		*
+		* @class ObjectCtx
+		* @namespace munge.pipeline.expressions.Expression
+		* @module munge
+		* @constructor
+		* @param opts
+		*	@param [opts.isDocumentOk]	{Boolean}
+		*	@param [opts.isTopLevel]	{Boolean}
+		*	@param [opts.isInclusionOk]	{Boolean}
+		**/
 		var klass = function ObjectCtx(opts /*= {isDocumentOk:..., isTopLevel:..., isInclusionOk:...}*/){
 			if(!(opts instanceof Object && opts.constructor == Object)) throw new Error("opts is required and must be an Object containing named args");
 			for (var k in opts) { // assign all given opts to self so long as they were part of klass.prototype as undefined properties
@@ -39,24 +49,37 @@ var Expression = module.exports = (function(){
 			}
 		}, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
+		// PROTOTYPE MEMBERS
 		proto.isDocumentOk =
 		proto.isTopLevel =
 		proto.isInclusionOk = undefined;
+
 		return klass;
 	})();
 
 	/**
-	* Decribes how and when to create an Op instance
-	*
-	* @class OpDesc
-	* @namespace munge.pipeline.expressions.Expression
-	* @module munge
+	* Reference to the `munge.pipeline.expressions.Expression.OpDesc` class
+	* @static
+	* @property OpDesc
 	**/
 	var OpDesc = Expression.OpDesc = (function(){
 		// CONSTRUCTOR
+		/**
+		* Decribes how and when to create an Op instance
+		*
+		* @class OpDesc
+		* @namespace munge.pipeline.expressions.Expression
+		* @module munge
+		* @constructor
+		* @param name
+		* @param factory
+		* @param flags
+		* @param argCount
+		**/
 		var klass = function OpDesc(name, factory, flags, argCount){
-			if (arguments[0] instanceof Object && arguments[0].constructor == Object) { //TODO: using this?
-				var opts = arguments[0];
+			var firstArg = arguments[0];
+			if (firstArg instanceof Object && firstArg.constructor == Object) { //TODO: using this?
+				var opts = firstArg;
 				for (var k in opts) { // assign all given opts to self so long as they were part of klass.prototype as undefined properties
 					if (opts.hasOwnProperty(k) && proto.hasOwnProperty(k) && proto[k] === undefined) this[k] = opts[k];
 				}
@@ -73,12 +96,16 @@ var Expression = module.exports = (function(){
 		klass.OBJECT_ARG = 2;
 
 		// PROTOTYPE MEMBERS
-
 		proto.name =
 		proto.factory =
 		proto.flags =
 		proto.argCount = undefined;
 
+		/**
+		* internal `OpDesc#name` comparer
+		* @method cmp
+		* @param that the other `OpDesc` instance
+		**/
 		proto.cmp = function cmp(that) {
 			return this.name < that.name ? -1 : this.name > that.name ? 1 : 0;
 		};
@@ -247,6 +274,8 @@ var Expression = module.exports = (function(){
 	/**
 	* Parse a BSONElement Object which has already been determined to be functional expression.
 	*
+	* @static
+	* @method parseExpression
 	* @param opName	the name of the (prefix) operator
 	* @param obj	the BSONElement to parse
 	* @returns the parsed Expression
@@ -280,12 +309,13 @@ var Expression = module.exports = (function(){
 			expr.addOperand(operand);
 		}
 
-        return expr;
+		return expr;
 	};
 
 	/**
 	* Parse a BSONElement which is an operand in an Expression.
 	*
+	* @static
 	* @param pBsonElement the expected operand's BSONElement
 	* @returns the parsed operand, as an Expression
 	**/
@@ -303,6 +333,7 @@ var Expression = module.exports = (function(){
 	* Produce a field path string with the field prefix removed.
 	* Throws an error if the field prefix is not present.
 	*
+	* @static
 	* @param prefixedField the prefixed field
 	* @returns the field path with the prefix removed
 	**/
@@ -312,7 +343,13 @@ var Expression = module.exports = (function(){
 		return prefixedField.substr(1);
 	};
 
-	/** @returns the sign of a number; -1, 1, or 0 **/
+	/**
+	* returns the signe of a number
+	*
+	* @static
+	* @method signum
+	* @returns the sign of a number; -1, 1, or 0
+	**/
 	klass.signum = function signum(i) {
 		if (i < 0) return -1;
 		if (i > 0) return 1;
@@ -321,11 +358,12 @@ var Expression = module.exports = (function(){
 
 
 	// PROTOTYPE MEMBERS
-	/***
+	/**
 	* Evaluate the Expression using the given document as input.
 	*
+	* @method evaluate
 	* @returns the computed value
-	***/
+	**/
 	proto.evaluate = function evaluate(obj) {
 		throw new Error("WAS NOT IMPLEMENTED BY INHERITOR!");
 	};
@@ -340,6 +378,7 @@ var Expression = module.exports = (function(){
 	*  not be the same object.  In the case of constant folding, a computed
 	*  expression may be replaced by a constant.
 	*
+	* @method optimize
 	* @returns the optimized Expression
 	**/
 	proto.optimize = function optimize() {
@@ -352,6 +391,7 @@ var Expression = module.exports = (function(){
 	* Top-level ExpressionObject gets pointer to empty vector.
 	* If any other Expression is an ancestor, or in other cases where {a:1} inclusion objects aren't allowed, they get NULL.
 	*
+	* @method addDependencies
 	* @param deps	output parameter
 	* @param path	path to self if all ancestors are ExpressionObjects.
 	**/
@@ -359,7 +399,10 @@ var Expression = module.exports = (function(){
 		throw new Error("WAS NOT IMPLEMENTED BY INHERITOR!");
 	};
 
-	/** simple expressions are just inclusion exclusion as supported by ExpressionObject **/
+	/**
+	* simple expressions are just inclusion exclusion as supported by ExpressionObject
+	* @method getIsSimple
+	**/
 	proto.getIsSimple = function getIsSimple() {
 		return false;
 	};

+ 4 - 1
lib/pipeline/expressions/FieldRangeExpression.js

@@ -36,7 +36,10 @@ var FieldRangeExpression = module.exports = (function(){
 
 	// NESTED CLASSES
 	var Range = (function(){
-		/** create a new Range; opts is either {cmpOp:..., value:...} or {bottom:..., isBottomOpen:..., top:..., isTopOpen:...} **/
+		/**
+		* create a new Range; opts is either {cmpOp:..., value:...} or {bottom:..., isBottomOpen:..., top:..., isTopOpen:...}
+		* @private
+		**/
 		var klass = function Range(opts){
 			this.isBottomOpen = this.isTopOpen = false;
 			this.bottom = this.top = undefined;

+ 3 - 0
lib/pipeline/expressions/IndexOfExpression.js

@@ -30,11 +30,14 @@ var IndexOfExpression = module.exports = (function(){
 	**/
 	proto.evaluate = function evaluate(doc){
 		this.checkArgCount(2);
+
 		var left = this.operands[0].evaluate(doc);
 		if (left === undefined) return undefined;
+
 		var right = this.operands[1].evaluate(doc);
 		if (right === undefined) return undefined;
 		if (!(right instanceof Array)) throw new Error("UserAssertion: expected the 2nd arg of the $indexOf expression to be an Array but got " + (typeof right === "object" ? right.constructor.name : typeof right));
+
 		return right.indexOf(left);
 	};
 

+ 5 - 2
lib/pipeline/expressions/MonthExpression.js

@@ -1,7 +1,7 @@
 var MonthExpression = module.exports = (function(){
 	// CONSTRUCTOR
 	/** 
-	 * An $Month pipeline expression. 
+	 * A $month pipeline expression. 
 	 *
 	 * @see evaluate 
 	 * @class MonthExpression
@@ -24,7 +24,10 @@ var MonthExpression = module.exports = (function(){
 		base.prototype.addOperand.call(this, expr);
 	};
 
-	/** Takes a date and returns the month as a number between 1 and 12. **/
+	/**
+	* Takes a date and returns the month as a number between 1 and 12.
+	* @method evaluate
+	**/
 	proto.evaluate = function evaluate(doc){
 		this.checkArgCount(1);
 		var date = this.operands[0].evaluate(doc);

+ 4 - 1
lib/pipeline/expressions/SecondExpression.js

@@ -24,7 +24,10 @@ var SecondExpression = module.exports = (function(){
 		base.prototype.addOperand.call(this, expr);
 	};
 
-	/** Takes a date and returns the second between 0 and 59, but can be 60 to account for leap seconds. **/
+	/**
+	* Takes a date and returns the second between 0 and 59, but can be 60 to account for leap seconds.
+	* @method evaluate
+	**/
 	proto.evaluate = function evaluate(doc){
 		this.checkArgCount(1);
 		var date = this.operands[0].evaluate(doc);

+ 4 - 1
lib/pipeline/expressions/YearExpression.js

@@ -28,7 +28,10 @@ var YearExpression = module.exports = (function(){
 		base.prototype.addOperand.call(this, expr);
 	};
 
-	/** Takes a date and returns the full year. **/
+	/**
+	* Takes a date and returns the full year.
+	* @method evaluate
+	**/
 	proto.evaluate = function evaluate(doc) {
 		this.checkArgCount(1);
 		var date = this.operands[0].evaluate(doc);