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

Commit 5c59702a by Mick Hansen

feat(instance): provide plain: true option to get() that will reduce included in…

…stances to plain value objects
1 parent bea65490
...@@ -33,7 +33,7 @@ module.exports = (function() { ...@@ -33,7 +33,7 @@ module.exports = (function() {
this.dataValues = {}; this.dataValues = {};
this._previousDataValues = {}; this._previousDataValues = {};
this.__options = this.Model.options; this.__options = this.Model.options;
this.options = options; this.options = options || {};
this.hasPrimaryKeys = this.Model.options.hasPrimaryKeys; this.hasPrimaryKeys = this.Model.options.hasPrimaryKeys;
this.__eagerlyLoadedAssociations = []; this.__eagerlyLoadedAssociations = [];
/** /**
...@@ -165,29 +165,45 @@ module.exports = (function() { ...@@ -165,29 +165,45 @@ module.exports = (function() {
* If key is given and a field or virtual getter is present for the key it will call that getter - else it will return the value for key. * If key is given and a field or virtual getter is present for the key it will call that getter - else it will return the value for key.
* *
* @param {String} [key] * @param {String} [key]
* @param {Object} [options]
* @param {Boolean} [options.plain=false] If set to true, included instances will be returned as plain objects
* @return {Object|any} * @return {Object|any}
*/ */
Instance.prototype.get = function(key) { Instance.prototype.get = function(key, options) {
if (options === undefined && typeof key === "object") {
options = key;
key = undefined;
}
if (key) { if (key) {
if (this._customGetters[key]) { if (this._customGetters[key]) {
return this._customGetters[key].call(this, key); return this._customGetters[key].call(this, key);
} }
if (options && options.plain && this.options.include && this.options.includeNames.indexOf(key) !== -1) {
return this.dataValues[key].get({plain: options.plain});
}
return this.dataValues[key]; return this.dataValues[key];
} }
if (this._hasCustomGetters) { if (this._hasCustomGetters || (options && options.plain && this.options.include)) {
var values = {} var values = {}
, _key; , _key;
for (_key in this._customGetters) { if (this._hasCustomGetters) {
if (this._customGetters.hasOwnProperty(_key)) { for (_key in this._customGetters) {
values[_key] = this.get(_key); if (this._customGetters.hasOwnProperty(_key)) {
values[_key] = this.get(_key);
}
} }
} }
for (_key in this.dataValues) { for (_key in this.dataValues) {
if (!values.hasOwnProperty(_key) && this.dataValues.hasOwnProperty(_key)) { if (!values.hasOwnProperty(_key) && this.dataValues.hasOwnProperty(_key)) {
values[_key] = this.dataValues[_key]; if (options.plain && this.options.include && this.options.includeNames.indexOf(_key) !== -1) {
values[_key] = this.dataValues[_key].get({plain: options.plain});
} else {
values[_key] = this.dataValues[_key];
}
} }
} }
return values; return values;
...@@ -282,7 +298,7 @@ module.exports = (function() { ...@@ -282,7 +298,7 @@ module.exports = (function() {
} else { } else {
// Check if we have included models, and if this key matches the include model names/aliases // Check if we have included models, and if this key matches the include model names/aliases
if (this.options && this.options.include && this.options.includeNames.indexOf(key) !== -1 && value) { if (this.options.include && this.options.includeNames.indexOf(key) !== -1 && value) {
// Pass it on to the include handler // Pass it on to the include handler
this._setInclude(key, value, options); this._setInclude(key, value, options);
return; return;
...@@ -883,7 +899,9 @@ module.exports = (function() { ...@@ -883,7 +899,9 @@ module.exports = (function() {
* @return {object} * @return {object}
*/ */
Instance.prototype.toJSON = function() { Instance.prototype.toJSON = function() {
return this.get(); return this.get({
plain: true
});
}; };
// private // private
......
...@@ -294,6 +294,7 @@ module.exports = (function() { ...@@ -294,6 +294,7 @@ module.exports = (function() {
return self._virtualAttributes.indexOf(key) !== -1; return self._virtualAttributes.indexOf(key) !== -1;
}); });
this.Instance.prototype.$Model =
this.Instance.prototype.Model = this; this.Instance.prototype.Model = this;
this._hasDefaultValues = !Utils._.isEmpty(this._defaultValues); this._hasDefaultValues = !Utils._.isEmpty(this._defaultValues);
......
/* jshint camelcase: false */
var chai = require('chai')
, Sequelize = require('../../index')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect()
, DataTypes = require(__dirname + "/../../lib/data-types")
, datetime = require('chai-datetime')
chai.use(datetime)
chai.config.includeStack = true
describe(Support.getTestDialectTeaser("DAO"), function () {
describe('Values', function () {
describe('set', function () {
it('doesn\'t overwrite generated primary keys', function () {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
var user = User.build({id: 1, name: 'Mick'})
expect(user.get('id')).to.equal(1)
expect(user.get('name')).to.equal('Mick')
user.set({
id: 2,
name: 'Jan'
})
expect(user.get('id')).to.equal(1)
expect(user.get('name')).to.equal('Jan')
})
it('doesn\'t overwrite defined primary keys', function () {
var User = this.sequelize.define('User', {
identifier: {type: DataTypes.STRING, primaryKey: true}
})
var user = User.build({identifier: 'identifier'})
expect(user.get('identifier')).to.equal('identifier')
user.set('identifier', 'another identifier')
expect(user.get('identifier')).to.equal('identifier')
})
it('doesn\'t set timestamps', function () {
var User = this.sequelize.define('User', {
identifier: {type: DataTypes.STRING, primaryKey: true}
})
var user = User.build()
user.set({
createdAt: new Date(2000, 1, 1),
updatedAt: new Date(2000, 1, 1)
})
expect(user.get('createdAt')).not.to.be.ok
expect(user.get('updatedAt')).not.to.be.ok
})
it('doesn\'t set underscored timestamps', function () {
var User = this.sequelize.define('User', {
identifier: {type: DataTypes.STRING, primaryKey: true}
}, {
underscored: true
})
var user = User.build()
user.set({
created_at: new Date(2000, 1, 1),
updated_at: new Date(2000, 1, 1)
})
expect(user.get('created_at')).not.to.be.ok
expect(user.get('updated_at')).not.to.be.ok
})
it('doesn\'t set value if not a dynamic setter or a model attribute', function() {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING},
email_hidden: {type: DataTypes.STRING}
}, {
setterMethods: {
email_secret: function (value) {
this.set('email_hidden', value)
}
}
})
var user = User.build()
user.set({
name: 'antonio banderaz',
email: 'antonio@banderaz.com',
email_secret: 'foo@bar.com'
})
user.set('email', 'antonio@banderaz.com')
expect(user.get('name')).to.equal('antonio banderaz')
expect(user.get('email_hidden')).to.equal('foo@bar.com')
expect(user.get('email')).not.to.be.ok
expect(user.dataValues.email).not.to.be.ok
})
it('allows use of sequelize.fn and sequelize.col in date and bool fields', function () {
var self = this
, User = this.sequelize.define('User', {
d: DataTypes.DATE,
b: DataTypes.BOOLEAN,
always_false: {
type: DataTypes.BOOLEAN,
defaultValue: false
}
}, {timestamps: false})
return User.sync({ force: true }).then(function () {
return User.create({}).then(function (user) {
// Create the user first to set the proper default values. PG does not support column references in insert,
// so we must create a record with the right value for always_false, then reference it in an update
var now = dialect === 'sqlite' ? self.sequelize.fn('', self.sequelize.fn('datetime', 'now')) : self.sequelize.fn('NOW')
user.set({
d: now,
b: self.sequelize.col('always_false')
})
expect(user.get('d')).to.be.instanceof(self.sequelize.Utils.fn)
expect(user.get('b')).to.be.instanceof(self.sequelize.Utils.col)
return user.save().then(function () {
return user.reload().then(function () {
expect(user.d).to.equalDate(new Date())
expect(user.b).to.equal(false)
})
})
})
})
})
describe('includes', function () {
it('should support basic includes', function () {
var Product = this.sequelize.define('product', {
title: Sequelize.STRING
})
var Tag = this.sequelize.define('tag', {
name: Sequelize.STRING
})
var User = this.sequelize.define('user', {
first_name: Sequelize.STRING,
last_name: Sequelize.STRING
})
Product.hasMany(Tag)
Product.belongsTo(User)
var product
product = Product.build({}, {
include: [
User,
Tag
]
})
product.set({
id: 1,
title: 'Chair',
tags: [
{id: 1, name: 'Alpha'},
{id: 2, name: 'Beta'}
],
user: {
id: 1,
first_name: 'Mick',
last_name: 'Hansen'
}
})
expect(product.tags).to.be.ok
expect(product.tags.length).to.equal(2)
expect(product.tags[0].Model).to.equal(Tag)
expect(product.user).to.be.ok
expect(product.user.Model).to.equal(User)
})
it('should support basic includes (with raw: true)', function () {
var Product = this.sequelize.define('Product', {
title: Sequelize.STRING
})
var Tag = this.sequelize.define('tag', {
name: Sequelize.STRING
})
var User = this.sequelize.define('user', {
first_name: Sequelize.STRING,
last_name: Sequelize.STRING
})
Product.hasMany(Tag)
Product.belongsTo(User)
var product
product = Product.build({}, {
include: [
User,
Tag
]
})
product.set({
id: 1,
title: 'Chair',
tags: [
{id: 1, name: 'Alpha'},
{id: 2, name: 'Beta'}
],
user: {
id: 1,
first_name: 'Mick',
last_name: 'Hansen'
}
}, {raw: true})
expect(product.tags).to.be.ok
expect(product.tags.length).to.equal(2)
expect(product.tags[0].Model).to.equal(Tag)
expect(product.user).to.be.ok
expect(product.user.Model).to.equal(User)
})
})
})
describe('get', function () {
it('should use custom attribute getters in get(key)', function () {
var Product = this.sequelize.define('Product', {
price: {
type: Sequelize.FLOAT,
get: function() {
return this.dataValues.price * 100
}
}
})
var product = Product.build({
price: 10
})
expect(product.get('price')).to.equal(1000)
})
it('should custom virtual getters in get(key)', function () {
var Product = this.sequelize.define('Product', {
priceInCents: {
type: Sequelize.FLOAT
}
}, {
getterMethods: {
price: function() {
return this.dataValues.priceInCents / 100
}
}
})
var product = Product.build({
priceInCents: 1000
})
expect(product.get('price')).to.equal(10)
})
it('should use custom getters in toJSON', function () {
var Product = this.sequelize.define('Product', {
price: {
type: Sequelize.STRING,
get: function() {
return this.dataValues.price * 100
}
}
}, {
getterMethods: {
withTaxes: function() {
return this.get('price') * 1.25
}
}
})
var product = Product.build({
price: 10
})
expect(product.toJSON()).to.deep.equal({withTaxes: 1250, price: 1000, id: null})
})
it('should work with save', function (done) {
var Contact = this.sequelize.define('Contact', {
first: { type: Sequelize.STRING },
last: { type: Sequelize.STRING },
tags: {
type: Sequelize.STRING,
get: function(field) {
var val = this.getDataValue(field);
return JSON.parse(val);
},
set: function(val, field) {
this.setDataValue(field, JSON.stringify(val));
}
}
});
this.sequelize.sync().done(function () {
var contact = Contact.build({
first: 'My',
last: 'Name',
tags: ['yes','no']
});
expect(contact.get('tags')).to.deep.equal(['yes', 'no'])
contact.save().done(function(err, me) {
expect(err).not.to.be.ok
expect(me.get('tags')).to.deep.equal(['yes', 'no'])
done();
});
});
})
})
describe('changed', function () {
it('should return false if object was built from database', function (done) {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
User.sync().done(function () {
User.create({name: 'Jan Meier'}).done(function (err, user) {
expect(err).not.to.be.ok
expect(user.changed('name')).to.be.false
expect(user.changed()).not.to.be.ok
expect(user.isDirty).to.be.false
done()
});
})
})
it('should return true if previous value is different', function () {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
var user = User.build({
name: 'Jan Meier'
})
user.set('name', 'Mick Hansen')
expect(user.changed('name')).to.be.true
expect(user.changed()).to.be.ok
expect(user.isDirty).to.be.true
})
it('should return false immediately after saving', function (done) {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
User.sync().done(function () {
var user = User.build({
name: 'Jan Meier'
})
user.set('name', 'Mick Hansen')
expect(user.changed('name')).to.be.true
expect(user.changed()).to.be.ok
expect(user.isDirty).to.be.true
user.save().done(function (err) {
expect(err).not.to.be.ok
expect(user.changed('name')).to.be.false
expect(user.changed()).not.to.be.ok
expect(user.isDirty).to.be.false
done()
})
})
})
it('setting the same value twice should not impact the result', function () {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
var user = User.build({
name: 'Jan Meier'
})
user.set('name', 'Mick Hansen')
user.set('name', 'Mick Hansen')
expect(user.changed('name')).to.be.true
expect(user.changed()).to.be.ok
expect(user.isDirty).to.be.true
expect(user.previous('name')).to.equal('Jan Meier')
})
})
describe('previous', function () {
it('should return the previous value', function () {
var User = this.sequelize.define('User', {
name: {type: DataTypes.STRING}
})
var user = User.build({
name: 'Jan Meier'
})
user.set('name', 'Mick Hansen')
expect(user.previous('name')).to.equal('Jan Meier')
expect(user.get('name')).to.equal('Mick Hansen')
})
})
})
})
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!