lib/leap_cli/config/object.rb in leap_cli-1.5.6 vs lib/leap_cli/config/object.rb in leap_cli-1.6.2

- old
+ new

@@ -6,24 +6,20 @@ if $ruby_version < [1,9] $KCODE = 'UTF8' end require 'ya2yaml' # pure ruby yaml -require 'leap_cli/config/macros' - module LeapCli module Config # # This class represents the configuration for a single node, service, or tag. # Also, all the nested hashes are also of this type. # # It is called 'object' because it corresponds to an Object in JSON. # class Object < Hash - include Config::Macros - attr_reader :node attr_reader :manager alias :global :manager def initialize(manager=nil, node=nil) @@ -42,11 +38,11 @@ # allows us greater compatibility regardless of installed ruby version and # greater control over how the yaml is exported (sorted keys, in particular). # def dump_yaml evaluate(@node) - ya2yaml(:syck_compatible => true) + sorted_ya2yaml(:syck_compatible => true) end # # export JSON # @@ -66,10 +62,15 @@ def [](key) get(key) end + # Overrride some default methods in Hash that are likely to + # be used as attributes. + alias_method :hkey, :key + def key; get('key'); end + # # make hash addressable like an object (e.g. obj['name'] available as obj.name) # def method_missing(method, *args, &block) get!(method) @@ -132,11 +133,22 @@ # - If both old and new values are arrays, the new one replaces the old. # - If one of the values is simple but the other is an array, the simple is added to the array. # def deep_merge!(object, prefer_self=false) object.each do |key,new_value| - old_value = self.fetch key, nil + if self.has_key?('+'+key) + mode = :add + old_value = self.fetch '+'+key, nil + self.delete('+'+key) + elsif self.has_key?('-'+key) + mode = :subtract + old_value = self.fetch '-'+key, nil + self.delete('-'+key) + else + mode = :normal + old_value = self.fetch key, nil + end # clean up boolean new_value = true if new_value == "true" new_value = false if new_value == "false" old_value = true if old_value == "true" @@ -158,19 +170,31 @@ elsif old_value.is_a?(Array) && !new_value.is_a?(Array) (value = (old_value.dup << new_value).compact.uniq).delete('REQUIRED') elsif new_value.is_a?(Array) && !old_value.is_a?(Array) (value = (new_value.dup << old_value).compact.uniq).delete('REQUIRED') + # merge two arrays + elsif old_value.is_a?(Array) && new_value.is_a?(Array) + if mode == :add + value = (old_value + new_value).sort.uniq + elsif mode == :subtract + value = new_value - old_value + elsif prefer_self + value = old_value + else + value = new_value + end + # catch errors elsif type_mismatch?(old_value, new_value) raise 'Type mismatch. Cannot merge %s (%s) with %s (%s). Key is "%s", name is "%s".' % [ old_value.inspect, old_value.class, new_value.inspect, new_value.class, key, self.class ] - # merge strings, numbers, and sometimes arrays + # merge simple strings & numbers else if prefer_self value = old_value else value = new_value @@ -204,10 +228,14 @@ end hsh end end + def eval_file(filename) + evaluate_ruby(filename, File.read(filename)) + end + protected # # walks the object tree, eval'ing all the attributes that are dynamic ruby (e.g. value starts with '= ') # @@ -244,48 +272,45 @@ # # evaluates the string `value` as ruby in the context of self. # (`key` is just passed for debugging purposes) # def evaluate_ruby(key, value) - result = nil - if LeapCli.log_level >= 2 - result = self.instance_eval(value) - else - begin - result = self.instance_eval(value) - rescue SystemStackError => exc - Util::log 0, :error, "while evaluating node '#{self.name}'" - Util::log 0, "offending key: #{key}", :indent => 1 - Util::log 0, "offending string: #{value}", :indent => 1 - Util::log 0, "STACK OVERFLOW, BAILING OUT. There must be an eval loop of death (variables with circular dependencies).", :indent => 1 - raise SystemExit.new(1) - rescue FileMissing => exc - Util::bail! do - if exc.options[:missing] - Util::log :missing, exc.options[:missing].gsub('$node', self.name) - else - Util::log :error, "while evaluating node '#{self.name}'" - Util::log "offending key: #{key}", :indent => 1 - Util::log "offending string: #{value}", :indent => 1 - Util::log "error message: no file '#{exc}'", :indent => 1 - end - end - rescue AssertionFailed => exc - Util.bail! do - Util::log :failed, "assertion while evaluating node '#{self.name}'" - Util::log 'assertion: %s' % exc.assertion, :indent => 1 - Util::log "offending key: #{key}", :indent => 1 - end - rescue SyntaxError, StandardError => exc - Util::bail! do - Util::log :error, "while evaluating node '#{self.name}'" - Util::log "offending key: #{key}", :indent => 1 - Util::log "offending string: #{value}", :indent => 1 - Util::log "error message: #{exc.inspect}", :indent => 1 - end + self.instance_eval(value, key, 1) + rescue ConfigError => exc + raise exc # pass through + rescue SystemStackError => exc + Util::log 0, :error, "while evaluating node '#{self.name}'" + Util::log 0, "offending key: #{key}", :indent => 1 + Util::log 0, "offending string: #{value}", :indent => 1 + Util::log 0, "STACK OVERFLOW, BAILING OUT. There must be an eval loop of death (variables with circular dependencies).", :indent => 1 + raise SystemExit.new(1) + rescue FileMissing => exc + Util::bail! do + if exc.options[:missing] + Util::log :missing, exc.options[:missing].gsub('$node', self.name).gsub('$file', exc.path) + else + Util::log :error, "while evaluating node '#{self.name}'" + Util::log "offending key: #{key}", :indent => 1 + Util::log "offending string: #{value}", :indent => 1 + Util::log "error message: no file '#{exc}'", :indent => 1 end + raise exc if DEBUG end - return result + rescue AssertionFailed => exc + Util.bail! do + Util::log :failed, "assertion while evaluating node '#{self.name}'" + Util::log 'assertion: %s' % exc.assertion, :indent => 1 + Util::log "offending key: #{key}", :indent => 1 + raise exc if DEBUG + end + rescue SyntaxError, StandardError => exc + Util::bail! do + Util::log :error, "while evaluating node '#{self.name}'" + Util::log "offending key: #{key}", :indent => 1 + Util::log "offending string: #{value}", :indent => 1 + Util::log "error message: #{exc.inspect}", :indent => 1 + raise exc if DEBUG + end end private # \ No newline at end of file