configmanager 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,19 +1,19 @@
1
- #
2
- # 20 Jul 2012
3
- #
4
-
5
- module ConfigManager
6
-
7
- # Raised when a property references itself - either directly or indirectly.
8
- class CircularReferenceError < Exception
9
- end
10
-
11
- # Raised when a requested property's key is invalid.
12
- class KeyError < Exception
13
- end
14
-
15
- # Raised when a requested property does not exist.
16
- class PropertyNotFoundError < Exception
17
- end
18
-
1
+ #
2
+ # 20 Jul 2012
3
+ #
4
+
5
+ module ConfigManager
6
+
7
+ # Raised when a property references itself - either directly or indirectly.
8
+ class CircularReferenceError < Exception
9
+ end
10
+
11
+ # Raised when a requested property's key is invalid.
12
+ class KeyError < Exception
13
+ end
14
+
15
+ # Raised when a requested property does not exist.
16
+ class PropertyNotFoundError < Exception
17
+ end
18
+
19
19
  end
@@ -1,65 +1,65 @@
1
- #
2
- # 20 Jul 2012
3
- #
4
-
5
- require "configmanager/configuration"
6
- require "configmanager/exceptions"
7
-
8
- module ConfigManager
9
-
10
- # The default interpolator that will look for ${} property references.
11
- # The interpolator will only inspect Strings and Strings in Arrays. All other types are returned as is.
12
- class DefaultInterpolator
13
-
14
- REFERENCE_REGEX = /\$\{[\w\.]+\}/
15
-
16
- # Replaces all ${} references with an actual value. Will evaluate any nested references too.
17
- # If circular references are detected, an exception is raised.
18
- #
19
- # @param [String] key the full (. separated) key against which this value was stored.
20
- # @param [String, Array] raw_value the raw value to interpolate.
21
- # @param [ConfigManager::Configuration] context the context under which the value should be evaluated.
22
- # @param [Hash] options extra configuration options. Supported keys are:
23
- # :replacements - a hash containing replacement values for references. References are replaced with these value on priority.
24
- # :tolerate_missing_properties - a flag to indicate if an exception should be raised if referenced properties are missing.
25
- #
26
- # @return [String] the fully interpolated value.
27
- def self.interpolate(key, raw_value, context, options = {})
28
- # Default options.
29
- replacements = get_option(options, :replacements, false, {})
30
- tolerate_missing_references = get_option(options, :tolerate_missing_references, false, false)
31
- interpolating = set_option(options, :interpolating, {}, false)
32
- # Mark this key
33
- interpolating[key] = true
34
- # Check for type.
35
- # Array - Recursively call this method.
36
- # Not a String - Return as is.
37
- # Otherwise, interpolate!
38
- return raw_value.map { |item| interpolate(key, item, context, options) } if raw_value.kind_of?(Array)
39
- return raw_value unless raw_value.kind_of?(String)
40
- raw_value.gsub(REFERENCE_REGEX) do |reference|
41
- # If the referenced key is still being interpolated, its a circular reference!
42
- # Otherwise, substitute it with its replacement value (either from the supplied hash, or the provided context)
43
- key = reference[2..-2]
44
- if interpolating[key]
45
- trace = (interpolating.keys + [key]).join(" -> ")
46
- raise ConfigManager::CircularReferenceError.new("Cannot interpolate - Circular reference detected. Trace: #{trace}")
47
- elsif replacements.has_key?(key)
48
- replacements[key]
49
- elsif context.has_property?(key)
50
- context.get_property(key, options)
51
- elsif tolerate_missing_references
52
- reference
53
- else
54
- trace = (interpolating.keys + [key]).join(" -> ")
55
- raise ConfigManager::PropertyNotFoundError.new("Cannot interpolate - Referenced key '#{key}' does not exist. Trace: #{trace}")
56
- end
57
- end
58
- ensure
59
- # Done, so mark as done.
60
- interpolating.delete(key)
61
- end
62
-
63
- end
64
-
65
- end
1
+ #
2
+ # 20 Jul 2012
3
+ #
4
+
5
+ require "configmanager/configuration"
6
+ require "configmanager/exceptions"
7
+
8
+ module ConfigManager
9
+
10
+ # The default interpolator that will look for ${} property references.
11
+ # The interpolator will only inspect Strings and Strings in Arrays. All other types are returned as is.
12
+ class DefaultInterpolator
13
+
14
+ REFERENCE_REGEX = /\$\{[\w\.]+\}/
15
+
16
+ # Replaces all ${} references with an actual value. Will evaluate any nested references too.
17
+ # If circular references are detected, an exception is raised.
18
+ #
19
+ # @param [String] key the full (. separated) key against which this value was stored.
20
+ # @param [String, Array] raw_value the raw value to interpolate.
21
+ # @param [ConfigManager::Configuration] context the context under which the value should be evaluated.
22
+ # @param [Hash] options extra configuration options. Supported keys are:
23
+ # :replacements - a hash containing replacement values for references. References are replaced with these value on priority.
24
+ # :tolerate_missing_properties - a flag to indicate if an exception should be raised if referenced properties are missing.
25
+ #
26
+ # @return [String] the fully interpolated value.
27
+ def self.interpolate(key, raw_value, context, options = {})
28
+ # Default options.
29
+ replacements = get_option(options, :replacements, false, {})
30
+ tolerate_missing_references = get_option(options, :tolerate_missing_references, false, false)
31
+ interpolating = set_option(options, :interpolating, {}, false)
32
+ # Mark this key
33
+ interpolating[key] = true
34
+ # Check for type.
35
+ # Array - Recursively call this method.
36
+ # Not a String - Return as is.
37
+ # Otherwise, interpolate!
38
+ return raw_value.map { |item| interpolate(key, item, context, options) } if raw_value.kind_of?(Array)
39
+ return raw_value unless raw_value.kind_of?(String)
40
+ raw_value.gsub(REFERENCE_REGEX) do |reference|
41
+ # If the referenced key is still being interpolated, its a circular reference!
42
+ # Otherwise, substitute it with its replacement value (either from the supplied hash, or the provided context)
43
+ key = reference[2..-2]
44
+ if interpolating[key]
45
+ trace = (interpolating.keys + [key]).join(" -> ")
46
+ raise ConfigManager::CircularReferenceError.new("Cannot interpolate - Circular reference detected. Trace: #{trace}")
47
+ elsif replacements.has_key?(key)
48
+ replacements[key]
49
+ elsif context.has_property?(key)
50
+ context.get_property(key, options)
51
+ elsif tolerate_missing_references
52
+ reference
53
+ else
54
+ trace = (interpolating.keys + [key]).join(" -> ")
55
+ raise ConfigManager::PropertyNotFoundError.new("Cannot interpolate - Referenced key '#{key}' does not exist. Trace: #{trace}")
56
+ end
57
+ end
58
+ ensure
59
+ # Done, so mark as done.
60
+ interpolating.delete(key)
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -1,182 +1,182 @@
1
- #
2
- # 20 Jul 2012
3
- #
4
-
5
- module ConfigManager
6
-
7
- # Loads properties from a YAML file.
8
- class YAMLPropertiesLoader
9
-
10
- # Loads properties from the specified YAML file. This is the default loader.
11
- #
12
- # @param [String] file_path the file to load.
13
- # @param [Configuration] configuration the configuration to add the loaded properties to.
14
- #
15
- # @return [Hash] the properties read from the file.
16
- def self.load_properties(file_path, configuration)
17
- require "yaml"
18
- file_path = File.expand_path(file_path)
19
- properties = YAML.load_file(file_path)
20
- files_to_import = properties.delete("import") { [] }
21
- files_to_import = [files_to_import] unless files_to_import.kind_of?(Array)
22
- configuration.add_properties(properties)
23
- # Load any referenced files too.
24
- file_dir = File.dirname(file_path)
25
- files_to_import.each { |file_to_import| load_properties(File.expand_path(file_to_import, file_dir), configuration) }
26
- end
27
-
28
- end
29
-
30
- # Loads properties from a Java style property file. The syntax is slightly different from regular property files.
31
- # Important differences are :
32
- # 1. Escape characters are not allowed.
33
- # 2. Properties can be assigned a type (int, float, regex, string, bool).
34
- # 3. Properties can be collected as arrays.
35
- #
36
- # A property would look like :
37
- # <key><:optional_type> = <value>
38
- # For arrays, add a [] to the property :
39
- # <key><:optional_type>[] = <value separated by ','>
40
- class JavaPropertiesLoader
41
-
42
- # Raised if a property file is not valid.
43
- class SyntaxError < Exception
44
- end
45
-
46
- LINE_REGEX = /^([\w\.]+)(:(\w+))?(\[\])?\s*([\s=])\s*(.*)$/
47
- COMMAND_REGEX = /^@(\w+)\s+(.*)$/
48
-
49
- TRUE_VALUES = %w(true yes on)
50
- FALSE_VALUES = %w(false no off)
51
- TYPE_CONVERTERS = {"bool" => :type_bool, "int" => :type_int, "float" => :type_float, "regex" => :type_regex,
52
- "string" => :type_string, nil => :type_string}
53
- COMMANDS = {"import" => :command_import}
54
-
55
- # Executes the import command.
56
- #
57
- # @param [String] file_to_import the file that needs to be imported.
58
- # @param [String] file_path the file that contained the command.
59
- # @param [Integer] line_number the line number where the command existed.
60
- # @param [ConfigManager::Configuration] configuration the configuration to load the properties into.
61
- #
62
- # @return [NilClass]
63
- def self.command_import(file_to_import, file_path, line_number, configuration)
64
- file_to_import = file_to_import[1..-2]
65
- file_dir = File.dirname(file_path)
66
- file_to_import = File.expand_path(file_to_import, file_dir)
67
- load_properties(file_to_import, configuration)
68
- nil
69
- end
70
-
71
- # Loads properties from the specified Java style property file.
72
- #
73
- # @param [String] file_path the file to load properties from.
74
- # @param [ConfigManager::Configuration] configuration the configuration to load the properties into.
75
- #
76
- # @return [NilClass]
77
- def self.load_properties(file_path, configuration)
78
- file_path = File.expand_path(file_path)
79
- lines = File.readlines(file_path)
80
- lines.each_with_index do |line, line_number|
81
- line = line.chomp.strip
82
- # Empty lines, ignore.
83
- # Comment lines, ignore.
84
- # Command - Call the command's method.
85
- next if line.empty?
86
- next if line.start_with?("#")
87
- match = line.match(COMMAND_REGEX)
88
- if match
89
- command_name = match[1]
90
- command_arg = match[2]
91
- raise SyntaxError.new("Unrecognized command '#{command_name}' #(#{file_path}:#{line_number})") unless COMMANDS.has_key?(command_name)
92
- send(COMMANDS[command_name], command_arg, file_path, line_number, configuration)
93
- next
94
- end
95
- # Other lines, parse as a property.
96
- # Raises an exception if the line was not valid.
97
- match = line.match(LINE_REGEX)
98
- raise SyntaxError.new("Syntax error '#{line}' (#{file_path}:#{line_number})") if match.nil?
99
- key, type, array, value = match[1], match[3], match[4], match[6]
100
- # Invalid types raise an exception.
101
- raise SyntaxError.new("Unrecognized type '#{type}' (#{file_path}:#{line_number})") unless TYPE_CONVERTERS.has_key?(type)
102
- type_converter = TYPE_CONVERTERS[type]
103
-
104
- # Split into array if required and apply the type converter.
105
- if array
106
- value = value.split(",").map { |item| send(type_converter, item.strip, file_path, line_number) }
107
- else
108
- value = send(type_converter, value, file_path, line_number)
109
- end
110
-
111
- # Finally, add to configuration.
112
- configuration.add_property(key, value)
113
- end
114
- nil
115
- end
116
-
117
- # Converts the specified string value into a boolean (TrueClass, FalseClass).
118
- #
119
- # @param [String] value the string value to convert.
120
- # @param [String] file_path the file that contains the property.
121
- # @param [Integer] line_number the line that contained this property.
122
- #
123
- # @return [TrueClass, FalseClass]
124
- def self.type_bool(value, file_path, line_number)
125
- return true if TRUE_VALUES.include?(value)
126
- return false if FALSE_VALUES.include?(value)
127
- raise SyntaxError.new("Invalid boolean '#{value}' (#{file_path}:#{line_number})")
128
- end
129
-
130
- # Converts the specified string value into float.
131
- # Invalid values will raise an exception.
132
- #
133
- # @param [String] value the string value to convert.
134
- # @param [String] file_path the file that contains the property.
135
- # @param [Integer] line_number the line that contained this property.
136
- #
137
- # @return [Float]
138
- def self.type_float(value, file_path, line_number)
139
- Float(value)
140
- rescue
141
- raise SyntaxError.new("Invalid float value '#{value}' (#{file_path}:#{line_number})")
142
- end
143
-
144
- # Converts the specified string value into a integer.
145
- # Invalid values will raise an exception
146
- #
147
- # @param [String] value the string value to convert.
148
- # @param [String] file_path the file that contains the property.
149
- # @param [Integer] line_number the line that contained this property.
150
- #
151
- # @return [Integer]
152
- def self.type_int(value, file_path, line_number)
153
- Integer(value)
154
- rescue
155
- raise SyntaxError.new("Invalid integer value '#{value}' (#{file_path}:#{line_number})")
156
- end
157
-
158
- # Converts the specified string value into a regexp.
159
- #
160
- # @param [String] value the string value to convert.
161
- # @param [String] file_path the file that contains the property.
162
- # @param [Integer] line_number the line that contained this property.
163
- #
164
- # @return [Regexp]
165
- def self.type_regex(value, file_path, line_number)
166
- Regexp.new(value)
167
- end
168
-
169
- # Returns the value as is.
170
- #
171
- # @param [String] value the string value to convert.
172
- # @param [String] file_path the file that contains the property.
173
- # @param [Integer] line_number the line that contained this property.
174
- #
175
- # @return [String]
176
- def self.type_string(value, file_path, line_number)
177
- value
178
- end
179
-
180
- end
181
-
182
- end
1
+ #
2
+ # 20 Jul 2012
3
+ #
4
+
5
+ module ConfigManager
6
+
7
+ # Loads properties from a YAML file.
8
+ class YAMLPropertiesLoader
9
+
10
+ # Loads properties from the specified YAML file. This is the default loader.
11
+ #
12
+ # @param [String] file_path the file to load.
13
+ # @param [Configuration] configuration the configuration to add the loaded properties to.
14
+ #
15
+ # @return [Hash] the properties read from the file.
16
+ def self.load_properties(file_path, configuration)
17
+ require "yaml"
18
+ file_path = File.expand_path(file_path)
19
+ properties = YAML.load_file(file_path)
20
+ files_to_import = properties.delete("import") { [] }
21
+ files_to_import = [files_to_import] unless files_to_import.kind_of?(Array)
22
+ configuration.add_properties(properties)
23
+ # Load any referenced files too.
24
+ file_dir = File.dirname(file_path)
25
+ files_to_import.each { |file_to_import| load_properties(File.expand_path(file_to_import, file_dir), configuration) }
26
+ end
27
+
28
+ end
29
+
30
+ # Loads properties from a Java style property file. The syntax is slightly different from regular property files.
31
+ # Important differences are :
32
+ # 1. Escape characters are not allowed.
33
+ # 2. Properties can be assigned a type (int, float, regex, string, bool).
34
+ # 3. Properties can be collected as arrays.
35
+ #
36
+ # A property would look like :
37
+ # <key><:optional_type> = <value>
38
+ # For arrays, add a [] to the property :
39
+ # <key><:optional_type>[] = <value separated by ','>
40
+ class JavaPropertiesLoader
41
+
42
+ # Raised if a property file is not valid.
43
+ class SyntaxError < Exception
44
+ end
45
+
46
+ LINE_REGEX = /^([\w\.]+)(:(\w+))?(\[\])?\s*([\s=])\s*(.*)$/
47
+ COMMAND_REGEX = /^@(\w+)\s+(.*)$/
48
+
49
+ TRUE_VALUES = %w(true yes on)
50
+ FALSE_VALUES = %w(false no off)
51
+ TYPE_CONVERTERS = {"bool" => :type_bool, "int" => :type_int, "float" => :type_float, "regex" => :type_regex,
52
+ "string" => :type_string, nil => :type_string}
53
+ COMMANDS = {"import" => :command_import}
54
+
55
+ # Executes the import command.
56
+ #
57
+ # @param [String] file_to_import the file that needs to be imported.
58
+ # @param [String] file_path the file that contained the command.
59
+ # @param [Integer] line_number the line number where the command existed.
60
+ # @param [ConfigManager::Configuration] configuration the configuration to load the properties into.
61
+ #
62
+ # @return [NilClass]
63
+ def self.command_import(file_to_import, file_path, line_number, configuration)
64
+ file_to_import = file_to_import[1..-2]
65
+ file_dir = File.dirname(file_path)
66
+ file_to_import = File.expand_path(file_to_import, file_dir)
67
+ load_properties(file_to_import, configuration)
68
+ nil
69
+ end
70
+
71
+ # Loads properties from the specified Java style property file.
72
+ #
73
+ # @param [String] file_path the file to load properties from.
74
+ # @param [ConfigManager::Configuration] configuration the configuration to load the properties into.
75
+ #
76
+ # @return [NilClass]
77
+ def self.load_properties(file_path, configuration)
78
+ file_path = File.expand_path(file_path)
79
+ lines = File.readlines(file_path)
80
+ lines.each_with_index do |line, line_number|
81
+ line = line.chomp.strip
82
+ # Empty lines, ignore.
83
+ # Comment lines, ignore.
84
+ # Command - Call the command's method.
85
+ next if line.empty?
86
+ next if line.start_with?("#")
87
+ match = line.match(COMMAND_REGEX)
88
+ if match
89
+ command_name = match[1]
90
+ command_arg = match[2]
91
+ raise SyntaxError.new("Unrecognized command '#{command_name}' #(#{file_path}:#{line_number})") unless COMMANDS.has_key?(command_name)
92
+ send(COMMANDS[command_name], command_arg, file_path, line_number, configuration)
93
+ next
94
+ end
95
+ # Other lines, parse as a property.
96
+ # Raises an exception if the line was not valid.
97
+ match = line.match(LINE_REGEX)
98
+ raise SyntaxError.new("Syntax error '#{line}' (#{file_path}:#{line_number})") if match.nil?
99
+ key, type, array, value = match[1], match[3], match[4], match[6]
100
+ # Invalid types raise an exception.
101
+ raise SyntaxError.new("Unrecognized type '#{type}' (#{file_path}:#{line_number})") unless TYPE_CONVERTERS.has_key?(type)
102
+ type_converter = TYPE_CONVERTERS[type]
103
+
104
+ # Split into array if required and apply the type converter.
105
+ if array
106
+ value = value.split(",").map { |item| send(type_converter, item.strip, file_path, line_number) }
107
+ else
108
+ value = send(type_converter, value, file_path, line_number)
109
+ end
110
+
111
+ # Finally, add to configuration.
112
+ configuration.add_property(key, value)
113
+ end
114
+ nil
115
+ end
116
+
117
+ # Converts the specified string value into a boolean (TrueClass, FalseClass).
118
+ #
119
+ # @param [String] value the string value to convert.
120
+ # @param [String] file_path the file that contains the property.
121
+ # @param [Integer] line_number the line that contained this property.
122
+ #
123
+ # @return [TrueClass, FalseClass]
124
+ def self.type_bool(value, file_path, line_number)
125
+ return true if TRUE_VALUES.include?(value)
126
+ return false if FALSE_VALUES.include?(value)
127
+ raise SyntaxError.new("Invalid boolean '#{value}' (#{file_path}:#{line_number})")
128
+ end
129
+
130
+ # Converts the specified string value into float.
131
+ # Invalid values will raise an exception.
132
+ #
133
+ # @param [String] value the string value to convert.
134
+ # @param [String] file_path the file that contains the property.
135
+ # @param [Integer] line_number the line that contained this property.
136
+ #
137
+ # @return [Float]
138
+ def self.type_float(value, file_path, line_number)
139
+ Float(value)
140
+ rescue
141
+ raise SyntaxError.new("Invalid float value '#{value}' (#{file_path}:#{line_number})")
142
+ end
143
+
144
+ # Converts the specified string value into a integer.
145
+ # Invalid values will raise an exception
146
+ #
147
+ # @param [String] value the string value to convert.
148
+ # @param [String] file_path the file that contains the property.
149
+ # @param [Integer] line_number the line that contained this property.
150
+ #
151
+ # @return [Integer]
152
+ def self.type_int(value, file_path, line_number)
153
+ Integer(value)
154
+ rescue
155
+ raise SyntaxError.new("Invalid integer value '#{value}' (#{file_path}:#{line_number})")
156
+ end
157
+
158
+ # Converts the specified string value into a regexp.
159
+ #
160
+ # @param [String] value the string value to convert.
161
+ # @param [String] file_path the file that contains the property.
162
+ # @param [Integer] line_number the line that contained this property.
163
+ #
164
+ # @return [Regexp]
165
+ def self.type_regex(value, file_path, line_number)
166
+ Regexp.new(value)
167
+ end
168
+
169
+ # Returns the value as is.
170
+ #
171
+ # @param [String] value the string value to convert.
172
+ # @param [String] file_path the file that contains the property.
173
+ # @param [Integer] line_number the line that contained this property.
174
+ #
175
+ # @return [String]
176
+ def self.type_string(value, file_path, line_number)
177
+ value
178
+ end
179
+
180
+ end
181
+
182
+ end