Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

392 lines (277 sloc) 9.064 kb

Slop

Slop is a simple option parser with an easy to remember syntax and friendly API.

Installation

Rubygems

gem install slop

GitHub

git clone git://github.com/injekt/slop.git
gem build slop.gemspec
gem install slop-<version>.gem

Usage

# parse assumes ARGV, otherwise you can pass it your own Array
opts = Slop.parse do
  on :v, :verbose, 'Enable verbose mode'       # boolean value
  on :n, :name, 'Your name', true              # compulsory argument
  on :s, :sex, 'Your sex', :optional => false  # the same thing
  on :a, :age, 'Your age', :optional => true   # optional argument
end

# if ARGV is `-v --name 'lee jarvis' -s male`
opts.verbose? #=> true
opts.name?    #=> true
opts[:name]   #=> 'lee jarvis'
opts.age?     #=> false
opts[:age]    #=> nil

You can also return your options as a Hash

opts.to_hash #=> {'name' => 'Lee Jarvis', 'verbose' => true, 'age' => nil, 'sex' => 'male'}

# Symbols
opts.to_hash(true) #=> {:name => 'Lee Jarvis', :verbose => true, :age => nil, :sex => 'male'}

If you don't like the method on (because it sounds like the option expects a block), you can use the opt or option alternatives.

on :v, :verbose
opt :v, :verbose
option :v, :verbose

If you don't like that Slop evaluates your block, or you want slop access inside of your block without referring to self, you can pass a block argument to parse.

Slop.parse do |opts|
  opts.on :v, :verbose
  opts.on :n, :name, 'Your name', true
end

If you want some pretty output for the user to see your options, you can just send the Slop object to puts or use the help method.

puts opts
puts opts.help

Will output something like

-v, --verbose      Enable verbose mode
-n, --name         Your name
-a, --age          Your age

You can also add a banner using the banner method

opts = Slop.parse
opts.banner = "Usage: foo.rb [options]"

or

opts = Slop.parse do
  banner "Usage: foo.rb [options]"
end

or

opts = Slop.new "Usage: foo.rb [options]"

Helpful Help

Long form:

Slop.parse do
  ...
  on :h, :help, 'Print this help message', :tail => true do
    puts help
    exit
  end
end

Shortcut:

Slop.parse :help => true do
  ...
end

Parsing

Slop's pretty good at parsing, let's take a look at what it'll extract for you

Slop.parse do
  on 's', 'server', true
  on 'p', 'port', true, :as => :integer
  on 'username', true, :matches => /[^a-zA-Z]+$/
  on 'password', true
end

Now throw some options at it:

-s ftp://foobar.com -p1234 --username=FooBar --password 'hello there'

Here's what we'll get back

{
    :server=>"ftp://foobar.com",
    :port=>1234,
    :username=>"FooBar",
    :password=>"hello there"
}

Callbacks

If you'd like to trigger an event when an option is used, you can pass a block to your option. Here's how:

Slop.parse do
  on :V, :version, 'Print the version' do
    puts 'Version 1.0.0'
    exit
  end
end

Now when using the --version option on the command line, the trigger will be called and its contents executed.

Yielding Non Options

If you pass a block to Slop#parse, Slop will yield non-options as they're found, just like OptionParser does it.

opts = Slop.new do
  on :n, :name, :optional => false
end

opts.parse do |arg|
  puts arg
end

# if ARGV is `foo --name Lee bar`
foo
bar

Negative Options

Slop also allows you to prefix --no- to an option which will force the option to return a false value.

opts = Slop.parse do
    on :v, :verbose, :default => true
end

# with no command line options
opts[:verbose] #=> true

# with `--no-verbose`
opts[:verbose] #=> false
opts.verbose?  #=> false

Short Switches

Want to enable multiple switches at once like rsync does? By default Slop will parse -abcd as the option a with the argument bcd, this can be disabled by passing the :multiple_switches option to a new Slop object.

opts = Slop.new(:strict, :multiple_switches) do
  on :a, 'First switch'
  on :b, 'Second switch'
  on :c, 'Third switch'
end

opts.parse

# Using `-ac`
opts[:a] #=> true
opts[:b] #=> false
opts[:c] #=> true

Lists

You can of course also parse lists into options. Here's how:

opts = Slop.parse do
  opt :people, true, :as => Array
end

# ARGV is `--people lee,john,bill`
opts[:people] #=> ['lee', 'john', 'bill']

You can also change both the split delimiter and limit

opts = Slop.parse do
  opt :people, true, :as => Array, :delimiter => ':', :limit => 2)
end

# ARGV is `--people lee:injekt:bob`
opts[:people] #=> ["lee", "injekt:bob"]

Ranges

What would Slop be if it didn't know what ranges were?

opts = Slop.parse do
  opt :r, :range, true, :as => Range
end

# ARGV is `--range 1..10` or 1-10, or 1,10 (yes Slop supports them all)
opts[:range].to_a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Ugh, Symbols

Fine, don't use them

Slop.parse do
  on :n, :name, 'Your name'
  on 'n', 'name', 'Your name'
  on '-n', '--name', 'Your name'
end

All of these options will do the same thing

Ugh, Blocks

C'mon man, this is Ruby, GTFO if you don't like blocks.

opts = Slop.new
opts.on :v, :verbose
opts.parse

Smart

Slop is pretty smart when it comes to building your options, for example if you want your option to have a flag attribute, but no --option attribute, you can do this:

on :n, "Your name"

and Slop will detect a description in place of an option, so you don't have to do this:

on :n, nil, "Your name", true

You can also try other variations:

on :name, "Your name"
on :n, :name
on :name, true

Strict Mode

Passing strict => true to Slop.parse causes it to raise a Slop::InvalidOptionError when an invalid option is found (false by default):

Slop.new(:strict => true).parse(%w/--foo/)
# => Slop::InvalidOptionError: Unknown option -- 'foo'

and it handles multiple invalid options with a sprinkling of pluralization:

Slop.new(:strict => true).parse(%w/--foo --bar -z/)
# => Slop::InvalidOptionError: Unknown options -- 'foo', 'bar', 'z'

Significantly, however, Slop will still parse the valid options:

slop = Slop.new(:strict => true, :help => true) do
  banner "Usage:\n\t./awesome_sauce [options]\n\nOptions:"
  on :n, :name, 'Your name'
end

begin
  slop.parse(%w/--foo --bar -z/)
rescue Slop::InvalidOptionError => e
  puts "\n#{e.message}\n\n"
  puts slop
  exit
end

yields:

Unknown options -- 'foo', 'bar', 'z'

Usage:
    ./awesome_sauce [options]

Options:
    -n, --name      Your name
    -h, --help      Print this help message

Commands

Slop allows you to nest more instances of Slop inside of commands. These instances will then be used to parse arguments if they're called upon.

Slop will use the first argument in the list of items passed to parse to check if it is a command.

Slop.parse ['foo', '--bar', 'baz']

Slop will look to see if the foo command exists, and if it does, it'll pass the options ['--bar', 'baz'] to the instance of Slop that belongs to foo. Here's how commands might look:

opts = Slop.new do
  command :foo do
    on :b, :bar, 'something', true
  end

  command :clean do
    on :v, :verbose, do
      puts 'Enabled verbose mode for clean'
    end
  end

  # non-command specific options
  on :v, :version do
    puts 'version 1'
  end
end
  • Run with run.rb -v
  • Output: version 1

  • Run with: run.rb clean -v

  • Output: Enabled verbose mode for clean

Woah woah, why you hating on OptionParser?

I'm not, honestly! I love OptionParser. I really do, it's a fantastic library. So why did I build Slop? Well, I find myself using OptionParser to simply gather a bunch of key/value options, usually you would do something like this:

require 'optparse'

things = {}

opt = OptionParser.new do |opt|
  opt.on('-n', '--name NAME', 'Your name') do |name|
    things[:name] = name
  end

  opt.on('-a', '--age AGE', 'Your age') do |age|
    things[:age] = age
  end

  # you get the point
end

opt.parse
things #=> { :name => 'lee', :age => 105 }

Which is all great and stuff, but it can lead to some repetition, the same thing in Slop:

require 'slop'

opts = Slop.parse do
  on :n, :name, 'Your name', true
  on :a, :age, 'Your age', true
end

opts.to_hash(true) #=> { :name => 'lee', :age => 105 }
Jump to Line
Something went wrong with that request. Please try again.