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

Commit 87a2540c by Jan Aagaard Meier

Replaced lingo with inflection, and took a small step towards more consistent names

1 parent 4c0a2680
...@@ -24,8 +24,9 @@ ...@@ -24,8 +24,9 @@
"shadow":true, "shadow":true,
"loopfunc":true, "loopfunc":true,
"evil":true, "evil":true,
"predef": [ "predef": [
"alert",
"describe", "describe",
"it", "it",
"before", "before",
......
...@@ -24,6 +24,7 @@ Notice: All 1.7.x changes are present in 2.0.x aswell ...@@ -24,6 +24,7 @@ Notice: All 1.7.x changes are present in 2.0.x aswell
- [BUG] Use the provided name for a unique index if one is given, instead of concating the column names together [#1944](https://github.com/sequelize/sequelize/issues/1944) - [BUG] Use the provided name for a unique index if one is given, instead of concating the column names together [#1944](https://github.com/sequelize/sequelize/issues/1944)
- [BUG] Create a composite primary key for doubled linked self reference [#1891](https://github.com/sequelize/sequelize/issues/1891) - [BUG] Create a composite primary key for doubled linked self reference [#1891](https://github.com/sequelize/sequelize/issues/1891)
- [INTERNALS] `bulkDeleteQuery` was removed from the MySQL / abstract query generator, since it was never used internally. Please use `deleteQuery` instead. - [INTERNALS] `bulkDeleteQuery` was removed from the MySQL / abstract query generator, since it was never used internally. Please use `deleteQuery` instead.
- [INTERNALS] Replaced lingo with inflection
#### Backwards compatability changes #### Backwards compatability changes
...@@ -35,6 +36,10 @@ Notice: All 1.7.x changes are present in 2.0.x aswell ...@@ -35,6 +36,10 @@ Notice: All 1.7.x changes are present in 2.0.x aswell
- `sequelize.transaction()` now returns a promise rather than a instance of Sequelize.Transaction - `sequelize.transaction()` now returns a promise rather than a instance of Sequelize.Transaction
- `bulkCreate`, `bulkUpdate` and `bulkDestroy` (and aliases) now take both a `hooks` and an `individualHooks` option, `hooks` defines whether or not to run the main hooks, and `individualHooks` defines whether to run hooks for each instance affected. - `bulkCreate`, `bulkUpdate` and `bulkDestroy` (and aliases) now take both a `hooks` and an `individualHooks` option, `hooks` defines whether or not to run the main hooks, and `individualHooks` defines whether to run hooks for each instance affected.
- It is no longer possible to disable pooling, disable pooling will just result in a 1/1 pool. - It is no longer possible to disable pooling, disable pooling will just result in a 1/1 pool.
- We are using a new inflection library, which should make pluralization and singularization in general more robust. However, a couple of pluralizations have changed as a result:
+ Person is now pluralized as people instead of persons
- Accesors for models with underscored names are no longer camel cased automatically. For example, if you have a model with name `my_model`, and `my_other_model` hasMany `my_model`, the getter will now be `getMy_model` instead of `getMyModel`.
- Removed support for setting sequelize.language. If your model names are not in english, use the name option provided by `sequelize.name` to defined singular and plural forms for your model.
# v2.0.0-dev11 # v2.0.0-dev11
### Caution: This release contains many changes and is highly experimental ### Caution: This release contains many changes and is highly experimental
......
...@@ -24,8 +24,12 @@ module.exports = (function() { ...@@ -24,8 +24,12 @@ module.exports = (function() {
if (this.as) { if (this.as) {
this.isAliased = true; this.isAliased = true;
this.options.name = {
singular: this.as
};
} else { } else {
this.as = Utils.singularize(this.target.name, this.target.options.language); this.as = this.target.options.name.singular;
this.options.name = this.target.options.name;
} }
if (!this.options.foreignKey) { if (!this.options.foreignKey) {
...@@ -43,10 +47,13 @@ module.exports = (function() { ...@@ -43,10 +47,13 @@ module.exports = (function() {
this.associationAccessor = this.as; this.associationAccessor = this.as;
this.options.useHooks = options.useHooks; this.options.useHooks = options.useHooks;
// Get singular name, trying to uppercase the first letter, unless the model forbids it
var singular = this.__getSingularName(true);
this.accessors = { this.accessors = {
get: Utils._.camelize('get_' + this.as), get: 'get' + singular,
set: Utils._.camelize('set_' + this.as), set: 'set' + singular,
create: Utils._.camelize('create_' + this.as) create: 'create' + singular
}; };
}; };
...@@ -137,5 +144,8 @@ module.exports = (function() { ...@@ -137,5 +144,8 @@ module.exports = (function() {
return this; return this;
}; };
BelongsTo.prototype.__getSingularName = Helpers.__getSingularName;
BelongsTo.prototype.__getPluralName = Helpers.__getPluralName;
return BelongsTo; return BelongsTo;
})(); })();
...@@ -131,19 +131,34 @@ module.exports = (function() { ...@@ -131,19 +131,34 @@ module.exports = (function() {
if (this.as) { if (this.as) {
this.isAliased = true; this.isAliased = true;
if (Utils._.isPlainObject(this.as)) {
this.options.name = this.as;
this.as = this.as.plural;
} else {
this.options.name = {
plural: this.as,
singular: Utils.singularize(this.as)
};
}
} else { } else {
this.as = (this.options.freezeAssociations ? this.target.tableName : Utils.pluralize(this.target.tableName, this.target.options.language)); this.as = this.target.options.name.singular;
this.options.name = this.target.options.name;
} }
// Get singular and plural names, trying to uppercase the first letter, unless the model forbids it
var plural = this.__getPluralName(true)
, singular = this.__getSingularName(true);
this.accessors = { this.accessors = {
get: Utils._.camelize('get_' + this.as), get: 'get' + plural,
set: Utils._.camelize('set_' + this.as), set: 'set' + plural,
addMultiple: Utils._.camelize('add_' + this.as, this.target.options.language), addMultiple: 'add' + plural,
add: Utils._.camelize(Utils.singularize('add_' + this.as, this.target.options.language)), add: 'add' + singular,
create: Utils._.camelize(Utils.singularize('create_' + this.as, this.target.options.language)), create: 'create' + singular,
remove: Utils._.camelize(Utils.singularize('remove_' + this.as, this.target.options.language)), remove: 'remove' + singular,
hasSingle: Utils._.camelize(Utils.singularize('has_' + this.as, this.target.options.language)), hasSingle: 'has' + singular,
hasAll: Utils._.camelize('has_' + this.as) hasAll: 'has' + plural
}; };
}; };
...@@ -155,7 +170,7 @@ module.exports = (function() { ...@@ -155,7 +170,7 @@ module.exports = (function() {
this.identifier = this.foreignKey || Utils._.camelizeIf( this.identifier = this.foreignKey || Utils._.camelizeIf(
[ [
Utils._.underscoredIf(Utils.singularize(this.source.name, this.source.options.language), this.source.options.underscored), Utils._.underscoredIf(this.source.options.name.singular, this.source.options.underscored),
this.source.primaryKeyAttribute this.source.primaryKeyAttribute
].join('_'), ].join('_'),
!this.source.options.underscored !this.source.options.underscored
...@@ -185,7 +200,7 @@ module.exports = (function() { ...@@ -185,7 +200,7 @@ module.exports = (function() {
if (this.isSelfAssociation && this.foreignIdentifier === this.identifier) { if (this.isSelfAssociation && this.foreignIdentifier === this.identifier) {
this.foreignIdentifier = Utils._.camelizeIf( this.foreignIdentifier = Utils._.camelizeIf(
[Utils.singularize(this.as, this.source.options.language), this.source.primaryKeyAttribute].join('_'), [this.__getSingularName(), this.source.primaryKeyAttribute].join('_'),
!this.source.options.underscored !this.source.options.underscored
); );
...@@ -463,6 +478,9 @@ module.exports = (function() { ...@@ -463,6 +478,9 @@ module.exports = (function() {
return this; return this;
}; };
HasMany.prototype.__getSingularName = Helpers.__getSingularName;
HasMany.prototype.__getPluralName = Helpers.__getPluralName;
/** /**
* The method checks if it is ok to delete the previously defined foreign key. * The method checks if it is ok to delete the previously defined foreign key.
* This is done because we need to keep the foreign key if another association * This is done because we need to keep the foreign key if another association
......
...@@ -24,14 +24,18 @@ module.exports = (function() { ...@@ -24,14 +24,18 @@ module.exports = (function() {
if (this.as) { if (this.as) {
this.isAliased = true; this.isAliased = true;
this.options.name = {
singular: this.as
};
} else { } else {
this.as = Utils.singularize(this.target.name, this.target.options.language); this.as = this.target.options.name.singular;
this.options.name = this.target.options.name;
} }
if (!this.options.foreignKey) { if (!this.options.foreignKey) {
this.options.foreignKey = Utils._.camelizeIf( this.options.foreignKey = Utils._.camelizeIf(
[ [
Utils._.underscoredIf(Utils.singularize(this.source.name, this.target.options.language), this.target.options.underscored), Utils._.underscoredIf(Utils.singularize(this.source.name), this.target.options.underscored),
this.source.primaryKeyAttribute this.source.primaryKeyAttribute
].join('_'), ].join('_'),
!this.source.options.underscored !this.source.options.underscored
...@@ -43,10 +47,13 @@ module.exports = (function() { ...@@ -43,10 +47,13 @@ module.exports = (function() {
this.associationAccessor = this.as; this.associationAccessor = this.as;
this.options.useHooks = options.useHooks; this.options.useHooks = options.useHooks;
// Get singular name, trying to uppercase the first letter, unless the model forbids it
var singular = this.__getSingularName(true);
this.accessors = { this.accessors = {
get: Utils._.camelize('get_' + this.as), get: 'get' + singular,
set: Utils._.camelize('set_' + this.as), set: 'set' + singular,
create: Utils._.camelize('create_' + this.as) create: 'create' + singular
}; };
}; };
...@@ -145,5 +152,8 @@ module.exports = (function() { ...@@ -145,5 +152,8 @@ module.exports = (function() {
return this; return this;
}; };
HasOne.prototype.__getSingularName = Helpers.__getSingularName;
HasOne.prototype.__getPluralName = Helpers.__getPluralName;
return HasOne; return HasOne;
})(); })();
...@@ -3,9 +3,21 @@ ...@@ -3,9 +3,21 @@
var Utils = require('./../utils'); var Utils = require('./../utils');
module.exports = { module.exports = {
checkNamingCollision: function (assocition) { __getSingularName: function (tryUppercase) {
if (assocition.source.rawAttributes.hasOwnProperty(assocition.as)) { return !this.options.name.verbatim && tryUppercase ?
throw new Error("Naming collision between attribute '" + assocition.as + "' and association '" + assocition.as + "' on model " + assocition.source.name + '. To remedy this, change either foreignKey or as in your association definition'); Utils.uppercaseFirst(this.options.name.singular) :
this.options.name.singular ;
},
__getPluralName: function (tryUppercase) {
return !this.options.name.verbatim && tryUppercase ?
Utils.uppercaseFirst(this.options.name.plural) :
this.options.name.plural ;
},
checkNamingCollision: function (association) {
if (association.source.rawAttributes.hasOwnProperty(association.as)) {
throw new Error("Naming collision between attribute '" + association.as + "' and association '" + association.as + "' on model " + association.source.name + '. To remedy this, change either foreignKey or as in your association definition');
} }
}, },
......
...@@ -97,7 +97,7 @@ var Mixin = module.exports = function() {}; ...@@ -97,7 +97,7 @@ var Mixin = module.exports = function() {};
var singleLinked = function (Type) { var singleLinked = function (Type) {
return function(targetModel, options) { return function(targetModel, options) {
if (!(targetModel instanceof this.sequelize.Model)) { if (!(targetModel instanceof this.sequelize.Model)) {
throw new Error(this.name + "." + Utils._.lowercaseFirst(Type.toString()) + " called with something that's not an instance of Sequelize.Model"); throw new Error(this.name + "." + Utils.lowercaseFirst(Type.toString()) + " called with something that's not an instance of Sequelize.Model");
} }
var sourceModel = this; var sourceModel = this;
...@@ -134,7 +134,7 @@ var singleLinked = function (Type) { ...@@ -134,7 +134,7 @@ var singleLinked = function (Type) {
* @param {Model} target * @param {Model} target
* @param {object} [options] * @param {object} [options]
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
* @param {string} [options.as] The alias of this model. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized version of target.name * @param {string} [options.as] The alias of this model, in singular form. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized name of target
* @param {string|object} [options.foreignKey] The name of the foreign key in the target table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). Defaults to the name of source + primary key of source * @param {string|object} [options.foreignKey] The name of the foreign key in the target table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). Defaults to the name of source + primary key of source
* @param {string} [options.onDelete='SET NULL'] * @param {string} [options.onDelete='SET NULL']
* @param {string} [options.onUpdate='CASCADE'] * @param {string} [options.onUpdate='CASCADE']
...@@ -158,7 +158,7 @@ Mixin.hasOne = singleLinked(HasOne); ...@@ -158,7 +158,7 @@ Mixin.hasOne = singleLinked(HasOne);
* @param {Model} target * @param {Model} target
* @param {object} [options] * @param {object} [options]
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
* @param {string} [options.as] The alias of this model. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized version of target.name * @param {string} [options.as] The alias of this model, in singular form. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized name of target
* @param {string|object} [options.foreignKey] The name of the foreign key in the source table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). Defaults to the name of target + primary key of target * @param {string|object} [options.foreignKey] The name of the foreign key in the source table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). Defaults to the name of target + primary key of target
* @param {string} [options.onDelete='SET NULL'] * @param {string} [options.onDelete='SET NULL']
* @param {string} [options.onUpdate='CASCADE'] * @param {string} [options.onUpdate='CASCADE']
...@@ -228,7 +228,7 @@ Mixin.belongsTo = singleLinked(BelongsTo); ...@@ -228,7 +228,7 @@ Mixin.belongsTo = singleLinked(BelongsTo);
* @param {object} [options] * @param {object} [options]
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
* @param {Model|string} [options.through] The name of the table that is used to join source and target in n:m associations. Can also be a sequelize model if you want to define the junction table yourself and add extra attributes to it. * @param {Model|string} [options.through] The name of the table that is used to join source and target in n:m associations. Can also be a sequelize model if you want to define the junction table yourself and add extra attributes to it.
* @param {string} [options.as] The alias of this model. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized version of target.name * @param {string|object} [options.as] The alias of this model. If you provide a string, it should be plural, and will be singularized using node.inflection. If you want to control the singular version yourself, provide an object with `plural` and `singular` keys, and optionally a `verbatim` key (to control whethet the first letter of the name should be upper cased in getter / setter methods). See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the pluralized name of target
* @param {string|object} [options.foreignKey] The name of the foreign key in the target table / join table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). Defaults to the name of source + primary key of source * @param {string|object} [options.foreignKey] The name of the foreign key in the target table / join table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). Defaults to the name of source + primary key of source
* @param {string} [options.onDelete='SET NULL|CASCADE'] Cascade if this is a n:m, and set null if it is a 1:m * @param {string} [options.onDelete='SET NULL|CASCADE'] Cascade if this is a n:m, and set null if it is a 1:m
* @param {string} [options.onUpdate='CASCADE'] * @param {string} [options.onUpdate='CASCADE']
......
...@@ -251,7 +251,7 @@ module.exports = (function() { ...@@ -251,7 +251,7 @@ module.exports = (function() {
options = Utils._.extend({ options = Utils._.extend({
indicesType: null, indicesType: null,
indexName: Utils._.underscored(tableName + '_' + onlyAttributeNames.join('_')), indexName: Utils.inflection.underscore(tableName + '_' + onlyAttributeNames.join('_')),
parser: null parser: null
}, options || {}); }, options || {});
...@@ -276,7 +276,7 @@ module.exports = (function() { ...@@ -276,7 +276,7 @@ module.exports = (function() {
, indexName = indexNameOrAttributes; , indexName = indexNameOrAttributes;
if (typeof indexName !== 'string') { if (typeof indexName !== 'string') {
indexName = Utils._.underscored(tableName + '_' + indexNameOrAttributes.join('_')); indexName = Utils.inflection.underscore(tableName + '_' + indexNameOrAttributes.join('_'));
} }
return Utils._.template(sql)({ tableName: this.quoteIdentifiers(tableName), indexName: indexName }); return Utils._.template(sql)({ tableName: this.quoteIdentifiers(tableName), indexName: indexName });
......
...@@ -368,7 +368,7 @@ module.exports = (function() { ...@@ -368,7 +368,7 @@ module.exports = (function() {
var include = this.options.includeMap[key] var include = this.options.includeMap[key]
, association = include.association , association = include.association
, self = this , self = this
, accessor = Utils._.camelize(key) , accessor = Utils.camelize(key, true)
, childOptions , childOptions
, primaryKeyAttribute = include.model.primaryKeyAttribute , primaryKeyAttribute = include.model.primaryKeyAttribute
, isEmpty; , isEmpty;
...@@ -395,7 +395,7 @@ module.exports = (function() { ...@@ -395,7 +395,7 @@ module.exports = (function() {
} }
isEmpty = value && value[primaryKeyAttribute] === null; isEmpty = value && value[primaryKeyAttribute] === null;
accessor = Utils.singularize(accessor, self.Model.options.language); accessor = Utils.singularize(accessor);
self[accessor] = self.dataValues[accessor] = isEmpty ? null : include.model.build(value, childOptions); self[accessor] = self.dataValues[accessor] = isEmpty ? null : include.model.build(value, childOptions);
} else { } else {
isEmpty = value[0] && value[0][primaryKeyAttribute] === null; isEmpty = value[0] && value[0][primaryKeyAttribute] === null;
......
...@@ -36,7 +36,6 @@ module.exports = (function() { ...@@ -36,7 +36,6 @@ module.exports = (function() {
whereCollection: null, whereCollection: null,
schema: null, schema: null,
schemaDelimiter: '', schemaDelimiter: '',
language: 'en',
defaultScope: null, defaultScope: null,
scopes: null, scopes: null,
hooks: { hooks: {
...@@ -54,7 +53,7 @@ module.exports = (function() { ...@@ -54,7 +53,7 @@ module.exports = (function() {
this.underscored = this.underscored || this.underscoredAll; this.underscored = this.underscored || this.underscoredAll;
if (!this.options.tableName) { if (!this.options.tableName) {
this.tableName = this.options.freezeTableName ? name : Utils._.underscoredIf(Utils.pluralize(name, this.options.language), this.options.underscoredAll); this.tableName = this.options.freezeTableName ? name : Utils._.underscoredIf(Utils.pluralize(name), this.options.underscoredAll);
} else { } else {
this.tableName = this.options.tableName; this.tableName = this.options.tableName;
} }
...@@ -633,7 +632,7 @@ module.exports = (function() { ...@@ -633,7 +632,7 @@ module.exports = (function() {
* @param {Array<String>} [options.attributes] A list of the attributes that you want to select. To rename an attribute, you can pass an array, with two elements - the first is the name of the attribute in the DB (or some kind of expression such as `Sequelize.literal`, `Sequelize.fn` and so on), and the second is the name you want the attribute to have in the returned instance * @param {Array<String>} [options.attributes] A list of the attributes that you want to select. To rename an attribute, you can pass an array, with two elements - the first is the name of the attribute in the DB (or some kind of expression such as `Sequelize.literal`, `Sequelize.fn` and so on), and the second is the name you want the attribute to have in the returned instance
* @param {Array<Object|Model>} [options.include] A list of associations to eagerly load using a left join. Supported is either `{ include: [ Model1, Model2, ...]}` or `{ include: [{ model: Model1, as: 'Alias' }]}`. If your association are set up with an `as` (eg. `X.hasMany(Y, { as: 'Z }`, you need to specify Z in the as attribute when eager loading Y). * @param {Array<Object|Model>} [options.include] A list of associations to eagerly load using a left join. Supported is either `{ include: [ Model1, Model2, ...]}` or `{ include: [{ model: Model1, as: 'Alias' }]}`. If your association are set up with an `as` (eg. `X.hasMany(Y, { as: 'Z }`, you need to specify Z in the as attribute when eager loading Y).
* @param {Model} [options.include[].model] The model you want to eagerly load * @param {Model} [options.include[].model] The model you want to eagerly load
* @param {String} [options.include[].as] The alias of the relation, in case the model you want to eagerly load is aliassed. * @param {String} [options.include[].as] The alias of the relation, in case the model you want to eagerly load is aliassed. For `hasOne` / `belongsTo`, this should be the singular name, and for `hasMany`, it should be the plural
* @param {Object} [options.include[].where] Where clauses to apply to the child models. Note that this converts the eager load to an inner join, unless you explicitly set `required: true` * @param {Object} [options.include[].where] Where clauses to apply to the child models. Note that this converts the eager load to an inner join, unless you explicitly set `required: true`
* @param {Array<String>} [options.include[].attributes] A list of attributes to select from the child model * @param {Array<String>} [options.include[].attributes] A list of attributes to select from the child model
* @param {Boolean} [options.include[].required] If true, converts to an inner join, which means that the parent model will only be loaded if it has any matching children. True if `include.where` is set, false otherwise. * @param {Boolean} [options.include[].required] If true, converts to an inner join, which means that the parent model will only be loaded if it has any matching children. True if `include.where` is set, false otherwise.
...@@ -1771,7 +1770,7 @@ module.exports = (function() { ...@@ -1771,7 +1770,7 @@ module.exports = (function() {
include.through = Utils._.defaults(include.through || {}, { include.through = Utils._.defaults(include.through || {}, {
model: through, model: through,
as: Utils.singularize(through.tableName, through.options.language), as: Utils.singularize(through.tableName),
association: { association: {
isSingleAssociation: true isSingleAssociation: true
}, },
......
...@@ -128,8 +128,7 @@ module.exports = (function() { ...@@ -128,8 +128,7 @@ module.exports = (function() {
replication: false, replication: false,
ssl: undefined, ssl: undefined,
pool: {}, pool: {},
quoteIdentifiers: true, quoteIdentifiers: true
language: 'en'
}, options || {}); }, options || {});
if (this.options.dialect === 'postgresql') { if (this.options.dialect === 'postgresql') {
...@@ -349,6 +348,10 @@ module.exports = (function() { ...@@ -349,6 +348,10 @@ module.exports = (function() {
* @param {Boolean} [options.underscoredAll=false] Converts camelCased model names to underscored tablenames if true * @param {Boolean} [options.underscoredAll=false] Converts camelCased model names to underscored tablenames if true
* @param {Boolean} [options.freezeTableName=false] If freezeTableName is true, sequelize will not try to alter the DAO name to get the table name. Otherwise, the dao name will be pluralized * @param {Boolean} [options.freezeTableName=false] If freezeTableName is true, sequelize will not try to alter the DAO name to get the table name. Otherwise, the dao name will be pluralized
* @param {Boolean} [options.freezeAssociations=false] If freezeAssociations is true, sequelize will not try to alter the DAO name to get the table name of the associated tables. Otherwise, the dao name will be pluralized * @param {Boolean} [options.freezeAssociations=false] If freezeAssociations is true, sequelize will not try to alter the DAO name to get the table name of the associated tables. Otherwise, the dao name will be pluralized
* @param {Object} [options.name] An object with the attributes, `singular`, `plural`, and `verbatim`, that are used when this model is associated to others.
* @param {String} [options.name.singular=inflection.singularize(modelName)]
* @param {String} [options.name.plural=inflection.pluralize(modelName)]
* @param {Boolean} [options.name.verbatim=false] Controls whether the names should always be used verbatim, i.e. no upper- / lower casing applied. If false, the first letter will be upper cased for getter / setter methods such as `getTasks` and `addTask`
* @param {String|Boolean} [options.createdAt] Override the name of the createdAt column if a string is provided, or disable it if false. Timestamps must be true * @param {String|Boolean} [options.createdAt] Override the name of the createdAt column if a string is provided, or disable it if false. Timestamps must be true
* @param {String|Boolean} [options.updatedAt] Override the name of the updatedAt column if a string is provided, or disable it if false. Timestamps must be true * @param {String|Boolean} [options.updatedAt] Override the name of the updatedAt column if a string is provided, or disable it if false. Timestamps must be true
* @param {String|Boolean} [options.deletedAt] Override the name of the deletedAt column if a string is provided, or disable it if false. Timestamps must be true * @param {String|Boolean} [options.deletedAt] Override the name of the deletedAt column if a string is provided, or disable it if false. Timestamps must be true
...@@ -382,8 +385,15 @@ module.exports = (function() { ...@@ -382,8 +385,15 @@ module.exports = (function() {
}); });
} }
options = Utils._.merge({
name: {
plural: Utils.inflection.pluralize(modelName),
singular: Utils.inflection.singularize(modelName),
verbatim: false
}
}, options);
options.omitNull = globalOptions.omitNull; options.omitNull = globalOptions.omitNull;
options.language = globalOptions.language;
// if you call "define" multiple times for the same modelName, do not clutter the factory // if you call "define" multiple times for the same modelName, do not clutter the factory
if (this.isDefined(modelName)) { if (this.isDefined(modelName)) {
......
...@@ -4,26 +4,26 @@ var util = require('util') ...@@ -4,26 +4,26 @@ var util = require('util')
, DataTypes = require('./data-types') , DataTypes = require('./data-types')
, SqlString = require('./sql-string') , SqlString = require('./sql-string')
, lodash = require('lodash') , lodash = require('lodash')
, _string = require('underscore.string')
, ParameterValidator = require('./utils/parameter-validator') , ParameterValidator = require('./utils/parameter-validator')
, inflection = require('inflection')
, uuid = require('node-uuid'); , uuid = require('node-uuid');
var Utils = module.exports = { var Utils = module.exports = {
inflection: inflection,
_: (function() { _: (function() {
var _ = lodash var _ = lodash;
, _s = _string;
_.mixin(_s.exports());
_.mixin({ _.mixin({
includes: _s.include, includes: function(str, needle){
lowercaseFirst: function (s) { if (needle === '') return true;
return s[0].toLowerCase() + s.slice(1); if (str === null) return false;
return String(str).indexOf(needle) !== -1;
}, },
camelizeIf: function(string, condition) { camelizeIf: function(string, condition) {
var result = string; var result = string;
if (condition) { if (condition) {
result = _.camelize(string); result = Utils.camelize(string);
} }
return result; return result;
...@@ -32,7 +32,7 @@ var Utils = module.exports = { ...@@ -32,7 +32,7 @@ var Utils = module.exports = {
var result = string; var result = string;
if (condition) { if (condition) {
result = _.underscored(string); result = inflection.underscore(string);
} }
return result; return result;
...@@ -57,6 +57,15 @@ var Utils = module.exports = { ...@@ -57,6 +57,15 @@ var Utils = module.exports = {
return _; return _;
})(), })(),
lowercaseFirst: function (s) {
return s[0].toLowerCase() + s.slice(1);
},
uppercaseFirst: function (s) {
return s[0].toUpperCase() + s.slice(1);
},
camelize: function(str){
return str.trim().replace(/[-_\s]+(.)?/g, function(match, c){ return c.toUpperCase(); });
},
format: function(arr, dialect) { format: function(arr, dialect) {
var timeZone = null; var timeZone = null;
// Make a clone of the array beacuse format modifies the passed args // Make a clone of the array beacuse format modifies the passed args
...@@ -364,11 +373,17 @@ var Utils = module.exports = { ...@@ -364,11 +373,17 @@ var Utils = module.exports = {
}, },
singularize: function(s, language) { singularize: function(s, language) {
return Utils.Lingo[language || 'en'].isSingular(s) ? s : Utils.Lingo[language || 'en'].singularize(s); if (language) {
throw new Error('no language please!');
}
return inflection.singularize(s);
}, },
pluralize: function(s, language) { pluralize: function(s, language) {
return Utils.Lingo[language || 'en'].isPlural(s) ? s : Utils.Lingo[language || 'en'].pluralize(s); if (language) {
throw new Error('no language please!');
}
return inflection.pluralize(s);
}, },
removeCommentsFromFunctionString: function(s) { removeCommentsFromFunctionString: function(s) {
......
...@@ -40,17 +40,17 @@ ...@@ -40,17 +40,17 @@
"url": "https://github.com/sequelize/sequelize/issues" "url": "https://github.com/sequelize/sequelize/issues"
}, },
"dependencies": { "dependencies": {
"lodash": "~2.4.0",
"underscore.string": "~2.3.0",
"lingo": "~0.0.5",
"validator": "~3.2.0",
"moment": "~2.5.0",
"dottie": "0.2.4", "dottie": "0.2.4",
"toposort-class": "~0.3.0",
"generic-pool": "2.0.4", "generic-pool": "2.0.4",
"sql": "~0.35.0", "inflection": "^1.3.8",
"lodash": "~2.4.0",
"moment": "~2.5.0",
"node-uuid": "~1.4.1",
"sequelize-bluebird": "git://github.com/sequelize/bluebird.git#9df139af53c5d346ffd38df30fc9dc60c62a9c98", "sequelize-bluebird": "git://github.com/sequelize/bluebird.git#9df139af53c5d346ffd38df30fc9dc60c62a9c98",
"node-uuid": "~1.4.1" "sql": "~0.35.0",
"toposort-class": "~0.3.0",
"underscore.string": "~2.3.0",
"validator": "~3.2.0"
}, },
"devDependencies": { "devDependencies": {
"sqlite3": "~2.1.12", "sqlite3": "~2.1.12",
......
...@@ -523,7 +523,7 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() { ...@@ -523,7 +523,7 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() {
allowNull: false allowNull: false
} }
}) })
Profile.belongsTo(User, { foreignKey: Profile.rawAttributes.user_id}) Profile.belongsTo(User, { foreignKey: Profile.rawAttributes.user_id})
expect(Profile.rawAttributes.user_id).to.be.defined expect(Profile.rawAttributes.user_id).to.be.defined
......
...@@ -484,7 +484,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -484,7 +484,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
allowNull: false allowNull: false
} }
}) })
User.hasOne(Profile, { foreignKey: Profile.rawAttributes.user_id}) User.hasOne(Profile, { foreignKey: Profile.rawAttributes.user_id})
expect(Profile.rawAttributes.user_id).to.be.defined expect(Profile.rawAttributes.user_id).to.be.defined
...@@ -509,7 +509,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -509,7 +509,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
it('should only generate one foreign key', function () { it('should only generate one foreign key', function () {
var Orders = this.sequelize.define('Orders', {}, {timestamps: false}) var Orders = this.sequelize.define('Orders', {}, {timestamps: false})
, InternetOrders = this.sequelize.define('InternetOrders', {}, {timestamps: false}) , InternetOrders = this.sequelize.define('InternetOrders', {}, {timestamps: false})
InternetOrders.belongsTo(Orders, { InternetOrders.belongsTo(Orders, {
foreignKeyConstraint: true foreignKeyConstraint: true
}); });
......
"use strict";
/* jshint camelcase: false, expr: true */ /* jshint camelcase: false, expr: true */
var chai = require('chai') var chai = require('chai')
, expect = chai.expect , expect = chai.expect
, Support = require(__dirname + '/../support') , Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types") , DataTypes = require(__dirname + "/../../lib/data-types")
, _ = require('lodash') , Sequelize = require(__dirname + "/../../index")
, Promise = Sequelize.Promise
, _ = require('lodash');
chai.config.includeStack = true chai.config.includeStack = true;
describe(Support.getTestDialectTeaser("Self"), function() { describe(Support.getTestDialectTeaser("Self"), function() {
it('supports freezeTableName', function (done) { it('supports freezeTableName', function () {
var Group = this.sequelize.define('Group', { var Group = this.sequelize.define('Group', {}, {
}, {
tableName: 'user_group', tableName: 'user_group',
timestamps: false, timestamps: false,
underscored: true, underscored: true,
...@@ -19,60 +21,50 @@ describe(Support.getTestDialectTeaser("Self"), function() { ...@@ -19,60 +21,50 @@ describe(Support.getTestDialectTeaser("Self"), function() {
}); });
Group.belongsTo(Group, { as: 'Parent', foreignKey: 'parent_id' }); Group.belongsTo(Group, { as: 'Parent', foreignKey: 'parent_id' });
Group.sync({force: true}).done(function (err) { return Group.sync({force: true}).then(function () {
expect(err).not.to.be.ok return Group.findAll({
Group.findAll({
include: [{ include: [{
model: Group, model: Group,
as: 'Parent' as: 'Parent'
}] }]
}).done(function (err) { });
expect(err).not.to.be.ok });
done() });
})
}) it('can handle 1:m associations', function () {
})
it('can handle 1:m associations', function (done) {
var Person = this.sequelize.define('Person', { name: DataTypes.STRING }); var Person = this.sequelize.define('Person', { name: DataTypes.STRING });
Person.hasMany(Person, { as: 'Children', foreignKey: 'parent_id'}); Person.hasMany(Person, { as: 'Children', foreignKey: 'parent_id'});
expect(Person.rawAttributes.parent_id).to.be.ok expect(Person.rawAttributes.parent_id).to.be.ok;
this.sequelize.sync({force: true}).done(function () { return this.sequelize.sync({force: true}).then(function () {
Person.create({ name: 'Mary' }).complete(function(err, mary) { return Promise.all([
expect(err).to.not.be.ok Person.create({ name: 'Mary' }),
Person.create({ name: 'John' }).complete(function(err, john) { Person.create({ name: 'John' }),
expect(err).to.not.be.ok Person.create({ name: 'Chris' })
Person.create({ name: 'Chris' }).complete(function(err, chris) { ]);
expect(err).to.not.be.ok }).spread(function (mary, john, chris) {
mary.setChildren([john, chris]).done(function (err) { return mary.setChildren([john, chris]);
expect(err).not.to.be.ok
done();
});
});
});
});
}); });
}); });
it('can handle n:m associations', function() { it('can handle n:m associations', function() {
var self = this var self = this;
var Person = this.sequelize.define('Person', { name: DataTypes.STRING }) var Person = this.sequelize.define('Person', { name: DataTypes.STRING });
Person.hasMany(Person, { as: 'Parents', through: 'Family' }) Person.hasMany(Person, { as: 'Parents', through: 'Family' });
Person.hasMany(Person, { as: 'Childs', through: 'Family' }) Person.hasMany(Person, { as: 'Childs', through: 'Family' });
var foreignIdentifiers = _.map(_.values(Person.associations), 'foreignIdentifier') var foreignIdentifiers = _.map(_.values(Person.associations), 'foreignIdentifier');
var rawAttributes = _.keys(this.sequelize.models.Family.rawAttributes) var rawAttributes = _.keys(this.sequelize.models.Family.rawAttributes);
expect(foreignIdentifiers.length).to.equal(2) expect(foreignIdentifiers.length).to.equal(2);
expect(rawAttributes.length).to.equal(4) expect(rawAttributes.length).to.equal(4);
expect(foreignIdentifiers).to.have.members([ 'PersonId', 'ChildId' ]) expect(foreignIdentifiers).to.have.members([ 'PersonId', 'ChildId' ]);
expect(rawAttributes).to.have.members([ 'createdAt', 'updatedAt', 'PersonId', 'ChildId' ]) expect(rawAttributes).to.have.members([ 'createdAt', 'updatedAt', 'PersonId', 'ChildId' ]);
return this.sequelize.sync({ force: true }).then(function() { return this.sequelize.sync({ force: true }).then(function() {
return self.sequelize.Promise.all([ return self.sequelize.Promise.all([
...@@ -81,17 +73,17 @@ describe(Support.getTestDialectTeaser("Self"), function() { ...@@ -81,17 +73,17 @@ describe(Support.getTestDialectTeaser("Self"), function() {
Person.create({ name: 'Chris' }) Person.create({ name: 'Chris' })
]).spread(function (mary, john, chris) { ]).spread(function (mary, john, chris) {
return mary.setParents([john]).then(function() { return mary.setParents([john]).then(function() {
return chris.addParent(john) return chris.addParent(john);
}).then(function() { }).then(function() {
return john.getChilds() return john.getChilds();
}).then(function(children) { }).then(function(children) {
expect(_.map(children, 'id')).to.have.members([mary.id, chris.id]) expect(_.map(children, 'id')).to.have.members([mary.id, chris.id]);
}) });
}) });
}) });
}) });
it('can handle n:m associations with pre-defined through table', function(done) { it('can handle n:m associations with pre-defined through table', function() {
var Person = this.sequelize.define('Person', { name: DataTypes.STRING }); var Person = this.sequelize.define('Person', { name: DataTypes.STRING });
var Family = this.sequelize.define('Family', { var Family = this.sequelize.define('Family', {
preexisting_child: { preexisting_child: {
...@@ -107,49 +99,46 @@ describe(Support.getTestDialectTeaser("Self"), function() { ...@@ -107,49 +99,46 @@ describe(Support.getTestDialectTeaser("Self"), function() {
Person.hasMany(Person, { as: 'Parents', through: Family, foreignKey: 'preexisting_child' }); Person.hasMany(Person, { as: 'Parents', through: Family, foreignKey: 'preexisting_child' });
Person.hasMany(Person, { as: 'Children', through: Family, foreignKey: 'preexisting_parent' }); Person.hasMany(Person, { as: 'Children', through: Family, foreignKey: 'preexisting_parent' });
var foreignIdentifiers = _.map(_.values(Person.associations), 'foreignIdentifier') var foreignIdentifiers = _.map(_.values(Person.associations), 'foreignIdentifier');
var rawAttributes = _.keys(Family.rawAttributes) var rawAttributes = _.keys(Family.rawAttributes);
expect(foreignIdentifiers.length).to.equal(2) expect(foreignIdentifiers.length).to.equal(2);
expect(rawAttributes.length).to.equal(2) expect(rawAttributes.length).to.equal(2);
expect(foreignIdentifiers).to.have.members([ 'preexisting_parent', 'preexisting_child' ]); expect(foreignIdentifiers).to.have.members([ 'preexisting_parent', 'preexisting_child' ]);
expect(rawAttributes).to.have.members([ 'preexisting_parent', 'preexisting_child' ]); expect(rawAttributes).to.have.members([ 'preexisting_parent', 'preexisting_child' ]);
this.sequelize.sync({ force: true }).complete(function() { return this.sequelize.sync({ force: true }).bind(this).then(function() {
Person.create({ name: 'Mary' }).complete(function(err, mary) { return Promise.all([
expect(err).to.not.be.ok Person.create({ name: 'Mary' }),
Person.create({ name: 'John' }).complete(function(err, john) { Person.create({ name: 'John' }),
expect(err).to.not.be.ok Person.create({ name: 'Chris' })
Person.create({ name: 'Chris' }).complete(function(err, chris) { ]);
expect(err).to.not.be.ok }).spread(function (mary, john, chris) {
mary.setParents([john]).done(function (err) { this.mary = mary;
expect(err).to.not.be.ok this.chris = chris;
mary.addParent(chris).on('sql', function(sql) { this.john = john;
if (sql.match(/INSERT/)) { return mary.setParents([john]).on('sql', function(sql) {
expect(sql).to.have.string('preexisting_child'); if (sql.match(/INSERT/)) {
expect(sql).to.have.string('preexisting_parent'); expect(sql).to.have.string('preexisting_child');
} expect(sql).to.have.string('preexisting_parent');
}).complete(function(err) { }
expect(err).to.not.be.ok });
john.getChildren().on('sql', function(sql) { }).then(function () {
var whereClause = sql.split('WHERE')[1]; // look only in the whereClause return this.mary.addParent(this.chris).on('sql', function(sql) {
expect(whereClause).to.have.string('preexisting_child'); if (sql.match(/INSERT/)) {
expect(whereClause).to.have.string('preexisting_parent'); expect(sql).to.have.string('preexisting_child');
}).complete(function(err, children) { expect(sql).to.have.string('preexisting_parent');
expect(_.map(children, 'id')).to.have.members([mary.id]); }
done() });
}) }).then(function () {
}) return this.john.getChildren().on('sql', function(sql) {
}).on('sql', function(sql) { var whereClause = sql.split('WHERE')[1]; // look only in the whereClause
if (sql.match(/INSERT/)) { expect(whereClause).to.have.string('preexisting_child');
expect(sql).to.have.string('preexisting_child'); expect(whereClause).to.have.string('preexisting_parent');
expect(sql).to.have.string('preexisting_parent'); });
} }).then(function(children) {
}); expect(_.map(children, 'id')).to.have.members([this.mary.id]);
}) });
}) });
}) });
})
})
})
\ No newline at end of file
...@@ -902,7 +902,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -902,7 +902,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done() done()
}) })
}) })
}) })
}) })
it('includes all associations', function(done) { it('includes all associations', function(done) {
...@@ -912,7 +912,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -912,7 +912,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(countries[0]).to.exist expect(countries[0]).to.exist
expect(countries[0].continent).to.exist expect(countries[0].continent).to.exist
expect(countries[0].industries).to.exist expect(countries[0].industries).to.exist
expect(countries[0].persons).to.exist expect(countries[0].people).to.exist
expect(countries[0].residents).to.exist expect(countries[0].residents).to.exist
done() done()
}) })
...@@ -925,7 +925,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -925,7 +925,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(countries[0]).to.exist expect(countries[0]).to.exist
expect(countries[0].continent).to.exist expect(countries[0].continent).to.exist
expect(countries[0].industries).not.to.exist expect(countries[0].industries).not.to.exist
expect(countries[0].persons).not.to.exist expect(countries[0].people).not.to.exist
expect(countries[0].residents).not.to.exist expect(countries[0].residents).not.to.exist
done() done()
}) })
...@@ -937,10 +937,10 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -937,10 +937,10 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(countries).to.exist expect(countries).to.exist
expect(countries[0]).to.exist expect(countries[0]).to.exist
expect(countries[0].industries).to.exist expect(countries[0].industries).to.exist
expect(countries[0].persons).to.exist expect(countries[0].people).to.exist
expect(countries[0].persons[0]).to.exist expect(countries[0].people[0]).to.exist
expect(countries[0].persons[0].name).not.to.be.undefined expect(countries[0].people[0].name).not.to.be.undefined
expect(countries[0].persons[0].lastName).to.be.undefined expect(countries[0].people[0].lastName).to.be.undefined
expect(countries[0].residents).to.exist expect(countries[0].residents).to.exist
expect(countries[0].residents[0]).to.exist expect(countries[0].residents[0]).to.exist
expect(countries[0].residents[0].name).not.to.be.undefined expect(countries[0].residents[0].name).not.to.be.undefined
...@@ -968,7 +968,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -968,7 +968,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(continents[0].countries).to.exist expect(continents[0].countries).to.exist
expect(continents[0].countries[0]).to.exist expect(continents[0].countries[0]).to.exist
expect(continents[0].countries[0].industries).to.exist expect(continents[0].countries[0].industries).to.exist
expect(continents[0].countries[0].persons).to.exist expect(continents[0].countries[0].people).to.exist
expect(continents[0].countries[0].residents).to.exist expect(continents[0].countries[0].residents).to.exist
expect(continents[0].countries[0].continent).not.to.exist expect(continents[0].countries[0].continent).not.to.exist
done() done()
...@@ -1081,9 +1081,9 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1081,9 +1081,9 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(continents[0].countries).to.exist expect(continents[0].countries).to.exist
expect(continents[0].countries[0]).to.exist expect(continents[0].countries[0]).to.exist
expect(continents[0].countries[0].name).to.equal(params[2]) expect(continents[0].countries[0].name).to.equal(params[2])
expect(continents[0].countries[0].persons).to.exist expect(continents[0].countries[0].people).to.exist
expect(continents[0].countries[0].persons[0]).to.exist expect(continents[0].countries[0].people[0]).to.exist
expect(continents[0].countries[0].persons[0].name).to.equal(params[3]) expect(continents[0].countries[0].people[0].name).to.equal(params[3])
callback() callback()
}) })
}, function() {done()}) }, function() {done()})
...@@ -1199,7 +1199,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1199,7 +1199,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
self.Country.findAll({ self.Country.findAll({
include: [ self.Industry ], include: [ self.Industry ],
order: [ order: [
[ self.Industry, 'name', params[0] ] [ self.Industry, 'name', params[0] ]
], ],
limit: 3 limit: 3
}).done(function(err, countries) { }).done(function(err, countries) {
...@@ -1448,7 +1448,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1448,7 +1448,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
this.sequelize.sync().done(function (err) { this.sequelize.sync().done(function (err) {
expect(err).not.be.ok expect(err).not.be.ok
// Add some data // Add some data
Citizen.create({ name: 'Alice' }).done(function (err, alice) { Citizen.create({ name: 'Alice' }).done(function (err, alice) {
expect(err).not.be.ok expect(err).not.be.ok
...@@ -1460,7 +1460,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1460,7 +1460,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
expect(err).not.be.ok expect(err).not.be.ok
election.setVoters([alice, bob]).done(function (err) { election.setVoters([alice, bob]).done(function (err) {
expect(err).not.be.ok expect(err).not.be.ok
var criteria = { var criteria = {
offset: 5, offset: 5,
limit: 1, limit: 1,
...@@ -1532,4 +1532,4 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1532,4 +1532,4 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
}) })
}) })
\ No newline at end of file
var chai = require('chai')
, expect = chai.expect
, Sequelize = require(__dirname + '/../index')
, Support = require(__dirname + '/support')
chai.config.includeStack = true
describe(Support.getTestDialectTeaser("Language Util"), function() {
beforeEach(function(done) {
this.sequelize.options.language = 'es'
done()
})
afterEach(function(done) {
this.sequelize.options.language = 'en'
done()
})
describe("Plural", function(){
it("should rename tables to their plural form...", function(done){
var self = this
, table = self.sequelize.define('arbol', {name: Sequelize.STRING})
, table2 = self.sequelize.define('androide', {name: Sequelize.STRING})
expect(table.tableName).to.equal('arboles')
expect(table2.tableName).to.equal('androides')
done()
})
it("should be able to pluralize/singularize associations...", function(done){
var self = this
, table = self.sequelize.define('arbol', {name: Sequelize.STRING})
, table2 = self.sequelize.define('androide', {name: Sequelize.STRING})
, table3 = self.sequelize.define('hombre', {name: Sequelize.STRING})
table.hasOne(table2)
table2.belongsTo(table)
table3.hasMany(table2)
expect(table.associations.androide.identifier).to.equal('arbolId')
expect(table2.associations.arbol).to.exist
expect(table3.associations.androideshombres).to.exist
done()
})
})
})
"use strict";
var chai = require('chai') var chai = require('chai')
, spies = require('chai-spies') , spies = require('chai-spies')
, expect = chai.expect , expect = chai.expect
, Utils = require(__dirname + '/../lib/utils') , Utils = require(__dirname + '/../lib/utils')
, Support = require(__dirname + '/support') , Support = require(__dirname + '/support');
chai.use(spies) chai.use(spies);
chai.config.includeStack = true chai.config.includeStack = true;
describe(Support.getTestDialectTeaser("Utils"), function() { describe(Support.getTestDialectTeaser("Utils"), function() {
describe('removeCommentsFromFunctionString', function() { describe('removeCommentsFromFunctionString', function() {
it("removes line comments at the start of a line", function(done) { it("removes line comments at the start of a line", function() {
var functionWithLineComments = function() { var functionWithLineComments = function() {
// noot noot // noot noot
} };
var string = functionWithLineComments.toString() var string = functionWithLineComments.toString()
, result = Utils.removeCommentsFromFunctionString(string) , result = Utils.removeCommentsFromFunctionString(string);
expect(result).not.to.match(/.*noot.*/) expect(result).not.to.match(/.*noot.*/);
done() });
})
it("removes lines comments in the middle of a line", function(done) { it("removes lines comments in the middle of a line", function() {
var functionWithLineComments = function() { var functionWithLineComments = function() {
alert(1) // noot noot alert(1); // noot noot
} };
var string = functionWithLineComments.toString() var string = functionWithLineComments.toString()
, result = Utils.removeCommentsFromFunctionString(string) , result = Utils.removeCommentsFromFunctionString(string);
expect(result).not.to.match(/.*noot.*/) expect(result).not.to.match(/.*noot.*/);
done() });
})
it("removes range comments", function(done) { it("removes range comments", function() {
var s = function() { var s = function() {
alert(1) /* alert(1); /*
noot noot noot noot
*/ */
alert(2) /* alert(2); /*
foo foo
*/ */
}.toString() }.toString();
var result = Utils.removeCommentsFromFunctionString(s) var result = Utils.removeCommentsFromFunctionString(s);
expect(result).not.to.match(/.*noot.*/) expect(result).not.to.match(/.*noot.*/);
expect(result).not.to.match(/.*foo.*/) expect(result).not.to.match(/.*foo.*/);
expect(result).to.match(/.*alert\(2\).*/) expect(result).to.match(/.*alert\(2\).*/);
done() });
}) });
})
describe('argsArePrimaryKeys', function() { describe('argsArePrimaryKeys', function() {
it("doesn't detect primary keys if primareyKeys and values have different lengths", function(done) { it("doesn't detect primary keys if primareyKeys and values have different lengths", function() {
expect(Utils.argsArePrimaryKeys([1,2,3], [1])).to.be.false expect(Utils.argsArePrimaryKeys([1,2,3], [1])).to.be.false;
done() });
})
it("doesn't detect primary keys if primary keys are hashes or arrays", function() {
it("doesn't detect primary keys if primary keys are hashes or arrays", function(done) { expect(Utils.argsArePrimaryKeys([[]], [1])).to.be.false;
expect(Utils.argsArePrimaryKeys([[]], [1])).to.be.false });
done()
}) it('detects primary keys if length is correct and data types are matching', function() {
expect(Utils.argsArePrimaryKeys([1,2,3], ["INTEGER", "INTEGER", "INTEGER"])).to.be.true;
it('detects primary keys if length is correct and data types are matching', function(done) { });
expect(Utils.argsArePrimaryKeys([1,2,3], ["INTEGER", "INTEGER", "INTEGER"])).to.be.true
done() it("detects primary keys if primary keys are dates and lengths are matching", function() {
}) expect(Utils.argsArePrimaryKeys([new Date()], ['foo'])).to.be.true;
});
it("detects primary keys if primary keys are dates and lengths are matching", function(done) { });
expect(Utils.argsArePrimaryKeys([new Date()], ['foo'])).to.be.true
done()
})
})
describe('underscore', function() { describe('underscore', function() {
describe('underscoredIf', function() { describe('underscoredIf', function() {
it('is defined', function(done) { it('is defined', function() {
expect(Utils._.underscoredIf).to.be.ok expect(Utils._.underscoredIf).to.be.ok;
done() });
})
it('underscores if second param is true', function() {
it('underscores if second param is true', function(done) { expect(Utils._.underscoredIf('fooBar', true)).to.equal('foo_bar');
expect(Utils._.underscoredIf('fooBar', true)).to.equal('foo_bar') });
done()
}) it("doesn't underscore if second param is false", function() {
expect(Utils._.underscoredIf('fooBar', false)).to.equal('fooBar');
it("doesn't underscore if second param is false", function(done) { });
expect(Utils._.underscoredIf('fooBar', false)).to.equal('fooBar') });
done()
})
})
describe('camelizeIf', function() { describe('camelizeIf', function() {
it('is defined', function(done) { it('is defined', function() {
expect(Utils._.camelizeIf).to.be.ok expect(Utils._.camelizeIf).to.be.ok;
done() });
})
it('camelizes if second param is true', function() {
it('camelizes if second param is true', function(done) { expect(Utils._.camelizeIf('foo_bar', true)).to.equal('fooBar');
expect(Utils._.camelizeIf('foo_bar', true)).to.equal('fooBar') });
done()
}) it("doesn't camelize if second param is false", function() {
expect(Utils._.underscoredIf('fooBar', true)).to.equal('foo_bar');
it("doesn't camelize if second param is false", function(done) { });
expect(Utils._.underscoredIf('fooBar', true)).to.equal('foo_bar') });
done() });
})
})
})
describe('format', function() { describe('format', function() {
it('should format where clause correctly when the value is truthy', function(done) { it('should format where clause correctly when the value is truthy', function() {
var where = ['foo = ?', 1] var where = ['foo = ?', 1];
expect(Utils.format(where)).to.equal('foo = 1') expect(Utils.format(where)).to.equal('foo = 1');
done() });
})
it('should format where clause correctly when the value is false', function() {
it('should format where clause correctly when the value is false', function(done) { var where = ['foo = ?', 0];
var where = ['foo = ?', 0] expect(Utils.format(where)).to.equal('foo = 0');
expect(Utils.format(where)).to.equal('foo = 0') });
done() });
})
})
describe('validateParameter', function() { describe('validateParameter', function() {
describe('method signature', function() { describe('method signature', function() {
it('throws an error if the value is not defined', function() { it('throws an error if the value is not defined', function() {
expect(function() { expect(function() {
Utils.validateParameter() Utils.validateParameter();
}).to.throw('No value has been passed.') }).to.throw('No value has been passed.');
}) });
it('does not throw an error if the value is not defined and the parameter is optional', function() { it('does not throw an error if the value is not defined and the parameter is optional', function() {
expect(function() { expect(function() {
Utils.validateParameter(undefined, Object, { optional: true }) Utils.validateParameter(undefined, Object, { optional: true });
}).to.not.throw() }).to.not.throw();
}) });
it('throws an error if the expectation is not defined', function() { it('throws an error if the expectation is not defined', function() {
expect(function() { expect(function() {
Utils.validateParameter(1) Utils.validateParameter(1);
}).to.throw('No expectation has been passed.') }).to.throw('No expectation has been passed.');
}) });
}) });
describe('expectation', function() { describe('expectation', function() {
it('uses the instanceof method if the expectation is a class', function() { it('uses the instanceof method if the expectation is a class', function() {
expect(Utils.validateParameter(new Number(1), Number)).to.be.true expect(Utils.validateParameter(new Number(1), Number)).to.be.true;
}) });
}) });
describe('failing expectations', function() { describe('failing expectations', function() {
it('throws an error if the expectation does not match', function() { it('throws an error if the expectation does not match', function() {
expect(function() { expect(function() {
Utils.validateParameter(1, String) Utils.validateParameter(1, String);
}).to.throw(/The parameter.*is no.*/) }).to.throw(/The parameter.*is no.*/);
}) });
}) });
}) });
})
describe('inflection', function () {
it('works better than lingo ;)', function () {
expect(Utils.pluralize('buy')).to.equal('buys');
expect(Utils.pluralize('holiday')).to.equal('holidays');
expect(Utils.pluralize('days')).to.equal('days');
expect(Utils.pluralize('status')).to.equal('statuses');
expect(Utils.singularize('status')).to.equal('status');
});
});
});
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!