Config merging
This commit is contained in:
parent
92ee042fc2
commit
c0ee3b06ff
|
@ -10,6 +10,60 @@ module Vagrant
|
|||
# @param [Proc] config_proc
|
||||
# @return [Object]
|
||||
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
|
||||
config_map = {}
|
||||
Vagrant.plugin("1").registered.each do |plugin|
|
||||
|
@ -19,14 +73,7 @@ module Vagrant
|
|||
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
|
||||
V1::Root.new(config_map)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,8 +8,8 @@ module Vagrant
|
|||
# configuration classes.
|
||||
#
|
||||
# @param [Hash] config_map Map of key to config class.
|
||||
def initialize(config_map)
|
||||
@keys = {}
|
||||
def initialize(config_map, keys=nil)
|
||||
@keys = keys || {}
|
||||
@config_map = config_map
|
||||
end
|
||||
|
||||
|
@ -29,6 +29,17 @@ module Vagrant
|
|||
super
|
||||
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
|
||||
|
|
|
@ -17,4 +17,18 @@ describe Vagrant::Config::V1::Root do
|
|||
instance = described_class.new({})
|
||||
expect { instance.foo }.to raise_error(NoMethodError)
|
||||
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
|
||||
|
|
|
@ -31,4 +31,51 @@ describe Vagrant::Config::V1 do
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue