Comments (5)
That's one way to do it, although you don't necessarily need the group. You can test each command individually if you use findOrSetObject
instead.
data class User(val login: String, val name: String?)
class UserCommand : CliktCommand() {
val login by option().required()
val name by option()
val users by findOrSetObject { mutableMapOf<String, User>() }
override fun run() {
users[login] = User(login, name)
}
}
class ReplaceCommand : CliktCommand() {
val users by findOrSetObject { mutableMapOf<String, User>() }
val args: Set<String> by argument().multiple().unique().help("files to do replacement(s) in")
override fun run() {
echo("users: $users")
echo("args: $args")
}
}
class Tool : CliktCommand(allowMultipleSubcommands = true) {
override fun run() {
// set the context object for subcommands
currentContext.obj = mutableMapOf<String, User>()
}
}
test
works with subcommands, you just have to register them like you normally would:
val command = Tool().subcommands(UserCommand(), ReplaceCommand())
val result = command.test(
"user --login a --name b user --login c replace file1 file2"
)
assertEquals(
result.output,
"""
users: {a=User(login=a, name=b), c=User(login=c, name=null)}
args: [file1, file2]
""".trimIndent()
)
from clikt.
Groups can't be repeated, but subcommands can. Use a subcommand instead of a group and you can have something like this:
$ ./tool --unrelatedOption person --name hoffi --age=30 person --name both --age=77 --opt=78 person --name jesus --opt=42
from clikt.
My goal is NOT to run()
a command consecutively, as I am transforming very big files,
I want to have some opts that are "globally" for "how" (e.g. case insensitive) to transform all the files,
PLUS multiple groups of options which tell "what" to do with different(!) parts/regions in each file.
So: for a region identified by A do X, for a region identified by B do Y, ...
As the files are very big, I don't want to run the subcommand x times for achieving all the x transformations,
but give a set of transformations (plus the global options once) so I can do a "transform once"-run on each file,
doing ALL the transformations to it which in turn are defined by the combined information in each group of options.
any way on how to achieve something like that?
addendum: how could I pass trailing arguments (the files to operate on) to ALL subcommand's execution??
Because if my subcommand has arguments val args: Set<String> by argument().multiple().unique()
It will "eat up" anything after the last option of the first subcommand as arguments (and not calling the subcommand a second time, but passing the 2nd 'person' as first arg to the subcommand)
example of what I have in mind:
$ kscript replaceInFiles \
--verbose --ignore-nonexisting --backup \
\
--region-start '^# START REPLACE Region 1' \
--region-end '^# END REPLACE Region 1' \
--replace '\d' "X" --replace 'ri' 'ir' \
\
--region-start '^# START REPLACE Region 2' \
--region-end '^# END REPLACE Region 2' \
--replace '^(\w+) ([A-Z]+)(.*)$' 'CHANGED $2' --replace 'regex' 'replaced by $2' \
\
~/tmp/original.txt ~/tmp/nonex.txt
I know, a bit more "invasive" ... but also would be way cool! :)
from clikt.
You don't have to do any processing in your run
; you can collect the info into your context object and process everything at the end.
But if you're fixed on not using subcommands, you'll have to collect the repeated options as arguments and group them yourself. Global options can be declared as usual.
You can't have arguments after a subcommand with multiple
arguments. There would be no way to know whether a token should belong to the parent or subcommand.
from clikt.
just to confirm if I correctly understood: (and if so, for others who come acoss this as reference)
for re-cooccuring OptionGroup
s you'd need
- a
CliktCommand
that has a singleOptionGroup
in it
and puts them in parents currentContext (or its own context to be fetched by subcommand name) - the parent CliktCommand has to
CliktCommand(allowMultipleSubcommands = true)
- the
CliktCommand
that actually uses the multiple gatherdOptionGroup
s
(which each of abovesCliktCommand
puts in the context)
by fetching them from the Clikt context (List or Map)
so e.g.:
the gathering one OptionGroup
CliktCommand:
class UserOptGroup() : CliktCommand(name = "user") {
class UserOptions : OptionGroup() {
override fun toString(): String = "user($login, ${name.singleQuote()}, $age, $opt)"
val login by option().required()
val name by option()
val age by option().int()
val opt by option().int()
}
data class User(val login: String, val name: String?, val age: Int?, val opt: Int?) {
override fun toString(): String = "User('$login', ${name.singleQuote()}, $age, $opt)"
}
val userOptions by UserOptions().cooccurring()
val unrelatedOption by option("--unrelatedOption").flag()
override fun run() {
val parentCtxUsers: MutableMap<String, User> = currentContext.parent!!.findOrSetObject { mutableMapOf() }
userOptions?.let { parentCtxUsers[it.login] = User(it.login, it.name, it.age, it.opt) }
// debug output
echo("UserOptGroup run():")
echo(" unrelatedOption: '${unrelatedOption}'")
userOptions?.let { echo(" $it") } ?: echo(" no userOptions")
}
}
the actual CliktCommand using all of above's OptionGroup
s
class DoWithUsers() : CliktCommand(name = "doWithUsers") {
val users: MutableMap<String, UserOptGroup.User> = mutableMapOf() // set/filled in run() from parent CliktCommand context
val args: Set<String> by argument().multiple().unique().help("files to do replacement(s) in")
override fun run() {
val parentUsers = currentContext.parent!!.findObject<MutableMap<String, UserOptGroup.User>>()
parentUsers?.let { users.putAll(it) }
// debug output
if (users.isEmpty()) echo("no users!")
echo("DoWithUsers: ${users.size} users: ${users.entries.joinToStringSingleQuoted()}")
echo("DoWithUsers: args: ${args.joinToStringSingleQuoted()}")
}
}
btw: doing it this way the actual CliktCommand cannot be unit tested without both CliktCommands
or if connected via a parent CliktComand all three of them,
as fun CliktCommand.test(...)
only tests a CliktCommand in isolation of all other CliktCommands
(at least not without some dirty hardcoded inject the List/Map of OptionGroup's into it.)
or do you have a fancy idea for testing things like above's class DoWithUsers() : CliktCommand(name = "doWithUsers")
? :)
from clikt.
Related Issues (20)
- Option to echo "raw" String HOT 4
- Feature request: generation of man pages or AsciiDoc HOT 1
- Feature request: choosing from options using arrow keys or fuzzy find HOT 1
- [QUESTION] Validate input arguments without running the command HOT 1
- Support path arguments for native HOT 3
- Other ways to construct a CliktCommand object HOT 4
- Disable prompt() globally HOT 3
- Cooccuring option group with required options is passed as nullable HOT 3
- Suspending command support HOT 16
- Question: by option().multiple().groupChoice() ? HOT 5
- Question: one single argument for multiple repeating subcommands HOT 1
- clikt 4.X grew in jar size to over 8mb HOT 8
- Mutually exclusive options in OptionGroup HOT 1
- 4.4.0 CHANGELOG correct?
- Control inputInteractive and outputInteractive in CliktTesting
- No way to timeout on missing input HOT 2
- Error message "java.lang.NoClassDefFoundError: java/lang/management/ManagementFactory" when running as distributable HOT 5
- Is clikt-core published? HOT 5
- Add imports to example Clikt program HOT 2
- Kotlin-Native charset error on Windows HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from clikt.