Init command. Tests for the base command.

This commit is contained in:
Mitchell Hashimoto 2010-08-24 10:27:36 -07:00
parent 2691b0493c
commit 48b7596357
7 changed files with 152 additions and 45 deletions

View File

@ -3,6 +3,15 @@ require 'virtualbox'
require "vagrant/util/glob_loader"
module Vagrant
# TODO: Move more classes over to the autoload model. We'll
# start small, but slowly move everything over.
autoload :CLI, 'vagrant/cli'
module Command
autoload :Base, 'vagrant/command/base'
end
class << self
attr_writer :ui
@ -31,7 +40,7 @@ end
# Load them up. One day we'll convert this to autoloads. Today
# is not that day. Low hanging fruit for anyone wishing to do it.
libdir = File.expand_path("lib/vagrant", Vagrant.source_root)
Vagrant::GlobLoader.glob_require(libdir, %w{util util/stacked_proc_runner cli
Vagrant::GlobLoader.glob_require(libdir, %w{util util/stacked_proc_runner
downloaders/base config provisioners/base provisioners/chef systems/base
action/exception_catcher hosts/base})

View File

@ -1,19 +1,24 @@
require 'thor'
module Vagrant
# Entrypoint for the Vagrant CLI. This class should never be
# initialized directly (like a typical Thor class). Instead,
# use {Environment#cli} to invoke the CLI.
class CLI < Thor
attr_reader :env
def initialize(args=[], options={}, config={})
super
# Set the UI to a shell based UI using the shell object which
# Thor sets up.
Vagrant.ui = UI::Shell.new(shell) if !Vagrant.ui.is_a?(UI::Shell)
# 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]
# Registers the given class with the CLI so it can be accessed.
# The class must be a subclass of either {Command} or {GroupCommand}.
def self.register(klass, name, usage, description)
if klass <= Thor # TODO: make Command::GroupBase
# A subclass of Thor is a subcommand, since it contains
# many smaller commands within it.
desc usage, description
subcommand name, klass
elsif klass <= Command::Base
# A subclass of Thor::Group is a single command, since it
# is invoked as a whole.
desc usage, description
define_method(name) { |*args| invoke klass, args }
end
end
end
end

View File

@ -0,0 +1,49 @@
require 'thor/group'
require 'thor/actions'
module Vagrant
module Command
# A CLI command is the subclass for all commands which are single
# commands, e.g. `vagrant init`, `vagrant up`. Not commands like
# `vagrant box add`. For commands which have more subcommands, use
# a {GroupBase}.
#
# A {Base} is a subclass of `Thor::Group`, so view the documentation
# there on how to add arguments, descriptions etc. The important note
# about this is that when invoked, _all public methods_ will be called
# in the order they are defined. If you don't want a method called when
# the command is invoked, it must be made `protected` or `private`.
#
# The best way to get examples of how to create your own command is to
# view the various Vagrant commands, which are relatively simple.
class Base < Thor::Group
include Thor::Actions
# Register the command with the main Vagrant CLI under the
# 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
# will be `vagrant lamp`.
#
# The description added to the class via the `desc` method will be
# used as a description for the command.
def self.register(usage)
# Extracts the name out of the usage string. So `init [foo] [bar]`
# becomes "init"
_, name = /^([a-zA-Z0-9]+)(\s+(.+?))?$/.match(usage).to_a
CLI.register(self, name, usage, desc)
end
def initialize(args=[], options={}, config={})
super
# Set the UI to a shell based UI using the shell object which
# Thor sets up.
Vagrant.ui = UI::Shell.new(shell) if !Vagrant.ui.is_a?(UI::Shell)
# 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]
end
end
end
end

View File

@ -0,0 +1,15 @@
module Vagrant
module Command
class InitCommand < Base
desc "Initializes the current folder for Vagrant usage"
argument :box_name, :type => :string, :optional => true, :default => "base"
argument :box_url, :type => :string, :optional => true
source_root File.expand_path("templates/commands/init", Vagrant.source_root)
register "init [box_name] [box_url]"
def execute
template "Vagrantfile.erb", "Vagrantfile"
end
end
end
end

View File

@ -0,0 +1,13 @@
Vagrant::Config.run do |config|
# All Vagrant configuration is done here. For a detailed explanation
# and listing of configuration options, please view the documentation
# online.
# Every Vagrant virtual environment requires a box to build off of.
config.vm.box = "<%= box_name %>"
<% if !box_url.nil? %>
# The url from where the 'config.vm.box' box will be fetched if it
# doesn't already exist on the user's system
config.vm.box_url = "<%= box_url %>"<% end %>
end

View File

@ -3,40 +3,14 @@ require "test_helper"
class CLITest < Test::Unit::TestCase
setup do
@klass = Vagrant::CLI
@env = mock_environment
end
context "setting up a UI" do
setup do
Vagrant.ui = nil
end
should "setup a shell UI" do
silence_stream(STDOUT) { @klass.start([], :env => @env) }
assert Vagrant.ui.is_a?(Vagrant::UI::Shell)
end
should "setup a shell UI only once" do
silence_stream(STDOUT) { @klass.start([], :env => @env) }
ui = Vagrant.ui
silence_stream(STDOUT) { @klass.start([], :env => @env) }
assert Vagrant.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
context "registering" do
should "register a base command as a single invokable" do
base = Class.new(Vagrant::Command::Base)
name = "__test_registering_single_subcommand"
@klass.register(base, name, name, "A description")
assert @klass.tasks[name]
end
end
end

View File

@ -0,0 +1,42 @@
require "test_helper"
class CommandBaseTest < Test::Unit::TestCase
setup do
@klass = Vagrant::Command::Base
@env = mock_environment
end
context "setting up a UI" do
setup do
Vagrant.ui = nil
end
should "setup a shell UI" do
silence_stream(STDOUT) { @klass.start([], :env => @env) }
assert Vagrant.ui.is_a?(Vagrant::UI::Shell)
end
should "setup a shell UI only once" do
silence_stream(STDOUT) { @klass.start([], :env => @env) }
ui = Vagrant.ui
silence_stream(STDOUT) { @klass.start([], :env => @env) }
assert Vagrant.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