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

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;
if (typeof length === 'number') {
this._length = length;
} else {
this._length = 255;
}
} else {
return new STRING(length, binary);
}
};
var CHAR = function(length, binary) { var ABSTRACT = function(options) {
if (this instanceof CHAR) {
this._binary = !!binary;
if (typeof length === 'number') {
this._length = length;
} else {
this._length = 255;
}
} else {
return new CHAR(length, binary);
}
};
STRING.prototype = {
get BINARY() {
this._binary = true;
return this;
},
get type() {
return this.toString();
},
toString: function() {
return 'VARCHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : '');
}
}; };
CHAR.prototype = { ABSTRACT.prototype.toString = function() {
get BINARY() { return this.toSql();
this._binary = true;
return this;
},
get type() {
return this.toString();
},
toString: function() {
return 'CHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : '');
}
}; };
ABSTRACT.prototype.toSql = function() {
Object.defineProperty(STRING, 'BINARY', { return this.key;
get: function() {
return new STRING(undefined, true);
}
});
Object.defineProperty(CHAR, 'BINARY', {
get: function() {
return new CHAR(undefined, true);
}
});
var INTEGER = function() {
return INTEGER.prototype.construct.apply(this, [INTEGER].concat(Array.prototype.slice.apply(arguments)));
}; };
var BIGINT = function() { /**
return BIGINT.prototype.construct.apply(this, [BIGINT].concat(Array.prototype.slice.apply(arguments))); * A variable length string. Default length 255
}; *
* Available properties: `BINARY`
*
* @property STRING
*/
var STRING = function(length, binary) {
var options = typeof length === "object" && length || {
length: length,
binary: binary
};
var FLOAT = function() { if (!(this instanceof STRING)) return new STRING(options);
return FLOAT.prototype.construct.apply(this, [FLOAT].concat(Array.prototype.slice.apply(arguments)));
};
var BLOB = function() { this._binary = options.binary;
return BLOB.prototype.construct.apply(this, [BLOB].concat(Array.prototype.slice.apply(arguments))); this._length = options.length || 255;
}; };
util.inherits(STRING, ABSTRACT);
var DECIMAL = function() { STRING.prototype.key = 'STRING';
return DECIMAL.prototype.construct.apply(this, [DECIMAL].concat(Array.prototype.slice.apply(arguments))); STRING.prototype.toSql = function() {
return 'VARCHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : '');
}; };
Object.defineProperty(STRING.prototype, 'BINARY', {
get: function() {
this._binary = true;
return this;
}
});
var VIRTUAL = function() { /**
* 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);
FLOAT._type = FLOAT; CHAR.prototype.key = 'CHAR';
FLOAT._typeName = 'FLOAT'; CHAR.prototype.toSql = function() {
INTEGER._type = INTEGER; return 'CHAR(' + this._length + ')' + ((this._binary) ? ' BINARY' : '');
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';
BLOB.toString = STRING.toString = CHAR.toString = INTEGER.toString = FLOAT.toString = BIGINT.toString = DECIMAL.toString = function() {
return new this._type().toString();
}; };
BLOB.prototype = {
construct: function(RealType, length) {
if (this instanceof RealType) {
this._typeName = RealType._typeName;
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()) { * An unlimited length text column
case 'tiny': * @property TEXT
return 'TINYBLOB'; */
case 'medium': var TEXT = function(options) {
return 'MEDIUMBLOB'; if (!(this instanceof TEXT)) return new TEXT(options);
case 'long':
return 'LONGBLOB';
default:
return this._typeName;
}
}
}; };
util.inherits(TEXT, ABSTRACT);
FLOAT.prototype = BIGINT.prototype = INTEGER.prototype = { TEXT.prototype.key = 'TEXT';
construct: function(RealType, length, decimals, unsigned, zerofill) {
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();
},
get UNSIGNED() {
this._unsigned = true;
return this;
},
get ZEROFILL() { var NUMBER = function(options) {
this._zerofill = true; if (!(this instanceof NUMBER)) return new NUMBER(options);
return this; this._length = options.length;
}, this._decimals = options.decimals;
this._precision = options.precision;
this._scale = options.scale;
this._unsigned = options.unsigned;
};
util.inherits(NUMBER, ABSTRACT);
toString: function() { NUMBER.prototype.key = 'NUMBER';
var result = this._typeName; NUMBER.prototype.toSql = function() {
var result = this.key;
if (this._length) { if (this._length) {
result += '(' + this._length; result += '(' + this._length;
if (typeof this._decimals === 'number') { if (typeof this._decimals === 'number') {
...@@ -189,243 +107,283 @@ FLOAT.prototype = BIGINT.prototype = INTEGER.prototype = { ...@@ -189,243 +107,283 @@ FLOAT.prototype = BIGINT.prototype = INTEGER.prototype = {
result += ' ZEROFILL'; result += ' ZEROFILL';
} }
return result; return result;
}
}; };
DECIMAL.prototype = { Object.defineProperty(NUMBER.prototype, 'UNSIGNED', {
construct: function(RealType, precision, scale) {
if (this instanceof RealType) {
this._typeName = RealType._typeName;
if (typeof precision === 'number') {
this._precision = precision;
} else {
this._precision = 0;
}
if (typeof scale === 'number') {
this._scale = scale;
} else {
this._scale = 0;
}
} else {
return new RealType(precision, scale);
}
},
get type() {
return this.toString();
},
get PRECISION() {
return this._precision;
},
get SCALE() {
return this._scale;
},
toString: function() {
if (this._precision || this._scale) {
return 'DECIMAL(' + this._precision + ',' + this._scale + ')';
}
return 'DECIMAL';
}
};
var unsignedDesc = {
get: function() {
return new this._type(undefined, undefined, true);
}
};
var zerofillDesc = {
get: function() { get: function() {
return new this._type(undefined, undefined, undefined, true); this._unsigned = true;
} return this;
};
var typeDesc = {
get: function() {
return new this._type().toString();
} }
}; });
Object.defineProperty(NUMBER.prototype, 'ZEROFILL', {
var decimalDesc = {
get: function() { get: function() {
return new this._type(undefined, undefined, undefined); this._zerofill = true;
return this;
} }
}; });
Object.defineProperty(STRING, 'type', typeDesc);
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);
Object.defineProperty(FLOAT, 'UNSIGNED', unsignedDesc);
Object.defineProperty(INTEGER, 'ZEROFILL', zerofillDesc);
Object.defineProperty(BIGINT, 'ZEROFILL', zerofillDesc);
Object.defineProperty(FLOAT, 'ZEROFILL', zerofillDesc);
Object.defineProperty(DECIMAL, 'PRECISION', decimalDesc);
Object.defineProperty(DECIMAL, 'SCALE', decimalDesc);
/** /**
* A convenience class holding commonly used data types. The datatypes are used when definining a new model using `Sequelize.define`, like this: * A 32 bit integer.
* ```js
* sequelize.define('model', {
* column: DataTypes.INTEGER
* })
* ```
* When defining a model you can just as easily pass a string as type, but often using the types defined here is beneficial. For example, using `DataTypes.BLOB`, mean
* that that column will be returned as an instance of `Buffer` when being fetched by sequelize.
*
* Some data types have special properties that can be accessed in order to change the data type. For example, to get an unsigned integer with zerofill you can do `DataTypes.INTEGER.UNSIGNED.ZEROFILL`.
* The order you access the properties in do not matter, so `DataTypes.INTEGER.ZEROFILL.UNSIGNED` is fine as well. The available properties are listed under each data type.
*
* To provide a length for the data type, you can invoke it like a function: `INTEGER(2)`
*
* Three of the values provided here (`NOW`, `UUIDV1` and `UUIDV4`) are special default values, that should not be used to define types. Instead they are used as shorthands for
* defining default values. For example, to get a uuid field with a default value generated following v1 of the UUID standard:
* ```js
* sequelize.define('model', {
* uuid: {
* type: DataTypes.UUID,
* defaultValue: DataTypes.UUIDV1,
* primaryKey: true
* }
* })
* ```
*
* @class DataTypes
*/
module.exports = {
/**
* A variable length string. Default length 255
* *
* Available properties: `BINARY` * Available properties: `UNSIGNED`, `ZEROFILL`
* *
* @property STRING * @property INTEGER
*/ */
STRING: STRING, var INTEGER = function(length) {
/** var options = typeof length === "object" && length || {
* A fixed length string. Default length 255 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: `BINARY` * Available properties: `UNSIGNED`, `ZEROFILL`
* *
* @property CHAR * @property BIGINT
*/
CHAR: CHAR,
/**
* An unlimited length text column
* @property TEXT
*/ */
TEXT: 'TEXT',
/** var BIGINT = function(length) {
* A 32 bit integer. var options = typeof length === "object" && length || {
length: length
};
if (!(this instanceof BIGINT)) return new BIGINT(options);
NUMBER.call(this, options);
};
util.inherits(BIGINT, NUMBER);
BIGINT.prototype.key = 'BIGINT';
/**
* Floating point number. Accepts one or two arguments for precision
* *
* Available properties: `UNSIGNED`, `ZEROFILL` * Available properties: `UNSIGNED`, `ZEROFILL`
* *
* @property INTEGER * @property FLOAT
*/ */
INTEGER: INTEGER,
/** var FLOAT = function(length, decimals) {
* A 64 bit integer. 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';
/**
* Decimal number. Accepts one or two arguments for precision
* *
* Available properties: `UNSIGNED`, `ZEROFILL` * Available properties: `UNSIGNED`, `ZEROFILL`
* *
* @property BIGINT * @property DECIMAL
*/ */
BIGINT: BIGINT, 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);
DECIMAL.prototype.key = 'DECIMAL';
DECIMAL.prototype.toSql = function() {
if (this._precision || this._scale) {
return 'DECIMAL(' + this._precision + ',' + this._scale + ')';
}
return 'DECIMAL';
};
/**
* 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 * A time column
* @property TIME * @property TIME
*/ */
TIME: 'TIME',
/** var TIME = function() {
if (!(this instanceof TIME)) return new TIME();
ABSTRACT.apply(this, arguments);
};
util.inherits(TIME, ABSTRACT);
TIME.prototype.key = 'TIME';
TIME.prototype.toSql = function() {
return 'TIME';
};
/**
* A datetime column * A datetime column
* @property DATE * @property DATE
*/ */
DATE: 'DATETIME', var DATE = function() {
/** if (!(this instanceof DATE)) return new DATE();
ABSTRACT.apply(this, arguments);
};
util.inherits(DATE, ABSTRACT);
DATE.prototype.key = 'DATE';
DATE.prototype.toSql = function() {
return 'DATETIME';
};
/**
* A date only column * A date only column
* @property DATEONLY * @property DATEONLY
*/ */
DATEONLY: 'DATE',
/** var DATEONLY = function() {
* A boolean / tinyint column, depending on dialect if (!(this instanceof DATEONLY)) return new DATEONLY();
* @property BOOLEAN ABSTRACT.apply(this, arguments);
};
util.inherits(DATEONLY, ABSTRACT);
DATEONLY.prototype.key = 'DATEONLY';
DATEONLY.prototype.toSql = function() {
return 'DATE';
};
/**
* A key / value column. Only available in postgres.
* @property HSTORE
*/ */
BOOLEAN: 'TINYINT(1)',
/** var HSTORE = function() {
* Floating point number. Accepts one or two arguments for precision if (!(this instanceof HSTORE)) return new HSTORE();
* ABSTRACT.apply(this, arguments);
* Available properties: `UNSIGNED`, `ZEROFILL` };
* util.inherits(HSTORE, ABSTRACT);
* @property FLOAT
HSTORE.prototype.key = 'HSTORE';
/**
* A JSON string column. Only available in postgres.
* @property JSON
*/ */
FLOAT: FLOAT, var JSON = function() {
/** if (!(this instanceof JSON)) return new JSON();
ABSTRACT.apply(this, arguments);
};
util.inherits(JSON, ABSTRACT);
JSON.prototype.key = 'JSON';
/*
* A pre-processed JSON data column. Only available in postgres.
* @property JSONB
*/
var JSONB = function() {
if (!(this instanceof JSONB)) return new JSONB();
ABSTRACT.apply(this, arguments);
};
util.inherits(JSONB, ABSTRACT);
JSONB.prototype.key = 'JSONB';
/**
* A default value of the current timestamp * A default value of the current timestamp
* @property NOW * @property NOW
*/ */
NOW: 'NOW', var NOW = function() {
/** if (!(this instanceof NOW)) return new NOW();
ABSTRACT.apply(this, arguments);
};
util.inherits(NOW, ABSTRACT);
NOW.prototype.key = 'NOW';
/**
* Binary storage. Available lengths: `tiny`, `medium`, `long` * Binary storage. Available lengths: `tiny`, `medium`, `long`
* *
* @property BLOB * @property BLOB
*/ */
BLOB: BLOB,
/** var BLOB = function(length) {
* Decimal number. Accepts one or two arguments for precision var options = typeof length === "object" && length || {
* length: length
* Available properties: `UNSIGNED`, `ZEROFILL` };
* if (!(this instanceof BLOB)) return new BLOB(options);
* @property DECIMAL this._length = options.length || '';
*/ };
DECIMAL: DECIMAL, 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;
}
};
/**
* A column storing a unique univeral identifier. Use with `UUIDV1` or `UUIDV4` for default values. * A column storing a unique univeral identifier. Use with `UUIDV1` or `UUIDV4` for default values.
* @property UUID * @property UUID
*/ */
UUID: 'UUID', var UUID = function() {
/** if (!(this instanceof UUID)) return new UUID();
ABSTRACT.apply(this, arguments);
};
util.inherits(UUID, ABSTRACT);
UUID.prototype.key = 'UUID';
/**
* A default unique universal identifier generated following the UUID v1 standard * A default unique universal identifier generated following the UUID v1 standard
* @property UUIDV1 * @property UUIDV1
*/ */
UUIDV1: 'UUIDV1',
/** var UUIDV1 = function() {
if (!(this instanceof UUIDV1)) return new UUIDV1();
ABSTRACT.apply(this, arguments);
};
util.inherits(UUIDV1, ABSTRACT);
UUIDV1.prototype.key = 'UUIDV1';
/**
* A default unique universal identifier generated following the UUID v2 standard * A default unique universal identifier generated following the UUID v2 standard
* @property UUIDV4 * @property UUIDV4
*/ */
UUIDV4: 'UUIDV4',
/** var UUIDV4 = function() {
* A key / value column. Only available in postgres. if (!(this instanceof UUIDV4)) return new UUIDV4();
* @property HSTORE ABSTRACT.apply(this, arguments);
*/ };
HSTORE: 'HSTORE', util.inherits(UUIDV4, ABSTRACT);
/**
* A JSON string column. Only available in postgres.
* @property JSON
*/
JSON: 'JSON',
/* UUIDV4.prototype.key = 'UUIDV4';
* 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 * 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. * that is returned to the user but not stored in the DB.
* *
...@@ -453,33 +411,126 @@ module.exports = { ...@@ -453,33 +411,126 @@ module.exports = {
* @property VIRTUAL * @property VIRTUAL
* @alias NONE * @alias NONE
*/ */
VIRTUAL: VIRTUAL, var VIRTUAL = function() {
NONE: VIRTUAL, if (!(this instanceof VIRTUAL)) return new VIRTUAL();
ABSTRACT.apply(this, arguments);
};
util.inherits(VIRTUAL, ABSTRACT);
VIRTUAL.prototype.key = 'VIRTUAL';
/** /**
* An enumeration. `DataTypes.ENUM('value', 'another value')`. * An enumeration. `DataTypes.ENUM('value', 'another value')`.
* *
* @property ENUM * @property ENUM
*/ */
get ENUM() { var ENUM = function(value) {
var result = function() { var options = typeof value === "object" && !Array.isArray(value) && value || {
return {
type: 'ENUM',
values: Array.prototype.slice.call(arguments).reduce(function(result, element) { values: Array.prototype.slice.call(arguments).reduce(function(result, element) {
return result.concat(Array.isArray(element) ? element : [element]); return result.concat(Array.isArray(element) ? element : [element]);
}, []), }, [])
toString: result.toString
}; };
}; if (!(this instanceof ENUM)) return new ENUM(options);
this.values = options.values;
result.toString = result.valueOf = function() { return 'ENUM'; }; };
util.inherits(ENUM, ABSTRACT);
return result; ENUM.prototype.key = 'ENUM';
},
/** /**
* An array of `type`, e.g. `DataTypes.ARRAY(DataTypes.DECIMAL)`. Only available in postgres. * An array of `type`, e.g. `DataTypes.ARRAY(DataTypes.DECIMAL)`. Only available in postgres.
* @property ARRAY * @property ARRAY
*/ */
ARRAY: function(type) { return type + '[]'; } 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:
* ```js
* sequelize.define('model', {
* column: DataTypes.INTEGER
* })
* ```
* When defining a model you can just as easily pass a string as type, but often using the types defined here is beneficial. For example, using `DataTypes.BLOB`, mean
* that that column will be returned as an instance of `Buffer` when being fetched by sequelize.
*
* Some data types have special properties that can be accessed in order to change the data type. For example, to get an unsigned integer with zerofill you can do `DataTypes.INTEGER.UNSIGNED.ZEROFILL`.
* The order you access the properties in do not matter, so `DataTypes.INTEGER.ZEROFILL.UNSIGNED` is fine as well. The available properties are listed under each data type.
*
* To provide a length for the data type, you can invoke it like a function: `INTEGER(2)`
*
* Three of the values provided here (`NOW`, `UUIDV1` and `UUIDV4`) are special default values, that should not be used to define types. Instead they are used as shorthands for
* defining default values. For example, to get a uuid field with a default value generated following v1 of the UUID standard:
* ```js
* sequelize.define('model', {
* uuid: {
* type: DataTypes.UUID,
* defaultValue: DataTypes.UUIDV1,
* primaryKey: true
* }
* })
* ```
*
* @class DataTypes
*/
module.exports = {
ABSTRACT: ABSTRACT,
STRING: STRING,
CHAR: CHAR,
TEXT: TEXT,
INTEGER: INTEGER,
BIGINT: BIGINT,
FLOAT: FLOAT,
TIME: TIME,
DATE: DATE,
DATEONLY: DATEONLY,
BOOLEAN: BOOLEAN,
NOW: NOW,
BLOB: BLOB,
DECIMAL: DECIMAL,
UUID: UUID,
UUIDV1: UUIDV1,
UUIDV4: UUIDV4,
HSTORE: HSTORE,
JSON: JSON,
JSONB: JSONB,
VIRTUAL: VIRTUAL,
NONE: VIRTUAL,
ENUM: ENUM
}; };
...@@ -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,7 +816,7 @@ module.exports = (function() { ...@@ -808,7 +816,7 @@ 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();
} }
} }
......
...@@ -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) {
......
...@@ -73,6 +73,7 @@ describe(Support.getTestDialectTeaser('DataTypes'), function() { ...@@ -73,6 +73,7 @@ describe(Support.getTestDialectTeaser('DataTypes'), function() {
[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'],
...@@ -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];
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); 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!