Last active
December 12, 2015 08:59
-
-
Save cburgdorf/4747825 to your computer and use it in GitHub Desktop.
Sneak peek of the new ViewCache that will be the heart of our new optimized caching :)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Ext.define('CouchCommerce.infrastructure.ViewCache', { | |
xtype: 'viewcache', | |
extend: 'Ext.Component', | |
constructor: function(){ | |
this._viewStore = {}; | |
this._viewStack = []; | |
}, | |
/** | |
* @public | |
* This method pushes new views on the cache | |
* @param {object} options | |
* options.name: name for the handle (will create a unique name if not specified) | |
* options.view: view instance to be pushed on the cache | |
* options.maxInstanceCount: maximum number of views allowed to be cached | |
* if not provided, views can be pushed indefinitely on the cache | |
* options.instancePredicate: a function to build a signature from the view | |
* on which instances should be counted as equal. If not provided views | |
* will be counted as equal based on their $className (e.g. Ext.Container) | |
* @return {string} name of the created handle | |
*/ | |
push: function(options){ | |
var me = this, | |
name = options.name, | |
view = options.view, | |
maxInstanceCount = options.maxInstanceCount, | |
instancePredicate = options.instancePredicate; | |
//in case no name was provided, create a unique one | |
if(name === undefined){ | |
name = this._createGuid(); | |
} | |
//we need to store the name on the view in order to remove the handle | |
//on the _viewStore when the view is destroyed | |
view.___viewStackName = name; | |
//a predicate to pick a signature on which instances should be counted | |
//as equal. If not provided we assume the $className (e.g. Ext.Container) | |
if (instancePredicate === undefined){ | |
instancePredicate = this._getClassName; | |
} | |
if(!view){ | |
this._throw('view is mandatory') | |
} | |
if(this._viewStore[name]){ | |
this._throw(name + ' is already in use'); | |
} | |
//a cleanup should only run in case the maximum amount of instances is | |
//restricted | |
if(maxInstanceCount > 0){ | |
me._cleanupIfNeeded(view, maxInstanceCount, instancePredicate); | |
} | |
//create a handle and put the view in the stack | |
this._viewStore[name] = view; | |
this._viewStack.push(view); | |
return name; | |
}, | |
/** | |
* @public | |
* This method enumerates the views from new to old. It's to give us the | |
* chance to count instances of the same type and then directly delete | |
* the oldest one without enumerating the array again | |
* @param {Function} fn to be applied on the current item. Get's the item | |
* and the index as parameter | |
*/ | |
enumerateReverse: function(fn){ | |
for (var i = this._viewStack.length - 1; i >= 0; i--) { | |
var ret = fn(this._viewStack[i], i); | |
if (ret){ | |
continue; | |
} | |
else if(ret === false){ | |
break; | |
} | |
} | |
}, | |
/** | |
* @public | |
* Get cached instance of view | |
* @param {String} name of the view to be retrieved from the cache | |
*/ | |
getView: function(name){ | |
return this._viewStore[name]; | |
}, | |
_cleanupIfNeeded: function(view, maxInstanceCount, instancePredicate){ | |
var me = this, | |
//find the signature on which instances should be counted | |
//for equality | |
signature = instancePredicate(view), | |
count = 1; | |
//enumerate all views from new to old | |
this.enumerateReverse(function(iView, index){ | |
var currentSignature = instancePredicate(iView); | |
if (currentSignature === signature){ | |
//count instances with the same signature | |
count++; | |
if (count > maxInstanceCount){ | |
//if we have more instances of a kind than allowed: | |
// * delete the direct handle | |
// * destroy the view | |
// * remove it from the stack | |
var viewName = iView.___viewStackName; | |
iView.destroy(); | |
delete me._viewStore[viewName]; | |
me._viewStack.splice(index, 1); | |
//make the iteration stop since we found the item to be | |
//deleted. | |
return false; | |
} | |
} | |
}); | |
}, | |
_getClassName: function(view){ | |
return view.$className; | |
}, | |
_throw: function(message){ | |
throw new Error(message); | |
}, | |
_createGuid: function(){ | |
//http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript | |
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { | |
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); | |
return v.toString(16); | |
}); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test('can create ViewCache instance', function() { | |
var viewCache = Ext.create('CouchCommerce.infrastructure.ViewCache'); | |
ok( viewCache !== undefined, 'ViewCache instance created' ); | |
}); | |
test('can add view', function() { | |
var viewCache = Ext.create('CouchCommerce.infrastructure.ViewCache'); | |
var view1 = Ext.create('Ext.Container'); | |
ok( view1 !== undefined, 'View created' ); | |
var viewName = viewCache.push({name: 'view1', view: view1}); | |
var myView = viewCache.getView('view1'); | |
ok(viewCache !== undefined, 'ViewCache instance created'); | |
ok(myView === view1, 'View instance retrieved'); | |
ok(viewName === 'view1', 'returns view name'); | |
}); | |
test('can add view without name', function() { | |
var viewCache = Ext.create('CouchCommerce.infrastructure.ViewCache'); | |
var view1 = Ext.create('Ext.Container'); | |
ok( view1 !== undefined, 'View created' ); | |
var viewName = viewCache.push({view: view1}); | |
var myView = viewCache.getView(viewName); | |
ok(viewCache !== undefined, 'ViewCache instance created'); | |
ok(myView === view1, 'View instance retrieved'); | |
ok(viewName.length > 1, 'view has a name'); | |
}); | |
test('cant implicitly override name', function() { | |
var viewCache = Ext.create('CouchCommerce.infrastructure.ViewCache'); | |
var view1 = Ext.create('Ext.Container'); | |
var view2 = Ext.create('Ext.Container'); | |
viewCache.push({name: 'view1', view: view1}); | |
expect(1); | |
try{ | |
viewCache.push({name: 'view1', view: view2}); | |
} | |
catch(ex){ | |
ok(true, 'cant override name'); | |
} | |
}); | |
test('removes first instance after two other instances of same kind were pushed', function() { | |
var viewCache = Ext.create('CouchCommerce.infrastructure.ViewCache'); | |
var view1 = Ext.create('Ext.Container'); | |
var view2 = Ext.create('Ext.Container'); | |
var view3 = Ext.create('Ext.Container'); | |
var button = Ext.create('Ext.Button'); | |
var view1Name = viewCache.push({view: view1}); | |
var view2Name = viewCache.push({view: view2}); | |
var view3Name = viewCache.push({view: view3, maxInstanceCount: 2}); | |
//just making sure that other view types don't interfere | |
var buttonName = viewCache.push({view: button, maxInstanceCount: 2}); | |
var tview1 = viewCache.getView(view1Name); | |
var tview2 = viewCache.getView(view2Name); | |
var tview3 = viewCache.getView(view3Name); | |
var tbutton = viewCache.getView(buttonName); | |
ok(tview1 === undefined, 'view was removed'); | |
ok(tview2 === view2, 'view still exits'); | |
ok(tview3 === view3, 'view still exits'); | |
ok(tbutton === button, 'view still exits'); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment