Skip to content

Commit f2e243d

Browse files
authored
Merge pull request #878 from takmar/fix/find-class-and-command-name
Correctly identify hyphenated and alias command names
2 parents 8f897d5 + 3821657 commit f2e243d

File tree

7 files changed

+56
-1
lines changed

7 files changed

+56
-1
lines changed

lib/thor.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,17 @@ def disable_required_check?(command) #:nodoc:
439439
command && disable_required_check.include?(command.name.to_sym)
440440
end
441441

442+
# Checks if a specified command exists.
443+
#
444+
# ==== Parameters
445+
# command_name<String>:: The name of the command to check for existence.
446+
#
447+
# ==== Returns
448+
# Boolean:: +true+ if the command exists, +false+ otherwise.
449+
def command_exists?(command_name) #:nodoc:
450+
commands.keys.include?(normalize_command_name(command_name))
451+
end
452+
442453
protected
443454

444455
# Returns this class exclusive options array set.

lib/thor/group.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,17 @@ def handle_argument_error(command, error, _args, arity) #:nodoc:
211211
raise error, msg
212212
end
213213

214+
# Checks if a specified command exists.
215+
#
216+
# ==== Parameters
217+
# command_name<String>:: The name of the command to check for existence.
218+
#
219+
# ==== Returns
220+
# Boolean:: +true+ if the command exists, +false+ otherwise.
221+
def command_exists?(command_name) #:nodoc:
222+
commands.keys.include?(command_name)
223+
end
224+
214225
protected
215226

216227
# The method responsible for dispatching given the args.

lib/thor/util.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def find_class_and_command_by_namespace(namespace, fallback = true)
133133
*pieces, command = namespace.split(":")
134134
namespace = pieces.join(":")
135135
namespace = "default" if namespace.empty?
136-
klass = Thor::Base.subclasses.detect { |thor| thor.namespace == namespace && thor.commands.keys.include?(command) }
136+
klass = Thor::Base.subclasses.detect { |thor| thor.namespace == namespace && thor.command_exists?(command) }
137137
end
138138
unless klass # look for a Thor::Group with the right name
139139
klass = Thor::Util.find_by_namespace(namespace)

spec/fixtures/script.thor

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,12 +264,15 @@ end
264264
class Apple < Thor
265265
namespace :fruits
266266
desc 'apple', 'apple'; def apple; end
267+
desc 'rotten-apple', 'rotten apple'; def rotten_apple; end
268+
map "ra" => :rotten_apple
267269
end
268270

269271
class Pear < Thor
270272
namespace :fruits
271273
desc 'pear', 'pear'; def pear; end
272274
end
275+
273276
class MyClassOptionScript < Thor
274277
class_option :free
275278

spec/group_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,16 @@
186186
end
187187
end
188188

189+
describe "#command_exists?" do
190+
it "returns true for a command that is defined in the class" do
191+
expect(MyCounter.command_exists?("one")).to be true
192+
end
193+
194+
it "returns false for a command that is not defined in the class" do
195+
expect(MyCounter.command_exists?("zero")).to be false
196+
end
197+
end
198+
189199
describe "edge-cases" do
190200
it "can handle boolean options followed by arguments" do
191201
klass = Class.new(Thor::Group) do

spec/thor_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,18 @@ def self.exit_on_failure?
340340
end
341341
end
342342

343+
describe "#command_exists?" do
344+
it "returns true for a command that is defined in the class" do
345+
expect(MyScript.command_exists?("zoo")).to be true
346+
expect(MyScript.command_exists?("name-with-dashes")).to be true
347+
expect(MyScript.command_exists?("animal_prison")).to be true
348+
end
349+
350+
it "returns false for a command that is not defined in the class" do
351+
expect(MyScript.command_exists?("animal_heaven")).to be false
352+
end
353+
end
354+
343355
describe "#map" do
344356
it "calls the alias of a method if one is provided" do
345357
expect(MyScript.start(%w(-T fish))).to eq(%w(fish))

spec/util_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ def self.clear_user_home!
114114
expect(Thor::Util.find_class_and_command_by_namespace("fruits:apple")).to eq([Apple, "apple"])
115115
expect(Thor::Util.find_class_and_command_by_namespace("fruits:pear")).to eq([Pear, "pear"])
116116
end
117+
118+
it "returns correct Thor class and the command name with hypen when shared namespaces" do
119+
expect(Thor::Util.find_class_and_command_by_namespace("fruits:rotten-apple")).to eq([Apple, "rotten-apple"])
120+
end
121+
122+
it "returns correct Thor class and the associated alias command name when shared namespaces" do
123+
expect(Thor::Util.find_class_and_command_by_namespace("fruits:ra")).to eq([Apple, "ra"])
124+
end
117125
end
118126

119127
describe "#thor_classes_in" do

0 commit comments

Comments
 (0)