Skip to content
/ clim Public
forked from at-grandpa/clim

Slim command line interface builder for Crystal.

License

Notifications You must be signed in to change notification settings

GregPK/clim

This branch is 248 commits behind at-grandpa/clim:master.

Folders and files

NameName
Last commit message
Last commit date
Aug 9, 2019
Aug 9, 2019
Oct 3, 2019
Nov 25, 2017
Jun 14, 2019
Mar 29, 2017
Jun 14, 2019
Oct 3, 2019
Oct 3, 2019

Repository files navigation

clim

"clim" is slim command line interface builder for Crystal.

"clim" = "cli" + "slim"

Build Status

Goals

  • Slim implementation.
  • Intuitive code.

Support

  • Option types
    • Int8
    • Int16
    • Int32
    • Int64
    • UInt8
    • UInt16
    • UInt32
    • UInt64
    • Float32
    • Float64
    • String
    • Bool
    • Array(Int8)
    • Array(Int16)
    • Array(Int32)
    • Array(Int64)
    • Array(UInt8)
    • Array(UInt16)
    • Array(UInt32)
    • Array(UInt64)
    • Array(Float32)
    • Array(Float64)
    • Array(String)
  • Default values for option
  • Required flag for option
  • Nested sub commands
  • --help option
  • Customizable help message
  • version macro
  • Command name alias

Installation

Add this to your application's shard.yml:

dependencies:
  clim:
    github: at-grandpa/clim
    version: 0.8.2

Minimum sample

src/minimum.cr

require "clim"

class MyCli < Clim
  main do
    run do |opts, args|
      puts "#{args.join(", ")}!"
    end
  end
end

MyCli.start(ARGV)
$ crystal build -o ./minimum src/minimum.cr
$ ./minimum foo bar baz
foo, bar, baz!

Command information sample

src/hello.cr

require "clim"

module Hello
  class Cli < Clim
    main do
      desc "Hello CLI tool."
      usage "hello [options] [arguments] ..."
      version "Version 0.1.0"
      option "-g WORDS", "--greeting=WORDS", type: String, desc: "Words of greetings.", default: "Hello"
      option "-n NAME", "--name=NAME", type: Array(String), desc: "Target name.", default: ["Taro"]
      run do |opts, args|
        print "#{opts.greeting}, "
        print "#{opts.name.join(", ")}!"
        print "\n"
      end
    end
  end
end

Hello::Cli.start(ARGV)
$ crystal build src/hello.cr
$ ./hello --help

  Hello CLI tool.

  Usage:

    hello [options] [arguments] ...

  Options:

    -g WORDS, --greeting=WORDS       Words of greetings. [type:String] [default:"Hello"]
    -n NAME, --name=NAME             Target name. [type:Array(String)] [default:["Taro"]]
    --help                           Show this help.
    --version                        Show version.

$ ./hello -n Ichiro -n Miko -g 'Good night'
Good night, Ichiro, Miko!

Sub commands sample

src/fake-crystal-command.cr

require "clim"

module FakeCrystalCommand
  class Cli < Clim
    main do
      desc "Fake Crystal command."
      usage "fcrystal [sub_command] [arguments]"
      run do |opts, args|
        puts opts.help_string # => help string.
      end
      sub "tool" do
        desc "run a tool"
        usage "fcrystal tool [tool] [arguments]"
        run do |opts, args|
          puts "Fake Crystal tool!!"
        end
        sub "format" do
          desc "format project, directories and/or files"
          usage "fcrystal tool format [options] [file or directory]"
          run do |opts, args|
            puts "Fake Crystal tool format!!"
          end
        end
      end
      sub "spec" do
        desc "build and run specs"
        usage "fcrystal spec [options] [files]"
        run do |opts, args|
          puts "Fake Crystal spec!!"
        end
      end
    end
  end
end

FakeCrystalCommand::Cli.start(ARGV)

Build and run.

$ crystal build -o ./fcrystal src/fake-crystal-command.cr
$ ./fcrystal

  Fake Crystal command.

  Usage:

    fcrystal [sub_command] [arguments]

  Options:

    --help                           Show this help.

  Sub Commands:

    tool   run a tool
    spec   build and run specs

Show sub command help.

$ ./fcrystal tool --help

  run a tool

  Usage:

    fcrystal tool [tool] [arguments]

  Options:

    --help                           Show this help.

  Sub Commands:

    format   format project, directories and/or files

Run sub sub command.

$ ./fcrystal tool format
Fake Crystal tool format!!

Usage

require & inherit

require "clim"

class MyCli < Clim

  # ...

end

Command Informations

desc

Description of the command. It is displayed in Help.

class MyCli < Clim
  main do
    desc "My Command Line Interface."
    run do |opts, args|
      # ...
    end
  end
end

usage

Usage of the command. It is displayed in Help.

class MyCli < Clim
  main do
    usage  "mycli [sub-command] [options] ..."
    run do |opts, args|
      # ...
    end
  end
end

alias_name

An alias for the command. It can be specified only for subcommand.

class MyCli < Clim
  main do
    run do |opts, args|
      # ...
    end
    sub "sub" do
      alias_name  "alias1", "alias2"
      run do |opts, args|
        puts "sub_command run!!"
      end
    end
  end
end
$ ./mycli sub
sub_command run!!
$ ./mycli alias1
sub_command run!!
$ ./mycli alias2
sub_command run!!

version

You can specify the string to be displayed with --version.

class MyCli < Clim
  main do
    version "mycli version: 1.0.1"
    run do |opts, args|
      # ...
    end
  end
end
$ ./mycli --version
mycli version: 1.0.1

If you want to display it even with -v, add short: "-v".

class MyCli < Clim
  main do
    version "mycli version: 1.0.1", short: "-v"
    run do |opts, args|
      # ...
    end
  end
end
$ ./mycli --version
mycli version: 1.0.1
$ ./mycli -v
mycli version: 1.0.1

Short option for help

The short help option is not set by default. If you want help to appear by specifying -h , specify help short: "-h" .

(However, it should not conflict with other options.)

class MyCli < Clim
  main do
    desc "help directive test."
    usage "mycli [options] [arguments]"
    help short: "-h"
    run do |opts, args|
      # ...
    end
  end
end
$ ./mycli -h

  help directive test.

  Usage:

    mycli [options] [arguments]

  Options:

    -h, --help                       Show this help.

$ ./mycli --help

  help directive test.

  Usage:

    mycli [options] [arguments]

  Options:

    -h, --help                       Show this help.

In addition to -h, you can specify any single character. For example, help short: "-a" .

option

You can specify multiple options for the command.

Argument Description Example Required Default
First argument short or long name -t TIMES, --times TIMES true -
Second argument long name --times TIMES false -
type option type type: Array(Float32) false String
desc option description desc: "option description." false "Option description."
default default value default: [1.1_f32, 2.2_f32] false nil
required required flag required: true false false
class MyCli < Clim
  main do
    option "--greeting=WORDS", desc: "Words of greetings.", default: "Hello"
    option "-n NAME", "--name=NAME", type: Array(String), desc: "Target name.", default: ["Taro"]
    run do |opts, args|
      puts typeof(opts.greeting) # => String
      puts typeof(opts.name)     # => Array(String)
    end
  end
end

The type of the option is determined by the default and required patterns.

Number

For example Int8.

default required Type
exist true Int8 (default: Your specified value.)
exist false Int8 (default: Your specified value.)
not exist true Int8
not exist false Int8 | Nil

String

default required Type
exist true String (default: Your specified value.)
exist false String (default: Your specified value.)
not exist true String
not exist false String | Nil

Bool

default required Type
exist true Bool (default: Your specified value.)
exist false Bool (default: Your specified value.)
not exist true Bool
not exist false Bool (default: false)

Array

default required Type
exist true Array(T) (default: Your specified value.)
exist false Array(T) (default: Your specified value.)
not exist true Array(T)
not exist false Array(T) (default: [] of T)

For Bool, you do not need to specify arguments for short or long.

class MyCli < Clim
  main do
    option "-v", "--verbose", type: Bool, desc: "Verbose."
    run do |opts, args|
      puts typeof(opts.verbose) # => Bool
    end
  end
end

Option method names are long name if there is a long, and short name if there is only a short. Also, hyphens are replaced by underscores.

class MyCli < Clim
  main do
    option "-n", type: String, desc: "name."  # => short name only.
    option "--my-age", type: Int32, desc: "age." # => long name only.
    run do |opts, args|
      puts typeof(opts.n)      # => (String | Nil)
      puts typeof(opts.my_age) # => (Int32 | Nil)
    end
  end
end

help_template

You can customize the help message by help_template block. It must be placed in main block. Also it needs to return String. Block arguments are desc : String, usage : String, options : HelpOptionsType and sub_commands : HelpSubCommandsType.

help_template_test.cr

require "clim"

class MyCli < Clim
  main do
    help_template do |desc, usage, options, sub_commands|
      options_help_lines = options.map do |option|
        option[:names].join(", ") + "\n" + "    #{option[:desc]}"
      end
      base = <<-BASE_HELP
      #{usage}

      #{desc}

      options:
      #{options_help_lines.join("\n")}

      BASE_HELP

      sub = <<-SUB_COMMAND_HELP

      sub commands:
      #{sub_commands.map(&.[](:help_line)).join("\n")}
      SUB_COMMAND_HELP

      sub_commands.empty? ? base : base + sub
    end
    desc "Your original command line interface tool."
    usage <<-USAGE
    usage: my_cli [--version] [--help] [-P PORT|--port=PORT]
                  [-h HOST|--host=HOST] [-p PASSWORD|--password=PASSWORD]
    USAGE
    version "version 1.0.0"
    option "-P PORT", "--port=PORT", type: Int32, desc: "Port number.", default: 3306
    option "-h HOST", "--host=HOST", type: String, desc: "Host name.", default: "localhost"
    option "-p PASSWORD", "--password=PASSWORD", type: String, desc: "Password."
    run do |opts, args|
    end
    sub "sub_command" do
      desc "my_cli's sub_comand."
      usage <<-USAGE
      usage: my_cli sub_command [--help] [-t|--tree]
                                [--html-path=PATH]
      USAGE
      option "-t", "--tree", type: Bool, desc: "Tree."
      option "--html-path=PATH", type: String, desc: "Html path."
      run do |opts, args|
      end
    end
  end
end

MyCli.start(ARGV)
$ crystal run src/help_template_test.cr -- --help
usage: my_cli [--version] [--help] [-P PORT|--port=PORT]
              [-h HOST|--host=HOST] [-p PASSWORD|--password=PASSWORD]

Your original command line interface tool.

options:
-P PORT, --port=PORT
    Port number.
-h HOST, --host=HOST
    Host name.
-p PASSWORD, --password=PASSWORD
    Password.
--help
    Show this help.
--version
    Show version.

sub commands:
    sub_command   my_cli's sub_comand.

options:

# `options` type
alias HelpOptionsType = Array(NamedTuple(
    names:     Array(String),
    type:      Int8.class | Int32.class | ... | String.class | Bool.clsss, # => Support Types
    desc:      String,
    default:   Int8 | Int32 | ... | String | Bool, # => Support Types,
    required:  Bool,
    help_line: String
))

# `options` example
[
  {
    names:     ["-g WORDS", "--greeting=WORDS"],
    type:      String,
    desc:      "Words of greetings.",
    default:   "Hello",
    required:  false,
    help_line: "    -g WORDS, --greeting=WORDS       Words of greetings. [type:String] [default:\"Hello\"]",
  },
  {
    names:     ["-n NAME"],
    type:      Array(String),
    desc:      "Target name.",
    default:   ["Taro"],
    required:  true,
    help_line: "    -n NAME                          Target name. [type:Array(String)] [default:[\"Taro\"]] [required]",
  },
  {
    names:     ["--help"],
    type:      Bool,
    desc:      "Show this help.",
    default:   false,
    required:  false,
    help_line: "    --help                           Show this help.",
  },
]

sub_commands:

# `sub_commands` type
alias HelpSubCommandsType = Array(NamedTuple(
    names:     Array(String),
    desc:      String,
    help_line: String
))

# `sub_commands` example
[
  {
    names:     ["abc", "def", "ghi"],
    desc:      "abc command.",
    help_line: "    abc, def, ghi            abc command.",
  },
  {
    names:     ["abcdef", "ghijkl", "mnopqr"],
    desc:      "abcdef command.",
    help_line: "    abcdef, ghijkl, mnopqr   abcdef command.",
  },
]

help string

class MyCli < Clim
  main do
    run do |opts, args|
      opts.help_string # => help string
    end
  end
end

Development

$ make spec

Contributing

  1. Fork it ( https://github.com/at-grandpa/clim/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

About

Slim command line interface builder for Crystal.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Crystal 99.9%
  • Makefile 0.1%