columns.forEach(function(column) { var key = column.metadata.colName.toLowerCase(); var value = column.value; if(value && underscore._.isDate(value)) { obj[key] = util.utilDate.format(value, 'yyyy-MM-dd hh:mm:ss'); } else { obj[key] = value ? value : ''; } });
exports.coerce = function(expectedType, value) { var types = { 'string': '', 'number': 0, 'array': [], 'boolean': false }; if (_.isDate(value)) { return value; } if (expectedType === 'date' && !_.isDate(value)) { return (value && new Date(value)) || new Date('1970/1/1'); } if (expectedType && typeof value === 'undefined') { return types[expectedType]; } if (expectedType && typeof value !== 'undefined') { if (expectedType === 'string') { return (value && value.toString()) || ''; } if (expectedType === 'number') { return _.isNumber(_.toInt(value)) ? _.toInt(value) : types.number; } if (expectedType === 'array') { return _.isArray(value) ? value : [ value || '' ]; } if (expectedType === 'boolean') { if (value === 'true') { return true; } if (value === 'false') { return false; } return _.isBoolean(value) ? value : types.boolean; } } return value || ''; };
work: function() { winston.info('manager:composeResponse'); // Update the expiration date on the bundle var tout = { expires: _.min( thisResponse, function( val ) { return val.expires } ).expires, lastModified: now }; if (_.has( thisResponse, 'redirect')) { tout.redirect = thisResponse.redirect, tout.guid = thisResponse.guid, tout.authBundle = thisResponse.authBundle, tout.authPart = thisResponse.authPart }; // Insert api responses into bundle _.each( thisResponse, function( val, idx ) { tout[val.cname] = val; }); // Perform cleanup function on bundle if (_.has(bundle, 'cleanup')) { tout = bundle.cleanup(tout); } // Determine the seconds left before expiry if ( 'expires' in tout && _.isDate(tout.expires) ) { tout.secleft = tout.expires.getSecondsBetween( now ) * -1; } else { tout.secleft = 3600; } // Save cached bundle in Redis client.set('bid'+bid, JSON.stringify(tout)); manager.enqueue('sendResponse', tout); this.finished = true; }
client.get(bid+key, function (err, doc) { if (err || doc === null){ self.finished = true; manager.enqueue('fulfillPart', api, bid, key, true ); } else { doc = JSON.parse( doc ); doc.expires = new Date(doc.expires); if ( ('expires' in doc) && _.isDate(doc.expires) ) { var secleft = doc.expires.getSecondsBetween( now ) * -1; } if (secleft < 0) { self.finished = true; manager.enqueue('fulfillPart', api, bid, key, true, doc ); } else { doc.fromcache = true; manager.enqueue('finishRequest', doc ); self.finished = true; } } });
date: function() { return _.isDate(this); },
exports.docPredicate = function (result, condition, value, conditions) { //if we have an array of objects, this is a deep traversal //we'll need to use a contains query to be sure we flex the index if(_.isArray(value) && _.isObject(value[0])) { //stringify the passed-in params result.params.push(JSON.stringify(conditions)); result.predicates.push(util.format("body @> $%s", result.params.length + result.offset)); } //if we have equality here, just use a JSON contains else if (condition.operator === '=' && !_.isArray(value)) { //parse the value into stringy JSON var param = {}; param[condition.field]=value; result.params.push(JSON.stringify(param)); result.predicates.push(util.format("body @> $%s", result.params.length + result.offset)); return result; } //comparison stuff - same as method above but this time //we'll be coercing the document key values using pg's :: //not ideal, but it works nicely else if (_.isBoolean(value)) { result.predicates.push( util.format("(body ->> '%s')::boolean %s %s", condition.field, condition.operator, value) ); } else if(_.isDate(value)) { result.params.push(value); result.predicates.push( util.format("(body ->> '%s')::timestamp %s $%d", condition.field, condition.operator, result.params.length + result.offset) ); } else if(_.isNumber(value)) { result.predicates.push( util.format("(body ->> '%s')::decimal %s %d", condition.field, condition.operator, value) ); } //anything non-array handling else if (!_.isArray(value)) { result.params.push(value); result.predicates.push( util.format("(body ->> '%s') %s $%s", condition.field, condition.operator, result.params.length + result.offset) ); } else { var arrayConditions = []; _.each(value, function(v) { result.params.push(v); arrayConditions.push("$" + (result.params.length + result.offset)); }); condition.operator = condition.operator === '=' ? 'IN' : 'NOT IN'; result.predicates.push( util.format("(body ->> '%s') %s (%s)", condition.field, condition.operator, arrayConditions.join(', ')) ); } return result; };
// isEqual: 判断两个对象是值相等 var moe = {name:'moe', b:[1,2,3]}; var clone = {name:'moe', b:[1,2,3]}; console.log(moe==clone);//false console.log(_.isEqual(moe, clone));//true //判断对象类型以下都为空 console.log(_.isEmpty({})); //如果object里没包含任何东西,将返回true console.log(_.isArray([1,2,3])); console.log(_.isObject({})); console.log((function(){ return _.isArguments(arguments); })(1, 2, 3)); console.log(_.isFunction(console.log)); console.log(_.isString("moe")); console.log(_.isNumber(8.4 * 5)); console.log(_.isFinite(-101)); console.log(_.isBoolean(true)); console.log(_.isDate(new Date())); console.log(_.isNaN(NaN)); console.log(_.isNull(null)); console.log(_.isUndefined(undefined)); // invert _.invert(object) // 返回一个object的副本,并且里面的键和值是对调的,要使之有效,必须确保object里所有的值都是唯一的且可以序列号成字符串 console.log(_.invert({a:'b', c:'d', e:'f'})); // { b: 'a', d: 'c', f: 'e' } // pairs _.pairs(object) // 把一个对象转换成一个[key, value]形式的数组 console.log(_.pairs({one:1, two:2, three: 3})); // [ [ 'one', 1 ], [ 'two', 2 ], [ 'three', 3 ] ] // values _.values(object) // 获取object对象的所有属性值 console.log(_.values({one:1, two:2, three:3})); // [ 1, 2, 3 ]
var sendResponse = function(jDoc, myRes, ip, bid, callback, gzip) { // Convert the string representation of date to a Date object jDoc.expires = new Date(jDoc.expires); jDoc.lastModified = new Date(jDoc.lastModified); // If there is a valid expiration date for the bundle if ( 'expires' in jDoc && _.isDate(jDoc.expires) ) { jDoc.secleft = jDoc.expires.getSecondsBetween( new Date() ) * -1; } else { // This should never happen jDoc.secleft = -1; } if (Number(jDoc.secleft) < 0 && !GLOBAL.bundles[bid].locked) { // Prevent other requests for this bundle from refreshing cache for time being GLOBAL.bundles[bid].locked = true; // The bundle has expired. Force a refresh exports.fulfill( myRes, ip, bid, callback, gzip, true ); } else { //Respond with the cached data var responseType = callback ? 'application/javascript' : 'application/json'; var responseHeaders = {'Content-Type': responseType, 'vary': 'Accept-Encoding', 'max-age': jDoc.secleft, 'cache-control': 'public, max-age='+jDoc.secleft+', no-transform', "Expires": jDoc.expires, "Last-Modified": jDoc.lastModified}; var ext = bid.match(/\.[\w]+$/); if (ext && (ext = ext[0])) { // When the bid has an extension, we only return the first api. var first = _.find(jDoc, function(api, key) { return api && api.cname; }); doc = first.result; switch (ext) { case ".kml": case ".xml": // When expecting response is KML or XML, we set the correct Content-Type // and wrap it with quotes if it's a JSONP request. responseHeaders['Content-Type'] = 'text/xml'; callback && (doc = "'"+ doc+"'"); break; case ".json": // Content-Type for JSON is already set above. doc = JSON.stringify(doc); default: break; } } else { doc = JSON.stringify(jDoc); } if (callback) { doc = callback + '(' + doc + ');'; } if (gzip) { responseHeaders['content-encoding'] = 'gzip'; zlib.gzip(doc, function(err, zbuf) { if (!err) { winston.event('Send gzipped response for ' + bid +', ' + zbuf.toString().length + ', ' + ip); myRes.writeHead(200, responseHeaders); myRes.end(zbuf); } }); } else { // If a callback name was passed, use it. Otherwise, just output the object var tbuf = new Buffer(doc); myRes.writeHead(200, responseHeaders); winston.event('Send response for ' + bid +', ' + doc.length + ', ' + ip); myRes.end(tbuf); } } }
var typecast = function(value, originalValue, properties) { // Allow transform to manipulate raw properties. if(properties.transform) { value = properties.transform.call(this, value, originalValue, properties); } switch(properties.type) { case 'string': // Reject if object or array. if(_.isObject(value) || _.isArray(value)) { throw new SetterError('String type cannot typecast Object or Array types.', value, originalValue, properties); } // If index is being set with null or undefined, set value and end. if(_.isUndefined(value) || value === null) { return value; } // Typecast to String. value = value + ''; // If stringTransform function is defined, use. // This is used before we do validation checks (except to be sure we have a string at all). if(properties.stringTransform) { value = properties.stringTransform.call(this, value, originalValue, properties); } // If clip property & maxLength properties are set, the string should be clipped. // This is basically a shortcut property that could be done with stringTransform. if(properties.clip && !_.isUndefined(properties.maxLength)) { value = value.substr(0, properties.maxLength); } // If enum is being used, be sure the value is within definition. if(_.isArray(properties.enum) && properties.enum.indexOf(value) === -1) { throw new SetterError('String does not exist in enum list.', value, originalValue, properties); } // If minLength is defined, check to be sure the string is > minLength. if(!_.isUndefined(properties.minLength) && value.length < properties.minLength) { throw new SetterError('String length too short to meet minLength requirement.', value, originalValue, properties); } // If maxLength is defined, check to be sure the string is < maxLength. if(!_.isUndefined(properties.maxLength) && value.length > properties.maxLength) { throw new SetterError('String length too long to meet maxLength requirement.', value, originalValue, properties); } // If regex is defined, check to be sure the string matches the regex pattern. if(properties.regex && !properties.regex.test(value)) { throw new SetterError('String does not match regular expression pattern.', value, originalValue, properties); } return value; break; case 'number': // Set values for boolean. if(_.isBoolean(value)) { value = value ? 1 : 0; } // Reject if array, object, or not numeric. if( _.isArray(value) || _.isObject(value) || !isNumeric(value)) { throw new SetterError('Number type cannot typecast Array or Object types.', value, originalValue, properties); } // Typecast to number. value = value * 1; // Transformation after typecasting but before validation and filters. if(properties.numberTransform) { value = properties.numberTransform.call(this, value, originalValue, properties); } if(!_.isUndefined(properties.min) && value < properties.min) { throw new SetterError('Number is too small to meet min requirement.', value, originalValue, properties); } if(!_.isUndefined(properties.max) && value > properties.max) { throw new SetterError('Number is too big to meet max requirement.', value, originalValue, properties); } return value; break; case 'boolean': // If is String and is 'false', return false. if(value === 'false') { return false; } // If is Number, <0 is true and >0 is false. if(isNumeric(value)) { return (value * 1) > 0 ? true : false; } // Use Javascript to eval and return boolean. value = value ? true : false; // Transformation after typecasting but before validation and filters. if(properties.booleanTransform) { value = properties.booleanTransform.call(this, value, originalValue, properties); } return value; break; case 'array': // If it's an object, typecast to an array and return array. if(_.isObject(value)) { value = _.toArray(value); } // Reject if not array. if(!_.isArray(value)) { throw new SetterError('Array type cannot typecast non-Array types.', value, originalValue, properties); } // Arrays are never set directly. // Instead, the values are copied over to the existing SchemaArray instance. // The SchemaArray is initialized immediately and will always exist. originalValue.length = 0; _.each(value, function(arrayValue) { originalValue.push(arrayValue); }); return originalValue; break; case 'object': // If it's not an Object, reject. if(!_.isObject(value)) { throw new SetterError('Object type cannot typecast non-Object types.', value, originalValue, properties); } // If object is schema object and an entirely new object was passed, clear values and set. // This preserves the object instance. if(properties.objectType) { // The object will usually exist because it's initialized immediately for deep access within SchemaObjects. // However, in the case of Array elements, it will not exist. var schemaObject; if(!_.isUndefined(originalValue)) { // Clear existing values. schemaObject = originalValue; schemaObject.clear(); } else { // The SchemaObject doesn't exist yet. Let's initialize a new one. // This is used for Array types. schemaObject = new properties.objectType; } // Copy value to SchemaObject and set value to SchemaObject. _.each(value, function(v, k) { schemaObject[k] = v; }); value = schemaObject; } // Otherwise, it's OK. return value; break; case 'date': // Reject if object, array or boolean. if(!_.isDate(value) && !_.isString(value) && !_.isNumber(value)) { throw new SetterError('Date type cannot typecast Array or Object types.', value, originalValue, properties); } // Attempt to parse string value with Date.parse (which returns number of milliseconds). if(_.isString(value)) { value = Date.parse(value); } // If is timestamp, convert to Date. if(_.isNumber(value)) { value = new Date((value + '').length > 10 ? value : value * 1000); } // If the date couldn't be parsed, do not modify index. if(value == 'Invalid Date' || !_.isDate(value)) { throw new SetterError('Could not parse date.', value, originalValue, properties); } // Transformation after typecasting but before validation and filters. if(properties.dateTransform) { value = properties.dateTransform.call(this, value, originalValue, properties); } return value; break; default: return value; break; } };