Module: Methadone::Main
- Includes:
- ARGVParser, ExitNow
- Defined in:
- lib/methadone/main.rb
Overview
Include this module to gain access to the “canonical command-line app structure” DSL. This is a very lightweight layer on top of what you might normally write that gives you just a bit of help to keep your code structured in a sensible way. You can use as much or as little as you want, though you must at least use #main to get any benefits.
Further, you must provide access to a logger via a method named #logger. If you include Methadone::CLILogging, this will be done for you
You also get a more expedient interface to OptionParser as well as checking for required arguments to your app. For example, if we want our app to accept a negatable switch named “switch”, a flag named “flag”, and two arguments “needed” (which is required) and “maybe” which is optional, we can do the following:
#!/usr/bin/env ruby
require 'methadone'
class App
include Methadone::Main
include Methadone::CLILogging
main do |needed, maybe|
options[:switch] => true or false, based on command line
options[:flag] => value of flag passed on command line
end
# Proxy to an OptionParser instance's on method
on("--[no]-switch")
on("--flag VALUE")
arg :needed
arg :maybe, :optional
defaults_from_env_var SOME_VAR
defaults_from_config_file '.my_app.rc'
go!
end
Our app then acts as follows:
$ our_app
# => parse error: 'needed' is required
$ our_app foo
# => succeeds; "maybe" in main is nil
$ our_app --flag foo
# => options[:flag] has the value "foo"
$ SOME_VAR='--flag foo' our_app
# => options[:flag] has the value "foo"
$ SOME_VAR='--flag foo' our_app --flag bar
# => options[:flag] has the value "bar"
Note that we’ve done all of this inside a class that we called App
. This isn’t strictly necessary, and you can just include
Methadone::Main and Methadone::CLILogging at the root of your bin
file if you like. This is somewhat unsafe, because self
inside the bin
file is Object, and any methods you create (or cause to be created via include
) will be present on every object. This can cause odd problems, so it’s recommended that you not do this.
Class Method Summary collapse
Instance Method Summary collapse
-
#arg(arg_name, *options) ⇒ Object
Sets the name of an arguments your app accepts.
-
#defaults_from_config_file(filename, options = {}) ⇒ Object
Set the path to the file where defaults can be configured.
-
#defaults_from_env_var(env_var) ⇒ Object
Set the name of the environment variable where users can place default options for your app.
-
#description(desc) ⇒ Object
Set the description of your app for inclusion in the help output.
-
#go! ⇒ Object
Start your command-line app, exiting appropriately when complete.
-
#leak_exceptions(leak) ⇒ Object
Configure the auto-handling of StandardError exceptions caught from calling go!.
-
#main(&block) ⇒ Object
Declare the main method for your app.
-
#on(*args, &block) ⇒ Object
Calls the
on
method of #opts with the given arguments (see RDoc for #opts for the additional help provided). -
#options ⇒ Object
Returns a Hash that you can use to store or retrieve options parsed from the command line.
-
#opts ⇒ Object
Returns an OptionParser that you can use to declare your command-line interface.
-
#version(version, version_options = {}) ⇒ Object
Set the version of your app so it appears in the banner.
Methods included from ExitNow
Class Method Details
.included(k) ⇒ Object
77 78 79 |
# File 'lib/methadone/main.rb', line 77 def self.included(k) k.extend(self) end |
Instance Method Details
#arg(arg_name, *options) ⇒ Object
Sets the name of an arguments your app accepts. Note that no sanity checking is done on the configuration of your arguments you create via multiple calls to this method. Namely, the last argument should be the only one that is a :many or a :any, but the system here won’t sanity check that.
arg_name
-
name of the argument to appear in documentation This will be converted into a String and used to create the banner (unless you have overridden the banner)
options
-
list (not Hash) of options:
:required
-
this arg is required (this is the default)
:optional
-
this arg is optional
:one
-
only one of this arg should be supplied (default)
:many
-
many of this arg may be supplied, but at least one is required
:any
-
any number, include zero, may be supplied
- A string
-
if present, this will be documentation for the argument and appear in the help
245 246 247 |
# File 'lib/methadone/main.rb', line 245 def arg(arg_name,*) opts.arg(arg_name,*) end |
#defaults_from_config_file(filename, options = {}) ⇒ Object
Set the path to the file where defaults can be configured.
The format of this file can be either a simple string of options, like what goes in the environment variable (see #defaults_from_env_var), or YAML, in which case it should be a hash where keys are the option names, and values their defaults.
Relative paths will be expanded relative to the user’s home directory.
- filename
-
path to the file. If relative, will look in user’s HOME directory. If absolute, this is the absolute path to where the file should be.
146 147 148 |
# File 'lib/methadone/main.rb', line 146 def defaults_from_config_file(filename,={}) @rc_file = File.(filename, ENV['HOME']) end |
#defaults_from_env_var(env_var) ⇒ Object
Set the name of the environment variable where users can place default options for your app. Omit this to disable the feature.
132 133 134 |
# File 'lib/methadone/main.rb', line 132 def defaults_from_env_var(env_var) @env_var = env_var end |
#description(desc) ⇒ Object
Set the description of your app for inclusion in the help output.
desc
-
a short, one-line description of your app
251 252 253 |
# File 'lib/methadone/main.rb', line 251 def description(desc) opts.description(desc) end |
#go! ⇒ Object
Start your command-line app, exiting appropriately when complete.
This will exit your program when it completes. If your #main block evaluates to an integer, that value will be sent to Kernel#exit, otherwise, this will exit with 0
If the command-line options couldn’t be parsed, this will exit with 64 and whatever message OptionParser provided.
If a required argument (see #arg) is not found, this exits with 64 and a message about that missing argument.
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/methadone/main.rb', line 162 def go! setup_defaults opts.post_setup opts.parse! opts.check_args! result = call_main if result.kind_of? Integer exit result else exit 0 end rescue OptionParser::ParseError => ex logger.error ex. puts puts opts.help exit 64 # Linux standard for bad command line end |
#leak_exceptions(leak) ⇒ Object
Configure the auto-handling of StandardError exceptions caught from calling go!.
- leak
-
if true, go! will not catch StandardError exceptions, but instead allow them to bubble up. If false, they will be caught and handled as normal. This does not affect Methadone::Error exceptions; those will NOT leak through.
126 127 128 |
# File 'lib/methadone/main.rb', line 126 def leak_exceptions(leak) @leak_exceptions = leak end |
#main(&block) ⇒ Object
Declare the main method for your app. This allows you to specify the general logic of your app at the top of your bin file, but can rely on any methods or other code that you define later.
For example, suppose you want to process a set of files, but wish to determine that list from another method to keep your code clean.
#!/usr/bin/env ruby -w
require 'methadone'
include Methadone::Main
main do
files_to_process.each do |file|
# process file
end
end
def files_to_process
# return list of files
end
go!
The block can accept any parameters, and unparsed arguments from the command line will be passed.
Note: #go! will modify ARGV
so any unparsed arguments that you do not declare as arguments to #main will essentially be unavailable. I consider this a bug, and it should be changed/fixed in a future version.
To run this method, call #go!
116 117 118 |
# File 'lib/methadone/main.rb', line 116 def main(&block) @main_block = block end |
#on(*args, &block) ⇒ Object
Calls the on
method of #opts with the given arguments (see RDoc for #opts for the additional help provided).
225 226 227 |
# File 'lib/methadone/main.rb', line 225 def on(*args,&block) opts.on(*args,&block) end |
#options ⇒ Object
Returns a Hash that you can use to store or retrieve options parsed from the command line. When you put values in here, if you do so before you’ve declared your command-line interface via #on, the value will be used in the docstring to indicate it is the default. You can use either a String or a Symbol and, after #go! is called and the command-line is parsed, the values will be available as both a String and a Symbol.
Example
main do
puts [:foo] # put the value of --foo that the user provided
end
[:foo] = "bar" # set "bar" as the default value for --foo, which
# will cause us to include "(default: bar)" in the
# docstring
on("--foo FOO","Sets the foo")
go!
276 277 278 |
# File 'lib/methadone/main.rb', line 276 def @options ||= {} end |
#opts ⇒ Object
Returns an OptionParser that you can use to declare your command-line interface. Generally, you won’t use this and will use #on directly, but this allows you to have complete control of option parsing.
The object returned has an additional feature that implements typical use of OptionParser.
opts.on("--flag VALUE")
Does this under the covers:
opts.on("--flag VALUE") do |value|
[:flag] = value
end
Since, most of the time, this is all you want to do, this makes it more expedient to do so. The key that is is set in #options will be a symbol and string of the option name, without the leading dashes. Note that if you use multiple option names, a key will be generated for each. Further, if you use the negatable form, only the positive key will be set, e.g. for --[no-]verbose
, only :verbose
will be set (to true or false).
As an example, this declaration:
opts.on("-f VALUE", "--flag")
And this command-line invocation:
$ my_app -f foo
Will result in all of these forms returning the String “foo”:
-
options['f']
-
options[:f]
-
options['flag']
-
options[:flag]
Further, any one of those keys can be used to determine the default value for the option.
219 220 221 |
# File 'lib/methadone/main.rb', line 219 def opts @option_parser ||= OptionParserProxy.new(OptionParser.new,) end |
#version(version, version_options = {}) ⇒ Object
Set the version of your app so it appears in the banner. This also adds –version as an option to your app which, when used, will act just like –help (see version_options to control this)
- version
-
the current version of your app. Should almost always be YourApp::VERSION, where the module YourApp should’ve been generated by the bootstrap script
- version_options
-
controls how the version option behaves. If this is a string, then the string will be used as documentation for the –version flag. If a Hash, more configuration is available:
- custom_docs
-
the string to document the –version flag if you don’t like the default
- compact
-
if true, –version will just show the app name and version - no help
- format
-
if provided, this can give limited control over the format of the compact version string. It should be a printf-style string and will be given two options: the first is the CLI app name, and the second is the version string
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/methadone/main.rb', line 295 def version(version,={}) opts.version(version) if .kind_of?(String) = { :custom_docs => } end [:custom_docs] ||= "Show help/version info" [:format] ||= "%s version %s" opts.on("--version",[:custom_docs]) do if [:compact] puts [:format] % [::File.basename($0),version] else puts opts.to_s end exit 0 end end |