First proper draft of DSL for Groovy Commands

Users can declare or Command, OptionHandler classes in an init script
or they can use a DSL, e.g.

command("foo") { args -> println "Do stuff with ${args} array" }

or

command("foo") {
  options { option "bar", "Help text for bar option" ithOptionArg() ofType Integer }
  run { options -> println "Do stuff with ${options.valueOf('bar')}" }
}
This commit is contained in:
Dave Syer
2014-01-05 10:11:48 +00:00
parent 1e75c0a55b
commit ac34f9c993
14 changed files with 340 additions and 138 deletions

View File

@@ -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");

View File

@@ -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"));
}
}

View File

@@ -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<String, Closure<?>> 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("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<String, Closure<?>> commands = ((Commands) main.newInstance()).getCommands();
assertEquals(1, commands.size());
assertEquals("foo", commands.keySet().iterator().next());
Closure<?> closure = commands.values().iterator().next();
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<String, Closure<?>> closures = commands.getCommands();
assertEquals(1, closures.size());
assertEquals("foo", closures.keySet().iterator().next());
final Closure<?> closure = closures.values().iterator().next();
Map<String, OptionHandler> 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 {

View File

@@ -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<String> optionsHelp = ["No options"]
boolean optionCommand = false
void run(String... args) {
println "Hello ${args[0]}"
}
}

View File

@@ -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"
}

View File

@@ -16,8 +16,6 @@
package org.test.command
import java.util.Collection;
class TestCommand implements Command {
String name = "foo"

View File

@@ -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')}"
}

View File

@@ -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')}"
}

View File

@@ -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')}"
}

View File

@@ -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"
}

View File

@@ -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')}" }
}