From 4f7654c7c48b7ca2bd09dbabca2d1a673d9581be Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Fri, 17 Feb 2017 15:14:17 -0500 Subject: [PATCH] Liberate numerous bucket classes * src/bucket/DelayedStagingBucket.js: Add class. * src/bucket/QuoteDataBucket.js: Add class. * src/bucket/StagingBucket.js: Add class. * src/bucket/StagingBucketAutoDiscard.js: Add class. * src/client/ClientDependencyFactory.js: Add class. --- src/bucket/DelayedStagingBucket.js | 186 +++++++++ src/bucket/QuoteDataBucket.js | 324 ++++++++++++++++ src/bucket/StagingBucket.js | 515 +++++++++++++++++++++++++ src/bucket/StagingBucketAutoDiscard.js | 103 +++++ src/client/ClientDependencyFactory.js | 8 +- 5 files changed, 1132 insertions(+), 4 deletions(-) create mode 100644 src/bucket/DelayedStagingBucket.js create mode 100644 src/bucket/QuoteDataBucket.js create mode 100644 src/bucket/StagingBucket.js create mode 100644 src/bucket/StagingBucketAutoDiscard.js diff --git a/src/bucket/DelayedStagingBucket.js b/src/bucket/DelayedStagingBucket.js new file mode 100644 index 0000000..1d2ca92 --- /dev/null +++ b/src/bucket/DelayedStagingBucket.js @@ -0,0 +1,186 @@ +/** + * Delayed writing to staging bucket + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of liza. + * + * liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Class = require( 'easejs' ).Class, + StagingBucket = require( './StagingBucket' ); + + +/** + * Holds changes until explicitly processed to avoid cascades + * + * Since each write could trigger any number of event listeners, writes + * should be queued and done en-masse. + */ +module.exports = Class( 'DelayedStagingBucket' ) + .extend( StagingBucket, +{ + /** + * Queued data + * @type {Object} + */ + 'private _queued': {}, + + /** + * Delay timer id + * @type {number} + */ + 'private _timer': 0, + + + 'public override setValues': function( data, merge_index, merge_null ) + { + for ( var name in data ) + { + if ( merge_index ) + { + if ( this._queued[ name ] === undefined ) + { + this._queued[ name ] = []; + } + + // merge individual indexes + this.merge( data[ name ], this._queued[ name ] ); + } + else + { + // no index merge; replace any existing data + this._queued[ name ] = Array.prototype.slice.call( + data[ name ], 0 + ); + + // this will ensure that no data will follow what we were + // provided + this._queued[ name ].push( null ); + } + } + + this._setTimer(); + return this; + }, + + + 'private _setTimer': function() + { + // no need to re-set timers + if ( this._timer ) + { + return; + } + + // invoke when stack clears + var _self = this; + this._timer = setTimeout( function() + { + _self.processValues(); + }, 0 ); + }, + + + /** + * Retrieve the data that will result after a merge + * + * This should be used sparingly, since if this is called before data is + * actually merged into the bucket, then it is possible that the values will + * change after validations are run. + */ + 'public getPendingDataByName': function( name, diff ) + { + diff = diff || this._queued; + + var pending = this.getDataByName['super'].call( this, name ); + if ( !( this._queued[ name ] || diff[ name ] ) ) + { + return pending; + } + + // merge the queued data + this.merge( ( this._queued[ name ] || diff[ name ] ), pending, true ); + return pending; + }, + + + 'public override getDataByName': function( name ) + { + // if enqueued data is requested, then we have no choice but to merge to + // ensure that the data is up-to-date + if ( this._queued[ name ] ) + { + this.processValues(); + } + + return this.__super.call( this, name ); + }, + + + 'public override getData': function() + { + // gah! + var _s = this.__super; + this.processValues(); + return _s.call( this ); + }, + + + 'public override each': function( c ) + { + var _s = this.__super; + this.processValues(); + return _s.call( this, c ); + }, + + + 'public override getFilledDiff': function() + { + var _s = this.__super; + this.processValues(); + return _s.call( this ); + }, + + + 'public override hasIndex': function( name, i ) + { + var _s = this.__super; + this.processValues(); + return _s.call( this, name, i ); + }, + + + 'public processValues': function() + { + // if no timer is set, then we have no data + if ( !this._timer ) + { + return this; + } + + // since additional data may be queued as a consequence of the below + // set, prepare for it by providing an empty queue + var oldqueue = this._queued; + this._queued = {}; + this._timer = 0; + + this.setValues['super'].call( this, + oldqueue, true, true + ); + + return this; + } +} ); diff --git a/src/bucket/QuoteDataBucket.js b/src/bucket/QuoteDataBucket.js new file mode 100644 index 0000000..b5c3719 --- /dev/null +++ b/src/bucket/QuoteDataBucket.js @@ -0,0 +1,324 @@ +/** + * Key/value store + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of liza. + * + * liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Class = require( 'easejs' ).Class, + Bucket = require( './Bucket' ), + EventEmitter = require( 'events' ).EventEmitter; + +/** + * General key/value store for document + * + * The term "Quote" here is an artifact from the initial design of the + * system used for insurance quoting. It will be renamed. + * + * @todo Rename to DocumentDataBucket + */ +module.exports = Class( 'QuoteDataBucket' ) + .implement( Bucket ) + .extend( EventEmitter, +{ + /** + * Triggered when data in the bucket is updated, before it's committed + * @type {string} + */ + 'const EVENT_UPDATE': 'update', + + + /** + * Raw key/value store + * @type {Object} + */ + 'private _data': {}, + + + /** + * Cleans a name for use in the bucket + * + * Removes trailing brackets, if present + * + * @return {string} cleaned name + */ + 'private _cleanName': function( name ) + { + name = ''+name || ''; + + var bracket = name.indexOf( '[' ); + if ( bracket == -1 ) + { + return name; + } + + return name.substring( 0, bracket ); + }, + + + /** + * Explicitly sets the contents of the bucket + * + * @param {Object.} data associative array of the data + * + * @param {boolean} merge_index whether to merge indexes individually + * @param {boolean} merge_null whether to merge undefined values (vs + * ignore) + * + * @return {QuoteDataBucket} self to allow for method chaining + */ + 'public setValues': function( data, merge_index, merge_null ) + { + this._mergeData( data, merge_index, merge_null ); + return this; + }, + + + /** + * Alias of setValues + * + * @return {QuoteDataBucket} self to allow for method chaining + */ + 'public setCommittedValues': function() + { + return this.setValues.apply( this, arguments ); + }, + + + /** + * Clears all data from the bucket + * + * @return {QuoteDataBucket} self + */ + 'public clear': function() + { + this._data = {}; + return this; + }, + + + /** + * Merges updated data with the existing data + * + * @param Object data updated data + * + * @return undefined + */ + 'private _mergeData': function( data, merge_index, merge_null ) + { + merge_index = !!merge_index; // default false + merge_null = !!merge_null; // default false + + var ignore = {}; + + // remove any data that has not been updated (the hooks do processing on + // this data, often updating the DOM, so it's faster to do this than to + // have a lot of unnecessary DOM work done) + for ( name in data ) + { + var data_set = data[ name ], + pre_set = this._data[ name ], + changed = false; + + // if there's no previous data for this key, or the lengths vary, + // then we want to keep it + if ( ( pre_set === undefined ) + || ( pre_set.length !== data_set.length ) + ) + { + continue; + } + + for ( var i = 0, len = data_set.length; i < len; i++ ) + { + if ( data_set[ i ] !== pre_set[ i ] ) + { + changed = true; + break; + } + } + + // data matches original---we do not want to delete it, since that + // would modify the provided object; instead, mark it to be ignored + if ( changed === false ) + { + ignore[ name ] = true; + } + } + + this.emit( this.__self.$('EVENT_UPDATE'), data ); + + for ( name in data ) + { + if ( ignore[ name ] ) + { + continue; + } + + var data_set = data[ name ]; + + // if we're not supposed to merge the indexes one by one, just set + // it + if ( merge_index === false ) + { + this._data[name] = data_set; + continue; + } + + // initialize it if its undefined in the bucket + if ( this._data[name] === undefined ) + { + this._data[name] = []; + } + + // merge the indexes one by one to preserve existing data + var data_set_len = data_set.length; + for ( var i = 0; i < data_set_len; i++ ) + { + // ignore undefined (since we're merging, if it's not set, then + // we don't want to remove the data that's already there) + if ( data_set[ i ] === undefined ) + { + continue; + } + + // ignore if set to null (implying the index was removed) + if ( !merge_null && data_set[i] === null ) + { + // this marks the end of the array as far as we're concerned + this._data[ name ].length = i; + break; + } + + this._data[name][i] = data_set[i]; + } + } + }, + + + /** + * Overwrites values in the original bucket + * + * For this buckeet, overwriteValues() is an alias for setValues() without + * index merging. However, other Bucket implementations may handle it + * differently. + * + * @param {Object.} data associative array of the data + * + * @return {Bucket} self + */ + 'public overwriteValues': function( data ) + { + this.setValues( data, false ); + return this; + }, + + + /** + * Calls a function for each each of the values in the bucket + * + * Note: This format is intended to be consistent with Array.forEach() + * + * @param {function(string,string)} callback function to call for each + * value in the bucket + * + * @return {QuoteDataBucket} self to allow for method chaining + */ + 'public each': function( callback ) + { + var bucket = this; + + for ( var name in this._data ) + { + callback( this._data[ name ], name ); + } + + return this; + }, + + + /** + * Calls a function for each each of the values in the bucket matching the + * given predicate + * + * @param {function(string)} pred predicate + * @param {function( Object, number )} c function to call for each + * value in the bucket + * + * @return {StagingBucket} self + */ + 'public filter': function( pred, c ) + { + this.each( function( data, name ) + { + if ( pred( name ) ) + { + c( data, name ); + } + } ); + }, + + + /** + * Returns the data for the requested field + * + * @param {string} name name of the field (with or without trailing brackets) + * + * @return {Array} data for the field, or empty array if none + */ + 'public getDataByName': function( name ) + { + var data = this._data[ this._cleanName( name ) ]; + + if ( data === undefined ) + { + return []; + } + + if ( data === null ) + { + return null; + } + + // return a copy of the data + return ( typeof data === 'object' ) ? data.slice( 0 ) : data; + }, + + + /** + * Returns the data as a JSON string + * + * @return {string} data represented as JSON + */ + 'public getDataJson': function() + { + return JSON.stringify( this._data ); + }, + + + /** + * Return raw bucket data + * + * TODO: remove; breaks encapsulation + * + * @return {Object} raw bucket data + */ + 'public getData': function() + { + return this._data; + } +} ); diff --git a/src/bucket/StagingBucket.js b/src/bucket/StagingBucket.js new file mode 100644 index 0000000..ec2751d --- /dev/null +++ b/src/bucket/StagingBucket.js @@ -0,0 +1,515 @@ +/** + * Staging key/value store + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of liza. + * + * liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Class = require( 'easejs' ).Class, + Bucket = require( './Bucket' ), + EventEmitter = require( 'events' ).EventEmitter; + + +/** + * Stages and merges values into underlying key/value store + */ +module.exports = Class( 'StagingBucket' ) + .implement( Bucket ) + .extend( EventEmitter, +{ + /** + * Triggered when data in the bucket is updated, before it's committed + * @type {string} + */ + 'const EVENT_UPDATE': 'update', + + 'const EVENT_STAGING_PRE_UPDATE': 'preStagingUpdate', + 'const EVENT_STAGING_UPDATE': 'stagingUpdate', + + 'const EVENT_PRE_COMMIT': 'preCommit', + 'const EVENT_COMMIT': 'commit', + + 'const EVENT_STAGING_PRE_REVERT': 'preRevert', + 'const EVENT_STAGING_REVERT': 'revert', + 'const EVENT_STAGING_POST_REVERT': 'postRevert', + + + /** + * Bucket to wrap + * @type {Bucket} + */ + 'private _bucket': null, + + + /** + * Contains staged (uncommitted) data + * @type {Object.} + */ + 'private _staged': {}, + + /** + * Represents the current state of the bucket for fast retrieval + * @type {Object.} + */ + 'private _curdata': {}, + + /** + * Whether data is staged but not committed + * + * Ah, brining back the "dirty" term from the good 'ol days of the "dirty + * bucket"! + * + * @type {boolean} + */ + 'private _dirty': false, + + + /** + * Initializes staging bucket with the provided data bucket + * + * @param {Bucket} bucket bucket in which to store data + * + * @return {undefined} + */ + 'public __construct': function( bucket ) + { + this._bucket = bucket; + + var _self = this, + _event = this.__self.$('EVENT_UPDATE'); + + // forward events + bucket.on( _event, function( data ) + { + _self.emit( _event, data ); + }); + + this._initState(); + }, + + + 'private _initState': function() + { + var data = this._bucket.getData(), + retdata = function() {}; + + // ensure that we don't modify the original data + retdata.prototype = data; + + this._curdata = new retdata(); + this._dirty = false; + }, + + + 'protected merge': function( src, dest, nonull ) + { + nonull = !!nonull; + + var len = src.length; + for ( var i = 0; i < len; i++ ) + { + var data = src[ i ]; + + // don't merge if it's undefined or if null and nulls were not + // permitted + if ( data === undefined ) + { + continue; + } + else if ( nonull && ( data === null ) ) + { + // nulls mark the end of the set + dest.length = i; + break; + } + + // merge with bucket data + dest[ i ] = data; + } + }, + + + /** + * Analgous to setValues(), but immediately commits the changes + * + * This still calls setValues() to ensure all events are properly kicked + * off. + */ + 'public setCommittedValues': function( data /*, ...*/ ) + { + this._bucket.setValues.apply( this._bucket, arguments ); + + // no use in triggering a pre-update, since these values are + // already committed + this.emit( this.__self.$('EVENT_STAGING_UPDATE'), data ); + + return this; + }, + + + /** + * Explicitly sets the contents of the bucket + * + * @param {Object.} data associative array of the data + * + * @param {boolean} merge_index whether to merge indexes individually + * @param {boolean} merge_null whether to merge undefined values (vs + * ignore) + * + * @return {Bucket} self + */ + 'virtual public setValues': function( data, merge_index, merge_null ) + { + this.emit( this.__self.$('EVENT_STAGING_PRE_UPDATE'), data ); + + for ( name in data ) + { + var item = Array.prototype.slice.call( data[ name ], 0 ); + + // initialize as array if necessary + if ( this._staged[ name ] === undefined ) + { + this._staged[ name ] = []; + } + + // since _curdata's prototype is a reference to the internal data of + // the other bucket (gah!---for perf reasons), we need to take care + // to ensure that we do not modify it...this accomplishes that + if ( Object.hasOwnProperty.call( this._curdata, name ) === false ) + { + if ( this._curdata[ name ] !== undefined ) + { + this._curdata[ name ] = Array.prototype.slice.call( + this._curdata[ name ], 0 + ); + } + else + { + this._curdata[ name ] = []; + } + } + + if ( merge_index ) + { + // merge with previous values + this.merge( item, this._staged[ name ] ); + + // we do not want nulls in our current representation of the + // data + this.merge( item, this._curdata[ name ], true ); + } + else + { + // overwrite + this._staged[ name ] = item; + this._curdata[ name ] = item; + } + } + + this._dirty = true; + this.emit( this.__self.$('EVENT_STAGING_UPDATE'), data ); + + return this; + }, + + + /** + * Overwrites values in the original bucket + * + * @param {Object.} data associative array of the data + * + * @return {StagingBucket} self + */ + 'public overwriteValues': function( data ) + { + var new_data = {}; + + for ( name in data ) + { + new_data[ name ] = Array.prototype.slice.call( data[ name ], 0 ); + + // a terminating null ensures all data is overwritten, rather than + // just the beginning indexes + new_data[ name ].push( null ); + } + + return this.setValues( new_data, false ); + }, + + + /** + * Returns staged data + * + * @return {Object.} + */ + 'public getDiff': function() + { + return this._staged; + }, + + + /** + * Returns a field-oriented diff filled with all values rather than a + * value-oriented diff + * + * Only the fields that have changed are returned. Each field contains its + * actual value---not the diff representation of what portions of the field + * have changed. + * + * return {Object} filled diff + */ + 'virtual public getFilledDiff': function() + { + var ret = {}; + + // return each staged field + for ( var field in this._staged ) + { + // retrieve the current value for this field + ret[ field ] = Array.prototype.slice.call( + this._curdata[ field ], 0 + ); + } + + return ret; + }, + + + /** + * Reverts staged changes, preventing them from being committed + * + * This will also generate a diff and raise the same events that would be + * raised by setting values in the conventional manner, allowing reverts to + * transparently integrate with the remainder of the system. + * + * @return {StagingBucket} self + */ + 'public revert': function( evented ) + { + evented = ( evented === undefined ) ? true : !!evented; + + var data = {}; + + // generate data for this revert (so that hooks may properly handle it) + for ( var name in this._staged ) + { + var curstaged = this._staged[ name ], + orig = this._bucket.getDataByName( name ); + + data[ name ] = []; + for ( var i in curstaged ) + { + // if the original value is undefined, then we want to remove + // the value entirely, *not* set it to undefiend (which would + // affect the length of the array) + if ( orig[ i ] === undefined ) + { + delete data[ name ][ i ]; + continue; + } + + data[ name ][ i ] = orig[ i ]; + } + } + + if ( evented ) + { + this.emit( this.__self.$('EVENT_STAGING_PRE_REVERT'), data ); + this.emit( this.__self.$('EVENT_STAGING_PRE_UPDATE'), data ); + } + + this._staged = {}; + this._initState(); + + // everything after this point is evented + if ( !( evented ) ) + { + return this; + } + + // trigger revert after update (since we did preRevert before update; + // this also allows logic to disable further updates; DO NOT CHANGE + // ORDER WITHOUT LOOKING AT WHAT USES THIS!) + this.emit( this.__self.$('EVENT_STAGING_UPDATE'), data ); + this.emit( this.__self.$('EVENT_STAGING_REVERT'), data ); + + // a distinct event lets hooks know that a revert has been completed + // (which may be useful for allowing asychronous data to be + // automatically committed following a revert, rather than once again + // allowing the staging bucket to be considred dirty) + this.emit( this.__self.$('EVENT_STAGING_POST_REVERT'), data ); + + return this; + }, + + + /** + * Commits staged changes, merging them with the bucket + * + * @return {StagingBucket} self + */ + 'public commit': function( store ) + { + var old = this._staged; + + this.emit( this.__self.$('EVENT_PRE_COMMIT') ); + + this._bucket.setValues( this._staged, true, false ); + this._staged = {}; + + this.emit( this.__self.$('EVENT_COMMIT') ); + + this._initState(); + + if ( typeof store === 'object' ) + { + store.old = old; + } + + return this; + }, + + + /** + * Clears all data from the bucket + * + * @return {Bucket} self + */ + 'public clear': function() + { + this._bucket.clear(); + return this; + }, + + + /** + * Calls a function for each each of the values in the bucket + * + * @param {function( Object, number )} callback function to call for each + * value in the bucket + * + * @return {Bucket} self + */ + 'virtual public each': function( callback ) + { + for ( var name in this._curdata ) + { + callback( this._curdata[ name ], name ); + } + + return this; + }, + + + /** + * Returns the data for the requested field + * + * WARNING: This can be a potentially expensive operation if there is a + * great deal of staged data. The staged data is merged with the bucket data + * on each call. Do not make frequent calls to retrieve the same data. Cache + * it instead. + * + * @param {string} name field name (with or without trailing brackets) + * + * @return {Array} data for the field, or empty array if none + */ + 'virtual public getDataByName': function( name ) + { + if ( this._curdata[ name ] ) + { + // important: return a clone so that operations on this data doesn't + // modify the bucket without us knowing! + return Array.prototype.slice.call( this._curdata[ name ] ); + } + + return []; + }, + + + /** + * Returns original bucket data by name, even if there is data staged atop + * of it + * + * There is no additional overhead of this operation versus getDataByName() + * + * @param {string} name field name (with or without trailing brackets) + * + * @return {Array} data for the field, or empty array if none + */ + 'public getOriginalDataByName': function( name ) + { + return this._bucket.getDataByName( name ); + }, + + + /** + * Returns the data as a JSON string + * + * @return {string} data represented as JSON + */ + 'public getDataJson': function() + { + return this._bucket.getDataJson(); + }, + + + /** + * Return raw bucket data + * + * todo: remove; breaks encapsulation + * + * @return {Object} raw bucket data + */ + 'virtual public getData': function() + { + return this._curdata; + }, + + + /** + * Calls a function for each each of the values in the bucket matching the + * given predicate + * + * @param {function(string)} pred predicate + * @param {function( Object, number )} c function to call for each + * value in the bucket + * + * @return {StagingBucket} self + */ + 'public filter': function( pred, c ) + { + this.each( function( data, name ) + { + if ( pred( name ) ) + { + c( data, name ); + } + } ); + }, + + + 'virtual public hasIndex': function( name, i ) + { + return ( this._curdata[ name ][ i ] !== undefined ); + }, + + + 'public isDirty': function() + { + return this._dirty; + } +} ); diff --git a/src/bucket/StagingBucketAutoDiscard.js b/src/bucket/StagingBucketAutoDiscard.js new file mode 100644 index 0000000..10d1dbf --- /dev/null +++ b/src/bucket/StagingBucketAutoDiscard.js @@ -0,0 +1,103 @@ +/** + * Automatically discard staging bucket contents + * + * Copyright (C) 2017 LoVullo Associates, Inc. + * + * This file is part of liza. + * + * liza is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +var Class = require( 'easejs' ).Class, + StagingBucket = require( './StagingBucket' ); + + +/** + * When enabled, automatically discards staging bucket contents on change. + * + * This may be useful when the staging bucket should be put into a temporary + * state where changes should be disallowed (e.g. in the middle of a + * revert). + */ +module.exports = Class( 'StagingBucketAutoDiscard', +{ + /** + * Automatically discards all staged data before it is processed for the + * given bucket + * + * This deletes the data before it is even merged into the staging bucket. + * + * N.B.: This method silently reverts any staged data currently in the + * bucket (without triggering any events), so it is important to handle + * existing data properly before calling this (unless the data is garbage). + * + * @param {StagingBucket} bucket staging bucket to enable on + * + * @return {StagingBucketAutoDiscard} self + */ + 'public enable': function( bucket ) + { + if ( !( Class.isA( StagingBucket, bucket ) ) ) + { + throw Error( 'Required StagingBucket' ); + } + + // Unhook to prevent duplicate event hooks. + this.disable( bucket ); + + // prevent the data write from even being attempted and revert anything + // that may be currently in the bucket (without triggering events) + bucket + .on( 'preStagingUpdate', this._clobberFields ) + .revert( false ); + + return this; + }, + + + /** + * Disables auto-discard on the given bucket + * + * @param {StagingBucket} bucket staging bucket to disable on + * + * @return {StagingBucketAutoDiscard} self + */ + 'public disable': function( bucket ) + { + if ( !( Class.isA( StagingBucket, bucket ) ) ) + { + throw Error( 'Required StagingBucket' ); + } + + bucket.removeListener( 'preStagingUpdate', this._clobberFields ); + return this; + }, + + + /** + * Deletes all fields on the given object + * + * @param {Object} data object to clobber + * + * @return {undefined} + */ + 'private _clobberFields': function( data ) + { + for ( var field in data ) + { + // oops! + delete data[ field ]; + } + } +} ); diff --git a/src/client/ClientDependencyFactory.js b/src/client/ClientDependencyFactory.js index bdf711d..178cdc5 100644 --- a/src/client/ClientDependencyFactory.js +++ b/src/client/ClientDependencyFactory.js @@ -55,10 +55,10 @@ var Step = require( '../step/Step' ), BaseQuote = require( 'program/quote/BaseQuote' ), QuoteClient = require( 'program/QuoteClient' ), - QuoteDataBucket = require( 'program/QuoteDataBucket' ), - StagingBucket = require( 'program/StagingBucket' ), - StagingBucketAutoDiscard = require( 'program/StagingBucketAutoDiscard' ), - DelayedStagingBucket = require( 'program/bucket/DelayedStagingBucket' ), + QuoteDataBucket = require( '../bucket/QuoteDataBucket' ), + StagingBucket = require( '../bucket/StagingBucket' ), + StagingBucketAutoDiscard = require( '../bucket/StagingBucketAutoDiscard' ), + DelayedStagingBucket = require( '../bucket/DelayedStagingBucket' ), DataValidator = require( '../validate/DataValidator' ), ValidStateMonitor = require( '../validate/ValidStateMonitor' ),