feat(ChangeDetector): implement enabling/disabling records
This commit is contained in:
parent
8dfbc242af
commit
daf8f72b74
|
@ -1,6 +1,6 @@
|
||||||
import {ProtoWatchGroup, WatchGroup} from './watch_group';
|
import {ProtoWatchGroup, WatchGroup} from './watch_group';
|
||||||
import {ProtoRecord, Record} from './record';
|
import {ProtoRecord, Record} from './record';
|
||||||
import {FIELD, int} from 'facade/lang';
|
import {FIELD, int, isPresent} from 'facade/lang';
|
||||||
export * from './record';
|
export * from './record';
|
||||||
export * from './watch_group'
|
export * from './watch_group'
|
||||||
|
|
||||||
|
@ -12,11 +12,10 @@ export class ChangeDetector {
|
||||||
}
|
}
|
||||||
|
|
||||||
detectChanges():int {
|
detectChanges():int {
|
||||||
var record:Record = this._rootWatchGroup.headRecord;
|
|
||||||
var count:int = 0;
|
var count:int = 0;
|
||||||
for (record = this._rootWatchGroup.headRecord;
|
for (var record = this._rootWatchGroup.headEnabledRecord;
|
||||||
record != null;
|
isPresent(record);
|
||||||
record = record.next) {
|
record = record.nextEnabled) {
|
||||||
if (record.check()) {
|
if (record.check()) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,12 @@ export class Record {
|
||||||
@FIELD('final protoRecord:ProtoRecord')
|
@FIELD('final protoRecord:ProtoRecord')
|
||||||
@FIELD('next:Record')
|
@FIELD('next:Record')
|
||||||
@FIELD('prev:Record')
|
@FIELD('prev:Record')
|
||||||
|
|
||||||
|
/// This reference can change.
|
||||||
|
@FIELD('nextEnabled:Record')
|
||||||
|
|
||||||
|
/// This reference can change.
|
||||||
|
@FIELD('prevEnabled:Record')
|
||||||
@FIELD('dest:Record')
|
@FIELD('dest:Record')
|
||||||
|
|
||||||
@FIELD('previousValue')
|
@FIELD('previousValue')
|
||||||
|
@ -86,6 +92,9 @@ export class Record {
|
||||||
|
|
||||||
this.next = null;
|
this.next = null;
|
||||||
this.prev = null;
|
this.prev = null;
|
||||||
|
this.nextEnabled = null;
|
||||||
|
this.prevEnabled = null;
|
||||||
|
this.disabled = false;
|
||||||
this.dest = null;
|
this.dest = null;
|
||||||
|
|
||||||
this.previousValue = null;
|
this.previousValue = null;
|
||||||
|
@ -163,9 +172,11 @@ export class Record {
|
||||||
return FunctionWrapper.apply(this.context, this.args);
|
return FunctionWrapper.apply(this.context, this.args);
|
||||||
|
|
||||||
case MODE_STATE_INVOKE_PURE_FUNCTION:
|
case MODE_STATE_INVOKE_PURE_FUNCTION:
|
||||||
|
this.watchGroup.disableRecord(this);
|
||||||
return FunctionWrapper.apply(this.funcOrValue, this.args);
|
return FunctionWrapper.apply(this.funcOrValue, this.args);
|
||||||
|
|
||||||
case MODE_STATE_CONST:
|
case MODE_STATE_CONST:
|
||||||
|
this.watchGroup.disableRecord(this);
|
||||||
return this.funcOrValue;
|
return this.funcOrValue;
|
||||||
|
|
||||||
case MODE_STATE_MARKER:
|
case MODE_STATE_MARKER:
|
||||||
|
@ -184,10 +195,12 @@ export class Record {
|
||||||
|
|
||||||
updateArg(value, position:int) {
|
updateArg(value, position:int) {
|
||||||
this.args[position] = value;
|
this.args[position] = value;
|
||||||
|
this.watchGroup.enableRecord(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateContext(value) {
|
updateContext(value) {
|
||||||
this.context = value;
|
this.context = value;
|
||||||
|
this.watchGroup.enableRecord(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,21 +56,11 @@ export class ProtoWatchGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
_createRecords(watchGroup:WatchGroup, formatters:Map) {
|
_createRecords(watchGroup:WatchGroup, formatters:Map) {
|
||||||
var tail, prevRecord;
|
for (var proto = this.headRecord; proto != null; proto = proto.next) {
|
||||||
watchGroup.headRecord = tail = new Record(watchGroup, this.headRecord, formatters);
|
var record = new Record(watchGroup, proto, formatters);
|
||||||
this.headRecord.recordInConstruction = watchGroup.headRecord;
|
proto.recordInConstruction = record;
|
||||||
|
watchGroup.addRecord(record);
|
||||||
for (var proto = this.headRecord.next; proto != null; proto = proto.next) {
|
|
||||||
prevRecord = tail;
|
|
||||||
|
|
||||||
tail = new Record(watchGroup, proto, formatters);
|
|
||||||
proto.recordInConstruction = tail;
|
|
||||||
|
|
||||||
tail.prev = prevRecord;
|
|
||||||
prevRecord.next = tail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watchGroup.tailRecord = tail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_setDestination() {
|
_setDestination() {
|
||||||
|
@ -95,9 +85,70 @@ export class WatchGroup {
|
||||||
this.dispatcher = dispatcher;
|
this.dispatcher = dispatcher;
|
||||||
this.headRecord = null;
|
this.headRecord = null;
|
||||||
this.tailRecord = null;
|
this.tailRecord = null;
|
||||||
|
this.headEnabledRecord = null;
|
||||||
|
this.tailEnabledRecord = null;
|
||||||
this.context = null;
|
this.context = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addRecord(record:Record) {
|
||||||
|
if (isPresent(this.tailRecord)) {
|
||||||
|
this.tailRecord.next = record;
|
||||||
|
this.tailRecord.nextEnabled = record;
|
||||||
|
record.prev = this.tailRecord;
|
||||||
|
record.prevEnabled = this.tailRecord;
|
||||||
|
this.tailRecord = this.tailEnabledRecord = record;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.headRecord = this.tailRecord = record;
|
||||||
|
this.headEnabledRecord = this.tailEnabledRecord = record;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disableRecord(record:Record) {
|
||||||
|
var prev = record.prevEnabled;
|
||||||
|
var next = record.nextEnabled;
|
||||||
|
|
||||||
|
record.disabled = true;
|
||||||
|
|
||||||
|
if (isPresent(prev)) {
|
||||||
|
prev.nextEnabled = next;
|
||||||
|
} else {
|
||||||
|
this.headEnabledRecord = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPresent(next)) {
|
||||||
|
next.prevEnabled = prev;
|
||||||
|
} else {
|
||||||
|
this.tailEnabledRecord = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enableRecord(record:Record) {
|
||||||
|
if (!record.disabled) return;
|
||||||
|
|
||||||
|
var prev = record.prev;
|
||||||
|
while (prev != null && prev.disabled) prev = prev.prev;
|
||||||
|
|
||||||
|
var next = record.next;
|
||||||
|
while (next != null && next.disabled) next = next.next;
|
||||||
|
|
||||||
|
record.disabled = false;
|
||||||
|
record.prevEnabled = prev;
|
||||||
|
record.nextEnabled = next;
|
||||||
|
|
||||||
|
if (isPresent(prev)) {
|
||||||
|
prev.nextEnabled = record;
|
||||||
|
} else {
|
||||||
|
this.headEnabledRecord = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPresent(next)) {
|
||||||
|
next.prevEnabled = record;
|
||||||
|
} else {
|
||||||
|
this.tailEnabledRecord = record;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
insertChildGroup(newChild:WatchGroup, insertAfter:WatchGroup) {
|
insertChildGroup(newChild:WatchGroup, insertAfter:WatchGroup) {
|
||||||
throw 'not implemented';
|
throw 'not implemented';
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,14 +152,37 @@ export function main() {
|
||||||
expect(executeWatch('m', '1 > 2 ? 1 : 2')).toEqual(['m=2']);
|
expect(executeWatch('m', '1 > 2 ? 1 : 2')).toEqual(['m=2']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("formatters", () => {
|
||||||
it("should support formatters", () => {
|
it("should support formatters", () => {
|
||||||
var formatters = MapWrapper.createFromPairs([
|
var formatters = MapWrapper.createFromPairs([
|
||||||
["uppercase", (v) => v.toUpperCase()],
|
['uppercase', (v) => v.toUpperCase()],
|
||||||
["wrap", (v, before, after) => `${before}${v}${after}`]
|
['wrap', (v, before, after) => `${before}${v}${after}`]]);
|
||||||
]);
|
|
||||||
expect(executeWatch('str', '"aBc" | uppercase', null, formatters)).toEqual(['str=ABC']);
|
expect(executeWatch('str', '"aBc" | uppercase', null, formatters)).toEqual(['str=ABC']);
|
||||||
expect(executeWatch('str', '"b" | wrap:"a":"c"', null, formatters)).toEqual(['str=abc']);
|
expect(executeWatch('str', '"b" | wrap:"a":"c"', null, formatters)).toEqual(['str=abc']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should rerun formatters only when arguments change", () => {
|
||||||
|
var counter = 0;
|
||||||
|
var formatters = MapWrapper.createFromPairs([
|
||||||
|
['formatter', (_) => {counter += 1; return 'value'}]
|
||||||
|
]);
|
||||||
|
|
||||||
|
var person = new Person('Jim');
|
||||||
|
|
||||||
|
var c = createChangeDetector('formatter', 'name | formatter', person, formatters);
|
||||||
|
var cd = c['changeDetector'];
|
||||||
|
|
||||||
|
cd.detectChanges();
|
||||||
|
expect(counter).toEqual(1);
|
||||||
|
|
||||||
|
cd.detectChanges();
|
||||||
|
expect(counter).toEqual(1);
|
||||||
|
|
||||||
|
person.name = 'bob';
|
||||||
|
cd.detectChanges();
|
||||||
|
expect(counter).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
import {ddescribe, describe, it, iit, xit, expect} from 'test_lib/test_lib';
|
||||||
|
|
||||||
|
import {List, ListWrapper, MapWrapper} from 'facade/collection';
|
||||||
|
import {Parser} from 'change_detection/parser/parser';
|
||||||
|
import {Lexer} from 'change_detection/parser/lexer';
|
||||||
|
import {ClosureMap} from 'change_detection/parser/closure_map';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ChangeDetector,
|
||||||
|
ProtoWatchGroup,
|
||||||
|
WatchGroup,
|
||||||
|
WatchGroupDispatcher,
|
||||||
|
ProtoRecord
|
||||||
|
} from 'change_detection/change_detector';
|
||||||
|
|
||||||
|
import {Record} from 'change_detection/record';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
function createRecord(wg) {
|
||||||
|
return new Record(wg, new ProtoRecord(null, null, null, null, null), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('watch group', () => {
|
||||||
|
describe("adding records", () => {
|
||||||
|
it("should add a record", () => {
|
||||||
|
var wg = new WatchGroup(null, null);
|
||||||
|
var record = createRecord(wg);
|
||||||
|
|
||||||
|
wg.addRecord(record);
|
||||||
|
|
||||||
|
expect(wg.headRecord).toBe(record);
|
||||||
|
expect(wg.tailRecord).toBe(record);
|
||||||
|
expect(wg.headEnabledRecord).toBe(record);
|
||||||
|
expect(wg.tailEnabledRecord).toBe(record);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add multiple records", () => {
|
||||||
|
var wg = new WatchGroup(null, null);
|
||||||
|
var record1 = createRecord(wg);
|
||||||
|
var record2 = createRecord(wg);
|
||||||
|
|
||||||
|
wg.addRecord(record1);
|
||||||
|
wg.addRecord(record2);
|
||||||
|
|
||||||
|
expect(wg.headRecord).toBe(record1);
|
||||||
|
expect(wg.tailRecord).toBe(record2);
|
||||||
|
|
||||||
|
expect(wg.headEnabledRecord).toBe(record1);
|
||||||
|
expect(wg.tailEnabledRecord).toBe(record2);
|
||||||
|
|
||||||
|
expect(record1.next).toBe(record2);
|
||||||
|
expect(record2.prev).toBe(record1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("enabling/disabling records", () => {
|
||||||
|
it("should disable a single record", () => {
|
||||||
|
var wg = new WatchGroup(null, null);
|
||||||
|
var record = createRecord(wg);
|
||||||
|
wg.addRecord(record);
|
||||||
|
|
||||||
|
wg.disableRecord(record);
|
||||||
|
|
||||||
|
expect(wg.headEnabledRecord).toBeNull();
|
||||||
|
expect(wg.tailEnabledRecord).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should enable a single record", () => {
|
||||||
|
var wg = new WatchGroup(null, null);
|
||||||
|
var record = createRecord(wg);
|
||||||
|
wg.addRecord(record);
|
||||||
|
wg.disableRecord(record);
|
||||||
|
|
||||||
|
wg.enableRecord(record);
|
||||||
|
|
||||||
|
expect(wg.headEnabledRecord).toBe(record);
|
||||||
|
expect(wg.tailEnabledRecord).toBe(record);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should disable a record", () => {
|
||||||
|
var wg = new WatchGroup(null, null);
|
||||||
|
var record1 = createRecord(wg);
|
||||||
|
var record2 = createRecord(wg);
|
||||||
|
var record3 = createRecord(wg);
|
||||||
|
var record4 = createRecord(wg);
|
||||||
|
wg.addRecord(record1);
|
||||||
|
wg.addRecord(record2);
|
||||||
|
wg.addRecord(record3);
|
||||||
|
wg.addRecord(record4);
|
||||||
|
|
||||||
|
wg.disableRecord(record2);
|
||||||
|
wg.disableRecord(record3);
|
||||||
|
|
||||||
|
expect(record2.disabled).toBeTruthy();
|
||||||
|
|
||||||
|
expect(wg.headEnabledRecord).toBe(record1);
|
||||||
|
expect(wg.tailEnabledRecord).toBe(record4);
|
||||||
|
|
||||||
|
expect(record1.nextEnabled).toBe(record4);
|
||||||
|
expect(record4.prevEnabled).toBe(record1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should enable a record", () => {
|
||||||
|
var wg = new WatchGroup(null, null);
|
||||||
|
var record1 = createRecord(wg);
|
||||||
|
var record2 = createRecord(wg);
|
||||||
|
var record3 = createRecord(wg);
|
||||||
|
var record4 = createRecord(wg);
|
||||||
|
wg.addRecord(record1);
|
||||||
|
wg.addRecord(record2);
|
||||||
|
wg.addRecord(record3);
|
||||||
|
wg.addRecord(record4);
|
||||||
|
wg.disableRecord(record2);
|
||||||
|
wg.disableRecord(record3);
|
||||||
|
|
||||||
|
wg.enableRecord(record2);
|
||||||
|
wg.enableRecord(record3);
|
||||||
|
|
||||||
|
expect(record1.nextEnabled).toBe(record2);
|
||||||
|
expect(record2.nextEnabled).toBe(record3);
|
||||||
|
expect(record3.nextEnabled).toBe(record4);
|
||||||
|
expect(record4.prevEnabled).toBe(record3);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue