UNPKG

dojox

Version:

Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.

133 lines (132 loc) 4.87 kB
define(['dojo/store/Memory', 'dojo/store/Cache', 'dojo/when', 'dojo/aspect', 'dojo/_base/lang'], function(Memory, Cache, when, aspect, lang){ // summary: // This is a transaction managing store. When transaction is started, by calling store.transaction(), // all request operations (put, add, remove), are entered into a transaction log. When commit() is // called, the actions in the log are then committed. This store relies on three other stores, // extending the dojo/store/Cache: // * masterStore - like with Cache, this store is the ultimate authority, and is usually connected to the backend // * cachingStore - This store includes local data, including changes within the current transaction // * transactionLogStore - This store holds the log of actions that need to be committed to the masterStore on the next commit var defaultTransactionLogStore; var stores = {}; var nextStoreId = 1; return function(options){ options = options || {}; var masterStore = options.masterStore; var cachingStore = options.cachingStore; var storeId = masterStore.id || masterStore.storeName || masterStore.name || (masterStore.id = nextStoreId++); if(storeId){ stores[storeId] = masterStore; } var transactionLogStore = options.transactionLogStore || defaultTransactionLogStore || (defaultTransactionLogStore = new Memory()); var autoCommit = true; function addToLog(method){ return function execute(target, options){ var transactionStore = this; if(autoCommit){ // immediately perform the action var result = masterStore[method](target, options); when(result, null, function(e){ if(transactionStore.errorHandler(e)){ // failed, and the errorHandler has signaled that it should be requeued autoCommit = false; options.error = e; execute.call(transactionStore, target, options); autoCommit = true; } }); return result; }else{ // add to the transaction log var previousId = method === 'remove' ? target : transactionStore.getIdentity(target); if(previousId !== undefined){ var previous = cachingStore.get(previousId); } return when(previous, function(previous){ return when(transactionLogStore.add({ objectId: previousId, method: method, target: target, previous: previous, options: options, storeId: storeId }), function(){ return target; }); }); } }; } // we need to listen for any notifications from the master store, and propagate these to the caching store // this arguably should actually be done in dojo/store/Cache aspect.before(masterStore, 'notify', function(object, existingId){ if(object){ cachingStore.put(object); }else{ cachingStore.remove(existingId); } }); return new Cache(lang.delegate(masterStore, { put: addToLog('put'), add: addToLog('add'), remove: addToLog('remove'), errorHandler: function(error){ // this is called whenever an error occurs when attempting to commit transactional actions // this can return true to indicate that the action should be reattempted in the next commit // this can return false to indicate that the action should be reverted // if this returns undefined, no action will be taken console.error(error); return true; }, commit: function(){ // commit everything in the transaction log autoCommit = true; var transactionStore = this; // query for everything in the log return transactionLogStore.query({}).map(function(action){ //var options = action.options || {}; var method = action.method; var store = stores[action.storeId]; var target = action.target; var result; try{ // execute the queued action result = store[method](target, action.options); }catch(e){ result = transactionStore.errorHandler(e); if(result === true){ // don't remove it from the log, let it be executed again return e; }else if(result === false){ // revert, by sending out a notification and updating the caching store if(method === 'add'){ cachingStore.remove(action.objectId); }else{ cachingStore.put(target); } store.notify && store.notify(method === 'add' ? null : action.previous, method === 'remove' ? undefined : action.objectId); } result = e; } // fired it, can remove now // TODO: handle async transactionLogStore.remove(action.id); return result; }); }, transaction: function(){ // start a new transaction (by just turning off autoCommit) autoCommit = false; var transactionStore = this; return { commit: function(){ return transactionStore.commit(); } }; } }), cachingStore, options); }; });