UNPKG

transactional

Version:

Reactive objects with transactional updates and automatic serialization

85 lines (69 loc) 2.38 kB
/** * Transaction object for bulk operations. * Encapsulate transaction logic, generalized on the case of the tree. * Implements two-phase commit: * 1) Make all changes * 2) Send change events * * Not clear what to do with owner change event. We already know that node is changed here. * (!) We could delay that until children will tell us about the change during its commit, so we can make `touch`. * (!) Which will work just fine. */ class Transaction { // open transaction constructor( model ){ this.changing = begin( model ); this.model = model; this.changed = []; this.nested = []; } // add nested transaction add( attr, transaction ){ this.nested.push( transaction ); /* Don't need it - children will `touch` an attribute when transaction will be commited. if( transaction.changed.length ){ this.changed.push( attr ); }*/ } // commit transaction commit( options ){ const { changes, nested, model } = this, { attributes } = model; if( changes.length ) model._pending = true; for( let i of nested ){ nested[ i ].commit( options ); } for( let i of changes ){ const attr = changes[ i ]; model.trigger( 'change:' + attr, attributes[ attr ], model, options ); } commit( model ); } } function set( attrs, options ){ this.createTransaction( attrs, options ).commit( options ); } function createTransaction( model, attrs, options ){ const transaction = new Transaction( this ), { changes } = transaction; model.forEachAttr( attrs, function( value, name, spec ){ // handle deep update... if( spec.deepUpdate ){ if( prev && !spec.isCompatible( value ) ){ transaction.add( name, prev.createTransaction( value, options ) ); return; } } // cast and hook... const next = spec.convert( value, prev, options ); if( spec.isChanged( next, prev ) ){ attributes[ name ] = next; changes.push( name ); // Do the rest of the job after assignment if( spec.handleChange ){ spec.handleChange( nex t, prev ); } } } ); return transaction; }