TINKERPOP-1653 Allow multiple scripts to be passed to gremlin.sh

This is a non-breaking change and simply allows multiple -i and -e options on 
gremlin.sh. The old method for passing a single script name to gremlin.sh 
remains intact as well. Had to write a custom parser for the command line 
arguments to do this because it doesn't appear that CliBuilder is capable of 
parsing the in the manner necessary to support this feature unfortunately.


Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/1503311f
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/1503311f
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/1503311f

Branch: refs/heads/TINKERPOP-1625
Commit: 1503311f030291745276805188e61b525576d604
Parents: 3821792
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Mar 17 10:23:34 2017 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Mar 17 10:23:34 2017 -0400

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   1 +
 .../src/reference/gremlin-applications.asciidoc |  13 ++
 .../upgrade/release-3.2.x-incubating.asciidoc   |  15 ++
 .../tinkerpop/gremlin/console/Console.groovy    | 139 ++++++++++++-------
 4 files changed, 119 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1503311f/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index a77991e..6e0204b 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 TinkerPop 3.2.5 (Release Date: NOT OFFICIALLY RELEASED YET)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+* Allowed for multiple scripts and related arguments to be passed to 
`gremlin.sh` via `-i` and `-e`.
 * Added various metrics to the `GremlinGroovyScriptEngine` around script 
compilation and exposed them in Gremlin Server.
 * Moved the `caffeine` dependency down to `gremlin-groovy` and out of 
`gremlin-server`.
 * Improved script compilation in `GremlinGroovyScriptEngine to use better 
caching, log long compile times and prevent failed compilations from 
recompiling on future requests.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1503311f/docs/src/reference/gremlin-applications.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/gremlin-applications.asciidoc 
b/docs/src/reference/gremlin-applications.asciidoc
index 3a2c446..bfaa153 100644
--- a/docs/src/reference/gremlin-applications.asciidoc
+++ b/docs/src/reference/gremlin-applications.asciidoc
@@ -303,6 +303,16 @@ $ bin/gremlin.sh -e gremlin.groovy vadas
 v[2]
 ----
 
+It is also possible to pass multiple scripts by specifying multiple `-e` 
options. The scripts will execute in the order
+that they are specified. Note that only the arguments from the last script 
executed will be preserved in the console.
+Finally, if the arguments conflict with the reserved flags that `gremlin.sh` 
responds double quotes can be used to
+wrap all the arugments to the option:
+
+[source,bash]
+----
+$ bin/gremlin.sh -e "gremlin.groovy -e -i --color"
+----
+
 [[interactive-mode]]
 Interactive Mode
 ~~~~~~~~~~~~~~~~
@@ -347,6 +357,9 @@ gremlin> g.V()
 Note that the user can now reference `g` (and `graph` for that matter) at 
startup without having to directly type that
 variable initialization code into the console.
 
+Like, execution mode, it is also possible to pass multiple scripts by 
specifying multiple `-i` options. See the
+<<execution-mode, Execution Mode Section>> for more information on the 
specfics of that capability.
+
 [[gremlin-server]]
 Gremlin Server
 --------------

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1503311f/docs/src/upgrade/release-3.2.x-incubating.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc 
b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
index d01f58f..79b1ac3 100644
--- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc
+++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
@@ -42,6 +42,21 @@ set. Note that metrics are captured for both sessionless 
requests as well as for
 
 See: https://issues.apache.org/jira/browse/TINKERPOP-1644[TINKERPOP-1644]
 
+Gremlin Console Scripting
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The `gremlin.sh` command has two flags, `-i` and `-e`, which are used to pass 
a script and arguments into the Gremlin
+Console for execution. Those flags now allow for passing multiple scripts and 
related arguments to be supplied which
+can yield greater flexibilty in automation tasks.
+
+[source,bash]
+----
+$ bin/gremlin.sh -i y.groovy 1 2 3 -i x.groovy
+$ bin/gremlin.sh -e y.groovy 1 2 3 -e x.groovy
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1653[TINKERPOP-1653]
+
 Gremlin-Python Driver
 ^^^^^^^^^^^^^^^^^^^^^
 Gremlin-Python now offers a more complete driver implementation that uses 
connection pooling and

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/1503311f/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
----------------------------------------------------------------------
diff --git 
a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
 
b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
index 651d6f3..393f1f2 100644
--- 
a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
+++ 
b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
@@ -69,10 +69,10 @@ class Console {
      */
     @Deprecated
     public Console(final String initScriptFile) {
-        this(new IO(System.in, System.out, System.err), initScriptFile.size() 
!= null ? [initScriptFile]: null, true)
+        this(new IO(System.in, System.out, System.err), initScriptFile.size() 
!= null ? [[initScriptFile]]: null, true)
     }
 
-    public Console(final IO io, final List<String> scriptAndArgs, final 
boolean interactive) {
+    public Console(final IO io, final List<List<String>> scriptsAndArgs, final 
boolean interactive) {
         this.io = io
         this.interactive = interactive
 
@@ -158,7 +158,7 @@ class Console {
         try {
             // if the init script contains :x command it will throw an 
ExitNotification so init script execution
             // needs to appear in the try/catch
-            if (scriptAndArgs != null && scriptAndArgs.size() > 0) 
executeInShell(scriptAndArgs)
+            if (scriptsAndArgs != null && !scriptsAndArgs.isEmpty()) 
executeInShell(scriptsAndArgs)
 
             // start iterating results to show as output
             showShellEvaluationOutput(true)
@@ -364,49 +364,51 @@ class Console {
         return Preferences.resultPrompt
     }
 
-    private void executeInShell(final List<String> scriptAndArgs) {
-        final String scriptFile = scriptAndArgs[0]
-        try {
-            // check if this script comes with arguments. if so then set them 
up in an "args" bundle
-            if (scriptAndArgs.size() > 1) {
-                List<String> args = scriptAndArgs.subList(1, 
scriptAndArgs.size())
-                groovy.execute("args = [\"" + args.join('\",\"') + "\"]")
-            } else {
-                groovy.execute("args = []")
-            }
-
-            File file = new File(scriptFile)
-            if (!file.exists() && !file.isAbsolute()) {
-                final String userWorkingDir = 
System.getProperty("user.working_dir");
-                if (userWorkingDir != null) {
-                    file = new File(userWorkingDir, scriptFile);
+    private void executeInShell(final List<List<String>> scriptsAndArgs) {
+        scriptsAndArgs.each { scriptAndArgs ->
+            final String scriptFile = scriptAndArgs[0]
+            try {
+                // check if this script comes with arguments. if so then set 
them up in an "args" bundle
+                if (scriptAndArgs.size() > 1) {
+                    List<String> args = scriptAndArgs.subList(1, 
scriptAndArgs.size())
+                    groovy.execute("args = [\"" + args.join('\",\"') + "\"]")
+                } else {
+                    groovy.execute("args = []")
                 }
-            }
-            int lineNumber = 0
-            def lines = file.readLines()
-            for (String line : lines) {
-                try {
-                    lineNumber++
-                    groovy.execute(line)
-                } catch (Exception ex) {
-                    io.err.println(Colorizer.render(Preferences.errorColor, 
"Error in $scriptFile at [$lineNumber: $line] - ${ex.message}"))
-                    if (interactive)
-                        break
-                    else {
-                        ex.printStackTrace(io.err)
-                        System.exit(1)
+
+                File file = new File(scriptFile)
+                if (!file.exists() && !file.isAbsolute()) {
+                    final String userWorkingDir = 
System.getProperty("user.working_dir");
+                    if (userWorkingDir != null) {
+                        file = new File(userWorkingDir, scriptFile);
                     }
+                }
+                int lineNumber = 0
+                def lines = file.readLines()
+                for (String line : lines) {
+                    try {
+                        lineNumber++
+                        groovy.execute(line)
+                    } catch (Exception ex) {
+                        
io.err.println(Colorizer.render(Preferences.errorColor, "Error in $scriptFile 
at [$lineNumber: $line] - ${ex.message}"))
+                        if (interactive)
+                            break
+                        else {
+                            ex.printStackTrace(io.err)
+                            System.exit(1)
+                        }
 
+                    }
                 }
-            }
 
-            if (!interactive) System.exit(0)
-        } catch (FileNotFoundException ignored) {
-            io.err.println(Colorizer.render(Preferences.errorColor, "Gremlin 
file not found at [$scriptFile]."))
-            if (!interactive) System.exit(1)
-        } catch (Exception ex) {
-            io.err.println(Colorizer.render(Preferences.errorColor, "Failure 
processing Gremlin script [$scriptFile] - ${ex.message}"))
-            if (!interactive) System.exit(1)
+                if (!interactive) System.exit(0)
+            } catch (FileNotFoundException ignored) {
+                io.err.println(Colorizer.render(Preferences.errorColor, 
"Gremlin file not found at [$scriptFile]."))
+                if (!interactive) System.exit(1)
+            } catch (Exception ex) {
+                io.err.println(Colorizer.render(Preferences.errorColor, 
"Failure processing Gremlin script [$scriptFile] - ${ex.message}"))
+                if (!interactive) System.exit(1)
+            }
         }
     }
 
@@ -414,11 +416,7 @@ class Console {
 
         Preferences.expandoMagic()
 
-        // need to do some up front processing to try to support 
"bin/gremlin.sh init.groovy" until this deprecated
-        // feature can be removed. ultimately this should be removed when a 
breaking change can go in
         IO io = new IO(System.in, System.out, System.err)
-        if (args.length == 1 && !args[0].startsWith("-"))
-            new Console(io, [args[0]], true)
 
         final CliBuilder cli = new CliBuilder(usage: 'gremlin.sh [options] 
[...]', formatter: new HelpFormatter(), stopAtNonOption: false)
 
@@ -452,6 +450,8 @@ class Console {
         }
 
         if (options.v) {
+        if (args.length == 1 && !args[0].startsWith("-"))
+            new Console(io, [args[0]], true)
             println("gremlin " + Gremlin.version())
             System.exit(0)
         }
@@ -469,9 +469,50 @@ class Console {
             System.exit(0)
         }
 
-        List<String> scriptAndArgs = options.e ?
-                (options.es != null && options.es ? options.es : null) :
-                (options.is != null && options.is ? options.is : null)
-        new Console(io, scriptAndArgs, !options.e)
+        // need to do some up front processing to try to support 
"bin/gremlin.sh init.groovy" until this deprecated
+        // feature can be removed. ultimately this should be removed when a 
breaking change can go in
+        if (args.length == 1 && !args[0].startsWith("-")) {
+            new Console(io, [[args[0]]], true)
+        } else {
+            def scriptAndArgs = parseArgs(options.e ? "-e" : "-i", args, cli)
+            new Console(io, scriptAndArgs, !options.e)
+        }
+    }
+
+    /**
+     * Provides a bit of a hack around the limitations of the {@code 
CliBuilder}. This method directly parses the
+     * argument list to allow for multiple {@code -e} and {@code -i} values 
and parses such parameters into a list
+     * of lists where the inner list is a script file and its arguments.
+     */
+    private static List<List<String>> parseArgs(final String option, final 
String[] args, final CliBuilder cli) {
+        def parsed = []
+        for (int ix = 0; ix < args.length; ix++) {
+            if (args[ix] == option) {
+                // increment the counter to move past the option that was 
found. should now be positioned on the
+                // first argument to that option
+                ix++
+
+                def parsedSet = []
+                for (ix; ix < args.length; ix++) {
+                    // this is a do nothing as there's no arguments to the 
option or it's the start of a new option
+                    if (cli.options.options.any { "-" + it.opt == args[ix] || 
"--" + it.longOpt == args[ix] }) {
+                        // rollback the counter now that we hit the next option
+                        ix--
+                        break;
+                    }
+                    parsedSet << args[ix]
+                }
+
+                if (!parsedSet.isEmpty()) {
+                    // check if the params were passed in with double quotes 
such that they arrive as a single arg
+                    if (parsedSet.size() == 1)
+                        parsed << parsedSet[0].toString().split(" ").toList()
+                    else
+                        parsed << parsedSet
+                }
+            }
+        }
+
+        return parsed
     }
 }

Reply via email to