Trees 🌲🌲🌲
Terminals are so vague. You have commands, sub commands, args, flags and ENV variables. All separated by spaces, dashes and equals signs... when really all you have is a line of text.
CLI frameworks are vague too. You build your beautiful structure then map user input to those components... but now there's a translation gap between the structure you've defined and what the user types.
With Trees you write what the literal user input will be, then Trees breaks it down into a tree of commands, subcommands and options if need be. Think of your commands as routes with params. Think of the structure as a tree 🌲.
✨ Features:
- Flat and nested commands use the exact same API
- Args and flags
- Zero-dependencies
Example
Taken from Rain CLI which powers the Raindeer web framework.
line('new :app_name') do |app_name|
execute { Rain::CLI::Template.build(app_name) }
line('--with-db', '-d') do |app_name|
summary { 'Set up your application with a database.' }
execute { Rain::CLI::Template.build(app_name, db: true) }
end
end
line('static build') do
execute { Static.build }
endNested VS Flat
The following structures are equivalent and produce the same tree-like structure internally:
Nested:
line('switch build') do
execute { Switch.build }
line('--reset-delay') do
execute { Switch.build(reset_delay: true) }
end
endFlat:
line('switch build') do
execute { Switch.build }
end
line('switch build --reset-delay') do
execute { Switch.build(reset_delay: true) }
endAutocomplete [UNRELEASED]
Because all the lines are stored as a prefix tree (AKA Trie), you get an autocomplete for free!
Execution Behaviour [UNRELEASED]
Trees will execute the deepest match with an execute block so that you can add summary blocks for flags without doubling up on execute blocks:
line('new :app_name') do |app_name, with_db:|
execute { Rain::CLI::Template.build(app_name, db: with_db) }
line('--with-db', '-d') do
summary { 'Set up your application with a database.' }
end
endThe above structure is equivalent to:
line('new :app_name') do |app_name|
execute { Rain::CLI::Template.build(app_name) }
line('--with-db', '-d') do |app_name|
summary { 'Set up your application with a database.' }
execute { Rain::CLI::Template.build(app_name, db: true) }
end
endAPI
Different placeholders in your string represent different data types.
:arg
Args are defined by prefixing a space separated word with a colon :.
You can do fancy stuff like root level args pretty easily:
$ cli @prod commandWhich would be defined like:
line('@:environment command')Flag [UNRELEASED]
Syntax: --flag, -f
Option
Syntax: --flag=:value or -o :value
line('build --env=:environment') do |environment|
# The environment arg is now available.
endsubcommand
Literal text will be interpreted as a subcommand.
line('command subcommand') do
# ...
endSummary block
Add a summary to a command, sub command or flag:
line('static build') do
summary { 'Build your static site' }
end