From 2f94a513bd9eece8254c5ce022178e982b18f839 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 8 Aug 2011 11:40:30 +0200 Subject: [PATCH] [ADD] method to extend classes in place (w/o reassignment) semantics similar to ruby's Module.include bzr revid: xmo@openerp.com-20110808094030-qfmi2s14vqz2nybz --- addons/base/static/src/js/core.js | 29 ++++++++++ addons/base/static/test/class.js | 88 +++++++++++++++++++++++++++++-- 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/addons/base/static/src/js/core.js b/addons/base/static/src/js/core.js index e8c2a104bb2..a96ff7d1b65 100644 --- a/addons/base/static/src/js/core.js +++ b/addons/base/static/src/js/core.js @@ -56,6 +56,35 @@ openerp.base.core = function(openerp) { } return this; } + Class.mix = function (properties) { + for (var name in properties) { + if (typeof properties[name] !== 'function' + || !fnTest.test(properties[name])) { + prototype[name] = properties[name]; + } else if (typeof prototype[name] === 'function' + && prototype.hasOwnProperty(name)) { + prototype[name] = (function (name, fn, previous) { + return function () { + var tmp = this._super; + this._super = previous; + var ret = fn.apply(this, arguments); + this._super = tmp; + return ret; + } + })(name, properties[name], prototype[name]); + } else if (typeof _super[name] === 'function') { + prototype[name] = (function (name, fn) { + return function () { + var tmp = this._super; + this._super = _super[name]; + var ret = fn.apply(this, arguments); + this._super = tmp; + return ret; + } + })(name, properties[name]); + } + } + }; // Populate our constructed prototype object Class.prototype = prototype; diff --git a/addons/base/static/test/class.js b/addons/base/static/test/class.js index 171729c65f8..f188d3920e5 100644 --- a/addons/base/static/test/class.js +++ b/addons/base/static/test/class.js @@ -1,6 +1,6 @@ $(document).ready(function () { var openerp; - module('Base Class', { + module('base-class', { setup: function () { openerp = window.openerp.init(); window.openerp.base.core(openerp); @@ -12,11 +12,11 @@ $(document).ready(function () { return this.somevar; } }); - var i1 = new C(); - i1.somevar = 3; + var instance = new C(); + instance.somevar = 3; - ok(i1 instanceof C); - strictEqual(i1.foo(), 3); + ok(instance instanceof C); + strictEqual(instance.foo(), 3); }); test('Class initialization', function () { var C1 = openerp.base.Class.extend({ @@ -57,4 +57,82 @@ $(document).ready(function () { strictEqual(new C1().foo(), 2); strictEqual(new C2().foo(), 3); }); + test('In-place extension', function () { + var C0 = openerp.base.Class.extend({ + foo: function () { + return 3; + }, + qux: function () { + return 3; + }, + bar: 3 + }); + C0.mix({ + foo: function () { + return 5; + }, + qux: function () { + return 2 + this._super(); + }, + bar: 5, + baz: 5 + }); + + strictEqual(new C0().bar, 5); + strictEqual(new C0().baz, 5); + strictEqual(new C0().foo(), 5); + strictEqual(new C0().qux(), 5); + }); + test('In-place extension and inheritance', function () { + var C0 = openerp.base.Class.extend({ + foo: function () { return 1; }, + bar: function () { return 1; } + }); + var C1 = C0.extend({ + foo: function () { return 1 + this._super(); } + }); + strictEqual(new C1().foo(), 2); + strictEqual(new C1().bar(), 1); + + C1.mix({ + foo: function () { return 2 + this._super(); }, + bar: function () { return 1 + this._super(); } + }); + strictEqual(new C1().foo(), 4); + strictEqual(new C1().bar(), 2); + }); + test('In-place extensions alter existing instances', function () { + var C0 = openerp.base.Class.extend({ + foo: function () { return 1; }, + bar: function () { return 1; } + }); + var instance = new C0(); + strictEqual(instance.foo(), 1); + strictEqual(instance.bar(), 1); + + C0.mix({ + foo: function () { return 2; }, + bar: function () { return 2 + this._super(); } + }); + strictEqual(instance.foo(), 2); + strictEqual(instance.bar(), 3); + }); + test('In-place extension of subclassed types', function () { + var C0 = openerp.base.Class.extend({ + foo: function () { return 1; }, + bar: function () { return 1; } + }); + var C1 = C0.extend({ + foo: function () { return 1 + this._super(); }, + bar: function () { return 1 + this._super(); } + }); + var instance = new C1(); + strictEqual(instance.foo(), 2); + C0.mix({ + foo: function () { return 2; }, + bar: function () { return 2 + this._super(); } + }); + strictEqual(instance.foo(), 3); + strictEqual(instance.bar(), 4); + }); });