'use strict'; var DataTypes = require('./data-types') , SqlString = require('./sql-string') , lodash = require('lodash') , parameterValidator = require('./utils/parameter-validator') , inflection = require('inflection') , dottie = require('dottie') , uuid = require('node-uuid') , deprecate = require('depd')('Utils'); var Utils = module.exports = { inflection: inflection, _: (function() { var _ = lodash; _.mixin({ includes: function(str, needle){ if (needle === '') return true; if (str === null) return false; return String(str).indexOf(needle) !== -1; }, camelizeIf: function(string, condition) { var result = string; if (condition) { result = Utils.camelize(string); } return result; }, underscoredIf: function(string, condition) { var result = string; if (condition) { result = inflection.underscore(string); } return result; }, /* * Returns an array with some falsy values removed. The values null, "", undefined and NaN are considered falsey. */ compactLite: function(array) { var index = -1, length = array ? array.length : 0, result = []; while (++index < length) { var value = array[index]; if (typeof value === 'boolean' || value === 0 || value) { result.push(value); } } return result; }, matchesDots: function (dots, value) { return function (item) { return dottie.get(item, dots) === value; }; } }); return _; })(), // Same concept as _.merge, but don't overwrite properties that have already been assigned mergeDefaults: function (a, b) { return this._.merge(a, b, function (objectValue, sourceValue) { // If it's an object, let _ handle it this time, we will be called again for each property if (!this._.isPlainObject(objectValue) && objectValue !== undefined) { return objectValue; } }, this); }, lowercaseFirst: function (s) { return s[0].toLowerCase() + s.slice(1); }, uppercaseFirst: function (s) { return s[0].toUpperCase() + s.slice(1); }, spliceStr: function (str, index, count, add) { return str.slice(0, index) + add + str.slice(index + count); }, camelize: function(str){ return str.trim().replace(/[-_\s]+(.)?/g, function(match, c){ return c.toUpperCase(); }); }, format: function(arr, dialect) { var timeZone = null; // Make a clone of the array beacuse format modifies the passed args return SqlString.format(arr[0], arr.slice(1), timeZone, dialect); }, formatNamedParameters: function(sql, parameters, dialect) { var timeZone = null; return SqlString.formatNamedParameters(sql, parameters, timeZone, dialect); }, cloneDeep: function(obj, fn) { return lodash.cloneDeep(obj, function (elem) { // Preserve special data-types like `fn` across clones. _.get() is used for checking up the prototype chain if (elem && typeof elem.clone === 'function') {return elem.clone(); } // Unfortunately, lodash.cloneDeep doesn't preserve Buffer.isBuffer, which we have to rely on for binary data if (Buffer.isBuffer(elem)) { return elem; } return fn ? fn(elem) : undefined; }); }, /* Used to map field names in attributes and where conditions */ mapOptionFieldNames: function(options, Model) { if (options.attributes) { options.attributes = options.attributes.map(function(attr) { // Object lookups will force any variable to strings, we don't want that for special objects etc if (typeof attr !== 'string') return attr; // Map attributes to aliased syntax attributes if (Model.rawAttributes[attr] && attr !== Model.rawAttributes[attr].field) { return [Model.rawAttributes[attr].field, attr]; } return attr; }); } if (options.where) { var attributes = options.where , attribute , rawAttribute; if (options.where instanceof Utils.and || options.where instanceof Utils.or) { attributes = undefined; options.where.args = options.where.args.map(function (where) { return Utils.mapOptionFieldNames({ where: where }, Model).where; }); } if (attributes) { for (attribute in attributes) { rawAttribute = Model.rawAttributes[attribute]; if (rawAttribute && rawAttribute.field !== rawAttribute.fieldName) { attributes[rawAttribute.field] = attributes[attribute]; delete attributes[attribute]; } if (lodash.isPlainObject(attributes[attribute])) { attributes[attribute] = Utils.mapOptionFieldNames({ where: attributes[attribute] }, Model).where; } if (Array.isArray(attributes[attribute])) { attributes[attribute] = attributes[attribute].map(function (where) { return Utils.mapOptionFieldNames({ where: where }, Model).where; }); } } } } return options; }, /* Used to map field names in values */ mapValueFieldNames: function (dataValues, fields, Model) { var values = {}; fields.forEach(function(attr) { if (dataValues[attr] !== undefined && !Model._isVirtualAttribute(attr)) { // Field name mapping if (Model.rawAttributes[attr] && Model.rawAttributes[attr].field && Model.rawAttributes[attr].field !== attr) { values[Model.rawAttributes[attr].field] = dataValues[attr]; } else { values[attr] = dataValues[attr]; } } }); return values; }, argsArePrimaryKeys: function(args, primaryKeys) { var result = (args.length === Object.keys(primaryKeys).length); if (result) { Utils._.each(args, function(arg) { if (result) { if (['number', 'string'].indexOf(typeof arg) !== -1) { result = true; } else { result = (arg instanceof Date) || Buffer.isBuffer(arg); } } }); } return result; }, canTreatArrayAsAnd: function(arr) { return arr.reduce(function(treatAsAnd, arg) { if (treatAsAnd) { return treatAsAnd; } else { return !(arg instanceof Date) && ((arg instanceof Utils.and) || (arg instanceof Utils.or) || Utils._.isPlainObject(arg)); } }, false); }, combineTableNames: function(tableName1, tableName2) { return (tableName1.toLowerCase() < tableName2.toLowerCase()) ? (tableName1 + tableName2) : (tableName2 + tableName1); }, singularize: function(s) { return inflection.singularize(s); }, pluralize: function(s) { return inflection.pluralize(s); }, removeCommentsFromFunctionString: function(s) { s = s.replace(/\s*(\/\/.*)/g, ''); s = s.replace(/(\/\*[\n\r\s\S]*?\*\/)/mg, ''); return s; }, toDefaultValue: function(value) { if (typeof value === 'function') { var tmp = value(); if (tmp instanceof DataTypes.ABSTRACT) { return tmp.toSql(); } else { return tmp; } } else if (value instanceof DataTypes.UUIDV1) { return uuid.v1(); } else if (value instanceof DataTypes.UUIDV4) { return uuid.v4(); } else if (value instanceof DataTypes.NOW) { return Utils.now(); } else { return value; } }, /** * Determine if the default value provided exists and can be described * in a db schema using the DEFAULT directive. * * @param {*} value Any default value. * @return {boolean} yes / no. */ defaultValueSchemable: function(value) { if (typeof value === 'undefined') { return false; } // TODO this will be schemable when all supported db // have been normalized for this case if (value instanceof DataTypes.NOW) { return false; } if (value instanceof DataTypes.UUIDV1 || value instanceof DataTypes.UUIDV4) { return false; } if (lodash.isFunction(value)) { return false; } return true; }, removeNullValuesFromHash: function(hash, omitNull, options) { var result = hash; options = options || {}; options.allowNull = options.allowNull || []; if (omitNull) { var _hash = {}; Utils._.forIn(hash, function(val, key) { if (options.allowNull.indexOf(key) > -1 || key.match(/Id$/) || ((val !== null) && (val !== undefined))) { _hash[key] = val; } }); result = _hash; } return result; }, inherit: function(SubClass, SuperClass) { if (SuperClass.constructor === Function) { // Normal Inheritance SubClass.prototype = new SuperClass(); SubClass.prototype.constructor = SubClass; SubClass.prototype.parent = SuperClass.prototype; } else { // Pure Virtual Inheritance SubClass.prototype = SuperClass; SubClass.prototype.constructor = SubClass; SubClass.prototype.parent = SuperClass; } return SubClass; }, stack: function _stackGrabber() { var orig = Error.prepareStackTrace; Error.prepareStackTrace = function(_, stack) { return stack; }; var err = new Error(); Error.captureStackTrace(err, _stackGrabber); var errStack = err.stack; Error.prepareStackTrace = orig; return errStack; }, sliceArgs: function (args, begin) { begin = begin || 0; var tmp = new Array(args.length - begin); for (var i = begin; i < args.length; ++i) { tmp[i - begin] = args[i]; } return tmp; }, now: function(dialect) { var now = new Date(); if (['postgres', 'sqlite'].indexOf(dialect) === -1) { now.setMilliseconds(0); } return now; }, tick: function(func) { var tick = (global.hasOwnProperty('setImmediate') ? global.setImmediate : process.nextTick); tick(func); }, // Note: Use the `quoteIdentifier()` and `escape()` methods on the // `QueryInterface` instead for more portable code. TICK_CHAR: '`', addTicks: function(s, tickChar) { tickChar = tickChar || Utils.TICK_CHAR; return tickChar + Utils.removeTicks(s, tickChar) + tickChar; }, removeTicks: function(s, tickChar) { tickChar = tickChar || Utils.TICK_CHAR; return s.replace(new RegExp(tickChar, 'g'), ''); }, /* * Utility functions for representing SQL functions, and columns that should be escaped. * Please do not use these functions directly, use Sequelize.fn and Sequelize.col instead. */ fn: function(fn, args) { this.fn = fn; this.args = args; }, col: function(col) { if (arguments.length > 1) { col = this.sliceArgs(arguments); } this.col = col; }, cast: function(val, type) { this.val = val; this.type = (type || '').trim(); }, literal: function(val) { this.val = val; }, and: function(args) { this.args = args; }, or: function(args) { this.args = args; }, json: function(conditionsOrPath, value) { if (Utils._.isObject(conditionsOrPath)) { this.conditions = conditionsOrPath; } else { this.path = conditionsOrPath; if (value) { this.value = value; } } }, where: function(attribute, comparator, logic) { if (logic === undefined) { logic = comparator; comparator = '='; } this.attribute = attribute; this.comparator = comparator; this.logic = logic; }, validateParameter: parameterValidator, formatReferences: function (obj) { if (!lodash.isPlainObject(obj.references)) { deprecate('Non-object references property found. Support for that will be removed in version 4. Expected { references: { model: "value", key: "key" } } instead of { references: "value", referencesKey: "key" }.'); obj.references = { model: obj.references, key: obj.referencesKey, deferrable: obj.referencesDeferrable }; obj.referencesKey = undefined; obj.referencesDeferrable = undefined; } return obj; } }; Utils.and.prototype._isSequelizeMethod = Utils.or.prototype._isSequelizeMethod = Utils.where.prototype._isSequelizeMethod = Utils.literal.prototype._isSequelizeMethod = Utils.cast.prototype._isSequelizeMethod = Utils.fn.prototype._isSequelizeMethod = Utils.col.prototype._isSequelizeMethod = Utils.json.prototype._isSequelizeMethod = true; Utils.fn.prototype.clone = function() { return new Utils.fn(this.fn, this.args); }; Utils.Promise = require('./promise');