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
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'configtoolkit/keyvalueconfig'
4
+ require 'configtoolkit/writer'
5
+
6
+ module ConfigToolkit
7
+
8
+ #
9
+ # This class implements the Writer interface for key-value configuration
10
+ # files.
11
+ #
12
+ # See KeyValue.txt for more details about the key-value configuration format.
13
+ #
14
+ class KeyValueWriter < Writer
15
+ #
16
+ # ====Description:
17
+ # This constructs a KeyValueWriter instance for a key-value
18
+ # format described by +config+ for +stream+,
19
+ # where stream is either a file name (a String)
20
+ # or an IO object.
21
+ #
22
+ # ====Parameters:
23
+ # [stream]
24
+ # A file name (String) or an IO object. If
25
+ # +stream+ is a file name, then the KeyValueWriter
26
+ # will open the associated file.
27
+ # [config]
28
+ # A KeyValueConfig that defines the format of the key-value
29
+ # output that this instance can write
30
+ #
31
+ def initialize(stream, config = KeyValueConfig::DEFAULT_CONFIG)
32
+ if(stream.class == String)
33
+ @stream = File.open(stream, "w")
34
+ else
35
+ @stream = stream
36
+ end
37
+
38
+ @config = config
39
+ end
40
+
41
+ #
42
+ # ====Returns:
43
+ # Returns +true+, since the KeyValueWriter requires Symbol parameter names
44
+ # in the Hash passed to write.
45
+ #
46
+ def require_symbol_parameter_names?
47
+ return true
48
+ end
49
+
50
+ #
51
+ # This class is used for printing nested elements of the structure
52
+ # passed to write. See the comments for HashArrayVisitor for
53
+ # details on how this visitor works.
54
+ #
55
+ class Visitor < HashArrayVisitor # :nodoc:
56
+ #
57
+ # ====Description:
58
+ # This constructs a Visitor to write structures in key-value format to
59
+ # stream stream.
60
+ #
61
+ # ====Parameters:
62
+ # [stream]
63
+ # The stream to which to write
64
+ # [config]
65
+ # The KeyValueConfig that defines the format of the key-value
66
+ # output that this instance can write
67
+ #
68
+ def initialize(stream, config)
69
+ @stream = stream
70
+ @config = config
71
+
72
+ super()
73
+ end
74
+
75
+ def enter_hash(hash)
76
+ @stream << @config.hash_opening
77
+
78
+ return nil
79
+ end
80
+
81
+ def leave_hash(popped_stack_frame)
82
+ @stream << @config.hash_closing
83
+ end
84
+
85
+ def visit_hash_element(key, value, index, is_container)
86
+ if(index != 0)
87
+ @stream << "#{@config.container_element_delimiter} "
88
+ end
89
+
90
+ @stream << "#{key} #{@config.hash_key_value_delimiter} "
91
+
92
+ if(!is_container)
93
+ @stream << "#{value}"
94
+ end
95
+ end
96
+
97
+ def enter_array(array)
98
+ @stream << @config.array_opening
99
+ end
100
+
101
+ def visit_array_element(value, index, is_container)
102
+ if(index != 0)
103
+ @stream << "#{@config.container_element_delimiter} "
104
+ end
105
+
106
+ if(!is_container)
107
+ @stream << "#{value}"
108
+ end
109
+ end
110
+
111
+ def leave_array(popped_stack_frame)
112
+ @stream << @config.array_closing
113
+ end
114
+ end
115
+
116
+ #
117
+ # ====Description:
118
+ # This method writes +config_hash+ to the underlying stream.
119
+ #
120
+ # ====Parameters:
121
+ # [config_hash]
122
+ # The configuration hash to write
123
+ # [containing_object_name]
124
+ # The configuration's containing object name
125
+ #
126
+ def write(config_hash, containing_object_name)
127
+ #
128
+ # Unfortunately, the Visitor cannot be used to write out the top level
129
+ # of configuration parameters, because the top-level format is different
130
+ # than the nested levels (the top-level *is* a hash, but is written
131
+ # out without braces and with one element on each line; the top-level
132
+ # also has a containing object name).
133
+ #
134
+ if(!containing_object_name.empty?())
135
+ key_prefix = "#{containing_object_name.gsub(/\./, @config.object_delimiter)}#{@config.object_delimiter}"
136
+ else
137
+ key_prefix = ""
138
+ end
139
+
140
+ visitor = Visitor.new(@stream, @config)
141
+ config_hash.each do |key, value|
142
+ @stream << "#{key_prefix}#{key} #{@config.key_value_delimiter} "
143
+
144
+ if(value.is_a?(Hash) || value.is_a?(Array))
145
+ visitor.visit(value)
146
+ else
147
+ @stream << "#{value}"
148
+ end
149
+
150
+ @stream << "\n"
151
+ end
152
+
153
+ @stream.flush()
154
+ end
155
+ end
156
+
157
+ end
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'configtoolkit/hasharrayvisitor'
4
+ require 'configtoolkit/writer'
5
+
6
+ module ConfigToolkit
7
+
8
+ #
9
+ # This class implements the Writer interface for pretty printing
10
+ # configuration classes to specified streams. This is used by
11
+ # BaseConfig#to_s.
12
+ #
13
+ class PrettyPrintWriter < Writer
14
+ #
15
+ # ====Description:
16
+ # This constructs a PrettyPrintWriter instance for +stream+,
17
+ # where +stream+ either is a file name (a String)
18
+ # or an IO object.
19
+ #
20
+ # ====Parameters:
21
+ # [stream]
22
+ # A file name (String) or an IO object. If
23
+ # +stream+ is a file name, then the PrettyPrintWriter
24
+ # will open the associated file.
25
+ #
26
+ def initialize(stream)
27
+ if(stream.class == String)
28
+ @stream = File.open(stream, "w")
29
+ else
30
+ @stream = stream
31
+ end
32
+ end
33
+
34
+ #
35
+ # This class is used for printing the elements of the structure
36
+ # passed to write. See the comments for HashArrayVisitor for
37
+ # details on how this visitor works.
38
+ #
39
+ class Visitor < HashArrayVisitor # :nodoc:
40
+ #
41
+ # The amount by which to indent for each nesting level when
42
+ # pretty printing.
43
+ #
44
+ NESTING_INDENT = " " * 4
45
+
46
+ StackFrame = Struct.new(:indent, :longest_param_name_length)
47
+
48
+ #
49
+ # ====Description:
50
+ # This constructs a Visitor to pretty pretty structures to
51
+ # stream stream.
52
+ #
53
+ # ====Parameters:
54
+ # [stream]
55
+ # The stream to which to write
56
+ #
57
+ def initialize(stream)
58
+ @stream = stream
59
+
60
+ super(StackFrame.new("", 0))
61
+ end
62
+
63
+ #
64
+ # ====Description:
65
+ # This method returns a new stack frame with
66
+ # longest parameter name length longest_param_name_length.
67
+ # The indentation for the new frame automatically is
68
+ # constructed from the current indentation.
69
+ #
70
+ # ====Parameters:
71
+ # [longest_param_name_length]
72
+ # The length of the longest parameter name in the
73
+ # container associated with the frame
74
+ #
75
+ # ====Returns:
76
+ # A new stack frame
77
+ #
78
+ def construct_new_stack_frame(longest_param_name_length)
79
+ indent = stack_top().indent + NESTING_INDENT
80
+
81
+ return StackFrame.new(indent, longest_param_name_length)
82
+ end
83
+
84
+ def enter_hash(hash)
85
+ @stream << "{"
86
+
87
+ longest_param_name_length = 0
88
+ hash.each_key do |key|
89
+ key_size = key.to_s().size()
90
+ if(key_size > longest_param_name_length)
91
+ longest_param_name_length = key_size
92
+ end
93
+ end
94
+
95
+ return construct_new_stack_frame(longest_param_name_length)
96
+ end
97
+
98
+ def leave_hash(popped_stack_frame)
99
+ @stream << "\n#{stack_top.indent}" << "}"
100
+ end
101
+
102
+ def visit_hash_element(key, value, index, is_container)
103
+ @stream << "\n#{stack_top.indent}"
104
+ @stream << key.to_s.ljust(stack_top.longest_param_name_length)
105
+ @stream << ": "
106
+
107
+ if(!is_container)
108
+ @stream << "#{value}"
109
+ end
110
+ end
111
+
112
+ def enter_array(array)
113
+ @stream << "["
114
+
115
+ return construct_new_stack_frame(stack_top().longest_param_name_length)
116
+ end
117
+
118
+ def visit_array_element(value, index, is_container)
119
+ if(index != 0)
120
+ @stream << ","
121
+ end
122
+
123
+ @stream << "\n#{stack_top.indent}"
124
+
125
+ if(!is_container)
126
+ @stream << "#{value}"
127
+ end
128
+ end
129
+
130
+ def leave_array(popped_stack_frame)
131
+ @stream << "\n#{stack_top.indent}" << "]"
132
+ end
133
+ end
134
+
135
+ #
136
+ # ====Returns:
137
+ # Returns +true+, since the PrettyPrintWriter requires Symbol parameter names
138
+ # in the hash passed to write.
139
+ #
140
+ def require_symbol_parameter_names?
141
+ return true
142
+ end
143
+
144
+ #
145
+ # ====Description:
146
+ # This method pretty prints +config_hash+ to the underlying stream.
147
+ #
148
+ # ====Parameters:
149
+ # [config_hash]
150
+ # The configuration hash to write
151
+ # [containing_object_name]
152
+ # The configuration's containing object name
153
+ #
154
+ def write(config_hash, containing_object_name)
155
+ if(!containing_object_name.empty?())
156
+ @stream << "#{containing_object_name}: "
157
+ end
158
+
159
+ Visitor.new(@stream).visit(config_hash)
160
+
161
+ @stream << "\n"
162
+
163
+ @stream.flush()
164
+ end
165
+ end
166
+
167
+ end
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+
5
+ module ConfigToolkit
6
+
7
+ #
8
+ # This is an "abstract class" that documents the Reader interface.
9
+ # I put "abstract class" in quotes because Ruby of course neither supports
10
+ # nor requires abstract classes (that is, one can create a new reader class
11
+ # without extending Reader). It solely serves to document the interface.
12
+ #
13
+ # Readers read data (the read method) from some underlying source and
14
+ # return configuration represented as a Hash of parameter names
15
+ # to parameter values. The parameter names can be either Strings
16
+ # or Symbols; the parameter values can be scalars, Hashes, or Arrays.
17
+ # Array or Hash parameter values can in turn have Arrays or Hash elements,
18
+ # arbitrarily deeply nested.
19
+ #
20
+ class Reader
21
+ public
22
+
23
+ #
24
+ # ====Description:
25
+ # This class method attempts to find a file system path for +stream+, where
26
+ # stream is an argument passed to a reader constructor (if a String, then
27
+ # stream names a file; otherwise, stream should be an IO or IO-like object)
28
+ # This is used by several of the the reader classes.
29
+ #
30
+ # ====Parameters:
31
+ # [stream]
32
+ # The underlying data source passed to a reader's constructor
33
+ #
34
+ # ====Returns:
35
+ # The file system path for +stream+, or +nil+ if unable to find this
36
+ #
37
+ def self.stream_path(stream)
38
+ if(stream.is_a?(String))
39
+ return Pathname.new(stream) # stream must be a file name
40
+ elsif(stream.is_a?(File))
41
+ return Pathname.new(stream.path())
42
+ else
43
+ return nil # Who knows?
44
+ end
45
+ end
46
+
47
+ #
48
+ # This reads and returns configuration represented as a Hash, where
49
+ # the elements are parameter names (Strings or Symbols) mapping to
50
+ # parameter values.
51
+ #
52
+ # This *must* be implemented by Readers.
53
+ #
54
+ # ====Returns:
55
+ # The contents of a configuration as a Hash
56
+ #
57
+ def read
58
+ raise NotImplementedError, "abstract method called"
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'configtoolkit/reader'
4
+ require 'configtoolkit/types'
5
+
6
+ require 'singleton'
7
+
8
+ module ConfigToolkit
9
+
10
+ #
11
+ # This class implements the Reader interface for Ruby configuration files.
12
+ # Ruby configuration files essentially are key-value configuration files
13
+ # parsed by the Ruby interpreter; they allow the full Ruby language to
14
+ # be used while building up a configuration. Parameters are specified
15
+ # by config.param_name or config.containing_object.param_name. They can
16
+ # be assigned to and, after assignment, used in other expressions. If a
17
+ # name first is referred to with a setter (i.e., "age" in
18
+ # "config.first.second.age = 5"), then the name is assumed to be a
19
+ # parameter name. If a name first is referred to with a
20
+ # getter (i.e., "first" and "second" in "config.first.second.age = 5"),
21
+ # then the name is assumed to be an object name (either a containing or
22
+ # a nested configuration object). Objects cannot be set
23
+ # (i.e., "config.first.second = 2" is illegal).
24
+ # Parameter values, once set, can be referenced in later
25
+ # expressions.
26
+ #
27
+ # See Ruby.txt for more details.
28
+ #
29
+ class RubyReader < Reader
30
+ #
31
+ # ====Description:
32
+ # This constructs a RubyReader instance for +stream+,
33
+ # where +stream+ either is a file name (a String)
34
+ # or an IO object.
35
+ #
36
+ # ====Parameters:
37
+ # [stream]
38
+ # A file name (String) or an IO object. If
39
+ # +stream+ is a file name, then the RubyReader
40
+ # will open the associated file.
41
+ #
42
+ def initialize(stream)
43
+ if(stream.class == String)
44
+ @stream = File.open(stream, "r")
45
+ else
46
+ @stream = stream
47
+ end
48
+
49
+ @stream_path = Reader.stream_path(stream)
50
+ end
51
+
52
+ #
53
+ # This is a helper class (not meant to be used externally).
54
+ # The Ruby code in Ruby configuration files is executed in the
55
+ # context of the instances of this class. It provides a config
56
+ # instance method that allows references to "config.param_name"
57
+ # in the configuration file to work. This method returns a
58
+ # ObjectProxy, which in turn re-implements method_missing
59
+ # so as to allow references to arbitrary parameters.
60
+ #
61
+ class Interpreter # :nodoc:
62
+ #
63
+ # This serves as a marker that allows the ObjectProxy
64
+ # to distinguish between "simple" (non-object) parameter values and
65
+ # objects. It is a singleton because there is no need for more than
66
+ # one simple parameter marker object.
67
+ #
68
+ class SimpleParam # :nodoc:
69
+ include Singleton
70
+ end
71
+
72
+ #
73
+ # ObjectProxy instances use method_missing to build up
74
+ # configuration Hashes (returned by the Reader's read method)
75
+ # from arbitrary parameter references in the Ruby configuration file.
76
+ # An instance maintains two Hashes. The first is the configuration hash
77
+ # that maps parameter names to parameter values and containing object names
78
+ # to Hashes. The second is the proxy hash that maps containing object
79
+ # names to ObjectProxy instances (and parameter names to
80
+ # the SimpleParam singleton marker).
81
+ #
82
+ # For instance, if the configuration file has "config.age = 5",
83
+ # age= will trigger a method_missing call that in turn will
84
+ # map :age to 5 in its configuration hash table (and also :age to
85
+ # SimpleParam in its proxy hash table if :age was encountered for the first
86
+ # time). A line like "config.first.age = 5", will trigger
87
+ # a method_missing call to first. Assuming first does not exist already,
88
+ # a new ObjectProxy will be created for it (:first => new
89
+ # ObjectProxy in the proxy hash table and :first => {} in the
90
+ # configuration hash table) and returned; age then will work with the
91
+ # new ObjectProxy as described.
92
+ #
93
+ # method_missing classifies references according to the following:
94
+ # 1.) Setter methods must be setting parameters.
95
+ # 2.) Getter methods must refer to a containing object if
96
+ # the name never has been encountered before and, if it has,
97
+ # to whatever it originally was resolved.
98
+ #
99
+ # Thus, containing objects are referred to via getters and only can be
100
+ # referred to via getters. Parameters first must be
101
+ # referred to via a setter and then can be referred to via a
102
+ # getter or a setter (you can't get the value of a parameter that hasn't
103
+ # been set yet, after all). Getters for containing objects return
104
+ # ObjectProxy instances, so that they in turn can service member
105
+ # references (i.e, "config.first.second.age = 2" has config, first, and
106
+ # second returning ObjectProxy instances).
107
+ #
108
+ # The configuration and proxy hashes have parallel structures, with
109
+ # the configuration hash mapping names to values and the proxy hash
110
+ # mapping names to containing object proxies.
111
+ #
112
+ class ObjectProxy # :nodoc:
113
+ #
114
+ # ====Description:
115
+ # This contructs a ObjectProxy instance to have
116
+ # empty configuration and proxy hashes.
117
+ #
118
+ def initialize(name)
119
+ @name = name
120
+ @config_hash = {}
121
+ @proxy_hash = {}
122
+ end
123
+
124
+ #
125
+ # ====Description:
126
+ # This is an accessor for the @config_hash member variable. It is
127
+ # surrounded by ___ characters on each side in order to reduce the
128
+ # chance of any collision occurring with a name referenced in the
129
+ # configuration file (___config_hash___ cannot be used as a
130
+ # parameter or containing object name).
131
+ #
132
+ # ====Returns:
133
+ # @config_hash
134
+ #
135
+ def ___config_hash___
136
+ return @config_hash
137
+ end
138
+
139
+ #
140
+ # ====Description:
141
+ # This method simulates methods in order to allow configuration
142
+ # files to reference arbitrary parameter and containing object names.
143
+ # When a configuration file contains "config.first.second.age = 2",
144
+ # the references to first, second, and age are handled by this
145
+ # method.
146
+ #
147
+ # ====Parameters:
148
+ # [method_name]
149
+ # The missing method
150
+ # [*args]
151
+ # The arguments to the missing method
152
+ #
153
+ # ====Returns:
154
+ # If a getter, then the ObjectProxy associated with the
155
+ # containing object or the parameter's value. If a setter (which
156
+ # must act on a parameter), then the new value.
157
+ #
158
+ def method_missing(method_name, *args)
159
+ method_name_str = method_name.to_s()
160
+
161
+ if((method_name_str.size() >= 2) && (method_name_str[0, 2] == "[]"))
162
+ raise Error, "cannot index with array notation into #{@name}"
163
+ elsif(method_name_str[-1,1] == "=")
164
+ new_value = args[0]
165
+
166
+ #
167
+ # The interpreter more or less guarantees that args.size() == 1
168
+ # for setter methods.
169
+ #
170
+
171
+ #
172
+ # Get rid of the trailing '='
173
+ #
174
+ object_name = method_name_str[0, method_name_str.size() - 1].to_sym()
175
+ proxy = @proxy_hash.fetch(object_name, nil)
176
+ if(proxy == nil)
177
+ #
178
+ # A parameter value is being set for the first time, so
179
+ # record the parameter name in the proxy hash
180
+ #
181
+ @proxy_hash[object_name] = SimpleParam.instance()
182
+ return (@config_hash[object_name] = new_value)
183
+ elsif(proxy.is_a?(SimpleParam))
184
+ return (@config_hash[object_name] = new_value)
185
+ else
186
+ raise Error, "cannot assign to object #{@name}.#{object_name}"
187
+ end
188
+ elsif(args.size != 0)
189
+ super.missing_method(method_name, *args)
190
+ else
191
+ object_name = method_name
192
+ proxy = @proxy_hash.fetch(object_name, nil)
193
+
194
+ if(proxy == nil)
195
+ #
196
+ # Since this is a getter and there is no record of the
197
+ # name, the name must refer to a containing object.
198
+ #
199
+ proxy = @proxy_hash[object_name] = ObjectProxy.new("#{@name}.#{object_name}")
200
+ @config_hash[object_name] = proxy.___config_hash___
201
+ return proxy
202
+ elsif(proxy.is_a?(ObjectProxy))
203
+ return proxy
204
+ else
205
+ return @config_hash[object_name]
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ #
212
+ # ====Description:
213
+ # This initializes the Interpreter with an empty
214
+ # configuration hash.
215
+ #
216
+ def initialize
217
+ @root_proxy = ObjectProxy.new("config")
218
+ end
219
+
220
+ #
221
+ # ====Description:
222
+ # This method returns a reference to the root ObjectProxy.
223
+ # It exists so that configuration parameters can be set in Ruby
224
+ # configuration files via lines like "config.age = 5".
225
+ #
226
+ # ====Returns:
227
+ # The root ObjectProxy
228
+ #
229
+ def config
230
+ return @root_proxy
231
+ end
232
+ private :config
233
+
234
+ #
235
+ # ====Description:
236
+ # This method interprets (evals) code (which should have been
237
+ # sourced from a file with path file_path) and returns a Hash
238
+ # representing the configuration built by code
239
+ #
240
+ # ====Parameters:
241
+ # [code]
242
+ # The code containing the configuration to be interpreted (String)
243
+ # [file_path]
244
+ # The path of the file containing the code (Pathname or nil)
245
+ #
246
+ # ====Returns:
247
+ # A Hash representing the configuration built by code
248
+ #
249
+ def interpret(code, file_path)
250
+ if(file_path == nil)
251
+ instance_eval(code)
252
+ else
253
+ instance_eval(code, file_path.to_s())
254
+ end
255
+
256
+ return @root_proxy.___config_hash___
257
+ end
258
+ end
259
+
260
+ #
261
+ # ====Returns:
262
+ # The contents of the ruby configuration file
263
+ #
264
+ def read
265
+ interpreter = Interpreter.new()
266
+ return interpreter.interpret(@stream.read(), @stream_path)
267
+ end
268
+ end
269
+
270
+ end