summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Keiser <jkeiser@opscode.com>2013-12-02 23:43:58 -0800
committerJohn Keiser <jkeiser@opscode.com>2013-12-02 23:43:58 -0800
commit24eb243b25d75f8cbd1bf2955509bad6dfe3af5b (patch)
tree6da7f23302c8f33deec0540db8c714bfaad2f725
parentfa0ed189b89a279ea9ce0b709f90a5704835952a (diff)
downloadmixlib-config-24eb243b25d75f8cbd1bf2955509bad6dfe3af5b.tar.gz
Add save/restore for tests/serialization
-rw-r--r--lib/mixlib/config.rb94
-rw-r--r--lib/mixlib/config/configurable.rb24
-rw-r--r--spec/mixlib/config_spec.rb284
3 files changed, 393 insertions, 9 deletions
diff --git a/lib/mixlib/config.rb b/lib/mixlib/config.rb
index 138eb5f..7b96fe0 100644
--- a/lib/mixlib/config.rb
+++ b/lib/mixlib/config.rb
@@ -31,7 +31,7 @@ module Mixlib
class << base; attr_accessor :config_parent; end
base.configuration = Hash.new
base.configurables = Hash.new
- base.config_contexts = Array.new
+ base.config_contexts = Hash.new
end
# Loads a given ruby file, and runs instance_eval against it in the context of the current
@@ -105,21 +105,97 @@ module Mixlib
# Resets all config options to their defaults.
def reset
self.configuration = Hash.new
- self.config_contexts.each { |config_context| config_context.reset }
+ self.config_contexts.values.each { |config_context| config_context.reset }
+ end
+
+ # Makes a copy of any non-default values.
+ #
+ # This returns a shallow copy of the hash; while the hash itself is
+ # duplicated a la dup, modifying data inside arrays and hashes may modify
+ # the original Config object.
+ #
+ # === Returns
+ #
+ # Hash of values the user has set.
+ #
+ # For example, this config class:
+ #
+ # class MyConfig << Mixlib::Config
+ # default :will_be_set, 1
+ # default :will_be_set_to_default, 1
+ # default :will_not_be_set, 1
+ # configurable(:computed_value) { |x| x*2 }
+ # config_context :group do
+ # default :will_not_be_set, 1
+ # end
+ # config_context :group_never_set
+ # end
+ #
+ # MyConfig.x = 2
+ # MyConfig.will_be_set = 2
+ # MyConfig.will_be_set_to_default = 1
+ # MyConfig.computed_value = 2
+ # MyConfig.group.x = 3
+ # MyConfig.save
+ #
+ # produces this:
+ #
+ # {
+ # :x => 2,
+ # :will_be_set => 2,
+ # :will_be_set_to_default => 1,
+ # :computed_value => 4,
+ # :group => {
+ # :x => 3
+ # }
+ # }
+ #
+ def save(include_defaults = false)
+ result = self.configuration.dup
+ (self.configurables.keys - result.keys).each do |missing_default|
+ # Ask any configurables to save themselves into the result array
+ self.configurables[missing_default].save(self.configuration, result, include_defaults)
+ end
+ result
+ end
+
+ # Restore non-default values from the given hash.
+ #
+ # This method is the equivalent of +reset+ followed by +merge!(hash)+.
+ #
+ # === Parameters
+ # hash<Hash>: a hash in the same format as output by save.
+ #
+ # === Returns
+ # self
+ def restore(hash)
+ reset
+ merge!(hash)
end
# Merge an incoming hash with our config options
#
# === Parameters
- # hash<Hash>:: The incoming hash
+ # hash<Hash>: a hash in the same format as output by save.
#
# === Returns
- # result of Hash#merge!
+ # self
def merge!(hash)
- self.configuration.merge!(hash)
+ hash.each do |key, value|
+ if self.config_contexts.has_key?(key)
+ # Grab the config context and let internal_get cache it if so desired
+ internal_get(key).restore(value)
+ else
+ self.configuration[key] = value
+ end
+ end
+ self
end
- # Return the set of config hash keys
+ # Return the set of config hash keys.
+ # This *only* returns hash keys which have been set by the user. In future
+ # versions this will likely be removed in favor of something more explicit.
+ # For now though, we want this to match has_key?
#
# === Returns
# result of Hash#keys
@@ -128,11 +204,13 @@ module Mixlib
end
# Creates a shallow copy of the internal hash
+ # NOTE: remove this in 3.0 in favor of save. This is completely useless
+ # with default values and configuration_context.
#
# === Returns
# result of Hash#dup
def hash_dup
- self.configuration.dup
+ save
end
# metaprogramming to ensure that the slot for method_symbol
@@ -210,7 +288,7 @@ module Mixlib
context = Class.new
context.extend(::Mixlib::Config)
context.config_parent = self
- config_contexts << context
+ config_contexts[symbol] = context
if block
context.instance_eval(&block)
end
diff --git a/lib/mixlib/config/configurable.rb b/lib/mixlib/config/configurable.rb
index 01de2bd..02081df 100644
--- a/lib/mixlib/config/configurable.rb
+++ b/lib/mixlib/config/configurable.rb
@@ -22,11 +22,13 @@ module Mixlib
def initialize(symbol)
@symbol = symbol
@default = nil
+ @has_default = false
@default_value = nil
@writes_value = nil
end
def defaults_to(default_value = nil, &block)
+ @has_default = true
@default = block
@default_value = default_value
self
@@ -42,9 +44,13 @@ module Mixlib
config[@symbol]
elsif @default
@default.call
+ elsif @default_value.is_a?(::Mixlib::Config)
+ # Don't dup config_contexts
+ @default_value
else
begin
# Some things cannot be dup'd, and you won't know this till after the fact
+ # because all values implement dup
config[@symbol] = @default_value.dup
rescue TypeError
@default_value
@@ -55,6 +61,24 @@ module Mixlib
def set(config, value)
config[@symbol] = @writes_value ? @writes_value.call(value) : value
end
+
+ # Sets the saved value into the result hash. Don't bother stashing defaults away or duping them.
+ def save(config, result, include_defaults)
+ if config.has_key?(@symbol)
+ result[@symbol] = config[@symbol]
+ elsif @default_value.is_a?(::Mixlib::Config)
+ saved_context = @default_value.save(include_defaults)
+ if saved_context != {} || include_defaults
+ result[@symbol] = saved_context
+ end
+ elsif include_defaults && @has_default
+ if @default
+ result[@symbol] = @default.call
+ else
+ result[@symbol] = @default_value
+ end
+ end
+ end
end
end
end
diff --git a/spec/mixlib/config_spec.rb b/spec/mixlib/config_spec.rb
index f955eae..b58dc97 100644
--- a/spec/mixlib/config_spec.rb
+++ b/spec/mixlib/config_spec.rb
@@ -275,6 +275,32 @@ describe Mixlib::Config do
@klass.reset
@klass.attr.should == 4
end
+
+ it "save should not save anything for it" do
+ @klass.save.should == {}
+ end
+
+ it "save with include_defaults should save all defaults" do
+ @klass.save(true).should == { :attr => 4 }
+ end
+
+ it "save should save the new value if it gets set" do
+ @klass.attr 5
+ (saved = @klass.save).should == { :attr => 5 }
+ @klass.reset
+ @klass.attr.should == 4
+ @klass.restore(saved)
+ @klass.attr.should == 5
+ end
+
+ it "save should save the new value even if it is set to its default value" do
+ @klass.attr 4
+ (saved = @klass.save).should == { :attr => 4 }
+ @klass.reset
+ @klass.save.should == {}
+ @klass.restore(saved)
+ @klass.save.should == { :attr => 4 }
+ end
end
describe "When config has a default value block" do
@@ -283,7 +309,7 @@ describe Mixlib::Config do
@klass.extend(::Mixlib::Config)
@klass.class_eval do
default :x, 4
- default(:attr) { x*2}
+ default(:attr) { x*2 }
end
end
@@ -317,6 +343,32 @@ describe Mixlib::Config do
@klass.reset
@klass.attr.should == 8
end
+
+ it "save should not save anything for it" do
+ @klass.save.should == {}
+ end
+
+ it "save with include_defaults should save all defaults" do
+ @klass.save(true).should == { :attr => 8, :x => 4 }
+ end
+
+ it "save should save the new value if it gets set" do
+ @klass.attr 5
+ (saved = @klass.save).should == { :attr => 5 }
+ @klass.reset
+ @klass.attr.should == 8
+ @klass.restore(saved)
+ @klass.attr.should == 5
+ end
+
+ it "save should save the new value even if it is set to its default value" do
+ @klass.attr 8
+ (saved = @klass.save).should == { :attr => 8 }
+ @klass.reset
+ @klass.save.should == {}
+ @klass.restore(saved)
+ @klass.save.should == { :attr => 8 }
+ end
end
describe "When config has an array default value" do
@@ -332,6 +384,32 @@ describe Mixlib::Config do
@klass.reset
@klass.attr.should == []
end
+
+ it "save should not save anything for it" do
+ @klass.save.should == {}
+ end
+
+ it "save with include_defaults should save all defaults" do
+ @klass.save(true).should == { :attr => [] }
+ end
+
+ it "save should save the new value if it gets set" do
+ @klass.attr << 'x'
+ (saved = @klass.save).should == { :attr => [ 'x' ] }
+ @klass.reset
+ @klass.attr.should == []
+ @klass.restore(saved)
+ @klass.attr.should == [ 'x' ]
+ end
+
+ it "save should save the new value even if it is set to its default value" do
+ @klass.attr = []
+ (saved = @klass.save).should == { :attr => [] }
+ @klass.reset
+ @klass.save.should == {}
+ @klass.restore(saved)
+ @klass.save.should == { :attr => [] }
+ end
end
describe "When config has a hash default value" do
@@ -347,6 +425,32 @@ describe Mixlib::Config do
@klass.reset
@klass.attr[:x].should == nil
end
+
+ it "save should not save anything for it" do
+ @klass.save.should == {}
+ end
+
+ it "save with include_defaults should save all defaults" do
+ @klass.save(true).should == { :attr => {} }
+ end
+
+ it "save should save the new value if it gets set" do
+ @klass.attr[:hi] = 'lo'
+ (saved = @klass.save).should == { :attr => { :hi => 'lo' } }
+ @klass.reset
+ @klass.attr.should == {}
+ @klass.restore(saved)
+ @klass.save.should == { :attr => { :hi => 'lo' } }
+ end
+
+ it "save should save the new value even if it is set to its default value" do
+ @klass.attr = {}
+ (saved = @klass.save).should == { :attr => {} }
+ @klass.reset
+ @klass.save.should == {}
+ @klass.restore(saved)
+ @klass.save.should == { :attr => {} }
+ end
end
describe "When config has a string default value" do
@@ -362,6 +466,32 @@ describe Mixlib::Config do
@klass.reset
@klass.attr.should == 'hello'
end
+
+ it "save should not save anything for it" do
+ @klass.save.should == {}
+ end
+
+ it "save with include_defaults should save all defaults" do
+ @klass.save(true).should == { :attr => 'hello' }
+ end
+
+ it "save should save the new value if it gets set" do
+ @klass.attr << ' world'
+ (saved = @klass.save).should == { :attr => 'hello world' }
+ @klass.reset
+ @klass.attr.should == 'hello'
+ @klass.restore(saved)
+ @klass.attr.should == 'hello world'
+ end
+
+ it "save should save the new value even if it is set to its default value" do
+ @klass.attr 'hello world'
+ (saved = @klass.save).should == { :attr => 'hello world' }
+ @klass.reset
+ @klass.save.should == {}
+ @klass.restore(saved)
+ @klass.save.should == { :attr => 'hello world' }
+ end
end
describe "When config has a a default value block" do
@@ -398,6 +528,32 @@ describe Mixlib::Config do
@klass.reset
@klass.attr.should == 4
end
+
+ it "save should not save anything for it" do
+ @klass.save.should == {}
+ end
+
+ it "save with include_defaults should save all defaults" do
+ @klass.save(true).should == { :attr => 4 }
+ end
+
+ it "save should save the new value if it gets set" do
+ @klass.attr 5
+ (saved = @klass.save).should == { :attr => 5 }
+ @klass.reset
+ @klass.attr.should == 4
+ @klass.restore(saved)
+ @klass.attr.should == 5
+ end
+
+ it "save should save the new value even if it is set to its default value" do
+ @klass.attr 4
+ (saved = @klass.save).should == { :attr => 4 }
+ @klass.reset
+ @klass.save.should == {}
+ @klass.restore(saved)
+ @klass.save.should == { :attr => 4 }
+ end
end
describe "When a configurable exists with writer and default value" do
@@ -449,6 +605,32 @@ describe Mixlib::Config do
@klass.reset
@klass.attr.should == 4
end
+
+ it "save should not save anything for it" do
+ @klass.save.should == {}
+ end
+
+ it "save with include_defaults should save all defaults" do
+ @klass.save(true).should == { :attr => 4 }
+ end
+
+ it "save should save the new value if it gets set" do
+ @klass.attr 5
+ (saved = @klass.save).should == { :attr => 10 }
+ @klass.reset
+ @klass.attr.should == 4
+ @klass.restore(saved)
+ @klass.attr.should == 10
+ end
+
+ it "save should save the new value even if it is set to its default value" do
+ @klass.attr 4
+ (saved = @klass.save).should == { :attr => 8 }
+ @klass.reset
+ @klass.save.should == {}
+ @klass.restore(saved)
+ @klass.save.should == { :attr => 8 }
+ end
end
describe "When a configurable exists with writer and default value set in chained form" do
@@ -497,6 +679,32 @@ describe Mixlib::Config do
@klass.reset
@klass.attr.should == 4
end
+
+ it "save should not save anything for it" do
+ @klass.save.should == {}
+ end
+
+ it "save with include_defaults should save all defaults" do
+ @klass.save(true).should == { :attr => 4 }
+ end
+
+ it "save should save the new value if it gets set" do
+ @klass.attr 5
+ (saved = @klass.save).should == { :attr => 10 }
+ @klass.reset
+ @klass.attr.should == 4
+ @klass.restore(saved)
+ @klass.attr.should == 10
+ end
+
+ it "save should save the new value even if it is set to its default value" do
+ @klass.attr 2
+ (saved = @klass.save).should == { :attr => 4 }
+ @klass.reset
+ @klass.save.should == {}
+ @klass.restore(saved)
+ @klass.save.should == { :attr => 4 }
+ end
end
describe "When a configurable exists with a context" do
@@ -532,6 +740,24 @@ describe Mixlib::Config do
@klass.reset
@klass.blah.x.should == 5
end
+
+ it "save should not save anything for it by default" do
+ @klass.save.should == {}
+ end
+
+ it "save with include_defaults should save all defaults" do
+ @klass.save(true).should == { :blah => { :x => 5 } }
+ end
+
+ it "save should save any new values that are set in the context" do
+ @klass.blah.x = 10
+ (saved = @klass.save).should == { :blah => { :x => 10 } }
+ @klass.reset
+ @klass.blah.x.should == 5
+ @klass.restore(saved)
+ @klass.blah.x.should == 10
+ @klass.save.should == { :blah => { :x => 10 } }
+ end
end
describe "When a configurable exists with a nested context" do
@@ -569,6 +795,62 @@ describe Mixlib::Config do
@klass.reset
@klass.blah.yarr.x.should == 5
end
+
+ it "save should not save anything for it by default" do
+ @klass.save.should == {}
+ end
+
+ it "save with include_defaults should save all defaults" do
+ @klass.save(true).should == { :blah => { :yarr => { :x => 5 } } }
+ end
+
+ it "save should save any new values that are set in the context" do
+ @klass.blah.yarr.x = 10
+ (saved = @klass.save).should == { :blah => { :yarr => { :x => 10 } } }
+ @klass.reset
+ @klass.blah.yarr.x.should == 5
+ @klass.restore(saved)
+ @klass.blah.yarr.x.should == 10
+ @klass.save.should == { :blah => { :yarr => { :x => 10 } } }
+ end
+ end
+
+ describe "When a config_context with no defaulted values exists" do
+ before :each do
+ @klass = Class.new
+ @klass.extend(::Mixlib::Config)
+ @klass.class_eval do
+ config_context(:blah) do
+ configurable(:x)
+ end
+ end
+ end
+
+ it "save does not save the hash for the config_context" do
+ @klass.save.should == {}
+ end
+
+ it "save with defaults saves the hash for the config_context" do
+ @klass.save(true).should == { :blah => {} }
+ end
+ end
+
+ describe "When a config_context with no configurables exists" do
+ before :each do
+ @klass = Class.new
+ @klass.extend(::Mixlib::Config)
+ @klass.class_eval do
+ config_context(:blah)
+ end
+ end
+
+ it "save does not save the hash for the config_context" do
+ @klass.save.should == {}
+ end
+
+ it "save with defaults saves the hash for the config_context" do
+ @klass.save(true).should == { :blah => {} }
+ end
end
describe "When a nested context has strict mode on" do