+ unless YAML.respond_to?(:load_file)
+ def YAML.load_file( filepath )
+ File.open( filepath ) do |f|
+ YAML::load( f )
+ end
+ end
+ end
+
+ class BotConfigValue
+ # allow the definition order to be preserved so that sorting by
+ # definition order is possible. The BotConfigWizard does this to allow
+ # the :wizard questions to be in a sensible order.
+ @@order = 0
+ attr_reader :type
+ attr_reader :desc
+ attr_reader :key
+ attr_reader :wizard
+ attr_reader :requires_restart
+ attr_reader :requires_rescan
+ attr_reader :order
+ attr_reader :manager
+ attr_reader :auth_path
+ def initialize(key, params)
+ @manager = BotConfig::configmanager
+ # Keys must be in the form 'module.name'.
+ # They will be internally passed around as symbols,
+ # but we accept them both in string and symbol form.
+ unless key.to_s =~ /^.+\..+$/
+ raise ArgumentError,"key must be of the form 'module.name'"
+ end
+ @order = @@order
+ @@order += 1
+ @key = key.to_sym
+ if params.has_key? :default
+ @default = params[:default]
+ else
+ @default = false
+ end
+ @desc = params[:desc]
+ @type = params[:type] || String
+ @on_change = params[:on_change]
+ @validate = params[:validate]
+ @wizard = params[:wizard]
+ @requires_restart = params[:requires_restart]
+ @requires_rescan = params[:requires_rescan]
+ @auth_path = "config::key::#{key.sub('.','::')}"
+ end
+ def default
+ if @default.instance_of?(Proc)
+ @default.call
+ else
+ @default
+ end
+ end
+ def get
+ return @manager.config[@key] if @manager.config.has_key?(@key)
+ return @default
+ end
+ alias :value :get
+ def set(value, on_change = true)
+ @manager.config[@key] = value
+ @manager.changed = true
+ @on_change.call(@manager.bot, value) if on_change && @on_change
+ end
+ def unset
+ @manager.config.delete(@key)
+ @manager.changed = true
+ @on_change.call(@manager.bot, value) if @on_change
+ end
+
+ # set string will raise ArgumentErrors on failed parse/validate
+ def set_string(string, on_change = true)
+ value = parse string
+ if validate value
+ set value, on_change
+ else
+ raise ArgumentError, "invalid value: #{string}"
+ end
+ end
+
+ # override this. the default will work for strings only
+ def parse(string)
+ string
+ end
+
+ def to_s
+ get.to_s
+ end
+
+ private
+ def validate(value)
+ return true unless @validate
+ if @validate.instance_of?(Proc)
+ return @validate.call(value)
+ elsif @validate.instance_of?(Regexp)
+ raise ArgumentError, "validation via Regexp only supported for strings!" unless value.instance_of? String
+ return @validate.match(value)
+ else
+ raise ArgumentError, "validation type #{@validate.class} not supported"
+ end
+ end
+ end
+
+ class BotConfigStringValue < BotConfigValue
+ end
+
+ class BotConfigBooleanValue < BotConfigValue
+ def parse(string)
+ return true if string == "true"
+ return false if string == "false"
+ if string =~ /^-?\d+$/
+ return string.to_i != 0
+ end
+ raise ArgumentError, "#{string} does not match either 'true' or 'false', and it's not an integer either"
+ end
+ def get
+ r = super
+ if r.kind_of?(Integer)
+ return r != 0
+ else
+ return r
+ end
+ end
+ end
+
+ class BotConfigIntegerValue < BotConfigValue
+ def parse(string)
+ return 1 if string == "true"
+ return 0 if string == "false"
+ raise ArgumentError, "not an integer: #{string}" unless string =~ /^-?\d+$/
+ string.to_i
+ end
+ def get
+ r = super
+ if r.kind_of?(Integer)
+ return r
+ else
+ return r ? 1 : 0
+ end
+ end
+ end
+
+ class BotConfigFloatValue < BotConfigValue
+ def parse(string)
+ raise ArgumentError, "not a float #{string}" unless string =~ /^-?[\d.]+$/
+ string.to_f
+ end
+ end
+
+ class BotConfigArrayValue < BotConfigValue
+ def parse(string)
+ string.split(/,\s+/)
+ end
+ def to_s
+ get.join(", ")
+ end
+ def add(val)
+ curval = self.get
+ set(curval + [val]) unless curval.include?(val)
+ end
+ def rm(val)
+ curval = self.get
+ raise ArgumentError, "value #{val} not present" unless curval.include?(val)
+ set(curval - [val])
+ end
+ end
+
+ class BotConfigEnumValue < BotConfigValue
+ def initialize(key, params)
+ super
+ @values = params[:values]
+ end
+ def values
+ if @values.instance_of?(Proc)
+ return @values.call(@manager.bot)
+ else
+ return @values
+ end
+ end
+ def parse(string)
+ unless values.include?(string)
+ raise ArgumentError, "invalid value #{string}, allowed values are: " + values.join(", ")
+ end
+ string
+ end
+ def desc
+ _("%{desc} [valid values are: %{values}]") % {:desc => @desc, :values => values.join(', ')}
+ end
+ end
+