Skip to content

Repository files navigation

Gem version GitHub repo Codeberg repo

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.

Trees just gets you to write what the literal user input will be, then 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 🌲.

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 }
end

Nested 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
end

Flat:

line('switch build') do
  execute { Switch.build }
end

line('switch build --reset-delay') do
  execute { Switch.build(reset_delay: true) }
end

Autocomplete [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
end

The 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
end

API

Different placeholders in your string represent different data types.

:variable

Variables are defined by prefixing a space separated word with a colon :.

You can do fancy stuff like root level args pretty easily:

$ cli @prod command

Which would be defined like:

line('@:environment command')

--flag, -f

...

subcommand

The literal text with no special characters will be interpreted as a subcommand.

...

Summary block

Add a summary to a command, sub command or flag:

line('static build') do
  summary { 'Build your static site' }
end

About

A CLI framework where you just write the literal terminal commands

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages