cli~amberframework

Yet another Crystal library for building command-line interface applications.
0.1.9 released

Crystal CLI

Yet another Crystal library for building command-line interface applications.

Build Status

Installation

Add this to your application's shard.yml:

dependencies:
  cli:
    github: mosop/cli

Features

Option Parser (Integrated with optarg)

class Command < Cli::Command
  class Options
    string "--hello"
  end

  def run
    puts "Hello, #{options.hello}!"
  end
end

Command.run %w(--hello world) # prints "Hello, world!"

Access to Options

class Command < Cli::Command
  class Options
    string "--option"
  end

  def run
    puts "#{options.option} #{args[0]} #{unparsed_args[0]}"
  end
end

Command.run %w(--option foo bar -- baz) # prints "foo bar baz"

Access from Options

class Command < Cli::Command
  class Options
    on("--go") { command.go(with: "the Wind") }
  end

  def go(with some)
    puts "Gone with #{some}"
    exit
  end
end

Command.run %w(--go) # prints "Gone with the Wind"

Exit

class Command < Cli::Command
  class Options
    on("--exit") { command.exit! }
    on("--abort") { command.error! }
  end
end

Command.run %w(--exit) # => 0
Command.run %w(--abort) # => 1

For more detail, see Handling Exit.

Subcommand

class Polygon < Cli::Supercommand
  command "triangle", default: true
  command "square"
  command "hexagon"

  module Commands
    class Triangle < Cli::Command
      def run
        puts 3
      end
    end

    class Square < Cli::Command
      def run
        puts 4
      end
    end

    class Hexagon < Cli::Command
      def run
        puts 6
      end
    end
  end
end

Polygon.run %w(triangle) # prints "3"
Polygon.run %w(square)   # prints "4"
Polygon.run %w(hexagon)  # prints "6"
Polygon.run %w()         # prints "3"

Aliasing

class Command < Cli::Supercommand
  command "loooooooooong"
  command "l", aliased: "loooooooooong"

  module Commands
    class Loooooooooong < Cli::Command
      def run
        sleep 1000
      end
    end
  end
end

Command.run %w(l) # sleeps

Inheritance

class Role < Cli::Command
  class Options
    string "--name"
  end
end

class Chase < Cli::Supercommand
  command "mouse"
  command "cat"

  module Commands
    class Mouse < Role
      def run
        puts "#{options.name} runs away."
      end
    end

    class Cat < Role
      def run
        puts "#{options.name} runs into a wall."
      end
    end
  end
end

Chase.run %w(mouse --name Jerry) # prints "Jerry runs away."
Chase.run %w(cat --name Tom)     # prints "Tom runs into a wall."

Help

class Lang < Cli::Command
  class Help
    title "#{global_name} [OPTIONS] #{argument_names}"
    header "Converts a language to other languages."
    footer "(C) 20XX mosop"
  end

  class Options
    arg "from", desc: "source language", required: true
    array "--to", var: "LANG", desc: "target language", default: %w(ruby crystal), min: 1
    string "--indent", var: "NUM", desc: "set number of tab size", default: "2"
    bool "--std", not: "--Std", desc: "use standard library", default: true
    on("--help", desc: "show this help") { command.help! }
  end
end

Lang.run %w(--help)
# lang [OPTIONS] FROM
#
# Converts a language to other languages.
#
# Arguments:
#   FROM (required)  source language
#
# Options:
#   --indent NUM            set number of tab size
#                           (default: 2)
#   --std                   use standard library
#                           (enabled as default)
#   --Std                   disable --std
#   --to LANG (at least 1)  target language
#                           (default: ruby, crystal)
#   --help                  show this help
#
# (C) 20XX mosop

Help for Subcommands

class Package < Cli::Supercommand
  command "install", default: true
  command "update"
  command "remove"
  command "uninstall", aliased: "remove"

  class Help
    title "#{global_name} [SUBCOMMAND] | [OPTIONS]"
  end

  class Options
    on("--help", desc: "show this help") { command.help! }
  end

  class Base < Cli::Command
    class Help
      title "#{global_name} [OPTIONS] #{argument_names}"
    end

    class Options
      arg "package_name", desc: "specify package's name"
      on("--help", desc: "show this help") { command.help! }
    end
  end

  module Commands
    class Install < Base
      class Help
        caption "install package"
      end

      class Options
        string "-v", var: "VERSION", desc: "specify package's version"
      end
    end

    class Update < Base
      class Help
        caption "update package"
      end

      class Options
        bool "--break", desc: "update major version if any"
      end
    end

    class Remove < Base
      class Help
        caption "remove package"
      end

      class Options
        bool "-f", desc: "force to remove"
      end
    end
  end
end

Package.run %w(--help)
# package [SUBCOMMAND] | [OPTIONS]
#
# Subcommands:
#   install (default)  install package
#   remove             remove package
#   uninstall          alias for remove
#   update             update package
#
# Options:
#   --help  show this help

Package.run %w(install --help)
# package install [OPTIONS] PACKAGE_NAME
#
# Arguments:
#   PACKAGE_NAME  specify package's name
#
# Options:
#   -v VERSION  specify package's version
#   --help      show this help
end

Package.run %w(update --help)
# package update [OPTIONS] PACKAGE_NAME
#
# Arguments:
#   PACKAGE_NAME  specify package's name
#
# Options:
#   --major  update major version if any
#   --help   show this help
end

Package.run %w(remove --help)
# package remove [OPTIONS] PACKAGE_NAME
#
# Arguments:
#   PACKAGE_NAME  specify package's name
#
# Options:
#   -f      force to remove
#   --help  show this help

Package.run %w(uninstall --help)
# package remove [OPTIONS] PACKAGE_NAME
#
# Arguments:
#   PACKAGE_NAME  specify package's name
#
# Options:
#   -f      force to remove
#   --help  show this help

Usage

require "cli"

and see Features.

Fundamentals

Crystal CLI provides 4 fundamental classes, Command, Supercommand, Options and Help.

Both Command and Supercommand inherit the CommandBase class that has several features commonly used.

Once you make a class inherit Command or Supercommand, then Options and Help is automatically defined into the class.

class AncientCommand < Cli::Command
end

This code seems that it simply defines the AncientCommand class. But, actually, it also makes AncientCommand::Options and AncientCommand::Help defined internally.

Parsing Options

The Options class is used for parsing command-line options. Options inherits the Optarg::Model class provided from the optarg library. So you can define options in it.

class AncientCommand < Cli::Command
  class Options
    string "--message"
  end
end

Running a Command

The virtual CommandBase#run method is the entry point for running your command.

Your command's class will be instantiated and its #run method will be invoked after calling the static .run method.

class AncientCommand < Cli::Command
  def run
    puts "We the Earth"
  end
end

AncientCommand.run

This prints as:

We the Earth

A command's instance is also accessible with the command method in an option parser's scope.

class AncientCommand < Cli::Command
  class Options
    on("--understand") { command.understand }
  end

  def understand
    puts "We know"
  end

  def run
    puts "We the Earth"
  end
end

AncientCommand.run %w(--understand)

This prints as:

We know
We the Earth

Handling Exit

When a command normally ends, it returns 0.

class Command < Cli::Command
  def run
  end
end

Command.run # => 0

When you want to abort your command, you may raise an exception:

class Command < Cli::Command
  def run
    raise "ERROR!"
  end
end

Command.run # => raises error

Or, instead, you can have more control of exit with one of the 3 methods: help!, exit! and error!.

help!

class Command < Cli::Command
  def run
    help!
  end
end

Command.run # => 0

This command just ends after printing its help message. Command.run returns 0 as exit code.

To print message to STDERR and exit with an error code, use error option.

help! error: true

If the :error option is true, run method returns 1 as exit code. To specify a number, use the :code option.

help! code: 22

You can also let it exit with an additional message:

help! message: "You passed an illegal option! See help!"

Or simply:

help! "You passed an illegal option! See help!"

Calling help! with the :message argument implies that the :error option is true. To exit normally, set false to :error.

exit! and error!

exit! is more general than help!.

class Command < Cli::Command
  def run
    exit!
  end
end

Command.run # => 0

It just ends and returns 0 as exit code without printing.

To print a message:

exit! "bye."

Or more variations:

exit! help: true # same as help!
exit! error: true # returns 1 as exit code
exit! "message", error: true, help: true # same as help!("message")

error! is similar to exit!, but the :error option is true as default.

error! # ends with 1 as exit code
error! "message" # same as exit!("message", error: true)
error! code: 22 # specifies exit code
error! help: true # same as help!(error: true)
error! "message", help: true # same as help!("message")

Formatting Help [WIP]

To format help texts, use the Help class.

class Ancient < Cli::Command
  class Help
    title global_name
    footer "(C) 1977 mosop"
  end

  def run
    help!
  end
end

Wish List

  • Application-Level Logger
  • Documentation for Formatting Help
  • Auto-Title

Releases

  • v0.1.9
    • CommandBase.run returns 0 when #run normally returns.
  • v0.1.4
    • help!, exit! and error!
  • v0.1.2
    • Array
  • v0.1.1
    • Aliasing

Development

[WIP]

Contributing

  1. Fork it ( https://github.com/mosop/cli/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

  • mosop - creator, maintainer
cli:
  github: amberframework/cli
  version: ~> 0.1.9
License MIT
Crystal none

Authors

  • mosop

Dependencies 3

  • optarg 0.2.0
    {'github' => 'mosop/optarg', 'version' => '0.2.0'}
  • stdio ~> 0.1.1
    {'github' => 'mosop/stdio', 'version' => '~> 0.1.1'}
  • string_inflection ~> 0.1.0
    {'github' => 'mosop/string_inflection', 'version' => '~> 0.1.0'}

Development Dependencies 0

Dependents 3

Last synced .
search fire star recently