Commands can now be registered as subcommands (similar to how config works)

This commit is contained in:
Mitchell Hashimoto 2010-04-13 13:13:55 -07:00
parent 83da66ee91
commit aec05eff0d
5 changed files with 109 additions and 33 deletions

View File

@ -21,18 +21,7 @@ module Vagrant
# Execute a subcommand with the given name and args. This method properly # Execute a subcommand with the given name and args. This method properly
# finds the subcommand, instantiates it, and executes. # finds the subcommand, instantiates it, and executes.
def subcommand(name, *args) def subcommand(name, *args)
command_klass = Commands.const_get(camelize(name)) Commands::Base.dispatch(env, name, *args)
command = command_klass.new(env)
command.execute(args)
end
# Camel-case a string.
def camelize(string)
parts = string.to_s.split(/[^a-z0-9]/).collect do |part|
part.capitalize
end
parts.join("")
end end
end end
end end

View File

@ -7,6 +7,61 @@ module Vagrant
class Base class Base
attr_reader :env attr_reader :env
class <<self
# Contains the list of registered subcommands. The registered commands are
# stored in a hash table and are therefore unordered.
#
# @return [Hash]
def subcommands
@subcommands ||= {}
end
# Registers a command with `vagrant`. This method allows 3rd parties to
# dynamically add new commands to the `vagrant` command, allowing plugins
# to act as 1st class citizens within vagrant.
#
# @param [String] key The subcommand which will invoke the registered command.
# @param [Class] klass. The subcommand class (a subclass of {Base})
def subcommand(key, klass)
subcommands[key] = klass
end
# Dispatches a subcommand to the proper registered command. Otherwise, it
# prints a help message.
def dispatch(env, name, *args)
klass = subcommands[name]
if klass.nil?
puts_help
return # For tests
end
command = klass.new(env)
command.execute(args)
end
# Prints out the list of supported commands and their descriptions (if
# available) then exits.
def puts_help
puts "Usage: vagrant SUBCOMMAND ...\n\n"
puts "Supported commands:"
subcommands.each do |key, klass|
puts "#{' ' * 4}#{key.ljust(20)}#{klass.description}"
end
exit
end
# Sets or reads the description, depending on if the value is set in the
# parameter.
def description(value=nil)
@description ||= ''
return @description if value.nil?
@description = value
end
end
def initialize(env) def initialize(env)
@env = env @env = env
end end

View File

@ -1,10 +1,15 @@
module Vagrant module Vagrant
class Commands class Commands
class Init < Base class Init < Base
Base.subcommand "init", self
description "Initializes current folder for Vagrant usage"
def execute(args) def execute(args)
parse_options(args) do |opts, options| parse_options(args) do |opts, options|
opts.banner = "Usage: vagrant init [name]" opts.banner = "Usage: vagrant init [name]"
end end
puts "HEY"
end end
end end
end end

View File

@ -43,28 +43,10 @@ class CommandTest < Test::Unit::TestCase
@instance.stubs(:camelize).with(@raw_name).returns(@name) @instance.stubs(:camelize).with(@raw_name).returns(@name)
end end
should "send the command to the proper class" do should "send the env, name, and args to the base class" do
klass = mock("klass")
instance = mock("instance")
args = [1,2,3] args = [1,2,3]
Vagrant::Commands.expects(:const_get).with(@name).returns(klass) Vagrant::Commands::Base.expects(:dispatch).with(@env, @name, *args)
klass.expects(:new).with(@env).returns(instance) @instance.subcommand(@name, *args)
instance.expects(:execute).with(args)
@instance.subcommand(@raw_name, *args)
end
end
context "camelizing" do
should "camel case a string" do
tests = {
"foo_bar_baz" => "FooBarBaz",
"ssh-config" => "SshConfig"
}
tests.each do |test, expected|
assert_equal expected, @instance.camelize(test)
end
end end
end end
end end

View File

@ -13,6 +13,51 @@ class CommandsBastTest < Test::Unit::TestCase
end end
end end
context "class methods" do
setup do
@klass.subcommands.clear
end
context "registering commands" do
should "register commands" do
klass = mock("klass")
@klass.subcommand("init", klass)
assert_equal klass, @klass.subcommands["init"]
end
end
context "dispatching to subcommands" do
setup do
@command_klass = mock("klass")
@name = "init"
@klass.subcommand(@name, @command_klass)
@args = [1,2,3]
end
should "instantiate and execute on registered subcommands" do
instance = mock("instance")
@command_klass.expects(:new).with(@env).returns(instance)
instance.expects(:execute).with(@args)
@klass.dispatch(@env, @name, *@args)
end
should "print help if command doesn't exist" do
@klass.expects(:puts_help).once
@klass.dispatch(@env, "#{@name}foo")
end
end
context "descriptions" do
should "be able to set description" do
description = "The lazy fox yada yada"
@klass.description(description)
assert_equal description, @klass.description
end
end
end
context "instance methods" do context "instance methods" do
setup do setup do
@env = mock_environment @env = mock_environment