configtoolkit 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/FAQ.txt +113 -25
  2. data/Hash.txt +128 -0
  3. data/History.txt +45 -2
  4. data/KeyValue.txt +235 -0
  5. data/Manifest.txt +66 -4
  6. data/README.txt +666 -202
  7. data/Rakefile +3 -5
  8. data/Ruby.txt +172 -0
  9. data/YAML.txt +188 -0
  10. data/examples/hash_example.rb +71 -0
  11. data/examples/key_value_example.dump.cfg +5 -0
  12. data/examples/key_value_example.normal1.cfg +20 -0
  13. data/examples/key_value_example.normal2.cfg +15 -0
  14. data/examples/key_value_example.rb +72 -0
  15. data/examples/key_value_example.wacky.cfg +13 -0
  16. data/examples/load_example.rb +32 -0
  17. data/examples/load_example.yaml +34 -0
  18. data/examples/load_group_example.rb +28 -0
  19. data/examples/load_group_example.yaml +33 -0
  20. data/examples/machineconfig.rb +77 -0
  21. data/examples/ruby_example.rb +32 -0
  22. data/examples/ruby_example.rcfg +52 -0
  23. data/examples/usage_example.rb +93 -0
  24. data/examples/yaml_example.dump.yaml +12 -0
  25. data/examples/yaml_example.rb +48 -0
  26. data/examples/yaml_example.yaml +59 -0
  27. data/lib/configtoolkit.rb +1 -1
  28. data/lib/configtoolkit/baseconfig.rb +522 -418
  29. data/lib/configtoolkit/hasharrayvisitor.rb +242 -0
  30. data/lib/configtoolkit/hashreader.rb +41 -0
  31. data/lib/configtoolkit/hashwriter.rb +45 -0
  32. data/lib/configtoolkit/keyvalueconfig.rb +105 -0
  33. data/lib/configtoolkit/keyvaluereader.rb +597 -0
  34. data/lib/configtoolkit/keyvaluewriter.rb +157 -0
  35. data/lib/configtoolkit/prettyprintwriter.rb +167 -0
  36. data/lib/configtoolkit/reader.rb +62 -0
  37. data/lib/configtoolkit/rubyreader.rb +270 -0
  38. data/lib/configtoolkit/types.rb +42 -26
  39. data/lib/configtoolkit/writer.rb +116 -0
  40. data/lib/configtoolkit/yamlreader.rb +10 -6
  41. data/lib/configtoolkit/yamlwriter.rb +113 -71
  42. data/test/bad_array_index.rcfg +1 -0
  43. data/test/bad_containing_object_assignment.rcfg +2 -0
  44. data/test/bad_directive.cfg +1 -0
  45. data/test/bad_include1.cfg +2 -0
  46. data/test/bad_include2.cfg +3 -0
  47. data/test/bad_parameter_reference.rcfg +1 -0
  48. data/test/contained_sample.cfg +10 -0
  49. data/test/contained_sample.pretty_print +30 -0
  50. data/test/contained_sample.rcfg +22 -0
  51. data/test/contained_sample.yaml +22 -21
  52. data/test/containers.cfg +6 -0
  53. data/test/exta_string_after_container.cfg +2 -0
  54. data/test/extra_container_closing.cfg +2 -0
  55. data/test/extra_string_after_container.cfg +2 -0
  56. data/test/extra_string_after_nested_container.cfg +1 -0
  57. data/test/missing_array_closing.cfg +2 -0
  58. data/test/missing_array_element.cfg +2 -0
  59. data/test/missing_directive.cfg +1 -0
  60. data/test/missing_hash_closing.cfg +2 -0
  61. data/test/missing_hash_element.cfg +2 -0
  62. data/test/missing_hash_value.cfg +1 -0
  63. data/test/missing_include_argument.cfg +1 -0
  64. data/test/missing_key_value_delimiter.cfg +1 -0
  65. data/test/readerwritertest.rb +28 -7
  66. data/test/sample.cfg +10 -0
  67. data/test/sample.pretty_print +30 -0
  68. data/test/sample.rcfg +26 -0
  69. data/test/test_baseconfig.rb +152 -38
  70. data/test/test_hash.rb +82 -0
  71. data/test/test_keyvalue.rb +185 -0
  72. data/test/test_prettyprint.rb +28 -0
  73. data/test/test_ruby.rb +50 -0
  74. data/test/test_yaml.rb +33 -26
  75. data/test/wacky_sample1.cfg +16 -0
  76. data/test/wacky_sample2.cfg +5 -0
  77. data/test/wacky_sample3.cfg +4 -0
  78. data/test/wacky_sample4.cfg +1 -0
  79. metadata +101 -10
  80. data/lib/configtoolkit/toolkit.rb +0 -20
  81. 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 parameters with
4
- no constraints (<code>ConstrainedArray.new(Object, nil, nil)</code>).
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 paramater, then this is called with the new value *before*
15
- the paramter is set. Since all parameter values loaded from a file (during
16
- the load method) are set with parameter setters, all values loaded from
17
- a file receive this validation.
18
-
19
- In addition, after a load operation and before a dump operation, the
20
- configuration is checked in order to ensure that all required parameters have
21
- been set. If an optional parameter is found that has not been set, then
22
- it is set with its associated default value. Finally, the validate_all_values
23
- method is called if it exists.
24
-
25
- The above ensures that parameters are set with only valid values, and that
26
- the configuration as a whole is valid right after load and right before
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 complex languages like YAML support typed data. Simple
31
- configuration formats like key/value pairs, however, do not support typed data;
32
- every parameter value is read as a String. Since the ConfigToolkit *knows* the
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 to
36
- convert classes into Strings, the to_s method). So, the ConfigToolkit does
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 as parameters
52
- within other ConfigToolkit::BaseConfig child classes arbitrarily deep. In
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 element classes (an array of configuration objects
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.load_group, which allows a hash of configs to be loaded with
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
+