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/Rakefile
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
|
3
|
-
$LOAD_PATH.unshift("lib")
|
4
|
-
|
5
3
|
require 'rubygems'
|
6
4
|
require 'hoe'
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
Hoe.new('configtoolkit', "2.0.0") do |p|
|
7
|
+
p.extra_deps << ['relative', '>= 1.0.0']
|
8
|
+
p.extra_deps << ['assertions', '>= 1.0.0']
|
11
9
|
p.remote_rdoc_dir = ''
|
12
10
|
p.developer('DesigningPatterns', 'technical.inquiries@designingpatterns.com')
|
13
11
|
end
|
data/Ruby.txt
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
== RUBY CONFIGURATION FILES:
|
2
|
+
What could be better than configuring Ruby programs with Ruby
|
3
|
+
configuration files, harnessing the full power of Ruby for
|
4
|
+
configuration? The ConfigToolkit::RubyReader supports a Ruby
|
5
|
+
configuration file format that allows configurations to leverage the
|
6
|
+
expressiveness of the Ruby language in a way that's more natural than
|
7
|
+
programatically initializing the configuration (via the
|
8
|
+
ConfigToolkit::HashReader, for instance).
|
9
|
+
|
10
|
+
Ruby configuration files essentially are key-value configuration files
|
11
|
+
parsed by the Ruby interpreter; they allow the full Ruby language to
|
12
|
+
be used while building up a configuration. Parameters are specified
|
13
|
+
by +config.param_name+ or +config.containing_object.param_name+. They
|
14
|
+
can be assigned to and, after assignment, used in other expressions.
|
15
|
+
If a name first is referred to with a setter (i.e., +age+ in
|
16
|
+
<code>config.first.second.age = 5</code>), then the name is assumed to
|
17
|
+
be a parameter name. If a name first is referred to with a getter
|
18
|
+
(i.e., +first+ and +second+ in <code>config.first.second.age =
|
19
|
+
5</code>), then the name is assumed to be an object name (either a
|
20
|
+
containing or a nested configuration object). Objects cannot be set
|
21
|
+
(i.e., <code>config.first.second = 2</code> is illegal). Parameter
|
22
|
+
values, once set, can be referenced in later expressions.
|
23
|
+
|
24
|
+
No +RubyWriter+ class is provided, since the strength of the format is
|
25
|
+
supporting dynamic expressions that calculate configuration values,
|
26
|
+
and any configuration file output by a writer only could consist of
|
27
|
+
static expressions.
|
28
|
+
|
29
|
+
== EXAMPLE:
|
30
|
+
This is a Ruby configuration file:
|
31
|
+
======+examples/ruby_example.rcfg+:
|
32
|
+
#
|
33
|
+
# This file contains MachineConfig configurations
|
34
|
+
# (see examples/machineconfig.rb for the configuration's specification).
|
35
|
+
#
|
36
|
+
|
37
|
+
require 'uri'
|
38
|
+
|
39
|
+
config.num_cpus = 32
|
40
|
+
|
41
|
+
#
|
42
|
+
# Note how the members of the nested OSConfig
|
43
|
+
# can be set by first referring to "os" and then to the
|
44
|
+
# desired member (i.e., config.os.name). config.os
|
45
|
+
# also could have been set with a Hash (see below
|
46
|
+
# for an example).
|
47
|
+
#
|
48
|
+
config.os.name = "AIX"
|
49
|
+
config.os.version = 5.3
|
50
|
+
|
51
|
+
config.behind_firewall = false
|
52
|
+
|
53
|
+
#
|
54
|
+
# Notice how the behind_firewall value can be referenced
|
55
|
+
# in the contains_sensitive_data value assignment, since
|
56
|
+
# it was set above.
|
57
|
+
#
|
58
|
+
config.contains_sensitive_data = config.behind_firewall
|
59
|
+
config.addresses = [
|
60
|
+
URI("http://default.designingpatterns.com"),
|
61
|
+
URI("http://apple.designingpatterns.com")
|
62
|
+
]
|
63
|
+
|
64
|
+
#
|
65
|
+
# A trivial example of using a Ruby expression to calculate
|
66
|
+
# a parameter value.
|
67
|
+
#
|
68
|
+
config.production.www.num_cpus = 32 + 16 + 8 + 4 + 2 + 1 + 1
|
69
|
+
|
70
|
+
#
|
71
|
+
# Note that here the nested OSConfig is being set with a Hash (the
|
72
|
+
# configuration above sets the OSConfig parameters individually).
|
73
|
+
#
|
74
|
+
config.production.www.os = {
|
75
|
+
:name => "Solaris",
|
76
|
+
:version => 10.0
|
77
|
+
}
|
78
|
+
|
79
|
+
config.production.www.contains_sensitive_data = true
|
80
|
+
config.production.www.addresses = [
|
81
|
+
URI("http://www.designingpatterns.com"),
|
82
|
+
URI("http://tokyo.designingpatterns.com")
|
83
|
+
]
|
84
|
+
|
85
|
+
This program loads the two configurations in the file:
|
86
|
+
======+examples/ruby_example.rb+:
|
87
|
+
#!/usr/bin/env ruby
|
88
|
+
|
89
|
+
#
|
90
|
+
# These example programs use the 'relative' gem in order to
|
91
|
+
# express paths relative to __FILE__ cleanly, allowing them to be run from
|
92
|
+
# any directory. In particular, they use the require_relative
|
93
|
+
# method to load examples/machineconfig.rb and
|
94
|
+
# File.expand_path_relative_to_caller in order to refer to
|
95
|
+
# configuration files within the examples directory.
|
96
|
+
#
|
97
|
+
require 'rubygems'
|
98
|
+
require 'relative'
|
99
|
+
require_relative 'machineconfig'
|
100
|
+
|
101
|
+
require 'configtoolkit/rubyreader'
|
102
|
+
|
103
|
+
CONFIGURATION_FILE = File.expand_path_relative_to_caller("ruby_example.rcfg")
|
104
|
+
|
105
|
+
#
|
106
|
+
# The first configuration has no containing object name.
|
107
|
+
#
|
108
|
+
reader = ConfigToolkit::RubyReader.new(CONFIGURATION_FILE)
|
109
|
+
first_config = MachineConfig.load(reader)
|
110
|
+
print("The first config:\n#{first_config}\n")
|
111
|
+
|
112
|
+
#
|
113
|
+
# The second configuration has "production.www" as a containing
|
114
|
+
# object name.
|
115
|
+
#
|
116
|
+
reader = ConfigToolkit::RubyReader.new(CONFIGURATION_FILE)
|
117
|
+
second_config = MachineConfig.load(reader, "production.www")
|
118
|
+
print("The second config:\n#{second_config}\n")
|
119
|
+
|
120
|
+
When run, it produces the following output:
|
121
|
+
======The output of <code>examples/ruby_example.rb</code>:
|
122
|
+
The first config:
|
123
|
+
{
|
124
|
+
os : {
|
125
|
+
name : AIX
|
126
|
+
version: 5.3
|
127
|
+
}
|
128
|
+
behind_firewall : false
|
129
|
+
num_cpus : 32
|
130
|
+
addresses : [
|
131
|
+
http://default.designingpatterns.com,
|
132
|
+
http://apple.designingpatterns.com
|
133
|
+
]
|
134
|
+
contains_sensitive_data: false
|
135
|
+
}
|
136
|
+
|
137
|
+
The second config:
|
138
|
+
production.www: {
|
139
|
+
os : {
|
140
|
+
name : Solaris
|
141
|
+
version: 10.0
|
142
|
+
}
|
143
|
+
behind_firewall : true
|
144
|
+
num_cpus : 64
|
145
|
+
addresses : [
|
146
|
+
http://www.designingpatterns.com,
|
147
|
+
http://tokyo.designingpatterns.com
|
148
|
+
]
|
149
|
+
contains_sensitive_data: true
|
150
|
+
}
|
151
|
+
|
152
|
+
|
153
|
+
|
154
|
+
== FUTURE DIRECTIONS:
|
155
|
+
The Ruby configuration language could be improved in several ways. Firstly,
|
156
|
+
it could provide better support for arrays. For instance, the following
|
157
|
+
configuration file is invalid right now:
|
158
|
+
config.array_param[0] = 3
|
159
|
+
In the future, the parser automatically could detect that +array_param+ is
|
160
|
+
an array and allow array elements to be set.
|
161
|
+
|
162
|
+
In addition, the following configuration file also is invalid right now:
|
163
|
+
config.array_param = []
|
164
|
+
config.array_param[0].fixnum_param = 3
|
165
|
+
config.array_param[0].string_param = "hello"
|
166
|
+
That is, array elements could support object notation. Right now, this would
|
167
|
+
have to be written with Hashes:
|
168
|
+
config.array_param = []
|
169
|
+
config.array_param[0] = {:fixnum_param => 3, :string_param => "hello"}
|
170
|
+
|
171
|
+
We would love feedback and suggestions from the community about future
|
172
|
+
directions for this configuration language!
|
data/YAML.txt
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
== YAML CONFIGURATION FILES:
|
2
|
+
YAML is a clean, flexible data representation language that easily can
|
3
|
+
represent complicated configurations. It natively supports simple
|
4
|
+
types (integers, floats, strings, booleans), and, since Ruby supports
|
5
|
+
serialization of objects to YAML, Ruby's YAML library supports
|
6
|
+
representing objects of virtually any type in YAML. Support for YAML
|
7
|
+
is available for many programming languages, making it attractive for
|
8
|
+
configurations that must be read by several different languages.
|
9
|
+
|
10
|
+
The ConfigToolkit::YAMLReader and ConfigToolkit::YAMLWriter allow
|
11
|
+
configurations to be loaded from and dumped to YAML files.
|
12
|
+
|
13
|
+
== EXAMPLE:
|
14
|
+
The parameter values in YAML configuration files can be native YAML
|
15
|
+
types (strings, integers, floats, booleans, etc.) and also can be
|
16
|
+
serialized Ruby classes. The following configuration file
|
17
|
+
demonstrates this:
|
18
|
+
======+examples/yaml_example.yaml+:
|
19
|
+
#
|
20
|
+
# This file contains MachineConfig configurations
|
21
|
+
# (see examples/machineconfig.rb for the configuration's specification).
|
22
|
+
#
|
23
|
+
|
24
|
+
############################################################
|
25
|
+
# First configuration
|
26
|
+
num_cpus: 32
|
27
|
+
os:
|
28
|
+
name: AIX
|
29
|
+
version: 5.3
|
30
|
+
behind_firewall: no
|
31
|
+
contains_sensitive_data: no
|
32
|
+
addresses:
|
33
|
+
- http://default.designingpatterns.com
|
34
|
+
- http://apple.designingpatterns.com
|
35
|
+
############################################################
|
36
|
+
|
37
|
+
production:
|
38
|
+
############################################################
|
39
|
+
# Second configuration (nested in the production.www
|
40
|
+
# containing object). Note that the elements of the
|
41
|
+
# addresses parameter array are expressed
|
42
|
+
# not as strings, as in the first configuration, but rather
|
43
|
+
# as serialized Ruby URI instances. The YAMLReader class
|
44
|
+
# can read both, and the YAMLWriter class can
|
45
|
+
# output values of either only YAML native types or of serialized Ruby
|
46
|
+
# classes.
|
47
|
+
www:
|
48
|
+
contains_sensitive_data: true
|
49
|
+
addresses:
|
50
|
+
- !ruby/object:URI::HTTP
|
51
|
+
fragment:
|
52
|
+
host: www.designingpatterns.com
|
53
|
+
opaque:
|
54
|
+
password:
|
55
|
+
path: ""
|
56
|
+
port: 80
|
57
|
+
query:
|
58
|
+
registry:
|
59
|
+
scheme: http
|
60
|
+
user:
|
61
|
+
- !ruby/object:URI::HTTP
|
62
|
+
fragment:
|
63
|
+
host: tokyo.designingpatterns.com
|
64
|
+
opaque:
|
65
|
+
password:
|
66
|
+
path: ""
|
67
|
+
port: 80
|
68
|
+
query:
|
69
|
+
registry:
|
70
|
+
scheme: http
|
71
|
+
user:
|
72
|
+
os:
|
73
|
+
name: Solaris
|
74
|
+
version: 10.0
|
75
|
+
num_cpus: 64
|
76
|
+
behind_firewall: true
|
77
|
+
############################################################
|
78
|
+
|
79
|
+
The first configuration is written with only native YAML types
|
80
|
+
while the second configuration is written with serialized Ruby URI
|
81
|
+
classes (the +addresses+ parameter). If the YAML file is to be read
|
82
|
+
by multiple languages, then only native YAML types should be used in
|
83
|
+
the file; FAQ.txt discusses how the ConfigToolkit converts YAML
|
84
|
+
strings to some Ruby types (Pathnames, URIs, Symbols, etc.). The following
|
85
|
+
program shows how this example YAML configuration file can be loaded,
|
86
|
+
and how a configuration can be written as a YAML file:
|
87
|
+
======+examples/yaml_example.rb+:
|
88
|
+
#!/usr/bin/env ruby
|
89
|
+
|
90
|
+
#
|
91
|
+
# These example programs use the 'relative' gem in order to
|
92
|
+
# express paths relative to __FILE__ cleanly, allowing them to be run from
|
93
|
+
# any directory. In particular, they use the require_relative
|
94
|
+
# method to load examples/machineconfig.rb and
|
95
|
+
# File.expand_path_relative_to_caller in order to refer to
|
96
|
+
# configuration files within the examples directory.
|
97
|
+
#
|
98
|
+
require 'rubygems'
|
99
|
+
require 'relative'
|
100
|
+
require_relative 'machineconfig'
|
101
|
+
|
102
|
+
require 'configtoolkit/yamlreader'
|
103
|
+
require 'configtoolkit/yamlwriter'
|
104
|
+
|
105
|
+
CONFIGURATION_FILE = File.expand_path_relative_to_caller("yaml_example.yaml")
|
106
|
+
|
107
|
+
#
|
108
|
+
# The first configuration has no containing object name.
|
109
|
+
#
|
110
|
+
reader = ConfigToolkit::YAMLReader.new(CONFIGURATION_FILE)
|
111
|
+
first_config = MachineConfig.load(reader)
|
112
|
+
print("The first config:\n#{first_config}\n")
|
113
|
+
|
114
|
+
#
|
115
|
+
# The second configuration has "production.www" as a containing
|
116
|
+
# object name. Note that the second configuration
|
117
|
+
# reprents the URI elements of the addresses parameter array
|
118
|
+
# as Ruby URI classes, leveraging Ruby's ability to unserialize
|
119
|
+
# objects from YAML.
|
120
|
+
#
|
121
|
+
reader = ConfigToolkit::YAMLReader.new(CONFIGURATION_FILE)
|
122
|
+
second_config = MachineConfig.load(reader, "production.www")
|
123
|
+
print("The second config:\n#{second_config}\n")
|
124
|
+
|
125
|
+
#
|
126
|
+
# Write second_config to a string stream, which then is written to
|
127
|
+
# stdout. Pass true as the second argument to new in order to specify that
|
128
|
+
# the writer only should write native YAML types (Strings, integers, floats,
|
129
|
+
# booleans, etc.).
|
130
|
+
#
|
131
|
+
string_stream = StringIO.new()
|
132
|
+
writer = ConfigToolkit::YAMLWriter.new(string_stream, true)
|
133
|
+
second_config.dump(writer)
|
134
|
+
print("The second configuration written in YAML with native YAML types:\n")
|
135
|
+
print("#{string_stream.string}")
|
136
|
+
|
137
|
+
Note that the ConfigToolkit::YAMLWriter can dump parameter values with only
|
138
|
+
native YAML types or with serialized Ruby classes, depending on the second
|
139
|
+
argument passed to the ConfigToolkit::YAMLWriter.new method. If a
|
140
|
+
ConfigToolkit::YAMLWriter is instructed to use only native YAML types to
|
141
|
+
express parameter values, it will use the +to_s+ method to convert all
|
142
|
+
parameter values that are not of a native YAML type to strings. When run, the
|
143
|
+
program produces:
|
144
|
+
======The output of <code>examples/yaml_example.rb</code>:
|
145
|
+
The first config:
|
146
|
+
{
|
147
|
+
num_cpus : 32
|
148
|
+
addresses : [
|
149
|
+
http://default.designingpatterns.com,
|
150
|
+
http://apple.designingpatterns.com
|
151
|
+
]
|
152
|
+
contains_sensitive_data: false
|
153
|
+
os : {
|
154
|
+
version: 5.3
|
155
|
+
name : AIX
|
156
|
+
}
|
157
|
+
behind_firewall : false
|
158
|
+
}
|
159
|
+
|
160
|
+
The second config:
|
161
|
+
production.www: {
|
162
|
+
num_cpus : 64
|
163
|
+
addresses : [
|
164
|
+
http://www.designingpatterns.com,
|
165
|
+
http://tokyo.designingpatterns.com
|
166
|
+
]
|
167
|
+
contains_sensitive_data: true
|
168
|
+
os : {
|
169
|
+
version: 10.0
|
170
|
+
name : Solaris
|
171
|
+
}
|
172
|
+
behind_firewall : true
|
173
|
+
}
|
174
|
+
|
175
|
+
The second configuration written in YAML with native YAML types:
|
176
|
+
---
|
177
|
+
production:
|
178
|
+
www:
|
179
|
+
addresses:
|
180
|
+
- http://www.designingpatterns.com
|
181
|
+
- http://tokyo.designingpatterns.com
|
182
|
+
os:
|
183
|
+
name: Solaris
|
184
|
+
version: 10.0
|
185
|
+
behind_firewall: true
|
186
|
+
num_cpus: 64
|
187
|
+
contains_sensitive_data: true
|
188
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# These example programs use the 'relative' gem in order to
|
5
|
+
# express paths relative to __FILE__ cleanly, allowing them to be run from
|
6
|
+
# any directory. In particular, they use the require_relative
|
7
|
+
# method to load examples/machineconfig.rb and
|
8
|
+
# File.expand_path_relative_to_caller in order to refer to
|
9
|
+
# configuration files within the examples directory.
|
10
|
+
#
|
11
|
+
require 'rubygems'
|
12
|
+
require 'relative'
|
13
|
+
require_relative 'machineconfig'
|
14
|
+
|
15
|
+
require 'configtoolkit/hashreader'
|
16
|
+
require 'configtoolkit/hashwriter'
|
17
|
+
|
18
|
+
#
|
19
|
+
# The first configuration has no containing object name.
|
20
|
+
#
|
21
|
+
config_hash = {
|
22
|
+
:num_cpus => 32,
|
23
|
+
:os => {
|
24
|
+
:name => "AIX",
|
25
|
+
:version => 5.3
|
26
|
+
},
|
27
|
+
:behind_firewall => false,
|
28
|
+
:contains_sensitive_data => false,
|
29
|
+
:addresses => [
|
30
|
+
URI("http://default.designingpatterns.com"),
|
31
|
+
URI("http://apple.designingpatterns.com")
|
32
|
+
]
|
33
|
+
}
|
34
|
+
reader = ConfigToolkit::HashReader.new(config_hash)
|
35
|
+
first_config = MachineConfig.load(reader)
|
36
|
+
print("The first config:\n#{first_config}\n")
|
37
|
+
|
38
|
+
#
|
39
|
+
# The second configuration has "production.www" as a containing
|
40
|
+
# object name and therefore the configuration is mapped to by
|
41
|
+
# :www in a Hash, which in turn is mapped to by :production
|
42
|
+
# in a containing Hash.
|
43
|
+
#
|
44
|
+
config_hash = {
|
45
|
+
:production => {
|
46
|
+
:www => {
|
47
|
+
:num_cpus => 64,
|
48
|
+
:os => {
|
49
|
+
:name => "Solaris",
|
50
|
+
:version => 10.0
|
51
|
+
},
|
52
|
+
:contains_sensitive_data => true,
|
53
|
+
:addresses => [
|
54
|
+
URI("http://www.designingpatterns.com"),
|
55
|
+
URI("http://tokyo.designingpatterns.com")
|
56
|
+
]
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
reader = ConfigToolkit::HashReader.new(config_hash)
|
61
|
+
second_config = MachineConfig.load(reader, "production.www")
|
62
|
+
print("The second config:\n#{second_config}\n")
|
63
|
+
|
64
|
+
#
|
65
|
+
# After the call to dump, the config_hash instance member of the HashWriter
|
66
|
+
# instance contains the Hash representation of second_config.
|
67
|
+
#
|
68
|
+
writer = ConfigToolkit::HashWriter.new()
|
69
|
+
second_config.dump(writer)
|
70
|
+
print("The Hash dumped from the second config:\n")
|
71
|
+
print("#{writer.config_hash.inspect}\n")
|