Catrope has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/172939

Change subject: Add getProp(), setProp() and isInstanceOfAny()
......................................................................

Add getProp(), setProp() and isInstanceOfAny()

Moved from VisualEditor core.

Also copied get/setProp() tests, and added tests for
isInstanceOfAny() which VE didn't have.

Change-Id: If99eb7732e6d63c09f75d2207936a94ac98c539f
---
M src/core.js
M tests/unit/core.test.js
2 files changed, 234 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/oojs/core refs/changes/39/172939/1

diff --git a/src/core.js b/src/core.js
index 1d3c9e4..bee01d1 100644
--- a/src/core.js
+++ b/src/core.js
@@ -145,6 +145,80 @@
 /* Object Methods */
 
 /**
+ * Checks if an object is an instance of one or more classes.
+ *
+ * @param {Object} subject Object to check
+ * @param {Function[]} classes Classes to compare with
+ * @returns {boolean} Object inherits from one or more of the classes
+ */
+oo.isInstanceOfAny = function ( subject, classes ) {
+       var i = classes.length;
+
+       while ( classes[--i] ) {
+               if ( subject instanceof classes[i] ) {
+                       return true;
+               }
+       }
+       return false;
+};
+
+/**
+ * Get a deeply nested property of an object using variadic arguments, 
protecting against
+ * undefined property errors.
+ *
+ * `quux = oo.getProp( obj, 'foo', 'bar', 'baz' );` is equivalent to `quux = 
obj.foo.bar.baz;`
+ * except that the former protects against JS errors if one of the 
intermediate properties
+ * is undefined. Instead of throwing an error, this function will return 
undefined in
+ * that case.
+ *
+ * @param {Object} obj
+ * @param {Mixed...} [keys]
+ * @returns obj[arguments[1]][arguments[2]].... or undefined
+ */
+oo.getProp = function ( obj ) {
+       var i, retval = obj;
+       for ( i = 1; i < arguments.length; i++ ) {
+               if ( retval === undefined || retval === null ) {
+                       // Trying to access a property of undefined or null 
causes an error
+                       return undefined;
+               }
+               retval = retval[arguments[i]];
+       }
+       return retval;
+};
+
+/**
+ * Set a deeply nested property of an object using variadic arguments, 
protecting against
+ * undefined property errors.
+ *
+ * `oo.setProp( obj, 'foo', 'bar', 'baz' );` is equivalent to `obj.foo.bar = 
baz;` except that
+ * the former protects against JS errors if one of the intermediate properties 
is
+ * undefined. Instead of throwing an error, undefined intermediate properties 
will be
+ * initialized to an empty object. If an intermediate property is null, or if 
obj itself
+ * is undefined or null, this function will silently abort.
+ *
+ * @param {Object} obj
+ * @param {Mixed...} [keys]
+ * @param {Mixed} [value]
+ */
+oo.setProp = function ( obj ) {
+       var i, prop = obj;
+       if ( Object( obj ) !== obj ) {
+               return;
+       }
+       for ( i = 1; i < arguments.length - 2; i++ ) {
+               if ( prop[arguments[i]] === undefined ) {
+                       prop[arguments[i]] = {};
+               }
+               if ( prop[arguments[i]] === null || typeof prop[arguments[i]] 
!== 'object' ) {
+                       return;
+               }
+               prop = prop[arguments[i]];
+       }
+       prop[arguments[arguments.length - 2]] = arguments[arguments.length - 1];
+};
+
+/**
  * Create a new object that is an instance of the same
  * constructor as the input, inherits from the same object
  * and contains the same own properties.
diff --git a/tests/unit/core.test.js b/tests/unit/core.test.js
index 253a58e..61f5c6d 100644
--- a/tests/unit/core.test.js
+++ b/tests/unit/core.test.js
@@ -189,6 +189,166 @@
                assert.strictEqual( quux.isBar(), 'method of Bar', 'method 
works as expected' );
        } );
 
+       QUnit.test( 'isInstanceOfAny', 7, function ( assert ) {
+               function Foo() {}
+               oo.initClass( Foo );
+
+               function Bar() {}
+               oo.initClass( Bar );
+
+               function SpecialFoo() {}
+               oo.inheritClass( SpecialFoo, Foo );
+
+               function VerySpecialFoo() {}
+               oo.inheritClass( VerySpecialFoo, SpecialFoo );
+
+               assert.strictEqual(
+                       oo.isInstanceOfAny( new Foo(), [ Foo ] ),
+                       true,
+                       'Foo is an instance of Foo'
+               );
+
+               assert.strictEqual(
+                       oo.isInstanceOfAny( new SpecialFoo(), [ Foo ] ),
+                       true,
+                       'SpecialFoo is an instance of Foo'
+               );
+
+               assert.strictEqual(
+                       oo.isInstanceOfAny( new SpecialFoo(), [ Bar ] ),
+                       false,
+                       'SpecialFoo is not an instance of Bar'
+               );
+
+               assert.strictEqual(
+                       oo.isInstanceOfAny( new SpecialFoo(), [ Bar, Foo ] ),
+                       true,
+                       'SpecialFoo is an instance of Bar or Foo'
+               );
+
+               assert.strictEqual(
+                       oo.isInstanceOfAny( new VerySpecialFoo(), [ Bar, Foo ] 
),
+                       true,
+                       'VerySpecialFoo is an instance of Bar or Foo'
+               );
+
+               assert.strictEqual(
+                       oo.isInstanceOfAny( new VerySpecialFoo(), [ Foo, 
SpecialFoo ] ),
+                       true,
+                       'VerySpecialFoo is an instance of Foo or SpecialFoo'
+               );
+
+               assert.strictEqual(
+                       oo.isInstanceOfAny( new VerySpecialFoo(), [] ),
+                       false,
+                       'VerySpecialFoo is not an instance of nothing'
+               );
+       } );
+
+       function testGetSetProp( type, obj ) {
+               QUnit.test( 'getProp( ' + type + ' )', 9, function ( assert ) {
+                       assert.deepEqual(
+                               oo.getProp( obj, 'foo' ),
+                               3,
+                               'single key'
+                       );
+                       assert.deepEqual(
+                               oo.getProp( obj, 'bar' ),
+                               { baz: null, quux: { whee: 'yay' } },
+                               'single key, returns object'
+                       );
+                       assert.deepEqual(
+                               oo.getProp( obj, 'bar', 'baz' ),
+                               null,
+                               'two keys, returns null'
+                       );
+                       assert.deepEqual(
+                               oo.getProp( obj, 'bar', 'quux', 'whee' ),
+                               'yay',
+                               'three keys'
+                       );
+                       assert.deepEqual(
+                               oo.getProp( obj, 'x' ),
+                               undefined,
+                               'missing property returns undefined'
+                       );
+                       assert.deepEqual(
+                               oo.getProp( obj, 'foo', 'bar' ),
+                               undefined,
+                               'missing 2nd-level property returns undefined'
+                       );
+                       assert.deepEqual(
+                               oo.getProp( obj, 'foo', 'bar', 'baz', 'quux', 
'whee' ),
+                               undefined,
+                               'multiple missing properties don\'t cause an 
error'
+                       );
+                       assert.deepEqual(
+                               oo.getProp( obj, 'bar', 'baz', 'quux' ),
+                               undefined,
+                               'accessing property of null returns undefined, 
doesn\'t cause an error'
+                       );
+                       assert.deepEqual(
+                               oo.getProp( obj, 'bar', 'baz', 'quux', 'whee', 
'yay' ),
+                               undefined,
+                               'accessing multiple properties of null'
+                       );
+               } );
+
+               QUnit.test( 'setProp( ' + type + ' )', 7, function ( assert ) {
+                       oo.setProp( obj, 'foo', 4 );
+                       assert.deepEqual( 4, obj.foo, 'setting an existing key 
with depth 1' );
+
+                       oo.setProp( obj, 'test', 'TEST' );
+                       assert.deepEqual( 'TEST', obj.test, 'setting a new key 
with depth 1' );
+
+                       oo.setProp( obj, 'bar', 'quux', 'whee', 'YAY' );
+                       assert.deepEqual( 'YAY', obj.bar.quux.whee, 'setting an 
existing key with depth 3' );
+
+                       oo.setProp( obj, 'bar', 'a', 'b', 'c' );
+                       assert.deepEqual( 'c', obj.bar.a.b, 'setting two new 
keys within an existing key' );
+
+                       oo.setProp( obj, 'a', 'b', 'c', 'd', 'e', 'f' );
+                       assert.deepEqual( 'f', obj.a.b.c.d.e, 'setting new keys 
with depth 5' );
+
+                       oo.setProp( obj, 'bar', 'baz', 'whee', 'wheee', 
'wheeee' );
+                       assert.deepEqual( null, obj.bar.baz, 'descending into 
null fails silently' );
+
+                       oo.setProp( obj, 'foo', 'bar', 'baz', 5 );
+                       assert.deepEqual( undefined, obj.foo.bar, 'descending 
into a non-object fails silently' );
+               } );
+       }
+
+       var plainObj, funcObj, arrObj;
+       plainObj = {
+               foo: 3,
+               bar: {
+                       baz: null,
+                       quux: {
+                               whee: 'yay'
+                       }
+               }
+       };
+       funcObj = function abc( d ) { return d; };
+       funcObj.foo = 3;
+       funcObj.bar = {
+               baz: null,
+               quux: {
+                       whee: 'yay'
+               }
+       };
+       arrObj = [ 'a', 'b', 'c' ];
+       arrObj.foo = 3;
+       arrObj.bar = {
+               baz: null,
+               quux: {
+                       whee: 'yay'
+               }
+       };
+
+       testGetSetProp( 'Object', plainObj );
+       testGetSetProp( 'Function', funcObj );
+       testGetSetProp( 'Array', arrObj );
+
        QUnit.test( 'cloneObject', 4, function ( assert ) {
                var myfoo, myfooClone, expected;
 

-- 
To view, visit https://gerrit.wikimedia.org/r/172939
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: If99eb7732e6d63c09f75d2207936a94ac98c539f
Gerrit-PatchSet: 1
Gerrit-Project: oojs/core
Gerrit-Branch: master
Gerrit-Owner: Catrope <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to