You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by an...@apache.org on 2014/09/05 20:14:19 UTC

[14/50] git commit: CB-7142 Add to for "plugin restore" command

CB-7142 Add <variable> to <feature> for "plugin restore" command

github: close #63


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/6050e93e
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/6050e93e
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/6050e93e

Branch: refs/heads/master
Commit: 6050e93ece9dc0521f1e70274f769fe7644f80c9
Parents: 3de053c
Author: motorro <mo...@gmail.com>
Authored: Wed Jul 16 05:43:36 2014 +0400
Committer: Anis Kadri <an...@apache.org>
Committed: Fri Sep 5 11:12:18 2014 -0700

----------------------------------------------------------------------
 cordova-lib/spec-cordova/ConfigParser.spec.js   | 158 +++++++++++++++++++
 cordova-lib/spec-cordova/test-config.xml        |  24 ++-
 cordova-lib/src/configparser/ConfigParser.js    |  96 +++++++++++
 .../src/configparser/ConfigParser.spec.js       |  99 ------------
 cordova-lib/src/cordova/platform.js             |  17 +-
 cordova-lib/src/cordova/prepare.js              |   2 +-
 cordova-lib/src/cordova/restore.js              |  60 +++----
 7 files changed, 325 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6050e93e/cordova-lib/spec-cordova/ConfigParser.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/ConfigParser.spec.js b/cordova-lib/spec-cordova/ConfigParser.spec.js
new file mode 100644
index 0000000..9c8c8e6
--- /dev/null
+++ b/cordova-lib/spec-cordova/ConfigParser.spec.js
@@ -0,0 +1,158 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+*/
+var path = require('path'),
+    fs = require('fs'),
+    ConfigParser = require('../src/configparser/ConfigParser'),
+    xml = path.join(__dirname, 'test-config.xml'),
+    xml_contents = fs.readFileSync(xml, 'utf-8');
+
+describe('config.xml parser', function () {
+    var readFile;
+    beforeEach(function() {
+        readFile = spyOn(fs, 'readFileSync').andReturn(xml_contents);
+    });
+
+    it('should create an instance based on an xml file', function() {
+        var cfg;
+        expect(function () {
+            cfg = new ConfigParser(xml);
+        }).not.toThrow();
+        expect(cfg).toBeDefined();
+        expect(cfg.doc).toBeDefined();
+    });
+
+    describe('methods', function() {
+        var cfg;
+        beforeEach(function() {
+            cfg = new ConfigParser(xml);
+        });
+
+        describe('package name / id', function() {
+            it('should get the (default) packagename', function() {
+                expect(cfg.packageName()).toEqual('io.cordova.hellocordova');
+            });
+            it('should allow setting the packagename', function() {
+                cfg.setPackageName('this.is.bat.country');
+                expect(cfg.packageName()).toEqual('this.is.bat.country');
+            });
+        });
+
+        describe('version', function() {
+            it('should get the version', function() {
+                expect(cfg.version()).toEqual('0.0.1');
+            });
+            it('should allow setting the version', function() {
+                cfg.setVersion('2.0.1');
+                expect(cfg.version()).toEqual('2.0.1');
+            });
+        });
+
+        describe('app name', function() {
+            it('should get the (default) app name', function() {
+                expect(cfg.name()).toEqual('Hello Cordova');
+            });
+            it('should allow setting the app name', function() {
+                cfg.setName('this.is.bat.country');
+                expect(cfg.name()).toEqual('this.is.bat.country');
+            });
+        });
+        describe('preference', function() {
+            it('should get value of existing preference', function() {
+                expect(cfg.getPreference('fullscreen')).toEqual('true');
+            });
+            it('should get undefined as non existing preference', function() {
+                expect(cfg.getPreference('zimzooo!')).toEqual(undefined);
+            });
+        });
+        describe('feature',function(){
+            it('should read feature id list', function() {
+               var expectedList = [
+                   "org.apache.cordova.featurewithvars",
+                   "org.apache.cordova.featurewithurl",
+                   "org.apache.cordova.featurewithversion",
+                   "org.apache.cordova.featurewithurlandversion",
+                   "org.apache.cordova.justafeature"
+               ];
+               var list = cfg.getFeatureIdList();
+               expect(list.length).toEqual(expectedList.length);
+               expectedList.forEach(function(feature){
+                   expect(list).toContain(feature);
+               });
+            });
+            it('should read feature given id', function(){
+                var feature = cfg.getFeature("org.apache.cordova.justafeature");
+                expect(feature).toBeDefined();
+                expect(feature.name).toEqual("A simple feature");
+                expect(feature.id).toEqual("org.apache.cordova.justafeature");
+                expect(feature.params).toBeDefined();
+                expect(feature.params.id).toBeDefined();
+                expect(feature.params.id).toEqual("org.apache.cordova.justafeature");
+            });
+            it('should not read feature given undefined id', function(){
+                var feature = cfg.getFeature("org.apache.cordova.undefinedfeature");
+                expect(feature).not.toBeDefined();
+            });
+            it('should read feature with url and set \'url\' param', function(){
+                var feature = cfg.getFeature("org.apache.cordova.featurewithurl");
+                expect(feature.url).toEqual("http://cordova.apache.org/featurewithurl");
+                expect(feature.params).toBeDefined();
+                expect(feature.params.url).toBeDefined();
+                expect(feature.params.url).toEqual("http://cordova.apache.org/featurewithurl");
+            });
+            it('should read feature with version and set \'version\' param', function(){
+                var feature = cfg.getFeature("org.apache.cordova.featurewithversion");
+                expect(feature.version).toEqual("1.1.1");
+                expect(feature.params).toBeDefined();
+                expect(feature.params.version).toBeDefined();
+                expect(feature.params.version).toEqual("1.1.1");
+            });
+            it('should read feature variables', function () {
+                var feature = cfg.getFeature("org.apache.cordova.featurewithvars");
+                expect(feature.variables).toBeDefined();
+                expect(feature.variables.var).toBeDefined();
+                expect(feature.variables.var).toEqual("varvalue");
+            });
+            it('should allow adding a new feature', function(){
+                cfg.addFeature('myfeature');
+                var features = cfg.doc.findall('feature');
+                var featureNames = features.map(function(feature){
+                    return feature.attrib.name;
+                });
+                expect(featureNames).toContain('myfeature');
+            });
+            it('should allow adding features with params', function(){
+                cfg.addFeature('afeature', JSON.parse('[{"name":"paraname", "value":"paravalue"}]'));
+                var features = cfg.doc.findall('feature');
+                var feature  = (function(){
+                    var i = features.length;
+                    var f;
+                    while (--i >= 0) {
+                        f = features[i];
+                        if ('afeature' === f.attrib.name) return f;
+                    }
+                    return undefined;
+                })();
+                expect(feature).toBeDefined();
+                var params = feature.findall('param');
+                expect(params[0].attrib.name).toEqual('paraname');
+                expect(params[0].attrib.value).toEqual('paravalue');
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6050e93e/cordova-lib/spec-cordova/test-config.xml
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-cordova/test-config.xml b/cordova-lib/spec-cordova/test-config.xml
index 5e01e48..3308ae0 100644
--- a/cordova-lib/spec-cordova/test-config.xml
+++ b/cordova-lib/spec-cordova/test-config.xml
@@ -22,5 +22,27 @@
     <icon id="logo" src="logo.png" width="255" height="255" />
     <platform name="android">
         <icon src="logo-android.png" width="255" height="255" density="mdpi" />
-    </platform>    
+    </platform>
+
+    <!-- Features -->
+    <feature name="A feature with preference">
+        <param name="id" value="org.apache.cordova.featurewithvars"/>
+        <variable name="var" value="varvalue"/>
+    </feature>
+    <feature name="A feature with url">
+        <param name="id" value="org.apache.cordova.featurewithurl" />
+        <param name="url" value="http://cordova.apache.org/featurewithurl" />
+    </feature>
+    <feature name="A feature with version">
+        <param name="id" value="org.apache.cordova.featurewithversion" />
+        <param name="version" value="1.1.1" />
+    </feature>
+    <feature name="A feature with url and version">
+        <param name="id" value="org.apache.cordova.featurewithurlandversion" />
+        <param name="version" value="1.1.1" />
+        <param name="url" value="http://cordova.apache.org/featurewithurlandversion" />
+    </feature>
+    <feature name="A simple feature">
+        <param name="id" value="org.apache.cordova.justafeature" />
+    </feature>
 </widget>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6050e93e/cordova-lib/src/configparser/ConfigParser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/configparser/ConfigParser.js b/cordova-lib/src/configparser/ConfigParser.js
index 84de2a6..75f798b 100644
--- a/cordova-lib/src/configparser/ConfigParser.js
+++ b/cordova-lib/src/configparser/ConfigParser.js
@@ -27,6 +27,15 @@ var et = require('elementtree'),
     CordovaError = require('../CordovaError'),
     fs = require('fs');
 
+/**
+ * Array of 'feature' params that are set as properties
+ * @type {string[]}
+ */
+var FEATURE_SPECIAL_PARAMS = [
+    'id',
+    'url',
+    'version'
+];
 
 /** Wraps a config.xml file */
 function ConfigParser(path) {
@@ -217,6 +226,93 @@ ConfigParser.prototype = {
     },
 
     /**
+     * Returns a list of features (IDs)
+     * @return {string[]} Array of feature IDs
+     */
+    getFeatureIdList: function () {
+        var features = this.doc.findall('feature'),
+            feature, idTag, id,
+            result = [];
+
+        // Check for valid features that have IDs set
+        for (var i = 0, l = features.length; i < l; ++i) {
+            feature = features[i];
+            idTag = feature.find('./param[@name="id"]');
+            if (null === idTag) {
+                // Invalid feature
+                continue;
+            }
+            id = idTag.attrib.value;
+            if (!!id) {
+                // Has id and id is non-empty
+                result.push(id);
+            }
+        }
+
+        return result;
+    },
+
+    /**
+     * Gets feature info
+     * @param {string} id Feature id
+     * @returns {Feature} Feature object
+     */
+    getFeature: function(id) {
+        if (!id) {
+            return undefined;
+        }
+        var feature = this.doc.find('./feature/param[@name="id"][@value="' + id + '"]/..');
+        if (null === feature) {
+            return undefined;
+        }
+
+        var result = {};
+        result.id = id;
+        result.name = feature.attrib.name;
+
+        // Iterate params and fill-in 'params' structure
+        // For special cases like 'id', 'url, 'version' - copy to the main space
+        result.params = processChildren (
+            'param',
+            function(name, value) {
+                if (FEATURE_SPECIAL_PARAMS.indexOf(name) >= 0) {
+                    result[name] = value;
+                }
+            }
+        );
+
+        // Iterate preferences
+        result.variables = processChildren('variable');
+
+        return result;
+
+        /**
+         * Processes a set of children
+         * having a pair of 'name' and 'value' attributes
+         * filling in 'output' object
+         * @param {string} xPath Search expression
+         * @param {function} [specialProcessing] Performs some additional actions on each valid element
+         * @return {object} A transformed object
+         */
+        function processChildren (xPath, specialProcessing) {
+            var result = {};
+            var needsProcessing = 'function' === typeof specialProcessing;
+            var nodes = feature.findall(xPath);
+            nodes.forEach(function(param){
+                var name = param.attrib.name;
+                var value = param.attrib.value;
+                if (name) {
+                    result[name] = value;
+                    if (needsProcessing) {
+                        specialProcessing(name, value);
+                    }
+                }
+            });
+            return result;
+        }
+    },
+
+    /**
      *This does not check for duplicate feature entries
      */
     addFeature: function (name, params){

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6050e93e/cordova-lib/src/configparser/ConfigParser.spec.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/configparser/ConfigParser.spec.js b/cordova-lib/src/configparser/ConfigParser.spec.js
deleted file mode 100644
index a1ba102..0000000
--- a/cordova-lib/src/configparser/ConfigParser.spec.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing,
-    software distributed under the License is distributed on an
-    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied.  See the License for the
-    specific language governing permissions and limitations
-    under the License.
-*/
-var path = require('path'),
-    fs = require('fs'),
-    ConfigParser = require('../src/cordova/ConfigParser'),
-    xml = path.join(__dirname, 'test-config.xml'),
-    xml_contents = fs.readFileSync(xml, 'utf-8');
-
-describe('config.xml parser', function () {
-    var readFile;
-    beforeEach(function() {
-        readFile = spyOn(fs, 'readFileSync').andReturn(xml_contents);
-    });
-
-    it('should create an instance based on an xml file', function() {
-        var cfg;
-        expect(function () {
-            cfg = new ConfigParser(xml);
-        }).not.toThrow();
-        expect(cfg).toBeDefined();
-        expect(cfg.doc).toBeDefined();
-    });
-
-    describe('methods', function() {
-        var cfg;
-        beforeEach(function() {
-            cfg = new ConfigParser(xml);
-        });
-
-        describe('package name / id', function() {
-            it('should get the (default) packagename', function() {
-                expect(cfg.packageName()).toEqual('io.cordova.hellocordova');
-            });
-            it('should allow setting the packagename', function() {
-                cfg.setPackageName('this.is.bat.country');
-                expect(cfg.packageName()).toEqual('this.is.bat.country');
-            });
-        });
-
-        describe('version', function() {
-            it('should get the version', function() {
-                expect(cfg.version()).toEqual('0.0.1');
-            });
-            it('should allow setting the version', function() {
-                cfg.setVersion('2.0.1');
-                expect(cfg.version()).toEqual('2.0.1');
-            });
-        });
-
-        describe('app name', function() {
-            it('should get the (default) app name', function() {
-                expect(cfg.name()).toEqual('Hello Cordova');
-            });
-            it('should allow setting the app name', function() {
-                cfg.setName('this.is.bat.country');
-                expect(cfg.name()).toEqual('this.is.bat.country');
-            });
-        });
-        describe('preference', function() {
-            it('should get value of existing preference', function() {
-                expect(cfg.getPreference('fullscreen')).toEqual('true');
-            });
-            it('should get undefined as non existing preference', function() {
-                expect(cfg.getPreference('zimzooo!')).toEqual(undefined);
-            });
-        });
-        describe('feature',function(){
-            it('should allow adding a new feature', function(){
-                cfg.addFeature('myfeature');
-                var features = cfg.doc.findall('feature');
-                expect(features[0].attrib.name).toEqual('myfeature');
-            });
-            it('should allow adding features with params', function(){
-                cfg.addFeature('afeature', JSON.parse('[{"name":"paraname", "value":"paravalue"}]'));
-                var features = cfg.doc.findall('feature');
-                expect(features[0].attrib.name).toEqual('afeature');
-                var params = features[0].findall('param');
-                expect(params[0].attrib.name).toEqual('paraname');
-                expect(params[0].attrib.value).toEqual('paravalue');
-            });
-        });
-    });
-});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6050e93e/cordova-lib/src/cordova/platform.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/platform.js b/cordova-lib/src/cordova/platform.js
index 2cb8c2e..7a01783 100644
--- a/cordova-lib/src/cordova/platform.js
+++ b/cordova-lib/src/cordova/platform.js
@@ -454,7 +454,22 @@ function call_into_create(target, projectRoot, cfg, libDir, template_dir, opts)
         return plugins.reduce(function(soFar, plugin) {
             return soFar.then(function() {
                 events.emit('verbose', 'Installing plugin "' + plugin + '" following successful platform add of ' + target);
-                return plugman.raw.install(target, output, path.basename(plugin), plugins_dir);
+                plugin = path.basename(plugin);
+                var options = (function(){
+                    // Get plugin preferences from config features if have any
+                    // Pass them as cli_variables to plugman
+                    var feature = cfg.getFeature(plugin);
+                    var variables = feature && feature.variables;
+                    if (!!variables) {
+                        events.emit('verbose', 'Found variables for "' + plugin + '". Processing as cli_variables.');
+                        return {
+                            cli_variables: variables
+                        };
+                    }
+                    return null;
+                })();
+
+                return plugman.raw.install(target, output, plugin, plugins_dir, options);
             });
         }, Q());
     });

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6050e93e/cordova-lib/src/cordova/prepare.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/prepare.js b/cordova-lib/src/cordova/prepare.js
index c69a4d7..09be122 100644
--- a/cordova-lib/src/cordova/prepare.js
+++ b/cordova-lib/src/cordova/prepare.js
@@ -126,7 +126,7 @@ function prepare(options) {
     });
 }
 
-var BLACKLIST = ['platform'];
+var BLACKLIST = ['platform', 'feature'];
 var SINGLETONS = ['content', 'author'];
 function mergeXml(src, dest, platform, clobber) {
     // Do nothing for blacklisted tags.

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6050e93e/cordova-lib/src/cordova/restore.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/restore.js b/cordova-lib/src/cordova/restore.js
index ffd1edc..6e4d262 100644
--- a/cordova-lib/src/cordova/restore.js
+++ b/cordova-lib/src/cordova/restore.js
@@ -60,39 +60,41 @@ function installPlatformsFromConfigXML(cfg){
 //returns a Promise
 function installPluginsFromConfigXML(cfg) {
     //Install plugins that are listed on config.xml
-    var pluginsFromConfig = [];
     var projectRoot = cordova_util.cdProjectRoot();
     var plugins_dir = path.join(projectRoot, 'plugins');
 
-    var features = cfg.doc.findall('feature');
-    features.forEach(function(feature){
-        var params = feature.findall('param');
-        var pluginId = '';
-        var pluginVersion = '';
-        for (var i = 0; i < params.length; i++) {
-            if (params[i].attrib.name === 'id') {
-                pluginId = params[i].attrib.value;
-            }
-            if (params[i].attrib.name === 'version') {
-                pluginVersion = params[i].attrib.value;
-            }
+    // Get all configured plugins
+    var features = cfg.getFeatureIdList();
+    if (0 === features.length) {
+        return Q.all('No config.xml plugins to install');
+    }
+
+    return features.reduce(function(soFar, featureId) {
+
+        var pluginPath =  path.join(plugins_dir, featureId);
+        if (fs.existsSync(pluginPath)) {
+            // Plugin already exists
+            return soFar;
         }
-        var pluginPath =  path.join(plugins_dir,pluginId);
-        // contents of the plugins folder takes precedence hence
-        // we ignore if the correct version is installed or not.
-        if (pluginId !== '' && !fs.existsSync(pluginPath)) {
-            if ( pluginVersion !== '') {
-                pluginId = pluginId + '@' + pluginVersion;
+
+        return soFar.then(function() {
+            events.emit('log', 'Discovered ' + featureId + ' in config.xml. Installing to the project');
+
+            var feature = cfg.getFeature(featureId);
+
+            // Install from given URL if defined or using a plugin id
+            var installFrom = feature.url;
+            if (!installFrom) {
+                installFrom = feature.id;
+                if (!!feature.version) {
+                    installFrom += ('@' + feature.version);
+                }
             }
-            events.emit('log', 'Discovered ' + pluginId + ' in config.xml. Installing to the project');
-            pluginsFromConfig.push(pluginId);
-        }
-    });
 
-    //Use cli instead of plugman directly ensuring all the hooks
-    // to get fired.
-    if (pluginsFromConfig.length >0) {
-        return plugin('add', pluginsFromConfig);
-    }
-    return Q.all('No config.xml plugins to install');
+            // Add feature preferences as CLI variables if have any
+            var options = 'undefined' !== typeof feature.variables ? {cli_variables: feature.variables} : null;
+
+            return plugin('add', installFrom, options);
+        });
+    }, Q());
 }