diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java index 97962758409..7eadae13b3f 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java @@ -117,11 +117,22 @@ public class InitCommand extends OptionParsingCommand { } } else if (Commands.class.isAssignableFrom(type)) { - Map> commands = ((Commands) type.newInstance()) - .getCommands(); + Commands instance = (Commands) type.newInstance(); + Map> commands = instance.getCommands(); + Map handlers = instance.getOptions(); for (String command : commands.keySet()) { - this.cli.register(new ScriptCommand(command, commands - .get(command))); + if (handlers.containsKey(command)) { + // An OptionHandler is available + OptionHandler handler = handlers.get(command); + handler.setClosure(commands.get(command)); + this.cli.register(new ScriptCommand(command, handler)); + } + else { + // Otherwise just a plain Closure + this.cli.register(new ScriptCommand(command, commands + .get(command))); + + } } } else if (Script.class.isAssignableFrom(type)) { @@ -161,6 +172,8 @@ public class InitCommand extends OptionParsingCommand { public static interface Commands { Map> getCommands(); + + Map getOptions(); } } diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/OptionHandler.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/OptionHandler.java index c6a37ca7dde..b187e75d0f5 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/OptionHandler.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/OptionHandler.java @@ -51,7 +51,7 @@ public class OptionHandler { private OptionParser parser; - private Closure closure; + private Closure closure; private String help; @@ -74,12 +74,9 @@ public class OptionHandler { } protected void options() { - if (this.closure != null) { - this.closure.call(); - } } - public void setOptions(Closure closure) { + public void setClosure(Closure closure) { this.closure = closure; } @@ -100,6 +97,9 @@ public class OptionHandler { * @throws Exception */ protected void run(OptionSet options) throws Exception { + if (this.closure != null) { + this.closure.call(options); + } } public String getHelp() { diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCompilationCustomizer.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCompilationCustomizer.java index 610c10df946..66ceefdd6dc 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCompilationCustomizer.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCompilationCustomizer.java @@ -27,30 +27,30 @@ import joptsimple.OptionSpecBuilder; import org.codehaus.groovy.ast.ClassCodeVisitorSupport; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; -import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.InnerClassNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.PropertyNode; import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.MapExpression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.EmptyStatement; import org.codehaus.groovy.ast.stmt.ExpressionStatement; -import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.groovy.classgen.GeneratorContext; import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.control.customizers.CompilationCustomizer; import org.codehaus.groovy.control.customizers.ImportCustomizer; -import org.springframework.asm.Opcodes; import org.springframework.boot.cli.Command; import org.springframework.boot.cli.command.InitCommand.Commands; /** - * Customizer for the compilation of CLI commands. + * Customizer for the compilation of CLI script commands. * * @author Dave Syer */ @@ -64,29 +64,92 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer { public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { findCommands(source, classNode); - overrideOptionsMethod(source, classNode); addImports(source, context, classNode); } + /** + * If the script defines a block in this form: + * + *
+	 * command("foo") { args ->
+	 *     println "Command foo called with args: ${args}"
+	 * }
+	 * 
+ * + * Then the block is taken and used to create a Command named "foo" that runs the + * closure when it is executed. + * + * If you want to declare options (and provide help text), use this form: + * + *
+	 * command("foo") {
+	 * 
+	 *   options {
+	 *     option "foo", "My Foo option"
+	 *     option "bar", "Bar has a value" withOptionalArg() ofType Integer
+	 *   }
+	 *   
+	 *   run { options ->
+	 *   	println "Command foo called with bar=${options.valueOf('bar')}"
+	 *   }
+	 * 
+	 * }
+	 * 
+ * + * In this case the "options" block is taken and used to override the + * {@link OptionHandler#options()} method. Each "option" is a call to + * {@link OptionHandler#option(String, String)}, and hence returns an + * {@link OptionSpecBuilder}. Makes a nice readable DSL for adding options. + * + * @param source the source node + * @param classNode the class node to manipulate + */ private void findCommands(SourceUnit source, ClassNode classNode) { - CommandVisitor visitor = new CommandVisitor(source); + CommandVisitor visitor = new CommandVisitor(source, classNode); classNode.visitContents(visitor); visitor.addFactory(classNode); } + /** + * Add imports to the class node to make writing simple commands easier. No need to + * import {@link OptionParser}, {@link OptionSet}, {@link Command} or + * {@link OptionHandler}. + * + * @param source the source node + * @param context the current context + * @param classNode the class node to manipulate + */ + private void addImports(SourceUnit source, GeneratorContext context, + ClassNode classNode) { + ImportCustomizer importCustomizer = new ImportCustomizer(); + importCustomizer.addImports("joptsimple.OptionParser", "joptsimple.OptionSet", + OptionParsingCommand.class.getCanonicalName(), + Command.class.getCanonicalName(), OptionHandler.class.getCanonicalName()); + importCustomizer.call(source, context, classNode); + } + + /** + * Helper to extract a Commands instance (adding that interface to the current class + * node) so individual commands can be registered with the CLI. + * + * @author Dave Syer + */ private static class CommandVisitor extends ClassCodeVisitorSupport { private SourceUnit source; - private MapExpression map = new MapExpression(); + private MapExpression closures = new MapExpression(); + private MapExpression options = new MapExpression(); private List statements = new ArrayList(); private ExpressionStatement statement; + private ClassNode classNode; - public CommandVisitor(SourceUnit source) { + public CommandVisitor(SourceUnit source, ClassNode classNode) { this.source = source; + this.classNode = classNode; } private boolean hasCommands() { - return !this.map.getMapEntryExpressions().isEmpty(); + return !this.closures.getMapEntryExpressions().isEmpty(); } private void addFactory(ClassNode classNode) { @@ -96,7 +159,10 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer { classNode.addInterface(ClassHelper.make(Commands.class)); classNode.addProperty(new PropertyNode("commands", Modifier.PUBLIC | Modifier.FINAL, ClassHelper.MAP_TYPE.getPlainNodeReference(), - classNode, this.map, null, null)); + classNode, this.closures, null, null)); + classNode.addProperty(new PropertyNode("options", Modifier.PUBLIC + | Modifier.FINAL, ClassHelper.MAP_TYPE.getPlainNodeReference(), + classNode, this.options, null, null)); } @Override @@ -128,9 +194,24 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer { this.statements.add(this.statement); ConstantExpression name = (ConstantExpression) arguments .getExpression(0); - ClosureExpression closure = (ClosureExpression) arguments - .getExpression(1); - this.map.addMapEntryExpression(name, closure); + Expression expression = arguments.getExpression(1); + if (expression instanceof ClosureExpression) { + ClosureExpression closure = (ClosureExpression) expression; + ActionExtractorVisitor action = new ActionExtractorVisitor( + this.source, this.classNode, name.getText()); + closure.getCode().visit(action); + if (action.hasOptions()) { + this.options.addMapEntryExpression(name, action.getOptions()); + expression = action.getAction(); + } + else { + expression = new ClosureExpression( + new Parameter[] { new Parameter( + ClassHelper.make(String[].class), "args") }, + closure.getCode()); + } + this.closures.addMapEntryExpression(name, expression); + } } } } @@ -138,81 +219,80 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer { } /** - * Add imports to the class node to make writing simple commands easier. No need to - * import {@link OptionParser}, {@link OptionSet}, {@link Command} or - * {@link OptionHandler}. + * Helper to pull out options and action closures from a command declaration (if they + * are there). * - * @param source the source node - * @param context the current context - * @param classNode the class node to manipulate + * @author Dave Syer */ - private void addImports(SourceUnit source, GeneratorContext context, - ClassNode classNode) { - ImportCustomizer importCustomizer = new ImportCustomizer(); - importCustomizer.addImports("joptsimple.OptionParser", "joptsimple.OptionSet", - OptionParsingCommand.class.getCanonicalName(), - Command.class.getCanonicalName(), OptionHandler.class.getCanonicalName()); - importCustomizer.call(source, context, classNode); - } + private static class ActionExtractorVisitor extends ClassCodeVisitorSupport { - /** - * If the script defines a block in this form: - * - *
-	 * options {
-	 *   option "foo", "My Foo option"
-	 *   option "bar", "Bar has a value" withOptionalArg() ofType Integer
-	 * }
-	 * 
- * - * Then the block is taken and used to override the {@link OptionHandler#options()} - * method. In the example "option" is a call to - * {@link OptionHandler#option(String, String)}, and hence returns an - * {@link OptionSpecBuilder}. Makes a nice readable DSL for adding options. - * - * @param source the source node - * @param classNode the class node to manipulate - */ - private void overrideOptionsMethod(SourceUnit source, ClassNode classNode) { - - ClosureExpression closure = options(source, classNode); - if (closure != null) { - classNode.addMethod(new MethodNode("options", Opcodes.ACC_PROTECTED, - ClassHelper.VOID_TYPE, new Parameter[0], new ClassNode[0], closure - .getCode())); - classNode.setSuperClass(ClassHelper.make(OptionHandler.class)); + private static final Parameter[] OPTIONS_PARAMETERS = new Parameter[] { new Parameter( + ClassHelper.make(OptionSet.class), "options") }; + private SourceUnit source; + private ClassNode classNode; + private Expression options; + private ClosureExpression action; + private String name; + + public ActionExtractorVisitor(SourceUnit source, ClassNode classNode, String name) { + this.source = source; + this.classNode = classNode; + this.name = name; } - } + @Override + protected SourceUnit getSourceUnit() { + return this.source; + } - private ClosureExpression options(SourceUnit source, ClassNode classNode) { - - BlockStatement block = source.getAST().getStatementBlock(); - List statements = block.getStatements(); - - for (Statement statement : new ArrayList(statements)) { - if (statement instanceof ExpressionStatement) { - ExpressionStatement expr = (ExpressionStatement) statement; - Expression expression = expr.getExpression(); - if (expression instanceof MethodCallExpression) { - MethodCallExpression method = (MethodCallExpression) expression; - if (method.getMethod().getText().equals("options")) { - statements.remove(statement); - expression = method.getArguments(); - if (expression instanceof ArgumentListExpression) { - ArgumentListExpression arguments = (ArgumentListExpression) expression; - expression = arguments.getExpression(0); - if (expression instanceof ClosureExpression) { - return (ClosureExpression) expression; - } - } + public boolean hasOptions() { + return this.options != null; + } + + public Expression getOptions() { + return this.options; + } + + public ClosureExpression getAction() { + return this.action != null ? this.action : new ClosureExpression( + OPTIONS_PARAMETERS, new EmptyStatement()); + } + + @Override + public void visitMethodCallExpression(MethodCallExpression call) { + Expression methodCall = call.getMethod(); + if (methodCall instanceof ConstantExpression) { + ConstantExpression method = (ConstantExpression) methodCall; + if ("options".equals(method.getValue())) { + ArgumentListExpression arguments = (ArgumentListExpression) call + .getArguments(); + Expression expression = arguments.getExpression(0); + if (expression instanceof ClosureExpression) { + ClosureExpression closure = (ClosureExpression) expression; + InnerClassNode type = new InnerClassNode(this.classNode, + this.classNode.getName() + "$" + this.name + + "OptionHandler", Modifier.PUBLIC, + ClassHelper.make(OptionHandler.class)); + type.addMethod("options", Modifier.PROTECTED, + ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, + ClassNode.EMPTY_ARRAY, closure.getCode()); + this.classNode.getModule().addClass(type); + this.options = new ConstructorCallExpression(type, + ArgumentListExpression.EMPTY_ARGUMENTS); + } + } + else if ("run".equals(method.getValue())) { + ArgumentListExpression arguments = (ArgumentListExpression) call + .getArguments(); + Expression expression = arguments.getExpression(0); + if (expression instanceof ClosureExpression) { + ClosureExpression closure = (ClosureExpression) expression; + this.action = new ClosureExpression(OPTIONS_PARAMETERS, + closure.getCode()); } } } } - - return null; - } } diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java index 67105dde5f9..f6020ef3b95 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java @@ -71,10 +71,37 @@ public class InitCommandTests { @Test public void initCommand() throws Exception { - this.command.run("src/test/resources/command.groovy"); + this.command.run("src/test/resources/commands/command.groovy"); verify(this.cli, times(this.defaultCount + 1)).register(any(Command.class)); } + @Test + public void initHandler() throws Exception { + this.command.run("src/test/resources/commands/handler.groovy"); + verify(this.cli, times(this.defaultCount + 1)).register(any(Command.class)); + } + + @Test + public void initClosure() throws Exception { + this.command.run("src/test/resources/commands/closure.groovy"); + verify(this.cli, times(this.defaultCount + 1)).register(any(Command.class)); + } + + @Test + public void initOptions() throws Exception { + this.command.run("src/test/resources/commands/options.groovy"); + verify(this.cli, times(this.defaultCount + 1)).register(any(Command.class)); + } + + @Test + public void runOptions() throws Exception { + SpringCli cli = new SpringCli(); + InitCommand command = cli.getInitCommand(); + command.run("src/test/resources/commands/options.groovy"); + cli.find("foo").run("--foo=bar", "--bar=123"); + assertTrue(this.output.toString().contains("Hello Foo: bar=123")); + } + @Test(expected = IllegalArgumentException.class) public void initNonExistentScript() throws Exception { this.command.run("nonexistent.groovy"); diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/ScriptCommandTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/ScriptCommandTests.java index 78132d2e288..d69aab8eeef 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/ScriptCommandTests.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/ScriptCommandTests.java @@ -18,7 +18,6 @@ package org.springframework.boot.cli.command; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.springframework.boot.OutputCapture; @@ -65,15 +64,14 @@ public class ScriptCommandTests { public void handler() throws Exception { this.init.run("src/test/resources/commands/handler.groovy"); this.cli.find("foo").run("Foo", "--foo=bar"); - assertTrue(executed); + assertTrue(this.output.toString().contains("Hello [Foo]")); } @Test - @Ignore public void options() throws Exception { this.init.run("src/test/resources/commands/options.groovy"); this.cli.find("foo").run("Foo", "--foo=bar"); - assertTrue(executed); + assertTrue(this.output.toString().contains("Hello Foo")); } } diff --git a/spring-boot-cli/src/test/java/cli/command/ScriptCompilationCustomizerTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/ScriptCompilationCustomizerTests.java similarity index 70% rename from spring-boot-cli/src/test/java/cli/command/ScriptCompilationCustomizerTests.java rename to spring-boot-cli/src/test/java/org/springframework/boot/cli/command/ScriptCompilationCustomizerTests.java index e08fe7b0b8b..eab060a4455 100644 --- a/spring-boot-cli/src/test/java/cli/command/ScriptCompilationCustomizerTests.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/command/ScriptCompilationCustomizerTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package cli.command; +package org.springframework.boot.cli.command; import groovy.lang.Closure; @@ -29,8 +29,6 @@ import org.junit.Test; import org.springframework.boot.OutputCapture; import org.springframework.boot.cli.Command; import org.springframework.boot.cli.command.InitCommand.Commands; -import org.springframework.boot.cli.command.OptionHandler; -import org.springframework.boot.cli.command.ScriptCompilationCustomizer; import org.springframework.boot.cli.compiler.GroovyCompiler; import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; import org.springframework.boot.cli.compiler.GroovyCompilerScope; @@ -75,24 +73,55 @@ public class ScriptCompilationCustomizerTests { @Test public void addsCommands() throws Exception { Class[] types = this.compiler.compile(new File( - "src/test/resources/scripts/options.groovy")); + "src/test/resources/scripts/commands.groovy")); Class main = types[0]; assertTrue(Commands.class.isAssignableFrom(main)); } @Test - public void commandsExecutable() throws Exception { + public void closureWithStringArgs() throws Exception { Class[] types = this.compiler.compile(new File( - "src/test/resources/scripts/options.groovy")); + "src/test/resources/scripts/commands.groovy")); + Class main = types[0]; + Map> commands = ((Commands) main.newInstance()).getCommands(); + assertEquals(1, commands.size()); + assertEquals("foo", commands.keySet().iterator().next()); + Closure closure = commands.values().iterator().next(); + closure.call("foo", "bar"); + assertTrue(this.output.toString().contains("Hello Command")); + } + + @Test + public void closureWithEmptyArgs() throws Exception { + Class[] types = this.compiler.compile(new File( + "src/test/resources/scripts/commands.groovy")); Class main = types[0]; Map> commands = ((Commands) main.newInstance()).getCommands(); assertEquals(1, commands.size()); assertEquals("foo", commands.keySet().iterator().next()); Closure closure = commands.values().iterator().next(); - closure.call(); // what about args? + closure.call(); assertTrue(this.output.toString().contains("Hello Command")); } + @Test + public void closureAndOptionsDefined() throws Exception { + Class[] types = this.compiler.compile(new File( + "src/test/resources/scripts/options.groovy")); + Class main = types[0]; + Commands commands = (Commands) main.newInstance(); + Map> closures = commands.getCommands(); + assertEquals(1, closures.size()); + assertEquals("foo", closures.keySet().iterator().next()); + final Closure closure = closures.values().iterator().next(); + Map options = commands.getOptions(); + assertEquals(1, options.size()); + OptionHandler handler = options.get("foo"); + handler.setClosure(closure); + handler.run("--foo=bar", "--bar=blah", "spam"); + assertTrue(this.output.toString().contains("Hello [spam]: true blah")); + } + private static class TestGroovyCompilerConfiguration implements GroovyCompilerConfiguration { diff --git a/spring-boot-cli/src/test/resources/command.groovy b/spring-boot-cli/src/test/resources/command.groovy deleted file mode 100644 index 08f5ed71487..00000000000 --- a/spring-boot-cli/src/test/resources/command.groovy +++ /dev/null @@ -1,19 +0,0 @@ -class MyCommand implements Command { - - String name = "foo" - - String description = "My script command" - - String help = "No options" - - String usageHelp = "Not very useful" - - Collection optionsHelp = ["No options"] - - boolean optionCommand = false - - void run(String... args) { - println "Hello ${args[0]}" - } - -} \ No newline at end of file diff --git a/spring-boot-cli/src/test/resources/commands/closure.groovy b/spring-boot-cli/src/test/resources/commands/closure.groovy new file mode 100644 index 00000000000..d710d6cdbe2 --- /dev/null +++ b/spring-boot-cli/src/test/resources/commands/closure.groovy @@ -0,0 +1,19 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed 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. + */ + +command("foo") { args -> + println "Hello Foo" +} diff --git a/spring-boot-cli/src/test/resources/commands/command.groovy b/spring-boot-cli/src/test/resources/commands/command.groovy index f756ad4c9a8..1ae3f9418b1 100644 --- a/spring-boot-cli/src/test/resources/commands/command.groovy +++ b/spring-boot-cli/src/test/resources/commands/command.groovy @@ -16,8 +16,6 @@ package org.test.command -import java.util.Collection; - class TestCommand implements Command { String name = "foo" diff --git a/spring-boot-cli/src/test/resources/commands/handler.groovy b/spring-boot-cli/src/test/resources/commands/handler.groovy index 9b320e01fda..aa5db82effd 100644 --- a/spring-boot-cli/src/test/resources/commands/handler.groovy +++ b/spring-boot-cli/src/test/resources/commands/handler.groovy @@ -31,7 +31,6 @@ class TestCommand extends OptionHandler { void run(OptionSet options) { // Demonstrate use of Grape.grab to load dependencies before running println "Clean: " + Git.open(".." as File).status().call().isClean() - org.springframework.boot.cli.command.ScriptCommandTests.executed = true println "Hello ${options.nonOptionArguments()}: ${options.has('foo')}" } diff --git a/spring-boot-cli/src/test/resources/commands/options.groovy b/spring-boot-cli/src/test/resources/commands/options.groovy index c4475d623db..71fd8e3dde9 100644 --- a/spring-boot-cli/src/test/resources/commands/options.groovy +++ b/spring-boot-cli/src/test/resources/commands/options.groovy @@ -14,8 +14,19 @@ * limitations under the License. */ -command("foo") { args -> + def foo() { + "Foo" + } + +command("foo") { + + options { + option "foo", "A foo of type String" + option "bar", "Bar has a value" withOptionalArg() ofType Integer + } + + run { options -> + println "Hello ${foo()}: bar=${options.valueOf('bar')}" + } - org.springframework.boot.cli.command.ScriptCommandTests.executed = true - println "Hello ${options.nonOptionArguments()}: ${options.has('foo')} ${options.valueOf('bar')}" } diff --git a/spring-boot-cli/src/test/resources/scripts/closure.groovy b/spring-boot-cli/src/test/resources/scripts/closure.groovy new file mode 100644 index 00000000000..44ef43fe3a8 --- /dev/null +++ b/spring-boot-cli/src/test/resources/scripts/closure.groovy @@ -0,0 +1,24 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed 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. + */ + +def foo() { + "Foo" +} + +command("foo") { options -> + def foo = foo() + println "Hello ${foo} ${options.nonOptionArguments()}: ${options.has('foo')} ${options.valueOf('bar')}" +} diff --git a/spring-boot-cli/src/test/resources/scripts/commands.groovy b/spring-boot-cli/src/test/resources/scripts/commands.groovy new file mode 100644 index 00000000000..795100d6445 --- /dev/null +++ b/spring-boot-cli/src/test/resources/scripts/commands.groovy @@ -0,0 +1,21 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed 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. + */ + +command("foo") { args -> + + println "Hello Command" + +} diff --git a/spring-boot-cli/src/test/resources/scripts/options.groovy b/spring-boot-cli/src/test/resources/scripts/options.groovy index 795100d6445..6942b329a45 100644 --- a/spring-boot-cli/src/test/resources/scripts/options.groovy +++ b/spring-boot-cli/src/test/resources/scripts/options.groovy @@ -14,8 +14,10 @@ * limitations under the License. */ -command("foo") { args -> - - println "Hello Command" - +command("foo") { + options { + option "foo", "Some foo description" withOptionalArg() + option "bar", "Some bar" withOptionalArg() + } + run { options -> println "Hello ${options.nonOptionArguments()}: ${options.has('foo')} ${options.valueOf('bar')}" } }