Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added reference support to properties file. cleaned up core methods.

  • Loading branch information...
commit c2cd516ae0f5c0f58ebae81f104e6323e055b300 1 parent 9ebefbc
Rahmal Conda authored
1  ChangeLog
View
@@ -3,6 +3,7 @@
* 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.
+* Added support for values referencing other properties in key-value config files
* Refactored settings module to allow for clean defaults, and overriding.
* Added RConfig logger.
* Renamed overlay to cascade
2  lib/generators/rconfig/install_generator.rb
View
@@ -4,7 +4,7 @@ class InstallGenerator < Rails::Generators::Base
desc "Create RConfig settings initializer file"
source_root File.expand_path("../templates", __FILE__)
- def create_initializer_file
+ def copy_initializer
template "rconfig.rb", "config/initializers/rconfig.rb"
end
end
7 lib/rconfig.rb
View
@@ -50,7 +50,7 @@
#
# demo.rb =>
# require 'rconfig'
-# RConfig.config_paths = ['$HOME/config', '#{APP_ROOT}/config', '/demo/conf']
+# RConfig.load_paths = ['$HOME/config', '#{APP_ROOT}/config', '/demo/conf']
# RConfig.demo[:server][:port] => 81
# RConfig.demo.server.address => 'host.domain.com'
#
@@ -68,6 +68,7 @@ module RConfig
autoload :Socket, 'socket'
autoload :YAML, 'yaml'
+ autoload :ERB, 'erb'
autoload :Logger, 'logger'
autoload :Singleton, 'singleton'
@@ -75,10 +76,6 @@ module RConfig
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'
-
autoload :Config, 'rconfig/config'
autoload :Logger, 'rconfig/logger'
autoload :Exceptions, 'rconfig/exceptions'
2  lib/rconfig/constants.rb
View
@@ -3,7 +3,7 @@ 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)
+ CONFIG_ROOT = File.join(::Rails.root || '', 'config').gsub(/^\//, '').gsub(/\/$/,'') 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
94 lib/rconfig/core_methods.rb
View
@@ -27,83 +27,83 @@ def load_config_files(name, force=false)
# 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 all the data from all yaml files into as configs
+ configs = config_files.collect do |f|
+ name, name_with_suffix, filename, ext, modified_time = * f
# Get the cached file info the specific file, if
# it's been loaded before.
- val, last_mtime, last_loaded = self.cache[filename]
+ config_data, last_modified, 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}"
+ logger.debug "f = #{f.inspect}\n" +
+ "cache #{name_with_suffix} filename = #{filename.inspect}\n" +
+ "cache #{name_with_suffix} config_data = #{config_data.inspect}\n" +
+ "cache #{name_with_suffix} last_modified = #{last_modified.inspect}\n" +
+ "cache #{name_with_suffix} last_loaded = #{last_loaded.inspect}\n"
# 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
+ if config_data.blank? || (now - last_loaded > self.reload_interval)
+ if force || config_data.blank? || modified_time != last_modified
+
+ logger.debug "modified_time #{name.inspect} #{filename.inspect} " +
+ "changed #{modified_time != last_modified} : #{modified_time.inspect} #{last_modified.inspect}"
+
+ logger.debug "RConfig: loading #{filename.inspect}"
+
+ config_data = read(filename, name, ext) # Get contents from config file
+
+ logger.debug "RConfig: loaded #{filename.inspect} => #{config_data.inspect}"
+
+ (self.config_loaded ||= {})[name] = config_files # add files to the loaded files cache
+
+ self.cache[filename] = [config_data, modified_time, now] # Save cached config file contents, and modified_time.
- # 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
+ self.cache_hash[name] = nil # Flush merged hash cache.
- # Config files changed or disappeared.
- self.cache_files[name] = config_files
+ self.cache_files[name] = config_files # Config files changed or disappeared.
- end # if val == nil || (now - last_loaded > self.reload_interval)
- end # if force || val == nil || mtime != last_mtime
+ end # if config_data == nil || (now - last_loaded > self.reload_interval)
+ end # if force || config_data == nil || modified_time != last_modified
- val
- end
- hashes.compact!
+ config_data
+ end # config_files.collect
+ configs.compact!
- logger.debug "load_config_files(#{name.inspect}) => #{hashes.inspect}"
+ logger.debug "load_config_files(#{name.inspect}) => #{configs.inspect}"
# Keep last loaded config files around in case self.reload_dsabled.
- self.cache_config_files[name] = hashes #unless hashes.empty?
+ self.cache_config_files[name] = configs #unless configs.empty?
- hashes
+ configs
end
##
- # Returns a list of all relevant config files as specified
- # by _suffixes list.
+ # 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.
+ #
+ # [
+ # "server", # The base name of the
+ # "server_production", # The suffixed name
+ # "/path/to/server.yml", # The absolute path to the file
+ # <Wed Apr 09 08:53:14> # The last modified time of the file or nil, if it doesn't exist.
# ]
+ #
def get_config_files(name)
files = []
- self.load_paths.reverse.each do |dir|
+ self.load_paths.reverse.each do |directory|
# 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|
+ suffixes.map { |suffix| [name_no_overlay, *suffix].compact.join('_') }.each do |name_with_suffix|
self.file_types.each do |ext|
- filename = filename_for_name(name_x, dir, ext)
+ filename = filename_for_name(name_with_suffix, directory, ext)
if File.exists?(filename)
- files << [name, name_x, filename, ext, File.stat(filename).mtime]
+ modified_time = File.stat(filename).mtime
+ files << [name, name_with_suffix, filename, ext, modified_time]
end
end
end
69 lib/rconfig/properties_file_parser.rb
View
@@ -41,6 +41,7 @@ class PropertiesFileParser
QUOTES = /^['"](.*)['"]$/
GROUP = /^\[(.+)\]$/
NAMEGRP = /^\[(.+) \"(.+)\"\]$/
+ KEY_REF = /(.+)?\%\{(.+)\}(.+)?/
##
# Parse config file and import data into hash to be stored in config.
@@ -53,30 +54,29 @@ def self.parse(config_file)
# 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
+ 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
+ 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
if val =~ QUOTES # if the value is in quotes
- value = $1 # strip out value from quotes
+ value = $1 # strip out value from quotes
else
- value = val # otherwise, leave as-is
+ value = val # otherwise, leave as-is
end
else
- value = '' # if value was nil, set it to empty string
+ 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
+ if topgrp # If there was a top-level named group, then there must be a group.
+ config[topgrp][group][key] = value # so 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 # otherwise...
+ config[key] = value # add the prop to top-level config
end
elsif match = NAMEGRP.match(line) # This line is a named group (i.e. [env "test"], [env "qa"], [env "production"])
@@ -92,8 +92,47 @@ def self.parse(config_file)
end
end
+ # Pre-populate the values that refer to other properties
+ # Example:
+ # root_path = /path/to/root
+ # image_path = %{root_path}/images
+ config = parse_references(config)
+
config # return config hash
end # def parse
+
+ ##
+ # Recursively checks all the values in the hash for references to other properties,
+ # and replaces the references with the actual values. The syntax for referring to
+ # another property in the config file is the ley of the property wrapped in curly
+ # braces, preceded by a percent sign. If the property is in a group, then the full
+ # namespace must be used.
+ #
+ # Example:
+ # root_path = /path/to/root # In this property file snippet
+ # image_path = %{root_path}/images # image path refers to root path
+ #
+ #
+ def self.parse_references hash, config=nil
+ config ||= hash.dup
+
+ hash.each do |key, val|
+ case val
+ when Hash
+ hash[key] = parse_references(val, config)
+ when String
+ pre, ref, post = KEY_REF.match(val).to_a[1..-1]
+ hash[key] = if ref
+ ref = ref.split('.').inject(config){|c,i| c && c[i] }
+ [pre, ref, post].join
+ else
+ val
+ end
+ end
+ end
+
+ hash
+ end # def parse_references
end # class PropertiesFileParser
-end # module RConfig
+end # module RConfig
3  lib/rconfig/settings.rb
View
@@ -1,6 +1,7 @@
module RConfig
module Settings
- extend Constants
+ extend Utils
+ include Constants
### Configuration Settings ====>
34 lib/rconfig/utils.rb
View
@@ -7,7 +7,7 @@ module Utils
def setup
yield self
raise_load_path_error if load_paths.empty?
- logger ||= DisabledLogger.new
+ self.logger ||= DisabledLogger.new
end
# Creates a class variable a sets it to the default value specified.
@@ -74,18 +74,28 @@ def create_dottable_hash(hash)
end
##
- # Parses file based on file type.
+ # Reads and parses the config data from the specified file.
+ def read(file, name, ext)
+ contents = File.read(file) # Read the contents from the file.
+ contents = ERB.new(contents).result # Evaluate any ruby code using ERB.
+ parse(contents, name, ext) # Parse the contents based on the file type
+ end
+
+ ##
+ # Parses contents of the config file based on file type.
+ # XML files expect the root element to be the same as the
+ # file name.
#
- def parse_file(conf_file, ext)
+ def parse(contents, name, 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
- RConfig::PropertiesFileParser.parse(conf_file)
+ when *YML_FILE_TYPES
+ YAML::load(contents)
+ when *XML_FILE_TYPES
+ Hash.from_xml(contents)[name] # xml document must have root tag matching the file name.
+ when *CNF_FILE_TYPES
+ RConfig::PropertiesFileParser.parse(contents)
else
- raise ConfigError, "Unknown File type:#{ext}"
+ raise ConfigError, "Unknown File type: #{ext}"
end
hash.freeze
end
@@ -98,14 +108,14 @@ def merge_hashes(hashes)
end
##
- # Recursively makes hashes into frozen IndifferentAccess ConfigFakerHash
+ # Recursively makes hashes into frozen IndifferentAccess Config Hash
# Arrays are also traversed and frozen.
#
def make_indifferent(hash)
case hash
when Hash
unless hash.frozen?
- hash.each_pair do |k, v|
+ hash.each do |k, v|
hash[k] = make_indifferent(v)
end
hash = RConfig::Config.new.merge!(hash).freeze
3  rconfig.gemspec
View
@@ -3,11 +3,12 @@
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
+require "rconfig"
Gem::Specification.new do |s|
# Metadata
- s.name = "RConfig"
+ s.name = 'rconfig'
s.version = RConfig::VERSION
s.authors = ['Rahmal Conda']
s.email = ['rahmal@gmail.com']
Please sign in to comment.
Something went wrong with that request. Please try again.