Skip to content

Commit

Permalink
inheritClass: Implement inherited 'static' property for classes.
Browse files Browse the repository at this point in the history
Previously tests for inheritClass (and other object management
utilities) were absent (as they were copied from upstream K-js).

I've copied the upstream test suite for this method here and
extended it with tests for this new feature.

Had to add es5:true to .jshintrc due to a bug in JSHint.
Repeated the setting in ve.inheritClass for future reference.

Source: https://github.com/Krinkle/K-js/blob/master/test/K.test.js

Change-Id: I63ac620d6ce7832ebfee454ddf7b7c90f6eb6121
  • Loading branch information
Krinkle authored and Gerrit Code Review committed Oct 9, 2012
1 parent bbb38c5 commit 07c86fc
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 6 deletions.
3 changes: 2 additions & 1 deletion .jshintrc
Expand Up @@ -23,8 +23,9 @@
"strict": false,
"trailing": true,

"smarttabs": true,
"es5": true,
"multistr": true,
"smarttabs": true,

"browser": true,
"jquery": true,
Expand Down
117 changes: 116 additions & 1 deletion modules/ve/test/ve.test.js
Expand Up @@ -9,6 +9,121 @@ QUnit.module( 've' );

/* Tests */

// ve.createObject: Tested upstream (K-js)

QUnit.test( 'inheritClass', 16, function ( assert ) {
var foo, bar;

function Foo() {
this.constructedFoo = true;
}

Foo.a = 'prop of Foo';
Foo.b = 'prop of Foo';
Foo.prototype.b = 'proto of Foo';
Foo.prototype.c = 'proto of Foo';
Foo.prototype.bFn = function () {
return 'proto of Foo';
};
Foo.prototype.cFn = function () {
return 'proto of Foo';
};

foo = new Foo();

function Bar() {
this.constructedBar = true;
}
ve.inheritClass( Bar, Foo );

assert.deepEqual(
Foo.static,
{},
'A "static" property (empty object) is automatically created if absent'
);

Foo.static.a = 'static of Foo';
Foo.static.b = 'static of Foo';

assert.notStrictEqual( Foo.static, Bar.static, 'Static property is not copied, but inheriting' );
assert.equal( Bar.static.a, 'static of Foo', 'Foo.static inherits from Bar.static' );

Bar.static.b = 'static of Bar';

assert.equal( Foo.static.b, 'static of Foo', 'Change to Bar.static does not affect Foo.static' );

Bar.a = 'prop of Bar';
Bar.prototype.b = 'proto of Bar';
Bar.prototype.bFn = function () {
return 'proto of Bar';
};

bar = new Bar();

assert.strictEqual(
Bar.b,
undefined,
'Constructor properties are not inherited'
);

assert.strictEqual(
foo instanceof Foo,
true,
'foo instance of Foo'
);
assert.strictEqual(
foo instanceof Bar,
false,
'foo not instance of Bar'
);

assert.strictEqual(
bar instanceof Foo,
true,
'bar instance of Foo'
);
assert.strictEqual(
bar instanceof Bar,
true,
'bar instance of Bar'
);

assert.equal( bar.constructor, Bar, 'constructor property is restored' );
assert.equal( bar.b, 'proto of Bar', 'own methods go first' );
assert.equal( bar.bFn(), 'proto of Bar', 'own properties go first' );
assert.equal( bar.c, 'proto of Foo', 'prototype properties are inherited' );
assert.equal( bar.cFn(), 'proto of Foo', 'prototype methods are inherited' );

Bar.prototype.dFn = function () {
return 'proto of Bar';
};
Foo.prototype.dFn = function () {
return 'proto of Foo';
};
Foo.prototype.eFn = function () {
return 'proto of Foo';
};

assert.equal( bar.dFn(), 'proto of Bar', 'inheritance is live (overwriting an inherited method)' );
assert.equal( bar.eFn(), 'proto of Foo', 'inheritance is live (adding a new method deeper in the chain)' );
});

// ve.mixinClass: Tested upstream (K-js)

// ve.cloneObject: Tested upstream (K-js)

// ve.isPlainObject: Tested upstream (jQuery)

// ve.isEmptyObject: Tested upstream (jQuery)

// ve.isArray: Tested upstream (jQuery)

// ve.bind: Tested upstream (jQuery)

// ve.indexOf: Tested upstream (jQuery)

// ve.extendObject: Tested upstream (jQuery)

QUnit.test( 'getHash: Basic usage', 5, function ( assert ) {
var tmp, hash, objects;

Expand Down Expand Up @@ -147,7 +262,7 @@ QUnit.test( 'getObjectValues', 6, function ( assert ) {
assert.deepEqual(
ve.getObjectValues( tmp ),
['foo', 'bar'],
'Function with static members'
'Function with properties'
);

assert.throws(
Expand Down
32 changes: 28 additions & 4 deletions modules/ve/ve.js
Expand Up @@ -55,16 +55,32 @@
* @example
* <code>
* function Foo() {}
*
* Foo.prototype.jump = function () {};
*
* -------
*
* function FooBar() {}
* ve.inheritClass( FooBar, Foo );
*
* FooBar.prop.feet = 2;
*
* FooBar.prototype.walk = function () {};
*
* -------
*
* function FooBarQuux() {}
* ve.inheritClass( FooBarQuux, FooBar );
*
* FooBarQuux.prototype.jump = function () {};
*
* -------
*
* FooBarQuux.prop.feet === 2;
* var fb = new FooBar();
* fb.jump();
* fb.walk();
* fb instanceof Foo && fb instanceof FooBar;
* fb instanceof Foo && fb instanceof FooBar && fb instanceof FooBarQuux;
* </code>
*
* @static
Expand All @@ -74,12 +90,20 @@
* @param {Function} originFn
*/
ve.inheritClass = function ( targetFn, originFn ) {
var tmp = targetFn.prototype.constructor;
// Doesn't really require ES5 (jshint/jshint#74@github)
/*jshint es5: true */
var targetConstructor = targetFn.prototype.constructor;

targetFn.prototype = ve.createObject( originFn.prototype );

// Restore original constructor property
targetFn.prototype.constructor = tmp;
// Restore constructor property of targetFn
targetFn.prototype.constructor = targetConstructor;

// Messing with static properties can be harmful, but we've agreed on one
// common property that will be inherited, and that one only. Use this for
// for making static values visible in child classes
originFn.static = originFn.static || {}; // Lazy-init
targetFn.static = ve.createObject( originFn.static );
};

/**
Expand Down

0 comments on commit 07c86fc

Please sign in to comment.