git-style-binaries

Ridiculously easy git-style binaries.

This gem uses trollop for option parsing

Installation

gem install jashmenn-git-style-binaries --source=http://gems.github.com

Screencast

Checkout the new screencast!

Try it out

cd `gem env gemdir`/gems/jashmenn-git-style-binaries-0.1.4/test/fixtures
./wordpress -h
./wordpress help post

Goal

Lets use the imaginary wordpress gem. Let's say we have three different actions we want to specify:

  • categories
  • list
  • post

Each command has its own binary in a directory structure like this:

bin/
|-- wordpress
|-- wordpress-categories
|-- wordpress-list
`-- wordpress-post

The goal is to be able to call commands in this manner:

wordpress -h          # gives help summary of all commands
wordpress-list -h     # gives long help of wordpress-list
wordpress list -h     # ditto
echo "about me" | wordpress-post --title="new post"  # posts a new post with that title

Example code

Our bin/wordpress binary is called the primary . Our primary only needs to contain the following line:

#!/usr/bin/env ruby
require 'git-style-binary/command'

git-style-binary will automatically make this command the primary.

The bin/wordpress-post binary could contain the following:

#!/usr/bin/env ruby
require 'git-style-binary/command'

GitStyleBinary.command do
  short_desc "create a blog post"
  banner <<-EOS
Usage: #{command.full_name} #{all_options_string} {content|STDIN}

Posts content to a wordpress blog

EOS
  opt :blog,     "short name of the blog to use", :default => 'default'
  opt :category, "tag/category. specify multiple times for multiple categories", :type => String, :multi => true
  opt :title,    "title for the post", :required => true, :type => String
  opt :type,     "type of the content [html|xhtml|text]", :default => 'html', :type => String

  run do |command|
    command.die :type, "type must be one of [html|xhtml|text]" unless command.opts[:type] =~ /^(x?html|text)$/i

    puts "Subcommand name:     #{command.name.inspect}"
    puts "Options:             #{command.opts.inspect}"
    puts "Remaining arguments: #{command.argv.inspect}"
  end
end

And so on with the other binaries.

Running the binaries

Now if we run wordpress -h we get the following output:

NAME
      wordpress

VERSION
      0.0.1 (c) 2009 Nate Murray - local

SYNOPSIS
      wordpress [--version] [--test-primary] [--help] [--verbose] COMMAND [ARGS]

SUBCOMMANDS
   wordpress-categories
       do something with categories

   wordpress-help      
       get help for a specific command

   wordpress-list      
       list blog postings

   wordpress-post      
       create a blog post


  See 'wordpress help COMMAND' for more information on a specific command.

OPTIONS
    -v, --verbose         
      verbose


    -t, --test-primary=<s>
      test an option on the primary


    -e, --version         
      Print version and exit


    -h, --help            
      Show this message

Default options, version string, and usage banner are automatically selected for you. The subcommands and their short descriptions are loaded automatically!

You can pass the -h flag to any one of the subcommands (with or without the connecting -) or use the built-in help subcommand for the same effect. For instance:

$ wordpress help post

NAME
      wordpress-post - create a blog post

VERSION
      0.0.1 (c) 2009 Nate Murray - local

SYNOPSIS
      wordpress-post [--type] [--version] [--test-primary] [--blog] [--help] [--verbose] [--category]
      [--title] COMMAND [ARGS] {content|STDIN} 

OPTIONS
    -v, --verbose         
      verbose


    -t, --test-primary=<s>
      test an option on the primary


    -b, --blog=<s>        
      short name of the blog to use (default: default)


    -c, --category=<s>    
      tag/category. specify multiple times for multiple
      categories


    -i, --title=<s>       
      title for the post


    -y, --type=<s>        
      type of the content [html|xhtml|text] (default: html)


    -e, --version         
      Print version and exit


    -h, --help            
      Show this message

For more examples, see the binaries in test/fixtures/.

Primary options

Often you may want the primary to have its own set of options. Simply call GitStyleBinary.primary with a block like so:

#!/usr/bin/env ruby
require 'git-style-binary/command'
GitStyleBinary.primary do
  version "#{command.full_name} 0.0.1 (c) 2009 Nate Murray - local"
  opt :test_primary, "a primary string option", :type => String

  run do |command|
    puts "Primary Options: #{command.opts.inspect}"
  end
end

Primary options are inherited by all subcommands. That means in this case all subcommands will now get the --test-primary option available to them as well as this new version string.

Option parsing

Option parsing is done by trollop. git-style-binary uses this more-or-less exactly. See the trollop documentation for information on how to setup the options and flags.

Callbacks

Callbacks are available on the primary and subcommands. Available callbacks currently are before/after_run. These execute before the run block of the command parser and take take one argument, which is the command itself

The run block

To get the 'introspection' on the individual binaries every binary is loaded on primary help. We need a way to get that information while not running every command when calling primary help. To achieve that you need to put what will be run in the run block.

run yields a Command object which contains a number of useful options such as name, full_name, opts, and argv.

  • command.opts is a hash of the options parsed
  • command.argv is an array of the remaining arguments

Features

  • automatic colorization
  • automatic paging

To Learn more

Play with the examples in the test/fixtures directory.

Credits

TODO

  • automagic tab completion - Automatic for subcommands and options for any library that uses this

Known Bugs/Problems

  • Young
  • A few places of really ugly code
  • A feeling that this could be done in 1/2 lines of code

Authors

By Nate Murray and Ari Lerner

The MIT License

Copyright (c) 2009 Nate Murray. See LICENSE for details.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.