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

Commit ec4f8ef7 by durango

Merge pull request #538 from iamjochem/feature/doaGettersAndSetters

Added DOA Getters and Setters
2 parents 41c1b6ae b335eaa1
...@@ -75,11 +75,33 @@ module.exports = (function() { ...@@ -75,11 +75,33 @@ module.exports = (function() {
Util.inherits(this.DAO, DAO); Util.inherits(this.DAO, DAO);
this.DAO.prototype.rawAttributes = this.rawAttributes; this.DAO.prototype.rawAttributes = this.rawAttributes;
if (this.options.instanceMethods) { if (this.options.instanceMethods) {
Utils._.each(this.options.instanceMethods, function(fct, name) { Utils._.each(this.options.instanceMethods, function(fct, name) {
self.DAO.prototype[name] = fct self.DAO.prototype[name] = fct
}) })
} }
Utils._.each(['Get', 'Set'], function(type) {
var prop = type.toLowerCase(),
opt = prop + 'terMethods',
meth = '__define' + type + 'ter__',
funcs = Utils._.isObject(self.options[opt]) ? self.options[opt] : {}
;
Utils._.each(self.rawAttributes, function(attr, name) {
if (attr.hasOwnProperty(prop))
funcs[name] = attr[prop]
});
Utils._.each(funcs, function(fct, name) {
if (!Utils._.isFunction(fct))
throw new Error(type + 'ter for "' + name + '" is not a function.')
self.DAO.prototype[meth](name, fct);
})
})
this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes); this.DAO.prototype.attributes = Object.keys(this.DAO.prototype.rawAttributes);
this.DAO.prototype.booleanValues = []; this.DAO.prototype.booleanValues = [];
......
...@@ -7,28 +7,15 @@ module.exports = (function() { ...@@ -7,28 +7,15 @@ module.exports = (function() {
var DAO = function(values, options, isNewRecord) { var DAO = function(values, options, isNewRecord) {
var self = this var self = this
this.dataValues = {}
this.__options = options this.__options = options
this.hasPrimaryKeys = options.hasPrimaryKeys this.hasPrimaryKeys = options.hasPrimaryKeys
this.selectedValues = values this.selectedValues = values
this.__eagerlyLoadedAssociations = [] this.__eagerlyLoadedAssociations = []
initAttributes.call(this, values, isNewRecord) initAttributes.call(this, values, isNewRecord)
if (this.hasDefaultValues) {
Utils._.each(this.defaultValues, function (value, name) {
if (typeof self[name] === 'undefined') {
self.addAttribute(name, value());
}
})
} }
if (this.booleanValues.length) {
this.booleanValues.forEach(function (name) {
//transform integer 0,1 into boolean
self[name] = !!self[name];
});
}
}
Utils._.extend(DAO.prototype, Mixin.prototype) Utils._.extend(DAO.prototype, Mixin.prototype)
Object.defineProperty(DAO.prototype, 'sequelize', { Object.defineProperty(DAO.prototype, 'sequelize', {
...@@ -42,7 +29,7 @@ module.exports = (function() { ...@@ -42,7 +29,7 @@ module.exports = (function() {
Object.defineProperty(DAO.prototype, 'isDeleted', { Object.defineProperty(DAO.prototype, 'isDeleted', {
get: function() { get: function() {
var result = this.__options.timestamps && this.__options.paranoid var result = this.__options.timestamps && this.__options.paranoid
result = result && this[this.__options.underscored ? 'deleted_at' : 'deletedAt'] !== null result = result && this.dataValues[this.__options.underscored ? 'deleted_at' : 'deletedAt'] !== null
return result return result
} }
...@@ -54,7 +41,10 @@ module.exports = (function() { ...@@ -54,7 +41,10 @@ module.exports = (function() {
, self = this , self = this
this.attributes.concat(this.__eagerlyLoadedAssociations).forEach(function(attr) { this.attributes.concat(this.__eagerlyLoadedAssociations).forEach(function(attr) {
result[attr] = self[attr] result[attr] = self.dataValues.hasOwnProperty(attr)
? self.dataValues[attr]
: self[attr]
;
}) })
return result return result
...@@ -67,7 +57,7 @@ module.exports = (function() { ...@@ -67,7 +57,7 @@ module.exports = (function() {
, self = this , self = this
Utils._.each(this.__factory.primaryKeys, function(_, attr) { Utils._.each(this.__factory.primaryKeys, function(_, attr) {
result[attr] = self[attr] result[attr] = self.dataValues[attr]
}) })
return result return result
...@@ -85,13 +75,21 @@ module.exports = (function() { ...@@ -85,13 +75,21 @@ module.exports = (function() {
} }
primaryKeys.forEach(function(identifier) { primaryKeys.forEach(function(identifier) {
result[identifier] = self[identifier] result[identifier] = self.dataValues[identifier]
}) })
return result return result
} }
}) })
DAO.prototype.getDataValue = function(name) {
return this.dataValues && this.dataValues.hasOwnProperty(name) ? this.dataValues[name] : this[name]
}
DAO.prototype.setDataValue = function(name, value) {
this.dataValues[name] = value
}
// if an array with field names is passed to save() // if an array with field names is passed to save()
// only those fields will be updated // only those fields will be updated
DAO.prototype.save = function(fields) { DAO.prototype.save = function(fields) {
...@@ -111,9 +109,11 @@ module.exports = (function() { ...@@ -111,9 +109,11 @@ module.exports = (function() {
} }
} }
var tmpVals = self.values
fields.forEach(function(field) { fields.forEach(function(field) {
if (self.values[field] !== undefined) { if (tmpVals[field] !== undefined) {
values[field] = self.values[field] values[field] = tmpVals[field]
} }
}) })
} }
...@@ -146,8 +146,8 @@ module.exports = (function() { ...@@ -146,8 +146,8 @@ module.exports = (function() {
} }
} }
if (this.__options.timestamps && this.hasOwnProperty(updatedAtAttr)) { if (this.__options.timestamps && this.dataValues.hasOwnProperty(updatedAtAttr)) {
this[updatedAtAttr] = values[updatedAtAttr] = Utils.now() this.dataValues[updatedAtAttr] = values[updatedAtAttr] = Utils.now()
} }
var errors = this.validate() var errors = this.validate()
...@@ -301,7 +301,7 @@ module.exports = (function() { ...@@ -301,7 +301,7 @@ module.exports = (function() {
DAO.prototype.destroy = function() { DAO.prototype.destroy = function() {
if (this.__options.timestamps && this.__options.paranoid) { if (this.__options.timestamps && this.__options.paranoid) {
var attr = this.__options.underscored ? 'deleted_at' : 'deletedAt' var attr = this.__options.underscored ? 'deleted_at' : 'deletedAt'
this[attr] = new Date() this.dataValues[attr] = new Date()
return this.save() return this.save()
} else { } else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id
...@@ -362,7 +362,37 @@ module.exports = (function() { ...@@ -362,7 +362,37 @@ module.exports = (function() {
} }
DAO.prototype.addAttribute = function(attribute, value) { DAO.prototype.addAttribute = function(attribute, value) {
this[attribute] = value if (typeof this.dataValues[attribute] !== 'undefined')
return;
if (this.booleanValues.length && this.booleanValues.indexOf(attribute) !== -1) // transform integer 0,1 into boolean
value = !!value;
var has = (function(o) {
var predef = Object.getOwnPropertyDescriptor(o, attribute);
if (predef && predef.hasOwnProperty('value'))
return true; // true here means 'this property exist as a simple value property, do not place setters or getters at all'
return {
get: (predef && predef.hasOwnProperty('get') ? predef.get : null) || o.__lookupGetter__(attribute),
set: (predef && predef.hasOwnProperty('set') ? predef.set : null) || o.__lookupSetter__(attribute)
};
})(this);
// @ node-v0.8.19:
// calling __defineGetter__ destroys any previously defined setters for the attribute in
// question *if* that property setter was defined on the object's prototype (which is what
// we do in dao-factory) ... therefore we need to [re]define both the setter and getter
// here with either the function that already existed OR the default/automatic definition
//
// (the same is true for __defineSetter and 'prototype' getters)
if (has !== true) {
this.__defineGetter__(attribute, has.get || function() { return this.dataValues[attribute]; });
this.__defineSetter__(attribute, has.set || function(v) { this.dataValues[attribute] = v; });
}
this[attribute] = value;
} }
DAO.prototype.setValidators = function(attribute, validators) { DAO.prototype.setValidators = function(attribute, validators) {
...@@ -376,8 +406,13 @@ module.exports = (function() { ...@@ -376,8 +406,13 @@ module.exports = (function() {
// private // private
var initAttributes = function(values, isNewRecord) { var initAttributes = function(values, isNewRecord) {
// set id to null if not passed as value, a newly created dao has no id
var defaults = this.hasPrimaryKeys ? {} : { id: null },
attrs = {},
key;
// add all passed values to the dao and store the attribute names in this.attributes // add all passed values to the dao and store the attribute names in this.attributes
for (var key in values) { for (key in values) {
if (values.hasOwnProperty(key)) { if (values.hasOwnProperty(key)) {
if (typeof values[key] === "string" && !!this.__factory && !!this.__factory.rawAttributes[key] && !!this.__factory.rawAttributes[key].type && !!this.__factory.rawAttributes[key].type.type && this.__factory.rawAttributes[key].type.type === DataTypes.HSTORE.type) { if (typeof values[key] === "string" && !!this.__factory && !!this.__factory.rawAttributes[key] && !!this.__factory.rawAttributes[key].type && !!this.__factory.rawAttributes[key].type.type && this.__factory.rawAttributes[key].type.type === DataTypes.HSTORE.type) {
values[key] = this.QueryInterface.QueryGenerator.toHstore(values[key]) values[key] = this.QueryInterface.QueryGenerator.toHstore(values[key])
...@@ -387,10 +422,6 @@ module.exports = (function() { ...@@ -387,10 +422,6 @@ module.exports = (function() {
} }
} }
// set id to null if not passed as value
// a newly created dao has no id
var defaults = this.hasPrimaryKeys ? {} : { id: null }
if (this.__options.timestamps && isNewRecord) { if (this.__options.timestamps && isNewRecord) {
defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = Utils.now() defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = Utils.now()
defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = Utils.now() defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = Utils.now()
...@@ -398,17 +429,38 @@ module.exports = (function() { ...@@ -398,17 +429,38 @@ module.exports = (function() {
if (this.__options.paranoid) { if (this.__options.paranoid) {
defaults[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = null defaults[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = null
} }
if (this.hasDefaultValues) {
Utils._.each(this.defaultValues, function(valueFn, key) {
if (!defaults.hasOwnProperty(key))
defaults[key] = valueFn()
})
}
} }
if (Utils._.size(defaults)) { if (Utils._.size(defaults)) {
for (var attr in defaults) { for (key in defaults) {
var value = defaults[attr] attrs[key] = Utils.toDefaultValue(defaults[key])
}
}
Utils._.each(this.attributes, function(key) {
if (!attrs.hasOwnProperty(key)) {
attrs[key] = undefined
}
})
if (!this.hasOwnProperty(attr)) { if (values) {
this.addAttribute(attr, Utils.toDefaultValue(value)) for (key in values) {
if (values.hasOwnProperty(key)) {
attrs[key] = values[key]
} }
} }
} }
for (key in attrs) {
this.addAttribute(key, attrs[key])
}
} }
return DAO return DAO
......
...@@ -100,6 +100,82 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -100,6 +100,82 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
var user = this.User.build({ username: 'John Wayne' }) var user = this.User.build({ username: 'John Wayne' })
expect(user.selectedValues).toEqual({ username: 'John Wayne' }) expect(user.selectedValues).toEqual({ username: 'John Wayne' })
}) })
it("attaches getter and setter methods from attribute definition", function() {
var Product = this.sequelize.define('ProductWithSettersAndGetters1', {
price: {
type: Sequelize.INTEGER,
get : function() {
return 'answer = ' + this.getDataValue('price');
},
set : function(v) {
return this.setDataValue('price', v + 42);
}
}
},{
});
expect(Product.build({price: 42}).price).toEqual('answer = 84');
var p = Product.build({price: 1});
expect(p.price).toEqual('answer = 43');
p.price = 0;
expect(p.price).toEqual('answer = 42'); // ah finally the right answer :-)
})
it("attaches getter and setter methods from options", function() {
var Product = this.sequelize.define('ProductWithSettersAndGetters2', {
priceInCents: {
type: Sequelize.INTEGER
}
},{
setterMethods: {
price: function(value) {
this.dataValues.priceInCents = value * 100;
}
},
getterMethods: {
price: function() {
return '$' + (this.getDataValue('priceInCents') / 100);
},
priceInCents: function() {
return this.dataValues.priceInCents;
}
}
});
expect(Product.build({price: 20}).priceInCents).toEqual(20 * 100);
expect(Product.build({priceInCents: 30 * 100}).price).toEqual('$' + 30);
})
it("attaches getter and setter methods from options only if not defined in attribute", function() {
var Product = this.sequelize.define('ProductWithSettersAndGetters3', {
price1: {
type: Sequelize.INTEGER,
set : function(v) { this.setDataValue('price1', v * 10); }
},
price2: {
type: Sequelize.INTEGER,
get : function(v) { return this.getDataValue('price2') * 10; }
}
},{
setterMethods: {
price1: function(v) { this.setDataValue('price1', v * 100); }
},
getterMethods: {
price2: function() { return '$' + this.getDataValue('price2'); }
}
});
var p = Product.build({ price1: 1, price2: 2 });
expect(p.price1).toEqual(10);
expect(p.price2).toEqual(20);
})
}) })
describe('findOrCreate', function () { describe('findOrCreate', function () {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!