configtoolkit 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/FAQ.txt +113 -25
- data/Hash.txt +128 -0
- data/History.txt +45 -2
- data/KeyValue.txt +235 -0
- data/Manifest.txt +66 -4
- data/README.txt +666 -202
- data/Rakefile +3 -5
- data/Ruby.txt +172 -0
- data/YAML.txt +188 -0
- data/examples/hash_example.rb +71 -0
- data/examples/key_value_example.dump.cfg +5 -0
- data/examples/key_value_example.normal1.cfg +20 -0
- data/examples/key_value_example.normal2.cfg +15 -0
- data/examples/key_value_example.rb +72 -0
- data/examples/key_value_example.wacky.cfg +13 -0
- data/examples/load_example.rb +32 -0
- data/examples/load_example.yaml +34 -0
- data/examples/load_group_example.rb +28 -0
- data/examples/load_group_example.yaml +33 -0
- data/examples/machineconfig.rb +77 -0
- data/examples/ruby_example.rb +32 -0
- data/examples/ruby_example.rcfg +52 -0
- data/examples/usage_example.rb +93 -0
- data/examples/yaml_example.dump.yaml +12 -0
- data/examples/yaml_example.rb +48 -0
- data/examples/yaml_example.yaml +59 -0
- data/lib/configtoolkit.rb +1 -1
- data/lib/configtoolkit/baseconfig.rb +522 -418
- data/lib/configtoolkit/hasharrayvisitor.rb +242 -0
- data/lib/configtoolkit/hashreader.rb +41 -0
- data/lib/configtoolkit/hashwriter.rb +45 -0
- data/lib/configtoolkit/keyvalueconfig.rb +105 -0
- data/lib/configtoolkit/keyvaluereader.rb +597 -0
- data/lib/configtoolkit/keyvaluewriter.rb +157 -0
- data/lib/configtoolkit/prettyprintwriter.rb +167 -0
- data/lib/configtoolkit/reader.rb +62 -0
- data/lib/configtoolkit/rubyreader.rb +270 -0
- data/lib/configtoolkit/types.rb +42 -26
- data/lib/configtoolkit/writer.rb +116 -0
- data/lib/configtoolkit/yamlreader.rb +10 -6
- data/lib/configtoolkit/yamlwriter.rb +113 -71
- data/test/bad_array_index.rcfg +1 -0
- data/test/bad_containing_object_assignment.rcfg +2 -0
- data/test/bad_directive.cfg +1 -0
- data/test/bad_include1.cfg +2 -0
- data/test/bad_include2.cfg +3 -0
- data/test/bad_parameter_reference.rcfg +1 -0
- data/test/contained_sample.cfg +10 -0
- data/test/contained_sample.pretty_print +30 -0
- data/test/contained_sample.rcfg +22 -0
- data/test/contained_sample.yaml +22 -21
- data/test/containers.cfg +6 -0
- data/test/exta_string_after_container.cfg +2 -0
- data/test/extra_container_closing.cfg +2 -0
- data/test/extra_string_after_container.cfg +2 -0
- data/test/extra_string_after_nested_container.cfg +1 -0
- data/test/missing_array_closing.cfg +2 -0
- data/test/missing_array_element.cfg +2 -0
- data/test/missing_directive.cfg +1 -0
- data/test/missing_hash_closing.cfg +2 -0
- data/test/missing_hash_element.cfg +2 -0
- data/test/missing_hash_value.cfg +1 -0
- data/test/missing_include_argument.cfg +1 -0
- data/test/missing_key_value_delimiter.cfg +1 -0
- data/test/readerwritertest.rb +28 -7
- data/test/sample.cfg +10 -0
- data/test/sample.pretty_print +30 -0
- data/test/sample.rcfg +26 -0
- data/test/test_baseconfig.rb +152 -38
- data/test/test_hash.rb +82 -0
- data/test/test_keyvalue.rb +185 -0
- data/test/test_prettyprint.rb +28 -0
- data/test/test_ruby.rb +50 -0
- data/test/test_yaml.rb +33 -26
- data/test/wacky_sample1.cfg +16 -0
- data/test/wacky_sample2.cfg +5 -0
- data/test/wacky_sample3.cfg +4 -0
- data/test/wacky_sample4.cfg +1 -0
- metadata +101 -10
- data/lib/configtoolkit/toolkit.rb +0 -20
- data/test/common.rb +0 -5
data/FAQ.txt
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
=== Why can't I use a normal Ruby Array rather than a ConstrainedArray in my configuration's specification?
|
2
2
|
You can, but you probably don't want to. Actually, underneath, the
|
3
|
-
ConfigToolkit converts Array parameters into ConstrainedArray
|
4
|
-
no constraints
|
3
|
+
ConfigToolkit converts Array parameters into ConfigToolkit::ConstrainedArray
|
4
|
+
parameters with no constraints
|
5
|
+
(ConfigToolkit::ConstrainedArray.new(Object, nil, nil)).
|
5
6
|
If you do this and therefore don't specify an element class constraint
|
6
7
|
for the array, the ConfigToolkit will accept *any* value for an element
|
7
8
|
of the array. If this really is what you want, then go wild!
|
@@ -11,30 +12,34 @@ Every time a parameter's setter is called, the new value is validated
|
|
11
12
|
against the parameter's specification in order to ensure that it is
|
12
13
|
of the proper class (or can be converted to the proper class) *before* the
|
13
14
|
parameter is set. In addition, if a custom validation block was specified
|
14
|
-
for the
|
15
|
-
the
|
16
|
-
|
17
|
-
a file receive this validation.
|
18
|
-
|
19
|
-
In addition,
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
15
|
+
for the parameter, then this is called with the new value *before*
|
16
|
+
the parameter is set. Since all parameter values loaded from a file (during
|
17
|
+
a ConfigToolkit::BaseConfig#load call) are set with parameter setters,
|
18
|
+
all values loaded from a file receive this validation.
|
19
|
+
|
20
|
+
In addition, ConfigToolkit::BaseConfig#enforce_specs is called at the end of
|
21
|
+
ConfigToolkit::BaseConfig#load and at the start of
|
22
|
+
ConfigToolkit::BaseConfig#dump. This method checks that all required
|
23
|
+
parameters have been set (a ConfigToolkit::Error will be raised if not).
|
24
|
+
In addition, it sets any optional parameters that have not been set and that
|
25
|
+
have default values to their associated default values. Finally, it calls
|
26
|
+
ConfigToolkit::BaseConfig#validate_all_values in order to enforce any
|
27
|
+
constraints between the values of different parameters.
|
28
|
+
|
29
|
+
The above checks ensure that parameters are set with only valid values, and
|
30
|
+
that the configuration as a whole is valid right after load and right before
|
27
31
|
dump operations.
|
28
32
|
|
29
33
|
=== How/why does the ConfigToolkit convert parameter values from Strings to other classes?
|
30
|
-
Relatively
|
31
|
-
|
32
|
-
|
34
|
+
Relatively sophisticated configuration formats like YAML configuration files
|
35
|
+
support typed data (see YAML.txt). Simple configuration formats like
|
36
|
+
key-value files, however, do not support typed data; every parameter value
|
37
|
+
is read as a String (see KeyValue.txt). Since the ConfigToolkit *knows* the
|
33
38
|
specified class of a parameter value, however, it can attempt to convert the
|
34
39
|
String to the specified class. Unfortunately, Ruby does not have a standard
|
35
|
-
way to convert Strings to other classes (whereas it does have a standard way
|
36
|
-
convert classes into Strings, the to_s method). So, the ConfigToolkit
|
37
|
-
the best that it can and provides this conversion for the most common
|
40
|
+
way to convert Strings to other classes (whereas it does have a standard way
|
41
|
+
to convert classes into Strings, the +to_s+ method). So, the ConfigToolkit
|
42
|
+
does the best that it can and provides this conversion for the most common
|
38
43
|
types:
|
39
44
|
* Integers
|
40
45
|
* Floats
|
@@ -48,8 +53,91 @@ arbitrary types via some kind of extension mechanism, please feel free to
|
|
48
53
|
request the enhancement.
|
49
54
|
|
50
55
|
=== What kind of nesting is supported by the ConfigToolkit?
|
51
|
-
ConfigToolkit::BaseConfig child classes can be nested
|
52
|
-
within other
|
56
|
+
Configuration classes (ConfigToolkit::BaseConfig child classes) can be nested
|
57
|
+
as parameters within other configuration classes arbitrarily deep (README.txt
|
58
|
+
has an example of nesting one configuration within another). In
|
53
59
|
addition, one can have ConfigToolkit::ConstrainedArray parameters with
|
54
|
-
ConfigToolkit::BaseConfig
|
55
|
-
as a parameter).
|
60
|
+
configuration classes (ConfigToolkit::BaseConfig child classes) as element
|
61
|
+
classes (an array of configurations as a parameter value).
|
62
|
+
|
63
|
+
=== Which is the "best" configuration file format?
|
64
|
+
This depends, of course, on what one wants to do. README.txt references
|
65
|
+
documentation that provides background on each format that the ConfigToolkit
|
66
|
+
supports. There is no "best" format but here are some guidelines:
|
67
|
+
* Key-value formats are primitive but support probably is present in all
|
68
|
+
languages (and, if not, easily can be written). A former employer had
|
69
|
+
C++ and Perl code that read this format.
|
70
|
+
* Libraries to read YAML files exist for most popular languages, and the format
|
71
|
+
is very flexible.
|
72
|
+
* The Ruby configuration file format only is supported by Ruby (although, using
|
73
|
+
an embedded Ruby interpreter, it also probably could be read by C) but is
|
74
|
+
the most expressive, as arbitrary code can calculate configuration parameter
|
75
|
+
values when the file is loaded.
|
76
|
+
|
77
|
+
YAML offers a good balance of expressiveness, features, cross-language support,
|
78
|
+
cross-platform support, and ease of editing. I would recommend YAML files in
|
79
|
+
all cases, except:
|
80
|
+
* The file *ONLY* is going to be used by a Ruby application (and this *NEVER*
|
81
|
+
will change), and the configuration must be very dynamic, in which case Ruby
|
82
|
+
configuration files are appropriate.
|
83
|
+
* The file is going to be used in a wide variety of environments by Ruby and
|
84
|
+
non-Ruby applications. The non-Ruby applications in particular are going to
|
85
|
+
be running in environments in which a YAML library is not and never can be
|
86
|
+
made available. In this case, a key-value format is best, but the format
|
87
|
+
cannot use key-value extensions like Hashes, Arrays, or include directives
|
88
|
+
(since writing support for these in a key-value implementation in another
|
89
|
+
language is non-trivial)
|
90
|
+
|
91
|
+
=== How can configuration parameters be documented with rdoc?
|
92
|
+
Unfortunately, +rdoc+ cannot document configuration parameters by default. One
|
93
|
+
can tell it to treat ConfigToolkit::BaseConfig.add_required_param and
|
94
|
+
ConfigToolkit::BaseConfig.add_optional_param as attribute specifiers via
|
95
|
+
command-line options (the -A option), but this produces slightly broken
|
96
|
+
output because +rdoc+ misinterprets the second and third (if present) arguments
|
97
|
+
to the method as additional attributes (since +attr_reader+, for instance,
|
98
|
+
can take multiple Symbols as arguments, each of which becomes an attribute).
|
99
|
+
The +configtoolkit+ gem includes small patch to +rdoc+ (*only* to version 2.0.0,
|
100
|
+
available as a gem at http://rubyforge.org/projects/rdoc/) that fixes this for
|
101
|
+
ConfigToolkit::BaseConfig.add_required_param and
|
102
|
+
ConfigToolkit::BaseConfig.add_optional_param:
|
103
|
+
======+rdoc_support/rdoc.patch+:
|
104
|
+
--- parse_rb.rb 2008-07-14 12:25:06.000000000 -0400
|
105
|
+
+++ /usr/lib/ruby/gems/1.8/gems/rdoc-2.0.0/lib/rdoc/parsers/parse_rb.rb 2008-07-14 12:32:02.000000000 -0400
|
106
|
+
@@ -2505,6 +2505,21 @@
|
107
|
+
rw = @options.extra_accessor_flags[tk.name]
|
108
|
+
end
|
109
|
+
|
110
|
+
+ #
|
111
|
+
+ # This is a temporary hack.
|
112
|
+
+ # At some point in the (near) future, this should be specifiable via
|
113
|
+
+ # rdoc's command-line options.
|
114
|
+
+ #
|
115
|
+
+ # Unlike the attr* family of functions, only the first argument of
|
116
|
+
+ # the add*param functions is an attribute; the other arguments
|
117
|
+
+ # constrain the attribute.
|
118
|
+
+ #
|
119
|
+
+ if((tk.name == "add_optional_param") || (tk.name == "add_required_param"))
|
120
|
+
+ if(args.size > 1)
|
121
|
+
+ args[1, args.size - 1] = nil
|
122
|
+
+ end
|
123
|
+
+ end
|
124
|
+
+
|
125
|
+
for name in args
|
126
|
+
att = RDoc::Attr.new get_tkread, name, rw, comment
|
127
|
+
context.add_attribute att
|
128
|
+
|
129
|
+
The patch should be applied to +rdoc-2.0.0/lib/rdoc/parsers/parse_rb.rb+ in the
|
130
|
+
Ruby gems directory with the +patch+ utility.
|
131
|
+
|
132
|
+
After applying this patch, +rdoc+ should register the parameters as attributes
|
133
|
+
with this command-line option:
|
134
|
+
--accessor=add_optional_param=rw,add_required_param=rw
|
135
|
+
|
136
|
+
As with standard attributes, +rdoc+ will display a comment block above the
|
137
|
+
call to add a new parameter in the generated HTML along with the attribute. The
|
138
|
+
ConfigToolkit::KeyValueConfig documentation was produced like this.
|
139
|
+
|
140
|
+
I realize that this is an unsatisfactory solution and have it on the
|
141
|
+
+configtoolkit+ TODO list (see README.txt) to craft a better one (one that
|
142
|
+
does not require patching rdoc). If this is particularly important to you,
|
143
|
+
please let me know so that I can prioritize this work.
|
data/Hash.txt
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
== LOADING AND DUMPING CONFIGURATIONS FROM AND TO HASHES:
|
2
|
+
Reader classes extending the ConfigToolkit::Reader interface implement
|
3
|
+
a +read+ method (see ConfigToolkit::Reader#read) that reads configuration
|
4
|
+
from some underlying data source and returns it to ConfigToolkit::BaseConfig
|
5
|
+
as a Hash. Writer classes extending the ConfigToolkit::Writer interface
|
6
|
+
implement a +write+ method (see ConfigToolkit::Writer#write)
|
7
|
+
that receives a configuration Hash from ConfigToolkit::BaseConfig and writes it
|
8
|
+
to some data sink. ConfigToolkit::HashReader and ConfigToolkit::HashWriter
|
9
|
+
implement this interface in order to allow programmers to specify and access
|
10
|
+
configurations as Hashes directly. ConfigToolkit::HashReader is particularly
|
11
|
+
useful in that it allows ConfigToolkit::BaseConfig#load to be run on data
|
12
|
+
specified programatically in a Hash.
|
13
|
+
|
14
|
+
== EXAMPLE:
|
15
|
+
======+examples/hash_example.rb+:
|
16
|
+
#!/usr/bin/env ruby
|
17
|
+
|
18
|
+
#
|
19
|
+
# These example programs use the 'relative' gem in order to
|
20
|
+
# express paths relative to __FILE__ cleanly, allowing them to be run from
|
21
|
+
# any directory. In particular, they use the require_relative
|
22
|
+
# method to load examples/machineconfig.rb and
|
23
|
+
# File.expand_path_relative_to_caller in order to refer to
|
24
|
+
# configuration files within the examples directory.
|
25
|
+
#
|
26
|
+
require 'rubygems'
|
27
|
+
require 'relative'
|
28
|
+
require_relative 'machineconfig'
|
29
|
+
|
30
|
+
require 'configtoolkit/hashreader'
|
31
|
+
require 'configtoolkit/hashwriter'
|
32
|
+
|
33
|
+
#
|
34
|
+
# The first configuration has no containing object name.
|
35
|
+
#
|
36
|
+
config_hash = {
|
37
|
+
:num_cpus => 32,
|
38
|
+
:os => {
|
39
|
+
:name => "AIX",
|
40
|
+
:version => 5.3
|
41
|
+
},
|
42
|
+
:behind_firewall => false,
|
43
|
+
:contains_sensitive_data => false,
|
44
|
+
:addresses => [
|
45
|
+
URI("http://default.designingpatterns.com"),
|
46
|
+
URI("http://apple.designingpatterns.com")
|
47
|
+
]
|
48
|
+
}
|
49
|
+
reader = ConfigToolkit::HashReader.new(config_hash)
|
50
|
+
first_config = MachineConfig.load(reader)
|
51
|
+
print("The first config:\n#{first_config}\n")
|
52
|
+
|
53
|
+
#
|
54
|
+
# The second configuration has "production.www" as a containing
|
55
|
+
# object name and therefore the configuration is mapped to by
|
56
|
+
# :www in a Hash, which in turn is mapped to by :production
|
57
|
+
# in a containing Hash.
|
58
|
+
#
|
59
|
+
config_hash = {
|
60
|
+
:production => {
|
61
|
+
:www => {
|
62
|
+
:num_cpus => 64,
|
63
|
+
:os => {
|
64
|
+
:name => "Solaris",
|
65
|
+
:version => 10.0
|
66
|
+
},
|
67
|
+
:contains_sensitive_data => true,
|
68
|
+
:addresses => [
|
69
|
+
URI("http://www.designingpatterns.com"),
|
70
|
+
URI("http://tokyo.designingpatterns.com")
|
71
|
+
]
|
72
|
+
}
|
73
|
+
}
|
74
|
+
}
|
75
|
+
reader = ConfigToolkit::HashReader.new(config_hash)
|
76
|
+
second_config = MachineConfig.load(reader, "production.www")
|
77
|
+
print("The second config:\n#{second_config}\n")
|
78
|
+
|
79
|
+
#
|
80
|
+
# After the call to dump, the config_hash instance member of the HashWriter
|
81
|
+
# instance contains the Hash representation of second_config.
|
82
|
+
#
|
83
|
+
writer = ConfigToolkit::HashWriter.new()
|
84
|
+
second_config.dump(writer)
|
85
|
+
print("The Hash dumped from the second config:\n")
|
86
|
+
print("#{writer.config_hash.inspect}\n")
|
87
|
+
|
88
|
+
This program loads two configurations, one with a containing object and one
|
89
|
+
without a containing object, directly from Hashes. Note how the nested
|
90
|
+
+OSConfig+ parameter was represented as a nested Hash. Also note how the
|
91
|
+
second configuration's containing object was represented by nesting the
|
92
|
+
configuration's Hash within Hashes representing the containing object, one
|
93
|
+
Hash for each member of the containing object (the Hash for +production+
|
94
|
+
contained a Hash for +www+ that in turn contained the configuration Hash).
|
95
|
+
When run, the program produces:
|
96
|
+
======The output of <code>examples/hash_example.rb</code>:
|
97
|
+
The first config:
|
98
|
+
{
|
99
|
+
addresses : [
|
100
|
+
http://default.designingpatterns.com,
|
101
|
+
http://apple.designingpatterns.com
|
102
|
+
]
|
103
|
+
num_cpus : 32
|
104
|
+
os : {
|
105
|
+
version: 5.3
|
106
|
+
name : AIX
|
107
|
+
}
|
108
|
+
behind_firewall : false
|
109
|
+
contains_sensitive_data: false
|
110
|
+
}
|
111
|
+
|
112
|
+
The second config:
|
113
|
+
production.www: {
|
114
|
+
addresses : [
|
115
|
+
http://www.designingpatterns.com,
|
116
|
+
http://tokyo.designingpatterns.com
|
117
|
+
]
|
118
|
+
num_cpus : 64
|
119
|
+
os : {
|
120
|
+
version: 10.0
|
121
|
+
name : Solaris
|
122
|
+
}
|
123
|
+
behind_firewall : true
|
124
|
+
contains_sensitive_data: true
|
125
|
+
}
|
126
|
+
|
127
|
+
The Hash dumped from the second config:
|
128
|
+
{:production=>{:www=>{:addresses=>[#<URI::HTTP:0xb7e2f8d0 URL:http://www.designingpatterns.com>, #<URI::HTTP:0xb7e2f880 URL:http://tokyo.designingpatterns.com>], :num_cpus=>64, :os=>{:version=>10.0, :name=>"Solaris"}, :behind_firewall=>true, :contains_sensitive_data=>true}}}
|
data/History.txt
CHANGED
@@ -1,6 +1,49 @@
|
|
1
|
+
=== 2.0.0 / 2008-07-03
|
2
|
+
This version of the ConfigToolkit has a ton of enhancements. I really would
|
3
|
+
welcome input from the community about it!
|
4
|
+
* IMPORTANT INTERFACE CHANGE: ConfigToolkit::BaseConfig#load_group now
|
5
|
+
returns a Hash mapping Symbols to config instances, not
|
6
|
+
a Hash mapping Strings to config instances.
|
7
|
+
* IMPORTANT INTERFACE CHANGE: The Hash passed to ConfigToolkit::Writer#write
|
8
|
+
uses Symbols instead of Strings for parameter names, unless
|
9
|
+
the writer returns +false+ from
|
10
|
+
ConfigToolkit::Writer#require_symbol_parameter_names?.
|
11
|
+
* IMPORTANT INTERFACE CHANGE: ConfigToolkit::Writer#write must
|
12
|
+
accept two arguments, the configuration hash and the containing
|
13
|
+
object name.
|
14
|
+
* Add support for Ruby configuration files through the
|
15
|
+
ConfigToolkit::RubyReader class (allowing the full power of Ruby to be
|
16
|
+
used within configuration files; the format is very similar to that used by
|
17
|
+
some of the Rails configuration files). See Ruby.txt for more details.
|
18
|
+
* Add support for key-value configuration files through the
|
19
|
+
ConfigToolkit::KeyValueReader and the ConfigToolkit::KeyValueWriter.
|
20
|
+
See KeyValue.txt for more details.
|
21
|
+
* Add support for loading and dumping configurations directly to and from
|
22
|
+
Hashes through the ConfigToolkit::HashReader and ConfigToolkit::HashWriter
|
23
|
+
(allowing configurations to be loaded programatically).
|
24
|
+
See Hash.txt for more details.
|
25
|
+
* Provide a way for +rdoc+ to detect and generate documentation for each
|
26
|
+
parameter in a configuration class, just as it does for attributes (see
|
27
|
+
FAQ.txt for how to enable this).
|
28
|
+
* Allow a block to be passed to ConfigToolkit::BaseConfig.new for
|
29
|
+
initialization of the configuration parameters.
|
30
|
+
* Allow optional parameters to not have default values, meaning that
|
31
|
+
optional parameters sometimes can be absent. A method to delete
|
32
|
+
the value of an optional parameter now is generated for each optional
|
33
|
+
parameter (+clear_param_name+).
|
34
|
+
* A predicate method now is generated for each parameter that returns
|
35
|
+
whether or not a value for the parameter is present (+param_name?+).
|
36
|
+
* Greatly improve the documentation. The YAML configuration file format
|
37
|
+
is described by YAML.txt; the key-value configuration file format is
|
38
|
+
described by KeyValue.txt; the Ruby configuration file format is described
|
39
|
+
by Ruby.txt; programatically loading configurations from and dumping
|
40
|
+
configurations to Hashes is described in Hash.txt.
|
41
|
+
* Many example programs exist in the +examples+ subdirectory (and are
|
42
|
+
integrated into the documentation).
|
43
|
+
|
1
44
|
=== 1.2.0 / 2008-05-20
|
2
|
-
Add BaseConfig
|
3
|
-
one call. Corrected a bug in how array parameters are printed.
|
45
|
+
Add ConfigToolkit::BaseConfig#load_group, which allows a hash of configs to
|
46
|
+
be loaded with one call. Corrected a bug in how array parameters are printed.
|
4
47
|
|
5
48
|
=== 1.1.0 / 2008-05-14
|
6
49
|
Finish the README.txt; this is the first production release!
|
data/KeyValue.txt
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
== KEY-VALUE CONFIGURATION FILES:
|
2
|
+
Key-value configuration files are quite common, although they come in a
|
3
|
+
myriad of different formats. +/etc/sysctl.conf+ on my system (CentOS 5.1), for
|
4
|
+
instance, uses "=" characters to divide keys and values.
|
5
|
+
+/etc/nsswitch.conf+, on the other hand, uses ":" characters to divide keys
|
6
|
+
and values.
|
7
|
+
|
8
|
+
The ConfigToolkit accomodates this diversity by allowing
|
9
|
+
ConfigToolkit::KeyValueReader and ConfigToolkit::KeyValueWriter
|
10
|
+
instances to be configured themselves with the format of the file to
|
11
|
+
be read or written. Configuration is accomplished through the
|
12
|
+
ConfigToolkit::KeyValueConfig class, which allows various key-value
|
13
|
+
format characteristics to be specified (including key-value
|
14
|
+
delimiter). Every ConfigToolkit::KeyValueConfig parameter has a
|
15
|
+
default value, so only parameters with values different than the
|
16
|
+
default need to be specified when creating a new
|
17
|
+
ConfigToolkit::KeyValueConfig instance. A full list of the parameters
|
18
|
+
can been seen in the ConfigToolkit::KeyValueConfig documentation.
|
19
|
+
|
20
|
+
The ConfigToolkit also extends the key-value format to allow hashes
|
21
|
+
and arrays (nested to any depth) to be values. In addition, ConfigToolkit
|
22
|
+
key-value files can include other key-value files through the use of include
|
23
|
+
directives. Include directives can handle absolute and relative (to the
|
24
|
+
configuration file currently being processed) file paths. Finally,
|
25
|
+
ConfigToolkit key-value files support specifying containing objects
|
26
|
+
in keys. Of course, if a key-value configuration file also must be read by
|
27
|
+
a program written in a different language, these extensions cannot be
|
28
|
+
used.
|
29
|
+
|
30
|
+
All configuration for a single parameter must be contained on a single line,
|
31
|
+
whatever the ConfigToolkit::KeyValueConfig. This can make
|
32
|
+
complicated confgurations containing hashes and arrays messy; YAML
|
33
|
+
represents such configurations more easily (see YAML.txt for details).
|
34
|
+
The Ruby configuration file format also offers a key-value like configuration
|
35
|
+
syntax but allows hashes and arrays to be spread across multiple lines (as
|
36
|
+
this is allowed by the Ruby language; see Ruby.txt for details); this may
|
37
|
+
an acceptable alternative if only Ruby applications ever will need to read
|
38
|
+
the file (see FAQ.txt for more discussion about which format to choose for
|
39
|
+
a new file).
|
40
|
+
|
41
|
+
== EXAMPLE:
|
42
|
+
The following configuration file contains one configuration in a common
|
43
|
+
key-value format (the ConfigToolkit's default key-value format, in fact):
|
44
|
+
======+examples/key_value_example.normal1.cfg+:
|
45
|
+
#
|
46
|
+
# This file contains MachineConfig configurations
|
47
|
+
# (see examples/machineconfig.rb for the configuration's specification).
|
48
|
+
#
|
49
|
+
|
50
|
+
############################################################
|
51
|
+
# First configuration
|
52
|
+
num_cpus = 32
|
53
|
+
os = {name => AIX, version => 5.3}
|
54
|
+
behind_firewall = no
|
55
|
+
contains_sensitive_data = no
|
56
|
+
addresses = [http://default.designingpatterns.com, http://apple.designingpatterns.com]
|
57
|
+
############################################################
|
58
|
+
|
59
|
+
#
|
60
|
+
# Include a second configuration from a different file.
|
61
|
+
# Note that the path to the second file is relative to
|
62
|
+
# *this* file (they are in the same directory).
|
63
|
+
#
|
64
|
+
*include key_value_example.normal2.cfg
|
65
|
+
|
66
|
+
It includes a second key-value configuration file containing another
|
67
|
+
configuration (in the same key-value format):
|
68
|
+
======+examples/key_value_example.normal2.cfg+:
|
69
|
+
#
|
70
|
+
# This file contains MachineConfig configurations
|
71
|
+
# (see examples/machineconfig.rb for the configuration's specification).
|
72
|
+
#
|
73
|
+
|
74
|
+
############################################################
|
75
|
+
# Second configuration (nested in the production.www
|
76
|
+
# containing object). Note how all of the keys are prefixed
|
77
|
+
# with production.www in order to indicate that they are
|
78
|
+
# within the production.www containing object.
|
79
|
+
production.www.num_cpus = 64
|
80
|
+
production.www.os = {name => Solaris, version => 10.0}
|
81
|
+
production.www.contains_sensitive_data = yes
|
82
|
+
production.www.addresses = [http://www.designingpatterns.com, http://tokyo.designingpatterns.com]
|
83
|
+
############################################################
|
84
|
+
|
85
|
+
Note that the second configuration has a +production.www+ containing object,
|
86
|
+
and so that all keys are prefixed with +production.www+. The first
|
87
|
+
configuration also is represented with a different key-value
|
88
|
+
format (the format uses ":" instead of "=" to separate keys and
|
89
|
+
values, "<<-->>" instead of "=>" to separate hash keys and values, and
|
90
|
+
";" instead of "#" to prefix comments) in this file:
|
91
|
+
======+examples/key_value_example.wacky.cfg+:
|
92
|
+
;
|
93
|
+
; This file contains MachineConfig configurations
|
94
|
+
; (see examples/machineconfig.rb for the configuration's specification).
|
95
|
+
;
|
96
|
+
|
97
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
98
|
+
; First configuration, but with a wacky format
|
99
|
+
num_cpus: 32
|
100
|
+
os: {name <<-->> AIX, version <<-->> 5.3}
|
101
|
+
behind_firewall: no
|
102
|
+
contains_sensitive_data: no
|
103
|
+
addresses: [http://default.designingpatterns.com, http://apple.designingpatterns.com]
|
104
|
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
105
|
+
|
106
|
+
The following program loads these configurations and writes the second
|
107
|
+
configuration in a new key-value format:
|
108
|
+
======+examples/key_value_example.rb+:
|
109
|
+
#!/usr/bin/env ruby
|
110
|
+
|
111
|
+
#
|
112
|
+
# These example programs use the 'relative' gem in order to
|
113
|
+
# express paths relative to __FILE__ cleanly, allowing them to be run from
|
114
|
+
# any directory. In particular, they use the require_relative
|
115
|
+
# method to load examples/machineconfig.rb and
|
116
|
+
# File.expand_path_relative_to_caller in order to refer to
|
117
|
+
# configuration files within the examples directory.
|
118
|
+
#
|
119
|
+
require 'rubygems'
|
120
|
+
require 'relative'
|
121
|
+
require_relative 'machineconfig'
|
122
|
+
|
123
|
+
require 'configtoolkit/keyvaluereader'
|
124
|
+
require 'configtoolkit/keyvaluewriter'
|
125
|
+
|
126
|
+
#
|
127
|
+
# The key-value format used by this file is the default format.
|
128
|
+
#
|
129
|
+
NORMAL_CONFIGURATION_FILE =
|
130
|
+
File.expand_path_relative_to_caller("key_value_example.normal1.cfg")
|
131
|
+
|
132
|
+
#
|
133
|
+
# The first configuration has no containing object name.
|
134
|
+
#
|
135
|
+
reader = ConfigToolkit::KeyValueReader.new(NORMAL_CONFIGURATION_FILE)
|
136
|
+
first_config = MachineConfig.load(reader)
|
137
|
+
print("The first config:\n#{first_config}\n")
|
138
|
+
|
139
|
+
#
|
140
|
+
# The second configuration has "production.www" as a containing
|
141
|
+
# object name. Note that the second configuration is contained in a different
|
142
|
+
# file that the first file includes.
|
143
|
+
#
|
144
|
+
reader = ConfigToolkit::KeyValueReader.new(NORMAL_CONFIGURATION_FILE)
|
145
|
+
second_config = MachineConfig.load(reader, "production.www")
|
146
|
+
print("The second config:\n#{second_config}\n")
|
147
|
+
|
148
|
+
#
|
149
|
+
# Configure a new, admittedly wacky, key-value format.
|
150
|
+
# Note how the block passed to the constructor initializes
|
151
|
+
# only a few of the KeyValueConfig parameters; the other
|
152
|
+
# parameters will be assigned their default values.
|
153
|
+
#
|
154
|
+
key_value_config = ConfigToolkit::KeyValueConfig.new() do |config|
|
155
|
+
config.key_value_delimiter = ":"
|
156
|
+
config.comment_line_prefix = ";"
|
157
|
+
config.hash_key_value_delimiter = "<<-->>"
|
158
|
+
end
|
159
|
+
|
160
|
+
#
|
161
|
+
# Read the first configuration from a file containing it in the new
|
162
|
+
# format.
|
163
|
+
#
|
164
|
+
WACKY_CONFIGURATION_FILE =
|
165
|
+
File.expand_path_relative_to_caller("key_value_example.wacky.cfg")
|
166
|
+
reader = ConfigToolkit::KeyValueReader.new(WACKY_CONFIGURATION_FILE,
|
167
|
+
key_value_config)
|
168
|
+
first_config = MachineConfig.load(reader)
|
169
|
+
print("The first config sourced from a different key-value format:\n")
|
170
|
+
print("#{first_config}\n")
|
171
|
+
|
172
|
+
#
|
173
|
+
# Write second_config in the new format to a string stream, the contents
|
174
|
+
# of which will be printed.
|
175
|
+
#
|
176
|
+
string_stream = StringIO.new()
|
177
|
+
writer = ConfigToolkit::KeyValueWriter.new(string_stream, key_value_config)
|
178
|
+
second_config.dump(writer)
|
179
|
+
print("The second configuration dumped to a new key-value file format:\n")
|
180
|
+
print("#{string_stream.string}")
|
181
|
+
|
182
|
+
When run, it produces:
|
183
|
+
======The output of <code>examples/key_value_example.rb</code>:
|
184
|
+
The first config:
|
185
|
+
{
|
186
|
+
behind_firewall : false
|
187
|
+
num_cpus : 32
|
188
|
+
addresses : [
|
189
|
+
http://default.designingpatterns.com,
|
190
|
+
http://apple.designingpatterns.com
|
191
|
+
]
|
192
|
+
contains_sensitive_data: false
|
193
|
+
os : {
|
194
|
+
name : AIX
|
195
|
+
version: 5.3
|
196
|
+
}
|
197
|
+
}
|
198
|
+
|
199
|
+
The second config:
|
200
|
+
production.www: {
|
201
|
+
behind_firewall : true
|
202
|
+
num_cpus : 64
|
203
|
+
addresses : [
|
204
|
+
http://www.designingpatterns.com,
|
205
|
+
http://tokyo.designingpatterns.com
|
206
|
+
]
|
207
|
+
contains_sensitive_data: true
|
208
|
+
os : {
|
209
|
+
name : Solaris
|
210
|
+
version: 10.0
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
The first config sourced from a different key-value format:
|
215
|
+
{
|
216
|
+
behind_firewall : false
|
217
|
+
num_cpus : 32
|
218
|
+
addresses : [
|
219
|
+
http://default.designingpatterns.com,
|
220
|
+
http://apple.designingpatterns.com
|
221
|
+
]
|
222
|
+
contains_sensitive_data: false
|
223
|
+
os : {
|
224
|
+
name : AIX
|
225
|
+
version: 5.3
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
The second configuration dumped to a new key-value file format:
|
230
|
+
production.www.behind_firewall : true
|
231
|
+
production.www.num_cpus : 64
|
232
|
+
production.www.addresses : [http://www.designingpatterns.com, http://tokyo.designingpatterns.com]
|
233
|
+
production.www.contains_sensitive_data : true
|
234
|
+
production.www.os : {name <<-->> Solaris, version <<-->> 10.0}
|
235
|
+
|