[ADD] basics for smarter collections to back the listview and its components

bzr revid: xmo@openerp.com-20110817085532-4chvix1odshuebvc
This commit is contained in:
Xavier Morel 2011-08-17 10:55:32 +02:00
parent ed9db7c2c7
commit 4ce473b8dc
4 changed files with 317 additions and 1 deletions

View File

@ -10,6 +10,9 @@ openerp.base.core = function(openerp) {
var initializing = false,
fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
/**
* @class
*/
openerp.base.Class = function(){};
// Create a new Class that inherits from this class

View File

@ -1179,7 +1179,164 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
});
}
});
/**
* @class
* @extends openerp.base.Class
*/
var Events = {
/**
* @param {String} event event to listen to on the current object, null for all events
* @param {Function} handler event handler to bind to the relevant event
* @returns this
*/
bind: function (event, handler) {
var calls = this['_callbacks'] || (this._callbacks = {});
if (event in calls) {
calls[event].push(handler);
} else {
calls[event] = [handler];
}
return this;
},
/**
* @param {String} event
* @returns this
*/
trigger: function (event) {
var calls;
if (!(calls = this._callbacks)) { return this; }
var callbacks = (calls[event] || []).concat(calls[null] || []);
for(var i=0, length=callbacks.length; i<length; ++i) {
callbacks[i].apply(this, arguments);
}
return this;
}
};
var Record = openerp.base.Class.extend(/** @lends Record# */{
/**
* @constructs
* @extends openerp.base.Class
* @borrows Events#bind as this.bind
* @borrows Events#trigger as this.trigger
* @param {Object} [data]
*/
init: function (data) {
this.data = data || {};
},
/**
* @param {String} key
* @returns {Object}
*/
get: function (key) {
return this.data[key];
},
/**
* @param key
* @param value
* @returns {Record}
*/
set: function (key, value) {
this.data[key] = value;
this.trigger('change:' + key, this, value);
this.trigger('change', this);
return this;
}
});
Record.include(Events);
var Collection = openerp.base.Class.extend(/** @lends Collection# */{
/**
* @constructs
* @extends openerp.base.Class
* @borrows Events#bind as this.bind
* @borrows Events#trigger as this.trigger
* @param {Array} [records] records to initialize the collection with
* @param {Object} [options]
*/
init: function (records, options) {
options = options || {};
_.bindAll(this, '_onRecordEvent');
this.records = [];
this._byId = {};
this._proxies = {};
this._key = options.key;
this._parent = options.parent;
if (records) {
this.add(records);
}
},
/**
* @param {Object|Array} record
* @returns this
*/
add: function (record) {
var records = record instanceof Array ? record : [record];
for(var i=0, length=records.length; i<length; ++i) {
var instance = (records[i] instanceof Record) ? records[i] : new Record(records[i]);
instance.bind(null, this._onRecordEvent);
this._byId[instance.get('id')] = instance;
this.records.push(instance);
}
return this;
},
/**
* Get a record by its index in the collection, can also take a group if
* the collection is not degenerate
*
* @param {Number} index
* @param {String} [group]
* @returns {Record|undefined}
*/
at: function (index, group) {
if (group) {
var groups = group.split('.');
return this._proxies[groups[0]].at(index, groups.join('.'));
}
return this.records[index];
},
/**
* Get a record by its database id
*
* @param {Number} id
* @returns {Record|undefined}
*/
get: function (id) {
if (!_(this._proxies).isEmpty()) {
var record = null;
_(this._proxies).detect(function (proxy) {
return record = proxy.get(id);
});
return record;
}
return this._byId[id];
},
/**
* Builds a proxy (insert/retrieve) to a subtree of the collection, by
* the subtree's group
*
* @param {String} section group path section
* @returns {Collection}
*/
proxy: function (section) {
return this._proxies[section] = new Collection(null, {
parent: this,
key: section
}).bind(null, this._onRecordEvent);
},
_onRecordEvent: function (event, record, options) {
this.trigger.apply(this, arguments);
}
});
Collection.include(Events);
openerp.base.list = {
Events: Events,
Record: Record,
Collection: Collection
}
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -0,0 +1,155 @@
$(document).ready(function () {
var openerp,
create = function (o) {
if (typeof Object.create === 'function') {
return Object.create(o);
}
function Cls() {}
Cls.prototype = o;
return new Cls;
};
module('list-events', {
setup: function () {
openerp = window.openerp.init();
window.openerp.base.list(openerp);
}
});
test('Simple event triggering', function () {
var e = create(openerp.base.list.Events), passed = false;
e.bind('foo', function () { passed = true; });
e.trigger('foo');
ok(passed);
});
test('Bind all', function () {
var e = create(openerp.base.list.Events), event = null;
e.bind(null, function (ev) { event = ev; });
e.trigger('foo');
strictEqual(event, 'foo');
});
test('Propagate trigger params', function () {
var e = create(openerp.base.list.Events), p = false;
e.bind(null, function (_, param) { p = param });
e.trigger('foo', true);
strictEqual(p, true)
});
test('Bind multiple callbacks', function () {
var e = create(openerp.base.list.Events), count;
e.bind('foo', function () { count++; })
.bind('bar', function () { count++; })
.bind(null, function () { count++; })
.bind('foo', function () { count++; })
.bind(null, function () { count++; })
.bind(null, function () { count++; });
count = 0;
e.trigger('foo');
strictEqual(count, 5);
count = 0;
e.trigger('bar');
strictEqual(count, 4);
count = 0;
e.trigger('baz');
strictEqual(count, 3);
});
test('Mixin events', function () {
var cls = openerp.base.Class.extend({
method: function () { this.trigger('e'); }
});
cls.include(openerp.base.list.Events);
var instance = new cls, triggered = false;
instance.bind('e', function () { triggered = true; });
instance.method();
ok(triggered);
});
module('list-records', {
setup: function () {
openerp = window.openerp.init();
window.openerp.base.list(openerp);
}
});
test('Basic record initialization', function () {
var r = new openerp.base.list.Record({qux: 3});
r.set('foo', 1);
r.set('bar', 2);
strictEqual(r.get('foo'), 1);
strictEqual(r.get('bar'), 2);
strictEqual(r.get('qux'), 3);
});
test('Change all the things', function () {
var r = new openerp.base.list.Record(), changed = false, field;
r.bind('change', function () { changed = true; });
r.bind(null, function (e) { field = field || e.split(':')[1]});
r.set('foo', 1);
strictEqual(r.get('foo'), 1);
ok(changed);
strictEqual(field, 'foo');
});
test('Change single field', function () {
var r = new openerp.base.list.Record(), changed = 0;
r.bind('change:foo', function () { changed++; });
r.set('foo', 1);
r.set('bar', 1);
strictEqual(r.get('foo'), 1);
strictEqual(r.get('bar'), 1);
strictEqual(changed, 1);
});
module('list-collections-degenerate', {
setup: function () {
openerp = window.openerp.init();
window.openerp.base.list(openerp);
}
});
test('Fetch from collection', function () {
var c = new openerp.base.list.Collection();
c.add({id: 1, value: 2});
c.add({id: 2, value: 3});
c.add({id: 3, value: 5});
c.add({id: 4, value: 7});
var r = c.at(2), r2 = c.get(1);
ok(r instanceof openerp.base.list.Record);
strictEqual(r.get('id'), 3);
strictEqual(r.get('value'), 5);
ok(r2 instanceof openerp.base.list.Record);
strictEqual(r2.get('id'), 1);
strictEqual(r2.get('value'), 2);
});
test('Events propagation', function () {
var values = [];
var c = new openerp.base.list.Collection([
{id: 1, value: 5},
{id: 2, value: 10},
{id: 3, value: 20}
]);
c.bind('change:value', function (e, record, value) {
values.push(value);
});
c.get(1).set('value', 6);
c.get(2).set('value', 11);
c.get(3).set('value', 21);
deepEqual(values, [6, 11, 21]);
});
test('BTree', function () {
var root = new openerp.base.list.Collection(),
c = root.proxy('admin'),
total = 0;
c.add({id: 1, name: "Administrator", login: 'admin'});
c.add({id: 3, name: "Demo", login: 'demo'});
root.bind('change:wealth', function () {
total = (root.get(1).get('wealth') || 0) + (root.get(3).get('wealth') || 0);
});
strictEqual(total, 0);
c.at(0).set('wealth', 42);
strictEqual(total, 42);
c.at(1).set('wealth', 5);
strictEqual(total, 47);
});
});

View File

@ -40,4 +40,5 @@
<script type="text/javascript" src="/base/static/test/class.js"></script>
<script type="text/javascript" src="/base/static/test/registry.js"></script>
<script type="text/javascript" src="/base/static/test/form.js"></script>
<script type="text/javascript" src="/base/static/test/list-utils.js"></script>
</html>