require 'yaml' require 'nano/kernel/constant' require 'mega/synchash' require 'glue/attribute' require 'glue/flexob' # A Configuration holds a group of Settings organized by # Owners. #-- # TODO: implement with annotations. #++ class Configuration # A hash of setting owners. Use double @'s to allow for # the Settings alias. #-- # TODO: find a better name. #++ @@owners = SyncHash.new # A datastructure to store Settings metadata. class Setting attr_accessor :owner, :name, :type, :value, :options def initialize(owner, name, options) raise ArgumentError.new('A default value is required') unless options.key?(:default) @owner, @name = owner, name @options = options @value = options[:default] @type = options[:type] = options[:type] || @value.class end def value=(value) @value = value constant(@owner).module_eval %{ @@#{@name} = #{value.inspect} } end def to_s @value.to_s end end class << self # Inject the configuration parameters to configuration # classes. def setup(options) options.each do |owner, ss| next unless ss begin owner = constant(owner) rescue NameError next end ss.each do |name, s| @@owners[owner][name.to_sym].value = s owner.module_eval %{ @@#{name} = #{s.inspect} } end end end # Parse configuration parameters in yaml format. def parse(options) temp = YAML::load(options) options = {} temp.each do |k, v| begin options[constant(k.gsub(/\./, '::').to_sym)] = v rescue Object options[k] = v end end setup(options) end # Load and parse an external yaml configuration file. def load(filename) parse(File.read(filename)) end def add_setting(owner, name, options) s = @@owners[owner] || {} s[name] = Setting.new(owner, name, options) @@owners[owner] = s end def settings(owner = nil) if owner @@owners[owner] else @@owners.values.inject([]) { |memo, obj| memo.concat(obj.values) } end end alias_method :all, :settings alias_method :[], :settings def method_missing(sym) if sym.to_s =~ /[A-Z]/ # nano's capitalized? is buggy at the moment. Flexob.new(self[constant(sym)]) end end end end # Alias for the Configuration class (shorter). class Settings < Configuration end class Module # Defines a configuration setting. #-- # TODO: implement with annotations. #++ def setting(sym, options = {}) Configuration.add_setting(self, sym, options) module_eval %{ mattr_accessor sym, options[:default] def self.#{sym.id2name}=(obj) @@#{sym.id2name} = obj Configuration[#{self}][:#{sym}].value = obj end } end end # * George Moschovitis