Skip to content

Instantly share code, notes, and snippets.

@cburgdorf
Last active December 12, 2015 08:59
Show Gist options
  • Save cburgdorf/4747825 to your computer and use it in GitHub Desktop.
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 :)
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);
});
}
});
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