Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

v0.4.0

  • Loading branch information...
commit 9ebefbc52d196aadeaee6cc61e19806a2c184313 1 parent 1cb653f
@rahmal authored
Showing with 1,331 additions and 1,800 deletions.
  1. +3 −7 .gitignore
  2. +1 −0  .rspec
  3. +1 −0  .rvmrc
  4. +10 −3 ChangeLog
  5. 0  Credits
  6. +9 −0 Gemfile
  7. +30 −0 Gemfile.lock
  8. +20 −21 LICENSE → LICENSE.txt
  9. +0 −31 Manifest
  10. 0  README.rdoc
  11. 0  Rakefile
  12. +0 −1  VERSION
  13. 0  doc/classes/ClassVariables.html
  14. 0  doc/classes/ConfigError.html
  15. 0  doc/classes/ConfigHash.html
  16. 0  doc/classes/Constants.html
  17. 0  doc/classes/Hash.html
  18. 0  doc/classes/InvalidConfigPathError.html
  19. 0  doc/classes/Object.html
  20. 0  doc/classes/PropertiesFileParser.html
  21. 0  doc/classes/RConfig.html
  22. 0  doc/created.rid
  23. 0  doc/files/README_rdoc.html
  24. 0  doc/files/lib/rconfig/class_variables_rb.html
  25. 0  doc/files/lib/rconfig/config_hash_rb.html
  26. 0  doc/files/lib/rconfig/constants_rb.html
  27. 0  doc/files/lib/rconfig/core_ext/hash_rb.html
  28. 0  doc/files/lib/rconfig/core_ext/object_rb.html
  29. 0  doc/files/lib/rconfig/core_ext_rb.html
  30. 0  doc/files/lib/rconfig/exceptions_rb.html
  31. 0  doc/files/lib/rconfig/properties_file_parser_rb.html
  32. 0  doc/files/lib/rconfig/rconfig_rb.html
  33. 0  doc/files/lib/rconfig_rb.html
  34. 0  doc/fr_class_index.html
  35. 0  doc/fr_file_index.html
  36. 0  doc/fr_method_index.html
  37. 0  doc/index.html
  38. 0  doc/rdoc-style.css
  39. +13 −0 lib/generators/rconfig/install_generator.rb
  40. +82 −0 lib/generators/rconfig/templates/rconfig.rb
  41. +98 −38 lib/rconfig.rb
  42. +11 −16 lib/rconfig/{mixins → }/callbacks.rb
  43. +56 −0 lib/rconfig/cascade.rb
  44. +103 −0 lib/rconfig/config.rb
  45. +0 −101 lib/rconfig/config_hash.rb
  46. +58 −0 lib/rconfig/constants.rb
  47. +0 −5 lib/rconfig/core_ext.rb
  48. +3 −0  lib/rconfig/core_ext/array.rb
  49. 0  lib/rconfig/core_ext/hash.rb
  50. +5 −0 lib/rconfig/core_ext/nil.rb
  51. +0 −46 lib/rconfig/core_ext/object.rb
  52. +3 −0  lib/rconfig/core_ext/string.rb
  53. +276 −0 lib/rconfig/core_methods.rb
  54. +19 −7 lib/rconfig/exceptions.rb
  55. +55 −0 lib/rconfig/load_paths.rb
  56. +67 −28 lib/rconfig/logger.rb
  57. +0 −10 lib/rconfig/mixins.rb
  58. +0 −71 lib/rconfig/mixins/class_variables.rb
  59. +0 −119 lib/rconfig/mixins/config_paths.rb
  60. +0 −54 lib/rconfig/mixins/constants.rb
  61. +0 −67 lib/rconfig/mixins/overlay.rb
  62. +0 −72 lib/rconfig/mixins/reload.rb
  63. +0 −120 lib/rconfig/mixins/utils.rb
  64. +0 −42 lib/rconfig/options.rb
  65. +52 −50 lib/rconfig/properties_file_parser.rb
  66. +0 −398 lib/rconfig/rconfig.rb
  67. +75 −0 lib/rconfig/reload.rb
  68. +92 −0 lib/rconfig/settings.rb
  69. +157 −0 lib/rconfig/utils.rb
  70. 0  { → lib}/tasks/gem.rake
  71. 0  { → lib}/tasks/rdoc.rake
  72. 0  { → lib}/tasks/spec.rake
  73. +0 −1  new-gem
  74. BIN  rconfig-0.3.2.gem
  75. +25 −24 rconfig.gemspec
  76. 0  spec/core_ext/object_spec.rb
  77. +7 −0 spec/rconfig_spec.rb
  78. 0  spec/spec.opts
  79. 0  spec/spec_helper.rb
  80. +0 −3  test/config_files/application.yml
  81. +0 −5 test/config_files/my_class.yml
  82. +0 −14 test/config_files/test.yml
  83. +0 −13 test/config_files/test_GB.yml
  84. +0 −15 test/config_files/test_US.yml
  85. +0 −9 test/config_files/test_local.yml
  86. +0 −15 test/config_files/test_production.yml
  87. +0 −394 test/rconfig_test.rb
View
10 .gitignore 100755 → 100644
@@ -1,21 +1,17 @@
*.iws
*.ipr
*.iml
-*.log
*.tmproj
+*.log
*.swp
*.*~
*~
.bundle
.directory
+.document
.idea
.project
-.redcar
-.rvmrc
-.rakeTasks
-.svn
-.yardoc
.DS_Store
+.rakeTasks
log
tmp
-doc
View
1  .rspec
@@ -0,0 +1 @@
+--color --format progress --backtrace
View
1  .rvmrc
@@ -0,0 +1 @@
+rvm use 1.9.2@rconfig
View
13 ChangeLog 100755 → 100644
@@ -1,16 +1,23 @@
0.4.0
=====
-* Extend object to add short-hand access to config.
-* Break rconfig core into separate modules to decrease core class size.
+* Broke RConfig core into separate modules to decrease core class size.
+* Added Rails generator to override RConfig settings in Rails projects
+* Added support for git-like config files, giving key/value properties files potentially three levels.
+* Refactored settings module to allow for clean defaults, and overriding.
+* Added RConfig logger.
+* Renamed overlay to cascade
* Fixed a syntax error in when clause of config_hash.rb for ruby 1.9.1 - From dvyjones/rconfig (github).
* Fixed raise on allow_reload method.
* Fixed unit test, rconfig_test.rb.
* Fixed minor typos, comments, and bugs.
* Refactored Hash#weave signature; changed dont_clobber=true, to clobber=false (reads more intuitively).
-* Added support for git-like config files, giving key/value properties files potentially three levels.
* Removed redundant validate method from PropertiesFileParser.
* Moved PropertiesFileParser regexps to constants.
+0.3.3
+=====
+* Fixed overlay class variable not loaded warning.
+
0.3.2
=====
* Fixed bug in 0.3.1 causing stackrace
View
0  Credits 100755 → 100644
File mode changed
View
9 Gemfile
@@ -0,0 +1,9 @@
+source 'http://rubygems.org'
+
+gem 'activesupport', '~> 3.0', :require => 'active_support'
+
+group :development do
+ gem 'rspec', '~> 2.3.0'
+ gem 'bundler', '~> 1.0.0'
+ gem 'jeweler', '~> 1.6.4'
+end
View
30 Gemfile.lock
@@ -0,0 +1,30 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activesupport (3.1.1)
+ multi_json (~> 1.0)
+ diff-lcs (1.1.3)
+ git (1.2.5)
+ jeweler (1.6.4)
+ bundler (~> 1.0)
+ git (>= 1.2.5)
+ rake
+ multi_json (1.1.0)
+ rake (0.9.2.2)
+ rspec (2.3.0)
+ rspec-core (~> 2.3.0)
+ rspec-expectations (~> 2.3.0)
+ rspec-mocks (~> 2.3.0)
+ rspec-core (2.3.1)
+ rspec-expectations (2.3.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.3.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ activesupport (~> 3.0)
+ bundler (~> 1.0.0)
+ jeweler (~> 1.6.4)
+ rspec (~> 2.3.0)
View
41 LICENSE → LICENSE.txt 100755 → 100644
@@ -1,21 +1,20 @@
-Copyright (C) 2009 Rahmal H. Conda
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
+Copyright (c) 2011 Rahmal Conda
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
31 Manifest
@@ -1,31 +0,0 @@
-ChangeLog
-demo/
-demo/demo.conf
-demo/demo.rb
-demo/demo.xml
-demo/demo.yml
-demo/global.yml
-lib/
-lib/rconfig/
-lib/rconfig/config_hash.rb
-lib/rconfig/config_parser.rb
-lib/rconfig/core_ext/
-lib/rconfig/core_ext/hash.rb
-rconfig.rb
-LICENSE
-Manifest
-Rakefile
-rconfig.gemspec
-README
-tasks/
-tasks/docs.rake
-tasks/gemspec.rake
-test/
-test/rconfig_test.rb
-test/test_files/
-test/test_files/global.yml
-test/test_files/test_development.yml
-test/test_files/test_local.yml
-test/test_files/test_production.yml
-test/test_files/test.yml
-VERSION
View
0  README.rdoc 100755 → 100644
File mode changed
View
0  Rakefile 100755 → 100644
File mode changed
View
1  VERSION
@@ -1 +0,0 @@
-0.3.3
View
0  doc/classes/ClassVariables.html 100755 → 100644
File mode changed
View
0  doc/classes/ConfigError.html 100755 → 100644
File mode changed
View
0  doc/classes/ConfigHash.html 100755 → 100644
File mode changed
View
0  doc/classes/Constants.html 100755 → 100644
File mode changed
View
0  doc/classes/Hash.html 100755 → 100644
File mode changed
View
0  doc/classes/InvalidConfigPathError.html 100755 → 100644
File mode changed
View
0  doc/classes/Object.html 100755 → 100644
File mode changed
View
0  doc/classes/PropertiesFileParser.html 100755 → 100644
File mode changed
View
0  doc/classes/RConfig.html 100755 → 100644
File mode changed
View
0  doc/created.rid 100755 → 100644
File mode changed
View
0  doc/files/README_rdoc.html 100755 → 100644
File mode changed
View
0  doc/files/lib/rconfig/class_variables_rb.html 100755 → 100644
File mode changed
View
0  doc/files/lib/rconfig/config_hash_rb.html 100755 → 100644
File mode changed
View
0  doc/files/lib/rconfig/constants_rb.html 100755 → 100644
File mode changed
View
0  doc/files/lib/rconfig/core_ext/hash_rb.html 100755 → 100644
File mode changed
View
0  doc/files/lib/rconfig/core_ext/object_rb.html 100755 → 100644
File mode changed
View
0  doc/files/lib/rconfig/core_ext_rb.html 100755 → 100644
File mode changed
View
0  doc/files/lib/rconfig/exceptions_rb.html 100755 → 100644
File mode changed
View
0  doc/files/lib/rconfig/properties_file_parser_rb.html 100755 → 100644
File mode changed
View
0  doc/files/lib/rconfig/rconfig_rb.html 100755 → 100644
File mode changed
View
0  doc/files/lib/rconfig_rb.html 100755 → 100644
File mode changed
View
0  doc/fr_class_index.html 100755 → 100644
File mode changed
View
0  doc/fr_file_index.html 100755 → 100644
File mode changed
View
0  doc/fr_method_index.html 100755 → 100644
File mode changed
View
0  doc/index.html 100755 → 100644
File mode changed
View
0  doc/rdoc-style.css 100755 → 100644
File mode changed
View
13 lib/generators/rconfig/install_generator.rb
@@ -0,0 +1,13 @@
+module RConfig
+ module Generators
+ class InstallGenerator < Rails::Generators::Base
+ desc "Create RConfig settings initializer file"
+ source_root File.expand_path("../templates", __FILE__)
+
+ def create_initializer_file
+ template "rconfig.rb", "config/initializers/rconfig.rb"
+ end
+ end
+ end
+end
+
View
82 lib/generators/rconfig/templates/rconfig.rb
@@ -0,0 +1,82 @@
+# Use this hook to configure rconfig mailer, warden hooks and so forth. The first
+# four configuration values can also be set straight in your models.
+RConfig.setup do |config|
+
+ # ==> Configuration File Load Paths
+ # The list of directories from which to load configuration files.
+ # If This is a Rails Application, it will default to "#{Rail.root}/config",
+ # It will also check for the existence of a CONFIG_ROOT constant, and adds
+ # it, if found. I will also attempt to use "config" if such a directory
+ # exists in the application root. Otherwise it will be empty, in which case
+ # one or more paths must be added manually here.
+ #
+ # config.load_paths = []
+
+ # ==> Overlay Suffix
+ # Use this to add a custom cascade to be used beyond that of environment,
+ # or platform cascades. For instance, besides supporting configuration for
+ # development as 'application_development.yml', you can use cascade to also
+ # provide a locale, by setting config.cascade = 'GB', RConfig will look for
+ # the file 'application_development_GB.yml'
+ # See documentation for details on how cascading configurations work.
+ #
+ # config.cascade = false
+
+ # ==> Configuration File Types
+ # The type of configuration files to load within the configuration
+ # directories. The supported file types are yaml, xml, and property
+ # files (.property). One or more can be used at once.
+ # The default is to use yml files only, but other types can added
+ # by using the pre-defined constants or simply setting an new array.
+ #
+ # Pre-defined Constants
+ # YML_FILE_TYPES = [:yml, :yaml] # yml, yaml => yaml files, parsable by YAML library
+ # XML_FILE_TYPES = [:xml] # xml => self-explanatory
+ # CNF_FILE_TYPES = [:cnf, :conf, :config, :properties] # conf, properties => key=value based config files
+ # CONFIG_FILE_TYPES = YML + XML + CNF # All => All of the above file types combined
+ #
+ # Examples:
+ # config.file_types = CONFIG_FILE_TYPES
+ # config.file_types = [ :yml, :xml, :property ]
+ #
+ # config.file_types = [ :yml ]
+
+ # ==> Enable/Disable Cache Reload
+ # Flag variable indicating whether or not periodic reloads should
+ # be performed. Defaults to false.
+ #
+ # config.enabled_reload = false
+
+ # ==> Interval for Reloading Configuration Data
+ # The interval in which to perform periodic reloading of config files (in
+ # seconds). Files are checked befor reload is executed. They are not reloaded
+ # if the data has not changed. Defaults to 300 seconds (5 minutes).
+ #
+ # config.reload_interval = 300
+
+ # ==> Logger
+ # The logger rconfig will log to. By default RConfig uses the Rails logger,
+ # or the standard Ruby logger if this is not a rails environment. The
+ # The logger can be changed to a different logger, like Log4r. You can also stick with the RConfig logger, but customize
+ # it's log options The available options are as follows:
+ # * level: The log level to log at. Default: ERROR
+ # * output: The outputter to send messages to. Default: STDERR
+ # * file: The name of the file to log to (cannot be used with output).
+ # * date_format: The format of the timestamp when something is logged.
+ # Options are passed in a hash to the new method.
+ # Example:
+ # RConfig::Logger.new(
+ # :file => "#{Rails.root}/log/production.log",
+ # :date_format => "%Y-%m-%d %H:%M:%S",
+ # :level => RConfig::Logger::ERROR
+ # )
+ #
+ # config.logger = RConfig::Logger.new
+
+ # ==> Log Level
+ # The log level the specified logger will be set to. By default it is set to WARN.
+ # It can also be set when initializing the logger, specifying the level in the options.
+ #
+ # config.log_level = RConfig::Logger::WARN
+end
+
View
136 lib/rconfig.rb 100755 → 100644
@@ -1,47 +1,107 @@
-#--
+##
+#
# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
+# -------------------------------------------------------------------
+# The complete solution for Ruby Configuration Management. RConfig is a Ruby library that
+# manages configuration within Ruby applications. It bridges the gap between yaml, xml, and
+# key/value based properties files, by providing a centralized solution to handle application
+# configuration from one location. It provides the simplicity of hash-based access, that
+# Rubyists have come to know and love, supporting your configuration style of choice, while
+# providing many new features, and an elegant API.
+#
+# -------------------------------------------------------------------
+# * Simple, easy to install and use.
+# * Supports yaml, xml, and properties files.
+# * Yaml and xml files supprt infinite level of configuration grouping.
+# * Intuitive dot-notation 'key chaining' argument access.
+# * Simple well-known hash/array based argument access.
+# * Implements multilevel caching to reduce disk access.
+# * Short-hand access to 'global' application configuration, and shell environment.
+# * Overlays multiple configuration files to support environment, host, and
+# even locale-specific configuration.
+#
+# -------------------------------------------------------------------
+# The overlay order of the config files is defined by SUFFIXES:
+# * nil
+# * _local
+# * _config
+# * _local_config
+# * _{environment} (.i.e _development)
+# * _{environment}_local (.i.e _development_local)
+# * _{hostname} (.i.e _whiskey)
+# * _{hostname}_config_local (.i.e _whiskey_config_local)
+#
+# -------------------------------------------------------------------
+#
+# Example:
+#
+# shell/console =>
+# export LANG=en
#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#++
+# demo.yml =>
+# server:
+# address: host.domain.com
+# port: 81
+# ...
+#
+# application.properties =>
+# debug_level = verbose
+# ...
+#
+# demo.rb =>
+# require 'rconfig'
+# RConfig.config_paths = ['$HOME/config', '#{APP_ROOT}/config', '/demo/conf']
+# RConfig.demo[:server][:port] => 81
+# RConfig.demo.server.address => 'host.domain.com'
+#
+# RConfig[:debug_level] => 'verbose'
+# RConfig[:lang] => 'en'
+# ...
+#
+require 'active_support'
+require 'rconfig/core_ext/array'
+require 'rconfig/core_ext/hash'
+require 'rconfig/core_ext/nil'
-$:.unshift File.dirname(__FILE__)
+module RConfig
+ VERSION = '0.4.0'
-autoload :Socket, 'socket'
-autoload :YAML, 'yaml'
-autoload :Logger, 'logger'
-autoload :Singleton, 'singleton'
+ autoload :Socket, 'socket'
+ autoload :YAML, 'yaml'
+ autoload :Logger, 'logger'
+ autoload :Singleton, 'singleton'
-require 'rubygems'
-require 'active_support'
+ autoload :Concern, 'active_support/concern'
+ autoload :Hash, 'active_support/core_ext/hash/conversions'
+ autoload :HashWithIndifferentAccess, 'active_support/core_ext/hash/indifferent_access'
-autoload :Hash, 'active_support/core_ext/hash/conversions'
-autoload :HashWithIndifferentAccess, 'active_support/core_ext/hash/indifferent_access'
+ #autoload :Hash, 'rconfig/core_ext/hash'
+ #autoload :NilClass, 'rconfig/core_ext/nil'
+ #autoload :Enumerable, 'rconfig/core_ext/enumerable'
-require 'rconfig/mixins'
-require 'rconfig/core_ext'
-require 'rconfig/config_hash'
-require 'rconfig/logger'
-require 'rconfig/properties_file_parser'
-require 'rconfig/exceptions'
-require 'rconfig/rconfig'
+ autoload :Config, 'rconfig/config'
+ autoload :Logger, 'rconfig/logger'
+ autoload :Exceptions, 'rconfig/exceptions'
+ autoload :Utils, 'rconfig/utils'
+ autoload :Constants, 'rconfig/constants'
+ autoload :Settings, 'rconfig/settings'
+ autoload :ConfigError, 'rconfig/exceptions'
+ autoload :LoadPaths, 'rconfig/load_paths'
+ autoload :Cascade, 'rconfig/cascade'
+ autoload :Callbacks, 'rconfig/callbacks'
+ autoload :Reload, 'rconfig/reload'
+ autoload :CoreMethods, 'rconfig/core_methods'
+ autoload :PropertiesFileParser, 'rconfig/properties_file_parser'
-# Create global reference to RConfig instance
-$config = RConfig.instance
+ extend ActiveSupport::Concern
+ extend Utils
+ extend Constants
+ extend Settings
+ extend Exceptions
+ extend LoadPaths
+ extend Cascade
+ extend Callbacks
+ extend Reload
+ extend CoreMethods
+end
View
27 lib/rconfig/mixins/callbacks.rb → lib/rconfig/callbacks.rb 100755 → 100644
@@ -1,4 +1,4 @@
-module Mixins
+module RConfig
module Callbacks
##
@@ -9,16 +9,16 @@ module Callbacks
# Example:
#
# class MyClass
- # @@my_config = { }
+ # self.my_config = { }
# RConfig.on_load(:cache) do
- # @@my_config = { }
+ # self.my_config = { }
# end
# def my_config
- # @@my_config ||= something_expensive_thing_on_config(RConfig.cache.memory_limit)
+ # self.my_config ||= something_expensive_thing_on_config(RConfig.cache.memory_limit)
# end
# end
#
- def self.on_load(*args, &blk)
+ def on_load(*args, &blk)
args << :ANY if args.empty?
proc = blk.to_proc
@@ -27,24 +27,19 @@ def self.on_load(*args, &blk)
# Register callback proc.
args.each do |name|
- name = name.to_s
- (@@on_load[name] ||= []) << proc
+ (self.callbacks[name.to_s] ||= []) << proc
end
end
- protected
-
##
# Executes all of the reload callbacks registered to the specified config name,
# and all of the callbacks registered to run on any config, as specified by the
# :ANY symbol.
- def self.fire_on_load(name)
- callbacks =
- (@@on_load['ANY'] || EMPTY_ARRAY) +
- (@@on_load[name] || EMPTY_ARRAY)
- callbacks.uniq!
- logger.debug "fire_on_load(#{name.inspect}): callbacks[#{callbacks.inspect}]" unless callbacks.empty?
- callbacks.each { |cb| cb.call() }
+ def fire_on_load(name)
+ procs = (self.callbacks['ANY'] || RConfig::EMPTY_ARRAY) + (self.callbacks[name] || RConfig::EMPTY_ARRAY)
+ procs.uniq!
+ logger.debug "fire_on_load(#{name.inspect}): callbacks[#{procs.inspect}]" unless procs.empty?
+ procs.each { |proc| proc.call() }
end
end
View
56 lib/rconfig/cascade.rb
@@ -0,0 +1,56 @@
+module RConfig
+ module Cascade
+ include Constants
+
+ ##
+ # Sets a custome overlay for
+ def overlay=(value)
+ reload(false) if self.overlay != value
+ self.overlay = value && value.dup.freeze
+ end
+
+ ##
+ # Returns a list of suffixes to try for a given config name.
+ #
+ # A config name with an explicit overlay (e.g.: 'name_GB')
+ # overrides any current _overlay.
+ #
+ # This allows code to specifically ask for config overlays
+ # for a particular locale.
+ #
+ def suffixes_for(name)
+ name = name.to_s
+ self.suffixes[name] ||= begin
+ ol = overlay
+ name_x = name.dup
+ if name_x.sub!(/_([A-Z]+)$/, '')
+ ol = $1
+ end
+ name_x.freeze
+ result = if ol
+ ol_ = ol.upcase
+ ol = ol.downcase
+ x = []
+ SUFFIXES.each do |suffix|
+ # Standard, no overlay:
+ # e.g.: database_<suffix>.yml
+ x << suffix
+
+ # Overlay:
+ # e.g.: database_(US|GB)_<suffix>.yml
+ x << [ol_, suffix]
+ end
+ [name_x, x.freeze]
+ else
+ [name.dup.freeze, SUFFIXES.freeze]
+ end
+ result.freeze
+
+ logger.debug "suffixes(#{name}) => #{result.inspect}"
+
+ result
+ end
+ end
+
+ end
+end
View
103 lib/rconfig/config.rb
@@ -0,0 +1,103 @@
+##
+# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
+#
+# Config is a special class, derived from HashWithIndifferentAccess.
+# It was specifically created for handling config data or creating mock
+# objects from yaml files. It provides a dotted notation for accessing
+# embedded hash values, similar to the way one might traverse a object tree.
+#
+module RConfig
+ class Config < HashWithIndifferentAccess
+
+ ##
+ # HashWithIndifferentAccess#default is broken in early versions of Rails.
+ # This is defined to use the hash version in Config#default
+ define_method(:hash_default, Hash.instance_method(:default))
+
+ ##
+ # Dotted notation can be used with arguments (useful for creating mock objects)
+ # in the YAML file the method name is a key, argument(s) form a nested key,
+ # so that the correct value is retrieved and returned.
+ #
+ # For example loading to variable foo a yaml file that looks like:
+ # customer:
+ # id: 12345678
+ # verified:
+ # phone: verified
+ # :address: info_not_available
+ # ? [name, employer]
+ # : not_verified
+ #
+ # Allows the following calls:
+ # foo.customer.id => 12345678
+ # foo.customer.verified.phone => verified
+ # foo.customer.verified("phone") => verified
+ # foo.customer.verified(:address) => info_not_available
+ # foo.customer.verified("name", "employer") => not_verified
+ #
+ # Note that :address is specified as a symbol, where phone is just a string.
+ # Depending on what kind of parameter the method being mocked out is going
+ # to be called with, define in the YAML file either a string or a symbol.
+ # This also works inside the composite array keys.
+ def method_missing(method, *args)
+ method = method.to_s
+ value = self[method]
+ case args.size
+ when 0 # e.g.: RConfig.application.method
+ value
+ when 1 # e.g.: RConfig.application.method(one_arg)
+ value.send(args[0])
+ else # e.g.: RConfig.application.method(arg_one, args_two, ...)
+ value[args]
+ end
+ end
+
+ ##
+ # HashWithIndifferentAccess#dup always returns HashWithIndifferentAccess!
+ # This is overriden to return Config (or any other derived class)
+ def dup
+ self.class.new(self)
+ end
+
+ ##
+ # Why the &*#^@*^&$ isn't HashWithIndifferentAccess doing this?
+ # HashWithIndifferentAccess doesn't (or didn't?) override Hash's
+ # []! That's why it's so destructive!
+ def [](key)
+ key = key.to_s if key.kind_of?(Symbol)
+ super(key)
+ end
+
+ ##
+ # Allow hash.default => hash['default']
+ # without breaking Hash's usage of default(key)
+ def default(key = self.default_key)
+ key = key.to_s if key.is_a?(Symbol)
+ key == self.default_key ? self['default'] : hash_default(key)
+ end
+
+ ##
+ # HashWithIndifferentAccess#update is broken in earlier versions of Rails
+ # Hash#update returns self, BUT, HashWithIndifferentAccess#update does not!
+ #
+ # { :a => 1 }.update({ :b => 2, :c => 3 })
+ # => { :a => 1, :b => 2, :c => 3 }
+ #
+ # HashWithIndifferentAccess.new({ :a => 1 }).update({ :b => 2, :c => 3 })
+ # => { :b => 2, :c => 3 } # WTF?
+ #
+ # Subclasses should *never* override methods and break their protocols!!!!
+ def update(hash)
+ super(hash)
+ self
+ end
+
+ ##
+ # Override HashWithIndifferentAccess#convert_value
+ # return instance of Config for Hash values.
+ def convert_value(value)
+ value.is_a?(Hash) ? self.class.new(value).freeze : super
+ end
+
+ end # class Config
+end
View
101 lib/rconfig/config_hash.rb
@@ -1,101 +0,0 @@
-##
-# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
-#
-# ConfigHash is a special class, derived from HashWithIndifferentAccess.
-# It was specifically created for handling config data or creating mock
-# objects from yaml files. It provides a dotted notation for accessing
-# embedded hash values, similar to the way one might traverse a object tree.
-#
-class ConfigHash < HashWithIndifferentAccess
-
- ##
- # HashWithIndifferentAccess#default is broken in early versions of Rails.
- # This is defined to use the hash version in ConfigHash#default
- define_method(:hash_default, Hash.instance_method(:default))
-
- ##
- # Dotted notation can be used with arguments (useful for creating mock objects)
- # in the YAML file the method name is a key, argument(s) form a nested key,
- # so that the correct value is retrieved and returned.
- #
- # For example loading to variable foo a yaml file that looks like:
- # customer:
- # id: 12345678
- # verified:
- # phone: verified
- # :address: info_not_available
- # ? [name, employer]
- # : not_verified
- #
- # Allows the following calls:
- # foo.customer.id => 12345678
- # foo.customer.verified.phone => verified
- # foo.customer.verified("phone") => verified
- # foo.customer.verified(:address) => info_not_available
- # foo.customer.verified("name", "employer") => not_verified
- #
- # Note that :address is specified as a symbol, where phone is just a string.
- # Depending on what kind of parameter the method being mocked out is going
- # to be called with, define in the YAML file either a string or a symbol.
- # This also works inside the composite array keys.
- def method_missing(method, *args)
- method = method.to_s
- value = self[method]
- case args.size
- when 0 # e.g.: RConfig.application.method
- value
- when 1 # e.g.: RConfig.application.method(one_arg)
- value.send(args[0])
- else # e.g.: RConfig.application.method(arg_one, args_two, ...)
- value[args]
- end
- end
-
- ##
- # HashWithIndifferentAccess#dup always returns HashWithIndifferentAccess!
- # This is overriden to return ConfigHash (or any other derived class)
- def dup
- self.class.new(self)
- end
-
- ##
- # Why the &*#^@*^&$ isn't HashWithIndifferentAccess doing this?
- # HashWithIndifferentAccess doesn't (or didn't?) override Hash's
- # []! That's why it's so destructive!
- def [](key)
- key = key.to_s if key.kind_of?(Symbol)
- super(key)
- end
-
- ##
- # Allow hash.default => hash['default']
- # without breaking Hash's usage of default(key)
- def default(key = self.default_key)
- key = key.to_s if key.is_a?(Symbol)
- key == self.default_key ? self['default'] : hash_default(key)
- end
-
- ##
- # HashWithIndifferentAccess#update is broken in earlier versions of Rails
- # Hash#update returns self, BUT, HashWithIndifferentAccess#update does not!
- #
- # { :a => 1 }.update({ :b => 2, :c => 3 })
- # => { :a => 1, :b => 2, :c => 3 }
- #
- # HashWithIndifferentAccess.new({ :a => 1 }).update({ :b => 2, :c => 3 })
- # => { :b => 2, :c => 3 } # WTF?
- #
- # Subclasses should *never* override methods and break their protocols!!!!
- def update(hash)
- super(hash)
- self
- end
-
- ##
- # Override HashWithIndifferentAccess#convert_value
- # return instance of ConfigHash for Hash values.
- def convert_value(value)
- value.is_a?(Hash) ? self.class.new(value).freeze : super
- end
-
-end # class ConfigHash
View
58 lib/rconfig/constants.rb
@@ -0,0 +1,58 @@
+module RConfig
+ module Constants
+
+ # Sets CONFIG_ROOT to RAILS_ROOT/config unless it has already
+ # been defined (i.e. in rails env, or calling ruby app).
+ CONFIG_ROOT = File.join(Rails.root, 'config') if defined?(::Rails) && !defined?(CONFIG_ROOT)
+
+ # ENV TIER i.e. (development, integration, staging, or production)
+ # Defaults to RAILS_ENV if running in Rails, otherwise, it checks
+ # if ENV['TIER'] is present. If not, it assumes production.
+ ENV_TIER = (defined?(RAILS_ENV) ? RAILS_ENV : (ENV['TIER'] || 'production')) unless defined? ENV_TIER
+
+ # yml, yaml => yaml files, parsable by YAML library
+ YML_FILE_TYPES = [:yml, :yaml].freeze unless defined? YML_FILE_TYPES
+
+ # xml => self-explanatory
+ XML_FILE_TYPES = [:xml].freeze unless defined? XML_FILE_TYPES
+
+ # conf, properties => <key=value> based config files
+ CNF_FILE_TYPES = [:cnf, :conf, :config, :properties].freeze unless defined? CNF_FILE_TYPES
+
+ # The type of file used for config. Valid choices
+ # include (yml, yaml, xml, conf, config, properties)
+ CONFIG_FILE_TYPES = (YML_FILE_TYPES + XML_FILE_TYPES + CNF_FILE_TYPES).freeze unless defined? CONFIG_FILE_TYPES
+
+ # Use CONFIG_HOSTNAME environment variable to
+ # test host-based configurations.
+ HOSTNAME = ENV['CONFIG_HOSTNAME'] || Socket.gethostname unless defined? HOSTNAME
+
+ # Short Hostname: removes all chars from HOSTNAME, after first "."
+ # Used to specify machine-specific config files.
+ HOSTNAME_SHORT = HOSTNAME.sub(/\..*$/, '').freeze unless defined? HOSTNAME_SHORT
+
+ # This is an array of filename suffixes facilitates cascading
+ # configuration overrides (i.e. 'services_local', 'services_development').
+ # These files get loaded in the order of the array. Meaning the last file
+ # loaded overrides everything before it. So config files suffixed with
+ # hostname has the highest precedence, and therefore overrides everything.
+ # Example:
+ # database_local.yml overrides database.yml
+ # database_staging.yml overrides database_local.yml
+ # database_appsvr01.yml overrides database_integration.yml
+ SUFFIXES = [
+ nil, # Empty suffix, used for default config file (i.e. database.yml).
+ :local, # Allows user to create 'local' overrides (i.e. database_local.yml), primarily used for development.
+ :config, :local_config,
+ ENV_TIER, [ENV_TIER, :local], # Environment configs (i.e. development, test, production).
+ HOSTNAME_SHORT, [HOSTNAME_SHORT, :config_local], # Short hostname (i.e. appsvr01), for server-specific configs.
+ HOSTNAME, [HOSTNAME, :config_local] # Hostname (i.e. appsvr01.acme.com), for server/domain-specific configs.
+ ] unless defined? SUFFIXES
+
+ # Used in place of undefined but expected arrays,
+ # to prevent creating a bunch of unecesary arrays
+ # in memory. See ConfigCore.fire_on_load
+ EMPTY_ARRAY = [].freeze unless defined? EMPTY_ARRAY
+
+ end
+end
View
5 lib/rconfig/core_ext.rb
@@ -1,5 +0,0 @@
-##
-# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
-#
-require 'rconfig/core_ext/hash'
-require 'rconfig/core_ext/object'
View
3  lib/rconfig/core_ext/array.rb
@@ -0,0 +1,3 @@
+class Array
+ alias_method :blank?, :empty?
+end
View
0  lib/rconfig/core_ext/hash.rb 100755 → 100644
File mode changed
View
5 lib/rconfig/core_ext/nil.rb
@@ -0,0 +1,5 @@
+class NilClass
+ def blank?
+ true
+ end
+end
View
46 lib/rconfig/core_ext/object.rb
@@ -1,46 +0,0 @@
-class Object
-
- ##
- # @person ? @person.name : nil
- # - or -
- # @people[:email] if @people
- # - vs -
- # @person.try(:name)
- def try(method, *args)
- result = send(method, * args) if respond_to?(method)
- result = send(:[], method) if result.nil? && respond_to?(:[])
- result
- end
-
- ##
- # Convience method for short-hand access to class specific config. If
- # a config specific to this class doesn't exist, it'll return the
- # root config instance.
- #
- # Example:
- #
- # # Given CONFIG_PATH/person.yml (with param sort_by_lastname: true)
- # @person = Person.new
- # @person.config => $config.person
- # @person.config.sort_by_lastname => true
- #
- # # Given CONFIG_PATH/bank_account.yml (with param mask_account_number: true)
- # bank_acct = BankAccount.new
- # bank_acct.config => $config.bank_account
- # bank_acct.config.mask_account_number => true
- #
- # # Given no specific config
- # @person = Person.new
- # @person.config => $config
- # @person.config.bank_account => $config.bank_account
- #
- # NOTE: If there is a class-specific config file, and an object needs to
- # access a different config, use the global instance ($config) or
- # the class (RConfig).
- #
- def config
- this_config = $config.send(self.class.name.underscore.to_sym)
- this_config.blank? ? $config : this_config
- end
-
-end
View
3  lib/rconfig/core_ext/string.rb
@@ -0,0 +1,3 @@
+class String
+ alias_method :blank?, :empty?
+end
View
276 lib/rconfig/core_methods.rb
@@ -0,0 +1,276 @@
+##
+# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
+#
+module RConfig
+ module CoreMethods
+ include Constants
+
+ ##
+ # Get each config file's yaml hash for the given config name,
+ # to be merged later. Files will only be loaded if they have
+ # not been loaded before or the files have changed within the
+ # last five minutes, or force is explicitly set to true.
+ #
+ def load_config_files(name, force=false)
+ name = name.to_s
+
+ # Return last config file hash list loaded,
+ # if reload is disabled and files have already been loaded.
+ return self.cache_config_files[name] if self.reload_disabled? && self.cache_config_files[name]
+
+ logger.info "Loading config files for: #{name}"
+ logger.debug "load_config_files(#{name.inspect})"
+
+
+ now = Time.now
+
+ # Get array of all the existing files file the config name.
+ config_files = self.get_config_files(name)
+
+ # Get all the data from all yaml files into as hashes
+ hashes = config_files.collect do |f|
+ name, name_x, filename, ext, mtime = * f
+
+ # Get the cached file info the specific file, if
+ # it's been loaded before.
+ val, last_mtime, last_loaded = self.cache[filename]
+
+ logger.debug "f = #{f.inspect}" +
+ "cache #{name_x} filename = #{filename.inspect}" +
+ "cache #{name_x} val = #{val.inspect}" +
+ "cache #{name_x} last_mtime = #{last_mtime.inspect}" +
+ "cache #{name_x} last_loaded = #{last_loaded.inspect}"
+
+ # Load the file if its never been loaded or its been more than
+ # so many minutes since last load attempt. (default: 5 minutes)
+ if val.blank? || (now - last_loaded > self.reload_interval)
+ if force || val.blank? || mtime != last_mtime
+
+ logger.debug "mtime #{name.inspect} #{filename.inspect} " +
+ "changed #{mtime != last_mtime} : #{mtime.inspect} #{last_mtime.inspect}"
+
+ # Get contents from config file
+ File.open(filename) do |f|
+ logger.debug "RConfig: loading #{filename.inspect}"
+ val = parse_file(f, ext)
+ val = val[name] if ext == :xml # xml document must have root tag matching the file name.
+ logger.debug "RConfig: loaded #{filename.inspect} => #{val.inspect}"
+ (self.config_loaded ||= {})[name] = config_files
+ end
+
+ # Save cached config file contents, and mtime.
+ self.cache[filename] = [val, mtime, now]
+ logger.debug "cache[#{filename.inspect}] = #{self.cache[filename].inspect}"
+
+ # Flush merged hash cache.
+ self.cache_hash[name] = nil
+
+ # Config files changed or disappeared.
+ self.cache_files[name] = config_files
+
+ end # if val == nil || (now - last_loaded > self.reload_interval)
+ end # if force || val == nil || mtime != last_mtime
+
+ val
+ end
+ hashes.compact!
+
+ logger.debug "load_config_files(#{name.inspect}) => #{hashes.inspect}"
+
+ # Keep last loaded config files around in case self.reload_dsabled.
+ self.cache_config_files[name] = hashes #unless hashes.empty?
+
+ hashes
+ end
+
+
+ ##
+ # Returns a list of all relevant config files as specified
+ # by _suffixes list.
+ # Each element is an Array, containing:
+ # [ "the-top-level-config-name",
+ # "the-suffixed-config-name",
+ # "/the/absolute/path/to/yaml.yml",
+ # # The mtime of the yml file or nil, if it doesn't exist.
+ # ]
+ def get_config_files(name)
+ files = []
+
+ self.load_paths.reverse.each do |dir|
+ # splatting *suffix allows us to deal with multipart suffixes
+ name_no_overlay, suffixes = suffixes_for(name)
+ suffixes.map { |suffix| [name_no_overlay, * suffix].compact.join('_') }.each do |name_x|
+ self.file_types.each do |ext|
+ filename = filename_for_name(name_x, dir, ext)
+ if File.exists?(filename)
+ files << [name, name_x, filename, ext, File.stat(filename).mtime]
+ end
+ end
+ end
+ end
+
+ logger.debug "get_config_files(#{name}) => #{files.select { |x| x[3] }.inspect}"
+
+ files
+ end
+
+ ##
+ # Return the config file information for the given config name.
+ #
+ def config_files(name)
+ self.cache_files[name] ||= get_config_files(name)
+ end
+
+
+ ##
+ # Returns whether or not the config for the given config name has changed
+ # since it was last loaded.
+ #
+ # Returns true if any files for config have changes since
+ # last load.
+ def config_changed?(name)
+ logger.debug "config_changed?(#{name.inspect})"
+ name = name.to_s
+ !(self.cache_files[name] === get_config_files(name))
+ end
+
+
+ ##
+ # Get the merged config hash for the named file.
+ # Returns a cached indifferent access faker hash merged
+ # from all config files for a name.
+ #
+ def get_config_data(name)
+ logger.debug "get_config_data(#{name.inspect})"
+
+ name = name.to_s
+ unless result = self.cache_hash[name]
+ result = self.cache_hash[name] =
+ make_indifferent(
+ merge_hashes(
+ load_config_files(name)
+ )
+ )
+ logger.debug "get_config_data(#{name.inspect}): reloaded"
+ end
+
+ result
+ end
+
+ ##
+ # If name is specified, checks that file for changes and
+ # reloads it if there are. Otherwise, checks all files
+ # in the cache, reloading the changed files.
+ def check_for_changes(name=nil)
+ changed = []
+ if name == nil
+ self.cache_hash.keys.dup.each do |name|
+ if reload_on_change(name)
+ changed << name
+ end
+ end
+ else
+ name = name.to_s
+ if reload_on_change(name)
+ changed << name
+ end
+ end
+ logger.debug "check_for_changes(#{name.inspect}) => #{changed.inspect}"
+ changed
+ end
+
+ ##
+ # If config files have changed, caches are flushed, on_load triggers are run.
+ def reload_on_change(name)
+ logger.debug "reload_on_change(#{name.inspect}), reload_disabled=#{self.reload_disabled?}"
+ if changed = config_changed?(name) && reload?
+ if self.cache_hash[name]
+ flush_cache(name) # flush cached config values.
+ fire_on_load(name) # force on_load triggers.
+ end
+ end
+ changed
+ end
+
+
+ ##
+ # This method provides shorthand to retrieve configuration data that
+ # is global in scope, and used on an application or environment-wide
+ # level. The default location that it checks is the application file.
+ # The application config file is a special config file that should be
+ # used for config data that is broad in scope and used throughout the
+ # application. Since RConfig gives special regard to the application
+ # config file, thought should be given to whatever config information
+ # is placed there.
+ #
+ # Most config data will be specific to particular part of the
+ # application (i.e. database, web service), and should therefore
+ # be placed in its own specific config file, such as database.yml,
+ # or services.xml
+ #
+ # This method also acts as a wrapper for ENV. If no value is
+ # returned from the application config, it will also check
+ # ENV for a value matching the specified key.
+ #
+ # Ex.1 RConfig[:test_mode] =>
+ # RConfig.application[:test_mode] ||
+ # RConfig.application.test_mode
+ #
+ # Ex.2 RConfig[:web_app_root] => ENV['WEB_APP_ROOT']
+ #
+ # NOTE: The application config file can be in any of
+ # the supported formats (yml, xml, conf, etc.)
+ #
+ def [](key, file=:application)
+ self.config_for(file)[key] || ENV[key.to_s.upcase]
+ end
+
+ ##
+ # Get the value specified by the args, in the file specified by th name
+ #
+ def with_file(name, *args)
+ logger.debug "with_file(#{name.inspect}, #{args.inspect})"
+ result = args.inject(config_for(name)) { |v, i|
+ logger.debug "v = #{v.inspect}, i = #{i.inspect}"
+ case v
+ when Hash
+ v[i.to_s]
+ when Array
+ i.is_a?(Integer) ? v[i] : nil
+ else
+ nil
+ end
+ }
+ logger.debug "with_file(#{name.inspect}, #{args.inspect}) => #{result.inspect}"
+ result
+ end
+
+ ##
+ # Get a hash of merged config data.
+ # Will auto check every 5 minutes, for longer running apps, unless reload is disabled.
+ #
+ def config_for(name)
+ name = name.to_s
+ check_for_changes(name) if auto_check?(name)
+ data = get_config_data(name)
+ logger.debug "config_for(#{name.inspect}) => #{data.inspect}"
+ data
+ end
+
+ ##
+ # Short-hand access to config file by its name.
+ #
+ # Example:
+ #
+ # RConfig.provider(:foo) => RConfig.with_file(:provider).foo
+ # RConfig.provider.foo => RConfig.with_file(:provider).foo
+ #
+ def method_missing(method, * args)
+ value = with_file(method, * args)
+ logger.debug "#{self}.method_missing(#{method.inspect}, #{args.inspect}) => #{value.inspect}"
+ value
+ end
+
+ end
+end
+
View
26 lib/rconfig/exceptions.rb 100755 → 100644
@@ -3,12 +3,24 @@
#
# RConfig Exceptions
#
-#--
-# General error in config initialization or operation.
-class ConfigError < StandardError;
-end
+module RConfig
+ # General error in config initialization or operation.
+ class ConfigError < StandardError; end
-# Config path(s) are not set, don't exist, or Invalid in some manner
-class InvalidConfigPathError < ConfigError;
-end
+ # Load path(s) are not set, don't exist, or Invalid in some manner
+ class InvalidLoadPathError < ConfigError; end
+
+ module Exceptions
+ # Raised when no valid load paths are available.
+ def raise_load_path_error
+ raise InvalidLoadPathError, "No load paths were provided, and none of the default paths were found."
+ end
+
+ # Raised if logging is enabled, but no logger is specified}.
+ def raise_logger_error
+ raise ConfigError, "No logger was specified, and a defualt logger was not found. Set logger to `false` to disable logging."
+ end
+
+ end
+end
View
55 lib/rconfig/load_paths.rb
@@ -0,0 +1,55 @@
+module RConfig
+ module LoadPaths
+ include Constants
+
+ ##
+ # Sets the list of directories to search for
+ # configuration files.
+ # The argument must be an array of strings representing
+ # the paths to the directories, or a string representing
+ # either a single path or a list of paths separated by
+ # either a colon (:) or a semi-colon (;).
+ def set_load_paths(paths)
+ self.load_paths = parse_load_paths(paths)
+ reload(true) # Load Paths have changed so force a reload
+ end
+
+ ##
+ # Adds the specified path to the list of directories to search for
+ # configuration files.
+ # It only allows one path to be entered at a time.
+ def add_load_path(path)
+ if path = parse_load_paths(path).first # only accept first one.
+ self.load_paths << path
+ return reload(true) # Load Paths have changed so force a reload
+ end
+ false
+ end
+
+ ##
+ # If the paths are made up of a delimited string, then parse out the
+ # individual paths. Verify that each path is valid.
+ def parse_load_paths(paths)
+ return [] unless paths
+ if paths.is_a? String
+ path_sep = (paths =~ /;/) ? ';' : ':'
+ paths = paths.split(/#{path_sep}+/)
+ end
+ raise ArgumentError, "Path(s) must be a String or an Array [#{paths.inspect}]" unless paths.is_a? Array
+ raise ArgumentError, "Must provide at least one load path: [#{paths.inspect}]" if paths.empty?
+ paths.each do |dir|
+ dir = CONFIG_ROOT if dir == 'CONFIG_ROOT'
+ raise RConfig::InvalidConfigPathError, "This directory is invalid: [#{dir.inspect}]" unless Dir.exists?(dir)
+ end
+ paths
+ end
+
+ ##
+ # Indicates whether or not config_paths have been set.
+ # Returns true if self.load_paths has at least one directory.
+ def load_paths_set?
+ not load_paths.blank?
+ end
+
+ end
+end
View
95 lib/rconfig/logger.rb 100755 → 100644
@@ -1,47 +1,86 @@
-require 'logger'
+module RConfig
+ class Logger #:nodoc:
+ attr_accessor :level, :log_format, :date_format
+ attr_reader :output
-##
-# Creates a logger for small apps or gems that may not be using log4r.
-# I created this to encapsulate all of the logging code in my smaller
-# projects so thay I don't clutter them up with utility code.
-#
-module DefaultLogger
+ FATAL = 4
+ ERROR = 3
+ WARN = 2
+ INFO = 1
+ DEBUG = 0
+
+ MAX_LEVEL = 4
- module ClassMethods
+ def initialize(options={})
+ # Use provided output
+ if output = options[:output] && output.respond_to?(:puts)
+ @output = output
+ @needs_close = false
+ end
- def create_logger options={}
- class_inheritable_accessor :default_logger, :options
+ # Use log file
+ if output.nil? && options[:file] && File.exists?(optios[:file])
+ @output = File.open(options[:file].to_s, 'a')
+ @needs_close = true
+ end
- self.options = options || {}
+ # Default to stderr, if no outputter or file provider
+ @output ||= STDERR
- logger = Logger.new(check_options(:file, STDOUT))
- logger.level = check_options(:level, ENV['LOG_LEVEL'] || Logger::INFO)
- logger.datetime_format = check_options(:format, "%Y-%m-%d %H:%M:%S")
- logger.progname = check_options(:app_name, 'RConfig')
+ # Use provided level or default to warn
+ @level = options[:level] ||
+ ((defined?(Rails) && %w[test development].include?(Rails.env)) ? DEBUG : WARN)
- self.default_logger = logger
+ # Date format
+ @date_format = options[:date_format] || '%Y-%m-%d %H:%M:%S'
+ #@log_format = options[:log_format] || "[%l] %d :: %M :: %t"
+ end
- include DefaultLogger::InstanceMethods
+ def close
+ output.close if @needs_close
end
- def logger
- create_logger if self.default_logger.nil?
- self.default_logger
+ def log(level, message)
+ if self.level <= level
+ indent = "%*s" % [MAX_LEVEL, "*" * (MAX_LEVEL - level)]
+ message.lines.each do |line|
+ log_str = "[#{indent}] #{Time.now.strftime(self.date_format)} :: #{line.strip}\n"
+ output.puts log_str
+ end
+ end
end
- def check_options key, default_value=nil
- puts "Key: #{key.inspect}, Value: #{self.options[key]}, Default: #{default_value}"
- self.options[key].nil? ? default_value : self.options[key]
+ def fatal(message)
+ log(FATAL, message)
end
- end
+ def error(message)
+ log(ERROR, message)
+ end
+
+ def warn(message)
+ log(WARN, message)
+ end
- module InstanceMethods
- def logger
- self.class.logger
+ def info(message)
+ log(INFO, message)
end
+
+ def debug(message)
+ log(DEBUG, message)
+ end
+
end
+ class DisabledLogger
+ def log(level, message) end
+ def dont_log(message) end
+
+ alias_method :fatal, :dont_log
+ alias_method :error, :dont_log
+ alias_method :warn, :dont_log
+ alias_method :info, :dont_log
+ alias_method :debug, :dont_log
+ end
end
-Object.class_eval { extend DefaultLogger::ClassMethods }
View
10 lib/rconfig/mixins.rb
@@ -1,10 +0,0 @@
-##
-# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
-#
-require 'rconfig/mixins/callbacks'
-require 'rconfig/mixins/class_variables'
-require 'rconfig/mixins/config_paths'
-require 'rconfig/mixins/constants'
-require 'rconfig/mixins/reload'
-require 'rconfig/mixins/overlay'
-require 'rconfig/mixins/utils'
View
71 lib/rconfig/mixins/class_variables.rb
@@ -1,71 +0,0 @@
-# These are all the variables used to store RConfigs state.
-module Mixins
- module ClassVariables
-
- # The list of directories to search for configuration files.
- @@config_paths = []
-
- # Hash of suffixes for a given config name.
- # @@suffixes['name'] vs @@suffix['name_GB']
- @@suffixes = {}
-
- @@overlay = ENV['CONFIG_OVERLAY']
-
- # Hash of yaml file names and their respective contents,
- # last modified time, and the last time it was loaded.
- # @@cache[filename] = [yaml_contents, mod_time, time_loaded]
- @@cache = {}
-
- # Hash of config file base names and their existing filenames
- # including the suffixes.
- # @@cache_files['ldap'] = ['ldap.yml', 'ldap_local.yml', 'ldap_<hostname>.yml']
- @@cache_files = {}
-
- # Hash of config base name and the contents of all its respective
- # files merged into hashes. This hash holds the data that is
- # accessed when RConfig is called. This gets re-created each time
- # the config files are loaded.
- # @@cache_hash['ldap'] = config_hash
- @@cache_hash = {}
-
- # The hash holds the same info as @@cache_hash, but it is only
- # loaded once. If reload is disabled, data will from this hash
- # will always be passed back when RConfig is called.
- @@cache_config_files = {} # Keep around incase reload_disabled.
-
- # Hash of config base name and the last time it was checked for
- # update.
- # @@last_auto_check['ldap'] = Time.now
- @@last_auto_check = {}
-
- # Hash of callbacks Procs for when a particular config file has changed.
- @@on_load = {}
-
- # The number of seconds between reloading of config files
- # and automatic reload checks. Defaults to 5 minutes.
- @@reload_interval = 300
-
- # Flag variable indicating whether or not reload should be executed.
- # Defaults to false.
- @@reload_disabled = false
-
- # Helper variable for white-box testing and debugging.
- # A hash of each file that has been loaded.
- @@config_file_loaded = nil
-
-
- # Magically unique value. Used to provide a key to retrieve a default value
- # specified in a config file. The symbol is wrapped in an array so that it will
- # not be treated like a normal key and changed to a string.
- #
- # Example:
- # currency:
- # us: dollar
- # gb: pound
- # default: dollar
- #
- # RConfig.currency.ca => 'dollar'
- @@no_key = [:no_key]
-
- end
-end
View
119 lib/rconfig/mixins/config_paths.rb
@@ -1,119 +0,0 @@
-module Mixins
- module ConfigPaths
-
- ##
- # Sets the list of directories to search for
- # configuration files.
- # The argument must be an array of strings representing
- # the paths to the directories, or a string representing
- # either a single path or a list of paths separated by
- # either a colon (:) or a semi-colon (;).
- # If reload is disabled, it can only be set once.
- def self.config_paths=(paths)
- return if @@reload_disabled && config_paths_set?
- if paths.is_a? String
- path_sep = (paths =~ /;/) ? ';' : ':'
- paths = paths.split(/#{path_sep}+/)
- end
- unless paths.is_a? Array
- raise ArgumentError,
- "Path(s) must be a String or an Array [#{paths.inspect}]"
- end
- if paths.empty?
- raise ArgumentError,
- "Must provide at least one paths: [#{paths.inspect}]"
- end
- paths.all? do |dir|
- dir = CONFIG_ROOT if dir == 'CONFIG_ROOT'
- unless File.directory?(dir)
- raise InvalidConfigPathError,
- "This directory is invalid: [#{dir.inspect}]"
- end
- end
- reload
- @@config_paths = paths
- end
-
- class << self;
- alias_method :set_config_paths, :config_paths=
- end
-
- ##
- # Adds the specified path to the list of directories to search for
- # configuration files.
- # It only allows one path to be entered at a time.
- # If reload is disabled, it can onle be set once.
- def self.set_config_path path
- return if @@reload_disabled && config_paths_set?
- return unless path.is_a?(String) # only allow string argument
- path_sep = (path =~ /;/) ? ';' : ':' # if string contains multiple paths
- path = path.split(/#{path_sep}+/)[0] # only accept first one.
-
- if @@config_paths.blank?
- set_config_paths(path)
- else
- config_paths << path if File.directory?(path)
- reload
- @@config_paths
- end
- end
-
- class << self;
- alias_method :add_config_path, :set_config_path
- end
-
- ##
- # Returns a list of directories to search for
- # configuration files.
- #
- # Can be preset with config_paths=/set_config_path,
- # controlled via ENV['CONFIG_PATH'],
- # or defaulted to CONFIG_ROOT (assumming some sort of
- # application initiation as in RAILS).
- # Defaults to [ CONFIG_ROOT ].
- #
- # Examples:
- # export CONFIG_PATH="$HOME/work/config:CONFIG_ROOT"
- # CONFIG_ROOT = RAILS_ROOT + "/config" unless defined? CONFIG_ROOT
- #
- def self.config_paths
- return @@config_paths unless @@config_paths.blank?
-
- begin
- config_paths = ENV['CONFIG_PATH']
- rescue
- logger.error {
- "Forget something? No config paths! ENV['CONFIG_PATH'] is not set.\n" +
- "Hint: Use config_paths= or set_config_path."
- }
- end
-
- begin
- config_paths = [CONFIG_ROOT]
- rescue
- logger.error {
- "Forget something? No config paths! CONFIG_ROOT is not set.\n" +
- "Hint: Use config_paths= or set_config_path."
- }
- end
-
- if @@config_paths.blank?
- raise InvalidConfigPathError,
- "Forget something? No config paths!\n" +
- "Niether ENV['CONFIG_PATH'] or CONFIG_ROOT is set.\n" +
- "Hint: Use config_paths= or set_config_path."
- end
-
- @@config_paths
- end
-
-
- ##
- # Indicates whether or not config_paths have been set.
- # Returns true if @@config_paths has at least one directory.
- def self.config_paths_set?
- !@@config_paths.blank?
- end
-
- end
-end
View
54 lib/rconfig/mixins/constants.rb
@@ -1,54 +0,0 @@
-module Mixins
- module Constants
-
- # Sets CONFIG_ROOT to RAILS_ROOT/config unless it has already
- # been defined (i.e. in rails env, or calling ruby app).
- CONFIG_ROOT = RAILS_ROOT + "/config" if defined?(RAILS_ROOT) && defined?(CONFIG_ROOT)
-
- # ENV TIER i.e. (development, integration, staging, or production)
- # Defaults to RAILS_ENV if running in Rails, otherwise, it checks
- # if ENV['TIER'] is present. If not, it assumes production.
- ENV_TIER = (defined?(RAILS_ENV) ? RAILS_ENV : (ENV['TIER'] || 'production')) unless defined? ENV_TIER
-
- # yml, yaml => yaml files, parsable by YAML library
- YML_FILE_TYPES = [:yml, :yaml] unless defined? YML_FILE_TYPES
-
- # xml => self-explanatory
- XML_FILE_TYPES = [:xml] unless defined? XML_FILE_TYPES
-
- # conf, properties => <key=value> based config files
- CNF_FILE_TYPES = [:cnf, :conf, :config, :properties] unless defined? CNF_FILE_TYPES
-
- # The type of file used for config. Valid choices
- # include (yml, yaml, xml, conf, config, properties)
- CONFIG_FILE_TYPES = YML_FILE_TYPES + XML_FILE_TYPES + CNF_FILE_TYPES unless defined? CONFIG_FILE_TYPES
-
- # Use CONFIG_HOSTNAME environment variable to
- # test host-based configurations.
- HOSTNAME = ENV['CONFIG_HOSTNAME'] || Socket.gethostname unless defined? HOSTNAME
-
- # Short Hostname: removes all chars from HOSTNAME, after first "."
- # Used to specify machine-specific config files.
- HOSTNAME_SHORT = HOSTNAME.sub(/\..*$/, '').freeze unless defined? HOSTNAME_SHORT
-
- # This is an array of filename suffixes facilitates overriding
- # configuration (i.e. 'services_local', 'services_development').
- # These files get loaded in order of the array the last file
- # loaded gets splatted on top of everything there.
- # Ex. database_whiskey.yml overrides database_integration.yml
- # overrides database.yml
- SUFFIXES = [nil,
- :local,
- :config, :local_config,
- ENV_TIER, [ENV_TIER, :local],
- HOSTNAME_SHORT, [HOSTNAME_SHORT, :config_local],
- HOSTNAME, [HOSTNAME, :config_local]
- ] unless defined? SUFFIXES
-
- # Used in place of undefined but expected arrays,
- # to prevent creating a bunch of unecesary arrays
- # in memory. See ConfigCore.fire_on_load
- EMPTY_ARRAY = [].freeze unless defined? EMPTY_ARRAY
-
- end
-end
View
67 lib/rconfig/mixins/overlay.rb
@@ -1,67 +0,0 @@
-module Mixins
- module Overlay
-
- # Specifies an additional overlay suffix.
- #
- # E.g. 'gb' for UK locale.
- #
- # Defaults from ENV['CONFIG_OVERLAY'].
- def self.overlay
- @@overlay ||= (value = ENV['CONFIG_OVERLAY']) && value.dup.freeze
- end
-
-
- ##
- # Sets overlay for
- def self.overlay=(value)
- flush_cache if @@overlay != value
- @@overlay = value && value.dup.freeze
- end
-
-
- ##
- # Returns a list of suffixes to try for a given config name.
- #
- # A config name with an explicit overlay (e.g.: 'name_GB')
- # overrides any current _overlay.
- #
- # This allows code to specifically ask for config overlays
- # for a particular locale.
- #
- def self.suffixes(name)
- name = name.to_s
- @@suffixes[name] ||=
- begin
- ol = overlay
- name_x = name.dup
- if name_x.sub!(/_([A-Z]+)$/, '')
- ol = $1
- end
- name_x.freeze
- result = if ol
- ol_ = ol.upcase
- ol = ol.downcase
- x = []
- SUFFIXES.each do |suffix|
- # Standard, no overlay:
- # e.g.: database_<suffix>.yml
- x << suffix
-
- # Overlay:
- # e.g.: database_(US|GB)_<suffix>.yml
- x << [ol_, suffix]
- end
- [name_x, x.freeze]
- else
- [name.dup.freeze, SUFFIXES.freeze]
- end
- result.freeze
-
- logger.debug { "suffixes(#{name}) => #{result.inspect}" }
-
- result
- end
- end
-
- end
-end
View
72 lib/rconfig/mixins/reload.rb
@@ -1,72 +0,0 @@
-module Mixins
- module Reload
-
- ##
- # Flag indicating whether or not reload should be executed.
- def self.reload?
- !@@reload_disabled
- end
-
- ##
- # Sets the number of seconds between reloading of config files
- # and automatic reload checks. Defaults to 5 minutes.
- def self.reload_interval=(interval)
- raise ArgumentError, 'Argument must be Integer.' unless interval.kind_of?(Integer)
- @@reload_interval = (interval || 300)
- end
-
-
- ##
- # Flushes cached config data, so that it can be reloaded from disk.
- # It is recommended that this should be used with caution, and any
- # need to reload in a production setting should minimized or
- # completely avoided if possible.
- def self.reload(force=false)
- raise ArgumentError, 'Argument must be true or false.' unless [true, false].include?(force)
- if force || reload?
- flush_cache
- end
- nil
- end
-
- ##
- # Sets the flag indicating whether or not reload should be executed.
- def self.allow_reload=(reload)
- raise ArgumentError, 'Argument must be true or false.' unless [true, false].include?(reload)
- @@reload_disabled = (not reload)
- end
-
-
- ##
- # Disables any reloading of config,
- # executes &block,
- # calls check_config_changed,
- # returns result of block
- def self.disable_reload(&block)
- # This should increment @@reload_disabled on entry, decrement on exit.
- result = nil
- reload_disabled_save = @@reload_disabled
- begin
- @@reload_disabled = true
- result = yield
- ensure
- @@reload_disabled = reload_disabled_save
- check_config_changed unless @@reload_disabled
- end
- result
- end
-
- protected
-
- def auto_check?(name)
- now = Time.now
- if (!@@last_auto_check[name]) || (now - @@last_auto_check[name]) > @@reload_interval
- @@last_auto_check[name] = now
- return true
- end
- return false
- end
-
-
- end
-end
View
120 lib/rconfig/mixins/utils.rb
@@ -1,120 +0,0 @@
-module Mixins
- module Utils
-
- ##
- # Helper method for white-box testing and debugging.
- # Sets the flag indicating whether or not to log
- # errors and application run-time information.
- def self.log_level=(level)
- logger.level = level unless level.nil?
- end
-
- def self.log_level
- logger.level
- end
-
- ##
- # Helper method for white-box testing and debugging.
- # Sets a hash of each file that has been loaded.
- def self.config_file_loaded=(loaded)
- @@config_file_loaded = loaded
- end
-
-
- ##
- # Helper method for white-box testing and debugging.
- # Returns a hash of each file that has been loaded.
- def self.config_file_loaded
- @@config_file_loaded
- end
-
- ##
- # Creates a dottable hash for all Hash objects, recursively.
- def self.create_dottable_hash(hash)
- make_indifferent(hash)
- end
-
- protected
-
- ##
- # Parses file based on file type.
- #
- def self.parse_file(conf_file, ext)
- hash = case ext
- when * YML_FILE_TYPES
- YAML::load(conf_file)
- when * XML_FILE_TYPES
- Hash.from_xml(conf_file)
- when * CNF_FILE_TYPES
- PropertiesFileParser.parse(conf_file)
- else
- raise ConfigError, "Unknown File type:#{ext}"
- end
- hash.freeze
- end
-
- ##
- # Returns a merge of hashes.
- #
- def self.merge_hashes(hashes)
- hashes.inject({}) { |n, h| n.weave(h, true) }
- end
-
-
- ##
- # Recursively makes hashes into frozen IndifferentAccess ConfigFakerHash
- # Arrays are also traversed and frozen.
- #
- def self.make_indifferent(hash)
- case hash
- when Hash
- unless hash.frozen?
- hash.each_pair do |k, v|
- hash[k] = make_indifferent(v)
- end
- hash = ConfigHash.new.merge!(hash).freeze
- end
- logger.debug "make_indefferent: x = #{hash.inspect}:#{hash.class}"
- when Array
- unless hash.frozen?
- hash.collect! do |v|
- make_indifferent(v)
- end
- hash.freeze
- end
- # Freeze Strings.
- when String
- hash.freeze
- end
- hash
- end
-
-
- ##
- # If a config file name is specified, flushes cached config values
- # for specified config file. Otherwise, flushes all cached config data.
- # The latter should be avoided in production environments, if possible.
- def self.flush_cache(name=nil)
- if name
- name = name.to_s
- @@cache_hash[name] = nil if @@cache_hash[name]
- else
- logger.warn "Flushing complete config data cache."
- @@suffixes = {}
- @@cache = {}
- @@cache_files = {}
- @@cache_hash = {}
- @@last_auto_check = {}
- self
- end
- end
-
- ##
- # Get complete file name, including file path for the given config name
- # and directory.
- def self.filename_for_name(name, dir=config_paths[0], ext=:yml)
- File.join(dir, "#{name}.#{ext}")
- end
-
- end
-end
View
42 lib/rconfig/options.rb
@@ -1,42 +0,0 @@
-module RConfig
- class Options
- class << self
- attr_accessor :config_paths
- attr_accessor :suffixes
- attr_accessor :overlay
- attr_accessor :cache
- attr_accessor :cache_files
- attr_accessor :cache_hash
- attr_accessor :cache_config_files
- attr_accessor :last_auto_check
- attr_accessor :on_load
- attr_accessor :reload_cache
- attr_accessor :reload_interval
- attr_accessor :config_file_loaded
- attr_accessor :default_key
-
- attr_accessor :options
-
- def setup
- yield self
- end
- end
-
- self.config_paths = []
- self.suffixes = {}
- self.overlay = false
- self.cache = {}
- self.cache_files = {}
- self.cache_hash = {}
- self.cache_config = {}
- self.last_auto_check = nil
- self.on_load = {}
- self.reload_cache = false
- self.reload_interval = 300
- self.config_file_loaded = nil
- self.default_key = [:default_key]
-
- self.options = {}
-
- end
-end
View
102 lib/rconfig/properties_file_parser.rb 100755 → 100644
@@ -32,66 +32,68 @@
# - or -
# RConfig.props.host[env].domain (where env is 'dev' or 'prod')
#
-class PropertiesFileParser
- include Singleton # Don't instantiate this class
+module RConfig
+ class PropertiesFileParser
+ include Singleton # Don't instantiate this class
- COMMENT = /^\#/
- KEYVAL = /\s*=\s*/
- QUOTES = /^['"](.*)['"]$/
- GROUP = /^\[(.+)\]$/
- NAMEGRP = /^\[(.+) \"(.+)\"\]$/
+ COMMENT = /^\#/
+ KEYVAL = /\s*=\s*/
+ QUOTES = /^['"](.*)['"]$/
+ GROUP = /^\[(.+)\]$/
+ NAMEGRP = /^\[(.+) \"(.+)\"\]$/
- ##
- # Parse config file and import data into hash to be stored in config.
- #
- def self.parse(config_file)
- raise ArgumentError, 'Invalid config file name.' unless config_file
+ ##
+ # Parse config file and import data into hash to be stored in config.
+ #
+ def self.parse(config_file)
+ raise ArgumentError, 'Invalid config file name.' unless config_file
- config = {}
+ config = {}
- # The config is top down.. anything after a [group] gets added as part
- # of that group until a new [group] is found.
- group, topgrp = nil
- config_file.each do |line| # for each line in the config file
- line.strip!
- unless (COMMENT.match(line)) # skip comments (lines that state with '#')
- if (KEYVAL.match(line)) # if this line is a config property
- key, val = line.split(KEYVAL, 2) # parse key and value from line
- key = key.chomp.strip
- val = val.chomp.strip
- if (val)
- if val =~ QUOTES # if the value is in quotes
- value = $1 # strip out value from quotes
+ # The config is top down.. anything after a [group] gets added as part
+ # of that group until a new [group] is found.
+ group, topgrp = nil
+ config_file.each do |line| # for each line in the config file
+ line.strip!
+ unless (COMMENT.match(line)) # skip comments (lines that state with '#')
+ if (KEYVAL.match(line)) # if this line is a config property
+ key, val = line.split(KEYVAL, 2) # parse key and value from line
+ key = key.chomp.strip
+ val = val.chomp.strip
+ if (val)
+ if val =~ QUOTES # if the value is in quotes
+ value = $1 # strip out value from quotes
+ else
+ value = val # otherwise, leave as-is
+ end
else
- value = val # otherwise, leave as-is
+ value = '' # if value was nil, set it to empty string
end
- else
- value = '' # if value was nil, set it to empty string
- end
- if topgrp # If there was a top-level named group
- config[topgrp][group][key] = # then there must be a group.
- value # add the prop to the named group
- elsif group # if this property is part of a group
- config[group][key] = value # then add it to the group
- else
- config[key] = value # otherwise, add it to top-level config
- end
+ if topgrp # If there was a top-level named group
+ config[topgrp][group][key] = # then there must be a group.
+ value # add the prop to the named group
+ elsif group # if this property is part of a group
+ config[group][key] = value # then add it to the group
+ else
+ config[key] = value # otherwise, add it to top-level config
+ end
- elsif match = NAMEGRP.match(line) # This line is a named group (i.e. [env "test"], [env "qa"], [env "production"])
- topgrp, group = match.to_a[1..-1] # There can be multiple groups within a single top-level group
- config[topgrp] ||= {} # add group to top-level group
- config[topgrp][group] ||= {} # add name of group as subgroup (properties are added to subgroup)
+ elsif match = NAMEGRP.match(line) # This line is a named group (i.e. [env "test"], [env "qa"], [env "production"])
+ topgrp, group = match.to_a[1..-1] # There can be multiple groups within a single top-level group
+ config[topgrp] ||= {} # add group to top-level group
+ config[topgrp][group] ||= {} # add name of group as subgroup (properties are added to subgroup)
- elsif match = GROUP.match(line) # if this line is a config group
- group = match.to_a[1] # parse the group name from line
- topgrp = nil # we got a new group with no namespace, so reset topgrp
- config[group] ||= {} # add group to top-level config
+ elsif match = GROUP.match(line) # if this line is a config group
+ group = match.to_a[1] # parse the group name from line
+ topgrp = nil # we got a new group with no namespace, so reset topgrp
+ config[group] ||= {} # add group to top-level config
+ end
end
end
- end
- config # return config hash
- end # def parse
+ config # return config hash
+ end # def parse
-end # class PropertiesFileParser
+ end # class PropertiesFileParser
+end # module RConfig
View
398 lib/rconfig/rconfig.rb
@@ -1,398 +0,0 @@
-##
-#
-# Copyright (c) 2009 Rahmal Conda <rahmal@gmail.com>
-# -------------------------------------------------------------------
-# The complete solution for Ruby Configuration Management. RConfig is a Ruby library that
-# manages configuration within Ruby applications. It bridges the gap between yaml, xml, and
-# key/value based properties files, by providing a centralized solution to handle application
-# configuration from one location. It provides the simplicity of hash-based access, that
-# Rubyists have come to know and love, supporting your configuration style of choice, while
-# providing many new features, and an elegant API.
-#
-# -------------------------------------------------------------------
-# * Simple, easy to install and use.
-# * Supports yaml, xml, and properties files.
-# * Yaml and xml files supprt infinite level of configuration grouping.
-# * Intuitive dot-notation 'key chaining' argument access.
-# * Simple well-known hash/array based argument access.
-# * Implements multilevel caching to reduce disk access.
-# * Short-hand access to 'global' application configuration, and shell environment.
-# * Overlays multiple configuration files to support environment, host, and
-# even locale-specific configuration.
-#
-# -------------------------------------------------------------------
-# The overlay order of the config files is defined by SUFFIXES:
-# * nil
-# * _local
-# * _config
-# * _local_config
-# * _{environment} (.i.e _development)
-# * _{environment}_local (.i.e _development_local)
-# * _{hostname} (.i.e _whiskey)
-# * _{hostname}_config_local (.i.e _whiskey_config_local)
-#
-# -------------------------------------------------------------------
-#
-# Example:
-#
-# shell/console =>
-# export LANG=en
-#
-# demo.yml =>
-# server:
-# address: host.domain.com
-# port: 81
-# ...
-#
-# application.properties =>
-# debug_level = verbose
-# ...
-#
-# demo.rb =>
-# require 'rconfig'
-# RConfig.config_paths = ['$HOME/config', '#{APP_ROOT}/config', '/demo/conf']
-# RConfig.demo[:server][:port] => 81
-# RConfig.demo.server.address => 'host.domain.com'
-#
-# RConfig[:debug_level] => 'verbose'
-# RConfig[:lang] => 'en'
-# ...
-#
-class RConfig
- include Singleton,
- Mixins::Constants, Mixins::ClassVariables,
- Mixins::ConfigPaths, Mixins::Overlay,
- Mixins::Reload, Mixins::Callbacks, Mixins::Utils
-
- create_logger
-
- ##
- # Convenience method to initialize necessary fields including,
- # config path(s), overlay, allow_reload, and log_level, all at
- # one time.
- # Examples:
- # RConfig.initialize(:config, 'en_US', true, :warn)
- # - or -
- # RConfig.initialize(:paths => ['config', 'var/config'], :reload => false)
- def self.initialize(*args)
- logger.info { "Initialing RConfig" }
- case args[0]