Skip to content

Commit 7e6e9ab

Browse files
committed
Add Instance.set() dresende#517
1 parent b31e079 commit 7e6e9ab

File tree

2 files changed

+149
-1
lines changed

2 files changed

+149
-1
lines changed

lib/Instance.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,39 @@ function Instance(Model, opts) {
458458
return false;
459459
}
460460

461+
// ('data.a.b', 5) => opts.data.a.b = 5
462+
var setPropertyByPath = function (path, value) {
463+
if (typeof path == 'string') {
464+
path = path.split('.');
465+
} else if (!Array.isArray(path)) {
466+
return;
467+
}
468+
469+
var propName = path.shift();
470+
var prop = Model.allProperties[propName] || opts.extra[propName];
471+
var currKey, currObj;
472+
473+
if (!prop) {
474+
return;
475+
}
476+
if (path.length == 0) {
477+
instance[propName] = value;
478+
return;
479+
}
480+
currObj = instance[propName];
481+
482+
while(currObj && path.length > 0 ) {
483+
currKey = path.shift();
484+
485+
if (path.length > 0) {
486+
currObj = currObj[currKey];
487+
} else if (currObj[currKey] !== value) {
488+
currObj[currKey] = value;
489+
opts.changes.push(propName);
490+
}
491+
}
492+
}
493+
461494
var addInstanceProperty = function (key) {
462495
var defaultValue = null;
463496
var prop = Model.allProperties[key];
@@ -619,6 +652,11 @@ function Instance(Model, opts) {
619652
enumerable: false,
620653
writable: true
621654
});
655+
Object.defineProperty(instance, "set", {
656+
value: setPropertyByPath,
657+
enumerable: false,
658+
writable: true
659+
});
622660
Object.defineProperty(instance, "isInstance", {
623661
value: true,
624662
enumerable: false

test/integration/instance.js

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ describe("Model instance", function() {
1616
name : String,
1717
age : { type: 'integer', required: false },
1818
height : { type: 'integer', required: false },
19-
weight : { type: 'number', required: false }
19+
weight : { type: 'number', required: false },
20+
data : { type: 'object', required: false }
2021
}, {
2122
cache: false,
2223
validations: {
@@ -144,6 +145,115 @@ describe("Model instance", function() {
144145
});
145146
});
146147

148+
describe("#set", function () {
149+
var person = null;
150+
var data = null;
151+
152+
function clone(obj) { return JSON.parse(JSON.stringify(obj)) };
153+
154+
beforeEach(function (done) {
155+
data = {
156+
a: {
157+
b: {
158+
c: 3,
159+
d: 4
160+
}
161+
},
162+
e: 5
163+
};
164+
Person.create({ name: 'Dilbert', data: data }, function (err, p) {
165+
if (err) return done(err);
166+
167+
person = p;
168+
done();
169+
});
170+
});
171+
172+
it("should do nothing with flat paths when setting to same value", function () {
173+
should.equal(person.saved(), true);
174+
person.set('name', 'Dilbert');
175+
should.equal(person.name, 'Dilbert');
176+
should.equal(person.saved(), true);
177+
});
178+
179+
it("should mark as dirty with flat paths when setting to different value", function () {
180+
should.equal(person.saved(), true);
181+
person.set('name', 'Dogbert');
182+
should.equal(person.name, 'Dogbert');
183+
should.equal(person.saved(), false);
184+
should.equal(person.__opts.changes.join(','), 'name');
185+
});
186+
187+
it("should do nothin with deep paths when setting to same value", function () {
188+
should.equal(person.saved(), true);
189+
person.set('data.e', 5);
190+
191+
var expected = clone(data);
192+
expected.e = 5;
193+
194+
should.equal(JSON.stringify(person.data), JSON.stringify(expected));
195+
should.equal(person.saved(), true);
196+
});
197+
198+
it("should mark as dirty with deep paths when setting to different value", function () {
199+
should.equal(person.saved(), true);
200+
person.set('data.e', 6);
201+
202+
var expected = clone(data);
203+
expected.e = 6;
204+
205+
should.equal(JSON.stringify(person.data), JSON.stringify(expected));
206+
should.equal(person.saved(), false);
207+
should.equal(person.__opts.changes.join(','), 'data');
208+
});
209+
210+
it("should do nothing with deeper paths when setting to same value", function () {
211+
should.equal(person.saved(), true);
212+
person.set('data.a.b.d', 4);
213+
214+
var expected = clone(data);
215+
expected.a.b.d = 4;
216+
217+
should.equal(JSON.stringify(person.data), JSON.stringify(expected));
218+
should.equal(person.saved(), true);
219+
});
220+
221+
it("should mark as dirty with deeper paths when setting to different value", function () {
222+
should.equal(person.saved(), true);
223+
person.set('data.a.b.d', 6);
224+
225+
var expected = clone(data);
226+
expected.a.b.d = 6;
227+
228+
should.equal(JSON.stringify(person.data), JSON.stringify(expected));
229+
should.equal(person.saved(), false);
230+
should.equal(person.__opts.changes.join(','), 'data');
231+
});
232+
233+
it("should mark as dirty with array path when setting to different value", function () {
234+
should.equal(person.saved(), true);
235+
person.set(['data', 'a', 'b', 'd'], 6);
236+
237+
var expected = clone(data);
238+
expected.a.b.d = 6;
239+
240+
should.equal(JSON.stringify(person.data), JSON.stringify(expected));
241+
should.equal(person.saved(), false);
242+
should.equal(person.__opts.changes.join(','), 'data');
243+
});
244+
245+
it("should do nothing with invalid paths", function () {
246+
should.equal(person.saved(), true);
247+
person.set('data.a.b.d.y.z', 1);
248+
person.set('data.y.z', 1);
249+
person.set('z', 1);
250+
person.set(4, 1);
251+
person.set(null, 1);
252+
person.set(undefined, 1);
253+
should.equal(person.saved(), true);
254+
});
255+
});
256+
147257
describe("#isShell", function () {
148258
it("should return true for shell models", function () {
149259
should.equal(Person(4).isShell(), true);

0 commit comments

Comments
 (0)