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.
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/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
- $stderr = STDERR
9
-
10
- Hoe.new('configtoolkit', "1.2.0") do |p|
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")