不要怂,就是干,撸起袖子干!

Commit 1ef192b9 by Mick Hansen

refactor(data-types): data types should always be functions, data types should a…

…lways be inited before use
1 parent 8d7c0703
'use strict'; 'use strict';
var STRING = function(length, binary) { var util = require('util');
if (this instanceof STRING) {
this._binary = !!binary; var ABSTRACT = function(options) {
if (typeof length === 'number') {
this._length = length;
} else {
this._length = 255;
}
} else {
return new STRING(length, binary);
}
}; };
var CHAR = function(length, binary) { ABSTRACT.prototype.toString = function() {
if (this instanceof CHAR) { return this.toSql();
this._binary = !!binary; };
if (typeof length === 'number') { ABSTRACT.prototype.toSql = function() {
this._length = length; return this.key;
} else { };
this._length = 255;
} /**
} else { * A variable length string. Default length 255
return new CHAR(length, binary); *
} * Available properties: `BINARY`
*
* @property STRING
*/
var STRING = function(length, binary) {
var options = typeof length === "object" && length || {
length: length,
binary: binary
};
if (!(this instanceof STRING)) return new STRING(options);
this._binary = options.binary;
this._length = options.length || 255;
}; };
util.inherits(STRING, ABSTRACT);
STRING.prototype = { STRING.prototype.key = 'STRING';
get BINARY() { STRING.prototype.toSql = function() {
return 'VARCHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : '');
};
Object.defineProperty(STRING.prototype, 'BINARY', {
get: function() {
this._binary = true; this._binary = true;
return this; return this;
},
get type() {
return this.toString();
},
toString: function() {
return 'VARCHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : '');
} }
});
/**
* A fixed length string. Default length 255
*
* Available properties: `BINARY`
*
* @property CHAR
*/
var CHAR = function(length, binary) {
var options = typeof length === "object" && length || {
length: length,
binary: binary
};
if (!(this instanceof CHAR)) return new CHAR(options);
STRING.apply(this, arguments);
}; };
util.inherits(CHAR, STRING);
CHAR.prototype = { CHAR.prototype.key = 'CHAR';
get BINARY() { CHAR.prototype.toSql = function() {
this._binary = true; return 'CHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : '');
return this; };
},
get type() {
return this.toString(); /**
}, * An unlimited length text column
toString: function() { * @property TEXT
return 'CHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : ''); */
var TEXT = function(options) {
if (!(this instanceof TEXT)) return new TEXT(options);
};
util.inherits(TEXT, ABSTRACT);
TEXT.prototype.key = 'TEXT';
var NUMBER = function(options) {
if (!(this instanceof NUMBER)) return new NUMBER(options);
this._length = options.length;
this._decimals = options.decimals;
this._precision = options.precision;
this._scale = options.scale;
this._unsigned = options.unsigned;
};
util.inherits(NUMBER, ABSTRACT);
NUMBER.prototype.key = 'NUMBER';
NUMBER.prototype.toSql = function() {
var result = this.key;
if (this._length) {
result += '(' + this._length;
if (typeof this._decimals === 'number') {
result += ',' + this._decimals;
}
result += ')';
}
if (this._unsigned) {
result += ' UNSIGNED';
}
if (this._zerofill) {
result += ' ZEROFILL';
} }
return result;
}; };
Object.defineProperty(STRING, 'BINARY', { Object.defineProperty(NUMBER.prototype, 'UNSIGNED', {
get: function() { get: function() {
return new STRING(undefined, true); this._unsigned = true;
return this;
} }
}); });
Object.defineProperty(NUMBER.prototype, 'ZEROFILL', {
Object.defineProperty(CHAR, 'BINARY', {
get: function() { get: function() {
return new CHAR(undefined, true); this._zerofill = true;
return this;
} }
}); });
var INTEGER = function() { /**
return INTEGER.prototype.construct.apply(this, [INTEGER].concat(Array.prototype.slice.apply(arguments))); * A 32 bit integer.
*
* Available properties: `UNSIGNED`, `ZEROFILL`
*
* @property INTEGER
*/
var INTEGER = function(length) {
var options = typeof length === "object" && length || {
length: length
};
if (!(this instanceof INTEGER)) return new INTEGER(options);
NUMBER.call(this, options);
}; };
util.inherits(INTEGER, NUMBER);
INTEGER.prototype.key = 'INTEGER';
/**
* A 64 bit integer.
*
* Available properties: `UNSIGNED`, `ZEROFILL`
*
* @property BIGINT
*/
var BIGINT = function() { var BIGINT = function(length) {
return BIGINT.prototype.construct.apply(this, [BIGINT].concat(Array.prototype.slice.apply(arguments))); var options = typeof length === "object" && length || {
length: length
};
if (!(this instanceof BIGINT)) return new BIGINT(options);
NUMBER.call(this, options);
}; };
util.inherits(BIGINT, NUMBER);
var FLOAT = function() { BIGINT.prototype.key = 'BIGINT';
return FLOAT.prototype.construct.apply(this, [FLOAT].concat(Array.prototype.slice.apply(arguments)));
/**
* Floating point number. Accepts one or two arguments for precision
*
* Available properties: `UNSIGNED`, `ZEROFILL`
*
* @property FLOAT
*/
var FLOAT = function(length, decimals) {
var options = typeof length === "object" && length || {
length: length,
decimals: decimals
};
if (!(this instanceof FLOAT)) return new FLOAT(options);
NUMBER.call(this, options);
}; };
util.inherits(FLOAT, NUMBER);
FLOAT.prototype.key = 'FLOAT';
var BLOB = function() { /**
return BLOB.prototype.construct.apply(this, [BLOB].concat(Array.prototype.slice.apply(arguments))); * Decimal number. Accepts one or two arguments for precision
*
* Available properties: `UNSIGNED`, `ZEROFILL`
*
* @property DECIMAL
*/
var DECIMAL = function(precision, scale) {
var options = typeof precision === "object" && precision || {
precision: precision,
scale: scale
};
if (!(this instanceof DECIMAL)) return new DECIMAL(options);
NUMBER.call(this, options);
}; };
util.inherits(DECIMAL, NUMBER);
var DECIMAL = function() { DECIMAL.prototype.key = 'DECIMAL';
return DECIMAL.prototype.construct.apply(this, [DECIMAL].concat(Array.prototype.slice.apply(arguments))); DECIMAL.prototype.toSql = function() {
if (this._precision || this._scale) {
return 'DECIMAL(' + this._precision + ',' + this._scale + ')';
}
return 'DECIMAL';
}; };
var VIRTUAL = function() { /**
* A boolean / tinyint column, depending on dialect
* @property BOOLEAN
*/
var BOOLEAN = function() {
if (!(this instanceof BOOLEAN)) return new BOOLEAN();
ABSTRACT.apply(this, arguments);
};
util.inherits(BOOLEAN, ABSTRACT);
BOOLEAN.prototype.key = 'BOOLEAN';
BOOLEAN.prototype.toSql = function() {
return 'TINYINT(1)';
};
/**
* A time column
* @property TIME
*/
var TIME = function() {
if (!(this instanceof TIME)) return new TIME();
ABSTRACT.apply(this, arguments);
}; };
util.inherits(TIME, ABSTRACT);
FLOAT._type = FLOAT; TIME.prototype.key = 'TIME';
FLOAT._typeName = 'FLOAT'; TIME.prototype.toSql = function() {
INTEGER._type = INTEGER; return 'TIME';
INTEGER._typeName = 'INTEGER'; };
BIGINT._type = BIGINT;
BIGINT._typeName = 'BIGINT';
STRING._type = STRING;
STRING._typeName = 'VARCHAR';
CHAR._type = CHAR;
CHAR._typeName = 'CHAR';
BLOB._type = BLOB;
BLOB._typeName = 'BLOB';
DECIMAL._type = DECIMAL;
DECIMAL._typeName = 'DECIMAL';
/**
* A datetime column
* @property DATE
*/
var DATE = function() {
if (!(this instanceof DATE)) return new DATE();
ABSTRACT.apply(this, arguments);
};
util.inherits(DATE, ABSTRACT);
BLOB.toString = STRING.toString = CHAR.toString = INTEGER.toString = FLOAT.toString = BIGINT.toString = DECIMAL.toString = function() { DATE.prototype.key = 'DATE';
return new this._type().toString(); DATE.prototype.toSql = function() {
return 'DATETIME';
}; };
BLOB.prototype = { /**
* A date only column
* @property DATEONLY
*/
construct: function(RealType, length) { var DATEONLY = function() {
if (this instanceof RealType) { if (!(this instanceof DATEONLY)) return new DATEONLY();
this._typeName = RealType._typeName; ABSTRACT.apply(this, arguments);
if (typeof length === 'string') {
this._length = length;
} else {
this._length = '';
}
} else {
return new RealType(length);
}
},
get type() {
return this.toString();
},
toString: function() {
switch (this._length.toLowerCase()) {
case 'tiny':
return 'TINYBLOB';
case 'medium':
return 'MEDIUMBLOB';
case 'long':
return 'LONGBLOB';
default:
return this._typeName;
}
}
}; };
util.inherits(DATEONLY, ABSTRACT);
FLOAT.prototype = BIGINT.prototype = INTEGER.prototype = { DATEONLY.prototype.key = 'DATEONLY';
DATEONLY.prototype.toSql = function() {
construct: function(RealType, length, decimals, unsigned, zerofill) { return 'DATE';
if (this instanceof RealType) { };
this._typeName = RealType._typeName;
this._unsigned = !!unsigned;
this._zerofill = !!zerofill;
if (typeof length === 'number') {
this._length = length;
}
if (typeof decimals === 'number') {
this._decimals = decimals;
}
} else {
return new RealType(length, decimals, unsigned, zerofill);
}
},
get type() { /**
return this.toString(); * A key / value column. Only available in postgres.
}, * @property HSTORE
*/
get UNSIGNED() { var HSTORE = function() {
this._unsigned = true; if (!(this instanceof HSTORE)) return new HSTORE();
return this; ABSTRACT.apply(this, arguments);
}, };
util.inherits(HSTORE, ABSTRACT);
get ZEROFILL() { HSTORE.prototype.key = 'HSTORE';
this._zerofill = true;
return this; /**
}, * A JSON string column. Only available in postgres.
* @property JSON
toString: function() { */
var result = this._typeName; var JSON = function() {
if (this._length) { if (!(this instanceof JSON)) return new JSON();
result += '(' + this._length; ABSTRACT.apply(this, arguments);
if (typeof this._decimals === 'number') {
result += ',' + this._decimals;
}
result += ')';
}
if (this._unsigned) {
result += ' UNSIGNED';
}
if (this._zerofill) {
result += ' ZEROFILL';
}
return result;
}
}; };
util.inherits(JSON, ABSTRACT);
DECIMAL.prototype = { JSON.prototype.key = 'JSON';
construct: function(RealType, precision, scale) { /*
if (this instanceof RealType) { * A pre-processed JSON data column. Only available in postgres.
this._typeName = RealType._typeName; * @property JSONB
if (typeof precision === 'number') { */
this._precision = precision; var JSONB = function() {
} else { if (!(this instanceof JSONB)) return new JSONB();
this._precision = 0; ABSTRACT.apply(this, arguments);
} };
if (typeof scale === 'number') { util.inherits(JSONB, ABSTRACT);
this._scale = scale;
} else {
this._scale = 0;
}
} else {
return new RealType(precision, scale);
}
},
get type() { JSONB.prototype.key = 'JSONB';
return this.toString();
},
get PRECISION() { /**
return this._precision; * A default value of the current timestamp
}, * @property NOW
*/
var NOW = function() {
if (!(this instanceof NOW)) return new NOW();
ABSTRACT.apply(this, arguments);
};
util.inherits(NOW, ABSTRACT);
get SCALE() { NOW.prototype.key = 'NOW';
return this._scale;
},
toString: function() { /**
if (this._precision || this._scale) { * Binary storage. Available lengths: `tiny`, `medium`, `long`
return 'DECIMAL(' + this._precision + ',' + this._scale + ')'; *
} * @property BLOB
*/
return 'DECIMAL'; var BLOB = function(length) {
var options = typeof length === "object" && length || {
length: length
};
if (!(this instanceof BLOB)) return new BLOB(options);
this._length = options.length || '';
};
util.inherits(BLOB, ABSTRACT);
BLOB.prototype.key = 'BLOB';
BLOB.prototype.toSql = function() {
switch (this._length.toLowerCase()) {
case 'tiny':
return 'TINYBLOB';
case 'medium':
return 'MEDIUMBLOB';
case 'long':
return 'LONGBLOB';
default:
return this.key;
} }
}; };
var unsignedDesc = { /**
get: function() { * A column storing a unique univeral identifier. Use with `UUIDV1` or `UUIDV4` for default values.
return new this._type(undefined, undefined, true); * @property UUID
} */
var UUID = function() {
if (!(this instanceof UUID)) return new UUID();
ABSTRACT.apply(this, arguments);
}; };
util.inherits(UUID, ABSTRACT);
var zerofillDesc = { UUID.prototype.key = 'UUID';
get: function() {
return new this._type(undefined, undefined, undefined, true); /**
} * A default unique universal identifier generated following the UUID v1 standard
* @property UUIDV1
*/
var UUIDV1 = function() {
if (!(this instanceof UUIDV1)) return new UUIDV1();
ABSTRACT.apply(this, arguments);
}; };
util.inherits(UUIDV1, ABSTRACT);
var typeDesc = { UUIDV1.prototype.key = 'UUIDV1';
get: function() {
return new this._type().toString(); /**
} * A default unique universal identifier generated following the UUID v2 standard
* @property UUIDV4
*/
var UUIDV4 = function() {
if (!(this instanceof UUIDV4)) return new UUIDV4();
ABSTRACT.apply(this, arguments);
}; };
util.inherits(UUIDV4, ABSTRACT);
var decimalDesc = { UUIDV4.prototype.key = 'UUIDV4';
get: function() {
return new this._type(undefined, undefined, undefined); /**
} * A virtual value that is not stored in the DB. This could for example be useful if you want to provide a default value in your model
* that is returned to the user but not stored in the DB.
*
* You could also use it to validate a value before permuting and storing it. Checking password length before hashing it for example:
* ```js
* sequelize.define('user', {
* password_hash: DataTypes.STRING
* password: {
* type: DataTypes.VIRTUAL,
* set: function (val) {
* this.setDataValue('password', val);
* this.setDataValue('password_hash', this.salt + val);
* },
* validate: {
* isLongEnough: function (val) {
* if (val.length < 7) {
* throw new Error("Please choose a longer password")
* }
* }
* }
* }
* })
* ```
* In the above code the password is stored plainly in the password field so it can be validated, but is never stored in the DB.
* @property VIRTUAL
* @alias NONE
*/
var VIRTUAL = function() {
if (!(this instanceof VIRTUAL)) return new VIRTUAL();
ABSTRACT.apply(this, arguments);
}; };
util.inherits(VIRTUAL, ABSTRACT);
Object.defineProperty(STRING, 'type', typeDesc); VIRTUAL.prototype.key = 'VIRTUAL';
Object.defineProperty(CHAR, 'type', typeDesc);
Object.defineProperty(INTEGER, 'type', typeDesc);
Object.defineProperty(BIGINT, 'type', typeDesc);
Object.defineProperty(FLOAT, 'type', typeDesc);
Object.defineProperty(BLOB, 'type', typeDesc);
Object.defineProperty(DECIMAL, 'type', typeDesc);
Object.defineProperty(INTEGER, 'UNSIGNED', unsignedDesc); /**
Object.defineProperty(BIGINT, 'UNSIGNED', unsignedDesc); * An enumeration. `DataTypes.ENUM('value', 'another value')`.
Object.defineProperty(FLOAT, 'UNSIGNED', unsignedDesc); *
* @property ENUM
*/
var ENUM = function(value) {
var options = typeof value === "object" && !Array.isArray(value) && value || {
values: Array.prototype.slice.call(arguments).reduce(function(result, element) {
return result.concat(Array.isArray(element) ? element : [element]);
}, [])
};
if (!(this instanceof ENUM)) return new ENUM(options);
this.values = options.values;
};
util.inherits(ENUM, ABSTRACT);
Object.defineProperty(INTEGER, 'ZEROFILL', zerofillDesc); ENUM.prototype.key = 'ENUM';
Object.defineProperty(BIGINT, 'ZEROFILL', zerofillDesc);
Object.defineProperty(FLOAT, 'ZEROFILL', zerofillDesc);
Object.defineProperty(DECIMAL, 'PRECISION', decimalDesc); /**
Object.defineProperty(DECIMAL, 'SCALE', decimalDesc); * An array of `type`, e.g. `DataTypes.ARRAY(DataTypes.DECIMAL)`. Only available in postgres.
* @property ARRAY
*/
var ARRAY = function(type) {
var options = typeof type === "object" && type || {
type: type
};
if (!(this instanceof ARRAY)) return new ARRAY(options);
this.type = typeof options.type === "function" ? new options.type() : options.type;
};
util.inherits(ARRAY, ABSTRACT);
ARRAY.prototype.key = 'ARRAY';
ARRAY.prototype.toSql = function() {
return this.type.toSql() + '[]';
};
var helpers = {
BINARY: [STRING, CHAR],
UNSIGNED: [NUMBER, INTEGER, BIGINT, FLOAT],
ZEROFILL: [NUMBER, INTEGER, BIGINT, FLOAT],
PRECISION: [DECIMAL],
SCALE: [DECIMAL]
};
Object.keys(helpers).forEach(function (helper) {
helpers[helper].forEach(function (DataType) {
if (!DataType[helper]) {
Object.defineProperty(DataType, helper, {
get: function() {
var dataType = new DataType();
if (typeof dataType[helper] === "object") {
dataType[helper];
return dataType;
}
return dataType[helper].apply(dataType, arguments);
}
});
}
});
});
/** /**
* A convenience class holding commonly used data types. The datatypes are used when definining a new model using `Sequelize.define`, like this: * A convenience class holding commonly used data types. The datatypes are used when definining a new model using `Sequelize.define`, like this:
...@@ -307,179 +510,27 @@ Object.defineProperty(DECIMAL, 'SCALE', decimalDesc); ...@@ -307,179 +510,27 @@ Object.defineProperty(DECIMAL, 'SCALE', decimalDesc);
* @class DataTypes * @class DataTypes
*/ */
module.exports = { module.exports = {
/** ABSTRACT: ABSTRACT,
* A variable length string. Default length 255
*
* Available properties: `BINARY`
*
* @property STRING
*/
STRING: STRING, STRING: STRING,
/**
* A fixed length string. Default length 255
*
* Available properties: `BINARY`
*
* @property CHAR
*/
CHAR: CHAR, CHAR: CHAR,
/** TEXT: TEXT,
* An unlimited length text column
* @property TEXT
*/
TEXT: 'TEXT',
/**
* A 32 bit integer.
*
* Available properties: `UNSIGNED`, `ZEROFILL`
*
* @property INTEGER
*/
INTEGER: INTEGER, INTEGER: INTEGER,
/**
* A 64 bit integer.
*
* Available properties: `UNSIGNED`, `ZEROFILL`
*
* @property BIGINT
*/
BIGINT: BIGINT, BIGINT: BIGINT,
/**
* A time column
* @property TIME
*/
TIME: 'TIME',
/**
* A datetime column
* @property DATE
*/
DATE: 'DATETIME',
/**
* A date only column
* @property DATEONLY
*/
DATEONLY: 'DATE',
/**
* A boolean / tinyint column, depending on dialect
* @property BOOLEAN
*/
BOOLEAN: 'TINYINT(1)',
/**
* Floating point number. Accepts one or two arguments for precision
*
* Available properties: `UNSIGNED`, `ZEROFILL`
*
* @property FLOAT
*/
FLOAT: FLOAT, FLOAT: FLOAT,
/** TIME: TIME,
* A default value of the current timestamp DATE: DATE,
* @property NOW DATEONLY: DATEONLY,
*/ BOOLEAN: BOOLEAN,
NOW: 'NOW', NOW: NOW,
/**
* Binary storage. Available lengths: `tiny`, `medium`, `long`
*
* @property BLOB
*/
BLOB: BLOB, BLOB: BLOB,
/**
* Decimal number. Accepts one or two arguments for precision
*
* Available properties: `UNSIGNED`, `ZEROFILL`
*
* @property DECIMAL
*/
DECIMAL: DECIMAL, DECIMAL: DECIMAL,
/** UUID: UUID,
* A column storing a unique univeral identifier. Use with `UUIDV1` or `UUIDV4` for default values. UUIDV1: UUIDV1,
* @property UUID UUIDV4: UUIDV4,
*/ HSTORE: HSTORE,
UUID: 'UUID', JSON: JSON,
/** JSONB: JSONB,
* A default unique universal identifier generated following the UUID v1 standard
* @property UUIDV1
*/
UUIDV1: 'UUIDV1',
/**
* A default unique universal identifier generated following the UUID v2 standard
* @property UUIDV4
*/
UUIDV4: 'UUIDV4',
/**
* A key / value column. Only available in postgres.
* @property HSTORE
*/
HSTORE: 'HSTORE',
/**
* A JSON string column. Only available in postgres.
* @property JSON
*/
JSON: 'JSON',
/*
* A pre-processed JSON data column. Only available in postgres.
* @property JSONB
*/
JSONB: 'JSONB',
/**
* A virtual value that is not stored in the DB. This could for example be useful if you want to provide a default value in your model
* that is returned to the user but not stored in the DB.
*
* You could also use it to validate a value before permuting and storing it. Checking password length before hashing it for example:
* ```js
* sequelize.define('user', {
* password_hash: DataTypes.STRING
* password: {
* type: DataTypes.VIRTUAL,
* set: function (val) {
* this.setDataValue('password', val);
* this.setDataValue('password_hash', this.salt + val);
* },
* validate: {
* isLongEnough: function (val) {
* if (val.length < 7) {
* throw new Error("Please choose a longer password")
* }
* }
* }
* }
* })
* ```
* In the above code the password is stored plainly in the password field so it can be validated, but is never stored in the DB.
* @property VIRTUAL
* @alias NONE
*/
VIRTUAL: VIRTUAL, VIRTUAL: VIRTUAL,
NONE: VIRTUAL, NONE: VIRTUAL,
ENUM: ENUM
/**
* An enumeration. `DataTypes.ENUM('value', 'another value')`.
*
* @property ENUM
*/
get ENUM() {
var result = function() {
return {
type: 'ENUM',
values: Array.prototype.slice.call(arguments).reduce(function(result, element) {
return result.concat(Array.isArray(element) ? element : [element]);
}, []),
toString: result.toString
};
};
result.toString = result.valueOf = function() { return 'ENUM'; };
return result;
},
/**
* An array of `type`, e.g. `DataTypes.ARRAY(DataTypes.DECIMAL)`. Only available in postgres.
* @property ARRAY
*/
ARRAY: function(type) { return type + '[]'; }
}; };
...@@ -249,7 +249,7 @@ module.exports = (function() { ...@@ -249,7 +249,7 @@ module.exports = (function() {
var template = "<%= type %>" var template = "<%= type %>"
, replacements = { type: dataType.type }; , replacements = { type: dataType.type };
if (dataType.type.toString() === DataTypes.ENUM.toString()) { if (dataType.type instanceof DataTypes.ENUM) {
replacements.type = "TEXT"; replacements.type = "TEXT";
if (!(Array.isArray(dataType.values) && (dataType.values.length > 0))) { if (!(Array.isArray(dataType.values) && (dataType.values.length > 0))) {
......
...@@ -368,7 +368,7 @@ InstanceValidator.prototype._validateSchema = function(rawAttribute, field, valu ...@@ -368,7 +368,7 @@ InstanceValidator.prototype._validateSchema = function(rawAttribute, field, valu
this.errors.push(error); this.errors.push(error);
} }
if (rawAttribute.type === DataTypes.STRING || rawAttribute.type instanceof DataTypes.STRING || rawAttribute.type === DataTypes.TEXT) { if (rawAttribute.type === DataTypes.STRING || rawAttribute.type instanceof DataTypes.STRING || rawAttribute.type === DataTypes.TEXT || rawAttribute.type instanceof DataTypes.TEXT) {
if (Array.isArray(value) || (_.isObject(value) && !value._isSequelizeMethod) && !Buffer.isBuffer(value)) { if (Array.isArray(value) || (_.isObject(value) && !value._isSequelizeMethod) && !Buffer.isBuffer(value)) {
error = new sequelizeError.ValidationErrorItem(field + ' cannot be an array or an object', 'string violation', field, value); error = new sequelizeError.ValidationErrorItem(field + ' cannot be an array or an object', 'string violation', field, value);
this.errors.push(error); this.errors.push(error);
......
...@@ -81,22 +81,23 @@ module.exports = (function() { ...@@ -81,22 +81,23 @@ module.exports = (function() {
throw new Error('Unrecognized data type for field ' + name); throw new Error('Unrecognized data type for field ' + name);
} }
if (attribute.type.toString() === DataTypes.ENUM.toString()) { if (typeof attribute.type === "function") attribute.type = new attribute.type();
if (attribute.type instanceof DataTypes.ENUM) {
// The ENUM is a special case where the type is an object containing the values // The ENUM is a special case where the type is an object containing the values
attribute.values = attribute.type.values || attribute.values || []; attribute.values = attribute.values || attribute.type.values || [];
if (!attribute.values.length) { if (!attribute.values.length) {
throw new Error('Values for ENUM haven\'t been defined.'); throw new Error('Values for ENUM haven\'t been defined.');
} }
attributes[name].validate = attributes[name].validate || { attribute.validate = attribute.validate || {
_checkEnum: function(value, next) { _checkEnum: function(value, next) {
var hasValue = value !== undefined var hasValue = value !== undefined
, isMySQL = ['mysql', 'mariadb'].indexOf(options.sequelize.options.dialect) !== -1 , isMySQL = ['mysql', 'mariadb'].indexOf(options.sequelize.options.dialect) !== -1
, ciCollation = !!options.collate && options.collate.match(/_ci$/i) !== null , ciCollation = !!options.collate && options.collate.match(/_ci$/i) !== null
, valueOutOfScope; , valueOutOfScope;
if (isMySQL && ciCollation && hasValue) { if (isMySQL && ciCollation && hasValue) {
var scopeIndex = (attributes[name].values || []).map(function(d) { return d.toLowerCase(); }).indexOf(value.toLowerCase()); var scopeIndex = (attributes[name].values || []).map(function(d) { return d.toLowerCase(); }).indexOf(value.toLowerCase());
valueOutOfScope = scopeIndex === -1; valueOutOfScope = scopeIndex === -1;
...@@ -290,7 +291,7 @@ module.exports = (function() { ...@@ -290,7 +291,7 @@ module.exports = (function() {
this.Instance.prototype.validators = {}; this.Instance.prototype.validators = {};
Utils._.each(this.rawAttributes, function(definition, name) { Utils._.each(this.rawAttributes, function(definition, name) {
var type = definition.type._typeName || definition.type; if (typeof definition.type === "function") definition.type = new definition.type();
definition.Model = self; definition.Model = self;
definition.fieldName = name; definition.fieldName = name;
...@@ -300,21 +301,28 @@ module.exports = (function() { ...@@ -300,21 +301,28 @@ module.exports = (function() {
definition.field = name; definition.field = name;
} }
if (type === DataTypes.BOOLEAN) { if (definition.type instanceof DataTypes.BOOLEAN) {
self._booleanAttributes.push(name); self._booleanAttributes.push(name);
} else if (type === DataTypes.DATE) { } else if (definition.type instanceof DataTypes.DATE) {
self._dateAttributes.push(name); self._dateAttributes.push(name);
} else if (type === DataTypes.HSTORE) { } else if (definition.type instanceof DataTypes.HSTORE) {
self._hstoreAttributes.push(name); self._hstoreAttributes.push(name);
} else if (type === DataTypes.JSON) { } else if (definition.type instanceof DataTypes.JSON) {
self._jsonAttributes.push(name); self._jsonAttributes.push(name);
} else if (type === DataTypes.VIRTUAL) { } else if (definition.type instanceof DataTypes.VIRTUAL) {
self._virtualAttributes.push(name); self._virtualAttributes.push(name);
} }
if (definition.hasOwnProperty('defaultValue')) { if (definition.hasOwnProperty('defaultValue')) {
self._defaultValues[name] = Utils._.partial( if (typeof definition.defaultValue === "function" && (
Utils.toDefaultValue, definition.defaultValue); definition.defaultValue === DataTypes.NOW ||
definition.defaultValue === DataTypes.UUIDV4 ||
definition.defaultValue === DataTypes.UUIDV4
)) {
definition.defaultValue = new definition.defaultValue();
}
self._defaultValues[name] = Utils._.partial(Utils.toDefaultValue, definition.defaultValue);
} }
if (definition.hasOwnProperty('validate')) { if (definition.hasOwnProperty('validate')) {
...@@ -808,12 +816,12 @@ module.exports = (function() { ...@@ -808,12 +816,12 @@ module.exports = (function() {
options.dataType = this.rawAttributes[field].type; options.dataType = this.rawAttributes[field].type;
} else { } else {
// Use FLOAT as fallback // Use FLOAT as fallback
options.dataType = DataTypes.FLOAT; options.dataType = new DataTypes.FLOAT();
} }
} }
options = paranoidClause(this, options); options = paranoidClause(this, options);
return this.QueryInterface.rawSelect(this.getTableName(options), options, aggregateFunction, this); return this.QueryInterface.rawSelect(this.getTableName(options), options, aggregateFunction, this);
}; };
......
...@@ -80,6 +80,27 @@ module.exports = (function() { ...@@ -80,6 +80,27 @@ module.exports = (function() {
attribute = { type: attribute, allowNull: true }; attribute = { type: attribute, allowNull: true };
} }
if (typeof attribute.type === "function") attribute.type = new attribute.type();
if (attribute.hasOwnProperty('defaultValue')) {
if (typeof attribute.defaultValue === "function" && (
attribute.defaultValue === DataTypes.NOW ||
attribute.defaultValue === DataTypes.UUIDV4 ||
attribute.defaultValue === DataTypes.UUIDV4
)) {
attribute.defaultValue = new definition.defaultValue();
}
}
if (attribute.type instanceof DataTypes.ENUM) {
// The ENUM is a special case where the type is an object containing the values
attribute.values = attribute.values || attribute.type.values || [];
if (!attribute.values.length) {
throw new Error('Values for ENUM haven\'t been defined.');
}
}
return attribute; return attribute;
}); });
...@@ -330,8 +351,9 @@ module.exports = (function() { ...@@ -330,8 +351,9 @@ module.exports = (function() {
}); });
}; };
QueryInterface.prototype.addColumn = function(table, key, dataType) { QueryInterface.prototype.addColumn = function(table, key, attribute) {
return this.sequelize.query(this.QueryGenerator.addColumnQuery(table, key, dataType), null, {logging: this.sequelize.options.logging}); if (typeof attribute.type === "function") attribute.type = new attribute.type();
return this.sequelize.query(this.QueryGenerator.addColumnQuery(table, key, attribute), null, {logging: this.sequelize.options.logging});
}; };
QueryInterface.prototype.removeColumn = function(tableName, attributeName) { QueryInterface.prototype.removeColumn = function(tableName, attributeName) {
...@@ -353,6 +375,8 @@ module.exports = (function() { ...@@ -353,6 +375,8 @@ module.exports = (function() {
attributes[attributeName] = dataTypeOrOptions; attributes[attributeName] = dataTypeOrOptions;
} }
if (typeof attributes[attributeName].type === "function") attributes[attributeName].type = new attributes[attributeName].type();
if (this.sequelize.options.dialect === 'sqlite') { if (this.sequelize.options.dialect === 'sqlite') {
// sqlite needs some special treatment as it cannot change a column // sqlite needs some special treatment as it cannot change a column
return SQLiteQueryInterface.changeColumn.call(this, tableName, attributes); return SQLiteQueryInterface.changeColumn.call(this, tableName, attributes);
...@@ -741,13 +765,13 @@ module.exports = (function() { ...@@ -741,13 +765,13 @@ module.exports = (function() {
if (dataType instanceof DataTypes.DECIMAL || dataType instanceof DataTypes.FLOAT) { if (dataType instanceof DataTypes.DECIMAL || dataType instanceof DataTypes.FLOAT) {
result = parseFloat(result); result = parseFloat(result);
} else if (dataType === DataTypes.INTEGER || dataType instanceof DataTypes.BIGINT) { } else if (dataType instanceof DataTypes.INTEGER || dataType instanceof DataTypes.BIGINT) {
result = parseInt(result, 10); result = parseInt(result, 10);
} else if (dataType === DataTypes.DATE) { } else if (dataType instanceof DataTypes.DATE) {
if (!Utils._.isDate(result)) { if (!Utils._.isDate(result)) {
result = new Date(result); result = new Date(result);
} }
} else if (dataType === DataTypes.STRING) { } else if (dataType instanceof DataTypes.STRING) {
// Nothing to do, result is already a string. // Nothing to do, result is already a string.
} }
} }
......
...@@ -473,11 +473,11 @@ var Utils = module.exports = { ...@@ -473,11 +473,11 @@ var Utils = module.exports = {
toDefaultValue: function(value) { toDefaultValue: function(value) {
if (lodash.isFunction(value)) { if (lodash.isFunction(value)) {
return value(); return value();
} else if (value === DataTypes.UUIDV1) { } else if (value instanceof DataTypes.UUIDV1) {
return uuid.v1(); return uuid.v1();
} else if (value === DataTypes.UUIDV4) { } else if (value instanceof DataTypes.UUIDV4) {
return uuid.v4(); return uuid.v4();
} else if (value === DataTypes.NOW) { } else if (value instanceof DataTypes.NOW) {
return Utils.now(); return Utils.now();
} else { } else {
return value; return value;
...@@ -496,9 +496,9 @@ var Utils = module.exports = { ...@@ -496,9 +496,9 @@ var Utils = module.exports = {
// TODO this will be schemable when all supported db // TODO this will be schemable when all supported db
// have been normalized for this case // have been normalized for this case
if (value === DataTypes.NOW) { return false; } if (value instanceof DataTypes.NOW) { return false; }
if (value === DataTypes.UUIDV1 || value === DataTypes.UUIDV4) { return false; } if (value instanceof DataTypes.UUIDV1 || value instanceof DataTypes.UUIDV4) { return false; }
if (lodash.isFunction(value)) { if (lodash.isFunction(value)) {
return false; return false;
......
...@@ -580,7 +580,7 @@ describe(Support.getTestDialectTeaser('BelongsTo'), function() { ...@@ -580,7 +580,7 @@ describe(Support.getTestDialectTeaser('BelongsTo'), function() {
User.belongsTo(Group); User.belongsTo(Group);
self.sequelize.sync({ force: true }).success(function() { self.sequelize.sync({ force: true }).success(function() {
expect(User.rawAttributes.GroupPKBTName.type.toString()).to.equal(DataTypes.STRING.toString()); expect(User.rawAttributes.GroupPKBTName.type.toString()).to.equal(DataTypes.STRING().toString());
done(); done();
}); });
}); });
...@@ -604,7 +604,7 @@ describe(Support.getTestDialectTeaser('BelongsTo'), function() { ...@@ -604,7 +604,7 @@ describe(Support.getTestDialectTeaser('BelongsTo'), function() {
.success(function() { .success(function() {
dataTypes.forEach(function(dataType, i) { dataTypes.forEach(function(dataType, i) {
expect(Tasks[dataType].rawAttributes.userId.type.toString()) expect(Tasks[dataType].rawAttributes.userId.type.toString())
.to.equal(dataType.toString()); .to.equal(dataType().toString());
if ((i + 1) === dataTypes.length) { if ((i + 1) === dataTypes.length) {
done(); done();
......
...@@ -2607,7 +2607,7 @@ describe(Support.getTestDialectTeaser('HasMany'), function() { ...@@ -2607,7 +2607,7 @@ describe(Support.getTestDialectTeaser('HasMany'), function() {
User.hasMany(Tasks[dataType], { foreignKey: 'userId', keyType: dataType, constraints: false }); User.hasMany(Tasks[dataType], { foreignKey: 'userId', keyType: dataType, constraints: false });
return Tasks[dataType].sync({ force: true }).then(function() { return Tasks[dataType].sync({ force: true }).then(function() {
expect(Tasks[dataType].rawAttributes.userId.type.toString()).to.equal(dataType.toString()); expect(Tasks[dataType].rawAttributes.userId.type instanceof dataType).to.be.ok;
}); });
}); });
}); });
...@@ -2624,7 +2624,7 @@ describe(Support.getTestDialectTeaser('HasMany'), function() { ...@@ -2624,7 +2624,7 @@ describe(Support.getTestDialectTeaser('HasMany'), function() {
User.hasMany(Task); User.hasMany(Task);
return this.sequelize.sync({ force: true }).then(function() { return this.sequelize.sync({ force: true }).then(function() {
expect(Task.rawAttributes.UserId.type).to.equal(DataTypes.STRING); expect(Task.rawAttributes.UserId.type instanceof DataTypes.STRING).to.be.ok;
}); });
}); });
......
...@@ -515,7 +515,7 @@ describe(Support.getTestDialectTeaser('HasOne'), function() { ...@@ -515,7 +515,7 @@ describe(Support.getTestDialectTeaser('HasOne'), function() {
Group.hasOne(User); Group.hasOne(User);
self.sequelize.sync({ force: true }).success(function() { self.sequelize.sync({ force: true }).success(function() {
expect(User.rawAttributes.GroupPKBTName.type.toString()).to.equal(Sequelize.STRING.toString()); expect(User.rawAttributes.GroupPKBTName.type.toString()).to.equal(Sequelize.STRING().toString());
done(); done();
}); });
}); });
...@@ -536,7 +536,7 @@ describe(Support.getTestDialectTeaser('HasOne'), function() { ...@@ -536,7 +536,7 @@ describe(Support.getTestDialectTeaser('HasOne'), function() {
Tasks[dataType].sync({ force: true }).success(function() { Tasks[dataType].sync({ force: true }).success(function() {
expect(Tasks[dataType].rawAttributes.userId.type.toString()) expect(Tasks[dataType].rawAttributes.userId.type.toString())
.to.equal(dataType.toString()); .to.equal(dataType().toString());
dataTypes.splice(dataTypes.indexOf(dataType), 1); dataTypes.splice(dataTypes.indexOf(dataType), 1);
if (!dataTypes.length) { if (!dataTypes.length) {
......
...@@ -70,15 +70,16 @@ describe(Support.getTestDialectTeaser('DataTypes'), function() { ...@@ -70,15 +70,16 @@ describe(Support.getTestDialectTeaser('DataTypes'), function() {
[Sequelize.BLOB('tiny'), 'BLOB(\'tiny\')', 'TINYBLOB'], [Sequelize.BLOB('tiny'), 'BLOB(\'tiny\')', 'TINYBLOB'],
[Sequelize.BLOB('medium'), 'BLOB(\'medium\')', 'MEDIUMBLOB'], [Sequelize.BLOB('medium'), 'BLOB(\'medium\')', 'MEDIUMBLOB'],
[Sequelize.BLOB('long'), 'BLOB(\'long\')', 'LONGBLOB'], [Sequelize.BLOB('long'), 'BLOB(\'long\')', 'LONGBLOB'],
[Sequelize.INTEGER, 'INTEGER', 'INTEGER'], [Sequelize.INTEGER, 'INTEGER', 'INTEGER'],
[Sequelize.INTEGER.UNSIGNED, 'INTEGER.UNSIGNED', 'INTEGER UNSIGNED'], [Sequelize.INTEGER.UNSIGNED, 'INTEGER.UNSIGNED', 'INTEGER UNSIGNED'],
[Sequelize.INTEGER.UNSIGNED.ZEROFILL, 'INTEGER.UNSIGNED', 'INTEGER UNSIGNED ZEROFILL'],
[Sequelize.INTEGER(11), 'INTEGER(11)', 'INTEGER(11)'], [Sequelize.INTEGER(11), 'INTEGER(11)', 'INTEGER(11)'],
[Sequelize.INTEGER(11).UNSIGNED, 'INTEGER(11).UNSIGNED', 'INTEGER(11) UNSIGNED'], [Sequelize.INTEGER(11).UNSIGNED, 'INTEGER(11).UNSIGNED', 'INTEGER(11) UNSIGNED'],
[Sequelize.INTEGER(11).UNSIGNED.ZEROFILL, 'INTEGER(11).UNSIGNED.ZEROFILL', 'INTEGER(11) UNSIGNED ZEROFILL'], [Sequelize.INTEGER(11).UNSIGNED.ZEROFILL, 'INTEGER(11).UNSIGNED.ZEROFILL', 'INTEGER(11) UNSIGNED ZEROFILL'],
[Sequelize.INTEGER(11).ZEROFILL, 'INTEGER(11).ZEROFILL', 'INTEGER(11) ZEROFILL'], [Sequelize.INTEGER(11).ZEROFILL, 'INTEGER(11).ZEROFILL', 'INTEGER(11) ZEROFILL'],
[Sequelize.INTEGER(11).ZEROFILL.UNSIGNED, 'INTEGER(11).ZEROFILL.UNSIGNED', 'INTEGER(11) UNSIGNED ZEROFILL'], [Sequelize.INTEGER(11).ZEROFILL.UNSIGNED, 'INTEGER(11).ZEROFILL.UNSIGNED', 'INTEGER(11) UNSIGNED ZEROFILL'],
[Sequelize.BIGINT, 'BIGINT', 'BIGINT'], [Sequelize.BIGINT, 'BIGINT', 'BIGINT'],
[Sequelize.BIGINT.UNSIGNED, 'BIGINT.UNSIGNED', 'BIGINT UNSIGNED'], [Sequelize.BIGINT.UNSIGNED, 'BIGINT.UNSIGNED', 'BIGINT UNSIGNED'],
[Sequelize.BIGINT(11), 'BIGINT(11)', 'BIGINT(11)'], [Sequelize.BIGINT(11), 'BIGINT(11)', 'BIGINT(11)'],
...@@ -124,7 +125,8 @@ describe(Support.getTestDialectTeaser('DataTypes'), function() { ...@@ -124,7 +125,8 @@ describe(Support.getTestDialectTeaser('DataTypes'), function() {
} }
} }
expect(test[0].toString()).to.equal(test[2]); if (typeof test[0] === "function") test[0] = new test[0]();
expect(test[0].toSql()).to.equal(test[2]);
done(); done();
}); });
}); });
......
...@@ -908,8 +908,7 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -908,8 +908,7 @@ describe(Support.getTestDialectTeaser('Model'), function() {
results.forEach(function(book, index) { results.forEach(function(book, index) {
expect(book.title).to.equal(data.title); expect(book.title).to.equal(data.title);
expect(book.author).to.equal(data.author); expect(book.author).to.equal(data.author);
expect(books[index].rawAttributes.id.type.toString()) expect(books[index].rawAttributes.id.type instanceof dataTypes[index]).to.be.ok;
.to.equal(dataTypes[index].toString());
}); });
done(); done();
}); });
......
...@@ -18,7 +18,7 @@ describe(Support.getTestDialectTeaser('CounterCache'), function() { ...@@ -18,7 +18,7 @@ describe(Support.getTestDialectTeaser('CounterCache'), function() {
User.hasMany(Group, { counterCache: true }); User.hasMany(Group, { counterCache: true });
expect(Object.keys(User.attributes)).to.contain('countGroups'); expect(Object.keys(User.attributes)).to.contain('countGroups');
expect(User.attributes.countGroups.type).to.equal(DataTypes.INTEGER); expect(User.attributes.countGroups.type instanceof DataTypes.INTEGER).to.be.ok;
}); });
it('supports `as`', function() { it('supports `as`', function() {
......
...@@ -834,7 +834,11 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() { ...@@ -834,7 +834,11 @@ describe(Support.getTestDialectTeaser('Sequelize'), function() {
Object.keys(customAttributes).forEach(function(attribute) { Object.keys(customAttributes).forEach(function(attribute) {
Object.keys(customAttributes[attribute]).forEach(function(option) { Object.keys(customAttributes[attribute]).forEach(function(option) {
var optionValue = customAttributes[attribute][option]; var optionValue = customAttributes[attribute][option];
expect(Picture.rawAttributes[attribute][option]).to.be.equal(optionValue); if (typeof optionValue === "function" && optionValue() instanceof DataTypes.ABSTRACT) {
expect(Picture.rawAttributes[attribute][option] instanceof optionValue).to.be.ok;
} else {
expect(Picture.rawAttributes[attribute][option]).to.be.equal(optionValue);
}
}); });
}); });
done(); done();
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!