# encoding: utf-8 # require 'kde-build/metaaid' module MJ; class ConfigurationError < Exception end module Configuration class Option attr_accessor :name, :attribute, :description, :default def initialize( obj, name, description ) @name = name @attribute =self.class.sanitize name @description = description @default = nil @one_of = nil @on_read = nil @on_write = nil @required = false install obj end def default=( value ) @default = value self end def self.sanitize( name ) name.gsub( /[^a-zA-Z_]/, '_' ) end def install( obj ) attribute = @attribute obj.class_def( attribute ) do opt = self.class.__options__[attribute] value = self.values[attribute] end obj.class_def( "#{attribute}=" ) do |value| opt = self.class.__options__[attribute] if !opt puts self puts self.class.__options__.inspect raise Exception, "????" end # First validate the value opt.validate(value) value = opt.write(value) # Then call the custom on_write method self.values[attribute] = value end end def validate( value ) if @one_of and !@one_of.include? value raise ConfigurationError, "Value #{value} is invalid for #{@name}." end if @required and value === nil || value.empty? raise ConfigurationError, "Required option #{@name} is not set." end end def one_of( options ) @one_of = options self end def on_write( &block ) @on_write = block end def required @required = true self end def write( value ) begin return @on_write.call( value ) if @on_write value rescue Exception => e $log.error("Error while setting #{@name} to #{value}") raise e end end def on_read( &block ) @on_read = block end def read( value ) return @on_read.call( value ) if @on_read value end end def flatten_values( val, &block ) if val.instance_of?( Array ) val.each do |i| flatten_values( i, &block ) end elsif val.instance_of?( Hash ) val.each do |k, v| yield k, v end else puts o.class puts o.inspect raise Exception, "??????????" end end module_function :flatten_values module Configurable def self.included( klass ) klass.extend( ClassMethods ) klass.send( :class_variable_set, '@@__options__', Hash.new ) end module ClassMethods def __options__ class_variable_get( '@@__options__' ) end def option( name, description ) opt = Option.new( self, name, description ) self.__options__[opt.attribute] = opt opt end end def values return @values if @values @values = Hash.new self.class.__options__.each_pair do |name, opt| @values[name] = opt.default end @values end def set( key, val ) begin # puts "Options = #{self.class.options.inspect}" self.send( "#{Option.sanitize( key )}=", val ) rescue BuildTool::VCS::UnknownVcsError => e $log.warn( "Unknown vcs '#{key}' configured for #{self.name}:#{self.class}" ) raise e rescue NameError => e $log.warn( "Unknown option #{key} configured for #{self.name}:#{self.class}" ) raise e rescue Exception => e $log.warn( "Caught exception #{e.message} for #{self.inspect}:#{self.class} while setting #{key} to #{val.inspect}" ) raise e end end def validate begin self.class.__options__.each_pair do |name, opt| opt.validate( self.values[opt.attribute] ) end rescue ConfigurationError => e $log.error( "Validation for #{self.class} (#{self.to_s}) failed: #{e}" ) puts e.backtrace if $verbose raise e end end def parse( val ) # If it is a array we expect it to only contain hashes if val.instance_of?( Array ) val.each { |i| parse( i ) } elsif val.instance_of?( Hash ) val.each_pair { |k, v| begin # puts "#{self.class}.#{k} = #{v}" send "#{Option.sanitize k}=", v rescue NameError => e $log.warn( "Unknown option '#{k}' for #{self.class} in #{self.name}: #{e}" ) end } else raise Exception, "FIXME" end self end end end end