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



commit a1b601531c960c552c0fbf6fc388592c23d3b9bd
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 +-
 Makefile-test.am      |    2 +
 modules/package.js    |    3 +-
 modules/params.js     |  128 +++++++++++++++++++++++++++++++++++++++++++++++++
 test/js/testParams.js |   64 ++++++++++++++++++++++++
 5 files changed, 198 insertions(+), 2 deletions(-)
---
diff --git a/Makefile-modules.am b/Makefile-modules.am
index e2ada18..ac4609e 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/Makefile-test.am b/Makefile-test.am
index 11bb0a3..2455f18 100644
--- a/Makefile-test.am
+++ b/Makefile-test.am
@@ -176,6 +176,8 @@ EXTRA_DIST +=                                       \
        test/js/testLang.js                     \
        test/js/testLocale.js                   \
        test/js/testMainloop.js                 \
+       test/js/testParams.js                   \
+       test/js/testParamSpec.js                \
        test/js/testSignals.js                  \
        test/js/testTweener.js                  \
        test/js/testUnicode.js                  \
diff --git a/modules/package.js b/modules/package.js
index fb647fe..3586532 100644
--- a/modules/package.js
+++ b/modules/package.js
@@ -180,7 +180,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;
+}
diff --git a/test/js/testParams.js b/test/js/testParams.js
new file mode 100644
index 0000000..d2cef37
--- /dev/null
+++ b/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();


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