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 +3 -0
- data/Manifest.txt +2 -2
- data/Rakefile +1 -1
- data/lib/configtoolkit/baseconfig.rb +298 -234
- data/lib/configtoolkit/types.rb +13 -0
- data/lib/configtoolkit/yamlreader.rb +22 -0
- data/lib/configtoolkit/yamlwriter.rb +106 -27
- data/test/{sample.ruby_yaml_types.yaml → sample.ruby_yaml_classes.yaml} +0 -0
- data/test/{sample.standard_yaml_types.yaml → sample.standard_yaml_classes.yaml} +0 -0
- data/test/test_yaml.rb +14 -14
- metadata +3 -3
data/History.txt
CHANGED
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.
|
21
|
-
test/sample.
|
20
|
+
test/sample.ruby_yaml_classes.yaml
|
21
|
+
test/sample.standard_yaml_classes.yaml
|
22
22
|
test/webserver.yaml
|
data/Rakefile
CHANGED
@@ -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
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
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 = "
|
96
|
+
NESTING_INDENT = " " * 4
|
97
97
|
|
98
98
|
#
|
99
99
|
# Create accessors for some class instance variables;
|
100
|
-
# these variables will contain the
|
101
|
-
# BaseConfig child class (
|
102
|
-
#
|
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
|
105
|
-
# access (we want to print them out in the order in which they
|
106
|
-
# were
|
107
|
-
#
|
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
|
-
#
|
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
|
170
|
-
# than a block, so as to avoid the need to access
|
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.
|
180
|
-
#
|
181
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
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
|
-
# [
|
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
|
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
|
-
# [
|
424
|
-
#
|
425
|
-
#
|
426
|
-
# ====Returns:
|
473
|
+
# [reason]
|
474
|
+
# The reason for the error
|
427
475
|
#
|
428
|
-
|
429
|
-
|
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
|
-
|
472
|
-
if(value_class < BaseConfig)
|
520
|
+
def dump_value_impl(value)
|
521
|
+
if(value.class < BaseConfig)
|
473
522
|
return value.dump_impl({})
|
474
|
-
elsif(
|
523
|
+
elsif(value.class == Array)
|
475
524
|
dumped_array = []
|
476
525
|
value.each do |element|
|
477
|
-
dumped_array.push(dump_value_impl(
|
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(
|
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
|
-
# [
|
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
|
-
|
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
|
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
|
560
|
-
elsif((value_class < BaseConfig) && (
|
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
|
563
|
-
# construct the child with the
|
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.
|
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) && (
|
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
|
582
|
-
|
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,
|
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,
|
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
|
-
((
|
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
|
679
|
+
# the reader's value if it's true or false.
|
605
680
|
#
|
606
|
-
return
|
607
|
-
elsif(
|
681
|
+
return raw_value
|
682
|
+
elsif(raw_value.class == String)
|
608
683
|
#
|
609
|
-
# Some
|
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
|
-
#
|
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(
|
692
|
+
return Integer(raw_value)
|
618
693
|
when (value_class <= Float)
|
619
|
-
return Float(
|
694
|
+
return Float(raw_value)
|
620
695
|
when (value_class <= Pathname)
|
621
|
-
return Pathname(
|
696
|
+
return Pathname(raw_value)
|
622
697
|
when (value_class <= Symbol)
|
623
|
-
return
|
698
|
+
return raw_value.to_sym()
|
624
699
|
when (value_class <= URI)
|
625
|
-
return URI(
|
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((
|
705
|
+
if((raw_value.casecmp("yes") == 0) || (raw_value.casecmp("true") == 0))
|
631
706
|
return true
|
632
|
-
elsif((
|
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 #{
|
639
|
-
raise Error, construct_error_message(param_name,
|
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
|
-
|
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
|
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
|
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 << "#{
|
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
|
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
|
-
#
|
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
|
-
|
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(
|
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(
|
856
|
+
elsif(value.class < Array)
|
786
857
|
#
|
787
|
-
# Writing an array requires
|
788
|
-
#
|
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,
|
865
|
+
write_value_impl(str, nesting_indent, element)
|
796
866
|
|
797
|
-
if(index
|
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,
|
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
|
#
|