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.
@@ -1,3 +1,7 @@
1
+ === 1.2.0 / 2008-05-20
2
+ Add BaseConfig.load_group, which allows a hash of configs to be loaded with
3
+ one call. Corrected a bug in how array parameters are printed.
4
+
1
5
  === 1.1.0 / 2008-05-14
2
6
  Finish the README.txt; this is the first production release!
3
7
 
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
@@ -7,7 +7,7 @@ require 'hoe'
7
7
 
8
8
  $stderr = STDERR
9
9
 
10
- Hoe.new('configtoolkit', "1.1.0") do |p|
10
+ Hoe.new('configtoolkit', "1.2.0") do |p|
11
11
  p.remote_rdoc_dir = ''
12
12
  p.developer('DesigningPatterns', 'technical.inquiries@designingpatterns.com')
13
13
  end
@@ -287,8 +287,7 @@ class BaseConfig
287
287
  # webserver, then the containing_object_name would be
288
288
  # 'production.webserver'.
289
289
  #
290
- attr_accessor :containing_object_name
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.containing_object_name = nested_containing_object_name
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 self from reader (with containing object
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
- # [reader]
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
- def load(reader, containing_object_name = "")
782
- param_hash = reader.read
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
- @containing_object_name.split(".").each do |object_name|
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.class}#load found "
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
- load_impl(containing_object_hash)
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.class < Array)
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.
@@ -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
@@ -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
- puts config
111
- end
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
- def verify_param_values(config, expected_config_param_values)
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
- expected_param_values)
185
+ expected_config)
186
186
  reader = ConfigToolkit::YAMLReader.new(config_file_name)
187
187
  config = MachineConfig.load(reader, containing_object_name)
188
- verify_param_values(config, expected_param_values)
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
- verify_correct_load(MACHINES_CONFIG_FILE_NAME, "", {
212
- :name => "apple",
213
- :architecture => "powerpc",
214
- :os => "AIX",
215
- :num_cpus => 32,
216
- :behind_firewall => false,
217
- :contains_sensitive_data => false,
218
- :addresses =>
219
- [
220
- URI("http://default.designingpatterns.com"),
221
- URI("http://apple.designingpatterns.com")
222
- ]
223
- })
224
-
225
- verify_correct_load(MACHINES_CONFIG_FILE_NAME, "production", {
226
- :name => "orange",
227
- :architecture => "ultrasparc",
228
- :os => "Solaris",
229
- :num_cpus => 64,
230
- :behind_firewall => true,
231
- :contains_sensitive_data => true,
232
- :addresses =>
233
- [
234
- URI("http://production.designingpatterns.com"),
235
- URI("http://orange.designingpatterns.com")
236
- ]
237
- })
238
-
239
- verify_correct_load(MACHINES_CONFIG_FILE_NAME, "test", {
240
- :name => "bananna",
241
- :architecture => "itanium",
242
- :os => "Linux",
243
- :num_cpus => 16,
244
- :behind_firewall => true,
245
- :contains_sensitive_data => false,
246
- :addresses =>
247
- [
248
- URI("http://test.designingpatterns.com"),
249
- URI("http://bananna.designingpatterns.com")
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#load found Fixnum rather than Hash when reading the bad_containing_object containing object")
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.1.0
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-15 00:00:00 -04:00
12
+ date: 2008-05-21 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency