Skip to content

Commit 35ab20d

Browse files
committed
Add disable_required_check! and make disable the check in help
In Thor 0.19.2 it was added a new class method disable_class_options. It was added to disable the check for required options in the help command. This new method disabled the option parser for help, so it is not possible to pass option to that command anymore. That change introduced a regression and it is fixing the problem at the wrong level. What we want is to disable the required check not the option parsing. So, we reverted the disable_class_options change and added a new method disable_required_check! to disable the required check in the commands we want. Theoretically this is a breaking change but the disable_class_options introduction was also a breaking change so it is better to revert it and fix the problem at the correct place. Related with #462 and #363.
1 parent e675416 commit 35ab20d

File tree

6 files changed

+97
-15
lines changed

6 files changed

+97
-15
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
## 0.20.0
2+
* Add `disable_required_check!` to disable check for required options in some commands.
3+
It is a substitute of `disable_class_options` that was not working as intended.
4+
25
* Add `inject_into_module`.
36

47
## 0.19.4, release 2016-11-28

lib/thor.rb

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,6 @@ def method_option(name, options = {})
158158
end
159159
alias_method :option, :method_option
160160

161-
def disable_class_options
162-
@disable_class_options = true
163-
end
164-
165161
# Prints help information for the given command.
166162
#
167163
# ==== Parameters
@@ -329,12 +325,31 @@ def stop_on_unknown_option?(command) #:nodoc:
329325
command && stop_on_unknown_option.include?(command.name.to_sym)
330326
end
331327

328+
# Disable the check for required options for the given commands.
329+
# This is useful if you have a command that does not need the required options
330+
# to work, like help.
331+
#
332+
# ==== Parameters
333+
# Symbol ...:: A list of commands that should be affected.
334+
def disable_required_check!(*command_names)
335+
disable_required_check.merge(command_names)
336+
end
337+
338+
def disable_required_check?(command) #:nodoc:
339+
command && disable_required_check.include?(command.name.to_sym)
340+
end
341+
332342
protected
333343

334344
def stop_on_unknown_option #:nodoc:
335345
@stop_on_unknown_option ||= Set.new
336346
end
337347

348+
# help command has the required check disabled by default.
349+
def disable_required_check #:nodoc:
350+
@disable_required_check ||= Set.new([:help])
351+
end
352+
338353
# The method responsible for dispatching given the args.
339354
def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength
340355
meth ||= retrieve_command_name(given_args)
@@ -393,12 +408,11 @@ def create_command(meth) #:nodoc:
393408
@usage ||= nil
394409
@desc ||= nil
395410
@long_desc ||= nil
396-
@disable_class_options ||= nil
397411

398412
if @usage && @desc
399413
base_class = @hide ? Thor::HiddenCommand : Thor::Command
400-
commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options, @disable_class_options)
401-
@usage, @desc, @long_desc, @method_options, @hide, @disable_class_options = nil
414+
commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
415+
@usage, @desc, @long_desc, @method_options, @hide = nil
402416
true
403417
elsif all_commands[meth] || meth == "method_missing"
404418
true
@@ -480,7 +494,6 @@ def help(command = nil, subcommand = true); super; end
480494
map HELP_MAPPINGS => :help
481495

482496
desc "help [COMMAND]", "Describe available commands or one specific command"
483-
disable_class_options
484497
def help(command = nil, subcommand = false)
485498
if command
486499
if self.class.subcommands.include? command

lib/thor/base.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ module Base
4242
# config<Hash>:: Configuration for this Thor class.
4343
#
4444
def initialize(args = [], local_options = {}, config = {})
45-
parse_options = config[:current_command] && config[:current_command].disable_class_options ? {} : self.class.class_options
45+
parse_options = self.class.class_options
4646

4747
# The start method splits inbound arguments at the first argument
4848
# that looks like an option (starts with - or --). It then calls
@@ -65,7 +65,8 @@ def initialize(args = [], local_options = {}, config = {})
6565
# declared options from the array. This will leave us with
6666
# a list of arguments that weren't declared.
6767
stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command]
68-
opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown)
68+
disable_required_check = self.class.disable_required_check? config[:current_command]
69+
opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check)
6970
self.options = opts.parse(array_options)
7071
self.options = config[:class_options].merge(options) if config[:class_options]
7172

@@ -157,6 +158,12 @@ def stop_on_unknown_option?(command_name) #:nodoc:
157158
false
158159
end
159160

161+
# If true, option set will not suspend the execution of the command when
162+
# a required option is not provided.
163+
def disable_required_check?(command_name) #:nodoc:
164+
false
165+
end
166+
160167
# If you want only strict string args (useful when cascading thor classes),
161168
# call strict_args_position! This is disabled by default to allow dynamic
162169
# invocations.

lib/thor/command.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
class Thor
2-
class Command < Struct.new(:name, :description, :long_description, :usage, :options, :disable_class_options, :ancestor_name)
2+
class Command < Struct.new(:name, :description, :long_description, :usage, :options, :ancestor_name)
33
FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
44

5-
def initialize(name, description, long_description, usage, options = nil, disable_class_options = false)
6-
super(name.to_s, description, long_description, usage, options || {}, disable_class_options)
5+
def initialize(name, description, long_description, usage, options = nil)
6+
super(name.to_s, description, long_description, usage, options || {})
77
end
88

99
def initialize_copy(other) #:nodoc:

lib/thor/parser/options.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ def self.to_switches(options)
2929
#
3030
# If +stop_on_unknown+ is true, #parse will stop as soon as it encounters
3131
# an unknown option or a regular argument.
32-
def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false)
32+
def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false, disable_required_check = false)
3333
@stop_on_unknown = stop_on_unknown
34+
@disable_required_check = disable_required_check
3435
options = hash_options.values
3536
super(options)
3637

@@ -111,7 +112,7 @@ def parse(args) # rubocop:disable MethodLength
111112
end
112113
end
113114

114-
check_requirement!
115+
check_requirement! unless @disable_required_check
115116

116117
assigns = Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)
117118
assigns.freeze

spec/thor_spec.rb

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,64 @@ def boring(*args)
159159
end
160160
end
161161

162+
describe "#disable_required_check!" do
163+
my_script = Class.new(Thor) do
164+
class_option "foo", :required => true
165+
166+
disable_required_check! :boring
167+
168+
desc "exec", "Run a command"
169+
def exec(*args)
170+
[options, args]
171+
end
172+
173+
desc "boring", "An ordinary command"
174+
def boring(*args)
175+
[options, args]
176+
end
177+
end
178+
179+
it "does not check the required option in the given command" do
180+
expect(my_script.start(%w(boring command))).to eq [{}, %w(command)]
181+
end
182+
183+
it "does check the required option of the remaining command" do
184+
content = capture(:stderr) { my_script.start(%w(exec command)) }
185+
expect(content).to eq "No value provided for required options '--foo'\n"
186+
end
187+
188+
it "does affects help by default" do
189+
expect(my_script.disable_required_check?(double(:name => "help"))).to be true
190+
end
191+
192+
context "when provided with multiple command names" do
193+
klass = Class.new(Thor) do
194+
disable_required_check! :foo, :bar
195+
end
196+
197+
it "affects all specified commands" do
198+
expect(klass.disable_required_check?(double(:name => "help"))).to be true
199+
expect(klass.disable_required_check?(double(:name => "foo"))).to be true
200+
expect(klass.disable_required_check?(double(:name => "bar"))).to be true
201+
expect(klass.disable_required_check?(double(:name => "baz"))).to be false
202+
end
203+
end
204+
205+
context "when invoked several times" do
206+
klass = Class.new(Thor) do
207+
disable_required_check! :foo
208+
disable_required_check! :bar
209+
end
210+
211+
it "affects all specified commands" do
212+
expect(klass.disable_required_check?(double(:name => "help"))).to be true
213+
expect(klass.disable_required_check?(double(:name => "foo"))).to be true
214+
expect(klass.disable_required_check?(double(:name => "bar"))).to be true
215+
expect(klass.disable_required_check?(double(:name => "baz"))).to be false
216+
end
217+
end
218+
end
219+
162220
describe "#map" do
163221
it "calls the alias of a method if one is provided" do
164222
expect(MyScript.start(%w(-T fish))).to eq(%w(fish))

0 commit comments

Comments
 (0)