From 92ee042fc2786501a7e9fd29c89103712c1f4d51 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 16 Apr 2012 22:26:38 -0700 Subject: [PATCH] V1 config loading using plugins as a source for config keys --- lib/vagrant/config.rb | 2 ++ lib/vagrant/config/v1.rb | 33 +++++++++++++++++++ lib/vagrant/config/v1/root.rb | 35 ++++++++++++++++++++ lib/vagrant/plugin/v1.rb | 41 ++++++++++++++++-------- test/unit/vagrant/config/v1/root_test.rb | 20 ++++++++++++ test/unit/vagrant/config/v1_test.rb | 34 ++++++++++++++++++++ test/unit/vagrant/plugin/v1_test.rb | 4 +-- 7 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 lib/vagrant/config/v1.rb create mode 100644 lib/vagrant/config/v1/root.rb create mode 100644 test/unit/vagrant/config/v1/root_test.rb create mode 100644 test/unit/vagrant/config/v1_test.rb diff --git a/lib/vagrant/config.rb b/lib/vagrant/config.rb index 74665edc1..574cd3256 100644 --- a/lib/vagrant/config.rb +++ b/lib/vagrant/config.rb @@ -6,6 +6,8 @@ module Vagrant autoload :Loader, 'vagrant/config/loader' autoload :Top, 'vagrant/config/top' + autoload :V1, 'vagrant/config/v1' + autoload :NFSConfig, 'vagrant/config/nfs' autoload :PackageConfig, 'vagrant/config/package' autoload :SSHConfig, 'vagrant/config/ssh' diff --git a/lib/vagrant/config/v1.rb b/lib/vagrant/config/v1.rb new file mode 100644 index 000000000..1cd00f338 --- /dev/null +++ b/lib/vagrant/config/v1.rb @@ -0,0 +1,33 @@ +require "vagrant/config/v1/root" + +module Vagrant + module Config + # This is the "version 1" configuration loader. + class V1 + # Loads the configuration for the given proc and returns a configuration + # object. + # + # @param [Proc] config_proc + # @return [Object] + def self.load(config_proc) + # Get all the registered plugins + config_map = {} + Vagrant.plugin("1").registered.each do |plugin| + plugin.config.each do |key, klass| + config_map[key] = klass + end + end + + # Create the configuration root object + root = V1::Root.new(config_map) + + # Call the proc with the root + config_proc.call(root) + + # Return the root object, which doubles as the configuration object + # we actually use for accessing as well. + root + end + end + end +end diff --git a/lib/vagrant/config/v1/root.rb b/lib/vagrant/config/v1/root.rb new file mode 100644 index 000000000..58dcbe324 --- /dev/null +++ b/lib/vagrant/config/v1/root.rb @@ -0,0 +1,35 @@ +module Vagrant + module Config + class V1 + # This is the root configuration class. An instance of this is what + # is passed into version 1 Vagrant configuration blocks. + class Root + # Initializes a root object that maps the given keys to specific + # configuration classes. + # + # @param [Hash] config_map Map of key to config class. + def initialize(config_map) + @keys = {} + @config_map = config_map + end + + # We use method_missing as a way to get the configuration that is + # used for Vagrant and load the proper configuration classes for + # each. + def method_missing(name, *args) + return @keys[name] if @keys.has_key?(name) + + config_klass = @config_map[name.to_sym] + if config_klass + # Instantiate the class and return the instance + @keys[name] = config_klass.new + return @keys[name] + else + # Super it up to probably raise a NoMethodError + super + end + end + end + end + end +end diff --git a/lib/vagrant/plugin/v1.rb b/lib/vagrant/plugin/v1.rb index bf69a3135..7e16a8667 100644 --- a/lib/vagrant/plugin/v1.rb +++ b/lib/vagrant/plugin/v1.rb @@ -18,7 +18,7 @@ module Vagrant # @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 + register! if name != UNSET_VALUE # Get or set the value get_or_set(:name, name) @@ -44,12 +44,38 @@ module Vagrant data[:config] ||= Registry.new # Register a new config class only if a name was given. - data[:config].register(name, &block) if name != UNSET_VALUE + data[:config].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:config] 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=nil) + plugin ||= self + + # 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 + + # This unregisters the plugin. Note that to re-register the plugin + # you must call `register!` again. + def self.unregister!(plugin=nil) + plugin ||= self + + # Unregister only on the root class + return V1.unregister!(plugin) if self != V1 + + # Unregister it from the registry + @registry ||= [] + @registry.delete(plugin) + end + protected # Sentinel value denoting that a value has not been set. @@ -75,17 +101,6 @@ module Vagrant # 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/config/v1/root_test.rb b/test/unit/vagrant/config/v1/root_test.rb new file mode 100644 index 000000000..752df2502 --- /dev/null +++ b/test/unit/vagrant/config/v1/root_test.rb @@ -0,0 +1,20 @@ +require File.expand_path("../../../../base", __FILE__) + +describe Vagrant::Config::V1::Root do + include_context "unit" + + it "should provide access to config objects" do + foo_class = Class.new + map = { :foo => foo_class } + + instance = described_class.new(map) + foo = instance.foo + foo.should be_kind_of(foo_class) + instance.foo.should eql(foo) + end + + it "should raise a proper NoMethodError if a config key doesn't exist" do + instance = described_class.new({}) + expect { instance.foo }.to raise_error(NoMethodError) + end +end diff --git a/test/unit/vagrant/config/v1_test.rb b/test/unit/vagrant/config/v1_test.rb new file mode 100644 index 000000000..179ae63bc --- /dev/null +++ b/test/unit/vagrant/config/v1_test.rb @@ -0,0 +1,34 @@ +require File.expand_path("../../../base", __FILE__) + +describe Vagrant::Config::V1 do + include_context "unit" + + describe "loading" do + it "should configure with all plugin config keys loaded" do + # Register a plugin for our test + plugin_class = Class.new(Vagrant.plugin("1")) do + name "test" + config "foo" do + Class.new do + attr_accessor :bar + end + end + end + + # Create the proc we're testing + config_proc = Proc.new do |config| + config.foo.bar = "value" + end + + begin + # Test that it works properly + config = described_class.load(config_proc) + config.foo.bar.should == "value" + ensure + # We have to unregister the plugin so that future tests + # aren't mucked up. + plugin_class.unregister! + end + end + end +end diff --git a/test/unit/vagrant/plugin/v1_test.rb b/test/unit/vagrant/plugin/v1_test.rb index e648efd3a..a241586a7 100644 --- a/test/unit/vagrant/plugin/v1_test.rb +++ b/test/unit/vagrant/plugin/v1_test.rb @@ -28,7 +28,7 @@ describe Vagrant::Plugin::V1 do config("foo") { "bar" } end - plugin.config["foo"].should == "bar" + plugin.config[:foo].should == "bar" end it "should lazily register configuration classes" do @@ -45,7 +45,7 @@ describe Vagrant::Plugin::V1 do # Now verify when we actually get the configuration key that # a proper error is raised. expect { - plugin.config["foo"] + plugin.config[:foo] }.to raise_error(StandardError) end