rconfig 0.3.3 → 0.4.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.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/ChangeLog +50 -0
- data/Credits +13 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +30 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +0 -0
- data/Rakefile +11 -0
- data/demo/application.conf +3 -0
- data/demo/demo.conf +13 -0
- data/demo/demo.rb +14 -0
- data/demo/demo.xml +13 -0
- data/demo/demo.yml +12 -0
- data/doc/classes/ClassVariables.html +111 -0
- data/doc/classes/ConfigError.html +120 -0
- data/doc/classes/ConfigHash.html +354 -0
- data/doc/classes/Constants.html +226 -0
- data/doc/classes/Hash.html +269 -0
- data/doc/classes/InvalidConfigPathError.html +119 -0
- data/doc/classes/Object.html +220 -0
- data/doc/classes/PropertiesFileParser.html +282 -0
- data/doc/classes/RConfig.html +1745 -0
- data/doc/created.rid +1 -0
- data/doc/files/README_rdoc.html +271 -0
- data/doc/files/lib/rconfig/class_variables_rb.html +107 -0
- data/doc/files/lib/rconfig/config_hash_rb.html +114 -0
- data/doc/files/lib/rconfig/constants_rb.html +101 -0
- data/doc/files/lib/rconfig/core_ext/hash_rb.html +114 -0
- data/doc/files/lib/rconfig/core_ext/object_rb.html +101 -0
- data/doc/files/lib/rconfig/core_ext_rb.html +114 -0
- data/doc/files/lib/rconfig/exceptions_rb.html +110 -0
- data/doc/files/lib/rconfig/properties_file_parser_rb.html +146 -0
- data/doc/files/lib/rconfig/rconfig_rb.html +186 -0
- data/doc/files/lib/rconfig_rb.html +117 -0
- data/doc/fr_class_index.html +35 -0
- data/doc/fr_file_index.html +37 -0
- data/doc/fr_method_index.html +75 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/lib/generators/rconfig/install_generator.rb +13 -0
- data/lib/generators/rconfig/templates/rconfig.rb +82 -0
- data/lib/rconfig.rb +98 -32
- data/lib/rconfig/callbacks.rb +46 -0
- data/lib/rconfig/cascade.rb +56 -0
- data/lib/rconfig/config.rb +78 -0
- data/lib/rconfig/constants.rb +58 -0
- data/lib/rconfig/core_ext/array.rb +3 -0
- data/lib/rconfig/core_ext/hash.rb +56 -57
- data/lib/rconfig/core_ext/nil.rb +5 -0
- data/lib/rconfig/core_ext/string.rb +3 -0
- data/lib/rconfig/core_methods.rb +276 -0
- data/lib/rconfig/exceptions.rb +21 -8
- data/lib/rconfig/load_paths.rb +55 -0
- data/lib/rconfig/logger.rb +86 -0
- data/lib/rconfig/properties_file.rb +138 -0
- data/lib/rconfig/reload.rb +75 -0
- data/lib/rconfig/settings.rb +93 -0
- data/lib/rconfig/utils.rb +175 -0
- data/lib/tasks/gem.rake +14 -0
- data/lib/tasks/rdoc.rake +11 -0
- data/lib/tasks/spec.rake +25 -0
- data/rconfig.gemspec +33 -0
- data/spec/core_ext/object_spec.rb +44 -0
- data/spec/rconfig_spec.rb +7 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +16 -0
- metadata +128 -32
- data/lib/rconfig/config_hash.rb +0 -105
- data/lib/rconfig/core_ext.rb +0 -6
- data/lib/rconfig/properties_file_parser.rb +0 -80
- data/lib/rconfig/rconfig.rb +0 -871
- data/test/rconfig_test.rb +0 -381
@@ -0,0 +1,276 @@
|
|
1
|
+
##
|
2
|
+
# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
|
3
|
+
#
|
4
|
+
module RConfig
|
5
|
+
module CoreMethods
|
6
|
+
include Constants
|
7
|
+
|
8
|
+
##
|
9
|
+
# Get each config file's yaml hash for the given config name,
|
10
|
+
# to be merged later. Files will only be loaded if they have
|
11
|
+
# not been loaded before or the files have changed within the
|
12
|
+
# last five minutes, or force is explicitly set to true.
|
13
|
+
#
|
14
|
+
def load_config_files(name, force=false)
|
15
|
+
name = name.to_s
|
16
|
+
|
17
|
+
# Return last config file hash list loaded,
|
18
|
+
# if reload is disabled and files have already been loaded.
|
19
|
+
return self.cache_config_files[name] if self.reload_disabled? && self.cache_config_files[name]
|
20
|
+
|
21
|
+
logger.info "Loading config files for: #{name}"
|
22
|
+
logger.debug "load_config_files(#{name.inspect})"
|
23
|
+
|
24
|
+
|
25
|
+
now = Time.now
|
26
|
+
|
27
|
+
# Get array of all the existing files file the config name.
|
28
|
+
config_files = self.get_config_files(name)
|
29
|
+
|
30
|
+
# Get all the data from all yaml files into as configs
|
31
|
+
configs = config_files.collect do |f|
|
32
|
+
name, name_with_suffix, filename, ext, modified_time = * f
|
33
|
+
|
34
|
+
# Get the cached file info the specific file, if
|
35
|
+
# it's been loaded before.
|
36
|
+
config_data, last_modified, last_loaded = self.cache[filename]
|
37
|
+
|
38
|
+
logger.debug "f = #{f.inspect}\n" +
|
39
|
+
"cache #{name_with_suffix} filename = #{filename.inspect}\n" +
|
40
|
+
"cache #{name_with_suffix} config_data = #{config_data.inspect}\n" +
|
41
|
+
"cache #{name_with_suffix} last_modified = #{last_modified.inspect}\n" +
|
42
|
+
"cache #{name_with_suffix} last_loaded = #{last_loaded.inspect}\n"
|
43
|
+
|
44
|
+
# Load the file if its never been loaded or its been more than
|
45
|
+
# so many minutes since last load attempt. (default: 5 minutes)
|
46
|
+
if config_data.blank? || (now - last_loaded > self.reload_interval)
|
47
|
+
if force || config_data.blank? || modified_time != last_modified
|
48
|
+
|
49
|
+
logger.debug "modified_time #{name.inspect} #{filename.inspect} " +
|
50
|
+
"changed #{modified_time != last_modified} : #{modified_time.inspect} #{last_modified.inspect}"
|
51
|
+
|
52
|
+
logger.debug "RConfig: loading #{filename.inspect}"
|
53
|
+
|
54
|
+
config_data = read(filename, name, ext) # Get contents from config file
|
55
|
+
|
56
|
+
logger.debug "RConfig: loaded #{filename.inspect} => #{config_data.inspect}"
|
57
|
+
|
58
|
+
(self.config_loaded ||= {})[name] = config_files # add files to the loaded files cache
|
59
|
+
|
60
|
+
self.cache[filename] = [config_data, modified_time, now] # Save cached config file contents, and modified_time.
|
61
|
+
|
62
|
+
logger.debug "cache[#{filename.inspect}] = #{self.cache[filename].inspect}"
|
63
|
+
|
64
|
+
self.cache_hash[name] = nil # Flush merged hash cache.
|
65
|
+
|
66
|
+
self.cache_files[name] = config_files # Config files changed or disappeared.
|
67
|
+
|
68
|
+
end # if config_data == nil || (now - last_loaded > self.reload_interval)
|
69
|
+
end # if force || config_data == nil || modified_time != last_modified
|
70
|
+
|
71
|
+
config_data
|
72
|
+
end # config_files.collect
|
73
|
+
configs.compact!
|
74
|
+
|
75
|
+
logger.debug "load_config_files(#{name.inspect}) => #{configs.inspect}"
|
76
|
+
|
77
|
+
# Keep last loaded config files around in case self.reload_dsabled.
|
78
|
+
self.cache_config_files[name] = configs #unless configs.empty?
|
79
|
+
|
80
|
+
configs
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
##
|
85
|
+
# Returns a list of all relevant config files as specified by suffixes list.
|
86
|
+
# Each element is an Array, containing:
|
87
|
+
#
|
88
|
+
# [
|
89
|
+
# "server", # The base name of the
|
90
|
+
# "server_production", # The suffixed name
|
91
|
+
# "/path/to/server.yml", # The absolute path to the file
|
92
|
+
# <Wed Apr 09 08:53:14> # The last modified time of the file or nil, if it doesn't exist.
|
93
|
+
# ]
|
94
|
+
#
|
95
|
+
def get_config_files(name)
|
96
|
+
files = []
|
97
|
+
|
98
|
+
self.load_paths.reverse.each do |directory|
|
99
|
+
# splatting *suffix allows us to deal with multipart suffixes
|
100
|
+
name_no_overlay, suffixes = suffixes_for(name)
|
101
|
+
suffixes.map { |suffix| [name_no_overlay, *suffix].compact.join('_') }.each do |name_with_suffix|
|
102
|
+
self.file_types.each do |ext|
|
103
|
+
filename = filename_for_name(name_with_suffix, directory, ext)
|
104
|
+
if File.exists?(filename)
|
105
|
+
modified_time = File.stat(filename).mtime
|
106
|
+
files << [name, name_with_suffix, filename, ext, modified_time]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
logger.debug "get_config_files(#{name}) => #{files.select { |x| x[3] }.inspect}"
|
113
|
+
|
114
|
+
files
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Return the config file information for the given config name.
|
119
|
+
#
|
120
|
+
def config_files(name)
|
121
|
+
self.cache_files[name] ||= get_config_files(name)
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
##
|
126
|
+
# Returns whether or not the config for the given config name has changed
|
127
|
+
# since it was last loaded.
|
128
|
+
#
|
129
|
+
# Returns true if any files for config have changes since
|
130
|
+
# last load.
|
131
|
+
def config_changed?(name)
|
132
|
+
logger.debug "config_changed?(#{name.inspect})"
|
133
|
+
name = name.to_s
|
134
|
+
!(self.cache_files[name] === get_config_files(name))
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
##
|
139
|
+
# Get the merged config hash for the named file.
|
140
|
+
# Returns a cached indifferent access faker hash merged
|
141
|
+
# from all config files for a name.
|
142
|
+
#
|
143
|
+
def get_config_data(name)
|
144
|
+
logger.debug "get_config_data(#{name.inspect})"
|
145
|
+
|
146
|
+
name = name.to_s
|
147
|
+
unless result = self.cache_hash[name]
|
148
|
+
result = self.cache_hash[name] =
|
149
|
+
make_indifferent(
|
150
|
+
merge_hashes(
|
151
|
+
load_config_files(name)
|
152
|
+
)
|
153
|
+
)
|
154
|
+
logger.debug "get_config_data(#{name.inspect}): reloaded"
|
155
|
+
end
|
156
|
+
|
157
|
+
result
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# If name is specified, checks that file for changes and
|
162
|
+
# reloads it if there are. Otherwise, checks all files
|
163
|
+
# in the cache, reloading the changed files.
|
164
|
+
def check_for_changes(name=nil)
|
165
|
+
changed = []
|
166
|
+
if name == nil
|
167
|
+
self.cache_hash.keys.dup.each do |name|
|
168
|
+
if reload_on_change(name)
|
169
|
+
changed << name
|
170
|
+
end
|
171
|
+
end
|
172
|
+
else
|
173
|
+
name = name.to_s
|
174
|
+
if reload_on_change(name)
|
175
|
+
changed << name
|
176
|
+
end
|
177
|
+
end
|
178
|
+
logger.debug "check_for_changes(#{name.inspect}) => #{changed.inspect}"
|
179
|
+
changed
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# If config files have changed, caches are flushed, on_load triggers are run.
|
184
|
+
def reload_on_change(name)
|
185
|
+
logger.debug "reload_on_change(#{name.inspect}), reload_disabled=#{self.reload_disabled?}"
|
186
|
+
if changed = config_changed?(name) && reload?
|
187
|
+
if self.cache_hash[name]
|
188
|
+
flush_cache(name) # flush cached config values.
|
189
|
+
fire_on_load(name) # force on_load triggers.
|
190
|
+
end
|
191
|
+
end
|
192
|
+
changed
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
##
|
197
|
+
# This method provides shorthand to retrieve configuration data that
|
198
|
+
# is global in scope, and used on an application or environment-wide
|
199
|
+
# level. The default location that it checks is the application file.
|
200
|
+
# The application config file is a special config file that should be
|
201
|
+
# used for config data that is broad in scope and used throughout the
|
202
|
+
# application. Since RConfig gives special regard to the application
|
203
|
+
# config file, thought should be given to whatever config information
|
204
|
+
# is placed there.
|
205
|
+
#
|
206
|
+
# Most config data will be specific to particular part of the
|
207
|
+
# application (i.e. database, web service), and should therefore
|
208
|
+
# be placed in its own specific config file, such as database.yml,
|
209
|
+
# or services.xml
|
210
|
+
#
|
211
|
+
# This method also acts as a wrapper for ENV. If no value is
|
212
|
+
# returned from the application config, it will also check
|
213
|
+
# ENV for a value matching the specified key.
|
214
|
+
#
|
215
|
+
# Ex.1 RConfig[:test_mode] =>
|
216
|
+
# RConfig.application[:test_mode] ||
|
217
|
+
# RConfig.application.test_mode
|
218
|
+
#
|
219
|
+
# Ex.2 RConfig[:web_app_root] => ENV['WEB_APP_ROOT']
|
220
|
+
#
|
221
|
+
# NOTE: The application config file can be in any of
|
222
|
+
# the supported formats (yml, xml, conf, etc.)
|
223
|
+
#
|
224
|
+
def [](key, file=:application)
|
225
|
+
self.config_for(file)[key] || ENV[key.to_s.upcase]
|
226
|
+
end
|
227
|
+
|
228
|
+
##
|
229
|
+
# Get the value specified by the args, in the file specified by th name
|
230
|
+
#
|
231
|
+
def with_file(name, *args)
|
232
|
+
logger.debug "with_file(#{name.inspect}, #{args.inspect})"
|
233
|
+
result = args.inject(config_for(name)) { |v, i|
|
234
|
+
logger.debug "v = #{v.inspect}, i = #{i.inspect}"
|
235
|
+
case v
|
236
|
+
when Hash
|
237
|
+
v[i.to_s]
|
238
|
+
when Array
|
239
|
+
i.is_a?(Integer) ? v[i] : nil
|
240
|
+
else
|
241
|
+
nil
|
242
|
+
end
|
243
|
+
}
|
244
|
+
logger.debug "with_file(#{name.inspect}, #{args.inspect}) => #{result.inspect}"
|
245
|
+
result
|
246
|
+
end
|
247
|
+
|
248
|
+
##
|
249
|
+
# Get a hash of merged config data.
|
250
|
+
# Will auto check every 5 minutes, for longer running apps, unless reload is disabled.
|
251
|
+
#
|
252
|
+
def config_for(name)
|
253
|
+
name = name.to_s
|
254
|
+
check_for_changes(name) if auto_check?(name)
|
255
|
+
data = get_config_data(name)
|
256
|
+
logger.debug "config_for(#{name.inspect}) => #{data.inspect}"
|
257
|
+
data
|
258
|
+
end
|
259
|
+
|
260
|
+
##
|
261
|
+
# Short-hand access to config file by its name.
|
262
|
+
#
|
263
|
+
# Example:
|
264
|
+
#
|
265
|
+
# RConfig.provider(:foo) => RConfig.with_file(:provider).foo
|
266
|
+
# RConfig.provider.foo => RConfig.with_file(:provider).foo
|
267
|
+
#
|
268
|
+
def method_missing(method, * args)
|
269
|
+
value = with_file(method, * args)
|
270
|
+
logger.debug "#{self}.method_missing(#{method.inspect}, #{args.inspect}) => #{value.inspect}"
|
271
|
+
value
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
data/lib/rconfig/exceptions.rb
CHANGED
@@ -1,13 +1,26 @@
|
|
1
|
-
|
2
|
-
#--
|
1
|
+
##
|
3
2
|
# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
|
4
3
|
#
|
5
4
|
# RConfig Exceptions
|
6
|
-
|
5
|
+
#
|
6
|
+
module RConfig
|
7
|
+
# General error in config initialization or operation.
|
8
|
+
class ConfigError < StandardError; end
|
9
|
+
|
10
|
+
# Load path(s) are not set, don't exist, or Invalid in some manner
|
11
|
+
class InvalidLoadPathError < ConfigError; end
|
12
|
+
|
13
|
+
module Exceptions
|
14
|
+
|
15
|
+
# Raised when no valid load paths are available.
|
16
|
+
def raise_load_path_error
|
17
|
+
raise InvalidLoadPathError, "No load paths were provided, and none of the default paths were found."
|
18
|
+
end
|
7
19
|
|
8
|
-
#
|
9
|
-
|
20
|
+
# Raised if logging is enabled, but no logger is specified}.
|
21
|
+
def raise_logger_error
|
22
|
+
raise ConfigError, "No logger was specified, and a defualt logger was not found. Set logger to `false` to disable logging."
|
23
|
+
end
|
10
24
|
|
11
|
-
|
12
|
-
|
13
|
-
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RConfig
|
2
|
+
module LoadPaths
|
3
|
+
include Constants
|
4
|
+
|
5
|
+
##
|
6
|
+
# Sets the list of directories to search for
|
7
|
+
# configuration files.
|
8
|
+
# The argument must be an array of strings representing
|
9
|
+
# the paths to the directories, or a string representing
|
10
|
+
# either a single path or a list of paths separated by
|
11
|
+
# either a colon (:) or a semi-colon (;).
|
12
|
+
def set_load_paths(paths)
|
13
|
+
self.load_paths = parse_load_paths(paths)
|
14
|
+
reload(true) # Load Paths have changed so force a reload
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Adds the specified path to the list of directories to search for
|
19
|
+
# configuration files.
|
20
|
+
# It only allows one path to be entered at a time.
|
21
|
+
def add_load_path(path)
|
22
|
+
if path = parse_load_paths(path).first # only accept first one.
|
23
|
+
self.load_paths << path
|
24
|
+
return reload(true) # Load Paths have changed so force a reload
|
25
|
+
end
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# If the paths are made up of a delimited string, then parse out the
|
31
|
+
# individual paths. Verify that each path is valid.
|
32
|
+
def parse_load_paths(paths)
|
33
|
+
return [] unless paths
|
34
|
+
if paths.is_a? String
|
35
|
+
path_sep = (paths =~ /;/) ? ';' : ':'
|
36
|
+
paths = paths.split(/#{path_sep}+/)
|
37
|
+
end
|
38
|
+
raise ArgumentError, "Path(s) must be a String or an Array [#{paths.inspect}]" unless paths.is_a? Array
|
39
|
+
raise ArgumentError, "Must provide at least one load path: [#{paths.inspect}]" if paths.empty?
|
40
|
+
paths.each do |dir|
|
41
|
+
dir = CONFIG_ROOT if dir == 'CONFIG_ROOT'
|
42
|
+
raise RConfig::InvalidConfigPathError, "This directory is invalid: [#{dir.inspect}]" unless Dir.exists?(dir)
|
43
|
+
end
|
44
|
+
paths
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Indicates whether or not config_paths have been set.
|
49
|
+
# Returns true if self.load_paths has at least one directory.
|
50
|
+
def load_paths_set?
|
51
|
+
not load_paths.blank?
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module RConfig
|
2
|
+
class Logger #:nodoc:
|
3
|
+
attr_accessor :level, :log_format, :date_format
|
4
|
+
attr_reader :output
|
5
|
+
|
6
|
+
FATAL = 4
|
7
|
+
ERROR = 3
|
8
|
+
WARN = 2
|
9
|
+
INFO = 1
|
10
|
+
DEBUG = 0
|
11
|
+
|
12
|
+
MAX_LEVEL = 4
|
13
|
+
|
14
|
+
def initialize(options={})
|
15
|
+
# Use provided output
|
16
|
+
if output = options[:output] && output.respond_to?(:puts)
|
17
|
+
@output = output
|
18
|
+
@needs_close = false
|
19
|
+
end
|
20
|
+
|
21
|
+
# Use log file
|
22
|
+
if output.nil? && options[:file] && File.exists?(optios[:file])
|
23
|
+
@output = File.open(options[:file].to_s, 'a')
|
24
|
+
@needs_close = true
|
25
|
+
end
|
26
|
+
|
27
|
+
# Default to stderr, if no outputter or file provider
|
28
|
+
@output ||= STDERR
|
29
|
+
|
30
|
+
# Use provided level or default to warn
|
31
|
+
@level = options[:level] ||
|
32
|
+
((defined?(Rails) && %w[test development].include?(Rails.env)) ? DEBUG : WARN)
|
33
|
+
|
34
|
+
# Date format
|
35
|
+
@date_format = options[:date_format] || '%Y-%m-%d %H:%M:%S'
|
36
|
+
#@log_format = options[:log_format] || "[%l] %d :: %M :: %t"
|
37
|
+
end
|
38
|
+
|
39
|
+
def close
|
40
|
+
output.close if @needs_close
|
41
|
+
end
|
42
|
+
|
43
|
+
def log(level, message)
|
44
|
+
if self.level <= level
|
45
|
+
indent = "%*s" % [MAX_LEVEL, "*" * (MAX_LEVEL - level)]
|
46
|
+
message.lines.each do |line|
|
47
|
+
log_str = "[#{indent}] #{Time.now.strftime(self.date_format)} :: #{line.strip}\n"
|
48
|
+
output.puts log_str
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def fatal(message)
|
54
|
+
log(FATAL, message)
|
55
|
+
end
|
56
|
+
|
57
|
+
def error(message)
|
58
|
+
log(ERROR, message)
|
59
|
+
end
|
60
|
+
|
61
|
+
def warn(message)
|
62
|
+
log(WARN, message)
|
63
|
+
end
|
64
|
+
|
65
|
+
def info(message)
|
66
|
+
log(INFO, message)
|
67
|
+
end
|
68
|
+
|
69
|
+
def debug(message)
|
70
|
+
log(DEBUG, message)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
class DisabledLogger
|
76
|
+
def log(level, message) end
|
77
|
+
def dont_log(message) end
|
78
|
+
|
79
|
+
alias_method :fatal, :dont_log
|
80
|
+
alias_method :error, :dont_log
|
81
|
+
alias_method :warn, :dont_log
|
82
|
+
alias_method :info, :dont_log
|
83
|
+
alias_method :debug, :dont_log
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|