Config
Table of Contents

Class

O.NestedStore

A Nested Store may be used to buffer changes before committing them to the parent store. The changes may be discarded instead of committing without ever affecting the parent store.

"use strict";

( function ( NS ) {

// Same as O.Status, inlined here for efficiency:
// Core states:
var EMPTY      =   1;
var READY      =   2;
var DESTROYED  =   4;
// Properties
var LOADING    =  16; // Request made to source to fetch record or updates.
var COMMITTING =  32; // Request been made to source to commit record.
var NEW        =  64; // Record has not been committed to source.
var DIRTY      = 128; // Record has local changes not commited to source
var OBSOLETE   = 256; // Source may have changes not yet loaded.

var NestedStore = NS.Class({

 Extends: NS.Store,

 autoCommit: false,
 isNested: true,

Constructor

O.NestedStore( store )

Parameters

storeO.Store The parent store (this may be another nested store).
init: function ( store ) {
   // Own record store
   this._skToRecord = {};
   // Copy on write, shared data object store
   this._skToData = Object.create( store._skToData );
   // Copy on write, shared status store.
   this._skToStatus = Object.create( store._skToStatus );

   // Share store key -> Type
   this._skToType = store._skToType;
   // Share Type -> store key -> id
   this._typeToSkToId = store._typeToSkToId;
   // Share Type -> id -> store key
   this._typeToIdToSk = store._typeToIdToSk;

   // Own changed map
   this._skToChanged = {};
   // Own previous attributes.
   this._skToCommitted = {};
   // Not used, but needs to be present to stop error on unload
   this._skToRollback = {};

   // Share last access timestamp for
   this._skToLastAccess = store._skToLastAccess;

   this._created = {};
   this._destroyed = {};

   // Own queries
   // Map id -> query
   this._idToQuery = {};
   // Map Type -> list of local queries
   this._liveQueries = {};
   // Set of remove queries.
   this._remoteQueries = [];
   // List of types needing a refresh.
   this._queryTypesNeedRefresh = [];

   // List of nested stores
   this._nestedStores = [];

   // Type -> [ store key ] of changed records.
   this._typeToChangedSks = {};

   this._typeToStatus = store._typeToStatus;

   store.addNested( this );

   this._source = store._source;
   this._parentStore = store;
 },

Method

O.NestedStore#destroy()

Removes the connection to the parent store so this store may be garbage collected.

destroy: function () {
   this._parentStore.removeNested( this );
 },

 // === Client API ==========================================================

Method

O.Store#commitChanges( callback )

Commits any outstanding changes (created/updated/deleted records) to the parent store.

Parameters

callbackFunction Optional A callback to be made after the changes have finished committing. As a nested store commits to a parent store rather than a remote source, the callback will be fired synchronously before this method returns.

Returns

O.NestedStore Returns self.

commitChanges: function ( callback ) {
   this.fire( 'willCommit' );
   var _created = this._created,
     _destroyed = this._destroyed,
     _skToData = this._skToData,
     _skToChanged = this._skToChanged,
     parent = this._parentStore,
     storeKey, status, data;

   for ( storeKey in _created ) {
     status = parent.getStatus( storeKey );
     data = _skToData[ storeKey ];
     if ( status === EMPTY || status === DESTROYED ) {
       parent.createRecord( storeKey, data );
     } else if ( ( status & ~(OBSOLETE|LOADING) ) ===
         (DESTROYED|COMMITTING) ) {
       parent._skToData[ storeKey ] = data;
       parent.setStatus( storeKey, READY|NEW|COMMITTING );
     } else if ( status & DESTROYED ) {
       delete parent._destroyed[ storeKey ];
       parent._skToData[ storeKey ] = data;
       parent.setStatus( storeKey,
         ( status & ~(DESTROYED|DIRTY) ) | READY );
     }
   }
   for ( storeKey in _skToChanged ) {
     parent.updateData( storeKey, Object.filter(
       _skToData[ storeKey ], _skToChanged[ storeKey ] ), true );
   }
   for ( storeKey in _destroyed ) {
     parent.destroyRecord( storeKey );
   }

   this._skToData = Object.create( parent._skToData );
   this._skToStatus = Object.create( parent._skToStatus );
   this._skToChanged = {};
   this._skToCommitted = {};
   this._created = {};
   this._destroyed = {};

   if ( callback ) { callback(); }

   return this.fire( 'didCommit' );
 },

Method

O.Store#discardChanges()

Discards any outstanding changes (created/updated/deleted records), reverting the store to the same state as its parent.

Returns

O.NestedStore Returns self.

discardChanges: function () {
   NestedStore.parent.discardChanges.call( this );

   var parent = this._parentStore;

   this._skToData = Object.create( parent._skToData );
   this._skToStatus = Object.create( parent._skToStatus );

   return this;
 },

 // === Low level (primarily internal) API: uses storeKey ===================

 getStatus: function ( storeKey ) {
   var status = this._skToStatus[ storeKey ] || EMPTY;
   return this._skToData.hasOwnProperty( storeKey ) ?
     status : status & ~(NEW|COMMITTING|DIRTY);
 },

 fetchAll: function ( storeKey ) {
   this._parentStore.fetchAll( storeKey );
   return this;
 },

 fetchData: function ( storeKey ) {
   this._parentStore.fetchData( storeKey );
   return this;
 },

 // === Notifications from parent store =====================================

Method

O.NestedStore#parentDidChangeStatus( storeKey, previous, status )

Called by the parent store whenever it changes the status of a record. The nested store uses this to update its own status value for that record (if it has diverged from the parent) and to notify any O.Record instances belonging to it of the change.

Parameters

storeKeyString The store key for the record.
previousO.Status The previous status value.
statusO.Status The new status value.
parentDidChangeStatus: function ( storeKey, previous, status ) {
   var _skToStatus = this._skToStatus;

   previous = previous & ~(NEW|COMMITTING|DIRTY);
   status = status & ~(NEW|COMMITTING|DIRTY);

   if ( _skToStatus.hasOwnProperty( storeKey ) ) {
     previous = _skToStatus[ storeKey ];
     if ( status & DESTROYED ) {
       if ( !( previous & DESTROYED ) ) {
         // Ready dirty -> ready clean.
         this.setData( storeKey, this._skToCommitted[ storeKey ] );
         delete this._skToCommitted[ storeKey ];
         delete this._skToChanged[ storeKey ];
       }
       // Ready clean/Destroyed dirty -> destroyed clean.
       delete this._skToData[ storeKey ];
       delete _skToStatus[ storeKey ];
     } else {
       _skToStatus[ storeKey ] = status =
         previous|( status & (OBSOLETE|LOADING) );
     }
   }

   if ( previous !== status ) {
     // wasReady !== isReady
     if ( ( previous ^ status ) & READY ) {
       this._recordDidChange( storeKey );
     }
     var record = this._skToRecord[ storeKey ];
     if ( record ) {
       record.propertyDidChange( 'status', previous, status );
     }
     this._nestedStores.forEach( function ( store ) {
       store.parentDidChangeStatus( storeKey, previous, status );
     });
   }
 },

Method

O.NestedStore#parentDidSetData( storeKey, changedKeys )

Called by the parent store when it sets the inital data for an empty record. The nested store can't have any changes as a nested store cannot load data independently of its parent, so all we need to do is notify any records.

Parameters

storeKeyString The store key for the record.
changedKeysObject A list of keys which have changed.
parentDidSetData: function ( storeKey, changedKeys ) {
   this._notifyRecordOfChanges( storeKey, changedKeys );
   this._nestedStores.forEach( function ( store ) {
     store.parentDidSetData( storeKey, changedKeys );
   });
 },

Method

O.NestedStore#parentDidUpdateData( storeKey, changedKeys )

Called by the parent store whenever it makes a change to the data object for a record. The nested store uses this to update its own copy of the data object if it has diverged from that of the parent (either rebasing changes on top of the new parent state or discarding changes, depending on the value of O.Store#rebaseConflicts).

Parameters

storeKeyString The store key for the record.
changedKeysObject A list of keys which have changed.
parentDidUpdateData: function ( storeKey, changedKeys ) {
   if ( this._skToData.hasOwnProperty( storeKey ) ) {
     var _skToData = this._skToData,
       _skToChanged = this._skToChanged,
       _skToCommitted = this._skToCommitted,
       parent = this._parentStore,
       rebase = this.rebaseConflicts,
       newBase = parent.getData( storeKey ),
       oldData = _skToData[ storeKey ],
       oldChanged = _skToChanged[ storeKey ],
       newData = {},
       newChanged = {},
       clean = true,
       isChanged, key;

     changedKeys = [];

     for ( key in oldData ) {
       isChanged = !NS.isEqual( oldData[ key ], newBase[ key ] );
       if ( rebase && ( key in oldChanged ) ) {
         if ( isChanged ) {
           newChanged[ key ] = true;
           clean = false;
         }
         newData[ key ] = oldData[ key ];
       } else {
         if ( isChanged ) {
           changedKeys.push( key );
         }
         newData[ key ] = newBase[ key ];
       }
     }
     if ( !clean ) {
       _skToChanged[ storeKey ] = newChanged;
       _skToCommitted[ storeKey ] = NS.clone( newBase );
       this.setData( storeKey, newData );
       return;
     }
     this.setStatus( storeKey,
       parent.getStatus( storeKey ) & ~(NEW|COMMITTING|DIRTY) );
     delete _skToData[ storeKey ];
     delete _skToChanged[ storeKey ];
     delete _skToCommitted[ storeKey ];
     delete this._skToStatus[ storeKey ];
   }
   this._notifyRecordOfChanges( storeKey, changedKeys );
   this._nestedStores.forEach( function ( store ) {
     store.parentDidUpdateData( storeKey, changedKeys );
   });
   this._recordDidChange( storeKey );
 },

 // === A nested store is not directly connected to a source ================

 sourceDidFetchRecords: null,
 sourceDidFetchPartialRecords: null,
 sourceCouldNotFindRecords: null,

 sourceDidFetchUpdates: null,
 sourceDidModifyRecords: null,
 sourceDidDestroyRecords: null,

 sourceCommitDidChangeState: null,

 sourceDidCommitCreate: null,
 sourceDidNotCreate: null,
 sourceDidCommitUpdate: null,
 sourceDidNotUpdate: null,
 sourceDidCommitDestroy: null,
 sourceDidNotDestroy: null,
 sourceDidError: null
});

NS.NestedStore = NestedStore;

}( O ) );
Animation
Application
Core
DataStore
DOM
DragDrop
Foundation
IO
Localisation
Selection
Parser
TimeZones
Storage
Touch
CollectionViews
UA
ContainerViews
ControlViews
PanelViews
View