Config

class
Superclass
Object
Included Modules
Configurability::Config::DataUtilities

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)

Attributes

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

Public Class Methods

anchor
load( path, defaults=nil, &block )

Read and return a Configurability::Config object from the file at the given path.

# File lib/configurability/config.rb, line 51
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
anchor
merge_complex_hashes( key, oldval, newval )

Recursive hash-merge function. Used as the block argument to a Hash#merge.

# File lib/configurability/config.rb, line 60
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
anchor
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.

# File lib/configurability/config.rb, line 77
def initialize( source=nil, path=nil, defaults=nil, &block )

        # Shift the hash parameter if it shows up as the path
        if path.is_a?( Hash )
                defaults = path
                path = nil
        end

        # Make a deep copy of the defaults before loading so we don't modify
        # the argument
        @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 ]

                # A block with an argument is called with the config as the argument
                # instead of instance_evaled
                case block.arity
                when 0, -1  # 1.9 and 1.8, respectively
                        Configurability.log.debug "Instance evaling in the context of %p" % [ self ]
                        self.instance_eval( &block )
                else
                        block.call( self )
                end
        end
end

Public Instance Methods

anchor
changed?()

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.

# File lib/configurability/config.rb, line 174
def changed?
        return self.changed_reason ? true : false
end
anchor
changed_reason()

If the configuration has changed, return the reason. If it hasn't, returns nil.

# File lib/configurability/config.rb, line 181
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
anchor
dump()

Return the config object as a YAML hash

# File lib/configurability/config.rb, line 139
def dump
        strhash = stringify_keys( self.to_h )
        return YAML.dump( strhash )
end
anchor
inspect()

Return a human-readable, compact representation of the configuration suitable for debugging.

# File lib/configurability/config.rb, line 228
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
anchor
install()

Install this config object in any objects that have added Configurability.

# File lib/configurability/config.rb, line 133
def install
        Configurability.configure_objects( self )
end
anchor
is_older_than?( path )

Return true if the specified file is newer than the time the receiver was created.

# File lib/configurability/config.rb, line 199
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
anchor
reload()

Reload the configuration from the original source if it has changed. Returns true if it was reloaded and false otherwise.

# File lib/configurability/config.rb, line 210
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
anchor
respond_to?( sym )

Returns true for methods which can be autoloaded

# File lib/configurability/config.rb, line 165
def respond_to?( sym )
        return true if @struct.member?( sym.to_s.sub(/(=|\?)$/, '').to_sym )
        super
end
anchor
write( path=@path, *args )

Write the configuration object using the specified name and any additional args.

# File lib/configurability/config.rb, line 147
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

anchor
log()

Delegate logging to the module's Logger.

# File lib/configurability/config.rb, line 282
def log
        Configurability.logger
end
anchor
make_configstruct_from_source( source, defaults=nil )

Read in the specified filename and return a config struct.

# File lib/configurability/config.rb, line 244
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( untaint_hash(hash) )
        idefaults = symbolify_keys( defaults )
        mergedhash = idefaults.merge( ihash, &mergefunc )

        return Configurability::Config::Struct.new( mergedhash )
end
anchor
method_missing( sym, *args )

Handle calls to struct-members

# File lib/configurability/config.rb, line 268
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