configtoolkit 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/README.txt +40 -0
- data/Rakefile +1 -1
- data/lib/configtoolkit/baseconfig.rb +93 -21
- data/test/machines.yaml +80 -0
- data/test/test_baseconfig.rb +81 -50
- metadata +2 -2
data/History.txt
CHANGED
data/README.txt
CHANGED
@@ -211,6 +211,46 @@ This will produce the following apple.yaml:
|
|
211
211
|
num_cpus: 32
|
212
212
|
behind_firewall: false
|
213
213
|
|
214
|
+
To load a group of named configurations at once from this machines.yaml file:
|
215
|
+
db_cluster:
|
216
|
+
db1:
|
217
|
+
name: cherry
|
218
|
+
architecture: powerpc
|
219
|
+
os: Linux
|
220
|
+
num_cpus: 24
|
221
|
+
contains_sensitive_data: yes
|
222
|
+
addresses:
|
223
|
+
- http://cherrydb.designingpatterns.com
|
224
|
+
- http://db1.designingpatterns.com
|
225
|
+
db2:
|
226
|
+
name: strawberry
|
227
|
+
architecture: powerpc
|
228
|
+
os: Linux
|
229
|
+
num_cpus: 24
|
230
|
+
contains_sensitive_data: yes
|
231
|
+
addresses:
|
232
|
+
- http://strawberrydb.designingpatterns.com
|
233
|
+
- http://db2.designingpatterns.com
|
234
|
+
db3:
|
235
|
+
name: blueberry
|
236
|
+
architecture: powerpc
|
237
|
+
os: Linux
|
238
|
+
num_cpus: 24
|
239
|
+
contains_sensitive_data: yes
|
240
|
+
addresses:
|
241
|
+
- http://blueberrydb.designingpatterns.com
|
242
|
+
- http://db3.designingpatterns.com
|
243
|
+
|
244
|
+
do:
|
245
|
+
require 'rubygems'
|
246
|
+
require 'configtoolkit'
|
247
|
+
require 'configtoolkit/yamlreader'
|
248
|
+
|
249
|
+
configs = MachineConfig.load_group(ConfigToolkit::YAMLReader.new("machines.yaml"), "db_cluster")
|
250
|
+
configs.each do |name, config|
|
251
|
+
print "The #{name} configuration:\n#{config}\n"
|
252
|
+
end
|
253
|
+
|
214
254
|
== REQUIREMENTS:
|
215
255
|
|
216
256
|
Hoe is required, but only for running the tests.
|
data/Rakefile
CHANGED
@@ -287,8 +287,7 @@ class BaseConfig
|
|
287
287
|
# webserver, then the containing_object_name would be
|
288
288
|
# 'production.webserver'.
|
289
289
|
#
|
290
|
-
|
291
|
-
protected :containing_object_name= # This is NOT part of the public interface
|
290
|
+
attr_reader :containing_object_name
|
292
291
|
|
293
292
|
#
|
294
293
|
# ====Description:
|
@@ -651,8 +650,7 @@ class BaseConfig
|
|
651
650
|
nested_containing_object_name = "#{@containing_object_name}.#{param_name}"
|
652
651
|
end
|
653
652
|
|
654
|
-
child_config.
|
655
|
-
child_config.load_impl(raw_value)
|
653
|
+
child_config.load_impl(raw_value, nested_containing_object_name)
|
656
654
|
return child_config
|
657
655
|
elsif((value_class < ConstrainedArray) && (raw_value.class == Array))
|
658
656
|
#
|
@@ -735,8 +733,11 @@ class BaseConfig
|
|
735
733
|
# ====Parameters:
|
736
734
|
# [containing_object_hash]
|
737
735
|
# The Hash containing the configuration's parameters
|
736
|
+
# [containing_object_name]
|
737
|
+
# The containing object name
|
738
738
|
#
|
739
|
-
def load_impl(containing_object_hash)
|
739
|
+
def load_impl(containing_object_hash, containing_object_name)
|
740
|
+
@containing_object_name = containing_object_name
|
740
741
|
@params_with_values.clear()
|
741
742
|
|
742
743
|
if(containing_object_hash != nil)
|
@@ -766,20 +767,21 @@ class BaseConfig
|
|
766
767
|
|
767
768
|
#
|
768
769
|
# ====Description:
|
769
|
-
# This method loads
|
770
|
-
# containing_object_name
|
770
|
+
# This method loads the configuration hash from reader for containing object
|
771
|
+
# containing_object_name. This method is not really part of BaseConfig's
|
772
|
+
# public API but, unfortunately, cannot be made private (since it is
|
773
|
+
# called from an instance method; there are no "protected" class methods,
|
774
|
+
# unfortunately).
|
771
775
|
#
|
772
776
|
# ====Parameters:
|
773
|
-
#
|
774
|
-
# The reader from which to load the parameter values. This method
|
775
|
-
# will call the read method of reader, which will return
|
776
|
-
# a Hash containing, in the most nested containing object entry,
|
777
|
-
# the configuration parameter values for self.
|
778
|
-
# [containing_object_name = ""]
|
779
|
-
# The containing object name
|
777
|
+
# See the parameter list for load.
|
780
778
|
#
|
781
|
-
|
782
|
-
|
779
|
+
# ====Returns:
|
780
|
+
# The configuration hash for containing_object_name (or nil, if none
|
781
|
+
# is found)
|
782
|
+
#
|
783
|
+
def self.load_containing_object_hash(reader, containing_object_name)
|
784
|
+
param_hash = reader.read()
|
783
785
|
|
784
786
|
if(param_hash.class != Hash)
|
785
787
|
message = "#{reader.class}#read returned #{param_hash.class} "
|
@@ -801,15 +803,14 @@ class BaseConfig
|
|
801
803
|
# is empty). On the other hand, if an object is found but it is not
|
802
804
|
# really an object (a Hash), then raise an exception.
|
803
805
|
#
|
804
|
-
@containing_object_name = containing_object_name
|
805
806
|
containing_object_hash = param_hash
|
806
|
-
|
807
|
+
containing_object_name.split(".").each do |object_name|
|
807
808
|
containing_object_hash = containing_object_hash.fetch(object_name, nil)
|
808
809
|
|
809
810
|
if(containing_object_hash == nil)
|
810
811
|
break
|
811
812
|
elsif(containing_object_hash.class != Hash)
|
812
|
-
message = "error: #{self.
|
813
|
+
message = "error: #{self}.load_containing_object_hash found "
|
813
814
|
message << "#{containing_object_hash.class} "
|
814
815
|
message << "rather than Hash when reading the "
|
815
816
|
message << "#{object_name} containing object"
|
@@ -817,7 +818,32 @@ class BaseConfig
|
|
817
818
|
end
|
818
819
|
end
|
819
820
|
|
820
|
-
|
821
|
+
return containing_object_hash
|
822
|
+
end
|
823
|
+
private_class_method :load_containing_object_hash
|
824
|
+
|
825
|
+
#
|
826
|
+
# ====Description:
|
827
|
+
# This method loads self from reader (with containing object
|
828
|
+
# containing_object_name).
|
829
|
+
#
|
830
|
+
# ====Parameters:
|
831
|
+
# [reader]
|
832
|
+
# The reader from which to load the parameter values. This method
|
833
|
+
# will call the read method of reader, which will return
|
834
|
+
# a Hash containing, in the most nested containing object entry,
|
835
|
+
# the configuration parameter values for self.
|
836
|
+
# [containing_object_name = ""]
|
837
|
+
# The containing object name
|
838
|
+
#
|
839
|
+
def load(reader, containing_object_name = "")
|
840
|
+
#
|
841
|
+
# Not being able to call private class methods from one of the
|
842
|
+
# class' instance methods is a language bug.
|
843
|
+
#
|
844
|
+
containing_object_hash = self.class.send(:load_containing_object_hash, reader, containing_object_name)
|
845
|
+
|
846
|
+
load_impl(containing_object_hash, containing_object_name)
|
821
847
|
end
|
822
848
|
|
823
849
|
#
|
@@ -837,6 +863,52 @@ class BaseConfig
|
|
837
863
|
instance.load(reader, containing_object_name)
|
838
864
|
return instance
|
839
865
|
end
|
866
|
+
|
867
|
+
#
|
868
|
+
# ====Description:
|
869
|
+
# This class method loads arbitrarily many configs from
|
870
|
+
# reader. This should be used when containing_object_name's
|
871
|
+
# elements all are hashes, each of which is a different instance of
|
872
|
+
# this class' configuration. This call returns a Hash containing
|
873
|
+
# elements mapping configuration name to configuration
|
874
|
+
# instance.
|
875
|
+
#
|
876
|
+
# ====Parameters:
|
877
|
+
# See the parameter list for load.
|
878
|
+
#
|
879
|
+
# ====Returns:
|
880
|
+
# A Hash of names (Strings) mapping to loaded configuration instances
|
881
|
+
#
|
882
|
+
def self.load_group(reader, containing_object_name = "")
|
883
|
+
containing_object_hash = load_containing_object_hash(reader, containing_object_name)
|
884
|
+
|
885
|
+
config_group = {}
|
886
|
+
if(containing_object_hash != nil)
|
887
|
+
containing_object_hash.each do |key, value|
|
888
|
+
value_containing_object_name = "#{containing_object_name}.#{key}"
|
889
|
+
|
890
|
+
if(!value.is_a?(Hash))
|
891
|
+
message = "error: #{self}.load_group found "
|
892
|
+
message << "#{value.class} "
|
893
|
+
message << "rather than Hash when reading the "
|
894
|
+
message << "#{value_containing_object_name} containing object"
|
895
|
+
raise Error, message
|
896
|
+
end
|
897
|
+
|
898
|
+
instance = new()
|
899
|
+
|
900
|
+
#
|
901
|
+
# Have to use send in order to call protected method.
|
902
|
+
# I think that not being able to call protected instance methods
|
903
|
+
# from class methods is a bug.
|
904
|
+
#
|
905
|
+
instance.send(:load_impl, value, value_containing_object_name)
|
906
|
+
config_group[key] = instance
|
907
|
+
end
|
908
|
+
end
|
909
|
+
|
910
|
+
return config_group
|
911
|
+
end
|
840
912
|
|
841
913
|
#
|
842
914
|
# ====Description:
|
@@ -862,7 +934,7 @@ class BaseConfig
|
|
862
934
|
str << "{\n"
|
863
935
|
value.write_impl(str, nesting_indent)
|
864
936
|
str << indent << "}"
|
865
|
-
elsif(value.
|
937
|
+
elsif(value.is_a?(Array))
|
866
938
|
#
|
867
939
|
# Writing an array requires calling write_value_impl for each element,
|
868
940
|
# just at one further indentation level.
|
data/test/machines.yaml
CHANGED
@@ -8,6 +8,17 @@ addresses:
|
|
8
8
|
- http://default.designingpatterns.com
|
9
9
|
- http://apple.designingpatterns.com
|
10
10
|
|
11
|
+
qa:
|
12
|
+
name: pear
|
13
|
+
architecture: x86_64
|
14
|
+
os: FreeBSD
|
15
|
+
num_cpus: 8
|
16
|
+
behind_firewall: true
|
17
|
+
contains_sensitive_data: true
|
18
|
+
addresses:
|
19
|
+
- http://release.designingpatterns.com
|
20
|
+
- http://pear.designingpatterns.com
|
21
|
+
|
11
22
|
production:
|
12
23
|
name: orange
|
13
24
|
architecture: ultrasparc
|
@@ -28,6 +39,69 @@ test:
|
|
28
39
|
- http://test.designingpatterns.com
|
29
40
|
- http://bananna.designingpatterns.com
|
30
41
|
|
42
|
+
#
|
43
|
+
# db1, db2, and db3 are duplicated at the top-level and
|
44
|
+
# within db_cluster, in order to properly test load_group.
|
45
|
+
#
|
46
|
+
db1:
|
47
|
+
name: cherry
|
48
|
+
architecture: powerpc
|
49
|
+
os: Linux
|
50
|
+
num_cpus: 24
|
51
|
+
contains_sensitive_data: yes
|
52
|
+
addresses:
|
53
|
+
- http://cherrydb.designingpatterns.com
|
54
|
+
- http://db1.designingpatterns.com
|
55
|
+
|
56
|
+
db2:
|
57
|
+
name: strawberry
|
58
|
+
architecture: powerpc
|
59
|
+
os: Linux
|
60
|
+
num_cpus: 24
|
61
|
+
contains_sensitive_data: yes
|
62
|
+
addresses:
|
63
|
+
- http://strawberrydb.designingpatterns.com
|
64
|
+
- http://db2.designingpatterns.com
|
65
|
+
|
66
|
+
db3:
|
67
|
+
name: blueberry
|
68
|
+
architecture: powerpc
|
69
|
+
os: Linux
|
70
|
+
num_cpus: 24
|
71
|
+
contains_sensitive_data: yes
|
72
|
+
addresses:
|
73
|
+
- http://blueberrydb.designingpatterns.com
|
74
|
+
- http://db3.designingpatterns.com
|
75
|
+
|
76
|
+
db_cluster1:
|
77
|
+
db1:
|
78
|
+
name: cherry
|
79
|
+
architecture: powerpc
|
80
|
+
os: Linux
|
81
|
+
num_cpus: 24
|
82
|
+
contains_sensitive_data: yes
|
83
|
+
addresses:
|
84
|
+
- http://cherrydb.designingpatterns.com
|
85
|
+
- http://db1.designingpatterns.com
|
86
|
+
db2:
|
87
|
+
name: strawberry
|
88
|
+
architecture: powerpc
|
89
|
+
os: Linux
|
90
|
+
num_cpus: 24
|
91
|
+
contains_sensitive_data: yes
|
92
|
+
addresses:
|
93
|
+
- http://strawberrydb.designingpatterns.com
|
94
|
+
- http://db2.designingpatterns.com
|
95
|
+
db3:
|
96
|
+
name: blueberry
|
97
|
+
architecture: powerpc
|
98
|
+
os: Linux
|
99
|
+
num_cpus: 24
|
100
|
+
contains_sensitive_data: yes
|
101
|
+
addresses:
|
102
|
+
- http://blueberrydb.designingpatterns.com
|
103
|
+
- http://db3.designingpatterns.com
|
104
|
+
|
31
105
|
bad_containing_object: 3
|
32
106
|
|
33
107
|
missing_num_cpus:
|
@@ -98,3 +172,9 @@ bad_contains_sensitive_data_behind_firewall_combo:
|
|
98
172
|
addresses:
|
99
173
|
- http://test.designingpatterns.com
|
100
174
|
- http://bananna.designingpatterns.com
|
175
|
+
|
176
|
+
bad_group:
|
177
|
+
bad_element:
|
178
|
+
- 3
|
179
|
+
- 4
|
180
|
+
- 5
|
data/test/test_baseconfig.rb
CHANGED
@@ -107,13 +107,13 @@ class BaseConfigTest < Test::Unit::TestCase
|
|
107
107
|
assert_equal(param_value, config.send(param_name))
|
108
108
|
end
|
109
109
|
|
110
|
-
|
111
|
-
|
110
|
+
#
|
111
|
+
# Ensure that the programatically set config is equal to one
|
112
|
+
# sourcing the same information via load.
|
113
|
+
#
|
114
|
+
assert_equal(MachineConfig.load(ConfigToolkit::YAMLReader.new(MACHINES_CONFIG_FILE_NAME), "qa"), config)
|
112
115
|
|
113
|
-
|
114
|
-
expected_config_param_values.each do |param_name, param_value|
|
115
|
-
assert_equal(param_value, config.send(param_name))
|
116
|
-
end
|
116
|
+
puts config
|
117
117
|
end
|
118
118
|
|
119
119
|
def verify_correct_dump(config_file_name, containing_object_name)
|
@@ -182,13 +182,25 @@ class BaseConfigTest < Test::Unit::TestCase
|
|
182
182
|
|
183
183
|
def verify_correct_load(config_file_name,
|
184
184
|
containing_object_name,
|
185
|
-
|
185
|
+
expected_config)
|
186
186
|
reader = ConfigToolkit::YAMLReader.new(config_file_name)
|
187
187
|
config = MachineConfig.load(reader, containing_object_name)
|
188
|
-
|
188
|
+
assert_equal(expected_config, config)
|
189
189
|
puts config
|
190
190
|
end
|
191
191
|
|
192
|
+
def verify_correct_load_group(config_file_name,
|
193
|
+
containing_object_name,
|
194
|
+
expected_configs)
|
195
|
+
reader = ConfigToolkit::YAMLReader.new(config_file_name)
|
196
|
+
configs = MachineConfig.load_group(reader, containing_object_name)
|
197
|
+
assert_equal(expected_configs, configs)
|
198
|
+
|
199
|
+
configs.each do |name, config|
|
200
|
+
assert_equal("#{containing_object_name}.#{name}", config.containing_object_name)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
192
204
|
def verify_load_error(config_file_name, containing_object_name, message)
|
193
205
|
reader = ConfigToolkit::YAMLReader.new(config_file_name)
|
194
206
|
|
@@ -203,52 +215,68 @@ class BaseConfigTest < Test::Unit::TestCase
|
|
203
215
|
assert(rescued_exception)
|
204
216
|
end
|
205
217
|
|
218
|
+
def verify_load_group_error(config_file_name, containing_object_name, message)
|
219
|
+
reader = ConfigToolkit::YAMLReader.new(config_file_name)
|
220
|
+
|
221
|
+
rescued_exception = false
|
222
|
+
begin
|
223
|
+
config = MachineConfig.load_group(reader, containing_object_name)
|
224
|
+
rescue ConfigToolkit::Error => e
|
225
|
+
assert_equal(message, e.message)
|
226
|
+
rescued_exception = true
|
227
|
+
end
|
228
|
+
|
229
|
+
assert(rescued_exception)
|
230
|
+
end
|
231
|
+
|
206
232
|
#
|
207
233
|
# Test whether a canned config file can be load correctly (whether
|
208
234
|
# the config instances ends up having the expected values).
|
209
235
|
#
|
210
236
|
def test_load
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
237
|
+
config = MachineConfig.new()
|
238
|
+
config.name = "apple"
|
239
|
+
config.architecture = "powerpc"
|
240
|
+
config.os = "AIX"
|
241
|
+
config.num_cpus = 32
|
242
|
+
config.behind_firewall = false
|
243
|
+
config.contains_sensitive_data = false
|
244
|
+
config.addresses = [
|
245
|
+
URI("http://default.designingpatterns.com"),
|
246
|
+
URI("http://apple.designingpatterns.com")
|
247
|
+
]
|
248
|
+
verify_correct_load(MACHINES_CONFIG_FILE_NAME, "", config)
|
249
|
+
|
250
|
+
config = MachineConfig.new()
|
251
|
+
config.name = "orange"
|
252
|
+
config.architecture = "ultrasparc"
|
253
|
+
config.os = "Solaris"
|
254
|
+
config.num_cpus = 64
|
255
|
+
config.behind_firewall = true
|
256
|
+
config.contains_sensitive_data = true
|
257
|
+
config.addresses = [
|
258
|
+
URI("http://production.designingpatterns.com"),
|
259
|
+
URI("http://orange.designingpatterns.com")
|
260
|
+
]
|
261
|
+
verify_correct_load(MACHINES_CONFIG_FILE_NAME, "production", config)
|
262
|
+
|
263
|
+
config = MachineConfig.new()
|
264
|
+
config.name = "bananna"
|
265
|
+
config.architecture = "itanium"
|
266
|
+
config.os = "Linux"
|
267
|
+
config.num_cpus = 16
|
268
|
+
config.behind_firewall = true
|
269
|
+
config.contains_sensitive_data = false
|
270
|
+
config.addresses = [
|
271
|
+
URI("http://test.designingpatterns.com"),
|
272
|
+
URI("http://bananna.designingpatterns.com")
|
273
|
+
]
|
274
|
+
verify_correct_load(MACHINES_CONFIG_FILE_NAME, "test", config)
|
275
|
+
|
276
|
+
verify_correct_load_group(MACHINES_CONFIG_FILE_NAME, "db_cluster1",
|
277
|
+
"db1" => MachineConfig.load(ConfigToolkit::YAMLReader.new(MACHINES_CONFIG_FILE_NAME), "db1"),
|
278
|
+
"db2" => MachineConfig.load(ConfigToolkit::YAMLReader.new(MACHINES_CONFIG_FILE_NAME), "db2"),
|
279
|
+
"db3" => MachineConfig.load(ConfigToolkit::YAMLReader.new(MACHINES_CONFIG_FILE_NAME), "db3"))
|
252
280
|
end
|
253
281
|
|
254
282
|
def test_load_errors
|
@@ -258,7 +286,7 @@ class BaseConfigTest < Test::Unit::TestCase
|
|
258
286
|
|
259
287
|
verify_load_error(MACHINES_CONFIG_FILE_NAME,
|
260
288
|
"bad_containing_object",
|
261
|
-
"error: BaseConfigTest::MachineConfig
|
289
|
+
"error: BaseConfigTest::MachineConfig.load_containing_object_hash found Fixnum rather than Hash when reading the bad_containing_object containing object")
|
262
290
|
|
263
291
|
verify_load_error(MACHINES_CONFIG_FILE_NAME,
|
264
292
|
"missing_num_cpus",
|
@@ -288,5 +316,8 @@ class BaseConfigTest < Test::Unit::TestCase
|
|
288
316
|
"bad_contains_sensitive_data_behind_firewall_combo",
|
289
317
|
"BaseConfigTest::MachineConfig#validate_all_values load error for bad_contains_sensitive_data_behind_firewall_combo: a machine cannot contain sensitive data and not be behind the firewall")
|
290
318
|
|
319
|
+
verify_load_group_error(MACHINES_CONFIG_FILE_NAME,
|
320
|
+
"bad_group",
|
321
|
+
"error: BaseConfigTest::MachineConfig.load_group found Array rather than Hash when reading the bad_group.bad_element containing object")
|
291
322
|
end
|
292
323
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: configtoolkit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- DesigningPatterns
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-05-
|
12
|
+
date: 2008-05-21 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|