diff --git a/lib/vagrant.rb b/lib/vagrant.rb index 1cfe0abcf..138b4a47b 100644 --- a/lib/vagrant.rb +++ b/lib/vagrant.rb @@ -91,6 +91,22 @@ module Vagrant @source_root ||= Pathname.new(File.expand_path('../../', __FILE__)) end + # Returns a superclass to use when creating a plugin for Vagrant. + # Given a specific version, this returns a proper superclass to use + # to register plugins for that version. + # + # Plugins should subclass the class returned by this method, and will + # be registered as soon as they have a name associated with them. + # + # @return [Class] + def self.plugin(version) + # We only support version 1 right now. + return Plugin::V1 if version == "1" + + # Raise an error that the plugin version is invalid + raise ArgumentError, "Invalid plugin version API: #{version}" + end + # Global registry of commands that are available via the CLI. # # This registry is used to look up the sub-commands that are available diff --git a/lib/vagrant/plugin.rb b/lib/vagrant/plugin.rb index 62da439c2..2237cfd9b 100644 --- a/lib/vagrant/plugin.rb +++ b/lib/vagrant/plugin.rb @@ -9,6 +9,9 @@ module Vagrant # (for debugging), the list of loaded plugins is stored in the {plugins} # array. class Plugin + # XXX: Make Plugin a module at some point + autoload :V1, 'vagrant/plugin/v1' + # The array of gem specifications that were loaded as plugins. @@plugins = [] diff --git a/lib/vagrant/plugin/v1.rb b/lib/vagrant/plugin/v1.rb new file mode 100644 index 000000000..16d4adab9 --- /dev/null +++ b/lib/vagrant/plugin/v1.rb @@ -0,0 +1,68 @@ +module Vagrant + class Plugin + # The superclass for version 1 plugins. + class V1 + # Returns a list of registered plugins for this version. + # + # @return [Array] + def self.registered + @registry || [] + end + + # Set the name of the plugin. The moment that this is called, the + # plugin will be registered and available. Before this is called, a + # plugin does not exist. The name must be unique among all installed + # plugins. + # + # @param [String] name Name of the plugin. + # @return [String] The name of the plugin. + def self.name(name=UNSET_VALUE) + # The plugin should be registered if we're setting a real name on it + register!(self) if name != UNSET_VALUE + + # Get or set the value + get_or_set(:name, name) + end + + # Sets a human-friendly descrition of the plugin. + # + # @param [String] value Description of the plugin. + # @return [String] Description of the plugin. + def self.description(value=UNSET_VALUE) + get_or_set(:description, value) + end + + protected + + # Sentinel value denoting that a value has not been set. + UNSET_VALUE = Object.new + + # Helper method that will set a value if a value is given, or otherwise + # return the already set value. + # + # @param [Symbol] key Key for the data + # @param [Object] value Value to store. + # @return [Object] Stored value. + def self.get_or_set(key, value=UNSET_VALUE) + @data ||= {} + + # If no value is to be set, then return the value we have already set + return @data[key] if value.eql?(UNSET_VALUE) + + # Otherwise set the value + @data[key] = value + end + + # Registers the plugin. This makes the plugin actually work with + # Vagrant. Prior to registering, the plugin is merely a skeleton. + def self.register!(plugin) + # Register only on the root class + return V1.register!(plugin) if self != V1 + + # Register it into the list + @registry ||= [] + @registry << plugin if !@registry.include?(plugin) + end + end + end +end diff --git a/test/unit/vagrant/plugin/v1_test.rb b/test/unit/vagrant/plugin/v1_test.rb new file mode 100644 index 000000000..03011b266 --- /dev/null +++ b/test/unit/vagrant/plugin/v1_test.rb @@ -0,0 +1,46 @@ +require File.expand_path("../../../base", __FILE__) + +describe Vagrant::Plugin::V1 do + after(:each) do + # We want to make sure that the registered plugins remains empty + # after each test. + described_class.registered.clear + end + + it "should be able to set and get the name" do + plugin = Class.new(described_class) do + name "foo" + end + + plugin.name.should == "foo" + end + + it "should be able to set and get the description" do + plugin = Class.new(described_class) do + description "bar" + end + + plugin.description.should == "bar" + end + + it "should have no registered plugins" do + described_class.registered.should be_empty + end + + it "should register a plugin when a name is set" do + plugin = Class.new(described_class) do + name "foo" + end + + described_class.registered.should == [plugin] + end + + it "should register a plugin only once" do + plugin = Class.new(described_class) do + name "foo" + name "bar" + end + + described_class.registered.should == [plugin] + end +end diff --git a/test/unit/vagrant_test.rb b/test/unit/vagrant_test.rb index de2b2f379..82aeb58ff 100644 --- a/test/unit/vagrant_test.rb +++ b/test/unit/vagrant_test.rb @@ -24,4 +24,15 @@ describe Vagrant do it "has a registry for provisioners" do described_class.provisioners.should be_a(Vagrant::Registry) end + + describe "plugin superclass" do + it "returns the proper class for version 1" do + described_class.plugin("1").should == Vagrant::Plugin::V1 + end + + it "raises an exception if an unsupported version is given" do + expect { described_class.plugin("88") }. + to raise_error(ArgumentError) + end + end end