[gjs/wip/package: 78/83] Add Params module based on the one in gnome-shell



commit bd8b4a5f9d79993abbdca8fd6cde7eb41bc5d6ea
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Fri Feb 22 18:20:27 2013 +0100

    Add Params module based on the one in gnome-shell
    
    This modules offers a convenient way to pass arguments to complex
    functions, in a way similar to keyword arguments in Python.
    To make it also suited for useful for inheriting from GObject classes,
    as the convention is to use a property bag in the constructor,
    a new function is introduced, filter(), which allows extending
    the set of supported properties in a trasparent way.
    Also, the confusing allowExtras boolean parameter is removed,
    in favor of two functions, parse() which throws and fill() which
    doesn't.
    With lots of documentation, code examples and tests.

 Makefile-modules.am                   |    3 +-
 installed-tests/Makefile-test.am      |    1 +
 installed-tests/test/js/testParams.js |   64 ++++++++++++++++
 modules/package.js                    |    3 +-
 modules/params.js                     |  128 +++++++++++++++++++++++++++++++++
 5 files changed, 197 insertions(+), 2 deletions(-)
---
diff --git a/Makefile-modules.am b/Makefile-modules.am
index a372be9..75ba668 100644
--- a/Makefile-modules.am
+++ b/Makefile-modules.am
@@ -16,7 +16,8 @@ dist_gjsjs_DATA +=            \
        modules/signals.js      \
        modules/promise.js      \
        modules/format.js       \
-       modules/package.js
+       modules/package.js      \
+       modules/params.js
 
 NATIVE_MODULES = libconsole.la libsystem.la
 if ENABLE_CAIRO
diff --git a/installed-tests/Makefile-test.am b/installed-tests/Makefile-test.am
index b74a462..ea295eb 100644
--- a/installed-tests/Makefile-test.am
+++ b/installed-tests/Makefile-test.am
@@ -75,6 +75,7 @@ jstests_DATA =                                        \
        test/js/testLang.js                     \
        test/js/testLocale.js                   \
        test/js/testMainloop.js                 \
+       test/js/testParams.js                   \
        test/js/testSignals.js                  \
        test/js/testSystem.js                   \
        test/js/testTweener.js                  \
diff --git a/installed-tests/test/js/testParams.js b/installed-tests/test/js/testParams.js
new file mode 100644
index 0000000..d2cef37
--- /dev/null
+++ b/installed-tests/test/js/testParams.js
@@ -0,0 +1,64 @@
+// application/javascript;version=1.8
+
+const Params = imports.params;
+
+function testParse() {
+    let one = Params.parse(null, { anInt: 42 });
+    assertEquals(42, one.anInt);
+
+    let two = Params.parse({ anInt: 16 },
+                           { anInt: 42,
+                             aString: 'foo' });
+    assertEquals(16, two.anInt);
+    assertEquals('foo', two.aString);
+
+    assertRaises(function() {
+        Params.parse({ aString: 'foo' },
+                     { anInt: 42 });
+    });
+}
+
+function testFill() {
+    let one = Params.fill(null, { anInt: 42 });
+    assertEquals(42, one.anInt);
+
+    let two = Params.fill({ anInt: 16 },
+                          { anInt: 42,
+                            aString: 'foo' });
+    assertEquals(16, two.anInt);
+    assertEquals('foo', two.aString);
+
+    let three = Params.fill({ anInt: 16,
+                              aString: 'foo' },
+                            { anInt: 42,
+                              aBool: false });
+    assertEquals(16, three.anInt);
+    assertEquals('foo', three.aString);
+    assertEquals(false, three.aBool);
+}
+
+function testFilter() {
+    let one = Params.filter(null, { anInt: 42 });
+    assertEquals(42, one.anInt);
+
+    let twoFrom = { anInt: 16 };
+    let two = Params.filter(twoFrom,
+                            { anInt: 42,
+                              aString: 'foo' });
+    assertEquals(16, two.anInt);
+    assertEquals('foo', two.aString);
+    assertFalse('anInt' in twoFrom);
+
+    let threeFrom = { anInt: 16,
+                      aString: 'foo' };
+    let three = Params.filter(threeFrom,
+                              { anInt: 42,
+                                aBool: false });
+    assertEquals(16, three.anInt);
+    assertEquals(false, three.aBool);
+    assertFalse('aString' in three);
+    assertTrue('aString' in threeFrom);
+    assertFalse('anInt' in threeFrom);
+}
+
+gjstestRun();
diff --git a/modules/package.js b/modules/package.js
index 53247e5..bb64ac4 100644
--- a/modules/package.js
+++ b/modules/package.js
@@ -197,7 +197,8 @@ function _isGjsModule(name, version) {
     const RECOGNIZED_MODULE_NAMES = ['Lang',
                                      'Mainloop',
                                      'Signals',
-                                     'System'];
+                                     'System',
+                                     'Params'];
     for (let i = 0; i < RECOGNIZED_MODULE_NAMES.length; i++) {
         let module = RECOGNIZED_MODULE_NAMES[i];
 
diff --git a/modules/params.js b/modules/params.js
new file mode 100644
index 0000000..4b8e1c4
--- /dev/null
+++ b/modules/params.js
@@ -0,0 +1,128 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+// Rules for versioning: bump major when making API incompatible changes,
+// bump minor when adding API
+var $API_VERSION = [1, 0];
+
+// Params:
+//
+// A set of convenience functions for dealing with pseudo-keyword
+// arguments.
+//
+// Examples:
+//
+// A function with complex arguments
+// function myFunction(params) {
+//     params = Params.parse(params, { myFlags: Flags.NONE,
+//                                     anInt: 42,
+//                                     aString: 'hello, world!',
+//                                    });
+//     ... params.anInt, params.myFlags, params.aString ...
+// }
+// myFunction({ anInt: -1 });
+//
+// Extend a method to allow more params in a subclass
+// The superclass can safely use Params.parse(), it won't see
+// the extensions.
+// const MyClass = new Lang.Class({
+//       ...
+//       method: function(params) {
+//           let mine = Params.filter(params, { anInt: 42 });
+//           this.parent(params);
+//           ... mine.anInt ...
+//       }
+// });
+
+// parse:
+// @params: caller-provided parameter object, or %null
+// @defaults: function-provided defaults object
+//
+// Examines @params and fills in default values from @defaults for
+// any properties in @defaults that don't appear in @params.
+// This function will throw a Error if @params contains a property
+// that is not recognized. Use fill() or filter() if you don't
+// want that.
+//
+// If @params is %null, this returns the values from @defaults.
+//
+// Return value: a new object, containing the merged parameters from
+// @params and @defaults
+function parse(params, defaults) {
+    let ret = {}, prop;
+    params = params || {};
+
+    for (prop in params) {
+        if (!(prop in defaults))
+            throw new Error('Unrecognized parameter "' + prop + '"');
+        ret[prop] = params[prop];
+    }
+
+    for (prop in defaults) {
+        if (!(prop in params))
+            ret[prop] = defaults[prop];
+    }
+
+    return ret;
+}
+
+// fill:
+// @params: caller-provided parameter object, or %null
+// @defaults: function-provided defaults object
+//
+// Examines @params and fills in default values from @defaults
+// for any properties in @defaults that don't appear in @params.
+//
+// Differently from parse(), this function does not throw for
+// unrecognized parameters.
+//
+// Return value: a new object, containing the merged parameters from
+// @params and @defaults
+function fill(params, defaults) {
+    let ret = {};
+    params = params || {};
+
+    for (prop in params)
+        ret[prop] = params[prop];
+
+    for (prop in defaults) {
+        if (!(prop in ret))
+            ret[prop] = defaults[prop];
+    }
+
+    return ret;
+}
+
+// filter:
+// @params: caller-provided parameter object, or %null
+// @defaults: function-provided defaults object
+//
+// Examines @params and returns an object containing the
+// same properties as @defaults, but with values taken from
+// @params where available.
+// Then it removes from @params all matched properties.
+//
+// This is similar to parse(), but it accepts unknown properties
+// and modifies @params for known ones.
+//
+// If @params is %null, this returns the values from @defaults.
+//
+// Return value: a new object, containing the merged parameters from
+// @params and @defaults
+function filter(params, defaults) {
+    let ret = {}, prop;
+    params = params || {};
+
+    for (prop in defaults) {
+        if (!(prop in params))
+            ret[prop] = defaults[prop];
+    }
+
+    for (prop in params) {
+        if (prop in defaults) {
+            ret[prop] = params[prop];
+            delete params[prop];
+        }
+    }
+
+    return ret;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]