= Methadone - kick the bash habit and start your command line apps off right
Author:: Dave Copeland (mailto:davetron5000 at g mail dot com)
Copyright:: Copyright (c) 2011 by Dave Copeland
License:: Distributes under the Apache License, see LICENSE.txt in the source distro
A smattering of tools to make your command-line apps easily awesome; kick the bash habit without sacrificing any of the power.
The overall goal of this project is to allow you to write a command-line app in Ruby that is as close as possible to the expedience and concisenes of bash, but with all the power of Ruby available when you need it.
Currently, this library is under development and has the following to offer:
* Bootstrapping a new CLI app
* Lightweight DSL to structure your bin file
* Utility Classes
* Methadone::CLILogger - a logger subclass that sends messages to standard error standard out as appropriate
* Methadone::CLILogging - a module that, when included in any class, provides easy access to a shared logger
* Cucumber Steps
== Links
* {Source on Github}[http://github.com/davetron5000/methadone]
* RDoc[http://rdoc.info/github/davetron5000/methadone/master/frames]
== Bootstrapping
The +methadone+ command-line app will bootstrap a new command-line app, setting up a proper gem structure, unit tests, and cucumber-based tests with aruba:
$ methadone --help
Usage: methadone [options] app_name
--force Overwrite files if they exist
$ methadone newgem
$ cd newgem
$ rake
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
1 scenario (1 passed)
3 steps (3 passed)
$ cat features/newgem.feature
Feature: My bootstrapped app kinda works
In order to get going on coding my awesome app
I want to have aruba and cucumber setup
So I don't have to do it myself
Scenario: App just runs
When I run `newgem --help`
Then the exit status should be 0
And the output should contain:
"""
Usage: newgem [options]
"""
Basically, this sets you up with all the boilerplate that you *should* be using to write a command-line app.
== DSL for your `bin` file
A canonical `OptionParser` driven app has a few problems with it structurally that methadone can solve
* Backwards organization - main logic is at the bottom of the file, not the top
* Verbose to use +opts.on+ just to set a value in a +Hash+
* No exception handling
Methadone gives you a simple,lightweight DSL to help. It's important to note that we're taking a light touch here; this is all a thin wrapper around +OptionParser+ and you still have complete access to it if you'd like. We're basically wrapping up some canonical boilerplate into more expedient code
#!/usr/bin/env ruby
require 'optparse'
require 'methadone'
include Methadone::Main
main do |name,password|
name # => guaranteed to be non-nil
password # => nil if user omitted on command line
options[:switch] # => true if user used --switch or -s
options[:s] # => ALSO true if user used --switch or -s
options[:f] # => value of FILE if used on command-line
options[:flag] # => ALSO value of FILE if used on command-line
# If something goes wrong, you can just raise an exception
# or call exit_now! if you want to control the exit status
#
# Note that if you set DEBUG in the environment, the exception
# will leak through; this can be handy to figure out why
# your app might be failing
end
description "One line summary of your awesome app"
on("--[no-]switch","-s","Some switch")
on("-f FILE","--flag","Some flag")
on("-x FOO") do |foo|
# something more complex; this is exactly OptionParser opts.on
end
arg :name
arg :password, :optional
# If you want to avoid automatic exception catching/logging do:
#
# leak_exceptions true
#
# Note that Methadone::Error exceptions are always caught and logged
go!
+go!+ runs the block you gave to +main+, passing it the unparsed +ARGV+ as parameters to the block. It will
also parse the command-line via +OptionParser+ and do a check on the remaining arguments to see
if there's enough to satisfy the :required args you've specified.
Finally, the banner/help string will be full constructed based on the interface you've declared. If you
don't accept options, [options] won't appear in the help. The names of your arguments
will appear in proper order and :optional ones will be in square brackets. You don't have to
touch a thing.
== Utility Classes
Currently, there are classes the assist in directing output logger-style to the right place; basically ensuring that errors go to +STDERR+ and everything else goes to +STDOUT+. All of this is, of course, configurable
=== Examples
==== Using STDOUT as a log, respecting STDERR
require 'methadone'
include Methadone::CLILogging
command = "rm -rf /tmp/*"
debug("About to run #{command}") # => goes only to STDOUT, no logging format
if system(command)
info("Succesfully ran #{command}") # => goes only to STDOUT, no logging format
else
error("There was a problem running #{command}") # => goes only to STDERR, no logging format
end
==== Using a log file, but respecting STDERR
Here, since we have a logfile, that logfile gets ALL messages and they have the default logger format.
require 'methadone'
include Methadone::CLILogging
self.logger = CLILogger.new("logfile.txt")
command = "rm -rf /tmp/*"
debug("About to run #{command}") # => goes only to logfile.txt, in the logger-style format
if system(command)
info("Succesfully ran #{command}") # => goes only to logfile.txt, in the logger-style format
else
error("There was a problem running #{command}")
# => goes to logfile.txt in the logger-style format, and
# to STDERR in a plain format
end
== Cucumber Steps
Methadone uses aruba[http://www.github.com/cucumber/aruba] for BDD-style testing with cucumber. This library has some awesome steps, and methadone provides additional, more opinionated, steps.
=== Example
Here's an example from methadone's own tests:
Scenario: Help is properly documented
When I get help for "methadone"
Then the exit status should be 0
And the following options should be documented:
|--force|
And the banner should be present
And the banner should include the version
And the banner should document that this app takes options
And the banner should document that this app's arguments are:
|app_name|which is required|
|dir_name|which is optional|
=== Steps Provided
* Run command_to_run --help using aruba
When I get help for "command_to_run"
* Make sure that each option shows up in the help and has *some* sort of documentation
Then the following options should be documented:
|--force|
|-x |
* Check an individual option for documentation:
Then the option "--force" should be documented
* Checks that the help has a proper usage banner
Then the banner should be present
* Checks that the banner includes the version
Then the banner should include the version
* Checks that the usage banner indicates it takes options via [options]
Then the banner should document that this app takes options
* Do the opposite; check that you don't indicate options are accepted
Then the banner should document that this app takes no options
* Checks that the app's usage banner documents that its arguments are args
Then the banner should document that this app's arguments are "args"
* Do the opposite; check that your app doesn't take any arguments
Then the banner should document that this app takes no arguments
* Check for a usage description which occurs after the banner and a blank line
Then there should be a one-line summary of what the app does
== What might be
See {the roadmap}[https://github.com/davetron5000/methadone/wiki/Roadmap]