- Author
-
Dave Copeland (davetron5000 at g mail dot com)
- Copyright
-
Copyright © 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
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.
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 aHash
-
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 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.
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
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
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
Methadone uses aruba for BDD-style testing with cucumber. This library has some awesome steps, and methadone provides additional, more opinionated, steps.
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 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|
-
Run
command_to_run --help
using arubaWhen 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 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
See the roadmap