fix(change_detector): adding new ranges when disabling the current enabled record
This commit is contained in:
		
							parent
							
								
									b4772fc79b
								
							
						
					
					
						commit
						7f941eb936
					
				| @ -40,7 +40,7 @@ export class ChangeDetector { | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       record = record.nextEnabled; |       record = record.findNextEnabled(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return count; |     return count; | ||||||
|  | |||||||
| @ -173,6 +173,10 @@ export class Record { | |||||||
|     return (this._mode & RECORD_FLAG_DISABLED) === RECORD_FLAG_DISABLED; |     return (this._mode & RECORD_FLAG_DISABLED) === RECORD_FLAG_DISABLED; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   isEnabled():boolean { | ||||||
|  |     return ! this.disabled; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   set disabled(value:boolean) { |   set disabled(value:boolean) { | ||||||
|     if (value) { |     if (value) { | ||||||
|       this._mode |= RECORD_FLAG_DISABLED; |       this._mode |= RECORD_FLAG_DISABLED; | ||||||
| @ -316,6 +320,49 @@ export class Record { | |||||||
|   groupMemento() { |   groupMemento() { | ||||||
|     return isPresent(this.protoRecord) ? this.protoRecord.groupMemento : null; |     return isPresent(this.protoRecord) ? this.protoRecord.groupMemento : null; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Returns the next enabled record. This search is not limited to the current range. | ||||||
|  |    * | ||||||
|  |    * [H ER1 T] [H ER2 T] _nextEnable(ER1) will return ER2 | ||||||
|  |    * | ||||||
|  |    * The function skips disabled sub ranges. | ||||||
|  |    */ | ||||||
|  |   findNextEnabled() { | ||||||
|  |     if (this.isEnabled()) return this.nextEnabled; | ||||||
|  | 
 | ||||||
|  |     var record = this.next; | ||||||
|  |     while (isPresent(record) && record.disabled) { | ||||||
|  |       if (record.isMarkerRecord && record.recordRange.disabled) { | ||||||
|  |         record = record.recordRange.tailRecord.next; | ||||||
|  |       } else { | ||||||
|  |         record = record.next; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return record; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Returns the prev enabled record. This search is not limited to the current range. | ||||||
|  |    * | ||||||
|  |    * [H ER1 T] [H ER2 T] _nextEnable(ER2) will return ER1 | ||||||
|  |    * | ||||||
|  |    * The function skips disabled sub ranges. | ||||||
|  |    */ | ||||||
|  |   findPrevEnabled() { | ||||||
|  |     if (this.isEnabled()) return this.prevEnabled; | ||||||
|  | 
 | ||||||
|  |     var record = this.prev; | ||||||
|  |     while (isPresent(record) && record.disabled) { | ||||||
|  |       if (record.isMarkerRecord && record.recordRange.disabled) { | ||||||
|  |         record = record.recordRange.headRecord.prev; | ||||||
|  |       } else { | ||||||
|  |         record = record.prev; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return record; | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function isSame(a, b) { | function isSame(a, b) { | ||||||
|  | |||||||
| @ -129,8 +129,8 @@ export class RecordRange { | |||||||
| 
 | 
 | ||||||
|   addRange(child:RecordRange) { |   addRange(child:RecordRange) { | ||||||
|     var lastRecord = this.tailRecord.prev; |     var lastRecord = this.tailRecord.prev; | ||||||
|     var prevEnabledRecord = RecordRange._prevEnabled(this.tailRecord); |     var prevEnabledRecord = this.tailRecord.findPrevEnabled(); | ||||||
|     var nextEnabledRerord = RecordRange._nextEnabled(this.tailRecord); |     var nextEnabledRerord = this.tailRecord.findNextEnabled(); | ||||||
| 
 | 
 | ||||||
|     var firstEnabledChildRecord = child.findFirstEnabledRecord(); |     var firstEnabledChildRecord = child.findFirstEnabledRecord(); | ||||||
|     var lastEnabledChildRecord = child.findLastEnabledRecord(); |     var lastEnabledChildRecord = child.findLastEnabledRecord(); | ||||||
| @ -174,10 +174,10 @@ export class RecordRange { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   enableRecord(record:Record) { |   enableRecord(record:Record) { | ||||||
|     if (!record.disabled) return; |     if (record.isEnabled()) return; | ||||||
| 
 | 
 | ||||||
|     var prevEnabled = RecordRange._prevEnabled(record); |     var prevEnabled = record.findPrevEnabled(); | ||||||
|     var nextEnabled = RecordRange._nextEnabled(record); |     var nextEnabled = record.findNextEnabled(); | ||||||
| 
 | 
 | ||||||
|     record.prevEnabled = prevEnabled; |     record.prevEnabled = prevEnabled; | ||||||
|     record.nextEnabled = nextEnabled; |     record.nextEnabled = nextEnabled; | ||||||
| @ -203,8 +203,8 @@ export class RecordRange { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   enable() { |   enable() { | ||||||
|     var prevEnabledRecord = RecordRange._prevEnabled(this.headRecord); |     var prevEnabledRecord = this.headRecord.findPrevEnabled(); | ||||||
|     var nextEnabledRecord = RecordRange._nextEnabled(this.tailRecord); |     var nextEnabledRecord = this.tailRecord.findNextEnabled(); | ||||||
| 
 | 
 | ||||||
|     var firstEnabledthisRecord = this.findFirstEnabledRecord(); |     var firstEnabledthisRecord = this.findFirstEnabledRecord(); | ||||||
|     var lastEnabledthisRecord = this.findLastEnabledRecord(); |     var lastEnabledthisRecord = this.findLastEnabledRecord(); | ||||||
| @ -268,44 +268,6 @@ export class RecordRange { | |||||||
|     return record === this.headRecord ? null : record; |     return record === this.headRecord ? null : record; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |  | ||||||
|    * Returns the next enabled record. This search is not limited to the current range. |  | ||||||
|    * |  | ||||||
|    * [H ER1 T] [H ER2 T] _nextEnable(ER1) will return ER2 |  | ||||||
|    * |  | ||||||
|    * The function skips disabled sub ranges. |  | ||||||
|    */ |  | ||||||
|   static _nextEnabled(record:Record) { |  | ||||||
|     record = record.next; |  | ||||||
|     while (isPresent(record) && record.disabled) { |  | ||||||
|       if (record.isMarkerRecord && record.recordRange.disabled) { |  | ||||||
|         record = record.recordRange.tailRecord.next; |  | ||||||
|       } else { |  | ||||||
|         record = record.next; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return record; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /** |  | ||||||
|    * Returns the prev enabled record. This search is not limited to the current range. |  | ||||||
|    * |  | ||||||
|    * [H ER1 T] [H ER2 T] _nextEnable(ER2) will return ER1 |  | ||||||
|    * |  | ||||||
|    * The function skips disabled sub ranges. |  | ||||||
|    */ |  | ||||||
|   static _prevEnabled(record:Record) { |  | ||||||
|     record = record.prev; |  | ||||||
|     while (isPresent(record) && record.disabled) { |  | ||||||
|       if (record.isMarkerRecord && record.recordRange.disabled) { |  | ||||||
|         record = record.recordRange.headRecord.prev; |  | ||||||
|       } else { |  | ||||||
|         record = record.prev; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return record; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /** |   /** | ||||||
|    * Sets the context (the object) on which the change detection expressions will |    * Sets the context (the object) on which the change detection expressions will | ||||||
|    * dereference themselves on. Since the RecordRange can be reused the context |    * dereference themselves on. Since the RecordRange can be reused the context | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| import {ddescribe, describe, it, iit, xit, expect} from 'test_lib/test_lib'; | import {ddescribe, describe, it, iit, xit, expect, beforeEach} from 'test_lib/test_lib'; | ||||||
| 
 | 
 | ||||||
| import {isPresent, isBlank, isJsObject} from 'facade/lang'; | import {isPresent, isBlank, isJsObject} from 'facade/lang'; | ||||||
| import {List, ListWrapper, MapWrapper} from 'facade/collection'; | import {List, ListWrapper, MapWrapper} from 'facade/collection'; | ||||||
| @ -28,7 +28,7 @@ export function main() { | |||||||
|     var prr = new ProtoRecordRange(); |     var prr = new ProtoRecordRange(); | ||||||
|     prr.addRecordsFromAST(ast(exp), memo, memo, content); |     prr.addRecordsFromAST(ast(exp), memo, memo, content); | ||||||
| 
 | 
 | ||||||
|     var dispatcher = new LoggingDispatcher(); |     var dispatcher = new TestDispatcher(); | ||||||
|     var rr = prr.instantiate(dispatcher, formatters); |     var rr = prr.instantiate(dispatcher, formatters); | ||||||
|     rr.setContext(context); |     rr.setContext(context); | ||||||
| 
 | 
 | ||||||
| @ -46,6 +46,17 @@ export function main() { | |||||||
| 
 | 
 | ||||||
|   describe('change_detection', () => { |   describe('change_detection', () => { | ||||||
|     describe('ChangeDetection', () => { |     describe('ChangeDetection', () => { | ||||||
|  |       function createRange(dispatcher, ast, group) { | ||||||
|  |         var prr = new ProtoRecordRange(); | ||||||
|  |         prr.addRecordsFromAST(ast, "memo", group); | ||||||
|  |         return prr.instantiate(dispatcher, null); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       function detectChangesInRange(recordRange) { | ||||||
|  |         var cd = new ChangeDetector(recordRange); | ||||||
|  |         cd.detectChanges(); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       it('should do simple watching', () => { |       it('should do simple watching', () => { | ||||||
|         var person = new Person("misko"); |         var person = new Person("misko"); | ||||||
| 
 | 
 | ||||||
| @ -345,18 +356,45 @@ export function main() { | |||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|       describe("onGroupChange", () => { |       describe("adding new ranges", () => { | ||||||
|  |         var dispatcher; | ||||||
|  | 
 | ||||||
|  |         beforeEach(() => { | ||||||
|  |           dispatcher = new TestDispatcher(); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|  |          * Tests that we can add a new range after the current | ||||||
|  |          * record has been disabled. The new range must be processed | ||||||
|  |          * during the same change detection run. | ||||||
|  |          */ | ||||||
|  |         it("should work when disabling the last enabled record", () => { | ||||||
|  |           var rr = createRange(dispatcher, ast("1"), 1); | ||||||
|  | 
 | ||||||
|  |           dispatcher.onChange = (group, _) => { | ||||||
|  |             if (group === 1) { // to prevent infinite loop
 | ||||||
|  |               var rangeToAppend = createRange(dispatcher, ast("2"), 2); | ||||||
|  |               rr.addRange(rangeToAppend); | ||||||
|  |             } | ||||||
|  |           }; | ||||||
|  | 
 | ||||||
|  |           detectChangesInRange(rr); | ||||||
|  | 
 | ||||||
|  |           expect(dispatcher.loggedValues).toEqual([[1], [2]]); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       describe("group changes", () => { | ||||||
|         it("should notify the dispatcher when a group of records changes", () => { |         it("should notify the dispatcher when a group of records changes", () => { | ||||||
|           var prr = new ProtoRecordRange(); |           var prr = new ProtoRecordRange(); | ||||||
|           prr.addRecordsFromAST(ast("1 + 2"), "memo", 1); |           prr.addRecordsFromAST(ast("1 + 2"), "memo", 1); | ||||||
|           prr.addRecordsFromAST(ast("10 + 20"), "memo", 1); |           prr.addRecordsFromAST(ast("10 + 20"), "memo", 1); | ||||||
|           prr.addRecordsFromAST(ast("100 + 200"), "memo2", 2); |           prr.addRecordsFromAST(ast("100 + 200"), "memo2", 2); | ||||||
| 
 | 
 | ||||||
|           var dispatcher = new LoggingDispatcher(); |           var dispatcher = new TestDispatcher(); | ||||||
|           var rr = prr.instantiate(dispatcher, null); |           var rr = prr.instantiate(dispatcher, null); | ||||||
| 
 | 
 | ||||||
|           var cd = new ChangeDetector(rr); |           detectChangesInRange(rr); | ||||||
|           cd.detectChanges(); |  | ||||||
| 
 | 
 | ||||||
|           expect(dispatcher.loggedValues).toEqual([[3, 30], [300]]); |           expect(dispatcher.loggedValues).toEqual([[3, 30], [300]]); | ||||||
|         }); |         }); | ||||||
| @ -365,13 +403,12 @@ export function main() { | |||||||
|           var prr = new ProtoRecordRange(); |           var prr = new ProtoRecordRange(); | ||||||
|           prr.addRecordsFromAST(ast("1 + 2"), "memo", "memo"); |           prr.addRecordsFromAST(ast("1 + 2"), "memo", "memo"); | ||||||
| 
 | 
 | ||||||
|           var dispatcher = new LoggingDispatcher(); |           var dispatcher = new TestDispatcher(); | ||||||
|           var rr = new RecordRange(null, dispatcher); |           var rr = new RecordRange(null, dispatcher); | ||||||
|           rr.addRange(prr.instantiate(dispatcher, null)); |           rr.addRange(prr.instantiate(dispatcher, null)); | ||||||
|           rr.addRange(prr.instantiate(dispatcher, null)); |           rr.addRange(prr.instantiate(dispatcher, null)); | ||||||
| 
 | 
 | ||||||
|           var cd = new ChangeDetector(rr); |           detectChangesInRange(rr); | ||||||
|           cd.detectChanges(); |  | ||||||
| 
 | 
 | ||||||
|           expect(dispatcher.loggedValues).toEqual([[3], [3]]); |           expect(dispatcher.loggedValues).toEqual([[3], [3]]); | ||||||
|         }); |         }); | ||||||
| @ -382,7 +419,7 @@ export function main() { | |||||||
|           prr.addRecordsFromAST(ast("b()"), "b", 2); |           prr.addRecordsFromAST(ast("b()"), "b", 2); | ||||||
|           prr.addRecordsFromAST(ast("c()"), "b", 2); |           prr.addRecordsFromAST(ast("c()"), "b", 2); | ||||||
| 
 | 
 | ||||||
|           var dispatcher = new LoggingDispatcher(); |           var dispatcher = new TestDispatcher(); | ||||||
|           var rr = prr.instantiate(dispatcher, null); |           var rr = prr.instantiate(dispatcher, null); | ||||||
| 
 | 
 | ||||||
|           var tr = new TestRecord(); |           var tr = new TestRecord(); | ||||||
| @ -391,8 +428,7 @@ export function main() { | |||||||
|           tr.c = () => {dispatcher.logValue('InvokeC'); return 'c'}; |           tr.c = () => {dispatcher.logValue('InvokeC'); return 'c'}; | ||||||
|           rr.setContext(tr); |           rr.setContext(tr); | ||||||
| 
 | 
 | ||||||
|           var cd = new ChangeDetector(rr); |           detectChangesInRange(rr); | ||||||
|           cd.detectChanges(); |  | ||||||
| 
 | 
 | ||||||
|           expect(dispatcher.loggedValues).toEqual(['InvokeA', ['a'], 'InvokeB', 'InvokeC', ['b', 'c']]); |           expect(dispatcher.loggedValues).toEqual(['InvokeA', ['a'], 'InvokeB', 'InvokeC', ['b', 'c']]); | ||||||
|         }); |         }); | ||||||
| @ -444,13 +480,15 @@ class TestData { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class LoggingDispatcher extends WatchGroupDispatcher { | class TestDispatcher extends WatchGroupDispatcher { | ||||||
|   log:List; |   log:List; | ||||||
|   loggedValues:List; |   loggedValues:List; | ||||||
|  |   onChange:Function; | ||||||
| 
 | 
 | ||||||
|   constructor() { |   constructor() { | ||||||
|     this.log = null; |     this.log = null; | ||||||
|     this.loggedValues = null; |     this.loggedValues = null; | ||||||
|  |     this.onChange = (_, __) => {}; | ||||||
|     this.clear(); |     this.clear(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -458,7 +496,7 @@ class LoggingDispatcher extends WatchGroupDispatcher { | |||||||
|     this.log = ListWrapper.create(); |     this.log = ListWrapper.create(); | ||||||
|     this.loggedValues = ListWrapper.create(); |     this.loggedValues = ListWrapper.create(); | ||||||
|   } |   } | ||||||
|    | 
 | ||||||
|   logValue(value) { |   logValue(value) { | ||||||
|     ListWrapper.push(this.loggedValues, value); |     ListWrapper.push(this.loggedValues, value); | ||||||
|   } |   } | ||||||
| @ -470,6 +508,8 @@ class LoggingDispatcher extends WatchGroupDispatcher { | |||||||
| 
 | 
 | ||||||
|     var values = ListWrapper.map(records, (r) => r.currentValue); |     var values = ListWrapper.map(records, (r) => r.currentValue); | ||||||
|     ListWrapper.push(this.loggedValues, values); |     ListWrapper.push(this.loggedValues, values); | ||||||
|  | 
 | ||||||
|  |     this.onChange(group, records); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   _asString(value) { |   _asString(value) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user