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 {ProtoRecord, Record} from './record';
|
||||
import {FIELD, int} from 'facade/lang';
|
||||
import {FIELD, int, isPresent} from 'facade/lang';
|
||||
export * from './record';
|
||||
export * from './watch_group'
|
||||
|
||||
|
@ -12,11 +12,10 @@ export class ChangeDetector {
|
|||
}
|
||||
|
||||
detectChanges():int {
|
||||
var record:Record = this._rootWatchGroup.headRecord;
|
||||
var count:int = 0;
|
||||
for (record = this._rootWatchGroup.headRecord;
|
||||
record != null;
|
||||
record = record.next) {
|
||||
for (var record = this._rootWatchGroup.headEnabledRecord;
|
||||
isPresent(record);
|
||||
record = record.nextEnabled) {
|
||||
if (record.check()) {
|
||||
count++;
|
||||
}
|
||||
|
|
|
@ -65,6 +65,12 @@ export class Record {
|
|||
@FIELD('final protoRecord:ProtoRecord')
|
||||
@FIELD('next:Record')
|
||||
@FIELD('prev:Record')
|
||||
|
||||
/// This reference can change.
|
||||
@FIELD('nextEnabled:Record')
|
||||
|
||||
/// This reference can change.
|
||||
@FIELD('prevEnabled:Record')
|
||||
@FIELD('dest:Record')
|
||||
|
||||
@FIELD('previousValue')
|
||||
|
@ -86,6 +92,9 @@ export class Record {
|
|||
|
||||
this.next = null;
|
||||
this.prev = null;
|
||||
this.nextEnabled = null;
|
||||
this.prevEnabled = null;
|
||||
this.disabled = false;
|
||||
this.dest = null;
|
||||
|
||||
this.previousValue = null;
|
||||
|
@ -163,9 +172,11 @@ export class Record {
|
|||
return FunctionWrapper.apply(this.context, this.args);
|
||||
|
||||
case MODE_STATE_INVOKE_PURE_FUNCTION:
|
||||
this.watchGroup.disableRecord(this);
|
||||
return FunctionWrapper.apply(this.funcOrValue, this.args);
|
||||
|
||||
case MODE_STATE_CONST:
|
||||
this.watchGroup.disableRecord(this);
|
||||
return this.funcOrValue;
|
||||
|
||||
case MODE_STATE_MARKER:
|
||||
|
@ -184,10 +195,12 @@ export class Record {
|
|||
|
||||
updateArg(value, position:int) {
|
||||
this.args[position] = value;
|
||||
this.watchGroup.enableRecord(this);
|
||||
}
|
||||
|
||||
updateContext(value) {
|
||||
this.context = value;
|
||||
this.watchGroup.enableRecord(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,21 +56,11 @@ export class ProtoWatchGroup {
|
|||
}
|
||||
|
||||
_createRecords(watchGroup:WatchGroup, formatters:Map) {
|
||||
var tail, prevRecord;
|
||||
watchGroup.headRecord = tail = new Record(watchGroup, this.headRecord, formatters);
|
||||
this.headRecord.recordInConstruction = watchGroup.headRecord;
|
||||
|
||||
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;
|
||||
for (var proto = this.headRecord; proto != null; proto = proto.next) {
|
||||
var record = new Record(watchGroup, proto, formatters);
|
||||
proto.recordInConstruction = record;
|
||||
watchGroup.addRecord(record);
|
||||
}
|
||||
|
||||
watchGroup.tailRecord = tail;
|
||||
}
|
||||
|
||||
_setDestination() {
|
||||
|
@ -95,9 +85,70 @@ export class WatchGroup {
|
|||
this.dispatcher = dispatcher;
|
||||
this.headRecord = null;
|
||||
this.tailRecord = null;
|
||||
this.headEnabledRecord = null;
|
||||
this.tailEnabledRecord = 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) {
|
||||
throw 'not implemented';
|
||||
}
|
||||
|
|
|
@ -152,13 +152,36 @@ export function main() {
|
|||
expect(executeWatch('m', '1 > 2 ? 1 : 2')).toEqual(['m=2']);
|
||||
});
|
||||
|
||||
it("should support formatters", () => {
|
||||
var formatters = MapWrapper.createFromPairs([
|
||||
["uppercase", (v) => v.toUpperCase()],
|
||||
["wrap", (v, before, after) => `${before}${v}${after}`]
|
||||
]);
|
||||
expect(executeWatch('str', '"aBc" | uppercase', null, formatters)).toEqual(['str=ABC']);
|
||||
expect(executeWatch('str', '"b" | wrap:"a":"c"', null, formatters)).toEqual(['str=abc']);
|
||||
describe("formatters", () => {
|
||||
it("should support formatters", () => {
|
||||
var formatters = MapWrapper.createFromPairs([
|
||||
['uppercase', (v) => v.toUpperCase()],
|
||||
['wrap', (v, before, after) => `${before}${v}${after}`]]);
|
||||
expect(executeWatch('str', '"aBc" | uppercase', 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