configtoolkit 1.0.6 → 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,6 @@
1
+ === 1.0.7 / 2008-05-13
2
+ Finish the code comments and code cleanup.
3
+
1
4
  === 1.0.6 / 2008-05-13
2
5
  Improve the code comments.
3
6
 
data/Manifest.txt CHANGED
@@ -17,6 +17,6 @@ test/bad_config.yaml
17
17
  test/contained_sample.yaml
18
18
  test/firewall.yaml
19
19
  test/machines.yaml
20
- test/sample.ruby_yaml_types.yaml
21
- test/sample.standard_yaml_types.yaml
20
+ test/sample.ruby_yaml_classes.yaml
21
+ test/sample.standard_yaml_classes.yaml
22
22
  test/webserver.yaml
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ require 'hoe'
7
7
 
8
8
  $stderr = STDERR
9
9
 
10
- Hoe.new('configtoolkit', "1.0.6") do |p|
10
+ Hoe.new('configtoolkit', "1.0.7") do |p|
11
11
  p.remote_rdoc_dir = 'rdoc'
12
12
  p.developer('DesigningPatterns', 'technical.inquiries@designingpatterns.com')
13
13
  end
@@ -40,10 +40,11 @@ class BaseConfig
40
40
  #
41
41
  # This class represents the specification for a parameter. Right now,
42
42
  # a parameter specification consists of:
43
- # 1.) The parameter name (Symbol)
44
- # 2.) The parameter value class (Class)
45
- # 3.) Whether or not the parameter is required
46
- # 4.) If the parameter is not required, a default value
43
+ # * The parameter name (Symbol)
44
+ # * The parameter value class (Class); values must be instances of this
45
+ # class or a child class.
46
+ # * Whether or not the parameter is required
47
+ # * If the parameter is not required, a default value
47
48
  #
48
49
  class ParamSpec
49
50
  attr_reader :name
@@ -52,20 +53,21 @@ class BaseConfig
52
53
 
53
54
  #
54
55
  # ====Description:
55
- #
56
+ # This constructs a Paramspec for parameter name and value
57
+ # of class value_class. If is_required, then the parameter is
58
+ # required; otherwise, the parameter is optional with a default
59
+ # value of default_value.
56
60
  #
57
61
  # ====Parameters:
58
62
  # [name]
59
- # ?
63
+ # The name of the parameter
60
64
  # [value_class]
61
- # ?
65
+ # The class of the parameter's values
62
66
  # [is_required]
63
- # ?
67
+ # Whether or not the parameter must be set in a configuration file
64
68
  # [default_value]
65
- # ?
66
- #
67
- # ====Returns:
68
- #
69
+ # If the parameter need not be set in a configuration file
70
+ # (!is_required), the value it defaults to if it's not specified.
69
71
  #
70
72
  def initialize(name, value_class, is_required, default_value)
71
73
  @name = name
@@ -77,12 +79,10 @@ class BaseConfig
77
79
  end
78
80
  end
79
81
 
80
- #
81
- # ====Description:
82
- #
83
82
  #
84
83
  # ====Returns:
85
- #
84
+ # true if and only if the parameter must be set in a configuration
85
+ # file; false otherwise.
86
86
  #
87
87
  def is_required?
88
88
  return @is_required
@@ -93,37 +93,52 @@ class BaseConfig
93
93
  # The indentation used to indicate nesting when printing out
94
94
  # a BaseConfig.
95
95
  #
96
- NESTING_INDENT = " " * 2
96
+ NESTING_INDENT = " " * 4
97
97
 
98
98
  #
99
99
  # Create accessors for some class instance variables;
100
- # these variables will contain the structure of each
101
- # BaseConfig child class (which parameters are supported,
102
- # which are required, etc.).
100
+ # these variables will contain the specifications for a given
101
+ # BaseConfig child class (what parameters are specified, which
102
+ # parameters are required, etc).
103
103
  #
104
- # The config parameters are stored in a list (@param_list) for ordered
105
- # access (we want to print them out in the order in which they
106
- # were added to the config) and in a hash (@param_hash), for fast lookups
107
- # (as we want to check whether a parameter that we read from a file
108
- # is supported by the class).
104
+ # The parameter specifications are stored in a list (@param_spec_list)
105
+ # for ordered access (we want to print them out in the order in which they
106
+ # were specified in the BaseConfig child class, for instance) and
107
+ # in a hash (@param_hash), for fast lookups (as we want to check whether
108
+ # a parameter that we read from a file is supported by the class).
109
109
  #
110
110
  class << self
111
+ #
112
+ # This is an Array (class instance variable) containing the parameter
113
+ # specifications (ParamSpecs) in the order in which they were specified
114
+ # in the BaseConfig child class.
115
+ #
111
116
  attr_reader :param_spec_list
117
+
118
+ #
119
+ # This is a Hash (class instance variable) mapping
120
+ # paramater names (Symbols) to parameter specifications (ParamSpecs)
121
+ #
112
122
  attr_reader :param_spec_lookup_table
123
+
124
+ #
125
+ # This is an Integer (class instance variable) containing the length
126
+ # of the longest parameter name. This is used to line up the output
127
+ # when printing out an instance.
128
+ #
113
129
  attr_reader :longest_param_name_length
114
- attr_reader :required_params
115
130
  end
116
131
 
117
132
  #
118
133
  # ====Description:
119
- #
134
+ # This is BaseConfig's implementation of the inherited hook, which is
135
+ # called by the Ruby interpreter whenever BaseConfig is extended. This
136
+ # function initializes the class instance variables of the new
137
+ # child class.
120
138
  #
121
139
  # ====Parameters:
122
140
  # [child_class]
123
- # ?
124
- #
125
- # ====Returns:
126
- #
141
+ # The new child class
127
142
  #
128
143
  def self.inherited(child_class)
129
144
  #
@@ -133,52 +148,68 @@ class BaseConfig
133
148
  @param_spec_list = []
134
149
  @param_spec_lookup_table = {}
135
150
  @longest_param_name_length = 0
136
- @required_params = Set.new()
137
151
  end
138
152
  end
139
153
 
140
- #
154
+ #
141
155
  # ====Description:
142
- #
156
+ # This class method adds a new parameter to a BaseConfig child
157
+ # class (it should be called within the body of the child class'
158
+ # definition). The new parameter has name name and has values
159
+ # of class value_class (or one of value_class' child classes).
160
+ # The parameter must be set in a configuration file if
161
+ # is_required is true; if !is_required, then default_value is the
162
+ # value of the parameter if it's not set in a configuration file.
163
+ #
164
+ # This method optionally accepts a block (validate_value_block), which,
165
+ # if present, is called with the new value for the parameter before setting
166
+ # the parameter with the new value, allowing custom validation
167
+ # code to be executed (if this validation fails, then raise_error should
168
+ # be called inside of the block).
169
+ #
170
+ # This method is not meant to be called directly; users instead should
171
+ # call add_required_param or add_optional_param.
143
172
  #
144
173
  # ====Parameters:
145
174
  # [name]
146
- # ?
175
+ # The name of the parameter
147
176
  # [value_class]
148
- # ?
177
+ # The parent class of the parameter's values
149
178
  # [is_required]
150
- # ?
179
+ # Whether or not the parameter must be specified in a
180
+ # configuration file.
151
181
  # [default_value]
152
- # ?
182
+ # If !is_required, then the default value of the parameter if it's
183
+ # not specified in a configuration file.
153
184
  # [validate_value_block]
154
- # ?
155
- #
156
- # ====Returns:
157
- #
185
+ # An optional block that, if present, is called to validate a new
186
+ # value for the parameter before setting the parameter to the new
187
+ # value.
158
188
  #
159
189
  def self.add_param(name,
160
190
  value_class,
161
191
  is_required,
162
192
  default_value,
163
- &validate_value_block)
193
+ &validate_value_block) #:doc:
164
194
  param_spec = ParamSpec.new(name, value_class, is_required, default_value)
165
195
  @param_spec_list.push(param_spec)
166
196
  @param_spec_lookup_table[name] = param_spec
167
197
 
168
198
  #
169
- # Define the setter method with a string passed to class_eval rather
170
- # than a block, so as to avoid the need to access the reflectoin APIs
171
- # when the setter actually is called.
199
+ # Define the getter and setter methods with a string passed to
200
+ # class_eval rather than a block, so as to avoid the need to access
201
+ # the reflectoin APIs when the setter actually is called.
172
202
  #
173
203
  # Note that the setter will call load_value_impl() on any value
174
204
  # that it is passed; this ensures that data specified programatically
175
- # gets processed the same way as that sourced from files.
205
+ # gets processed in the same way as that sourced from files.
176
206
  #
177
207
  # There is a bit of hackiness to get the specified value class; the
178
208
  # value class is retrieved from a lookup table rather
179
- # than being sourced from value_class. The reason for this is that the
180
- # value_class argument could be an anonymous class without a valid name
181
- # and so cannot safely be expanded in a string. Instead, we'll access a
209
+ # than being sourced from the value_class argument.
210
+ # The reason for this is that the value_class argument could be
211
+ # an anonymous class without a valid name and so cannot safely
212
+ # be expanded in a string. Instead, we'll access a
182
213
  # variable storing this class from the param_spec_lookup_table.
183
214
  #
184
215
  methods = String.new()
@@ -189,6 +220,7 @@ class BaseConfig
189
220
  methods << " loaded_value = load_value_impl(\"#{name}\", value_class, value)\n"
190
221
  if(validate_value_block != nil)
191
222
  validate_value_method = "validate_#{name}_value".to_sym()
223
+ define_method(validate_value_method, &validate_value_block)
192
224
  methods << " begin\n"
193
225
  methods << " #{validate_value_method}(value)\n"
194
226
  methods << " rescue Error => e\n"
@@ -202,76 +234,57 @@ class BaseConfig
202
234
 
203
235
  class_eval(methods)
204
236
 
205
- if(validate_value_block != nil)
206
- define_method(validate_value_method, &validate_value_block)
207
- end
208
-
209
237
  name_str = name.to_s
210
238
  if(name_str.length > @longest_param_name_length)
211
239
  @longest_param_name_length = name_str.length
212
240
  end
213
-
214
- if(is_required)
215
- @required_params.add(name)
216
- end
217
241
  end
218
242
  private_class_method :add_param
219
243
 
220
244
  #
221
245
  # ====Description:
222
- #
246
+ # This is a wrapper around add_param that passes is_required = true.
223
247
  #
224
248
  # ====Parameters:
225
- # [name]
226
- # ?
227
- # [value_class]
228
- # ?
229
- # [validate_value_block]
230
- # ?
231
- #
232
- # ====Returns:
233
- #
249
+ # See add_param's parameter list.
234
250
  #
235
251
  def self.add_required_param(name,
236
252
  value_class,
237
- &validate_value_block)
253
+ &validate_value_block) #:doc:
238
254
  add_param(name, value_class, true, nil, &validate_value_block)
239
255
  end
240
256
  private_class_method :add_required_param
241
257
 
242
258
  #
243
259
  # ====Description:
244
- #
260
+ # This is a wrapper around add_param that passes is_required = false and
261
+ # default_value = default_value.
245
262
  #
246
263
  # ====Parameters:
247
- # [name]
248
- # ?
249
- # [value_class]
250
- # ?
251
- # [default_value]
252
- # ?
253
- # [validate_value_block]
254
- # ?
255
- #
256
- # ====Returns:
257
- #
264
+ # See add_param's parameter list.
258
265
  #
259
266
  def self.add_optional_param(name,
260
267
  value_class,
261
268
  default_value,
262
- &validate_value_block)
269
+ &validate_value_block) #:doc:
263
270
  add_param(name, value_class, false, default_value, &validate_value_block)
264
271
  end
265
272
  private_class_method :add_optional_param
266
273
 
267
- attr_reader :containing_object_name
274
+ #
275
+ # This is a String (instance variable) containing the name of the object that
276
+ # contains this instance's configuration. For example, if
277
+ # all of this instance's configuration is under production and
278
+ # webserver, then the containing_object_name would be
279
+ # 'production.webserver'.
280
+ #
281
+ attr_accessor :containing_object_name
282
+ protected :containing_object_name= # This is NOT part of the public interface
268
283
 
269
284
  #
270
285
  # ====Description:
271
- #
272
- #
273
- # ====Returns:
274
- #
286
+ # This constructs a BaseConfig with no parameters set (all parameters
287
+ # initially have a nil value).
275
288
  #
276
289
  def initialize
277
290
  @containing_object_name = ""
@@ -284,19 +297,29 @@ class BaseConfig
284
297
 
285
298
  #
286
299
  # ====Description:
287
- #
300
+ # This method enforces the BaseConfig's specifications. In particular,
301
+ # it:
302
+ # * Checks hat all required parameters have been set. If a required
303
+ # parameter is missing, then this method raises an Error.
304
+ # * Sets all optional parameters without values to their
305
+ # default values.
306
+ # * Calls validate_all_values, if this exists.
307
+ #
308
+ # This method is called at the end of a load operation (verifying that
309
+ # a valid configuration was loaded from a reader) and before a dump operation
310
+ # (verifying that a valid configuration will be dumped). The operation_name
311
+ # parameter is a String containing the name of the operation calling
312
+ # this method; this String is used in Error messages.
288
313
  #
289
314
  # ====Parameters:
290
315
  # [operation_name]
291
- # ?
292
- #
293
- # ====Returns:
294
- #
316
+ # A String containing the name of the operation calling this
317
+ # method ("load" or "dump", for example)
295
318
  #
296
319
  def enforce_specs(operation_name)
297
320
  #
298
321
  # Iterate through the parameters without values. If any are
299
- # required parameters, then throw (after completing the
322
+ # required parameters, then raise (after completing the
300
323
  # iteration in order to get the full list of missing
301
324
  # parameters). Otherwise, set all optional, missing
302
325
  # parameters to their default values.
@@ -308,18 +331,23 @@ class BaseConfig
308
331
  if(param_spec.is_required?)
309
332
  missing_params.push(param_spec.name)
310
333
  else
334
+ #
335
+ # Even the default values are set through the setter.
336
+ #
311
337
  send("#{param_spec.name}=", param_spec.default_value)
312
338
  end
313
339
  end
314
340
  end
315
341
 
316
342
  if(!missing_params.empty?)
343
+ if(@containing_object_name.empty?)
344
+ param_prefix = ""
345
+ else
346
+ param_prefix = "#{@containing_object_name}."
347
+ end
348
+
317
349
  missing_param_spec_list = missing_params.map do |param_name|
318
- if(@containing_object_name.empty?)
319
- next "#{param_name}"
320
- else
321
- next "#{@containing_object_name}.#{param_name}"
322
- end
350
+ "#{param_prefix}#{param_name}"
323
351
  end
324
352
 
325
353
  raise Error, "missing parameter(s): #{missing_param_spec_list.join(", ")}"
@@ -329,6 +357,10 @@ class BaseConfig
329
357
  begin
330
358
  validate_all_values()
331
359
  rescue Error => e
360
+ #
361
+ # Rescue the error, add some information to the error message, and
362
+ # throw a repackaged error.
363
+ #
332
364
  message = "#{self.class}#validate_all_values #{operation_name} error"
333
365
 
334
366
  if(!@containing_object_name.empty?)
@@ -345,23 +377,31 @@ class BaseConfig
345
377
  #
346
378
  # ====Description:
347
379
  # Because the to_s in Ruby 1.8 for the Array and Hash classes
348
- # are horrible...
380
+ # are horrible... This method returns a readable String representation
381
+ # of value, recursively expanding the contents of Array and Hash
382
+ # value arguments and otherwise returning value.to_s. This is
383
+ # called by construct_error_message in order to construct a nicely formatted
384
+ # error message.
349
385
  #
350
386
  # ====Parameters:
351
387
  # [value]
352
- # ?
388
+ # The object for which to return a String representation
353
389
  #
354
390
  # ====Returns:
355
- #
391
+ # A nicely formatted String representation of value
356
392
  #
357
393
  def construct_value_str(value)
358
394
  if(value.is_a?(Array))
395
+ #
396
+ # Recursively call construct_value_str for each Array element,
397
+ # surrounding the element strings in brackets.
398
+ #
359
399
  str = "["
360
400
 
361
401
  value.each_with_index do |element, index|
362
402
  str << construct_value_str(element)
363
403
 
364
- if(index != (value.size - 1))
404
+ if(index < (value.size - 1))
365
405
  str << ", "
366
406
  end
367
407
  end
@@ -369,12 +409,16 @@ class BaseConfig
369
409
  str << "]"
370
410
  return str
371
411
  elsif(value.is_a?(Hash))
412
+ #
413
+ # Recursively call construct_value_str for each Hash element,
414
+ # surrounding the element strings in braces.
415
+ #
372
416
  str = "{"
373
417
 
374
418
  value.each_with_index do |key_value, index|
375
419
  str << "#{construct_value_str(key_value[0])}=>#{construct_value_str(key_value[1])}"
376
420
 
377
- if(index != (value.size - 1))
421
+ if(index < (value.size - 1))
378
422
  str << ", "
379
423
  end
380
424
  end
@@ -389,57 +433,62 @@ class BaseConfig
389
433
 
390
434
  #
391
435
  # ====Description:
392
- #
436
+ # This method returns an error message for an error (described by
437
+ # reason) that occurred while setting parameter param_name with
438
+ # value value.
393
439
  #
394
440
  # ====Parameters:
395
441
  # [param_name]
396
- # ?
442
+ # The parameter being set when the error occurred (String)
443
+ # This need not be a "real" parameter value; it also can be
444
+ # an element in an Array value (i.e., the 3rd element of Array
445
+ # parameter servers will be passed as "servers[2]").
397
446
  # [value]
398
- # ?
399
- # [message]
400
- # ?
447
+ # The parameter value that caused the error
448
+ # [reason]
449
+ # The reason for the error (String)
401
450
  #
402
451
  # ====Returns:
452
+ # An error message that includes the parameter name, the problem
453
+ # parameter value, and the error description.
403
454
  #
404
- #
405
- def construct_error_message(param_name, value, message)
455
+ def construct_error_message(param_name, value, reason)
406
456
  if(@containing_object_name.empty?)
407
- full_param_name = ""
457
+ full_param_name = "#{param_name}"
408
458
  else
409
- full_param_name = "#{@containing_object_name}."
459
+ full_param_name = "#{@containing_object_name}.#{param_name}"
410
460
  end
411
461
 
412
- full_param_name += param_name
413
-
414
- return "error setting parameter #{full_param_name} with value #{construct_value_str(value)}: #{message}."
462
+ return "error setting parameter #{full_param_name} with value #{construct_value_str(value)}: #{reason}."
415
463
  end
416
464
  private :construct_error_message
417
465
 
418
466
  #
419
467
  # ====Description:
420
- #
468
+ # This method raises an Error with message reason. It is meant to
469
+ # be called from user-defined parameter validation blocks. This is
470
+ # a Hotel California type of call; it does not return.
421
471
  #
422
472
  # ====Parameters:
423
- # [message]
424
- # ?
425
- #
426
- # ====Returns:
473
+ # [reason]
474
+ # The reason for the error
427
475
  #
428
- #
429
- def raise_error(message)
430
- raise Error, message, caller()
476
+ def raise_error(reason)
477
+ raise Error, reason, caller()
431
478
  end
432
479
 
433
480
  #
434
481
  # ====Description:
435
- #
482
+ # Equality operator for BaseConfig; this iterates through the
483
+ # specified parameters and compares the parameter values of
484
+ # self and rhs using the equality operator of the parameter values.
436
485
  #
437
486
  # ====Parameters:
438
487
  # [rhs]
439
- # ?
488
+ # The instance to compare with self
440
489
  #
441
490
  # ====Returns:
442
- #
491
+ # True if and only if the values of self and rhs are equal
443
492
  #
444
493
  def ==(rhs)
445
494
  if(rhs == nil)
@@ -457,24 +506,24 @@ class BaseConfig
457
506
 
458
507
  #
459
508
  # ====Description:
460
- #
509
+ # This method returns a dump of parameter value value.
510
+ # It recursively calls itself and dump_impl to handle
511
+ # Array elements and BaseConfig instances.
461
512
  #
462
513
  # ====Parameters:
463
- # [value_class]
464
- # ?
465
514
  # [value]
466
- # ?
515
+ # The parameter value to dump
467
516
  #
468
517
  # ====Returns:
518
+ # A dump of value
469
519
  #
470
- #
471
- def dump_value_impl(value_class, value)
472
- if(value_class < BaseConfig)
520
+ def dump_value_impl(value)
521
+ if(value.class < BaseConfig)
473
522
  return value.dump_impl({})
474
- elsif(value_class < ConstrainedArray)
523
+ elsif(value.class == Array)
475
524
  dumped_array = []
476
525
  value.each do |element|
477
- dumped_array.push(dump_value_impl(value_class.element_class, element))
526
+ dumped_array.push(dump_value_impl(element))
478
527
  end
479
528
  return dumped_array
480
529
  else
@@ -485,14 +534,20 @@ class BaseConfig
485
534
 
486
535
  #
487
536
  # ====Description:
488
- #
537
+ # This method dumps self into containing_object_hash; that is,
538
+ # it adds entries to containing_object_hash for each parameter.
539
+ # An entry's key is the parameter name (as a String), and the value is
540
+ # the parameter value. The method returns containing_object_hash
541
+ # after the dump. Before dumping everything, this method calls
542
+ # enforce_specs in order to ensure that valid data is being
543
+ # dumped.
489
544
  #
490
545
  # ====Parameters:
491
546
  # [containing_object_hash]
492
- # ?
547
+ # The hash into which to dump self
493
548
  #
494
549
  # ====Returns:
495
- #
550
+ # containing_object_hash after the dump
496
551
  #
497
552
  def dump_impl(containing_object_hash)
498
553
  #
@@ -502,7 +557,7 @@ class BaseConfig
502
557
  enforce_specs("dump")
503
558
 
504
559
  self.class.param_spec_list.each do |param_spec|
505
- containing_object_hash[param_spec.name.to_s] = dump_value_impl(param_spec.value_class, send(param_spec.name))
560
+ containing_object_hash[param_spec.name.to_s] = dump_value_impl(send(param_spec.name))
506
561
  end
507
562
 
508
563
  return containing_object_hash
@@ -511,14 +566,16 @@ class BaseConfig
511
566
 
512
567
  #
513
568
  # ====Description:
514
- #
569
+ # This method dumps self to writer. That is, it constructs
570
+ # a Hash containing entries for the containing object (and,
571
+ # in the the most nested containing object entry, entries for the
572
+ # paramters) and passes the Hash to the write method of writer. The
573
+ # configuration's data is checked against its specifications
574
+ # before anything is dumped.
515
575
  #
516
576
  # ====Parameters:
517
577
  # [writer]
518
- # ?
519
- #
520
- # ====Returns:
521
- #
578
+ # The writer to which to dump
522
579
  #
523
580
  def dump(writer)
524
581
  dump_hash = {}
@@ -536,31 +593,46 @@ class BaseConfig
536
593
 
537
594
  #
538
595
  # ====Description:
539
- #
596
+ # This method validates and returns a parameter value of class value_class
597
+ # for parameter param_name (String) from raw_value. The logic in this
598
+ # method is a bit gnarly. In particular, it needs to:
599
+ # * Construct BaseConfig child instances from Hash values, recursively
600
+ # loading these with load_impl.
601
+ # * Recursively call itself on Array elements in order to construct
602
+ # Array parameter values.
603
+ # * Load true and false values for Boolean parameters
604
+ # * Convert String values to a small number of non-String parameter
605
+ # types, including Integers, Floats, Booleans, Pathnames, and URIs.
606
+ # This is done because not all reader classes will read different
607
+ # types from files and so BaseConfig has to do some lexical casting.
540
608
  #
541
609
  # ====Parameters:
542
610
  # [param_name]
543
- # ?
611
+ # The name of the parameter whose value is being loaded (String).
612
+ # This need not be a "real" parameter value; it also can be
613
+ # an element in an Array value (i.e., the 3rd element of Array
614
+ # parameter servers will be passed as "servers[2]").
544
615
  # [value_class]
545
- # ?
546
- # [value]
547
- # ?
616
+ # The class of the parameter's values (Class)
617
+ # [raw_value]
618
+ # The raw value from which to load the parameter value
548
619
  #
549
620
  # ====Returns:
621
+ # A parameter value of class value_class for param_name loaded from raw_value
550
622
  #
551
- #
552
- def load_value_impl(param_name, value_class, value)
553
- if(value.class <= value_class)
623
+ def load_value_impl(param_name, value_class, raw_value)
624
+ if(raw_value.class <= value_class)
554
625
  #
555
- # If the ConfigReader has returned a value of the same class as
626
+ # If the reader has returned a value of the same class as
556
627
  # was specified for that parameter (or a child class of the specified
557
628
  # class), then just return the value.
558
629
  #
559
- return value
560
- elsif((value_class < BaseConfig) && (value.class == Hash))
630
+ return raw_value
631
+ elsif((value_class < BaseConfig) && (raw_value.class == Hash))
561
632
  #
562
- # If we're looking for a BaseConfig child and have a hash, then
563
- # construct the child with the hash.
633
+ # If we're looking for a BaseConfig child and have a Hash, then
634
+ # construct the BaseConfig child with the Hash by (recursively) calling
635
+ # load_impl.
564
636
  #
565
637
  child_config = value_class.new()
566
638
 
@@ -570,91 +642,92 @@ class BaseConfig
570
642
  nested_containing_object_name = "#{@containing_object_name}.#{param_name}"
571
643
  end
572
644
 
573
- child_config.load_impl(value, nested_containing_object_name)
645
+ child_config.containing_object_name = nested_containing_object_name
646
+ child_config.load_impl(raw_value)
574
647
  return child_config
575
- elsif((value_class < ConstrainedArray) && (value.class == Array))
648
+ elsif((value_class < ConstrainedArray) && (raw_value.class == Array))
576
649
  #
577
650
  # If we're looking for a ConstrainedArray and have a Ruby Array, then
578
651
  # iterate over each element of the Ruby Array and recursively
579
- # load it.
652
+ # load it with load_value_impl.
580
653
  #
581
- value.each_with_index do |element, index|
582
- value[index] = load_value_impl("#{param_name}[#{index}]", value_class.element_class, element)
654
+ value = []
655
+
656
+ raw_value.each_with_index do |element, index|
657
+ value.push(load_value_impl("#{param_name}[#{index}]", value_class.element_class, element))
583
658
  end
584
659
 
585
660
  if((value_class.min_num_elements != nil) &&
586
661
  (value.length < value_class.min_num_elements))
587
662
  message = "the number of elements (#{value.length}) is less than the specified "
588
663
  message << "minimum number of elements (#{value_class.min_num_elements})"
589
- raise Error, construct_error_message(param_name, value, message)
664
+ raise Error, construct_error_message(param_name, raw_value, message)
590
665
  end
591
666
 
592
667
  if((value_class.max_num_elements != nil) &&
593
668
  (value.length > value_class.max_num_elements))
594
669
  message = "the number of elements (#{value.length}) is greater than the specified "
595
670
  message << "maximum number of elements (#{value_class.max_num_elements})"
596
- raise Error, construct_error_message(param_name, value, message)
671
+ raise Error, construct_error_message(param_name, raw_value, message)
597
672
  end
598
673
 
599
674
  return value
600
675
  elsif((value_class == Boolean) &&
601
- ((value.class == TrueClass) || (value.class == FalseClass)))
676
+ ((raw_value.class == TrueClass) || (raw_value.class == FalseClass)))
602
677
  #
603
678
  # If we're looking for a Boolean, then we just can return
604
- # the ConfigReader's value if it's true or false.
679
+ # the reader's value if it's true or false.
605
680
  #
606
- return value
607
- elsif(value.class == String)
681
+ return raw_value
682
+ elsif(raw_value.class == String)
608
683
  #
609
- # Some ConfigReaders only may return strings and leave it up to
684
+ # Some readers only may return Strings and leave it up to
610
685
  # the caller to figure out the proper types for the parameters.
611
- # In our case, we'll levarage Ruby's built-in conversion functions
686
+ # We'll levarage Ruby's built-in conversion functions
612
687
  # to support the most common parameter types. Should this be
613
688
  # made extensible?
614
689
  #
615
690
  case
616
691
  when (value_class <= Integer)
617
- return Integer(value)
692
+ return Integer(raw_value)
618
693
  when (value_class <= Float)
619
- return Float(value)
694
+ return Float(raw_value)
620
695
  when (value_class <= Pathname)
621
- return Pathname(value)
696
+ return Pathname(raw_value)
622
697
  when (value_class <= Symbol)
623
- return value.to_sym()
698
+ return raw_value.to_sym()
624
699
  when (value_class <= URI)
625
- return URI(value)
700
+ return URI(raw_value)
626
701
  when (value_class == Boolean)
627
702
  #
628
703
  # Support both yes/no and true/false boolean values.
629
704
  #
630
- if((value.casecmp("yes") == 0) || (value.casecmp("true") == 0))
705
+ if((raw_value.casecmp("yes") == 0) || (raw_value.casecmp("true") == 0))
631
706
  return true
632
- elsif((value.casecmp("no") == 0)|| (value.casecmp("false") == 0))
707
+ elsif((raw_value.casecmp("no") == 0)|| (raw_value.casecmp("false") == 0))
633
708
  return false
634
709
  end
635
710
  end
636
711
  end
637
712
 
638
- message = "cannot convert from value class #{value.class.name} to specified class #{value_class.name}"
639
- raise Error, construct_error_message(param_name, value, message)
713
+ message = "cannot convert from value class #{raw_value.class.name} to specified class #{value_class.name}"
714
+ raise Error, construct_error_message(param_name, raw_value, message)
640
715
  end
641
716
  private :load_value_impl
642
717
 
643
718
  #
644
719
  # ====Description:
645
- #
720
+ # This method loads self from containing_object_hash, which
721
+ # must contain key/value pairs, where the keys are
722
+ # parameter names (Strings) and the values are parameter
723
+ # values (to be passed to load_value_impl). The configuration's
724
+ # specifications are enforced by this method.
646
725
  #
647
726
  # ====Parameters:
648
727
  # [containing_object_hash]
649
- # ?
650
- # [containing_object_name]
651
- # ?
728
+ # The Hash containing the configuration's parameters
652
729
  #
653
- # ====Returns:
654
- #
655
- #
656
- def load_impl(containing_object_hash, containing_object_name)
657
- @containing_object_name = containing_object_name
730
+ def load_impl(containing_object_hash)
658
731
  @params_with_values.clear()
659
732
 
660
733
  if(containing_object_hash != nil)
@@ -684,16 +757,17 @@ class BaseConfig
684
757
 
685
758
  #
686
759
  # ====Description:
687
- #
760
+ # This method loads self from reader (with containing object
761
+ # containing_object_name).
688
762
  #
689
763
  # ====Parameters:
690
764
  # [reader]
691
- # ?
765
+ # The reader from which to load the parameter values. This method
766
+ # will call the read method of reader, which will return
767
+ # a Hash containing, in the most nested containing object entry,
768
+ # the configuration parameter values for self.
692
769
  # [containing_object_name = ""]
693
- # ?
694
- #
695
- # ====Returns:
696
- #
770
+ # The containing object name
697
771
  #
698
772
  def load(reader, containing_object_name = "")
699
773
  param_hash = reader.read
@@ -705,11 +779,12 @@ class BaseConfig
705
779
  end
706
780
 
707
781
  #
708
- # Work through the param_hash until containing_object_name is found by
782
+ # Work through the param_hash until the most nested object in
783
+ # containing_object_name is found by
709
784
  # splitting up containing_object_name into an object list and
710
785
  # hashing for each. For example, production.webserver.tokyo would
711
786
  # result in a lookup for production, the results of which them would be
712
- # searched for webserver, the results of which finally would be search
787
+ # searched for webserver, the results of which finally would be searched
713
788
  # for tokyo. Not being able to find one of the objects is not
714
789
  # (necessarily) an error; that just means that none of the parameters will
715
790
  # be set from param_hash, which is allowable if all of the parameters
@@ -717,8 +792,9 @@ class BaseConfig
717
792
  # is empty). On the other hand, if an object is found but it is not
718
793
  # really an object (a Hash), then raise an exception.
719
794
  #
795
+ @containing_object_name = containing_object_name
720
796
  containing_object_hash = param_hash
721
- containing_object_name.split(".").each do |object_name|
797
+ @containing_object_name.split(".").each do |object_name|
722
798
  containing_object_hash = containing_object_hash.fetch(object_name, nil)
723
799
 
724
800
  if(containing_object_hash == nil)
@@ -727,26 +803,25 @@ class BaseConfig
727
803
  message = "error: #{self.class}#load found "
728
804
  message << "#{containing_object_hash.class} "
729
805
  message << "rather than Hash when reading the "
730
- message << "#{containing_object_name} containing object"
806
+ message << "#{object_name} containing object"
731
807
  raise Error, message
732
808
  end
733
809
  end
734
810
 
735
- load_impl(containing_object_hash, containing_object_name)
811
+ load_impl(containing_object_hash)
736
812
  end
737
813
 
738
814
  #
739
815
  # ====Description:
740
- #
816
+ # This class method creates a new config instance and
817
+ # calls load on it with the specified parameters. This
818
+ # is a second "constructor" for the class.
741
819
  #
742
820
  # ====Parameters:
743
- # [reader]
744
- # ?
745
- # [containing_object_name = ""]
746
- # ?
821
+ # See the parameter list for load.
747
822
  #
748
823
  # ====Returns:
749
- #
824
+ # The new instance, preloaded!
750
825
  #
751
826
  def self.load(reader, containing_object_name = "")
752
827
  instance = new()
@@ -756,25 +831,21 @@ class BaseConfig
756
831
 
757
832
  #
758
833
  # ====Description:
759
- #
834
+ # This method writes parameter value value to str, with
835
+ # indentation indent.
760
836
  #
761
837
  # ====Parameters:
762
838
  # [str]
763
- # ?
839
+ # The String to which to write
764
840
  # [indent]
765
- # ?
766
- # [value_class]
767
- # ?
841
+ # The indentation at which to write value
768
842
  # [value]
769
- # ?
843
+ # The parameter value to write
770
844
  #
771
- # ====Returns:
772
- #
773
- #
774
- def write_value_impl(str, indent, value_class, value)
845
+ def write_value_impl(str, indent, value)
775
846
  nesting_indent = indent + NESTING_INDENT
776
847
  if(value != nil)
777
- if(value_class < BaseConfig)
848
+ if(value.class < BaseConfig)
778
849
  #
779
850
  # Writing a nested config requires calling its write(),
780
851
  # just at one further indentation level.
@@ -782,19 +853,18 @@ class BaseConfig
782
853
  str << "{\n"
783
854
  value.write_impl(str, nesting_indent)
784
855
  str << indent << "}"
785
- elsif(value_class < ConstrainedArray)
856
+ elsif(value.class < Array)
786
857
  #
787
- # Writing an array requires going through each element and
788
- # calling write_value_impl, just at one further indentation
789
- # level.
858
+ # Writing an array requires calling write_value_impl for each element,
859
+ # just at one further indentation level.
790
860
  #
791
861
  str << "[\n"
792
862
 
793
863
  value.each_with_index do |element, index|
794
864
  str << nesting_indent
795
- write_value_impl(str, nesting_indent, value_class.element_class, element)
865
+ write_value_impl(str, nesting_indent, element)
796
866
 
797
- if(index != (value.length - 1))
867
+ if(index < (value.length - 1))
798
868
  str << ","
799
869
  end
800
870
 
@@ -811,34 +881,28 @@ class BaseConfig
811
881
 
812
882
  #
813
883
  # ====Description:
814
- #
884
+ # This method writes self to str, at indentation indent.
815
885
  #
816
886
  # ====Parameters:
817
887
  # [str]
818
- # ?
888
+ # The String to which to write
819
889
  # [indent]
820
- # ?
821
- #
822
- # ====Returns:
823
- #
890
+ # The indentation at which to write self
824
891
  #
825
892
  def write_impl(str, indent)
893
+ name_field_length = self.class.longest_param_name_length
826
894
  self.class.param_spec_list.each do |param_spec|
827
- name_field_length = self.class.longest_param_name_length
828
895
  str << indent << param_spec.name.to_s.ljust(name_field_length) << ": "
829
896
  value = send(param_spec.name)
830
- write_value_impl(str, indent, param_spec.value_class, value)
897
+ write_value_impl(str, indent, value)
831
898
  str << "\n"
832
899
  end
833
900
  end
834
901
  protected :write_impl
835
902
 
836
- #
837
- # ====Description:
838
- #
839
903
  #
840
904
  # ====Returns:
841
- #
905
+ # String representation of self
842
906
  #
843
907
  def to_s
844
908
  #