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