Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add #stop_on_unknown_option!

This can be used to stop parsing of options as soon as an unknown option
or a regular argument is encountered.  It is particularly useful if you
are implementing a "shellout wrapper", like bundlers `exec` command.
  • Loading branch information...
commit 55faac974aa901ac6aa820050ecfc36079ce5794 1 parent 96a427b
Simon Hengel sol authored
14 lib/thor.rb
View
@@ -251,6 +251,14 @@ def check_unknown_options?(config) #:nodoc:
end
end
+ def stop_on_unknown_option!
+ @stop_on_unknown_option = true
+ end
+
+ def stop_on_unknown_option? #:nodoc:
+ !!@stop_on_unknown_option
+ end
+
protected
# The method responsible for dispatching given the args.
@@ -276,6 +284,12 @@ def dispatch(meth, given_args, given_opts, config) #:nodoc:
if task
args, opts = Thor::Options.split(given_args)
+ if stop_on_unknown_option? && !args.empty?
+ # given_args starts with a non-option, so we treat everything as
+ # ordinary arguments
+ args.concat opts
+ opts.clear
+ end
else
args, opts = given_args, nil
task = Thor::DynamicTask.new(meth)
10 lib/thor/base.rb
View
@@ -59,7 +59,8 @@ def initialize(args=[], options={}, config={})
# Let Thor::Options parse the options first, so it can remove
# declared options from the array. This will leave us with
# a list of arguments that weren't declared.
- opts = Thor::Options.new(parse_options, hash_options)
+ stop_on_unknown = self.class.stop_on_unknown_option?
+ opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown)
self.options = opts.parse(array_options)
# If unknown options are disallowed, make sure that none of the
@@ -143,6 +144,13 @@ def check_unknown_options?(config) #:nodoc:
!!check_unknown_options
end
+ # If true, option parsing is suspended as soon as an unknown option or a
+ # regular argument is encountered. All remaining arguments are passed to
+ # the task as regular arguments.
+ def stop_on_unknown_option? #:nodoc:
+ false
+ end
+
# If you want only strict string args (useful when cascading thor classes),
# call strict_args_position! This is disabled by default to allow dynamic
# invocations.
10 lib/thor/parser/options.rb
View
@@ -26,7 +26,11 @@ def self.to_switches(options)
end
# Takes a hash of Thor::Option and a hash with defaults.
- def initialize(hash_options={}, defaults={})
+ #
+ # If +stop_on_unknown+ is true, #parse will stop as soon as it encounters
+ # an unknown option or a regular argument.
+ def initialize(hash_options={}, defaults={}, stop_on_unknown=false)
+ @stop_on_unknown = stop_on_unknown
options = hash_options.values
super(options)
@@ -88,6 +92,10 @@ def parse(args)
switch = normalize_switch(switch)
option = switch_option(switch)
@assigns[option.human_name] = parse_peek(switch, option)
+ elsif @stop_on_unknown
+ @extra << shifted
+ @extra << shift while peek
+ break
elsif match
@extra << shifted
@extra << shift while peek && peek !~ /^-/
35 spec/parser/options_spec.rb
View
@@ -2,12 +2,12 @@
require 'thor/parser'
describe Thor::Options do
- def create(opts, defaults={})
+ def create(opts, defaults={}, stop_on_unknown=false)
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)
+ @opt = Thor::Options.new(opts, defaults, stop_on_unknown)
end
def parse(*args)
@@ -208,6 +208,37 @@ def remaining
end
end
+ context "when stop_on_unknown is true" do
+ before do
+ create({:foo => :string, :verbose => :boolean}, {}, true)
+ end
+
+ it "stops parsing on first non-option" do
+ expect(parse(%w[foo --verbose])).to eq({})
+ expect(remaining).to eq(["foo", "--verbose"])
+ end
+
+ it "stops parsing on unknown option" do
+ expect(parse(%w[--bar --verbose])).to eq({})
+ expect(remaining).to eq(["--bar", "--verbose"])
+ end
+
+ it "still accepts options that are given before non-options" do
+ expect(parse(%w[--verbose foo])).to eq({"verbose" => true})
+ expect(remaining).to eq(["foo"])
+ end
+
+ it "still accepts options that require a value" do
+ expect(parse(%w[--foo bar baz])).to eq({"foo" => "bar"})
+ expect(remaining).to eq(["baz"])
+ end
+
+ it "still interprets everything after -- as args instead of options" do
+ expect(parse(%w[-- --verbose])).to eq({})
+ expect(remaining).to eq(["--verbose"])
+ end
+ end
+
describe "with :string type" do
before do
create ["--foo", "-f"] => :required
34 spec/thor_spec.rb
View
@@ -89,6 +89,40 @@
end
end
+ describe "#stop_on_unknown_option!" do
+ my_script = Class.new(Thor) do
+ class_option "verbose", :type => :boolean
+ class_option "mode", :type => :string
+
+ stop_on_unknown_option!
+
+ desc "exec", "Run a command"
+ def exec(*args)
+ return options, args
+ end
+ end
+
+ it "passes remaining args to task when it encounters a non-option" do
+ expect(my_script.start(%w[exec command --verbose])).to eq [{}, ["command", "--verbose"]]
+ end
+
+ it "passes remaining args to task when it encounters an unknown option" do
+ expect(my_script.start(%w[exec --foo command --bar])).to eq [{}, ["--foo", "command", "--bar"]]
+ end
+
+ it "still accepts options that are given before non-options" do
+ expect(my_script.start(%w[exec --verbose command --foo])).to eq [{"verbose" => true}, ["command", "--foo"]]
+ end
+
+ it "still accepts options that require a value" do
+ expect(my_script.start(%w[exec --mode rashly command])).to eq [{"mode" => "rashly"}, ["command"]]
+ end
+
+ it "still passes everything after -- to task" do
+ expect(my_script.start(%w[exec -- --verbose])).to eq [{}, ["--verbose"]]
+ end
+ end
+
describe "#map" do
it "calls the alias of a method if one is provided" do
expect(MyScript.start(["-T", "fish"])).to eq(["fish"])
Please sign in to comment.
Something went wrong with that request. Please try again.