Permalink
Browse files

Allow to check and raise error on unknown options.

  • Loading branch information...
1 parent fc8270a commit dca8f99c303d7748a8b49f2524f160c9aa8caf99 @josevalim josevalim committed Feb 16, 2010
View
@@ -2,6 +2,7 @@
* Several bug fixes
* Decoupled Thor::Group and Thor, so it's easier to vendor
+* Added check_unknown_options! in case you want error messages to be raised in valid switches.
== 0.12, released 2010-01-02
View
@@ -38,9 +38,8 @@ module Base
# config<Hash>:: Configuration for this Thor class.
#
def initialize(args=[], options={}, config={})
- Thor::Arguments.parse(self.class.arguments, args).each do |key, value|
- send("#{key}=", value)
- end
+ args = Thor::Arguments.parse(self.class.arguments, args)
+ args.each { |key, value| send("#{key}=", value) }
parse_options = self.class.class_options
@@ -52,9 +51,9 @@ def initialize(args=[], options={}, config={})
array_options, hash_options = [], options
end
- options = Thor::Options.parse(parse_options, array_options)
- self.options = Thor::CoreExt::HashWithIndifferentAccess.new(options).merge!(hash_options)
- self.options.freeze
+ opts = Thor::Options.new(parse_options, hash_options)
+ self.options = opts.parse(array_options)
+ opts.check_unknown! if self.class.check_unknown_options?
end
class << self
@@ -109,6 +108,16 @@ def attr_accessor(*) #:nodoc:
no_tasks { super }
end
+ # If you want to raise an error for unknown options, call check_unknown_options!
+ # This is disabled by default to allow dynamic invocations.
+ def check_unknown_options!
+ @check_unknown_options = true
+ end
+
+ def check_unknown_options? #:nodoc:
+ @check_unknown_options || false
+ end
+
# Adds an argument to the class and creates an attr_accessor for it.
#
# Arguments are different from options in several aspects. The first one
View
@@ -19,6 +19,9 @@ class UndefinedTaskError < Error
class InvocationError < Error
end
+ class UnknownArgumentError < Error
+ end
+
class RequiredArgumentMissingError < InvocationError
end
@@ -16,8 +16,9 @@ def self.split(args)
return arguments, args[Range.new(arguments.size, -1)]
end
- def self.parse(base, args)
- new(base).parse(args)
+ def self.parse(*args)
+ to_parse = args.pop
+ new(*args).parse(to_parse)
end
# Takes an array of Thor::Argument objects.
@@ -116,7 +117,7 @@ def parse_numeric(name)
return shift if peek.is_a?(Numeric)
unless peek =~ NUMERIC && $& == peek
- raise MalformattedArgumentError, "expected numeric value for '#{name}'; got #{peek.inspect}"
+ raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}"
end
$&.index('.') ? shift.to_f : shift.to_i
@@ -137,7 +138,7 @@ def check_requirement!
end.join("', '")
class_name = self.class.name.split('::').last.downcase
- raise RequiredArgumentMissingError, "no value provided for required #{class_name} '#{names}'"
+ raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'"
end
end
@@ -10,7 +10,6 @@ class Options < Arguments #:nodoc:
SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i
# Receives a hash and makes it switches.
- #
def self.to_switches(options)
options.map do |key, value|
case value
@@ -28,12 +27,18 @@ def self.to_switches(options)
end.join(" ")
end
- # Takes a hash of Thor::Option objects.
- #
- def initialize(options={})
- options = options.values
+ # Takes a hash of Thor::Option and a hash with defaults.
+ def initialize(hash_options={}, defaults={})
+ options = hash_options.values
super(options)
- @shorts, @switches = {}, {}
+
+ # Add defaults
+ defaults.each do |key, value|
+ @assigns[key] = value
+ @non_assigned_required.delete(hash_options[key])
+ end
+
+ @shorts, @switches, @unknown = {}, {}, []
options.each do |option|
@switches[option.switch_name] = option
@@ -61,16 +66,24 @@ def parse(args)
end
switch = normalize_switch(switch)
- next unless option = switch_option(switch)
-
+ option = switch_option(switch)
@assigns[option.human_name] = parse_peek(switch, option)
+ elsif peek =~ /^\-/
+ @unknown << shift
else
shift
end
end
check_requirement!
- @assigns
+
+ assigns = Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)
+ assigns.freeze
+ assigns
+ end
+
+ def check_unknown!
+ raise UnknownArgumentError, "Unknown switches '#{@unknown.join(', ')}'" unless @unknown.empty?
end
protected
@@ -130,7 +143,7 @@ def parse_peek(switch, option)
elsif option.string? && !option.required?
return option.human_name # Return the option name
else
- raise MalformattedArgumentError, "no value provided for option '#{switch}'"
+ raise MalformattedArgumentError, "No value provided for option '#{switch}'"
end
end
View
@@ -232,6 +232,12 @@ def hello
MyScript.start ["what", "--debug"]
}.must raise_error(Thor::UndefinedTaskError, 'Could not find task "what" in "my_script" namespace.')
end
+
+ it "checks unknown options" do
+ capture(:stderr) {
+ MyScript.start(["foo", "bar", "--force", "true", "--unknown", "baz"])
+ }.strip.must == "Unknown switches '--unknown'"
+ end
end
describe "attr_*" do
@@ -1,4 +1,6 @@
class MyScript < Thor
+ check_unknown_options!
+
attr_accessor :some_attribute
attr_writer :another_attribute
attr_reader :another_attribute
@@ -42,7 +42,7 @@ def parse(*args)
it "and required arguments raises an error" do
create :string => nil, :numeric => nil
- lambda { parse }.must raise_error(Thor::RequiredArgumentMissingError, "no value provided for required arguments 'string', 'numeric'")
+ lambda { parse }.must raise_error(Thor::RequiredArgumentMissingError, "No value provided for required arguments 'string', 'numeric'")
end
it "and default arguments returns default values" do
@@ -35,16 +35,6 @@ def option(name, description=nil, required=false, type=nil, default=nil, banner=
end
end
- describe "equals to :optional" do
- it "has type equals to :boolean" do
- capture(:stderr){ parse(:foo, :optional).type.must == :boolean }
- end
-
- it "has no default value" do
- capture(:stderr){ parse(:foo, :optional).default.must be_nil }
- end
- end
-
describe "and symbol is not a reserved key" do
it "has type equals to :string" do
parse(:foo, :bar).type.must == :string
@@ -2,18 +2,22 @@
require 'thor/parser'
describe Thor::Options do
- def create(opts)
+ 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)
+ @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).must == "--color"
@@ -81,7 +85,18 @@ def parse(*args)
it "returns the default value if none is provided" do
create :foo => "baz", :bar => :required
- parse("--bar=boom")["foo"].must == "baz"
+ parse("--bar", "boom")["foo"].must == "baz"
+ end
+
+ it "returns the default value from defaults hash to required arguments" do
+ create Hash[:bar => :required], Hash[:bar => "baz"]
+ parse["bar"].must == "baz"
+ end
+
+ it "raises an error for unknown switches" do
+ create :foo => "baz", :bar => :required
+ parse("--bar", "baz", "--baz", "unknown")
+ lambda { check_unknown! }.must raise_error(Thor::UnknownArgumentError, "Unknown switches '--baz'")
end
describe "with no input" do
@@ -97,7 +112,7 @@ def parse(*args)
it "and a required switch raises an error" do
create "--foo" => :required
- lambda { parse }.must raise_error(Thor::RequiredArgumentMissingError, "no value provided for required options '--foo'")
+ lambda { parse }.must raise_error(Thor::RequiredArgumentMissingError, "No value provided for required options '--foo'")
end
end
@@ -247,7 +262,7 @@ def parse(*args)
it "raises error when value isn't numeric" do
lambda { parse("-n", "foo") }.must raise_error(Thor::MalformattedArgumentError,
- "expected numeric value for '-n'; got \"foo\"")
+ "Expected numeric value for '-n'; got \"foo\"")
end
end

0 comments on commit dca8f99

Please sign in to comment.