...
Code Block |
---|
@Component public class LsCommandExampleCommand implements Command<List<File>>Command<String> { @ServiceDependency private volatile Navigator m_navigator; //1 @Override public String getName() { return "lsexample"; } //2 @Override public Plugin getPlugin() { return BootstrapPlugin.INSTANCEnew MyPlugin(); } @Override public Scope getScope() { return Scope.WORKSPACE; } //3 @Override public List<Question<?>> getQuestions() { return Arrays.asList( new BooleanQuestion("R", "recursive", false), new BooleanQuestionStringQuestion("Ff", "files onlyfilter", false""), new StringQuestion(Question.DEFAULT_KEY, "filtersome argument", "")); } //4 @Override public List<File> execute(String... args) { Answers answers = Answers.parse(getQuestions(), args); FileFilter filter = null; boolean recursive = answers.get("R"); booleanString filesOnlyfilter = answers.get("Ff"); String nameFiltersomeArg = answers.get(Question.DEFAULT_KEY); //Do stuff } } |
- The name of the command. On the command line this will be combined with the name of the plugin (getPlugin()). If the plugin name would be "bootstrap"; the command could be executed as 'bootstrap-example'.
- Reference to the plugin class. Multiple commands can share the same Plugin, that way they inherit the same base name. Also a Plugin can contain additional information such as the developer name and website.
- A list of questions that this command uses. This is the most important change in the API. Questions are now defined static instead of ad-hoc using the old prompt API. This command line uses this to define arguments for the command, which is necessary to generate a "man" message and to make argument completion possible. This also means that it is no longer possible to prompt for more questions during command execution. While this is a restriction, it is easy to work around it with a slightly different command design. The answers API is based on Commons CLI, which gives us support for both named and unnamed arguments. More about that later.
- The actual implementation of the command. Most commands will first parse the answers to te defined questions and continue from there. Besides this Answers method call instead of calls to the Prompt API the code of existing commands can just be copy/pasted to migrate them to the new API.
More about arguments
Arguments can be either named or unnamed. The command described about could be invoked as follows:
Code Block |
---|
bootstrap-example -R -f testfilter myunnamed-arg |
This would set the recursive variable to true, the filter variable to "testfilter" and the someArg variable to "myunnamed-arg".
A BooleanQuestion is represented as a flag; by adding it to the parameters it's set to true, omitting it sets it to false.
When the Question.DEFAULT_KEY key is used for a question it becomes an unnamed argument. This way we support commands that can be of similar form as most shells.
Currently the following question types are supported:
- StringQuestion
- BooleanQuestion
- ChoiceQuestion
- FqnQuestion
Unit testing the new API
Because arguments can easily be passed from code with the new API it becomes easier to write unit tests for commands. For example, these a few tests for the "ls" command.
Code Block |
---|
@RunWith(MockitoJUnitRunner.class)
public class LsCommandTest {
@Spy @InjectMocks LsCommand cmd = new LsCommand();
@Mock Navigator m_navigator;
@Before
public void setup() {
when(m_navigator.getCurrentDir()).thenReturn(new File( System.getProperty("user.dir"), "test/ls-testdir").toPath());
}
@Test
public void withoutArgs() {
List<File> result = cmd.execute();
assertThat(result.size(), is(4));
}
@Test
public void recursive() {
List<File> result = cmd.execute("-R");
assertThat(result.size(), is(8));
}
@Test
public void files() {
List<File> result = cmd.execute("-F");
assertThat(result.size(), is(2));
}
@Test
public void recursiveFilesOnly() {
List<File> result = cmd.execute("-R","-F");
assertThat(result.size(), is(6));
}
@Test
public void name() {
List<File> result = cmd.execute("file1.*");
assertThat(result.size(), is(1));
}
@Test
public void combined() {
List<File> result = cmd.execute("-R", "-F", ".*file1.*");
assertThat(result.size(), is(3));
}
} |
Java 8
Bootstrap 1.0 will be based on Java 8. While there are no plans to move other Amdatu libraries to Java 8, Amdatu Bootstrap is a bit different because it only requires Java 8 on the developer's machine.
Next steps
I have migrated Bootstrap core, the core plugins and the Amdatu plugins. It would be great to get some help migrating the other plugins. To make sure we're getting to a release I would like to propose a release date for 1.0.0 at September 5 (we also have a LT meeting planned that day).