aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgearnode <bryan@frimin.fr>2020-09-02 16:05:29 +0200
committergearnode <bryan@frimin.fr>2020-09-02 16:05:29 +0200
commit33e1e9b07948540b45be92bf4c567d8735571d32 (patch)
tree708b60e5ca6e96ba5b537facb11b393255e8d32b
parent9416aca6eb394c01c31d51ece938ec926731a49b (diff)
update documentation
-rw-r--r--.gitignore4
-rw-r--r--README.md39
-rw-r--r--doc/handbook.md86
-rw-r--r--lib/cmdline.rb145
4 files changed, 273 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index c111b33..5a9c412 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
*.gem
+/pkg/
+/.yardoc/
+/doc/*
+!/doc/handbook.md
diff --git a/README.md b/README.md
index 87ce8f8..6627cb3 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,38 @@
-# rb-cmdline
+# Introduction
+`RbCmdline` is a simple Ruby library to built command line interface.
+
+# Motivation
+Existing Ruby library to build command line interface have complex API and
+abuse a of DSL. This library expose a simple API and do not use any DSL.
+
+# Documentation
+The [handbook](doc/handbook.md) contains information about various aspects of
+the library.
+
+You can also use the [yard](https://yardoc.org) documentation tool to read
+code documentation, for example:
+
+ yard doc
+
+# Contact
+If you find a bug or have any question, feel free to open a Github issue or to
+contact me [by email](mailto:bryan@frimin.fr).
+
+Please note that I do not currently review or accept any contribution.
+
+# License
+Released under the ISC license.
+
+Copyright (c) 2020 Bryan Frimin <bryan@frimin.fr>.
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/doc/handbook.md b/doc/handbook.md
new file mode 100644
index 0000000..e79c68c
--- /dev/null
+++ b/doc/handbook.md
@@ -0,0 +1,86 @@
+# Introduction
+This document explain how to use the `rb-cmdline` library.
+
+# Usage
+## Minimal
+```ruby
+require("rb-cmdline")
+
+cmd = Cmdline.new
+cmd.parse(Cmdline.argv)
+```
+## Options
+```ruby
+require("rb-cmdline")
+
+cmd = Cmdline.new
+cmd.add_option("a", "option-a", "value", "a simple option")
+cmd.set_option_default("a", "42")
+cmd.add_option("b", "", "value", "an short option")
+cmd.add_option("", "option-c", "value", "a long option")
+cmd.add_flag("d", "flag-d", "a simple flag")
+cmd.parse(Cmdline.argv)
+
+printf("a: %s\n", cmd.option_value("a"))
+
+if cmd.is_option_set("b")
+ printf("b: %s\n", cmd.option_value("b"))
+end
+
+if cmd.is_option_set("option-c")
+ printf("option-c: %s\n", cmd.option_value("option-c"))
+end
+
+printf("d: %s\n", cmd.is_option_set("d"))
+```
+
+## Arguments
+```ruby
+#!/usr/bin/env ruby
+
+require("rb-cmdline")
+
+cmd = Cmdline.new
+cmd.add_argument("foo", "the first argument")
+cmd.add_argument("bar", "the second argument")
+cmd.add_trailing_arguments("name", "a trailing argument")
+
+cmd.parse(Cmdline.argv)
+
+printf("foo: %s\n", cmd.argument_value("foo"))
+printf("bar: %s\n", cmd.argument_value("bar"))
+printf("names: %s\n", cmd.trailing_arguments_values("name"))
+```
+
+## Commands
+```ruby
+#!/usr/bin/env ruby
+
+require("rb-cmdline")
+
+cmd = Cmdline.new
+
+cmd.add_command("foo", "subcommand 1")
+cmd.add_command("bar", "subcommand 2")
+
+cmd.parse(Cmdline.argv)
+
+commands = {
+ "foo" => -> (args) {
+ printf("running command \"foo\" with arguments %s\n", args[1..])
+ },
+ "bar" => -> (args) {
+ cmd = CLI.new
+ cmd.add_option("n", "", "value", "an example value")
+ cmd.parse(args)
+
+ printf("running command \"bar\" with arguments %s\n", args[1..])
+
+ if cmd.is_option_set("n")
+ printf("n: %s\n", cmd.option_value("n"))
+ end
+ }
+}
+
+commands[cmd.command_name].call(cmd.command_name_and_arguments)
+```
diff --git a/lib/cmdline.rb b/lib/cmdline.rb
index 2d909a7..9bbb047 100644
--- a/lib/cmdline.rb
+++ b/lib/cmdline.rb
@@ -98,6 +98,16 @@ module Cmdline
self.program_name ||= ""
end
+ # Add flag to the command line interface.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [String] short the short name of the flag
+ # @param [String] long the long name of the flag
+ # @param [String] description the description of the flag
+ #
+ # @return [void]
def add_flag(short, long, description)
option = Option.new(
short_name: short.to_s,
@@ -109,6 +119,17 @@ module Cmdline
addopt(option)
end
+ # Add option to the command line interface.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [String] short the short name of the option
+ # @param [String] long the long name of the option
+ # @param [String] value the demo value of the option
+ # @param [String] description the description of the option
+ #
+ # @return [void]
def add_option(short, long, value, description)
option = Option.new(
short_name: short.to_s,
@@ -120,6 +141,15 @@ module Cmdline
addopt(option)
end
+ # A default value to an option.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [String] name the name of the option
+ # @param [Object] value the default value of the option
+ #
+ # @return [void]
def set_option_default(name, value)
option = self.options[name]
raise(ArgumentError, "unknown option") if option.nil?
@@ -128,6 +158,15 @@ module Cmdline
option.default = value
end
+ # Add argument to the command line interface.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [String] name the argument name
+ # @param [String] description the description of the argument
+ #
+ # @return [void]
def add_argument(name, description)
argument = Argument.new(
name: name.to_s,
@@ -137,6 +176,15 @@ module Cmdline
addarg(argument)
end
+ # Add trailing argument to the command line interface.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [String] name the name of the trailing arguments
+ # @param [String] description the description of the trailing arguments
+ #
+ # @return [void]
def add_trailing_arguments(name, description)
argument = Argument.new(
name: name.to_s,
@@ -147,6 +195,17 @@ module Cmdline
addarg(argument)
end
+ # Add command to the command line interface.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [String] name the name of the command
+ # @param [String] description the description of the command
+ #
+ # @raise [ArgumentError] when the command line has already argument
+ #
+ # @return [void]
def add_command(name, description)
if self.arguments.size.zero?
add_argument("command", "the command to execute")
@@ -162,12 +221,28 @@ module Cmdline
self.commands[cmd.name] = cmd
end
+ # Exit with error.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param (see Kernel#sprintf)
+ #
+ # @return [nil]
def die(format, *args)
msg = sprintf(format, *args)
STDERR.puts("error: #{msg}")
exit(1)
end
+ # Parse the argument values.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [Array<String>] args
+ #
+ # @return [void]
def parse(args)
die("empty argument array") if args.size == 0
@@ -249,6 +324,12 @@ module Cmdline
end
end
+ # Print the usage of the command line interface.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @return [void]
def print_usage
usage = sprintf("Usage: %s OPTIONS", self.program_name)
if self.arguments.size > 0
@@ -343,12 +424,32 @@ module Cmdline
printf(usage)
end
+ # Check if an option is set.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [String] name the name of the option
+ #
+ # @raise [ArgumentError] when the option does not exist
+ #
+ # @return [Boolean]
def is_option_set(name)
opt = self.options[name]
raise(ArgumentError, "unknown option") if opt.nil?
opt.set
end
+ # Get the value of an option.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [String] name the name of the option
+ #
+ # @raise [ArgumentError] when the option does not exist
+ #
+ # @return [String]
def option_value(name)
opt = self.options[name]
raise(ArgumentError, "unknown option") if opt.nil?
@@ -357,6 +458,16 @@ module Cmdline
opt.default
end
+ # Get the value of an argument.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [String] name the name of the argument
+ #
+ # @raise [ArgumentError] when the argument does not exist.
+ #
+ # @return [String]
def argument_value(name)
self.arguments.each do |arg|
if arg.name == name
@@ -366,6 +477,16 @@ module Cmdline
raise(ArgumentError, "unknown argument")
end
+ # Get the values of the trailing arguments.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @param [String] name the name of the trailing arguments.
+ #
+ # @raise [ArgumentError] when the trailing arguments are empty.
+ #
+ # @return [Array<String>]
def trailing_arguments_values(name)
raise(ArgumentError, "empty argument array") if self.arguments.empty?
last = self.arguments.last
@@ -373,16 +494,34 @@ module Cmdline
last.trailing_values
end
+ # Get the name of command to execute.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @return [String]
def command_name
raise(RuntimeError, "no command defined") if self.commands.empty?
self.command
end
+ # Get command arguments.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @return [Array<String>]
def command_arguments_values
raise(RuntimeError, "no command defined") if self.commands.empty?
self.command_arguments
end
+ # Get the command name and their arguments.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @return [Array<String>]
def command_name_and_arguments
raise(RuntimeError, "no command defined") if self.commands.empty?
[self.command, *self.command_arguments]
@@ -421,6 +560,12 @@ module Cmdline
end
end
+ # Create new command line interface.
+ #
+ # @author Gearnode <bryan@frimin.fr>
+ # @since 1.0.0
+ #
+ # @return [CmdLine]
def self.new
cmd = CmdLine.new
cmd.add_flag("h", "help", "print help and exit")