configtoolkit 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|