Optitron strives to be simple, minimal and to do the “right thing” most of the time. The structure is easy: you have global options, a list of commands, and each of those commands takes a number of arguments and options.
To create an Optitron command line interface (CLI), start with a class you want to wire up as a CLI. In our example, we’ll use the class Runner, which we want to work as a CLI.
class Runner def start # ... starts end def stop # ... stops end def status # ... reports status end end
To make this class suitable for use as a CLI, either extend Optitron::CLI
or include Optitron::ClassDsl
. Then, describe each argument as follows:
class Runner < Optitron::CLI desc "Starts the process" def start # ... starts end desc "Stops the process" def stop # ... stops end desc "Report the status" def status # ... reports status end end
Help is added by default, but, if you don’t want to use this, include the command dont_use_help
in your class. Help is available from either --help
or -?
.
Only methods described will be available to the CLI. If you have a method that needs arguments, simply include them as method arguments. It will respect splats and defaults. For instance
desc "Starts the process" def start(mode) # ... starts end
Will generate
start [mode] # Starts the process
In your help file.
desc "Starts the process" def start(mode = 'production') # ... starts end
Will generate
start <mode="production"> # Starts the process
And
desc "Starts the process" def start(*modes) # ... starts end
Will generate
start <modes1 modes2 ...> # Starts the process
As well, you can provide type hints to the args:
desc "Does something awesome" arg_types :numeric, :string, :boolean def start(number, string, truth) # ... does something end
Will generate
start [number] [string] [truth] # Does something awesome
And those arguments have to conform to the types given. For instance, calling this with
start 123 something flase
Will produce the error Truth is invalid
. Calling with
start 123 something true
however, will correctly invoke the method.
These same arg type hints can be provided by ending your arguments in _type. The accepted types are: float, string, int, numeric, array, hash. For example
desc "Does something awesome" def start(number_int, something_string, opt_boolean) # ... does something end
Options are specified with the opt
method in your class, as follows:
class Runner < Optitron::CLI desc "Starts the process" opt "verbose" opt "environment", :in => ['development', 'production', 'staging', 'test'] def start # ... starts end # .. more methods end
If you need an option available for all methods, use class_opt
to specify it.
class Runner < Optitron::CLI class_opt "verbose", "Be loud" # ... your methods end
The last line in your runner has to be the class name and dispatch. This will parse ARGV
and execute normal dispatching on it.
class Runner < Optitron::CLI # ... your methods end Runner.dispatch
Options can have defaults, types and inclusion checks. Here are all the options available on an opt:
This allows you to specify a default value. The type of value is used to infer the type required for this option. This can be over-ridden with :type
.
This allows you to set a short name for the option, though, one will be assigned automatically from the short names available.
This allows you to run an arbitrary block for an option. The proc will be called with the value, and the response object.
This allows you to test for inclusion in a range or array (or anything that responds to #include?
and #first
). The first item in the object will be used to infer the type. This can be over-ridden with :type
.
This allows you to force an option to be required. False by default.
This allows you to specify the type. Acceptable options are :numeric
, :array
, :hash
, :string
or :boolean
.
With boolean type, you can additionally supply :use_no => true
to be able to use –no-option style options.
You can create parsers and parse using them.
@parser = Optitron.new { help opt 'verbose', "Be very loud" cmd "install", "This installs things" do arg "file", "The file to install" end cmd "show", "This shows things" do arg "first", "The first thing to show" arg "second", "The second optional thing to show", :required => false end cmd "kill", "This kills things" do opt "pids", "A list of pids to kill", :type => :array opt "pid", "A pid to kill", :type => :numeric opt "names", "Some sort of hash", :type => :hash end cmd "join", "This joins things" do arg "thing", "Stuff to join", :type => :greedy end }
To generate help, use the #help
method.
@parser.help
Which returns,
Commands show [first] <second> # This shows things install [file] # This installs things kill # This kills things -p/--pids=[ARRAY] # A list of pids to kill -P/--pid=[NUMERIC] # A pid to kill -n/--names=[HASH] # Some sort of hash join [thing1 thing2 ...] # This joins things Global options -v/--verbose # Be very loud
The parse method can parse a list of arguments. For example, @parser.parse(%w(-v install file))
gives back:
response = @parser.parse(%w(-v install file)) response.command => "install" response.args => ["file"] response.params => {"verbose" => true}
If you try parsing invalid parameters, you can get back friendly error messages using #error_messages
.
@parser.parse(%w()).error_messages => ["Unknown command"] @parser.parse(%w(something)).error_messages => ["Something is an unknown command"] @parser.parse(%w(install)).error_messages => ["File is required"] @parser.parse(%w(kill --pid=something)).error_messages => ["Pid is invalid"]
The response from #parse can also be used to dispatch. All that’s required is the target object be able to respond to params=
.
For example
class Runner attr_accessor :params def install(file) puts "I'm installing #{file} with #{params.inspect}" end end parser = Optitron.new { help opt 'verbose', "Be very loud" cmd "install", "This installs things" do arg "file", "The file to install" end } parser.parse(%w(install this_file -v)).dispatch(Runner.new)
Will output
I'm installing this_file with {"help"=>false, "verbose"=>true}