configtoolkit 1.0.6 → 1.0.7

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 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
  #