1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
module LeapCli
module Config
#
# This class represents the configuration for a single node, service, or tag.
#
class Base < Hash
def initialize(manager=nil, node=nil)
@manager = manager
@node = node || self
end
##
## FETCHING VALUES
##
#
# lazily eval dynamic values when we encounter them.
#
def [](key)
value = fetch(key, nil)
if value.is_a? Array
value
elsif value.nil?
nil
else
if value =~ /^= (.*)$/
begin
value = eval($1)
self[key] = value
rescue Exception => exc
puts "Eval error in '#{name}'"
puts " string: #{$1}"
puts " error: #{exc}"
end
end
value
end
end
def name
@node['name']
end
#
# make hash addressable like an object (e.g. obj['name'] available as obj.name)
#
def method_missing(method, *args, &block)
method = method.to_s
if self.has_key?(method)
self[method]
elsif @node != self
@node.send(method) # send call up the tree...
else
raise NoMethodError.new(method)
end
end
#
# a deep (recursive) merge with another hash or node.
#
def deep_merge!(hsh)
hsh.each do |key,new_value|
old_value = self[key]
if old_value.is_a?(Hash) || new_value.is_a?(Hash)
# merge hashes
value = Base.new(@manager, @node)
old_value.is_a?(Hash) ? value.deep_merge!(old_value) : (value[key] = old_value if old_value.any?)
new_value.is_a?(Hash) ? value.deep_merge!(new_value) : (value[key] = new_value if new_value.any?)
elsif old_value.is_a?(Array) || new_value.is_a?(Array)
# merge arrays
value = []
old_value.is_a?(Array) ? value += old_value : value << old_value
new_value.is_a?(Array) ? value += new_value : value << new_value
value.compact!
elsif new_value.nil?
value = old_value
elsif old_value.nil?
value = new_value
elsif old_value.is_a?(Boolean) && new_value.is_a?(Boolean)
value = new_value
elsif old_value.class != new_value.class
raise 'Type mismatch. Cannot merge %s with %s. Key value is %s, name is %s.' % [old_value.class, new_value.class, key, name]
else
value = new_value
end
self[key] = value
end
self
end
#def deep_merge!(new_node)
# new_node.each do |key, value|
# if value.is_a? self.class
# value = Base.new(@manager, @node).deep_merge!(value)
# self[key] = new_node[key]
# end
# self
#end
#
# like a normal deep_merge, but replace any hash it encounters with a Config::Base
#
#def deep_merge(other_hash)
# p [self['name'], other_hash['name']]
# self.merge(other_hash) do |key, oldval, newval|
# oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
# newval = newval.to_hash if newval.respond_to?(:to_hash)
# p key
# p oldval.class
# p newval.class
# if oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash'
# oldval.deep_merge(newval)
# elsif newval.class.to_s == 'Hash'
# p key
# Base.new(@manager, node).replace(newval)
# else
# newval
# end
# end
#end
#
#def deep_merge!(other_hash)
# replace(deep_merge(other_hash))
#end
private
##
## MACROS
## these are methods used when eval'ing a value in the .json configuration
##
#
# inserts the contents of a file
#
def file(filename)
filepath = Path.find_file(name, filename)
if filepath
File.read(filepath)
else
log0('no such file, "%s"' % filename)
""
end
end
end # class
end # module
end # module
|