示例#1
0
        it.beforeAll(function () {
            MockTimestampDs = comb.define(MockDS, {
                instance: {

                    fetchRows: function (sql, cb) {
                        var from = this.__opts.from[0], ret = new comb.Promise();
                        if (from.toString() === "schema_info") {
                            ret = comb.async.array([
                                {version: patioMigrationVersion}
                            ]);
                        } else if (from.toString() === "schema_migrations") {
                            sortMigrationFiles();
                            ret = comb.async.array(patioMigrationFiles.map(function (f) {
                                return {filename: f};
                            }));
                        } else if (from.toString() === "sm") {
                            ret = comb.async.array(patioMigrationFiles.map(function (f) {
                                return {fn: f};
                            }));
                        }
                        return ret;
                    },

                    insert: function (values) {
                        var from = this.__opts.from[0].toString(), ret = new comb.Promise().callback(0);
                        if (from === "schema_info") {
                            patioMigrationVersion = values[Object.keys(values)[0]];
                        } else if (from === "schema_migrations" || from === "sm") {
                            patioMigrationFiles.push(values[Object.keys(values)[0]]);
                        }
                        return ret;
                    },

                    remove: function () {
                        var from = this.__opts.from[0].toString(), ret = new comb.Promise().callback(1);
                        if (from === "schema_migrations" || from === "sm") {
                            var where = this.__opts.where.args, index = patioMigrationFiles.indexOf(where[where.length - 1]);
                            if (index > -1) {
                                patioMigrationFiles.splice(index, 1);
                            }
                        }
                        return ret;
                    },

                    getters: {
                        columns: function () {
                            var from = this.__opts.from[0].toString(), ret = new comb.Promise();
                            if (from === "schema_info") {
                                ret.callback(["version"]);
                            } else if (from === "schema_migrations") {
                                ret.callback(["filename"]);
                            } else if (from === "sm") {
                                ret.callback(["fn"]);
                            }
                            return ret;
                        }
                    }
                }
            });
            MockTimestampDB = comb.define(MockDB, {
                instance: {
                    getters: {
                        dataset: function () {
                            return new MockTimestampDs(this);
                        }
                    }
                }
            });

            TSDB = new MockTimestampDB();
        });
示例#2
0
module.exports = exports = comb.define(null, {
    static: {

        addRoute: function (route, cb) {
            this.routes.push(["get", route, cb]);
        },

        findByIdRoute: function (params) {
            var ret = new comb.Promise();
            return this.findById(params.id).chain(function (model) {
                if (model) {
                    return model.toObject();
                } else {
                    throw new Error("Could not find a model with id " + id);
                }
            });
        },

        removeByIdRoute: function (params) {
            return this.removeById(params.id);
        },

        __routeProxy: function (cb) {
            return function (req, res) {
                comb.when(cb(req.params)).chain(comb.hitch(res, "send"), function (err) {
                    res.send({error: err.message});
                });
            }
        },

        route: function (app) {
            var routes = this.routes;
            for (var i in routes) {
                var route = routes[i];
                app[route[0]](route[1], this.__routeProxy(route[2]));
            }
        },

        getters: {
            routes: function () {
                if (comb.isUndefined(this.__routes)) {
                    var routes = this.__routes = [
                        ["get", "/" + this.tableName + "/:id", comb.hitch(this, "findByIdRoute")],
                        ["delete", "/" + this.tableName + "/:id", comb.hitch(this, "removeByIdRoute")]
                    ];
                }
                return this.__routes;
            }

        }
    }
});
示例#3
0
文件: hive.js 项目: C2FO/hive
exports = module.exports = comb.define(null, {

    instance : {
        /**
         * @lends Hive.prototype
         */

        __checkInterval : 1000,

        constructor : function(options) {
            options = options || {};
            //create a tree for times so we can quickly find values less
            //than a specified time
            this.__timeTree = new comb.collections.AVLTree({
                compare : function(a, b) {
                    var ret = 0;
                    if (a.expires < b.expires) {
                        ret = -1;
                    } else if (a.expires > b.expires) {
                        ret = 1;
                    }
                    return ret;
                }
            });
            //use a tree so we can have complex keys later
            //otherwise we are limited to just strings
            //also this performs great on look up!
            //Also allows quick lookups on multiple values
            var cmp = options.compare ? options.compare : function(a, b) {
                var ret = 0;
                if (a < b) {
                    ret = -1;
                } else if (a > b) {
                    ret = 1;
                }
                return ret;
            }
            this.__values = new comb.collections.AVLTree({
                compare : function(a, b) {
                    return cmp(a.key, b.key);
                }
            });
            this.__cleanupInterval = setInterval(comb.hitch(this, this._checkValues), this.__checkInterval);
        },

        /**
         * Kills the clean up process for looking for expired keys.
         */
        kill : function() {
            clearInterval(this.__cleanupInterval);
        },

        /**
         * Casts a key
         * @param {*} key the key to cast
         */
        __castKey : function(key) {
            //TODO make this overridable.
            return "" + key;
        },

        /**
         * Checks for expired values.
         */
        _checkValues : function() {
            var timeTree = this.__timeTree,
                    valueTree = this.__values,
                    now = new Date().getTime();
            var vals = this.__timeTree.findLessThan(now);
            vals.forEach(function(v) {
                timeTree.remove(v);
                valueTree.remove(v);
            });
        },

        /**
         * Covert seconds to milliseconds
         * @param {Number} seconds number of seconds to convert.
         */
        _convertSeconds : function(seconds) {
            return seconds * 60000;
        },

        /**
         * Retrives all values with a key less than the provided key.
         *
         * @param {*} key the key to look up.
         *
         * @return {Array} an array of values.
         */
        getKeyLt : function(key) {
            key = this.__castKey(key);
            return this.__values.findLessThan({key : key}, true).map(function(v) {
                return v.value
            });
        },

        /**
         * Retrives all values with a key less or equal to a than the provided key.
         *
         * @param {*} key the key to look up.
         *
         * @return {Array} an array of values.
         */
        getKeyLte : function(key) {
            key = this.__castKey(key);
            return this.__values.findLessThan({key : key}).map(function(v) {
                return v.value
            });
        },

        /**
         * Retrives all values with a greater than the provided key.
         *
         * @param {*} key the key to look up.
         *
         * @return {Array} an array of values.
         */
        getKeyGt : function(key) {
            key = this.__castKey(key);
            return this.__values.findGreaterThan({key : key}, true).map(function(v) {
                return v.value
            });
        },

        /**
         * Retrives all values with a greater or equal to than the provided key.
         *
         * @param {*} key the key to look up.
         *
         * @return {Array} an array of values.
         */
        getKeyGte : function(key) {
            key = this.__castKey(key);
            return this.__values.findGreaterThan({key : key}).map(function(v) {
                return v.value
            });
        },

        /**
         * Retrive the value for a specified key
         *
         * @param {*} key the key to look up.
         *
         * * @return {*} the value or null if not found.
         */
        get : function(key) {
            key = this.__castKey(key);
            var val = this.__values.find({key : key});
            return val ? val.value : null;
        },

        /**
         * Set a key value pair.
         * @param {*} key the key to store
         * @param {*} value the value to asociate with the key.
         * @param {Number} [expires=Infinity] if provided sets a max life on a key, value pair.
         *
         * * @return {*} the value
         */
        set : function(key, value, expires) {
            expires = !expires ? Infinity : new Date().getTime() + this._convertSeconds(expires);
            //we need to keep expires to ensure we can look up from tree also;
            var node = {key : key, value : value, expires : expires};
            //store the pointer in both
            this.__values.insert(node);
            if (expires != Infinity) {
                //dont worry about saving values that are set to infinity
                //as it doesnt matter
                this.__timeTree.insert(node);
            }
            return value;
        },

        /**
         * Replace a key value pair in this Store.
         *
         * @param {*} key the key to store
         * @param {*} value the value to asociate with the key.
         * @param {Number} [expires=Infinity] if provided sets a max life on a key, value pair.
         *
         * @return {*} the value
         */
        replace : function(key, value, expires) {
            var currValue = this.__values.find({key : key});
            if (currValue) {
                currValue.value = value;
            } else {
                this.set(key, value, expires);
            }
            return value;
        },

        /**
         * Appends a value to a current value, if the current value is a string, and the appending
         * value is a string then it will be appeneded to the value otherwise an array is created to
         * store both values.
         *
         * @param {*} key the key to look up.
         * @param {*} value the value to append
         */
        append : function(key, value) {
            var currNode = this.__values.find({key : key});
            if (currNode) {
                var currValue = currNode.value;
                //if they are both strings assume you can just appened it!
                if (comb.isString(currValue) && comb.isString(value)) {
                    currNode.value += value;
                } else {
                    //make it an array
                    currNode.value = [currValue, value];
                }
            }
        },

        /**
         * Prepends a value to a current value, if the current value is a string, and the prepedning
         * value is a string then it will be prepeneded to the value otherwise an array is created to
         * store both values.
         *
         * @param {*} key the key to look up.
         * @param {*} value the value to prepend
         */
        prepend : function(key, value) {
            var currNode = this.__values.find({key : key});
            if (currNode) {
                var currValue = currNode.value;
                //if they are both strings assume you can just appened it!
                if (comb.isString(currValue) && comb.isString(value)) {
                    currNode.value = value + currValue;
                } else {
                    //make it an array
                    currNode.value = [value, currValue];
                }
            }
        },

        /**
         * Increments a value, if it is numeric.
         *
         * @param {*} key the key to look up the value to increment.
         */
        incr : function(key) {
            var num;
            var currNode = this.__values.find({key : key});
            if (currNode) {
                var currValue = currNode.value;
                if (comb.isNumber(currValue)) {
                    //update the pointers value
                    return ++currNode.value;
                } else {
                    num = Number(currValue);
                    if (!isNaN(num)) {
                        currNode.value = ++num;
                    }
                }
            }
            return num;
        },

        /**
         * Decrements a value, if it is numeric.
         *
         * @param {*} key the key to look up the value to decrement.
         */
        decr : function(key) {
            var num;
            var currNode = this.__values.find({key : key});
            if (currNode) {
                var currValue = currNode.value;
                if (comb.isNumber(currValue)) {
                    //update the pointers value
                    return --currNode.value;
                } else {
                    num = Number(currValue);
                    if (!isNaN(num)) {
                        currNode.value = --num;
                    }
                }
            }
            return num;
        },

        /**
         * Remove a value from this store.
         *
         * @param {*} key the key of the key value pair to remove.
         */
        remove : function(key) {
            var currValue = this.__values.find({key : key});
            if (currValue) {
                this.__values.remove(currValue);
                if (currValue.expires != Infinity) {
                    this.__timeTree.remove(currValue);
                }
            }
            return null;
        },

        /**
         * Remove all values from the store.
         */
        flushAll : function() {
            this.__values.clear();
            this.__timeTree.clear();
            return true;
        }
    }

});
示例#4
0
comb.define([actions, graph, features, query, sql], {
    instance:{

        /**@lends patio.Dataset.prototype*/

        /**
         * Class that is used for querying/retirving datasets from a database.
         *
         * <p> Dynamically genertated methods include
         * <ul>
         *     <li>Join methods from {@link patio.Dataset.CONDITIONED_JOIN_TYPES} and
         *         {@link  patio.Dataset.UNCONDITIONED_JOIN_TYPES}, these methods handle the type call
         *         to {@link patio.dataset.Query#joinTable}, so to invoke include all arguments that
         *         {@link patio.dataset.Query#joinTable} requires except the type parameter. The default list includes.
         *         <ul>
         *             <li>Conditioned join types that accept conditions.
         *                  <ul>
         *                      <li>inner - INNER JOIN</li>
         *                      <li>fullOuter - FULL OUTER</li>
         *                      <li>rightOuter - RIGHT OUTER JOIN</li>
         *                      <li>leftOuter - LEFT OUTER JOIN</li>
         *                      <li>full - FULL JOIN</li>
         *                      <li>right - RIGHT JOIN</li>
         *                      <li>left - LEFT JOIN</li>
         *                  </ul>
         *             </li>
         *             <li>Unconditioned join types that do not accept join conditions
         *                  <ul>
         *                      <li>natural - NATURAL JOIN</li>
         *                      <li>naturalLeft - NATURAL LEFT JOIN</li>
         *                      <li>naturalRight - NATURAL RIGHT JOIN</li>
         *                      <li>naturalFull - NATURA FULLL JOIN</li>
         *                      <li>cross - CROSS JOIN</li>
         *                  </ul>
         *             </li>
         *         </ul>
         *      </li>
         *  </li>
         * </ul>
         *
         * <p>
         *     <h4>Features:</h4>
         *     <p>
         *           Features that a particular {@link patio.Dataset} supports are shown in the example below.
         *           If you wish to implement an adapter please override these values depending on the database that
         *           you are developing the adapter for.
         *      </p>
         *      <pre class="code">
         *          var ds = DB.from("test");
         *
         *          //The default values returned
         *
         *          //Whether this dataset quotes identifiers.
         *          //Whether this dataset quotes identifiers.
         *          ds.quoteIdentifiers //=>true
         *
         *          //Whether this dataset will provide accurate number of rows matched for
         *          //delete and update statements.  Accurate in this case is the number of
         *          //rows matched by the dataset's filter.
         *          ds.providesAccurateRowsMatched; //=>true
         *
         *          //Times Whether the dataset requires SQL standard datetimes (false by default,
         *          // as most allow strings with ISO 8601 format).
         *          ds.requiresSqlStandardDate; //=>false
         *
         *          //Whether the dataset supports common table expressions (the WITH clause).
         *          ds.supportsCte; //=>true
         *
         *          //Whether the dataset supports the DISTINCT ON clause, false by default.
         *          ds.supportsDistinctOn; //=>false
         *
         *          //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
         *          ds.supportsIntersectExcept; //=>true
         *
         *          //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default
         *          ds.supportsIntersectExceptAll; //=>true
         *
         *          //Whether the dataset supports the IS TRUE syntax.
         *          ds.supportsIsTrue; //=>true
         *
         *          //Whether the dataset supports the JOIN table USING (column1, ...) syntax.
         *          ds.supportsJoinUsing; //=>true
         *
         *          //Whether modifying joined datasets is supported.
         *          ds.supportsModifyingJoin; //=>false
         *
         *          //Whether the IN/NOT IN operators support multiple columns when an
         *          ds.supportsMultipleColumnIn; //=>true
         *
         *          //Whether the dataset supports timezones in literal timestamps
         *          ds.supportsTimestampTimezone; //=>false
         *
         *          //Whether the dataset supports fractional seconds in literal timestamps
         *          ds.supportsTimestampUsecs; //=>true
         *
         *          //Whether the dataset supports window functions.
         *          ds.supportsWindowFunctions; //=>false
         *       </pre>
         * <p>
         * <p>
         *     <h4>Actions</h4>
         *     <p>
         *         Each dataset does not actually send any query to the database until an action method has
         *         been called upon it(with the exception of {@link patio.Dataset#graph} because columns
         *         from the other table might need retrived in order to set up the graph). Each action
         *         returns a <i>comb.Promise</i> that will be resolved with the result or errback, it is important
         *         that you account for errors otherwise it can be difficult to track down issues.
         *         The list of action methods is:
         *         <ul>
         *             <li>{@link patio.Dataset#all}</li>
         *             <li>{@link patio.Dataset#one}</li>
         *             <li>{@link patio.Dataset#avg}</li>
         *             <li>{@link patio.Dataset#count}</li>
         *             <li>{@link patio.Dataset#columns}</li>
         *             <li>{@link patio.Dataset#remove}</li>
         *             <li>{@link patio.Dataset#forEach}</li>
         *             <li>{@link patio.Dataset#empty}</li>
         *             <li>{@link patio.Dataset#first}</li>
         *             <li>{@link patio.Dataset#get}</li>
         *             <li>{@link patio.Dataset#import}</li>
         *             <li>{@link patio.Dataset#insert}</li>
         *             <li>{@link patio.Dataset#save}</li>
         *             <li>{@link patio.Dataset#insertMultiple}</li>
         *             <li>{@link patio.Dataset#saveMultiple}</li>
         *             <li>{@link patio.Dataset#interval}</li>
         *             <li>{@link patio.Dataset#last}</li>
         *             <li>{@link patio.Dataset#map}</li>
         *             <li>{@link patio.Dataset#max}</li>
         *             <li>{@link patio.Dataset#min}</li>
         *             <li>{@link patio.Dataset#multiInsert}</li>
         *             <li>{@link patio.Dataset#range}</li>
         *             <li>{@link patio.Dataset#selectHash}</li>
         *             <li>{@link patio.Dataset#selectMap}</li>
         *             <li>{@link patio.Dataset#selectOrderMap}</li>
         *             <li>{@link patio.Dataset#set}</li>
         *             <li>{@link patio.Dataset#singleRecord}</li>
         *             <li>{@link patio.Dataset#singleValue}</li>
         *             <li>{@link patio.Dataset#sum}</li>
         *             <li>{@link patio.Dataset#toCsv}</li>
         *             <li>{@link patio.Dataset#toHash}</li>
         *             <li>{@link patio.Dataset#truncate}</li>
         *             <li>{@link patio.Dataset#update}</li>
         *         </ul>
         *
         *     </p>
         * </p>
         *
         * @constructs
         *
         *
         * @param {patio.Database} db the database this dataset should use when querying for data.
         * @param {Object} opts options to set on this dataset instance
         *
         * @property {Function} rowCb callback to be invoked for each row returned from the database.
         *                      the return value will be used as the result of query. The rowCb can also return a promise,
         *                      The resolved value of the promise will be used as result.
         *
         * @property {String} identifierInputMethod this is the method that will be called on each identifier returned from the database.
         *                                          This value will be defaulted to whatever the identifierInputMethod
         *                                          is on the database used in initialization.
         *
         * @property {String} identifierOutputMethod this is the method that will be called on each identifier sent to the database.
         *                                          This value will be defaulted to whatever the identifierOutputMethod
         *                                          is on the database used in initialization.
         * @property {String} firstSourceAlias The first source (primary table) for this dataset. If the table is aliased, returns the aliased name.
         *                                     throws a {patio.DatasetError} tf the dataset doesn't have a table.
         * <pre class="code">
         *   DB.from("table").firstSourceAlias;
         *   //=> "table"
         *
         *   DB.from("table___t").firstSourceAlias;
         *   //=> "t"
         * </pre>
         *
         * @property {String} firstSourceTable  The first source (primary table) for this dataset.  If the dataset doesn't
         *                                      have a table, raises a {@link patio.erros.DatasetError}.
         *<pre class="code">
         *
         *  DB.from("table").firstSourceTable;
         *         //=> "table"
         *
         *  DB.from("table___t").firstSourceTable;
         *         //=> "t"
         * </pre>
         * @property {Boolean} isSimpleSelectAll Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false.
         * <pre class="code">
         *     DB.from("items").isSimpleSelectAll; //=> true
         *     DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false
         * </pre>
         * @property {boolean} [quoteIdentifiers=true]  Whether this dataset quotes identifiers.
         * @property {boolean} [providesAccurateRowsMatched=true] Whether this dataset will provide accurate number of rows matched for
         *   delete and update statements.  Accurate in this case is the number of
         *   rows matched by the dataset's filter.
         * @property {boolean} [requiresSqlStandardDate=false] Whether the dataset requires SQL standard datetimes (false by default,
         *                                                     as most allow strings with ISO 8601 format).
         * @property {boolean} [supportsCte=true] Whether the dataset supports common table expressions (the WITH clause).
         * @property {boolean} [supportsDistinctOn=false] Whether the dataset supports the DISTINCT ON clause, false by default.
         * @property {boolean} [supportsIntersectExcept=true] Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default.
         * @property {boolean} [supportsIntersectExceptAll=true] Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default.
         * @property {boolean} [supportsIsTrue=true] Whether the dataset supports the IS TRUE syntax.
         * @property {boolean} [supportsJoinUsing=true] Whether the dataset supports the JOIN table USING (column1, ...) syntax.
         * @property {boolean} [supportsModifyingJoin=false] Whether modifying joined datasets is supported.
         * @property {boolean} [supportsMultipleColumnIn=true] Whether the IN/NOT IN operators support multiple columns when an
         * @property {boolean} [supportsTimestampTimezone=false] Whether the dataset supports timezones in literal timestamps
         * @property {boolean} [supportsTimestampUsecs=true] Whether the dataset supports fractional seconds in literal timestamps
         * @property {boolean} [supportsWindowFunctions=false] Whether the dataset supports window functions.
         */
        constructor:function (db, opts) {
            this._super(arguments);
            this.db = db;
            this.__opts = {};
            this.__rowCb = null;
            if (db) {
                this.__quoteIdentifiers = db.quoteIdentifiers;
                this.__identifierInputMethod = db.identifierInputMethod;
                this.__identifierOutputMethod = db.identifierOutputMethod;
            }
        },


        /**
         * Returns a new clone of the dataset with with the given options merged into the current datasets options.
         * If the options changed include options in {@link patio.dataset.Query#COLUMN_CHANGE_OPTS}, the cached
         * columns are deleted.  This method should generally not be called
         * directly by user code.
         *
         * @param {Object} opts options to merge into the curred datasets options and applied to the returned dataset.
         * @return [patio.Dataset] a cloned dataset with the merged options
         **/
        mergeOptions:function (opts) {
            opts = comb.isUndefined(opts) ? {} : opts;
            var ds = new this._static(this.db, {});
            ds.rowCb = this.rowCb;
            this._static.FEATURES.forEach(function (f) {
                ds[f] = this[f]
            }, this);
            ds.__opts = comb.merge({}, this.__opts, opts);
            ds.identifierInputMethod = this.identifierInputMethod;
            ds.identifierOutputMethod = this.identifierOutputMethod;
            var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS;
            if (Object.keys(opts).some(function (o) {
                return columnChangeOpts.indexOf(o) != -1;
            })) {
                ds.__opts.columns = null;
            }
            return ds;
        },


        /**
         * Converts a string to an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
         * or {@link patio.sql.AliasedExpression}, depending on the format:
         *
         * <ul>
         *      <li>For columns : table__column___alias.</li>
         *      <li>For tables : schema__table___alias.</li>
         * </ul>
         * each portion of the identifier is optional. See example below
         *
         * @example
         *
         * ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a");
         * ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column);
         * ds.stringToIdentifier("table__column___alias");
         *      //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);
         *
         * @param {String} name the name to covert to an an {@link patio.sql.Identifier}, {@link patio.sql.QualifiedIdentifier},
         * or {@link patio.sql.AliasedExpression}.
         *
         * @return  {patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression} an identifier generated based on the name string.
         */
        stringToIdentifier:function (name) {
            if (comb.isString(name)) {
                var parts = this._splitString(name);
                var schema = parts[0], table = parts[1], alias = parts[2];
                return (schema && table && alias
                        ? new AliasedExpression(new QualifiedIdentifier(schema, table), alias)
                        : (schema && table
                        ? new QualifiedIdentifier(schema, table)
                        : (table && alias
                        ? new AliasedExpression(new Identifier(table), alias) : new Identifier(table))));
            } else {
                return name;
            }
        },

        /**
         * Can either be a string or null.
         *
         *
         * @example
         * //columns
         *  table__column___alias //=> table.column as alias
         *  table__column //=> table.column
         *  //tables
         *  schema__table___alias //=> schema.table as alias
         *  schema__table //=> schema.table
         *
         * //name and alias
         * columnOrTable___alias //=> columnOrTable as alias
         *
         *
         *
         * @return {String[]} an array with the elements being:
         * <ul>
         *      <li>For columns :[table, column, alias].</li>
         *      <li>For tables : [schema, table, alias].</li>
         * </ul>
         */
        _splitString:function (s) {
            var ret, m;
            if ((m = s.match(this._static.COLUMN_REF_RE1)) != null) {
                ret = m.slice(1);
            }
            else if ((m = s.match(this._static.COLUMN_REF_RE2)) != null) {
                ret = [null, m[1], m[2]];
            }
            else if ((m = s.match(this._static.COLUMN_REF_RE3)) != null) {
                ret = [m[1], m[2], null];
            }
            else {
                ret = [null, s, null];
            }
            return ret;
        },

        /**
         * @ignore
         **/
        getters:{

            rowCb:function () {
                return this.__rowCb;
            },

            identifierInputMethod:function () {
                return this.__identifierInputMethod;
            },

            identifierOutputMethod:function () {
                return this.__identifierOutputMethod;
            },

            firstSourceAlias:function () {
                var source = this.__opts.from;
                if (comb.isUndefinedOrNull(source) || !source.length) {
                    throw new DatasetError("No source specified for the query");
                }
                var source = source[0];
                if (comb.isInstanceOf(source, AliasedExpression)) {
                    return source.alias;
                } else if (comb.isString(source)) {
                    var parts = this._splitString(source);
                    var alias = parts[2];
                    return alias ? alias : source;
                } else {
                    return source;
                }
            },

            firstSourceTable:function () {
                var source = this.__opts.from;
                if (comb.isUndefinedOrNull(source) || !source.length) {
                    throw new QueryError("No source specified for the query");
                }
                var source = source[0];
                if (comb.isInstanceOf(source, AliasedExpression)) {
                    return source.expression;
                } else if (comb.isString(source)) {
                    var parts = this._splitString(source);
                    return source;
                } else {
                    return source;
                }
            }
        },

        /**
         * @ignore
         **/
        setters:{
            /**@lends patio.Dataset.prototype*/

            identifierInputMethod:function (meth) {
                this.__identifierInputMethod = meth;
            },

            identifierOutputMethod:function (meth) {
                this.__identifierOutputMethod = meth;
            },

            rowCb:function (cb) {
                if (comb.isFunction(cb) || comb.isNull(cb)) {
                    this.__rowCb = cb;
                } else {
                    throw new DatasetError("rowCb mus be a function");
                }
            }
        }
    },

    static:{
        COLUMN_REF_RE1:/^(\w+)__(\w+)___(\w+)$/,
        COLUMN_REF_RE2:/^(\w+)___(\w+)$/,
        COLUMN_REF_RE3:/^(\w+)__(\w+)$/
    }
}).as(module);
示例#5
0
var UsersClient = comb.define(client,{
	instance : {
		constructor : function(options){
			options = options || {};
			options.url = "users";
            this._super([options]);
		},
		
		authenticate : function(username, password, callback){
			request({
				uri : this.host + this.url + '/authenticate',
				method : 'post',
				headers : {
					authorization : this.auth
				},
				json : {
					username : username,
					password : password
				}
			}, callback);
		},
		
		signup : function(username, password, callback){
			request({
				uri : this.host + this.url + '/signup',
				method : 'post',
				headers : {
					authorization : this.auth
				},
				json : {
					username : username,
					password : password
				}
			}, callback);
		},
		
		forgot : function(username, callback){
			request({
				uri : this.host + this.url + '/forgot',
				method : 'post',
				headers : {
					authorization : this.auth
				},
				json : {
					username : username
				}
			}, callback);
		},
		
		sendVerificationEmail : function(username, callback){
			request({
				uri : this.host + this.url + '/send_verification',
				method : 'post',
				headers : {
					authorization : this.auth
				},
				json : {
					username : username
				}
			}, callback);
		}
	}
});
示例#6
0
文件: query.js 项目: pvilchez/patio
var QueryPlugin = comb.define(null, {
    instance:{
        /**@lends patio.Model.prototype*/

        _getPrimaryKeyQuery:function () {
            var q = {};
            this.primaryKey.forEach(function (k) {
                q[k] = this[k];
            }, this);
            return q;
        },

        _clearPrimaryKeys:function () {
            var q = {};
            this.__ignore = true;
            this.primaryKey.forEach(function (k) {
                this.__values[k] = null;
            }, this);
            this.__ignore = false;
        },

        /**
         * Forces the reload of the data for a particular model instance.  The Promise returned is resolved with the
         * model.
         *
         * @example
         *
         * myModel.reload().then(function(model){
         *    //model === myModel
         * });
         *
         * @return {comb.Promise} resolved with the model instance.
         */
        reload:function () {
            var ret = new Promise();
            if (!this.__isNew) {
                this.dataset.naked().filter(this._getPrimaryKeyQuery()).one().then(hitch(this, function (values) {
                    this.__set(values, true);
                    ret.callback(this);
                }), hitch(ret, "errback"));
            } else {
                ret.callback(this);
            }
            return ret;
        },

        /**
         * This method removes the instance of the model. If the model {@link patio.Model#isNew} then the promise is
         * resolved with a 0 indicating no rows were affected. Otherwise the model is removed, primary keys are cleared
         * and the model's isNew flag is set to true.
         *
         * @example
         * myModel.remove().then(function(){
         *     //model is deleted
         *     assert.isTrue(myModel.isNew);
         * });
         *
         * //dont use a transaction to remove this model
         * myModel.remove({transaction : false}).then(function(){
         *     //model is deleted
         *     assert.isTrue(myModel.isNew);
         * });
         *
         * @param {Object} [options] additional options.
         * @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when
         * removing the model.
         *
         * @return {comb.Promise} called back after the deletion is successful.
         */
        remove:function (options) {
            if (!this.__isNew) {
                return this._checkTransaction(options, comb.hitch(this, function () {
                    var ret = new Promise();
                    this._hook("pre", "remove").then(comb.hitch(this, function () {
                        this._remove().then(comb.hitch(this, function () {
                            this._hook("post", "remove").then(comb.hitch(this, function(){
                                this._clearPrimaryKeys();
                                this.__isNew = true;
                                ret.callback(this);
                            }), comb.hitch(ret, "errback"));
                        }), comb.hitch(ret, "errback"));
                    }), comb.hitch(ret, "errback"));
                    return ret;
                }));
            } else {
                return new comb.Promise().callback(0);
            }
        },

        _remove:function () {
            return this.dataset.filter(this._getPrimaryKeyQuery()).remove();
        },

        /**
         * @private
         * Called after a save action to reload the model properties,
         * abstracted out so this can be overidden by sub classes
         */
        _saveReload:function () {
            return this._static.reloadOnSave ? this.reload() : new comb.Promise().callback(this);
        },

        /**
         * @private
         * Called after an update action to reload the model properties,
         * abstracted out so this can be overidden by sub classes
         */
        _updateReload:function () {
            return this._static.reloadOnUpdate ? this.reload() : new comb.Promise().callback(this);
        },

        /**
         * Updates a model. This action checks if the model is not new and values have changed.
         * If the model is new then the {@link patio.Model#save} action is called.
         *
         * @example
         *
         * //set values before saving
         * someModel.update({
         *      myVal1 : "newValue1",
         *      myVal2 : "newValue2",
         *      myVal3 : "newValue3"
         *      }).then(function(){
         *     //do something
         * });
         *
         * //set vals before saving and don't use a transaction
         * someModel.update({
         *      myVal1 : "newValue1",
         *      myVal2 : "newValue2",
         *      myVal3 : "newValue3"
         *      }, {transaction : false}).then(function(){
         *     //do something
         * });
         *
         * //or
         *
         * someModel.myVal1 = "newValue1";
         * someModel.myVal2 = "newValue2";
         * someModel.myVal3 = "newValue3";
         *
         * //update model with current values
         * someModel.update().then(function(){
         *     //do something
         * });
         *
         *
         * //don't use a transaction
         * someModel.update(null, {transaction : false});
         *
         * @param {Object} [vals] optional values hash to set on the model before saving.
         * @param {Object} [options] additional options.
         * @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when
         * updating the model.
         *
         * @return {comb.Promise} resolved when the update action has completed.
         */
        update:function (vals, options) {
            if (!this.__isNew) {
                return this._checkTransaction(options, comb.hitch(this, function () {
                    comb.isHash(vals) && this.__set(vals);
                    var saveChange = !comb.isEmpty(this.__changed);
                    var ret = new Promise();
                    this._hook("pre", "update").then(comb.hitch(this, function () {
                        (saveChange ? this._update() : new Promise().callback()).then(comb.hitch(this, function () {
                            this._hook("post", "update").then(comb.hitch(ret, "callback", this), comb.hitch(ret, "errback"));
                        }), comb.hitch(ret, "errback"));
                    }), comb.hitch(ret, "errback"));

                    return ret;

                }));
            } else if (this.__isNew && this.__isChanged) {
                return this.save(vals, options);
            } else {
                return new comb.Promise().callback(this);
            }
        },

        _update:function () {
            var ret = new comb.Promise();
            this.dataset.filter(this._getPrimaryKeyQuery()).update(this.__changed)
                .chain(comb.hitch(this, "_updateReload"), comb.hitch(ret, "errback"))
                .then(comb.hitch(ret, "callback"), comb.hitch(ret, "errback"));
            return ret;
        },

        /**
         * Updates a model. This action checks if the model is new and values have changed.
         * If the model is not new then the {@link patio.Model#update} action is called.
         *
         * @example
         *
         * //set new values and save the model
         * someModel.save({
         *      myVal1 : "newValue1",
         *      myVal2 : "newValue2",
         *      myVal3 : "newValue3"
         *      }).then(function(){
         *          //do something
         *      });
         *
         * //or
         *
         * //set new values and save the model and DO NOT use a transaction
         * someModel.save({
         *      myVal1 : "newValue1",
         *      myVal2 : "newValue2",
         *      myVal3 : "newValue3"
         *      }, {transaction : false}).then(function(){
         *          //do something
         *      });
         *
         * //or
         * someModel.myVal1 = "newValue1";
         * someModel.myVal2 = "newValue2";
         * someModel.myVal3 = "newValue3";
         *
         * someModel.save().then(function(){
         *     //do something
         * });
         *
         * //or
         *
         * //dont use a transaction
         * someModel.save(null, {transaction : false}).then(function(){
         *     //do something
         * });
         *
         * @param {Object} [vals] optional values hash to set on the model before saving.
         * @param {Object} [options] additional options.
         * @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when
         * saving the model.
         *
         * @return {comb.Promise} resolved when the save action has completed.
         */
        save:function (vals, options) {
            if (this.__isNew) {
                return this._checkTransaction(options, comb.hitch(this, function () {
                    comb.isHash(vals) && this.__set(vals);
                    var ret = new comb.Promise();
                    this._hook("pre", "save")
                        .chain(comb.hitch(this, "_save"), comb.hitch(ret, "errback")).then(comb.hitch(this, function () {
                        this._hook("post", "save").then(comb.hitch(ret, "callback", this), comb.hitch(ret, "errback"));
                    }), comb.hitch(ret, "errback"));

                    return ret;
                }));
            } else {
                return this.update(vals, options);
            }
        },

        _save:function () {
            var pk = this._static.primaryKey[0];
            var ret = new comb.Promise()
            this.dataset.insert(this._toObject()).then(comb.hitch(this, function (id) {
                this.__ignore = true;
                if (id) {
                    this[pk] = id;
                }
                this.__ignore = false;
                this.__isNew = false;
                this.__isChanged = false;
                this._saveReload().then(comb.hitch(this, function () {
                    ret.callback(this);
                }), comb.hitch(ret, "errback"));
            }), comb.hitch(ret, "errback"));
            return ret;
        }

    },

    static:{

        /**@lends patio.Model*/

        /**
         * Retrieves a record by the primaryKey/s of a table.
         *
         * @example
         *
         * var User = patio.getModel("user");
         *
         * User.findById(1).then(function(userOne){
         *
         * });
         *
         * //assume the primary key is a compostie of first_name and last_name
         * User.findById(["greg", "yukon"]).then(function(userOne){
         *
         * });
         *
         *
         * @param {*} id the primary key of the record to find.
         *
         * @return {comb.Promise} called back with the record or null if one is not found.
         */
        findById:function (id) {
            var pk = this.primaryKey;
            pk = pk.length == 1 ? pk[0] : pk;
            var q = {};
            if (comb.isArray(id) && comb.isArray(pk)) {
                if (id.length === pk.length) {
                    pk.forEach(function (k, i) {
                        q[k] = id[i]
                    });
                } else {
                    throw new ModelError("findById : ids length does not equal the primaryKeys length.");
                }
            } else {
                q[pk] = id;
            }
            return this.filter(q).one();
        },

        /**
         * Finds a single model according to the supplied filter.
         * See {@link patio.Dataset#filter} for filter options.
         *
         *
         *
         * @param id
         */
        find:function (id) {
            return this.filter.apply(this, args).first();
        },

        /**
         * Update multiple rows with a set of values.
         *
         * @example
         * var User = patio.getModel("user");
         *
         * //BEGIN
         * //UPDATE `user` SET `password` = NULL WHERE (`last_accessed` <= '2011-01-27')
         * //COMMIT
         * User.update({password : null}, function(){
         *  return this.lastAccessed.lte(comb.date.add(new Date(), "year", -1));
         * });
         * //same as
         * User.update({password : null}, {lastAccess : {lte : comb.date.add(new Date(), "year", -1)}});
         *
         * //UPDATE `user` SET `password` = NULL WHERE (`last_accessed` <= '2011-01-27')
         * User.update({password : null}, function(){
         *  return this.lastAccessed.lte(comb.date.add(new Date(), "year", -1));
         * }, {transaction : false});
         *
         * @param {Object} vals a hash of values to update. See {@link patio.Dataset#update}.
         * @param query a filter to apply to the UPDATE.  See {@link patio.Dataset#filter}.
         *
         * @param {Object} [options] additional options.
         * @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when
         * updating the models.
         *
         * @return {Promise} a promise that is resolved once the update statement has completed.
         */
        update:function (vals, /*?object*/query, options) {
            options = options || {};
            var args = comb.argsToArray(arguments);
            return this._checkTransaction(options, comb.hitch(this, function () {
                var dataset = this.dataset;
                if (!comb.isUndefined(query)) {
                    dataset = dataset.filter(query);
                }
                return dataset.update(vals);
            }));
        },

        /**
         * Remove(delete) models. This can be used to do a mass delete of models.
         *
         * @example
         * var User = patio.getModel("user");
         *
         * //remove all users
         * User.remove();
         *
         * //remove all users who's names start with a.
         * User.remove({name : /A%/i});
         *
         * //remove all users who's names start with a, without a transaction.
         * User.remove({name : /A%/i}, {transaction : false});
         *
         * @param {Object} [q] query to filter the rows to remove. See {@link patio.Dataset#filter}.
         * @param {Object} [options] additional options.
         * @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when
         * removing the models.
         * @param {Boolean} [options.load=true] boolean set to prevent the loading of each model. This is more efficient
         * but the pre/post remove hooks not be notified of the deletion.
         *
         * @return {comb.Promise} called back when the removal completes.
         */
        remove:function (q, options) {
            options = options || {};
            var loadEach = comb.isBoolean(options.load) ? options.load : true;
            //first find all records so we call alert the middleware for each model
            return this._checkTransaction(options, comb.hitch(this, function () {
                var ds = this.dataset;
                ds = ds.filter.call(ds, q);
                if (loadEach) {
                    return ds.map(function (r) {
                        //todo this sucks find a better way!
                        return r.remove(options);
                    });
                } else {
                    return ds.remove();
                }
            }));
        },

        /**
         * Similar to remove but takes an id or an array for a composite key.
         *
         * @example
         *
         * User.removeById(1);
         *
         * @param id id or an array for a composite key, to find the model by
         * @param {Object} [options] additional options.
         * @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when
         * removing the model.
         *
         * @return {comb.Promise} called back when the removal completes.
         */
        removeById:function (id, options) {
            return this._checkTransaction(options, comb.hitch(this, function () {
                var p = new Promise();
                this.findById(id).then(function (model) {
                    if (model) {
                        model.remove(options).then(hitch(p, "callback"), hitch(p, "errback"));
                    } else {
                        p.callback(0);
                    }
                }, hitch(p, "errback"));
                return p;
            }));
        },

        /**
         * Save either a new model or list of models to the database.
         *
         * @example
         * var Student = patio.getModel("student");
         * Student.save([
         *      {
         *          firstName:"Bob",
         *          lastName:"Yukon",
         *          gpa:3.689,
         *          classYear:"Senior"
         *      },
         *      {
         *          firstName:"Greg",
         *          lastName:"Horn",
         *          gpa:3.689,
         *          classYear:"Sohpmore"
         *      },
         *      {
         *          firstName:"Sara",
         *          lastName:"Malloc",
         *          gpa:4.0,
         *          classYear:"Junior"
         *      },
         *      {
         *          firstName:"John",
         *          lastName:"Favre",
         *          gpa:2.867,
         *          classYear:"Junior"
         *      },
         *      {
         *          firstName:"Kim",
         *          lastName:"Bim",
         *          gpa:2.24,
         *          classYear:"Senior"
         *      },
         *      {
         *          firstName:"Alex",
         *          lastName:"Young",
         *          gpa:1.9,
         *          classYear:"Freshman"
         *      }
         * ]).then(function(users){
         *     //work with the users
         * });
         *
         * Save a single record
         * MyModel.save(m1);
         *
         * @param {patio.Model|Object|patio.Model[]|Object[]} record the record/s to save.
         * @param {Object} [options] additional options.
         * @param {Boolean} [options.transaction] boolean indicating if a transaction should be used when
         * saving the models.
         *
         * @return {comb.Promise} called back with the saved record/s.
         */
        save:function (items, options) {
            options = options || {};
            return this._checkTransaction(options, hitch(this, function () {
                var ps;
                if (comb.isArray(items)) {
                    ps = items.map(function (o) {
                        if (!comb.isInstanceOf(o, this)) {
                            o = new this(o);
                        }
                        return o.save(null, options);
                    }, this);
                    var ret = new comb.Promise();
                    return new PromiseList(ps, true).then(hitch(ret, "callback"), hitch(ret, "errback"));
                } else {
                    var ret = new comb.Promise();
                    try {
                        if (!comb.isInstanceOf(items, this)) {
                            items = new this(items);
                        }
                        ret = items.save(null, options);
                    } catch (e) {
                        ret.errback(e);
                    }
                    return ret;
                }
            }));
        }
    }
}).as(exports, "QueryPlugin");
示例#7
0
var Database = comb.define(null, {
    instance:{
        /**@lends patio.Database.prototype*/

        /**
         * The method name to invoke on a connection. The method name
         * should be overrode by an adapter if the method to execute
         * a query is different for the adapter specific connection class.
         */
        connectionExecuteMethod:"execute",

        /**
         * The <b>BEGIN</b> SQL fragment used to signify the start of a transaciton.
         */
        SQL_BEGIN:'BEGIN',

        /**
         * The <b>COMMIT</b> SQL fragment used to signify the end of a transaction and the final commit.
         */
        SQL_COMMIT:'COMMIT',

        /**
         * The <b>RELEASE SAVEPOINT</b> SQL fragment used by trasactions when using save points.
         * The adapter should override this SQL fragment if the adapters SQL is different.
         * <p>
         *      <b>This fragment will not be used if {@link patio.Database#supportsSavepoints} is false.</b>
         * </p>
         */
        SQL_RELEASE_SAVEPOINT:'RELEASE SAVEPOINT autopoint_%d',

        /**
         * The <b>ROLLBACK</b> SQL fragment used to rollback a database transaction.
         * This should be overrode by adapters if the SQL for the adapters
         * database is different.
         */
        SQL_ROLLBACK:'ROLLBACK',

        /**
         * The <b>ROLLBACK TO SAVEPOINT</b> SQL fragment used to rollback a database transaction
         * to a particular save point.
         * This should be overrode by adapters if the SQL for the adapters
         * database is different.
         *
         * <p>
         *      <b>This fragment will not be used if {@link patio.Database#supportsSavepoints} is false.</b>
         * </p>
         */
        SQL_ROLLBACK_TO_SAVEPOINT:'ROLLBACK TO SAVEPOINT autopoint_%d',

        /**
         * The <b>SAVEPOINT</b> SQL fragment used for creating a save point in a
         * database transaction.
         * <p>
         *      <b>This fragment will not be used if {@link patio.Database#supportsSavepoints} is false.</b>
         * </p>
         */
        SQL_SAVEPOINT:'SAVEPOINT autopoint_%d',

        /**
         * Object containing different database transaction isolation levels.
         * This object is used to look up the proper SQL when starting a new transaction
         * and setting the isolation level in the options.
         * @field
         */
        TRANSACTION_ISOLATION_LEVELS:{
            uncommitted:'READ UNCOMMITTED',
            committed:'READ COMMITTED',
            repeatable:'REPEATABLE READ',
            serializable:'SERIALIZABLE'
        },

        /**
         * @ignore
         */
        POSTGRES_DEFAULT_RE:/^(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))$/,

        /**
         * @ignore
         */
        MSSQL_DEFAULT_RE:/^(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))$/,

        /**
         * @ignore
         */
        MYSQL_TIMESTAMP_RE:/^CURRENT_(?:DATE|TIMESTAMP)?$/,

        /**
         * @ignore
         */
        STRING_DEFAULT_RE:/^'(.*)'$/,

        /**
         * @ignore
         */
        POSTGRES_TIME_PATTERN:"HH:mm:ss",

        /**
         * @ignore
         */
        POSTGRES_DATE_TIME_PATTERN:"yyyy-MM-dd HH:mm:ss.SSZ",

        __transactions:null,


        /**
         * @ignore
         */
        constructor:function () {
            this._super(arguments);
            this.__transactions = [];
            this.__transactionQueue = new comb.collections.Queue();
        },

        /**
         * Executes the given SQL on the database. This method should be implemented by adapters.
         * <b>This method should not be called directly by user code.</b>
         */
        execute:function (sql, options, cb, conn) {
            throw new NotImplemented("execute should be implemented by adapter");
        },

        /**
         * Return a Promise that is resolved with an object containing index information.
         * <p>
         *     The keys are index names. Values are objects with two keys, columns and unique.  The value of columns
         *     is an array of column names.  The value of unique is true or false
         *     depending on if the index is unique.
         * </p>
         *
         * <b>Should not include the primary key index, functional indexes, or partial indexes.</b>
         *
         * @example
         *   DB.indexes("artists").then(function(indexes){
         *     //e.g. indexes === {artists_name_ukey : {columns : [name], unique : true}};
         *   })
         **/
        indexes:function (table, opts) {
            throw new NotImplemented("indexes should be overridden by adapters");
        },

        /**
         * Proxy for {@link patio.Dataset#get}.
         */
        get:function () {
            return this.dataset.get.apply(this.dataset, arguments);
        },

        /**
         * @ignore
         * //todo implement prepared statements
         *
         * Call the prepared statement with the given name with the given object
         * of arguments.
         *
         *   DB.from("items").filter({id : 1}).prepare("first", "sa");
         *   DB.call("sa") //=> SELECT * FROM items WHERE id = 1
         *   */
        call:function (psName, hash) {
            hash = hash | {};
            this.preparedStatements[psName](hash);
        },

        /**
         * Method that should be used when submitting any DDL (Data DefinitionLanguage) SQL,
         * such as {@link patio.Database#createTable}. By default, calls {@link patio.Database#executeDui}.
         * <b>This method should not be called directly by user code.</b>
         */
        executeDdl:function (sql, opts, cb) {
            opts = opts || {};
            return this.executeDui(sql, opts, cb)
        },


        /**
         * Method that should be used when issuing a DELETE, UPDATE, or INSERT
         * statement.  By default, calls {@link patio.Database#execute}.
         * <b>This method should not be called directly by user code.</b>
         **/
        executeDui:function (sql, opts, cb) {
            opts = opts || {};
            return this.execute(sql, opts, cb)
        },

        /**
         * Method that should be used when issuing a INSERT
         * statement.  By default, calls {@link patio.Database#executeDui}.
         * <b>This method should not be called directly by user code.</b>
         **/
        executeInsert:function (sql, opts, cb) {
            opts = opts || {};
            return this.executeDui(sql, opts, cb);
        },


        /**
         * Runs the supplied SQL statement string on the database server..
         *
         * @example
         * DB.run("SET some_server_variable = 42")
         *
         * @param {String} sql the SQL to run.
         * @return {Promise} a promise that is resolved with the result of the query.
         **/
        run:function (sql, opts) {
            opts = opts || {};
            return this.executeDdl(sql, opts);
        },

        /**
         * Parse the schema from the database.
         *
         * @example
         *
         *   DB.schema("artists").then(function(schema){
         *     //example schema
         *     {
         *      id :  {
         *          type : "integer",
         *          primaryKey : true,
         *          "default" : "nextval('artist_id_seq'::regclass)",
         *          jsDefault : null,
         *          dbType : "integer",
         *          allowNull : false
         *       },
         *      name : {
         *          type : "string",
         *          primaryKey : false,
         *          "default" : null,
         *          jsDefault  : null,
         *          dbType : "text",
         *          allowNull : false
         *       }
         *     }
         *   })
         *
         * @param {String|patio.sql.Identifier|patio.sql.QualifiedIdentifier} table the table to get the schema for.
         * @param {Object} [opts=null] Additinal options.
         * @param {boolean} [opts.reload=false] Set to true to ignore any cached results.
         * @param {String|patio.sql.Identifier} [opts.schema] An explicit schema to use.  It may also be implicitly provided
         *            via the table name.
         *
         * @return {Promise} Returns a Promise that is resolved with the schema for the given table as an object
         * where the key is the column name and the value is and object containg column information. The default
         * column information returned.
         * <ul>
         *     <li>allowNull : Whether NULL is an allowed value for the column.</li>
         *     <li>dbType : The database type for the column, as a database specific string.</li>
         *     <li>"default" : The database default for the column, as a database specific string.</li>
         *     <li>primaryKey : Whether the columns is a primary key column.  If this column is not present,
         *                 it means that primary key information is unavailable, not that the column
         *                 is not a primary key.</li>
         *     <li>jsDefault : The database default for the column, as a javascript object.  In many cases, complex
         *                  database defaults cannot be parsed into javascript objects.</li>
         *     <li>type : A string specifying the type, such as "integer" or "string".</li>
         * <ul>
         *
         */
        schema:function (table, opts) {
            if (!comb.isFunction(this.schemaParseTable)) {
                throw new Error("Schema parsing is not implemented on this database");
            }
            var ret = new Promise();
            opts = opts || {};
            var schemaParts = this.__schemaAndTable(table);
            var sch = schemaParts[0], tableName = schemaParts[1];
            var quotedName = this.__quoteSchemaTable(table);
            opts = sch && !opts.schema ? comb.merge({schema:sch}, opts) : opts;
            opts.reload && delete this.schemas[quotedName];
            if (this.schemas[quotedName]) {
                ret.callback(this.schemas[quotedName]);
                return ret;
            } else {
                this.schemaParseTable(tableName, opts).then(hitch(this, function (cols) {
                    var schema = {};
                    if (!cols || cols.length == 0) {
                        ret.errback("Error parsing schema, no columns returns, table probably doesnt exist");
                    } else {
                        for (var i in cols) {
                            var c = cols[i];
                            var name = c[0], c = c[1];
                            c.jsDefault = this.__columnSchemaToJsDefault(c["default"], c.type);
                            schema[name] = c;
                        }
                        this.schemas[quotedName] = schema;
                        ret.callback(schema);
                    }
                }), hitch(ret, "errback"));
            }
            return ret;
        },

        /**
         * Remove the cached schema for the given table name
         * @example
         * DB.schema("artists").then(function(){
         *      DB.removeCachedSchema("artists");
         * });
         * @param {String|patio.sql.Identifier|patio.sql.QualifiedIdentifier} the table to remove from this
         * databases cached schemas.
         */
        removeCachedSchema:function (table) {
            if (this.schemas && !comb.isEmpty(this.schemas)) {
                delete this.schemas[this.__quoteSchemaTable(table)];
            }
        },

        /**
         * Determine if a table exists.
         * @example
         * comb.executeInOrder(DB, function(DB){
         *    return {
         *          table1Exists : DB.tableExists("table1"),
         *          table2Exists : DB.tableExists("table2")
         *    };
         * }).then(function(ret){
         *      //ret.table1Exists === true
         *      //ret.table2Exists === false
         * });
         * @param {String|patio.sql.Identifier|patio.sql.QualifiedIdentifier} the table to remove from this
         *
         * @return {Promise} a promise resolved with a boolean indicating if the table exists.
         */
        tableExists:function (table) {
            var ret = new Promise();
            this.from(table).first().then(function () {
                ret.callback(true);
            }, function () {
                ret.callback(false);
            });
            return ret;
        },

        /**
         * Returns a promise with a list of tables names in this database. This method
         * should be implemented by the adapter.
         *
         * @example
         * DB.tables().then(function(tables){
         *    //e.g. tables === ["table1", "table2", "table3"];
         * });
         *
         * @return {Promise} a promise that is resolved with a list of tablenames.
         */
        tables:function () {
            throw new NotImplemented("tables should be implemented by the adapter");
        },

        /**
         * Starts a database transaction.  When a database transaction is used,
         * either all statements are successful or none of the statements are
         * successful.
         * <p>
         *      <b>Note</b> that MySQL MyISAM tables do not support transactions.</p>
         * </p>
         *
         * @example
         * //normal transaction
         * DB.transaction(function() {
         *       this.execute('DROP TABLE test;');
         *       this.execute('DROP TABLE test2;');
         *  });
         *
         * //transaction with a save point.
         * DB.transaction(function() {
         *      this.transaction({savepoint : true}, function() {
         *          this.execute('DROP TABLE test;');
         *          this.execute('DROP TABLE test2;');
         *      });
         *});
         *
         * //WITH ISOLATION LEVELS
         *
         * db.supportsTransactionIsolationLevels = true;
         * //BEGIN
         * //SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
         * //DROP TABLE test1'
         * //COMMIT
         * DB.transaction({isolation:"uncommited"}, function(d) {
         *      d.run("DROP TABLE test1");
         * });
         *
         * //BEGIN
         * //SET TRANSACTION ISOLATION LEVEL READ COMMITTED
         * //DROP TABLE test1
         * //COMMIT
         *  DB.transaction({isolation:"committed"}, function(d) {
         *      d.run("DROP TABLE test1");
         *  });
         *
         * //BEGIN
         * //SET TRANSACTION ISOLATION LEVEL REPEATABLE READ'
         * //DROP TABLE test1
         * //COMMIT
         *  DB.transaction({isolation:"repeatable"}, function(d) {
         *      d.run("DROP TABLE test1");
         *  });
         *
         * //BEGIN
         * //SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
         * //DROP TABLE test1
         * //COMMIT
         *  DB.transaction({isolation:"serializable"}, function(d) {
         *      d.run("DROP TABLE test1");
         *  });
         *
         * //With an Error
         * //BEGIN
         * //DROP TABLE test
         * //ROLLBACK
         * DB.transaction(function(d) {
         *      d.execute('DROP TABLE test');
         *      throw "Error";
         * });
         *
         * @param {Object} [opts={}] options to use when performing the transaction.
         * @param {String} [opts.isolation] The transaction isolation level to use for this transaction,
         *               should be "uncommitted", "committed", "repeatable", or "serializable",
         *               used if given and the database/adapter supports customizable
         *               transaction isolation levels.
         * @param {String} [opts.prepare] A string to use as the transaction identifier for a
         *             prepared transaction (two-phase commit), if the database/adapter
         *             supports prepared transactions.
         * @param {Boolean} [opts.savepoint] Whether to create a new savepoint for this transaction,
         *               only respected if the database/adapter supports savepoints.  By
         *               default patio will reuse an existing transaction, so if you want to
         *               use a savepoint you must use this option.
         * @param {Function} cb a function used to perform the transaction. This function is
         * called in the scope of the database by default so one can use this. The funciton is also
         * called with the database as the first argument. If you have complex operations within your
         * transactions you will want to return a promise to ensure proper transaction scope.
         * <pre class="code">
         * var ds = db.from("user");
         * db.transaction(function(){
         *      var ret = new comb.Promise();
         *      ds.insert({
         *          firstName:"Jane",
         *          lastName:"Gorgenson",
         *          password:"******",
         *          dateOfBirth:new Date(1956, 1, 3)
         *      }).then(function(){
         *          ds.forEach(function(user){
         *                  return ds.where({id:user.id}).update({firstName:user.firstName + 1});
         *          }).then(comb.hitch(ret, "callback"), comb.hitch(ret, "errback"));
         *      }, comb.hitch(ret, "errback"));
         *     return ret;
         * });
         * </pre>
         *
         * @return {Promise} a promise that is resolved once the transaction is complete.
         **/
        transaction:function (opts, cb) {
            if (comb.isFunction(opts)) {
                cb = opts;
                opts = {};
            } else {
                opts = opts || {};
            }
            if (!this.__alreadyInTransaction) {
                this.__alreadyInTransaction = true;
                return this.__transaction(null, opts, cb);
            } else {
                var ret =  new comb.Promise();
                var transaction = comb.hitch(this, function(){
                    this.__transaction(null, opts, cb).then(hitch(ret, "callback"), hitch(ret, "errback"));
                });
                this.__transactionQueue.enqueue(transaction);
                return ret;
            }
        },

        __transactionProxy:function (cb, conn) {
            var promises = [];
            var repl = [];
            //set up our proxy methos
            ["transaction", "execute"].forEach(function (n) {
                var orig = this[n];
                repl.push({name:n, orig:orig});
                this[n] = function (arg1, arg2) {
                    try {
                        var ret;
                        if (n == "transaction") {
                            //if its a transaction with no options then we just create a promise from what ever is returned
                            if (comb.isFunction(arg1)) {
                                ret = comb.when(arg1.apply(this, [this]));
                            } else {
                                if (this.supportsSavepoints && !comb.isUndefinedOrNull(arg1.savepoint)) {
                                    //if we support save points there is a save point option then we
                                    //use __transaction again with the previous connection
                                    ret = this.__transaction(conn, arg1, arg2);
                                } else {
                                    //other wise use the function passed in to get the returned promise
                                    ret = comb.when(arg2.apply(this, [this]));
                                }
                            }
                        } else {
                            ret = orig.apply(this, comb.argsToArray(arguments).concat(conn));

                        }
                    } catch (e) {
                        ret = new Promise().errback(e);
                    }
                    promises.push(comb.when(ret));
                    return ret;
                }
            }, this);
            try {
                var cbRet = cb.apply(this, [this]);
                if (cbRet && comb.isPromiseLike(cbRet, Promise)) {
                    promises.push(cbRet);
                }
            } catch (e) {
                promises.push(new Promise().errback(e));
            }
            if (promises.length == 0) {
                promises.push(new Promise().callback());
            }
            return new PromiseList(promises).both(hitch(this, function () {
                repl.forEach(function (o) {
                    this[o.name] = o.orig;
                }, this)
            }));

        },

        _getConnection:function () {
            return this.pool.getConnection();
        },

        _returnConnection:function (conn) {
            if (!this.alreadyInTransaction(conn)) {
                this.pool.returnConnection(conn);
            }
        },

        __transaction:function (conn, opts, cb) {
            try {
                var promise = new comb.Promise();
                var connPromise = conn ? new comb.Promise().callback(conn) : this._getConnection().addCallback(comb.hitch(this, function (conn) {
                    //add the connection to the transactions
                    this.__transactions.push(conn);
                    //reset transaction depth to 0, this is used for keeping track of save points.
                    conn.__transactionDepth = 0;
                }));
                connPromise.then(hitch(this, function (conn) {
                    this.__beginTransaction(conn, opts).then(hitch(this, function () {
                        this.__transactionProxy(cb, conn).then(hitch(this, function (res) {
                            this.__commitTransaction(conn).then(comb.hitch(this, "__transactionComplete", promise, "callback", conn), comb.hitch(this, "__transactionComplete", promise, "errback", conn));
                        }), hitch(this, "__rollback", promise, conn));
                    }), comb.hitch(this, "__transactionComplete", promise, "errback", conn));
                }), comb.hitch(this, "__transactionComplete", promise, "errback"));
            } catch (e) {
                this.logError(e);
                promise.errback(e);
            }
            return promise;
        },

        __transactionComplete:function (promise, type, conn) {
            this.__finishTransactionAndCheckForMore(conn);
            promise[type].apply(promise, comb.argsToArray(arguments).slice(3));
        },

        __rollback:function (promise, conn, err) {
            this.__rollbackTransaction(conn, null, err).both(comb.hitch(this, "__finishTransactionAndCheckForMore", conn)).then(hitch(this, function () {
                if (conn.__transactionDepth <= 1) {
                    this.__transactionError(err, promise);
                } else {
                    promise.errback(err);
                }
            }));
        },

        __transactionError:function (err, promise) {
            if (comb.isArray(err)) {
                for (var i in err) {
                    var e = err[i];
                    if (comb.isArray(e) && e.length == 2) {
                        var realE = e[1];
                        if (realE != "ROLLBACK") {
                            promise.errback(realE);
                            break;
                        } else {
                            promise.callback();
                            break;
                        }
                    } else {
                        promise.errback(e);
                        break;
                    }
                }
            } else {
                if (e != "ROLLBACK") {
                    throw e;
                }
            }
        },

        __finishTransactionAndCheckForMore:function (conn) {
            if (this.alreadyInTransaction(conn)) {
                if (!this.supportsSavepoints || ((conn.__transactionDepth -= 1) <= 0)) {
                    conn && this.pool.returnConnection(conn);
                    var index, transactions = this.__transactions;
                    if ((index = transactions.indexOf(conn)) > -1) {
                        transactions.splice(index, 1);
                    }
                    if(!this.__transactionQueue.isEmpty){
                        this.__transactionQueue.dequeue()();
                    }else{
                        this.__alreadyInTransaction = false;
                    }
                }
            }
        },

        //SQL to start a new savepoint
        __beginSavepointSql:function (depth) {
            return format(Database.SQL_SAVEPOINT, depth);
        },

        // Start a new database connection on the given connection
        __beginNewTransaction:function (conn, opts) {
            var ret = new comb.Promise();
            this.__logConnectionExecute(conn, this.beginTransactionSql).chain(comb.hitch(this, "__setTransactionIsolation", conn, opts), hitch(ret, "errback")).then(hitch(ret, "callback"), hitch(ret, "errback"));
            return ret;
        },

        //Start a new database transaction or a new savepoint on the given connection.
        __beginTransaction:function (conn, opts) {
            var ret;
            if (this.supportsSavepoints) {
                if (conn.__transactionDepth > 0) {
                    ret = this.__logConnectionExecute(conn, this.__beginSavepointSql(conn.__transactionDepth));
                } else {
                    ret = this.__beginNewTransaction(conn, opts);
                }
                conn.__transactionDepth += 1;
            } else {
                ret = this.__beginNewTransaction(conn, opts);
            }
            return  ret;
        },

        // SQL to commit a savepoint
        __commitSavepointSql:function (depth) {
            return format(this.SQL_RELEASE_SAVEPOINT, depth);
        },

        //Commit the active transaction on the connection
        __commitTransaction:function (conn, opts) {
            opts = opts || {};
            if (this.supportsSavepoints) {
                var depth = conn.__transactionDepth;
                var sql = null;
                if (depth > 1) {
                    sql = this.__commitSavepointSql(depth - 1);
                } else {
                    this.__commiting = true;
                    sql = this.commitTransactionSql;
                }
                return this.__logConnectionExecute(conn, (sql));
            } else {
                this.__commiting = true;
                return this.__logConnectionExecute(conn, this.commitTransactionSql);
            }
        },


        //SQL to rollback to a savepoint
        __rollbackSavepointSql:function (depth) {
            return format(this.SQL_ROLLBACK_TO_SAVEPOINT, depth);
        },

        //Rollback the active transaction on the connection
        __rollbackTransaction:function (conn, opts, err) {
            opts = opts || {};
            if (this.supportsSavepoints) {
                var sql, depth = conn.__transactionDepth;
                if (depth > 1) {
                    sql = this.__rollbackSavepointSql(depth - 1);
                } else {
                    this.__commiting = true;
                    sql = this.rollbackTransactionSql;
                }
                return this.__logConnectionExecute(conn, sql);
            } else {
                this.__commiting = false;
                return this.__logConnectionExecute(conn, this.rollbackTransactionSql);
            }
        },

        // Set the transaction isolation level on the given connection
        __setTransactionIsolation:function (conn, opts) {
            var level;
            var ret = new Promise();
            if (this.supportsTransactionIsolationLevels && !comb.isUndefinedOrNull(level = comb.isUndefinedOrNull(opts.isolation) ? this.transactionIsolationLevel : opts.isolation)) {
                return this.__logConnectionExecute(conn, this.__setTransactionIsolationSql(level)).then(hitch(ret, "callback"), hitch(ret, "errback"));
            } else {
                ret.callback();
            }
            return ret;
        },

        // SQL to set the transaction isolation level
        __setTransactionIsolationSql:function (level) {
            return format("SET TRANSACTION ISOLATION LEVEL %s", this.TRANSACTION_ISOLATION_LEVELS[level]);
        },

        //Convert the given default, which should be a database specific string, into
        //a javascript object.
        __columnSchemaToJsDefault:function (def, type) {
            if (comb.isNull(def) || comb.isUndefined(def)) {
                return null;
            }
            var origDefault = def, m, datePattern, dateTimePattern, timeStampPattern, timePattern;
            if (this.type == "postgres" && (m = def.match(this.POSTGRES_DEFAULT_RE)) != null) {
                def = m[1] || m[2];
                dateTimePattern = this.POSTGRES_DATE_TIME_PATTERN, timePattern = this.POSTGRES_TIME_PATTERN;
            }
            if (this.type == "mssql" && (m = def.match(this.MSSQL_DEFAULT_RE)) != null) {
                def = m[1] || m[2];
            }
            if (["string", "blob", "date", "datetime", "year", "timestamp", "time", "enum"].indexOf(type) != -1) {
                if (this.type == "mysql") {
                    if (["date", "datetime", "time", "timestamp"].indexOf(type) != -1 && def.match(this.MYSQL_TIMESTAMP_RE)) {
                        return null;
                    }
                    origDefault = def = "'" + def + "'".replace("\\", "\\\\");
                }
                if ((m = def.match(this.STRING_DEFAULT_RE)) == null) {
                    return null;
                }
                def = m[1].replace("''", "'")
            }
            var ret = null;
            try {
                switch (type) {
                    case "boolean":
                        if (def.match(/[f0]/i)) {
                            ret = false;
                        } else if (def.match(/[t1]/i)) {
                            ret = true;
                        } else if (comb.isBoolean(def)) {
                            ret = def;
                        }

                        break;
                    case "string":
                    case "enum":
                        ret = def;
                        break;
                    case  "integer":
                        ret = parseInt(def, 10);
                        isNaN(ret) && (ret = null);
                        break;
                    case  "float":
                    case  "decimal":
                        ret = parseFloat(def, 10);
                        isNaN(ret) && (ret = null);
                        break;
                    case "year" :
                        ret = this.patio.stringToYear(def);
                        break;
                    case "date":
                        ret = this.patio.stringToDate(def, datePattern);
                        break;
                    case "timestamp":
                        ret = this.patio.stringToTimeStamp(def, timeStampPattern);
                        break;
                    case "datetime":
                        ret = this.patio.stringToDateTime(def, dateTimePattern);
                        break;
                    case "time":
                        ret = this.patio.stringToTime(def, timePattern);
                        break;
                }
            } catch (e) {
            }
            return ret;
        },

        /**
         * Match the database's column type to a javascript type via a
         * regular expression, and return the javascript type as a string
         * such as "integer" or "string".
         * @private
         */
        schemaColumnType:function (dbType) {
            var ret = null, m;
            if (dbType.match(/^interval$/i)) {
                ret = "interval";
            } else if (dbType.match(/^(character( varying)?|n?(var)?char|n?text)/i)) {
                ret = "string";
            } else if (dbType.match(/^int(eger)?|(big|small|tiny)int/i)) {
                ret = "integer";
            } else if (dbType.match(/^date$/i)) {
                ret = "date";
            } else if (dbType.match(/^year/i)) {
                ret = "year";
            } else if (dbType.match(/^((small)?datetime|timestamp( with(out)? time zone)?)$/i)) {
                ret = "datetime";
            } else if (dbType.match(/^time( with(out)? timezone)?$/i)) {
                ret = "time";
            } else if (dbType.match(/^(bit|boolean)$/i)) {
                ret = "boolean";
            } else if (dbType.match(/^(real|float|double( precision)?)$/i)) {
                ret = "float";
            } else if ((m = dbType.match(/^(?:(?:(?:num(?:ber|eric)?|decimal|double)(?:\(\d+,\s*(\d+)\))?)|(?:small)?money)/i))) {
                ret = m[1] && m[1] == '0' ? "integer" : "decimal";
            } else if (dbType.match(/^bytea|blob|image|(var)?binary/i)) {
                ret = "blob";
            } else if (dbType.match(/^enum/i)) {
                ret = "enum";
            } else if (dbType.match(/^set/i)) {
                ret = "set";
            }
            return ret;
        },

        /**
         * Returns true if this DATABASE is currently in a transaction.
         *
         * @param opts
         * @return {Boolean} true if this dabase is currently in a transaction.
         */
        alreadyInTransaction:function (conn, opts) {
            opts = opts || {};
            return this.__transactions.indexOf(conn) != -1 && (!this.supportsSavepoints || !opts.savepoint);
        },

        /**@ignore*/
        getters:{
            /**@lends patio.Database.prototype*/

            /**
             * SQL to BEGIN a transaction.
             * See {@link patio.Database#SQL_BEGIN} for default,
             * @field
             * @type String
             */
            beginTransactionSql:function () {
                return this.SQL_BEGIN;
            },

            /**
             * SQL to COMMIT a transaction.
             * See {@link patio.Database#SQL_COMMIT} for default,
             * @field
             * @type String
             */
            commitTransactionSql:function () {
                return this.SQL_COMMIT;
            },

            /**
             * SQL to ROLLBACK a transaction.
             * See {@link patio.Database#SQL_ROLLBACK} for default,
             * @field
             * @type String
             */
            rollbackTransactionSql:function () {
                return this.SQL_ROLLBACK;
            },

            /**
             * Return a function for the dataset's {@link patio.Dataset#outputIdentifierMethod}.
             * Used in metadata parsing to make sure the returned information is in the
             * correct format.
             *
             * @field
             * @type Function
             */
            outputIdentifierFunc:function () {
                return comb.hitch(this.dataset, this.dataset.outputIdentifier);
            },

            /**
             * Return a function for the dataset's {@link patio.Dataset#inputIdentifierMethod}.
             * Used in metadata parsing to make sure the returned information is in the
             * correct format.
             *
             * @field
             * @type Function
             */
            inputIdentifierFunc:function () {
                return comb.hitch(this.dataset, this.dataset.inputIdentifier);
            },

            /**
             * Return a dataset that uses the default identifier input and output methods
             * for this database.  Used when parsing metadata so that column are
             * returned as expected.
             *
             * @field
             * @type patio.Dataset
             */
            metadataDataset:function () {
                if (this.__metadataDataset) {
                    return this.__metadataDataset;
                }
                var ds = this.dataset;
                ds.identifierInputMethod = this.identifierInputMethod;
                ds.identifierOutputMethod = this.identifierOutputMethod;
                this.__metadataDataset = ds;
                return ds;
            }
        }
    },


    static:{
        SQL_BEGIN:'BEGIN',
        SQL_COMMIT:'COMMIT',
        SQL_RELEASE_SAVEPOINT:'RELEASE SAVEPOINT autopoint_%d',
        SQL_ROLLBACK:'ROLLBACK',
        SQL_ROLLBACK_TO_SAVEPOINT:'ROLLBACK TO SAVEPOINT autopoint_%d',
        SQL_SAVEPOINT:'SAVEPOINT autopoint_%d',

        TRANSACTION_BEGIN:'Transaction.begin',
        TRANSACTION_COMMIT:'Transaction.commit',
        TRANSACTION_ROLLBACK:'Transaction.rollback',

        TRANSACTION_ISOLATION_LEVELS:{
            uncommitted:'READ UNCOMMITTED',
            committed:'READ COMMITTED',
            repeatable:'REPEATABLE READ',
            serializable:'SERIALIZABLE'
        },

        POSTGRES_DEFAULT_RE:/^(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))$/,
        MSSQL_DEFAULT_RE:/^(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))$/,
        MYSQL_TIMESTAMP_RE:/^CURRENT_(?:DATE|TIMESTAMP)?$/,
        STRING_DEFAULT_RE:/^'(.*)'$/


    }
}).as(module);
示例#8
0
comb.define(null,{
    //define your instance methods and properties
    instance:{

        //will be called whenever a new instance is created
        constructor:function (options) {
            options = options || {};
            this._super(arguments);
            this._type = options.type || "mammal";
        },

        speak:function () {
            return  "A mammal of type " + this._type + " sounds like";
        },

        reproduce:function (opts) {
            //notice we can create a new instance regardless of subclass
            return new this._static(opts);
        },

        //Define your getters
        getters:{

            //can be accessed by using the get method. (mammal.get("type"))
            type:function () {
                return this._type;
            }
        },

        //Define your setters
        setters:{

            //can be accessed by using the set method. (mammal.set("type", "mammalType"))
            type:function (t) {
                this._type = t;
            }
        }
    },

    //Define your static methods
    static:{

        //Mammal.soundOff(); //"Im a mammal!!"
        soundOff:function () {
            return "Im a mammal!!";
        }
    }
}).as(module);
示例#9
0
    MockDataset = helper.MockDataset,
    comb = require("comb");


var ret = (module.exports = exports = new comb.Promise());
var suite = vows.describe("Dataset graphing");

moose.quoteIdentifiers = false;
moose.identifierInputMethod = null;
moose.identifierOutputMethod = null;

var DS1 = comb.define(Dataset, {
    instance:{
        getters:{
            columns:function () {
                return new comb.Promise().callback(["id", "x", "y"]);
            }
        }
    }
});

var DS2 = comb.define(Dataset, {
    instance:{
        getters:{
            columns:function () {
                return new comb.Promise().callback(["id", "x", "y", "graphId"]);
            }
        }
    }
});
var comb = require('comb');
var logger = require(LIB_DIR + 'log_factory').create("experiment_visits_dao");
var DAO = require('./dao.js');

var ExperimentVisitsDAO = comb.define(DAO,{
	instance : {
		constructor : function(options){
			options = options || {};
			options.model = "ExperimentVisits";
            this._super([options]);
		}
	}
});

module.exports = new ExperimentVisitsDAO();
示例#11
0
module.exports = exports = define(null, {

  static:{
    /**@lends patio.plugins.TimeStampPlugin*/

    /**
     * Adds timestamp functionality to a table.
     * @param {Object} [options]
     * @param {String} [options.updated="updated"] the name of the column to set the updated timestamp on.
     * @param {String} [options.created="created"] the name of the column to set the created timestamp on
     * @param {Boolean} [options.updateOnCreate=false] Set to true to set the updated column on creation
     **/
    timestamp:function(options) {
      options = options || {};
      var updateColumn = options.updated || "updated";
      var createdColumn = options.created || "created";
      var updateOnCreate = options.updateOnCreate || false;
      this.pre("save", function(next) {
        this[createdColumn] = new Date();
        if (updateOnCreate) {
          this[updateColumn] = new Date();
        }
        next();
      });
      this.pre("update", function(next) {
        this[updateColumn] = new Date();
        next();
      });
    }
  }

});
var ExperimentsBucket = comb.define(Bucket, {
	instance : {
		constructor : function(options){
			options = options || {};
			
			this.experimentId = options.experimentId;
			this.variations = options.variations;
			
			this._super(arguments);
		},
		
		/**
		 * 
		 * @param channel
		 * @param message
		 * @returns {Array}
		 */
		filter : function(channel, message){
			var ref = this;
			var tokens = message.split('__');
			
			var values = [];
			if(channel == CHANNEL_VARIATIONS){ // message Will be something like : 1000:1200__1001:4000 where 1000 and 1001 are experiement ids
				_.each(tokens, function(token){
					var exTokens = token.split(':');
					if(exTokens.length == 2 && exTokens[0] == ref.experimentId){ // first exToken must be experiment id
						logger.info("Matched experiment id : " + ref.experimentId);
						
						var variationId = exTokens[1]; // second exToken is variation id
						
						values.push({
							experimentId : ref.experimentId,
							variationId : variationId
						});
					} 
				});
			}else if(channel == CHANNEL_GOALS){ // message Will be something like : 100:1000:1200__100:1001:4000 where 100 is the goal id, 1000 and 1001 are experiement ids
				_.each(tokens, function(token){
					var gTokens = token.split(':');
					if(gTokens.length == 3 && gTokens[1] == ref.experimentId){ // second gtoken must be experiment id
						logger.info("Matched experiment id : " + ref.experimentId);
						
						var goalId = gTokens[0]; // first gToken is goal id
						var variationId = gTokens[2]; // third exToken is variation id
						
						values.push({
							experimentId : ref.experimentId,
							goalId : goalId,
							variationId : variationId
						});
					} 
				});
			}
			return values;
		},
		
		process : function(channel, values){
			var ref = this;
			if(values){
				if(channel == CHANNEL_VARIATIONS){ // mark the total field
					_.each(values, function(value){
						var key = "ex:" + value.experimentId;
						ref.client.hincrby(key, 'total', 1, function(err, reply){
							console.log(reply);
						});
					});
				}else if(channel == CHANNEL_GOALS){ // mark the goal hit
					_.each(values, function(value){
						var key = "ex:" + value.experimentId;
						var field = "g:" + value.goalId;
						
						ref.client.hincrby(key, field, 1, function(err, reply){
							console.log(reply);
						});
					});
				}
			}
		},
		
		addBuckets : function(){
			var ref = this;
			
			var createBucket = function(variation){
				var variationId = variation.id;
				
				if(variationId){
					logger.debug("creating new variation bucket for ex : " + ref.experimentId + " v : " + variationId);
					new VariationsBucket({experimentId : ref.experimentId, variationId : variationId});
				}
			};
			
			if(this.variations){
				_.each(this.variations, function(variation){
					createBucket(variation);
				});
			}else{
				VariationsClient.search(function(err, response){
					if(response.body){
						var body = JSON.parse(response.body);
						if(body.status && body.status.code == 1000){
							var data = body.data;
							if(data){
								if(_.isArray(data)){
									_.each(data, function(variation){
										createBucket(variation);
									});
								}else{
									createBucket(data);
								}
							}
						}else{
							logger.error("Failed to fetch variations for experiment : " + ref.experimentId);
						}
					}else{
						logger.error("Failed to fetch variations for experiment : " + ref.experimentId);
					}
				},'experimentId:eq:' + ref.experimentId + '___isDisabled:0');
			}
		}
	}
});
var Service = comb.define(comb.plugins.Middleware, {

    instance:{

        log:DEFAULT_LOG,


        constructor:function (options) {
            comb.merge(this, options || {});
            this._super(arguments);
        },

        start:function (opts) {
            this.log.info("Starting service");
            return comb.serial([
                hitch(this, "_hook", "pre", "startService"),
                hitch(this, "_startService", opts),
                hitch(this, "_hook", "post", "startService")
            ]);
        },


        status:function () {
            this.log.debug("Checking status");
            return {
                process:{
                    gid:process.getgid(),
                    pid:process.pid,
                    uid:process.getuid(),
                    cwd:process.cwd,
                    version:process.version,
                    title:process.title,
                    memoryUsage:process.memoryUsage(),
                    uptime:process.uptime(),
                },
                log:this.log.fullName
            };
        },

        errorHandler:function (err) {
            this.log.error(err);
        },

        stop:function (opts) {
            this.log.info("Stopping");
            return comb.serial([
                hitch(this, "_hook", "pre", "stopService"),
                hitch(this, "_stopService", opts),
                hitch(this, "_hook", "post", "stopService")
            ]).addErrback(this.errorHandler.bind(this));
        },

        restart:function () {
            comb.serial([
                hitch(this, "_hook", "pre", "restartService"),
                comb.hitch(this, "stop"),
                comb.hitch(this, "start"),
                hitch(this, "_hook", "post", "restartService")
            ]).addErrback(this.errorHandler.bind(this));
            ;
        },

        _startService:function (opts) {
            var ret = new comb.Promise();
            try {
                comb.when(this.startService(opts)).then(ret);
            } catch (e) {
                ret.errback(e);
            }
            return ret;
        },

        _stopService:function (opts) {
            var ret = new comb.Promise();
            try {
                comb.when(this.stopService(opts)).then(ret);
            } catch (e) {
                ret.errback(e);
            }
            return ret;
        },

        startService:function (opts) {
        },

        stopService:function (opts) {
        }
    },

    static:{

        SERVICES:[],

        init:function () {
            this._super(arguments);
            this.SERVICES.push(this);
        },

        start:function (options) {
            var ret = new this(options);
            ret.start();
            return ret;
        }
    }

}).as(exports, "Service");
示例#14
0
文件: index.js 项目: doug-martin/hare
var Hare = comb.define(_Options, {

    instance: {

        _url: null,

        url: function (url) {
            if (isHash(url)) {
                this._url = url;
            } else {
                this._url = {url: url};
            }
            return this;
        },

        "set": function set(opt, value) {
            var options = this._options;
            var opts = options.options || (options.options = {});
            opts[opt] = value;
            return this;
        },

        "get": function get(opt) {
            return this._options.options[opt];
        },

        connect: function () {
            return connect(this._url, this.options);
        },

        exchange: function (name) {
            return new Exchange(name, this.connect.bind(this));
        },

        queue: function queue(name, subscribeOptions) {
            var ret = new Queue(name, this.connect.bind(this)).ack(true);
            comb(subscribeOptions || {}).merge(SUBSCRIBE_OPTIONS).forEach(function configForEach(val, key) {
                ret[key](val);
            });
            return ret;
        },

        pubSub: function pubSub(name, subscribeOptions) {
            var ret = this.exchange(name).type("fanout").queue().exclusive(true);
            comb(subscribeOptions || {}).merge(SUBSCRIBE_OPTIONS).forEach(function configForEach(val, key) {
                ret[key](val);
            });
            return ret;
        },

        topic: function topic(name, routingKey, subscribeOptions) {
            var ret = this.exchange(name).type("topic").queue().exclusive(true).routingKey(routingKey);
            comb(subscribeOptions || {}).forEach(function configForEach(val, key) {
                ret[key](val);
            });
            return ret;
        },

        route: function route(name, routingKey, subscribeOptions) {
            var ret = this.exchange(name, this.connect.bind(this)).type("direct").queue().exclusive(true).routingKey(routingKey);
            comb(subscribeOptions || {}).merge(SUBSCRIBE_OPTIONS).forEach(function configForEach(val, key) {
                ret[key](val);
            });
            return ret;
        },

        workerQueue: function workerQueue(name, subscribeOptions) {
            var ret = this.queue(name).ack(true);
            comb(subscribeOptions || {}).merge(SUBSCRIBE_OPTIONS).forEach(function configForEach(val, key) {
                ret[key](val);
            });
            return ret;
        }
    },

    "static": {
        OPTIONS: ["defaultExchangeName", "reconnect", "reconnectBackoffStrategy",
            "reconnectExponentialLimit", "reconnectExponentialLimit", "reconnectBackoffTime"]
    }

});
示例#15
0
comb.define(Appender, {
    instance : {
        _amqpOptions : null,
        _queue : null,
        queueName : null,
        sendJson : true,
        inited : false,
        constructor : function (options) {
            options = options || {};
            var amqpOptions = this._amqpOptions = {}, sendJson = options.sendJson;
            this._queue = [];
            this.sendJson = comb.isBoolean(sendJson) ? sendJson : true;
            if (comb.isDefined(options.address)) {
                amqpOptions.url = options.address;
            } else {
                ["host", "port", "login", "password","vhost", "defaultExchangeName"].forEach(function (option) {
                    var opt = options[option];
                    if (opt) {
                        amqpOptions[option] = opt;
                    }
                });
            }
            !options.name && (options.name = "amqpAppender");
            var queueName = this.queueName = options.queueName;
            if(!queueName){
                throw new Error("queueName must be defined");
            }
            comb.listenForExit(this.__onExit.bind(this));
            this._super([options]);
        },

        __onExit : function () {
            if (this.connection) {
                this.connection.end();
            }
        },

        _setup : function () {
            if (!this.inited) {
                this.inited = true;
                var connection = amqp.createConnection(this._amqpOptions);
                connection.on('ready', function () {
                    this._queue.forEach(function (event) {
                        this._append(event, connection);
                    }.bind(this));
                    this._queue.length = 0;
                    this.connection = connection;
                }.bind(this));
            }
        },

        append:function (event) {
            if (this._canAppend(event)) {
                this._append(event);
            }
        },

        _append : function (event, connection) {
            connection = connection || this.connection;
            if (connection) {
                connection.publish(this.queueName, this.sendJson ? event : format(this.pattern, event));
            } else {
                this._queue.push(event);
                if (!this.inited) {
                    this._setup();
                }
            }
        }
    }
}).as(module);
示例#16
0
var Mysql = (module.exports = exports = comb.define(SQL, {
    instance : {

        sqlObject : null,

        _needLogic : false,

        table : null,

        db : null,

        constructor: function(table, db) {
            this.super(arguments);
            //override all super methods for operators
            for (var i in isOperator) {

                addIsFunction(i, this, this._isOp);
            }
            for (i in logicalOperator) {
                addFunction(i, this, this._logicalOp);
            }
            for (i in booleanOperator) {
                addFunction(i, this, this._booleanOp);
            }
            for (i in conditionedJoinTypes) {
                addJoinFunction(i, this, this._joinOp);
            }
            for (i in unConditionedJoinTypes) {
                addJoinFunction(i, this, this._joinOp);
            }
            for (i in inOperator) {
                addFunction(i, this, this._inOp);
            }
            for (i in betweenOperator) {
                addFunction(i, this, this._betweenOp);
            }
            for (i in aggregateOperator) {
                addGroupFunction(i, this, this._aggregateOp);
            }
            for (i in likeOperator) {
                addFunction(i, this, this._likeOp);
            }
        },

        _set : function(vals) {
            var sqlObject = this.sqlObject;
            if (sqlObject.update) {
                if (!sqlObject.set) {
                    sqlObject.set = "set ";
                }
                if (typeof vals == "object" && !(vals instanceof Array)) {
                    var values = [];
                    var set = [];
                    for (var i in vals) {
                        set.push(i + "=?");
                        values.push(vals[i]);
                    }
                    sqlObject.set += this.format(set.join(","), values);
                } else {
                    throw new Error("Vals must be an object");
                }
            }
            return this;
        },

        _betweenOp : function(oper, options) {
            if (!this.sql) this.find();
            if (options) {
                var sqlObject = this.sqlObject;
                if (sqlObject.having) {
                    this.having();
                } else if (!sqlObject.where) {
                    this.where();
                }
                for (var i in options) {
                    if (this._needLogic) {
                        this.and();
                    }
                    var key = i, val = options[i];
                    if (val instanceof Array && val.length == 2) {
                        var opts = {};
                        if (oper == "between") {
                            opts[key] = val[0];
                            this.gte(opts);
                            opts[key] = val[1];
                            this.lte(opts);
                        } else if (oper == "notBetween") {
                            opts[key] = val[0];
                            this.lte(opts);
                            opts[key] = val[1];
                            this.gte(opts);
                        } else {
                            throw new Error(oper + " is not supported");
                        }
                        this._needLogic = true;
                    } else {
                        throw new Error("when calling between value must be an array and have a of length 2");
                    }
                }
            }
            return this;
        },

        _inOp : function(oper, options) {
            if (!this.sql) this.find();
            if (options) {
                oper = inOperator[oper];
                var sqlObject = this.sqlObject;
                var sqlKey = "where";
                if (sqlObject.having) {
                    this.having();
                    sqlKey = "having";
                } else if (!sqlObject.where) {
                    this.where();
                }
                for (var i in options) {
                    if (this._needLogic) {
                        this.and();
                    }
                    var key = i, val = options[i];
                    if (val instanceof Mysql) {
                        sqlObject[sqlKey] += key + " " + oper + " (" + val.sql + ")";
                        this._needLogic = true;
                    } else if (val instanceof Array) {
                        var vals = "";
                        vals = val.map(function() {
                            return "?";
                        });
                        sqlObject[sqlKey] += key + " " + oper + " (" + this.format(vals.join(","), val) + ")";
                        this._needLogic = true;
                    } else if (typeof val == "string") {
                        sqlObject[sqlKey] += key + " in (" + val + ")";
                        this._needLogic = true;
                    } else {
                        throw new Error("when calling in value must be a string or array");
                    }
                }
            }
            return this;
        },

        _aggregateOp : function(oper, options) {
            var op = aggregateOperator[oper];
            var sqlObject = this.sqlObject;
            if (sqlObject.update || sqlObject["delete"]) throw new Error(oper + " cannot be used with update or delete");
            this._aggregated = true;
            if (options) {
                if (typeof options == "string") {
                    if (sqlObject.select) {
                        var lastChar = sqlObject.select.charAt(sqlObject.select.length - 1);
                        if (sqlObject != "select" && lastChar == "*" || lastChar == ' ') {
                            sqlObject.select += ", ";
                        }
                    } else {
                        sqlObject.select = "select ";
                    }
                    sqlObject.select += op + "(" + options + ") as " + options + "_" + op;
                } else if (options instanceof Array) {
                    options.forEach(this[op], this);
                } else {
                    throw new Error("when calling " + oper + " you must pass in a string or array or nothing");
                }
            } else {
                if (sqlObject.select) {
                    if (sqlObject.select.charAt(sqlObject.select.length - 1) == " ") {
                        sqlObject.select = sqlObject.select.substr(0, sqlObject.select.length - 1) + ", ";
                    }
                } else {
                    sqlObject.select = "select ";
                }
                sqlObject.select += op + "(*) as " + op;
            }
            if (!sqlObject.find) this._from();
            return this;
        },

        _logicalOp : function(oper, options) {
            oper = logicalOperator[oper];
            if (this._needLogic) {
                var sqlObject = this.sqlObject;
                var sqlKey = "where";
                if (sqlObject.having) {
                    //this.having();
                    sqlKey = "having";
                } else if (!sqlObject.where) {
                    this.where();
                }
                sqlObject[sqlKey] += " " + logicalOperator[oper] + " ";
                this._needLogic = false;
                if (options) {
                    var params = [];
                    var count = 0;
                    for (var i in options) {
                        if (count) throw new Error(oper + "operation can only be one deep");
                        this._createSQLFromObject(i, options[i]);
                        count++;
                    }
                }
            } else {
                throw new Error("logical operator not needed");
            }
            return this;
        },

        _joinOp : function(oper, table, options) {
            if (!this.sql) this.find();
            var sqlObject = this.sqlObject;
            var fromClause = "from";

            if (!sqlObject.update && !sqlObject.from) {
                this._from();
            } else if (sqlObject.update) {
                fromClause = "update";
            }
            if (typeof table == "string") {
                if (sqlObject["delete"]) {
                    if (sqlObject["delete"] == "delete ") {
                        sqlObject["delete"] += this.table;
                    }
                }
                var joinType;
                if (oper in conditionedJoinTypes) {
                    if (options) {
                        joinType = conditionedJoinTypes[oper];
                        sqlObject[fromClause] += " " + joinType + " " + table;
                        if (options instanceof Array) {
                            sqlObject[fromClause] += " using (" + options.join(", ") + ")";
                        } else if (typeof options == "object") {
                            sqlObject[fromClause] += " on ";
                            for (var i in options) {
                                sqlObject[fromClause] += this.table + "." + i + "=" + table + "." + options[i];
                            }
                        } else {
                            throw new Error("join options must be an array or object");
                        }
                    } else {
                        throw new Error(oper + " requires a join condition");
                    }
                } else if (oper in unConditionedJoinTypes) {
                    joinType = unConditionedJoinTypes[oper];
                    sqlObject[fromClause] += " " + joinType + " " + table;
                }
            } else if (table instanceof Mysql) {
                this._joinOp(oper, table.sql, options);
            }
            return this;
        },

        _booleanOp : function(oper, options) {
            if (!this.sql) this.find();
            var sqlObject = this.sqlObject;
            var sqlKey = "where";
            if (sqlObject.having) {
                //this.having();
                sqlKey = "having";
            } else if (!sqlObject.where) {
                this.where();
            }
            oper = booleanOperator[oper];
            var params = [];
            for (var i in options) {
                if (this._needLogic) {
                    this.and();
                }
                sqlObject[sqlKey] += this.format(i + " " + oper + " ?", [options[i]]);
                this._needLogic = true;
            }
            return this;
        },

        _isOp : function(oper, options) {
            if (!this.sql) this.find();
            var sqlObject = this.sqlObject;
            var sqlKey = "where";
            if (sqlObject.having) {
                this.having();
                sqlKey = "having";
            } else if (!sqlObject.where) {
                this.where();
            }
            oper = isOperator[oper];
            var sql = "";
            if (typeof options == "object") {
                for (var i in options) {
                    if (this._needLogic) {
                        this.and();
                    }
                    sqlObject[sqlKey] += i + " " + oper + " " + transformBooleanOperator(options[i]);
                    this._needLogic = true;
                }
            } else {
                if (this._needLogic) {
                    this.and();
                }
                sqlObject[sqlKey] += options + " " + oper;
                this._needLogic = true;
            }
            return this;
        },

        _likeOp : function(oper, options) {
            if (!this.sql) this.find();
            var sqlObject = this.sqlObject;
            var sqlKey = "where";
            if (sqlObject.having) {
                this.having();
                sqlKey = "having";
            } else if (!sqlObject.where) {
                this.where();
            }
            oper = likeOperator[oper];
            var sql = "";
            if (typeof options == "object") {
                for (var i in options) {
                    if (this._needLogic) {
                        this.and();
                    }
                    sqlObject[sqlKey] += this.format(i + " " + oper + " ?", [options[i]]);
                    this._needLogic = true;
                }
            } else {
                throw new Error("when calling like options must be a hash of {columnName : <like condition>}");
            }
            return this;
        },

        where : function(options) {
            if (!this.sql) this.find();
            var sqlObject = this.sqlObject;
            if (!sqlObject.update && !sqlObject.from) {
                this._from();
            }
            if (!sqlObject.where) {
                sqlObject.where = "where ";
            }
            this._parseObjectAndCreateSQL(options);
            return this;
        },

        select : function(values, options) {
            var sqlObject = this.sqlObject;
            if (!sqlObject.update && !sqlObject["delete"]) {
                if (!sqlObject.select || sqlObject.select.indexOf("*") != -1) {
                    sqlObject.select = "select ";
                }
                if (values) {
                    if (typeof values == "string") {
                        sqlObject.select += values;
                    } else if (values instanceof Array) {
                        for (var i in values) {
                            var val = values[i];
                            if (typeof val == "string") {
                                if (i > 0) sqlObject.select += ", ";
                                sqlObject.select += val;
                            } else {
                                throw new Error("select params must be a string");
                            }
                        }
                    }
                }
                if (options) {
                    this.where(options);
                }
            } else {
                throw new Error("Cannot call select after update or delete");
            }
            return this;
        },

        distinct : function() {
            var sqlObject = this.sqlObject;
            if (!sqlObject.update && !sqlObject["delete"]) {
                if (!sqlObject.select) {
                    sqlObject.select = "select distinct *";
                } else {
                    sqlObject.select = sqlObject.select.replace("select", "select distinct");
                }
            } else {
                throw new Error("Cannot call select after update or delete");
            }
            return this;
        },

        update : function(values, options) {
            var sqlObject = this.sqlObject;
            if (!sqlObject.select && !sqlObject["delete"]) {
                if (values) {
                    if (!sqlObject.update) {
                        sqlObject.update = "update " + this.table;
                    }
                    this._set(values);
                    if (options) {
                        this.where(options);
                    }
                } else {
                    throw new Error("To call update you must provide values to update");
                }
            } else {
                throw new Error("Cannot call udpate after select or delete!");
            }
            return this;
        },

        remove : function(values, options) {
            var sqlObject = this.sqlObject;
            if (!sqlObject.update && !sqlObject["delete"]) {
                if (!sqlObject["delete"]) {
                    sqlObject["delete"] = "delete ";
                }
                if (values) {
                    if (sqlObject["delete"] == "delete ") {
                        sqlObject["delete"] += this.table;
                    }
                    if (typeof values == "string") {
                        sqlObject["delete"] += ", " + values;
                    } else if (values instanceof Array) {
                        sqlObject["delete"] += ", " + values.join(", ");
                    }
                } else if (!sqlObject.from) {
                    this._from();
                }
                if (options) {
                    this.where(options);
                }
            } else {
                throw new Error("Cannot call delete after update or delete");
            }
            return this;
        },

        /*
         options = {name : "fred"},
         select * from <table> where name = 'fred'
         options = {x : {gt : 2}}
         select * from <table> where x > 2
         options = {x : {lt : 2}}
         select * from <table> where x < 2
         options = {x : {gte : 2}}
         select * from <table> where x >= 2
         options = {x : {lte : 2}}
         select * from <table> where x <= 2
         options = {x : {in : [1, 2, 3]}}
         select * from <table> where x in (1,2,3);
         options = {x : {ne : 2}}
         select * from <table> where x != 2;
         options = {flag : {is : (TRUE|FALSE|UNKNOWN)}}
         select * from <table> where flag is (TRUE|FALSE|UNKNOWN);
         options = {flag : {isNot : (TRUE|FALSE|UNKNOWN)}}
         select * from <table> where flag IS NOT (TRUE|FALSE|UNKNOWN);
         options = {x : {isNull : (TRUE|FALSE|UNKNOWN)}}
         select * from <table> where flag IS NULL;
         options = {x : {isNotNull : (TRUE|FALSE|UNKNOWN)}}
         select * from <table> where flag IS NOT NULL;
         options = {x : {between : [1,5]}}
         select * from <table> where x BETWEEN 1 AND 5;
         options = {x : {notBetween : [1,5]}}
         select * from <table> where x NOT BETWEEN 1 AND 5;
         options = {name : {like : "Fred"}}
         select * from <table> where x NOT BETWEEN 1 AND 5;
         */
        find : function(options) {
            //reset sql
            var sqlObject = this.sqlObject;
            if (!sqlObject.select && !sqlObject.update && !sqlObject["delete"]) {
                sqlObject.select = "select *";
            }
            if (!sqlObject.update && !sqlObject.from) this._from();
            if (options) {
                if (sqlObject.having) {
                    this.having(options);
                } else {
                    this.where(options);
                }
            }
            return this;
        },

        /*
         *   add order tp query
         *   mysql.order(x)
         *   select * from <table> order by x
         *   mysql.order([x,y])
         *   select * from <table> order by x,y
         *   mysql.order({x : "desc"})
         *   select * from <table> order by x desc
         *   mysql.order([{x : "desc"}, y])
         *   select * from <table> order by x desc, y
         *   mysql.order([{x : "desc"}, {y : desc}])
         *   select * from <table> order by x desc, y desc

         */
        order : function(options) {
            if (!this.sql) this.find();
            var sqlObject = this.sqlObject;
            if (!sqlObject.from) {
                this._from();
            }
            if (options) {
                if (!sqlObject.order) {
                    sqlObject.order = "order by ";
                    this._orderedCount = 0;
                } else if (this._orderedCount) {
                    sqlObject.order += ", ";
                }
                if (typeof options == "string") {
                    sqlObject.order += options;
                } else if (options instanceof Array) {
                    options.forEach(this.order, this);
                } else if (typeof options == "object") {
                    var count = 0;
                    for (var i in options) {
                        if (count) throw new Error("when providing an object to order only one key is allowed");
                        var type = options[i];
                        if (type == 'desc' || type == "asc") {
                            sqlObject.order += i + " " + type;
                        } else {
                            throw new Error("Only 'asc' or 'desc' is allowed as a value for an ordered object");
                        }
                    }
                }
                this._orderedCount++;
            }
            return this;
        },

        orderBy : function(options) {
            return this.order(options);
        },


        limit : function(limit, offset) {
            var sqlObject = this.sqlObject;
            if (!sqlObject.update && !sqlObject.from) {
                this.find();
            }
            if (limit) {
                if (typeof limit == "number") {
                    sqlObject.limit = "limit " + limit;
                } else {
                    throw new Error("when using limit the param must be a number");
                }
            }
            offset && this.offset(offset);
            return this;
        },

        offset : function(offset) {
            if (!this.sql) this.find();
            var sqlObject = this.sqlObject;
            if (!sqlObject.update && !sqlObject["delete"]) {
                if (!sqlObject.from) {
                    this._from();
                }
                if (offset) {
                    if (typeof offset == "number") {
                        sqlObject.offset = "offset " + offset;
                    } else {
                        throw new Error("when using offset the param must be a number");
                    }
                }
            } else {
                throw new Error("Cannot call offset on update or delete");
            }
            return this;
        },

        /*
         *   mysql.join(<tablename>, options);
         *   select * from inner join <thisTable
         * */
        join : function(table, options) {
            return this._joinOp("innerJoin", table, options);

        },

        /*
         * Creats a group clause for sql
         * mysql.group(<columnName>);
         * select * from <tableName> group by <columnName>
         * mysql.group([col1,col2...]);
         * select * from <tableName> group by col1, col2...
         * mysql.group(col, havingOptions);
         * select * from <tableName> group by col having <having options>
         * mysql.group([col1, col2...], {col1 : a});
         * select * from <tableName> group by col1, col2.... having col2 = 'a'
         * */
        group : function(key, options) {
            if (!this.sql) this.find();
            var sqlObject = this.sqlObject;
            if (!sqlObject.update && !sqlObject["delete"]) {
                if (!sqlObject.from) {
                    this._from();
                }
                if (key) {
                    if (typeof key == "string") {
                        if (!sqlObject.group) {
                            sqlObject.group = "group by";
                        }
                        sqlObject.group += " " + key;
                    } else if (key instanceof Array) {
                        if (!sqlObject.group) {
                            sqlObject.group = "group by";
                        }
                        sqlObject.group += " " + key.join(", ");
                    } else {
                        throw new Error("key must be a string or array");
                    }
                    if (sqlObject.group && options) {
                        this.having(options);
                    }
                } else {
                    throw new Error("when calling group a grouping column is required");
                }
            } else {
                throw new Error("Cannot group on an update or delete");
            }
            return this;
        },

        /*
         * Creates a having clause, group must have been previously called
         * mysql.having({x : 1});
         * select * from <tableName> group by <*> having x = 1
         * you may also use find if a group clause has been defined
         * */
        having : function(options) {
            var sqlObject = this.sqlObject;
            if (sqlObject.group) {
                if (!sqlObject.having) {
                    sqlObject.having = "having ";
                    this._needLogic = false;
                }
                if (options) {
                    this._parseObjectAndCreateSQL(options);
                }
            } else {
                throw new Error("a group clause must be previously defined");
            }
            return this;
        },

        logicGroup : function(options) {
            if (!this.sql) this.find();
            var sqlObject = this.sqlObject;
            var sqlKey = "where";
            if (sqlObject.having) {
                this.having();
                sqlKey = "having";
            } else if (!sqlObject.where) {
                this.where();
            }
            if (options) {
                sqlObject[sqlKey] += "(";
                this.where(options);
                sqlObject[sqlKey] += ")";
            }
            return this;
        },

        /*call this to finish off select clause and not execute
         *else just call execute();
         *i.e you just call mysql.find() or mysql.select(params);
         *mysql.find.end(); mysql.find.select().end();
         * */
        end : function() {
            var sqlObject = this.sqlObject;
            if (!sqlObject.select && !sqlObject.update && !sqlObject["delete"]) {
                this.find();
            }
            if (sqlObject.select) {
                if (!sqlObject.from) this._from();
            }
            return this;
        }
    },

    static : {
        createTable : function(table, db) {
            var promise = new Promise();
            db.query(table.createTableSql).then(hitch(promise, "callback", true), hitch(promise, "errback"));
            return promise;
        },

        alterTable : function(table, db) {
            var promise = new Promise();
            db.query(table.alterTableSql).then(hitch(promise, "callback", true), hitch(promise, "errback"));
            return promise;
        },

        dropTable : function(table, db) {
            var promise = new Promise();
            db.query(table.dropTableSql).then(hitch(promise, "callback", true), hitch(promise, "errback"));
            return promise;
        },

        save : function(table, object, db) {
            if (table && object && db) {
                var promise = new Promise();
                var sql = "";
                if (object instanceof Array) {
                    sql = object.map(
                            function(o) {
                                return createInsertStatement(table, o, db);
                            }).join("");
                } else {
                    sql = createInsertStatement(table, object, db);
                }
                db.query(sql).then(hitch(promise, "callback"), hitch(promise, "errback"));
                return promise;
            } else {
                throw new Error("Table, object, and db required when calling mysql.save");
            }
        },

        schema : function(tableName, db) {
            if (typeof tableName != "string") throw new Error("tablename must be a string");
            if (tableName && db) {
                var promise = new Promise();
	            var database = db.database;
                db.query("DESCRIBE " + escape(tableName)).then(hitch(this, function(database,results) {
                    var obj = {};
                    if (results.length) {
                        var schema = {database : database};
                        var pks = [];
                        results.forEach(function(o) {
                            var t = mysqlTypes.fromColDef(o);
                            if (t.isPrimaryKey()) {
                                pks.push(o.Field);
                            }
                            schema[o.Field] = t;
                        });
                        pks.length && (schema.primaryKey = pks);
                        promise.callback(new Table(tableName, schema));
                    } else {
                        promise.callback(null);
                    }
                }, database), hitch(promise, "errback"));
                return promise;
            } else {
                throw new Error("Table name and db conneciton required to retrieve schema");
            }
        },

        getLastInsertId : function(db) {
            var promise = new Promise();
            var sql = "SELECT LAST_INSERT_ID() as id";
            db.query(sql).then(hitch(promise, "callback"), hitch(promise, "errback"));
            return promise;
        },

        foreignKey : mysqlTypes.foreignKey,
        addForeignKey : mysqlTypes.addForeignKey,
        dropForeignKey : mysqlTypes.dropForeignKey,
        primaryKey : mysqlTypes.primaryKey,
        addPrimaryKey : mysqlTypes.addPrimaryKey,
        dropPrimaryKey : mysqlTypes.dropPrimaryKey,
        unique : mysqlTypes.unique,
        addUnique : mysqlTypes.addUnique,
        dropUnique : mysqlTypes.dropUnique,
        dropColumn : mysqlTypes.dropColumn,
        alterColumn : mysqlTypes.alterColumn,
        column : mysqlTypes.column,
        addColumn : mysqlTypes.addColumn,
        isValidType : mysqlTypes.isValidType,
        client : MySQLClient.Client,
        ConnectionPool : MySQLClient.ConnectionPool,
        types : mysqlTypes.types

    }
}));
示例#17
0
文件: csv.js 项目: doug-martin/ssrs
comb.define([Report, Iterable], {

    instance:{

        constructor:function () {
            this._super(arguments);
            var opts = this._opts;
            opts.rs.format = "CSV";

            !opts.csvOpts && (opts.csvOpts = {columns:true, delimeter:this._static.DEFAULT_FIELD_DELIMETER});
        },

        fieldDelimeter:function (fieldDelimiter) {
            this.rc("fieldDelimiter", fieldDelimiter);
            this._opts.csvOpts.delimeter = fieldDelimiter;
        },

        encoding : function(encoding){
            this.rc("encoding", encoding);
        },

        noHeader:function (noHeader) {
            this.rc("noHeader", noHeader);
            this._opts.csvOpts.columns = noHeader;
        },

        qualifier:function (qualifier) {
            this.rc("qualifier", qualifier);
            this._opts.csvOpts.escape = qualifier;
        },

        rowCb:function (cb) {
            return this._merge({rowCb:cb});
        },

        opts:function (opts) {
            return this._merge(merge({delimeter:this._static.DEFAULT_FIELD_DELIMETER}, opts));
        },

        raw:function () {
            return this._merge({raw:true});
        },

        _handleResponse:function (response, body) {
            var opts = this._opts, ret = new comb.Promise(), retObj = [];
            if (!opts.raw) {
                var csvParse = csv().from(body, opts.csvOpts || {});
                var transform = opts.rowCb || function (data) {
                    return data;
                };
                csvParse.transform(transform)
                    .on('data', function (data, index) {
                        retObj.push(data);
                    })
                    .on('end', function (count) {
                        ret.callback(retObj);
                    })
                    .on('error', function (error) {
                        ret.errback(error);
                    });
                return ret;
            } else {
                return body;
            }
        }

    },

    static:{
        DEFAULT_FIELD_DELIMETER:",",
        RS_PARAMS:"command parameterLanguage snapshot persistStreams getNextStream".split(" "),
        RC_PARAMS:("excelMode recordDelimiter suppressLineBreaks useFormattedValues").split(" ")
    }

}).as(module);
var comb = require('comb');
var request = require('request');
var client = require('./client.js');
var logger = require(LIB_DIR + 'log_factory').create("goal_visits_client");

var GoalVisitsClient = comb.define(client,{
	instance : {
		constructor : function(options){
			options = options || {};
			options.url = "goal_visits";
            this._super([options]);
		}		
	}
});

module.exports = new GoalVisitsClient();
示例#19
0
文件: oneToOne.js 项目: c0d3x42/moose
module.exports = exports = comb.define(ManyToOne, {
    instance : {

        //override
        //@see _Association
        _fetchMethod : "one",

        //override
        //@see _Association
        _setter : function(val, self) {
            var loadedKey = self.loadedKey, name = self.name;
            if (!(val instanceof self.model)) {
                val = new self.model(val);
            }
            if (this.isNew) {
                this["_" + name] = val;
                this[loadedKey] = true;
            } else {
                //set my foreign key
                val[self.rightKey] = this[self.leftKey];
                this["_" + name] = val;
                this[loadedKey] = true;
            }
        },

        //override
        //@see _Association
        _postSave : function(next, self) {
            if (self.filter && self.isEager()) {
                this[self.name].then(hitch(this, next));
            } else {
                var loadedKey = self.loadedKey, name = self.name;
                if (this[loadedKey] && this["_" + name]) {
                    var val = this["_" + name];
                    val[self.rightKey] = this[self.leftKey];
                    val.save().then(hitch(this, next));
                } else {
                    next();
                }
            }
        },

        //override
        //@see _Association
        _preRemove : function(next, self) {
            if (self.filter) next();
            var loadedKey = self.loadedKey, name = self.name;
            if (!this[loadedKey]) {
                this[name].then(hitch(this, function(value) {
                    if (value) {
                        value.remove();
                    }
                    next();
                }));
            } else {
                var value = this[name];
                value.remove();
                next();
            }

        }
    }
});
示例#20
0
    Database = patio.Database;

patio.quoteIdentifiers = false

var MockDataset = comb.define(Dataset, {
    instance : {
        insert : function() {
            return this.db.execute(this.insertSql.apply(this, arguments));
        },

        update : function() {
            return this.db.execute(this.updateSql.apply(this, arguments));
        },

        fetchRows : function(sql) {
            var ret = new comb.Promise();
            this.db.execute(sql);
            ret.callback({id : 1, x : 1});
            return ret;
        },

        _quotedIdentifier : function(c) {
            return '"' + c + '"';
        }

    }
}).as(exports, "MockDataset");

var MockDB = comb.define(Database, {

    instance : {
示例#21
0
var GoalsImpl = comb.define(impl,{
	instance : {
		displayName : "Goal",
		constructor : function(options){
			options = options || {};
			options.dao = goalsDao;
            this._super([options]);
		},
		create : function(params, callback){
			var bus = new Bus();
			
			var ref = this;
			var m = this._getSuper();
			
			// User ID should not be valid
			var userId = params['userId'];
			if(check.isNull(userId) || !check.isInt(userId)){
				callback(response.error(codes.error.VALID_USER_REQUIRED()));
				return;
			}
			
			// Name should not be blank
			var name = params['name'];
			if(check.isNull(name)){
				callback(response.error(codes.error.GOAL_NAME_REQUIRED()));
				return;
			}
			
			// Type should not be blank
			if(check.isNull(params['type'])){
				callback(response.error(codes.error.GOAL_TYPE_REQUIRED()));
				return;
			}
			
			// URL should not be blank
			if(check.isNull(params['url'])){
				callback(response.error(codes.error.GOAL_URL_REQUIRED()));
				return;
			}
			
			// URL should not be blank
			if(!check.isURL(params['url'])){
				callback(response.error(codes.error.INVALID_GOAL_URL()));
				return;
			}
			
			bus.on('start', function(){
				StateMachine.getStartState(GOAL.name, function(err, data){
					if(err != null){
						callback(err, null);
						return;
					}else{
						startState = data.data[0].name;
						params['status'] = startState; // Start State
						
						bus.fire('stateSet');
					}
				});
			});
			
			bus.on('stateSet', function(){
				ref.search(function(err,data){
					// If error occurred
					if(err){
						callback(err);
						return;
					}
					
					if(data && data.totalCount > 0){ // Records with same User Id and Name can not exist 
						callback(response.error(codes.error.GOAL_USER_ID_NAME_EXISTS()));
					}else{
						bus.fire('noDuplicates');
					}
				}, 'userId:eq:' + params.userId + '___name:eq:' + params.name);
			});
			
			bus.on('noDuplicates', function(){
				m.call(ref, params, callback);
				
				// Mark script old for the user
				emitter.emit(EVENT_MARK_SCRIPT_OLD, userId);
			});
			
			bus.fire('start');
		},
		
		update : function(id, params, callback){
			if(id == null){
				callback(response.error(codes.error.ID_NULL));
			}else{
				
				var bus = new Bus();
				
				var ref = this;
				var m = this._getSuper();
				
				var userId = null;
				
				bus.on('start', function(){
					ref._dao.getById(id).then(function(model){
						if(model == undefined){
							callback(response.error(codes.error.RECORD_WITH_ID_NOT_EXISTS([ref.displayName, id])));
						}else{
							userId = model.userId;
							bus.fire('modelFound', model);
						}
					}, function(error){
						callback(response.error(codes.error.RECORD_WITH_ID_NOT_FETCHED([ref.displayName, id])));
					});
				});
				
				bus.on('modelFound', function(model){
					if(params.userId && params.userId != model.userId){
						// Can't change the user id of an experiment
						callback(response.error(codes.error.GOAL_USER_ID_CANT_UPDATE()));
						return;
					}
					
					if(params.url && params.url != model.url){ // URL is getting changes
						// URL should not be blank
						if(check.isNull(params['url'])){
							callback(response.error(codes.error.GOAL_URL_REQUIRED()));
							return;
						}
						
						// URL should not be blank
						if(!check.isURL(params['url'])){
							callback(response.error(codes.error.INVALID_GOAL_URL()));
							return;
						}
					}
					
					if(params.name && params.name != model.name){ //Name is getting updated
						var name = params.name || model.name;
						ref.search(function(err,data){
							// If error occurred
							if(err){
								callback(err);
								return;
							}
							
							if(data && data.totalCount > 0){ // Records with same User Id and Name can not exist 
								callback(response.error(codes.error.GOAL_USER_ID_NAME_EXISTS()));
							}else{
								bus.fire('noDuplicates', model);
							}
						}, 'userId:eq:' + model.userId + '___name:eq:' + name);
					}else{
						bus.fire('noDuplicates', model);
					}
				});
				
				bus.on('noDuplicates', function(model){
					if(params.status && params.status != model.status){
						StateMachine.isValidTransition(GOAL.name, model.status, params.status, function(err, data){
							if(err != null){
								callback(err, null);
								return;
							}else{
								bus.fire('validOrNoTransitions');
							}
						});
					}else{
						bus.fire('validOrNoTransitions');
					}
				});
				
				bus.on('validOrNoTransitions', function(){
					m.call(ref, id, params, callback);
					
					// Mark script old for the user
					emitter.emit(EVENT_MARK_SCRIPT_OLD, userId);
				});
				
				bus.fire('start');
			}
			
		}
	}
});
示例#22
0
var FactHash = comb.define(null, {

    instance:{
        constructor:function (def) {
            this.memory = [];
            this.memoryValues = [];
        },

        get:function (k) {
            return this.memoryValues[this.memory.indexOf(k)];
        },

        remove:function (v) {
            var facts = v.match.facts, j = facts.length, mv = this.memoryValues, m = this.memory;
            while (j--) {
                var i = m.indexOf(facts[j].object);
                var arr = mv[i], index = arr.indexOf(v);
                arr.splice(index, 1);
            }
        },

        insert:function (insert) {
            var facts = insert.match.facts, mv = this.memoryValues, m = this.memory;
            var k = facts.length;
            while (k--) {
                var o = facts[k].object, i = m.indexOf(o), arr = mv[i];
                if (!arr) {
                    arr = mv[m.push(o) - 1] = [];
                }
                arr.push(insert);
            }
        }
    }

});
var response = require(LIB_DIR + 'response');

var ExperimentVisitsImpl = comb.define(impl, {
	instance : {
		displayName : "Experiment Visit",
		constructor : function(options){
			options = options || {};
			options.dao = dao;
            this._super([options]);
		},
	},
	
	create : function(params, callback){
		var m = this._getSuper();
		
		// Experiment Id should not be blank
		if(check.isNull(params['experimentId']) || !check.isInt(params['experimentId'])){
			callback(response.error(codes.error.EXPERIMENT_ID_NULL()));
			return;	
		}
		
		// Visits should not be blank
		if(check.isNull(params['visits']) || !check.isInt(params['visits'])){
			callback(response.error(codes.error.VISITS_NULL()));
			return;
		}
		
		m.call(this, params, callback);
	}
});
module.exports = new ExperimentVisitsImpl();
示例#24
0
文件: index.js 项目: c0d3x42/moose
exports.Type = comb.define(null, {
    instance : {
        /**@lends Type.prototype*/

        constructor : function(options) {
            this.__options = options;
        },

        /**
         * Set a property on this Type, such as isNull, unique, default etc...
         *
         * @param {String} name the name of the property.
         * @param {*} value the value to set it to.
         */
        set : function(name, value) {
            this.__options[name] = value;
        },


        /**
         * Is this Type a primary key.
         *
         * @return {Boolean} true if this Type is a primary key.
         */
        isPrimaryKey : function() {
            if (this.__options.primaryKey) {
                return true;
            } else {
                false;
            }
        },

        /**
         * Convert an SQL value to the javascript equivalent. This method will do a type check after the conversionn.
         *
         * @param {String} val the sql string to convert.
         *
         * @return {*} the javascript value.
         */
        fromSql : function(val) {
            var ret = (val == "null" || val == "NULL") ? null : val;
            if (ret != null) {
                ret = this.__options.setSql(ret);
            }
            return ret;
        },

        /**
         * Converts a javacript value to the corresponding sql equivalent.
         * This function does a type check on the value before conversion, if it is not the right type an error is thrown.
         * @param {*} val the javacript value to convert
         *
         * @return {String} the sql value.
         */
        toSql : function(val) {
            this.check(val);
            if (val instanceof Date) {
                val = toSqlDate(val, this.__options.type);
            } else if (val instanceof Array) {
                val = val.join(",");
            } else if (val == null || val == undefined) {
                val = null;
            }
            return val;
        },

        /**
         * Checks a value against this Type column definition.
         *
         * @param {*} value the value to check.
         *
         * @return {Boolean} true if the value is valid.
         */
        check : function(value) {
            if ((value == null && !this.__options.allowNull) && !(this.primaryKey && this.__options.autoIncrement)) {
                throw new Error("value is not allowed to be null");
            } else if (value != null) {
                this.__options.checkType(value);
            }
            return true;
        },

        getters : {
            sql : function() {
                return genStringColumnDef(this.__options);
            }
        }

    }
});
示例#25
0
var ExperimentsImpl = comb.define(impl,{
	instance : {
		displayName : "Email",
		constructor : function(options){
			options = options || {};
			options.dao = emailsDao;
            this._super([options]);
		},
		create : function(params, callback){
			var bus = new Bus();
			
			var ref = this;
			var m = this._getSuper();
			
			bus.on('start', function(){
				StateMachine.getStartState(EMAIL.name, function(err, data){
					if(err != null){
						callback(err, null);
						return;
					}else{
						startState = data.data[0].name;
						params['status'] = startState; // Start State
						
						bus.fire('stateSet');
					}
				});
			});
			
			bus.on('stateSet', function(){
				m.call(ref, params, callback);
			});
			
			bus.fire('start');
		},
		
		update : function(id, params, callback){
			if(id == null){
				callback(response.error(codes.error.ID_NULL));
			}else{
				
				var bus = new Bus();
				
				var ref = this;
				var m = this._getSuper();
				
				// Only Status of an email can be updated
				var status = params.status;
				
				bus.on('start', function(){
					ref._dao.getById(id).then(function(model){
						if(model == undefined){
							callback(response.error(codes.error.RECORD_WITH_ID_NOT_EXISTS([ref.displayName, id])));
						}else{
							bus.fire('modelFound', model);
						}
					}, function(error){
						callback(response.error(codes.error.RECORD_WITH_ID_NOT_FETCHED([ref.displayName, id])));
					});
				});
				
				bus.on('modelFound', function(model){
					if(status && status != model.status){ //Status is getting changed
						StateMachine.isValidTransition(EMAIL.name, model.status, status, function(err, data){
							if(err != null){
								callback(err, null);
								return;
							}else{
								bus.fire('validOrNoTransitions');
							}
						});
					}else{
						bus.fire('validOrNoTransitions');
					}
				});
				
				bus.on('validOrNoTransitions', function(){
					m.call(ref, id, {status : status}, callback);
				});
				
				bus.fire('start');
			}
		},
		
		markSent : function(id, callback){
			this.update(id, {status : EMAIL.SENT}, callback);
		},
		
		markProcessing : function(id, callback){
			this.update(id, {status : EMAIL.PROCESSING}, callback);
		},
		
		markFailed : function(id, callback){
			this.update(id, {status : EMAIL.FAILED}, callback);
		},
		
		markQueued : function(id, callback){
			this.update(id, {status : EMAIL.QUEUED}, callback);
		},
		
		lockUpdate : function(modelsJSON, batchSize, count, callback){
			var ref = this;
			this._dao.lockUpdate({status : EMAIL.QUEUED}, {status : EMAIL.PROCESSING})
				.then(function(model){
					if(model){
						modelsJSON.push(model.toJSON());
						if(count == batchSize){
							callback(null, modelsJSON);
						}else
							ref.lockUpdate(modelsJSON, batchSize, count + 1, callback);
					}else{
						callback(null, modelsJSON);
					}
				}, function(error){
					logger.error(error);
					callback(null, modelsJSON);
	//				callback(response.error(codes.error.EMAIL_BATCH_UPDATE_FAILED()));
				});
		},
		
		updateBatchToProcessing : function(batchSize, callback){
			var modelsJSON = [];
			
			this.lockUpdate(modelsJSON, batchSize, 1, function(err, data){
				if(err){
					logger.error(err);
				}else{
					callback(null, response.success(modelsJSON, modelsJSON.length, codes.success.EMAIL_BATCH_UPDATED()));
				}
			});
		},
		
		generateEmailBody : function(template, params, callback){
			var tplPath = EMAILS_DIR + template;
			
			jade.renderFile(tplPath, params, function(err, html){
				if(err != undefined){
					logger.error(err);
					callback(response.error(codes.error.EMAIL_BODY_NOT_BUILT()));
				}else{
					callback(null, html);
				}
			});
		},
		
		sendFromTemplate : function(template, templateOpts, emailOpts, callback){
			var ref = this;
			ref.generateEmailBody(template, templateOpts, function(err, html){
				if(err != undefined){
					logger.error("Failed to compile " + template + " email template");
					callback(err);
				}else{
					emailOpts.body = html;
					ref.create(emailOpts, callback);
				}
			});
		}
	}
});
示例#26
0
(function () {
    "use strict";

    var comb = require("comb"), constraintMatcher;

    var Constraint = comb.define(null, {

        instance:{
            constructor:function (type, constraint) {
                if (!constraintMatcher) {
                    constraintMatcher = require("./constraintMatcher");
                }
                this.type = type;
                this.constraint = constraint;
            },
            assert:function () {
                throw new Error("not implemented");
            },

            equal:function (constraint) {
                return comb.isInstanceOf(constraint, this._static) && this.alias === constraint.alias && constraintMatcher.equal(this.constraint, constraint.constraint);
            },

            getters:{
                variables:function () {
                    return [this.alias];
                }
            }


        }
    });

    comb.define(Constraint, {
        instance:{
            constructor:function (type) {
                this._super(["object", type]);
            },

            assert:function (param) {
                return param instanceof this.constraint || param.constructor === this.constraint;
            },

            equal:function (constraint) {
                return comb.isInstanceOf(constraint, this._static) && this.constraint === constraint.constraint;
            }
        }
    }).as(exports, "ObjectConstraint");

    comb.define(Constraint, {

        instance:{
            constructor:function (constraint) {
                this._super(["equality", constraint]);
                this._matcher = constraintMatcher.getMatcher(constraint);
            },

            assert:function (values) {
                return this._matcher(values);
            }
        }
    }).as(exports, "EqualityConstraint");

    comb.define(Constraint, {

        instance:{
            constructor:function (constraint) {
                this._super(["equality", [true]]);
            },

            equal:function (constraint) {
                return comb.isInstanceOf(constraint, this._static) && this.alias === constraint.alias;
            },


            assert:function (assertable) {
                return true;
            }
        }
    }).as(exports, "TrueConstraint");

    comb.define(Constraint, {

        instance:{
            constructor:function (constraint) {
                this._super(["reference", constraint]);
                this._js  = constraintMatcher.toJs(constraint);
                this._matcher = constraintMatcher.getMatcher(constraint);
            },

            assert:function (values) {
                return this._matcher(values);
            },

            getters:{
                variables:function () {
                    return this.vars;
                },

                alias:function () {
                    return this._alias;
                }
            },

            setters:{
                alias:function (alias) {
                    this._alias = alias;
                    this.vars = constraintMatcher.getIdentifiers(this.constraint).filter(function (v) {
                        return v !== alias;
                    });
                }
            }
        }

    }).as(exports, "ReferenceConstraint");


    comb.define(Constraint, {
        instance:{
            constructor:function (hash) {
                this._super(["hash", hash]);
            },

            equal:function (constraint) {
                return comb.isInstanceOf(constraint, this._static) && this.alias === constraint.alias && comb.deepEqual(this.constraint, constraint.constraint);
            },

            assert:function (factHash) {
                var fact = factHash[this.alias], constraint = this.constraint;
                Object.keys(constraint).forEach(function (k) {
                    var v = constraint[k];
                    factHash[v] = fact[k];
                });
                return true;
            },

            getters:{
                variables:function () {
                    return this.constraint;
                }
            }

        }
    }).as(exports, "HashConstraint");

})();
示例#27
0
var comb = require("comb");


comb.define(null, {
    instance:{},
    static:{

        configure:function (model) {

        }
    }
}).as(exports, "SingleTableInheritance");


/**
 * @class This plugin enables
 * <a href="http://www.martinfowler.com/eaaCatalog/classTableInheritance.html" target="patioapi">
 *     class table inheritance
 * </a>.
 *
 *<div>
 *     Consider the following table model.
 *     <pre class="code">
 *          employee
 *            - id
 *            - name (varchar)
 *            - kind (varchar)
 *     /                          \
 * staff                        manager
 *   - id (fk employee)           - id (fk employee)
 *   - manager_id (fk manger)     - numStaff (number)
示例#28
0
var Dataset = comb.define(null, {
    instance : {
        /**@lends Dataset.prototype*/

        constructor: function(table, db, type, model) {
            if (!table) throw new Error("table is required by dataset");
            if (!db) throw new Error("db is required by dataset");
            //if(!model) throw new Error("model is required by dataset");
            this._super(arguments);
            this.model = model;
            this.type = type;
        },

        _load : function(results) {
            var promises = [], retPromise;
            promises = results.map(function(o) {
                var p = new Promise();
                if (this.model) {
                    var m = this.model.load(o).then(function(m) {
                        m.__isNew = false;
                        p.callback(m);
                    });
                } else {
                    p.callback(o);
                }
                return p;
            }, this);

            retPromise = new PromiseList(promises);
            return retPromise;
        },

        /**
         * Provide Array style looping a query results.
         *
         * @example
         * dataset.forEach(function(r, i){
         *     console.log("Row %d", i);
         * });
         *
         *
         * @param {Function} [callback] executed for each row returned.
         * @param {Function} [errback] executed if an error occurs.
         * @param {Object} [scope] scope to execute the callback and errback in.
         *
         * @return {comb.Promise} called back with results or the error if one occurs.
         */
        forEach : function(callback, errback, scope) {
            var retPromise = new Promise();
            if (callback) {
                this.all().addCallback(hitch(this, function(results) {
                    if (results && results.length) {
                        results.forEach(callback, scope);
                    } else {
                        results = null;
                        callback.call(scope || this, null);
                    }
                    retPromise.callback(results);
                })).addErrback(hitch(retPromise, "errback"));
                retPromise.addErrback(errback);
            } else {
                throw new Error("callback required");
            }
            return retPromise;
        },

        /**
         * Retrieve one row result from the query.
         *
         * @example
         *
         * dataset.one(function(r){
         *     Do something....
         * }, function(err){
         *     Do something...
         * });
         *
         * //OR
         *
         * dataset.one().then(function(r){
         *     Do something....
         * }, function(err){
         *     Do something...
         * });
         *
         * @param {Function} [callback] executed with the row
         * @param {Function} [errback] executed if an error occurs.
         *
         * @return {comb.Promise} called back with result or the error if one occurs.
         */
        one : function(callback, errback) {
            var retPromise = new Promise();
            this.limit(1);
            this.exec().addCallback(hitch(this, function(results, fields) {
                if (results && results.length) {
                    results = this._load(results).then(hitch(this, function(results) {
                        results = results[0][1];
                        callback && callback(results);
                        retPromise.callback(results);
                    }));
                } else {
                    results = null;
                    callback && callback(results);
                    retPromise.callback(results);
                }

            })).addErrback(hitch(retPromise, "errback"));
            retPromise.addErrback(errback);
            return retPromise;
        },

        /**
         * Retrieve the first result from an ordered query.
         *
         * @example
         * dataset.first(function(r){
         *     Do something....
         * }, function(err){
         *     Do something...
         * });
         *
         * //OR
         *
         * dataset.first().then(function(r){
         *     Do something....
         * }, function(err){
         *     Do something...
         * });
         *
         * @param {Function} [callback] executed with the row
         * @param {Function} [errback] executed if an error occurs.
         *
         * @return {comb.Promise} called back with result or the error if one occurs.
         */
        first : function(callback, errback) {
            var retPromise = new Promise();
            this.exec().addCallback(hitch(this, function(results, fields) {
                if (results && results.length) {
                    results = this._load(results).then(hitch(this, function(results) {
                        results = results[0][1];
                        callback && callback(results);
                        retPromise.callback(results);
                    }));
                } else {
                    results = null;
                    callback && callback(results);
                    retPromise.callback(results);
                }
            })).addErrback(hitch(retPromise, "errback"));
            retPromise.addErrback(errback);
            return retPromise;
        },

        /**
         * Retrieve the last result from an ordered query. If the query is not ordered then the result is ambiguous.
         *
         * @example
         *
         * dataset.last(function(r){
         *     Do something....
         * }, function(err){
         *     Do something...
         * });
         *
         * //OR
         *
         * dataset.last().then(function(r){
         *     Do something....
         * }, function(err){
         *     Do something...
         * });
         *
         * @param {Function} [callback] executed with the row
         * @param {Function} [errback] executed if an error occurs.
         *
         * @return {comb.Promise} called back with result or the error if one occurs.
         */
        last : function(callback, errback) {
            var retPromise = new Promise();
            this.exec().addCallback(hitch(this, function(results, fields) {
                if (results && results.length) {
                    results = this._load(results).then(hitch(this, function(results) {
                        results = results[results.length - 1][1];
                        callback && callback(results);
                        retPromise.callback(results);
                    }));
                } else {
                    results = null;
                    callback && callback(results);
                    retPromise.callback(results);
                }
            })).addErrback(hitch(retPromise, "errback"));
            retPromise.addErrback(errback);
            return retPromise;

        },

        /**
         * Retrieve all rows from the query.
         *
         * @example
         *
         * dataset.all(function(r){
         *     Do something....
         * }, function(err){
         *     Do something...
         * });
         *
         * //OR
         *
         * dataset.all().then(function(r){
         *     Do something....
         * }, function(err){
         *     Do something...
         * });
         *
         * @param {Function} [callback] executed with the results.
         * @param {Function} [errback] executed if an error occurs.
         *
         * @return {comb.Promise} called back with results or the error if one occurs.
         */
        all : function(callback, errback) {
            var retPromise = new Promise();
            this.exec().addCallback(hitch(this, function(results, fields) {
                if (results && results.length) {
                    results = this._load(results).then(hitch(this, function(results) {
                        results = results.map(function(r) {
                            return r[1];
                        });
                        callback && callback(results);
                        retPromise.callback(results);
                    }));
                } else {
                    callback && callback(results);
                    retPromise.callback(results);
                }
            })).addErrback(hitch(retPromise, "errback"));
            retPromise.addErrback(errback);
            return retPromise;
        },

        /**
         * Retrieve the last inserted id from the database.
         *
         * @example
         *
         * dataset.getLastInsertId(function(r){
         *     Do something....
         * }, function(err){
         *     Do something...
         * });
         *
         * //OR
         *
         * dataset.getLastInsertId().then(function(r){
         *     Do something....
         * }, function(err){
         *     Do something...
         * });
         *
         * @param {Function} [callback] executed with the id
         * @param {Function} [errback] executed if an error occurs.
         *
         * @return {comb.Promise} called back with id or the error if one occurs.
         */
        getLastInsertId : function(callback, errback) {
            var retPromise = new Promise();
            adapter.getLastInsertId(this.db).addCallback(hitch(this, function(results) {
                if (results) {
                    retPromise.callback(results[0].id);
                } else {
                    retPromise.callback(null);
                }
            })).addErrback(hitch(retPromise, "errback"));
            retPromise.then(callback, errback);
            return retPromise;
        },

        /**
         * Save values to a table.
         *
         * </br>
         * <b>This should not be used directly</b>
         *
         * @param {Function} [callback] executed with the row
         * @param {Function} [errback] executed if an error occurs.
         *
         * @return {comb.Promise} called back with results or the error if one occurs.
         */
        save : function(vals, loadId, callback, errback) {
            var retPromise = new Promise();
            adapter.save(this.table, vals, this.db).addCallback(hitch(this, function(results) {
                if (loadId) {
                    retPromise.callback(results.insertId);
                } else {
                    retPromise.callback(results);
                }
            })).addErrback(hitch(retPromise, "errback"));
            retPromise.addErrback(errback);
            return retPromise;
        },

        /**
         * Alias for {@link Dataset#all}
         */
        run : function(callback, errback) {
            return this.all(callback, errback);
        }
    }
});
示例#29
0
文件: patio.test.js 项目: C2FO/patio
        return patio.disconnect().chain(function () {
            DummyDataset = comb.define(patio.Dataset, {
                instance: {
                    first: function () {
                        var ret = new comb.Promise();
                        if (this.__opts.from[0] === "a") {
                            ret.errback();
                        } else {
                            ret.callback();
                        }
                        return ret;
                    }
                }
            });
            DummyDatabase = comb.define(patio.Database, {
                instance: {
                    constructor: function () {
                        this._super(arguments);
                        this.sqls = [];
                        this.identifierInputMethod = null;
                        this.identifierOutputMethod = null;
                    },

                    createConnection: function (options) {
                        this.connected = true;
                        return new comb.Promise().callback({});
                    },

                    closeConnection: function (conn) {
                        this.connected = false;
                        return new comb.Promise().callback();
                    },

                    validate: function (conn) {
                        return new Promise().callback(true);
                    },

                    execute: function (sql, opts) {
                        this.pool.getConnection();
                        var ret = new comb.Promise();
                        this.sqls.push(sql);
                        ret.callback();
                        return ret;
                    },

                    executeError: function () {
                        return this.execute.apply(this, arguments).chain(
                            function () {
                                throw new Error();
                            }, function () {
                                throw new Error();
                            });
                    },

                    reset: function () {
                        this.sqls = [];
                    },

                    transaction: function (opts, cb) {
                        var ret = new comb.Promise();
                        cb();
                        ret.callback();
                        return ret;
                    },

                    getters: {
                        dataset: function () {
                            return new DummyDataset(this);
                        }
                    }
                },

                "static": {
                    init: function () {
                        this.setAdapterType("dummydb");
                    }
                }
            });
            patio.resetIdentifierMethods();
        });
示例#30
0
    it.beforeAll(function () {
        MockDS = comb.define(patio.Dataset, {
            instance: {

                fetchRows: function (sql, cb) {
                    return comb.async.array([
                        {version: patioMigrationVersion}
                    ]);
                },

                insert: function (values) {
                    var from = this.__opts.from[0], ret = new comb.Promise().callback(0);
                    if (from.toString() === "schema_info") {
                        patioMigrationVersion = values[Object.keys(values)[0]];
                    }
                    return ret;
                },

                update: function (values) {
                    var from = this.__opts.from[0], ret = new comb.Promise().callback(1);
                    if (from.toString() === "schema_info") {
                        patioMigrationVersion = values[Object.keys(values)[0]];
                    }
                    return ret;
                },

                count: function () {
                    return new comb.Promise().callback(1);
                },

                getters: {
                    columns: function () {
                        var from = this.__opts.from[0], ret = new comb.Promise();
                        ret.callback(this.db.columnsCreated);
                        return ret;
                    }
                }
            }
        });

        MockDB = comb.define(patio.Database, {

            instance: {

                constructor: function () {
                    this._super(arguments);
                    this.type = this._static.type;
                    this.quoteIdentifiers = false;
                    this.identifierInputMethod = null;
                    this.identifierOutputMethod = null;
                    this.connectionExecuteMethod = "query";
                    this.sqls = [];
                    this.tables = {};
                    this.alteredTables = {};
                    this.closedCount = 0;
                    this.createdCount = 0;
                    this.columnsCreated = [];
                    this.columnsAltered = {};
                    this.droppedTables = [];
                },

                createConnection: function () {
                    this.createdCount++;
                    return {
                        query: function (sql) {
                            DB.sqls.push(sql);
                            return new comb.Promise().callback(sql);
                        }
                    };
                },

                closeConnection: function () {
                    this.closedCount++;
                    return new comb.Promise().callback();
                },

                validate: function () {
                    return new comb.Promise().callback(true);
                },

                execute: function (sql, opts) {
                    var ret = new comb.Promise();
                    this.sqls.push(sql);
                    ret.callback();
                    return ret;
                },

                createTable: function (name, args) {
                    this.tables[name] = true;
                    return this._super(arguments).chain(function () {
                        var match = this.sqls[this.sqls.length - 1].match(/ \(?(\w+) integer.*\)?$/);
                        if (match != null) {
                            this.columnsCreated.push(match[1]);
                        }
                    }.bind(this));
                },

                dropTable: function (name) {
                    comb.argsToArray(arguments).forEach(function (name) {
                        this.droppedTables.push(name);
                        this.tables[name] = null;
                    }, this);
                    return new comb.Promise().callback();
                },

                tableExists: function (name) {
                    return new comb.Promise().callback(!comb.isUndefinedOrNull(this.tables[name]));
                },

                alterTable: function (name) {
                    this.alteredTables[name] = true;
                    var promise = this._super(arguments);
                    return promise.chain(function () {
                        this.columnsCreated = [];
                        this.columnsAltered = {};
                        this.sqls.forEach(function (sql) {
                            var match = sql.match(/ \(?(\w+) integer.*\)?$/);
                            var alterMatch = sql.match(/(\w+) TO (\w+)$/);
                            if (match != null) {
                                this.columnsCreated.push(match[1]);
                            }
                            if (alterMatch != null) {
                                this.columnsAltered[alterMatch[1]] = alterMatch[2];
                            }
                        }, this);
                        return 1;
                    }.bind(this));
                },


                reset: function () {
                    this.sqls = [];
                    this.columnsCreated = [];
                    this.droppedTables = [];
                    this.columnsAltered = {};
                    this.tables = {};
                    this.alteredTables = {};
                    patioMigrationVersion = -1;
                    patioMigrationFiles = [];
                },

                getters: {
                    dataset: function () {
                        return new MockDS(this);
                    }
                }
            }

        });

        DB = new MockDB();
    });