Command::GroupBase for creating subcommands for Thor.
This commit is contained in:
parent
1facebc3d9
commit
5af0537e56
|
@ -9,7 +9,8 @@ module Vagrant
|
||||||
autoload :CLI, 'vagrant/cli'
|
autoload :CLI, 'vagrant/cli'
|
||||||
|
|
||||||
module Command
|
module Command
|
||||||
autoload :Base, 'vagrant/command/base'
|
autoload :Base, 'vagrant/command/base'
|
||||||
|
autoload :GroupBase, 'vagrant/command/group_base'
|
||||||
end
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
|
@ -8,13 +8,13 @@ module Vagrant
|
||||||
# Registers the given class with the CLI so it can be accessed.
|
# Registers the given class with the CLI so it can be accessed.
|
||||||
# The class must be a subclass of either {Command} or {GroupCommand}.
|
# The class must be a subclass of either {Command} or {GroupCommand}.
|
||||||
def self.register(klass, name, usage, description)
|
def self.register(klass, name, usage, description)
|
||||||
if klass <= Thor # TODO: make Command::GroupBase
|
if klass <= Command::GroupBase
|
||||||
# A subclass of Thor is a subcommand, since it contains
|
# A subclass of GroupBase is a subcommand, since it contains
|
||||||
# many smaller commands within it.
|
# many smaller commands within it.
|
||||||
desc usage, description
|
desc usage, description
|
||||||
subcommand name, klass
|
subcommand name, klass
|
||||||
elsif klass <= Command::Base
|
elsif klass <= Command::Base
|
||||||
# A subclass of Thor::Group is a single command, since it
|
# A subclass of Base is a single command, since it
|
||||||
# is invoked as a whole.
|
# is invoked as a whole.
|
||||||
desc usage, description
|
desc usage, description
|
||||||
define_method(name) { |*args| invoke klass, args }
|
define_method(name) { |*args| invoke klass, args }
|
||||||
|
|
|
@ -19,6 +19,8 @@ module Vagrant
|
||||||
class Base < Thor::Group
|
class Base < Thor::Group
|
||||||
include Thor::Actions
|
include Thor::Actions
|
||||||
|
|
||||||
|
attr_reader :env
|
||||||
|
|
||||||
# Register the command with the main Vagrant CLI under the
|
# Register the command with the main Vagrant CLI under the
|
||||||
# given name. The name will be used for accessing it from the CLI,
|
# given name. The name will be used for accessing it from the CLI,
|
||||||
# so if you name it "lamp", then the command to invoke this
|
# so if you name it "lamp", then the command to invoke this
|
||||||
|
@ -27,10 +29,13 @@ module Vagrant
|
||||||
# The description added to the class via the `desc` method will be
|
# The description added to the class via the `desc` method will be
|
||||||
# used as a description for the command.
|
# used as a description for the command.
|
||||||
def self.register(usage)
|
def self.register(usage)
|
||||||
# Extracts the name out of the usage string. So `init [foo] [bar]`
|
CLI.register(self, extract_name_from_usage(usage), usage, desc)
|
||||||
# becomes "init"
|
end
|
||||||
_, name = /^([a-zA-Z0-9]+)(\s+(.+?))?$/.match(usage).to_a
|
|
||||||
CLI.register(self, name, usage, desc)
|
# Extracts the name of the command from a usage string. Example:
|
||||||
|
# `init [box_name] [box_url]` becomes just `init`.
|
||||||
|
def self.extract_name_from_usage(usage)
|
||||||
|
/^([a-zA-Z0-9]+)(\s+(.+?))?$/.match(usage).to_a[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(args=[], options={}, config={})
|
def initialize(args=[], options={}, config={})
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
module Vagrant
|
||||||
|
module Command
|
||||||
|
class BoxCommand < GroupBase
|
||||||
|
register "box", "Commands to manage system boxes"
|
||||||
|
|
||||||
|
desc "add NAME URI", "Add a box to the system"
|
||||||
|
def add(name, uri)
|
||||||
|
Box.add(env, name, uri)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,37 @@
|
||||||
|
require 'thor'
|
||||||
|
require 'thor/actions'
|
||||||
|
|
||||||
|
module Vagrant
|
||||||
|
module Command
|
||||||
|
# A {GroupBase} is the subclass which should be used if you're
|
||||||
|
# creating a CLI command which has subcommands such as `vagrant box`,
|
||||||
|
# which has subcommands such as `add`, `remove`, `list`. If you're
|
||||||
|
# creating a simple command which has no subcommands, such as `vagrant up`,
|
||||||
|
# then use {Base} instead.
|
||||||
|
class GroupBase < Thor
|
||||||
|
include Thor::Actions
|
||||||
|
|
||||||
|
attr_reader :env
|
||||||
|
|
||||||
|
# Register the command with the main Vagrant CLI under the given
|
||||||
|
# usage. The usage will be used for accessing it from the CLI,
|
||||||
|
# so if you give it a usage of `lamp [subcommand]`, then the command
|
||||||
|
# to invoke this will be `vagrant lamp` (with a subcommand).
|
||||||
|
#
|
||||||
|
# Additionally, unlike {Base}, a description must be specified to
|
||||||
|
# this register command, since there is no class-wide description.
|
||||||
|
def self.register(usage, description)
|
||||||
|
CLI.register(self, Base.extract_name_from_usage(usage), usage, description)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(args=[], options={}, config={})
|
||||||
|
super
|
||||||
|
|
||||||
|
# The last argument must _always_ be a Vagrant Environment class.
|
||||||
|
raise CLIMissingEnvironment.new("This command requires that a Vagrant environment be properly passed in as the last parameter.") if !config[:env]
|
||||||
|
@env = config[:env]
|
||||||
|
@env.ui = UI::Shell.new(shell) if !@env.ui.is_a?(UI::Shell)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,5 +12,12 @@ class CLITest < Test::Unit::TestCase
|
||||||
@klass.register(base, name, name, "A description")
|
@klass.register(base, name, name, "A description")
|
||||||
assert @klass.tasks[name]
|
assert @klass.tasks[name]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "register a group base as a subcommand" do
|
||||||
|
base = Class.new(Vagrant::Command::GroupBase)
|
||||||
|
name = "_test_registering_single_group"
|
||||||
|
@klass.register(base, name, name, "A description")
|
||||||
|
assert @klass.subcommands.include?(name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,13 @@ class CommandBaseTest < Test::Unit::TestCase
|
||||||
@env = mock_environment
|
@env = mock_environment
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "extracting a name from a usage string" do
|
||||||
|
should "extract properly" do
|
||||||
|
assert_equal "init", @klass.extract_name_from_usage("init")
|
||||||
|
assert_equal "init", @klass.extract_name_from_usage("init [foo] [bar]")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "setting up a UI" do
|
context "setting up a UI" do
|
||||||
setup do
|
setup do
|
||||||
@env.ui = nil
|
@env.ui = nil
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class CommandGroupBaseTest < Test::Unit::TestCase
|
||||||
|
setup do
|
||||||
|
@klass = Vagrant::Command::GroupBase
|
||||||
|
@env = mock_environment
|
||||||
|
end
|
||||||
|
|
||||||
|
context "setting up a UI" do
|
||||||
|
setup do
|
||||||
|
@env.ui = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
should "setup a shell UI" do
|
||||||
|
silence_stream(STDOUT) { @klass.start([], :env => @env) }
|
||||||
|
assert @env.ui.is_a?(Vagrant::UI::Shell)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "setup a shell UI only once" do
|
||||||
|
silence_stream(STDOUT) { @klass.start([], :env => @env) }
|
||||||
|
ui = @env.ui
|
||||||
|
silence_stream(STDOUT) { @klass.start([], :env => @env) }
|
||||||
|
assert @env.ui.equal?(ui)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "requiring an environment" do
|
||||||
|
should "raise an exception if the environment is not sent in" do
|
||||||
|
assert_raises(Vagrant::CLIMissingEnvironment) {
|
||||||
|
@klass.start([])
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
should "not raise an exception if the environment is properly sent in" do
|
||||||
|
silence_stream(STDOUT) do
|
||||||
|
assert_nothing_raised {
|
||||||
|
@klass.start([], :env => @env)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue