Add support for global help options

- Essentially this commit registeres on default `--help` and
  `-h` options to every command and execution short circuits
  in presense of help options to help command.
- Add Supplier<CommandRegistration.Builder> as a bean which
  can be autowired registration beans.
- Make this common bean customisable via CommandRegistrationCustomizer.
- Change StandardMethodTargetRegistrar to use supplier so that
  annotated commands gets common customizations.
- Change sample commands to use supplier.
- Add new group, spring.shell.help to config props.
- Docs changes
- Fixes #582
- Fixes #585
This commit is contained in:
Janne Valkealahti
2022-12-04 17:23:25 +00:00
parent b555148ce9
commit ef191e66f3
27 changed files with 773 additions and 78 deletions

View File

@@ -0,0 +1,41 @@
[[commands-helpoptions]]
=== Help Options
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
_Spring Shell_ has a build-in `help` command but not all favour getting command help
from it as you always need to call it with arguments for target command. It's
common in many cli frameworks for every command having options _--help_ and _-h_
to print out command help.
Default functionality is that every command will get modified to have options
_--help_ and _-h_, which if present in a given command will automatically
short circuit command execution into a existing `help` command regardless
what other command-line options is typed.
Below example shows its default settings.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationHelpOptionsSnippets.java[tag=defaults]
----
====
It is possible to change default behaviour via configuration options.
====
[source, yaml]
----
spring:
shell:
help:
enabled: true
long-names: help
short-names: h
command: help
----
====
NOTE: Commands defined programmationally or via annotations will automatically add
help options. With annotation model you can only turn things off globally, programmatic
model gives option to modify settings per command.

View File

@@ -1,11 +1,40 @@
==== Programmatic Model
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
In the programmatic model, `CommandRegistration` is defined as a `@Bean`, and it is automatically registered:
In the programmatic model, `CommandRegistration` can be defined as a `@Bean`
and it will be automatically registered.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=snippet1]
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=plain]
----
====
If all your commands have something in common, an instance of
a _Supplier<CommandRegistration.Builder>_ is created which can
be autowired. Default implementation of this supplier returns
a new builder so you don't need to worry about its internal state.
IMPORTANT: Commands registered programmatically automatically
add _help options_ mentioned in <<commands-helpoptions>>.
If bean of this supplier type is defined then auto-configuration
will back off giving you an option to redefine default functionality.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=fromsupplier]
----
====
`CommandRegistrationCustomizer` beans can be defined if you want to centrally
modify builder instance given you by supplier mentioned above.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=customizer]
----
====

View File

@@ -15,6 +15,8 @@ include::using-shell-commands-exceptionhandling.adoc[]
include::using-shell-commands-hidden.adoc[]
include::using-shell-commands-helpoptions.adoc[]
include::using-shell-commands-interactionmode.adoc[]
include::using-shell-commands-builtin.adoc[]

View File

@@ -15,17 +15,44 @@
*/
package org.springframework.shell.docs;
import java.util.function.Supplier;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.boot.CommandRegistrationCustomizer;
import org.springframework.shell.command.CommandRegistration;
public class CommandRegistrationBeanSnippets {
// tag::snippet1[]
@Bean
CommandRegistration commandRegistration() {
return CommandRegistration.builder()
.command("mycommand")
.build();
class Dump1 {
// tag::plain[]
@Bean
CommandRegistration commandRegistration() {
return CommandRegistration.builder()
.command("mycommand")
.build();
}
// end::plain[]
}
class Dump2 {
// tag::fromsupplier[]
@Bean
CommandRegistration commandRegistration(Supplier<CommandRegistration.Builder> builder) {
return builder.get()
.command("mycommand")
.build();
}
// end::fromsupplier[]
}
class Dump3 {
// tag::customizer[]
@Bean
CommandRegistrationCustomizer commandRegistrationCustomizerExample() {
return builder -> {
// customize instance of CommandRegistration.Builder
};
}
// end::customizer[]
}
// end::snippet1[]
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2022 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
*
* https://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.
*/
package org.springframework.shell.docs;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.CommandRegistration;
class CommandRegistrationHelpOptionsSnippets {
class Dump1 {
// tag::defaults[]
@Bean
CommandRegistration commandRegistration() {
return CommandRegistration.builder()
.command("mycommand")
.withHelpOptions()
.enabled(true)
.longNames("help")
.shortNames('h')
.command("help")
.and()
.build();
}
// end::defaults[]
}
}