Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly identify hyphenated and alias command names #878

Merged
merged 2 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions lib/thor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,17 @@ def disable_required_check?(command) #:nodoc:
command && disable_required_check.include?(command.name.to_sym)
end

# Checks if a specified command exists.
#
# ==== Parameters
# command_name<String>:: The name of the command to check for existence.
#
# ==== Returns
# Boolean:: +true+ if the command exists, +false+ otherwise.
def command_exists?(command_name) #:nodoc:
commands.keys.include?(normalize_command_name(command_name))
end

protected

# Returns this class exclusive options array set.
Expand Down
11 changes: 11 additions & 0 deletions lib/thor/group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,17 @@ def handle_argument_error(command, error, _args, arity) #:nodoc:
raise error, msg
end

# Checks if a specified command exists.
#
# ==== Parameters
# command_name<String>:: The name of the command to check for existence.
#
# ==== Returns
# Boolean:: +true+ if the command exists, +false+ otherwise.
def command_exists?(command_name) #:nodoc:
commands.keys.include?(command_name)
end

protected

# The method responsible for dispatching given the args.
Expand Down
2 changes: 1 addition & 1 deletion lib/thor/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def find_class_and_command_by_namespace(namespace, fallback = true)
*pieces, command = namespace.split(":")
namespace = pieces.join(":")
namespace = "default" if namespace.empty?
klass = Thor::Base.subclasses.detect { |thor| thor.namespace == namespace && thor.commands.keys.include?(command) }
klass = Thor::Base.subclasses.detect { |thor| thor.namespace == namespace && thor.command_exists?(command) }
end
unless klass # look for a Thor::Group with the right name
klass = Thor::Util.find_by_namespace(namespace)
Expand Down
3 changes: 3 additions & 0 deletions spec/fixtures/script.thor
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,15 @@ end
class Apple < Thor
namespace :fruits
desc 'apple', 'apple'; def apple; end
desc 'rotten-apple', 'rotten apple'; def rotten_apple; end
map "ra" => :rotten_apple
end

class Pear < Thor
namespace :fruits
desc 'pear', 'pear'; def pear; end
end

class MyClassOptionScript < Thor
class_option :free

Expand Down
10 changes: 10 additions & 0 deletions spec/group_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,16 @@
end
end

describe "#command_exists?" do
it "returns true for a command that is defined in the class" do
expect(MyCounter.command_exists?("one")).to be true
end

it "returns false for a command that is not defined in the class" do
expect(MyCounter.command_exists?("zero")).to be false
end
end

describe "edge-cases" do
it "can handle boolean options followed by arguments" do
klass = Class.new(Thor::Group) do
Expand Down
12 changes: 12 additions & 0 deletions spec/thor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,18 @@ def self.exit_on_failure?
end
end

describe "#command_exists?" do
it "returns true for a command that is defined in the class" do
expect(MyScript.command_exists?("zoo")).to be true
expect(MyScript.command_exists?("name-with-dashes")).to be true
expect(MyScript.command_exists?("animal_prison")).to be true
end

it "returns false for a command that is not defined in the class" do
expect(MyScript.command_exists?("animal_heaven")).to be false
end
end

describe "#map" do
it "calls the alias of a method if one is provided" do
expect(MyScript.start(%w(-T fish))).to eq(%w(fish))
Expand Down
8 changes: 8 additions & 0 deletions spec/util_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ def self.clear_user_home!
expect(Thor::Util.find_class_and_command_by_namespace("fruits:apple")).to eq([Apple, "apple"])
expect(Thor::Util.find_class_and_command_by_namespace("fruits:pear")).to eq([Pear, "pear"])
end

it "returns correct Thor class and the command name with hypen when shared namespaces" do
expect(Thor::Util.find_class_and_command_by_namespace("fruits:rotten-apple")).to eq([Apple, "rotten-apple"])
end

it "returns correct Thor class and the associated alias command name when shared namespaces" do
expect(Thor::Util.find_class_and_command_by_namespace("fruits:ra")).to eq([Apple, "ra"])
end
end

describe "#thor_classes_in" do
Expand Down