Skip to content

Commit

Permalink
Adding specs for Thor::Options.
Browse files Browse the repository at this point in the history
Also fixing a few bugs found along the way.
  • Loading branch information
nex3 committed May 22, 2008
1 parent d7f98ea commit 1ab354a
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 26 deletions.
54 changes: 28 additions & 26 deletions lib/thor/options.rb
Expand Up @@ -19,7 +19,15 @@ def initialize(args, switches)

switches = switches.map do |names, type|
type = :boolean if type == true
names = [names, names[2].chr] if names.is_a?(String)

if names.is_a?(String)
if names =~ LONG_RE
names = [names, "-" + names[2].chr]
else
names = [names]
end
end

[names, type]
end

Expand Down Expand Up @@ -63,7 +71,7 @@ def getopts
while looking_at_opt?
case pop
when SHORT_SQ_RE
push($1.split("").map {|s| s = "-#{s}"})
push(*$1.split("").map {|s| s = "-#{s}"})
next
when LONG_EQ_RE
push($1, $2)
Expand All @@ -72,23 +80,11 @@ def getopts
switch = $1
end

raise Error, "in@valid switch '#{switch}'" unless @valid.include?(switch)

# Required arguments
if @types[switch] == :required
nextval = peek

raise Error, "no value provided for required argument '#{switch}'" if nextval.nil?
raise Error, "cannot pass switch '#{nextval}' as an argument" if @valid.include?(nextval)

# If the same option appears more than once, put the values
# in array.
if hash[switch]
hash[switch] = [hash[switch], nextval].flatten
else
hash[switch] = nextval
end
pop
raise Error, "no value provided for required argument '#{switch}'" if peek.nil?
raise Error, "cannot pass switch '#{peek}' as an argument" if @valid.include?(peek)
hash[switch] = pop
end

# For boolean arguments set the switch's value to true.
Expand All @@ -107,11 +103,11 @@ def getopts
# For optional argument, there may be an argument. If so, it
# cannot be another switch. If not, it is set to true.
if @types[switch] == :optional
nextval = peek
hash[switch] = @valid.include?(peek) || pop
hash[switch] = @valid.include?(peek) || peek.nil? || pop
end
end

check_required_args hash
normalize_hash hash
end

Expand All @@ -123,7 +119,7 @@ def peek

def pop
arg = peek
@args = @args[1..-1]
@args = @args[1..-1] || []
arg
end

Expand All @@ -140,13 +136,19 @@ def looking_at_opt?
end
end

def check_required_args(hash)
@types.select {|k,v| v == :required}.map {|k,v| @syns[k]}.uniq.each do |syns|
raise Error, "no value provided for required argument '#{syns.first}'" unless syns.any? {|s| hash[s]}
end
end

# Set synonymous switches to the same value, e.g. if -t is a synonym
# for --test, and the user passes "--test", then set "-t" to the same
# value that "--test" was set to.
#
# This allows users to refer to the long or short switch and get
# the same value
def normalize_hash(hash)
# Set synonymous switches to the same value, e.g. if -t is a synonym
# for --test, and the user passes "--test", then set "-t" to the same
# value that "--test" was set to.
#
# This allows users to refer to the long or short switch and get
# the same value
hash.map do |switch, val|
@syns[switch].map {|key| [key, val]}
end.inject([]) {|a, v| a + v}.map do |key, value|
Expand Down
185 changes: 185 additions & 0 deletions spec/options_spec.rb
@@ -0,0 +1,185 @@
require File.dirname(__FILE__) + '/spec_helper'
require "thor/options"

describe Thor::Options do
it "automatically aliases long switches with their first letter" do
Thor::Options.new(["--foo"], {"--foo" => true}).getopts["f"].must be_true
Thor::Options.new(["-f"], {"--foo" => true}).getopts["foo"].must be_true
end

it "doesn't alias switches that have multiple names given" do
Thor::Options.new(["--foo"], {["--foo", "--bar"] => true}).getopts["f"].must_not be
Thor::Options.new(["-f"], {["--foo", "--bar"] => true}).getopts["foo"].must_not be
end

it "allows multiple aliases for a given switch" do
Thor::Options.new(["--bar", "12"], {["--foo", "--bar", "--baz"] => :optional}).getopts.must ==
{"foo" => "12", "bar" => "12", "baz" => "12"}
end

it "allows custom short names" do
Thor::Options.new(["-f", "12"], {"-f" => :optional}).getopts.must == {"f" => "12"}
end

it "allows custom short-name aliases" do
Thor::Options.new(["-f", "12"], {["--bar", "-f"] => :optional}).getopts.must == {"bar" => "12", "f" => "12"}
end

it "accepts =-format switch assignment" do
Thor::Options.new(["--foo=12"], {"--foo" => :required}).getopts["foo"].must == "12"
end

it "accepts conjoined short switches" do
opts = Thor::Options.new(["-fba"], {"--foo" => true, "--bar" => true, "--app" => true}).getopts
opts["foo"].must be_true
opts["bar"].must be_true
opts["app"].must be_true
end

it "accepts conjoined short switches with arguments" do
opts = Thor::Options.new(["-fba", "12"], {"--foo" => true, "--bar" => true, "--app" => :required}).getopts
opts["foo"].must be_true
opts["bar"].must be_true
opts["app"].must == "12"
end

describe " with no arguments" do
describe " and no switches" do
before :each do
@options = Thor::Options.new([], {})
end

it "returns an empty array for #skip_non_opts" do
@options.skip_non_opts.must == []
end

it "returns an empty hash for #getopts" do
@options.getopts.must == {}
end
end

describe " and several switches" do
before :each do
@options = Thor::Options.new([], {"--foo" => true, "--bar" => :optional})
end

it "returns an empty array for #skip_non_opts" do
@options.skip_non_opts.must == []
end

it "returns an empty hash for #getopts" do
@options.getopts.must == {}
end
end

describe " and a required switch" do
before :each do
@options = Thor::Options.new([], {"--foo" => :required})
end

it "raises an error for #getopts" do
lambda { @options.getopts }.must raise_error(Thor::Options::Error, "no value provided for required argument '--foo'")
end
end
end

describe " with several boolean switches" do
before :each do
@switches = {"--foo" => true, "--bar" => :boolean}
end

it "sets existant switches to true" do
Thor::Options.new(["--foo"], @switches).getopts["foo"].must be_true
Thor::Options.new(["--bar"], @switches).getopts["bar"].must be_true
opts = Thor::Options.new(["--foo", "--bar"], @switches).getopts
opts["foo"].must be_true
opts["bar"].must be_true
end

it "doesn't set nonexistant switches" do
Thor::Options.new(["--foo"], @switches).getopts["bar"].must_not be
Thor::Options.new(["--bar"], @switches).getopts["foo"].must_not be
opts = Thor::Options.new([], @switches).getopts
opts["foo"].must_not be
opts["bar"].must_not be
end
end

describe " with several optional switches" do
before :each do
@switches = {"--foo" => :optional, "--bar" => :optional}
end

it "sets switches without arguments to true" do
Thor::Options.new(["--foo"], @switches).getopts["foo"].must be_true
Thor::Options.new(["--bar"], @switches).getopts["bar"].must be_true
end

it "doesn't set nonexistant switches" do
Thor::Options.new(["--foo"], @switches).getopts["bar"].must_not be
Thor::Options.new(["--bar"], @switches).getopts["foo"].must_not be
end

it "sets switches with arguments to their arguments" do
Thor::Options.new(["--foo", "12"], @switches).getopts["foo"].must == "12"
Thor::Options.new(["--bar", "12"], @switches).getopts["bar"].must == "12"
end

it "assumes something that could be either a switch or an argument is a switch" do
Thor::Options.new(["--foo", "--bar"], @switches).getopts["foo"].must be_true
end

it "overwrites earlier values with later values" do
Thor::Options.new(["--foo", "--foo", "12"], @switches).getopts["foo"].must == "12"
Thor::Options.new(["--foo", "12", "--foo", "13"], @switches).getopts["foo"].must == "13"
end
end

describe " with one required and one optional switch" do
before :each do
@switches = {"--foo" => :required, "--bar" => :optional}
end

it "raises an error if the required switch has no argument" do
lambda { Thor::Options.new(["--foo"], @switches).getopts }.must raise_error(Thor::Options::Error, "no value provided for required argument '--foo'")
end

it "raises an error if the required switch isn't given" do
lambda { Thor::Options.new(["--bar"], @switches).getopts }.must raise_error(Thor::Options::Error, "no value provided for required argument '--foo'")
end

it "raises an error if a switch name is given as the argument to the required switch" do
lambda { Thor::Options.new(["--foo", "--bar"], @switches).getopts }.must raise_error(Thor::Options::Error, "cannot pass switch '--bar' as an argument")
end

it "sets the required switch to its argument" do
Thor::Options.new(["--foo", "12"], @switches).getopts["foo"].must == "12"
end

it "overwrites earlier values with later values" do
Thor::Options.new(["--foo", "12", "--foo", "13"], @switches).getopts["foo"].must == "13"
end
end

describe " with several non-switch arguments" do
before :each do
@options = Thor::Options.new(["foo", "bar", "--baz", "--foo", "12", "--bar", "-T", "bang"],
"--foo" => :required, "--bar" => true)
end

it "returns the initial non-option arguments for #skip_non_opts" do
@options.skip_non_opts.must == ["foo", "bar", "--baz"]
end

it "parses the options for #getopts after #skip_non_opts" do
@options.skip_non_opts
@options.getopts.must == {"foo" => "12", "f" => "12", "bar" => true, "b" => true}
end

it "returns the remaining non-option arguments for #args after #skip_non_opts and #getopts" do
@options.skip_non_opts
@options.getopts
@options.args == ["-T", "bang"]
end
end
end

0 comments on commit 1ab354a

Please sign in to comment.