Configurability::
Config
class
A configuration object class for systems with Configurability
Author/s
This class also delegates some of its methods to the underlying struct:
Configurability::Config::Struct#to_hash
|
to_hash (delegated to its internal Struct )
|
Configurability::Config::Struct#member?
|
member? (delegated to its internal Struct )
|
Configurability::Config::Struct#members
|
members (delegated to its internal Struct )
|
Configurability::Config::Struct#merge
|
merge (delegated to its internal Struct )
|
Configurability::Config::Struct#merge!
|
merge! (delegated to its internal Struct )
|
Configurability::Config::Struct#each
|
each (delegated to its internal Struct )
|
Configurability::Config::Struct#[]
|
[] (delegated to its internal Struct )
|
Configurability::Config::Struct#[]=
|
[]= (delegated to its internal Struct )
|
- path RW
the path to the config file, if loaded from a file
- struct R
The underlying config data structure
- time_created RW
The time the configuration was loaded
load( path, defaults=nil, &block )
Read and return a Configurability::Config
object from the file at the given path
.
def self::load( path, defaults=nil, &block )
path = Pathname( path ).expand_path
source = path.read
Configurability.log.debug "Read %d bytes from %s" % [ source.length, path ]
return new( source, path, defaults, &block )
end
merge_complex_hashes( key, oldval, newval )
Recursive hash-merge function. Used as the block argument to a Hash#merge.
def self::merge_complex_hashes( key, oldval, newval )
return oldval.merge( newval, &method(:merge_complex_hashes) ) if
oldval.respond_to?( :merge ) && newval.respond_to?( :merge )
return newval
end
new( source=nil, path=nil, defaults=nil, &block )
Create a new Configurability::Config
object. If the optional source
argument is specified, parse the config from it. If one is given, the block will be evaluated in the context of the config object after the config is loaded, unless it accepts an argument, in which case the config object is passed as the argument.
def initialize( source=nil, path=nil, defaults=nil, &block )
if path.is_a?( Hash )
defaults = path
path = nil
end
@defaults = defaults ? Marshal.load( Marshal.dump(defaults) ) : nil
@time_created = Time.now
@path = path
if source
@struct = self.make_configstruct_from_source( source, @defaults )
else
@struct = Configurability::Config::Struct.new( @defaults )
end
if block
Configurability.log.debug "Block arity is: %p" % [ block.arity ]
case block.arity
when 0, -1
Configurability.log.debug "Instance evaling in the context of %p" % [ self ]
self.instance_eval( &block )
else
block.call( self )
end
end
end
Returns true
if the configuration has changed since it was last loaded, either by setting one of its members or changing the file from which it was loaded.
def changed?
return self.changed_reason ? true : false
end
If the configuration has changed, return the reason. If it hasn’t, returns nil.
def changed_reason
if @struct.dirty?
Configurability.log.debug "Changed_reason: struct was modified"
return "Struct was modified"
end
if self.path && self.is_older_than?( self.path )
Configurability.log.debug "Source file (%s) has changed." % [ self.path ]
return "Config source (%s) has been updated since %s" %
[ self.path, self.time_created ]
end
return nil
end
Return the config object as a YAML hash
def dump
strhash = stringify_keys( self.to_h )
return YAML.dump( strhash )
end
Return a human-readable, compact representation of the configuration suitable for debugging.
def inspect
return "#<%s:0x%0x16 loaded from %s; %d sections: %s>" % [
self.class.name,
self.object_id * 2,
self.path ? self.path : "memory",
self.struct.members.length,
self.struct.members.join( ', ' )
]
end
Install this config object in any objects that have added Configurability
.
def install
Configurability.configure_objects( self )
end
Return true
if the specified file
is newer than the time the receiver was created.
def is_older_than?( path )
return false unless path.exist?
st = path.stat
Configurability.log.debug "File mtime is: %s, comparison time is: %s" %
[ st.mtime, @time_created ]
return st.mtime > @time_created
end
Reload the configuration from the original source if it has changed. Returns true
if it was reloaded and false
otherwise.
def reload
raise "can't reload from an in-memory source" unless self.path
if self.changed?
self.time_created = Time.now
source = self.path.read
@struct = self.make_configstruct_from_source( source, @defaults )
self.install
return true
else
return false
end
end
respond_to?( sym, include_all=false )
Returns true
for methods which can be autoloaded
def respond_to?( sym, include_all=false )
return true if @struct.member?( sym.to_s.sub(/(=|\?)$/, '').to_sym )
super
end
write( path=@path, *args )
Write the configuration object using the specified name and any additional args
.
def write( path=@path, *args )
unless path.is_a?( String ) || path.is_a?( Pathname )
args.unshift( path )
path = @path
end
raise ArgumentError,
"No name associated with this config." unless path
self.log.info "Writing config to %s with args: %p" % [ path, args ]
path = Pathname( path )
path.open( File::WRONLY|File::CREAT|File::TRUNC, *args ) do |ofh|
ofh.print( self.dump )
end
end
Protected Instance Methods
Delegate logging to the module’s Logger.
def log
Configurability.logger
end
make_configstruct_from_source( source, defaults=nil )
Read in the specified filename
and return a config struct.
def make_configstruct_from_source( source, defaults=nil )
defaults ||= {}
mergefunc = Configurability::Config.method( :merge_complex_hashes )
hash = nil
if source.is_a?( Hash )
hash = source
else
hash = if defined?( SafeYAML ) then
YAML.load( source, :safe => true )
else
YAML.load( source )
end
end
ihash = symbolify_keys( hash )
idefaults = symbolify_keys( defaults )
mergedhash = idefaults.merge( ihash, &mergefunc )
return Configurability::Config::Struct.new( mergedhash )
end
method_missing( sym, *args )
Handle calls to struct-members
def method_missing( sym, *args )
key = sym.to_s.sub( /(=|\?)$/, '' ).to_sym
self.class.class_eval %{
def #{key}; @struct.#{key}; end
def #{key}=(arg); @struct.#{key} = arg; end
def #{key}?; @struct.#{key}?; end
}
return self.method( sym ).call( *args )
end