Config merging
This commit is contained in:
parent
92ee042fc2
commit
c0ee3b06ff
|
@ -10,6 +10,60 @@ module Vagrant
|
||||||
# @param [Proc] config_proc
|
# @param [Proc] config_proc
|
||||||
# @return [Object]
|
# @return [Object]
|
||||||
def self.load(config_proc)
|
def self.load(config_proc)
|
||||||
|
# Create a root configuration object
|
||||||
|
root = new_root_object
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Merges two configuration objects.
|
||||||
|
#
|
||||||
|
# @param [V1::Root] old The older root config.
|
||||||
|
# @param [V1::Root] new The newer root config.
|
||||||
|
# @return [V1::Root]
|
||||||
|
def self.merge(old, new)
|
||||||
|
# Grab the internal states, we use these heavily throughout the process
|
||||||
|
old_state = old.__internal_state
|
||||||
|
new_state = new.__internal_state
|
||||||
|
|
||||||
|
# The config map for the new object is the old one merged with the
|
||||||
|
# new one.
|
||||||
|
config_map = old_state["config_map"].merge(new_state["config_map"])
|
||||||
|
|
||||||
|
# Merge the keys.
|
||||||
|
old_keys = old_state["keys"]
|
||||||
|
new_keys = new_state["keys"]
|
||||||
|
keys = {}
|
||||||
|
old_keys.each do |key, old|
|
||||||
|
if new_keys.has_key?(key)
|
||||||
|
# We need to do a merge, which we expect to be available
|
||||||
|
# on the config class itself.
|
||||||
|
keys[key] = old.merge(new_keys[key])
|
||||||
|
else
|
||||||
|
# We just take the old value, but dup it so that we can modify.
|
||||||
|
keys[key] = old.dup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
new_keys.each do |key, new|
|
||||||
|
# Add in the keys that the new class has that we haven't merged.
|
||||||
|
if !keys.has_key?(key)
|
||||||
|
keys[key] = new.dup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the final root object
|
||||||
|
V1::Root.new(config_map, keys)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def self.new_root_object
|
||||||
# Get all the registered plugins
|
# Get all the registered plugins
|
||||||
config_map = {}
|
config_map = {}
|
||||||
Vagrant.plugin("1").registered.each do |plugin|
|
Vagrant.plugin("1").registered.each do |plugin|
|
||||||
|
@ -19,14 +73,7 @@ module Vagrant
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create the configuration root object
|
# Create the configuration root object
|
||||||
root = V1::Root.new(config_map)
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,8 +8,8 @@ module Vagrant
|
||||||
# configuration classes.
|
# configuration classes.
|
||||||
#
|
#
|
||||||
# @param [Hash] config_map Map of key to config class.
|
# @param [Hash] config_map Map of key to config class.
|
||||||
def initialize(config_map)
|
def initialize(config_map, keys=nil)
|
||||||
@keys = {}
|
@keys = keys || {}
|
||||||
@config_map = config_map
|
@config_map = config_map
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -29,6 +29,17 @@ module Vagrant
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns the internal state of the root object. This is used
|
||||||
|
# by outside classes when merging, and shouldn't be called directly.
|
||||||
|
# Note the strange method name is to attempt to avoid any name
|
||||||
|
# clashes with potential configuration keys.
|
||||||
|
def __internal_state
|
||||||
|
{
|
||||||
|
"config_map" => @config_map,
|
||||||
|
"keys" => @keys
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,4 +17,18 @@ describe Vagrant::Config::V1::Root do
|
||||||
instance = described_class.new({})
|
instance = described_class.new({})
|
||||||
expect { instance.foo }.to raise_error(NoMethodError)
|
expect { instance.foo }.to raise_error(NoMethodError)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can be created with initial state" do
|
||||||
|
instance = described_class.new({}, { :foo => "bar" })
|
||||||
|
instance.foo.should == "bar"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return internal state" do
|
||||||
|
map = { "foo" => Object, "bar" => Object }
|
||||||
|
instance = described_class.new(map)
|
||||||
|
instance.__internal_state.should == {
|
||||||
|
"config_map" => map,
|
||||||
|
"keys" => {}
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,4 +31,51 @@ describe Vagrant::Config::V1 do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "merging" do
|
||||||
|
it "should merge available configuration keys" do
|
||||||
|
old = Vagrant::Config::V1::Root.new({ :foo => Object })
|
||||||
|
new = Vagrant::Config::V1::Root.new({ :bar => Object })
|
||||||
|
result = described_class.merge(old, new)
|
||||||
|
result.foo.should be_kind_of(Object)
|
||||||
|
result.bar.should be_kind_of(Object)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should merge instantiated objects" do
|
||||||
|
config_class = Class.new do
|
||||||
|
attr_accessor :value
|
||||||
|
end
|
||||||
|
|
||||||
|
old = Vagrant::Config::V1::Root.new({ :foo => config_class })
|
||||||
|
old.foo.value = "old"
|
||||||
|
|
||||||
|
new = Vagrant::Config::V1::Root.new({ :bar => config_class })
|
||||||
|
new.bar.value = "new"
|
||||||
|
|
||||||
|
result = described_class.merge(old, new)
|
||||||
|
result.foo.value.should == "old"
|
||||||
|
result.bar.value.should == "new"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should merge conflicting classes by calling `merge`" do
|
||||||
|
config_class = Class.new do
|
||||||
|
attr_accessor :value
|
||||||
|
|
||||||
|
def merge(new)
|
||||||
|
result = self.class.new
|
||||||
|
result.value = @value + new.value
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
old = Vagrant::Config::V1::Root.new({ :foo => config_class })
|
||||||
|
old.foo.value = 10
|
||||||
|
|
||||||
|
new = Vagrant::Config::V1::Root.new({ :foo => config_class })
|
||||||
|
new.foo.value = 15
|
||||||
|
|
||||||
|
result = described_class.merge(old, new)
|
||||||
|
result.foo.value.should == 25
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue