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
@@ -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