Skip to content

Commit 96b52ea

Browse files
authored
Merge pull request #652 from deivid-rodriguez/multiple_usages
Multiple usages
2 parents bbe7932 + adcf9a5 commit 96b52ea

File tree

7 files changed

+59
-17
lines changed

7 files changed

+59
-17
lines changed

lib/thor.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def command_help(shell, command_name)
175175
handle_no_command_error(meth) unless command
176176

177177
shell.say "Usage:"
178-
shell.say " #{banner(command)}"
178+
shell.say " #{banner(command).split("\n").join("\n ")}"
179179
shell.say
180180
class_options_help(shell, nil => command.options.values)
181181
if command.long_description
@@ -398,7 +398,9 @@ def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable Me
398398
# the namespace should be displayed as arguments.
399399
#
400400
def banner(command, namespace = nil, subcommand = false)
401-
"#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
401+
command.formatted_usage(self, $thor_runner, subcommand).split("\n").map do |formatted_usage|
402+
"#{basename} #{formatted_usage}"
403+
end.join("\n")
402404
end
403405

404406
def baseclass #:nodoc:

lib/thor/base.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ def handle_argument_error(command, error, args, arity) #:nodoc:
502502
msg = "ERROR: \"#{basename} #{name}\" was called with ".dup
503503
msg << "no arguments" if args.empty?
504504
msg << "arguments " << args.inspect unless args.empty?
505-
msg << "\nUsage: #{banner(command).inspect}"
505+
msg << "\nUsage: \"#{banner(command).split("\n").join("\"\n \"")}\""
506506
raise InvocationError, msg
507507
end
508508

lib/thor/command.rb

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,32 @@ def formatted_usage(klass, namespace = true, subcommand = false)
4949

5050
formatted ||= "".dup
5151

52-
# Add usage with required arguments
53-
formatted << if klass && !klass.arguments.empty?
54-
usage.to_s.gsub(/^#{name}/) do |match|
55-
match << " " << klass.arguments.map(&:usage).compact.join(" ")
56-
end
57-
else
58-
usage.to_s
59-
end
52+
Array(usage).map do |specific_usage|
53+
formatted_specific_usage = formatted
6054

61-
# Add required options
62-
formatted << " #{required_options}"
55+
formatted_specific_usage += required_arguments_for(klass, specific_usage)
6356

64-
# Strip and go!
65-
formatted.strip
57+
# Add required options
58+
formatted_specific_usage += " #{required_options}"
59+
60+
# Strip and go!
61+
formatted_specific_usage.strip
62+
end.join("\n")
6663
end
6764

6865
protected
6966

67+
# Add usage with required arguments
68+
def required_arguments_for(klass, usage)
69+
if klass && !klass.arguments.empty?
70+
usage.to_s.gsub(/^#{name}/) do |match|
71+
match << " " << klass.arguments.map(&:usage).compact.join(" ")
72+
end
73+
else
74+
usage.to_s
75+
end
76+
end
77+
7078
def not_debugging?(instance)
7179
!(instance.class.respond_to?(:debugging) && instance.class.debugging)
7280
end

spec/command_spec.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
require "helper"
22

33
describe Thor::Command do
4-
def command(options = {})
4+
def command(options = {}, usage = "can_has")
55
options.each do |key, value|
66
options[key] = Thor::Option.parse(key, value)
77
end
88

9-
@command ||= Thor::Command.new(:can_has, "I can has cheezburger", "I can has cheezburger\nLots and lots of it", "can_has", options)
9+
@command ||= Thor::Command.new(:can_has, "I can has cheezburger", "I can has cheezburger\nLots and lots of it", usage, options)
1010
end
1111

1212
describe "#formatted_usage" do
@@ -30,6 +30,11 @@ def command(options = {})
3030
object = Struct.new(:namespace, :arguments).new("foo", [Thor::Argument.new(:bar, options)])
3131
expect(command(:foo => :required).formatted_usage(object)).to eq("foo:can_has BAR --foo=FOO")
3232
end
33+
34+
it "allows multiple usages" do
35+
object = Struct.new(:namespace, :arguments).new("foo", [])
36+
expect(command({ :bar => :required }, ["can_has FOO", "can_has BAR"]).formatted_usage(object, false)).to eq("can_has FOO --bar=BAR\ncan_has BAR --bar=BAR")
37+
end
3338
end
3439

3540
describe "#dynamic" do

spec/fixtures/script.thor

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ END
5151
[bar, options]
5252
end
5353

54+
method_option :all, :desc => "Do bazing for all the things"
55+
desc ["baz THING", "baz --all"], "super cool"
56+
def baz(thing = nil)
57+
raise if thing.nil? && !options.include?(:all)
58+
end
59+
5460
desc "example_default_command", "example!"
5561
method_options :with => :string
5662
def example_default_command
@@ -215,6 +221,10 @@ module Scripts
215221
desc "optional_arg [ARG]", "takes an optional arg"
216222
def optional_arg(arg='default')
217223
end
224+
225+
desc ["multiple_usages ARG --foo", "multiple_usages ARG --bar"], "takes mutually exclusive combinations of args and flags"
226+
def multiple_usages(arg)
227+
end
218228
end
219229
end
220230

spec/helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
WebMock.disable_net_connect!(:allow => "coveralls.io")
2424

2525
# Set shell to basic
26+
ENV["THOR_COLUMNS"] = "10000"
2627
$0 = "thor"
2728
$thor_runner = true
2829
ARGV.clear

spec/thor_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ def boring(*args)
440440
Usage: "thor scripts:arities:two_args ARG1 ARG2"'
441441
arity_asserter.call %w(optional_arg one two), 'ERROR: "thor optional_arg" was called with arguments ["one", "two"]
442442
Usage: "thor scripts:arities:optional_arg [ARG]"'
443+
arity_asserter.call %w(multiple_usages), 'ERROR: "thor multiple_usages" was called with no arguments
444+
Usage: "thor scripts:arities:multiple_usages ARG --foo"
445+
"thor scripts:arities:multiple_usages ARG --bar"'
443446
end
444447

445448
it "raises an error if the invoked command does not exist" do
@@ -552,6 +555,19 @@ def shell
552555
END
553556
end
554557

558+
it "provides full help info when talking about a specific command with multiple usages" do
559+
expect(capture(:stdout) { MyScript.command_help(shell, "baz") }).to eq(<<-END)
560+
Usage:
561+
thor my_script:baz THING
562+
thor my_script:baz --all
563+
564+
Options:
565+
[--all=ALL] # Do bazing for all the things
566+
567+
super cool
568+
END
569+
end
570+
555571
it "raises an error if the command can't be found" do
556572
expect do
557573
MyScript.command_help(shell, "unknown")

0 commit comments

Comments
 (0)