require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') require 'thor/parser' describe Thor::Options do def create(opts, defaults={}) opts.each do |key, value| opts[key] = Thor::Option.parse(key, value) unless value.is_a?(Thor::Option) end @opt = Thor::Options.new(opts, defaults) end def parse(*args) @opt.parse(args.flatten) end def check_unknown! @opt.check_unknown! end describe "#to_switches" do it "turns true values into a flag" do Thor::Options.to_switches(:color => true).should == "--color" end it "ignores nil" do Thor::Options.to_switches(:color => nil).should == "" end it "ignores false" do Thor::Options.to_switches(:color => false).should == "" end it "writes --name value for anything else" do Thor::Options.to_switches(:format => "specdoc").should == '--format "specdoc"' end it "joins several values" do switches = Thor::Options.to_switches(:color => true, :foo => "bar").split(' ').sort switches.should == ['"bar"', "--color", "--foo"] end it "accepts arrays" do Thor::Options.to_switches(:count => [1,2,3]).should == "--count 1 2 3" end it "accepts hashes" do Thor::Options.to_switches(:count => {:a => :b}).should == "--count a:b" end it "accepts underscored options" do Thor::Options.to_switches(:under_score_option => "foo bar").should == '--under_score_option "foo bar"' end end describe "#parse" do it "allows multiple aliases for a given switch" do create ["--foo", "--bar", "--baz"] => :string parse("--foo", "12")["foo"].should == "12" parse("--bar", "12")["foo"].should == "12" parse("--baz", "12")["foo"].should == "12" end it "allows custom short names" do create "-f" => :string parse("-f", "12").should == {"f" => "12"} end it "allows custom short-name aliases" do create ["--bar", "-f"] => :string parse("-f", "12").should == {"bar" => "12"} end it "accepts conjoined short switches" do create ["--foo", "-f"] => true, ["--bar", "-b"] => true, ["--app", "-a"] => true opts = parse("-fba") opts["foo"].should be_true opts["bar"].should be_true opts["app"].should be_true end it "accepts conjoined short switches with input" do create ["--foo", "-f"] => true, ["--bar", "-b"] => true, ["--app", "-a"] => :required opts = parse "-fba", "12" opts["foo"].should be_true opts["bar"].should be_true opts["app"].should == "12" end it "returns the default value if none is provided" do create :foo => "baz", :bar => :required parse("--bar", "boom")["foo"].should == "baz" end it "returns the default value from defaults hash to required arguments" do create Hash[:bar => :required], Hash[:bar => "baz"] parse["bar"].should == "baz" end it "gives higher priority to defaults given in the hash" do create Hash[:bar => true], Hash[:bar => false] parse["bar"].should == false end it "raises an error for unknown switches" do create :foo => "baz", :bar => :required parse("--bar", "baz", "--baz", "unknown") lambda { check_unknown! }.should raise_error(Thor::UnknownArgumentError, "Unknown switches '--baz'") end it "skips leading non-switches" do create(:foo => "baz") parse("asdf", "--foo", "bar").should == {"foo" => "bar"} end it "correctly recognizes things that look kind of like options, but aren't, as not options" do create(:foo => "baz") parse("--asdf---asdf", "baz", "--foo", "--asdf---dsf--asdf").should == {"foo" => "--asdf---dsf--asdf"} check_unknown! end it "excepts underscores in commandline args hash for boolean" do create :foo_bar => :boolean parse("--foo_bar")["foo_bar"].should == true parse("--no_foo_bar")["foo_bar"].should == false end it "excepts underscores in commandline args hash for strings" do create :foo_bar => :string, :baz_foo => :string parse("--foo_bar", "baz")["foo_bar"].should == "baz" parse("--baz_foo", "foo bar")["baz_foo"].should == "foo bar" end describe "with no input" do it "and no switches returns an empty hash" do create({}) parse.should == {} end it "and several switches returns an empty hash" do create "--foo" => :boolean, "--bar" => :string parse.should == {} end it "and a required switch raises an error" do create "--foo" => :required lambda { parse }.should raise_error(Thor::RequiredArgumentMissingError, "No value provided for required options '--foo'") end end describe "with one required and one optional switch" do before :each do create "--foo" => :required, "--bar" => :boolean end it "raises an error if the required switch has no argument" do lambda { parse("--foo") }.should raise_error(Thor::MalformattedArgumentError) end it "raises an error if the required switch isn't given" do lambda { parse("--bar") }.should raise_error(Thor::RequiredArgumentMissingError) end it "raises an error if the required switch is set to nil" do lambda { parse("--no-foo") }.should raise_error(Thor::RequiredArgumentMissingError) end it "does not raises an error if the required option has a default value" do create :foo => Thor::Option.new("foo", nil, true, :string, "baz"), :bar => :boolean lambda { parse("--bar") }.should_not raise_error end end describe "with :string type" do before(:each) do create ["--foo", "-f"] => :required end it "accepts a switch assignment" do parse("--foo", "12")["foo"].should == "12" end it "accepts a switch= assignment" do parse("-f=12")["foo"].should == "12" parse("--foo=12")["foo"].should == "12" parse("--foo=bar=baz")["foo"].should == "bar=baz" end it "must accept underscores switch=value assignment" do create :foo_bar => :required parse("--foo_bar=http://example.com/under_score/")["foo_bar"].should == "http://example.com/under_score/" end it "accepts a --no-switch format" do create "--foo" => "bar" parse("--no-foo")["foo"].should be_nil end it "does not consume an argument for --no-switch format" do create "--cheese" => :string parse('burger', '--no-cheese', 'fries')["cheese"].should be_nil end it "accepts a --switch format on non required types" do create "--foo" => :string parse("--foo")["foo"].should == "foo" end it "accepts a --switch format on non required types with default values" do create "--baz" => :string, "--foo" => "bar" parse("--baz", "bang", "--foo")["foo"].should == "bar" end it "overwrites earlier values with later values" do parse("--foo=bar", "--foo", "12")["foo"].should == "12" parse("--foo", "12", "--foo", "13")["foo"].should == "13" end end describe "with :boolean type" do before(:each) do create "--foo" => false end it "accepts --opt assignment" do parse("--foo")["foo"].should == true parse("--foo", "--bar")["foo"].should == true end it "accepts --opt=value assignment" do parse("--foo=true")["foo"].should == true parse("--foo=false")["foo"].should == false end it "accepts --[no-]opt variant, setting false for value" do parse("--no-foo")["foo"].should == false end it "accepts --[skip-]opt variant, setting false for value" do parse("--skip-foo")["foo"].should == false end it "will prefer 'no-opt' variant over inverting 'opt' if explicitly set" do create "--no-foo" => true parse("--no-foo")["no-foo"].should == true end it "will prefer 'skip-opt' variant over inverting 'opt' if explicitly set" do create "--skip-foo" => true parse("--skip-foo")["skip-foo"].should == true end it "accepts inputs in the human name format" do create :foo_bar => :boolean parse("--foo-bar")["foo_bar"].should == true parse("--no-foo-bar")["foo_bar"].should == false parse("--skip-foo-bar")["foo_bar"].should == false end end describe "with :hash type" do before(:each) do create "--attributes" => :hash end it "accepts a switch= assignment" do parse("--attributes=name:string", "age:integer")["attributes"].should == {"name" => "string", "age" => "integer"} end it "accepts a switch assignment" do parse("--attributes", "name:string", "age:integer")["attributes"].should == {"name" => "string", "age" => "integer"} end it "must not mix values with other switches" do parse("--attributes", "name:string", "age:integer", "--baz", "cool")["attributes"].should == {"name" => "string", "age" => "integer"} end end describe "with :array type" do before(:each) do create "--attributes" => :array end it "accepts a switch= assignment" do parse("--attributes=a", "b", "c")["attributes"].should == ["a", "b", "c"] end it "accepts a switch assignment" do parse("--attributes", "a", "b", "c")["attributes"].should == ["a", "b", "c"] end it "must not mix values with other switches" do parse("--attributes", "a", "b", "c", "--baz", "cool")["attributes"].should == ["a", "b", "c"] end end describe "with :numeric type" do before(:each) do create "n" => :numeric, "m" => 5 end it "accepts a -nXY assignment" do parse("-n12")["n"].should == 12 end it "converts values to numeric types" do parse("-n", "3", "-m", ".5").should == {"n" => 3, "m" => 0.5} end it "raises error when value isn't numeric" do lambda { parse("-n", "foo") }.should raise_error(Thor::MalformattedArgumentError, "Expected numeric value for '-n'; got \"foo\"") end end end end