attr_reader :desc
attr_reader :key
attr_reader :wizard
+ attr_reader :store_default
attr_reader :requires_restart
attr_reader :requires_rescan
attr_reader :order
@order = @@order
@@order += 1
@key = key.to_sym
- if params.has_key? :default
+ if @manager.overrides.key?(@key)
+ @default = @manager.overrides[@key]
+ elsif params.has_key? :default
@default = params[:default]
else
@default = false
@on_change = params[:on_change]
@validate = params[:validate]
@wizard = params[:wizard]
+ @store_default = params[:store_default]
@requires_restart = params[:requires_restart]
@requires_rescan = params[:requires_rescan]
@auth_path = "config::key::#{key.sub('.','::')}"
end
def get
return @manager.config[@key] if @manager.config.has_key?(@key)
- return @default
+ 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
+ return self
end
def unset
@manager.config.delete(@key)
@manager.changed = true
@on_change.call(@manager.bot, value) if @on_change
+ return self
end
# set string will raise ArgumentErrors on failed parse/validate
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)
+ protected
+ def validate(val, validator = @validate)
+ case validator
+ when false, nil
+ return true
+ when Proc
+ return validator.call(val)
+ when Regexp
+ raise ArgumentError, "validation via Regexp only supported for strings!" unless String === val
+ return validator.match(val)
else
- raise ArgumentError, "validation type #{@validate.class} not supported"
+ raise ArgumentError, "validation type #{validator.class} not supported"
end
end
end
end
class ArrayValue < Value
+ def initialize(key, params)
+ super
+ @validate_item = params[:validate_item]
+ @validate ||= Proc.new do |v|
+ !v.find { |i| !validate_item(i) }
+ end
+ end
+
+ def validate_item(item)
+ validate(item, @validate_item)
+ end
+
def parse(string)
string.split(/,\s+/)
end
get.join(", ")
end
def add(val)
- curval = self.get
- set(curval + [val]) unless curval.include?(val)
+ newval = self.get.dup
+ unless newval.include? val
+ newval << val
+ validate_item(val) or raise ArgumentError, "invalid item: #{val}"
+ validate(newval) or raise ArgumentError, "invalid value: #{newval.inspect}"
+ set(newval)
+ end
end
def rm(val)
curval = self.get
attr_reader :bot
attr_reader :items
attr_reader :config
+ attr_reader :overrides
attr_accessor :changed
def initialize
def reset_config
@items = Hash.new
@config = Hash.new(false)
+
+ # We allow default values for config keys to be overridden by
+ # the config file /etc/rbot.conf
+ # The main purpose for this is to allow distro or system-wide
+ # settings such as external program paths (figlet, toilet, ispell)
+ # to be set once for all the bots.
+ @overrides = Hash.new
+ etcfile = "/etc/rbot.conf"
+ if File.exist?(etcfile)
+ log "Loading defaults from #{etcfile}"
+ etcconf = YAML::load_file(etcfile)
+ etcconf.each { |k, v|
+ @overrides[k.to_sym] = v
+ }
+ end
end
# Associate with bot _bot_
return unless @bot
@changed = false
- if(File.exist?("#{@bot.botclass}/conf.yaml"))
+ conf = @bot.path 'conf.yaml'
+ if File.exist? conf
begin
- newconfig = YAML::load_file("#{@bot.botclass}/conf.yaml")
+ newconfig = YAML::load_file conf
newconfig.each { |key, val|
@config[key.to_sym] = val
}
error "failed to read conf.yaml: #{$!}"
end
end
+ # config options with :store_default to true should store
+ # their default value at first run.
+ # Some defaults might change anytime the bot starts
+ # for instance core.db or authpw
+ @items.values.find_all {|i| i.store_default }.each do |value|
+ @config[value.key] = value.default
+ end
+
# if we got here, we need to run the first-run wizard
Wizard.new(@bot).run
# save newly created config
return false
end
- # TODO should I implement this via Value or leave it direct?
- # def []=(key, value)
- # end
+ def []=(key, value)
+ return @items[key.to_sym].set(value) if @items.has_key?(key.to_sym)
+ if @config.has_key?(key.to_sym)
+ warning _("Unregistered lookup #{key.to_sym.inspect}")
+ return @config[key.to_sym] = value
+ end
+ end
# pass everything else through to the hash
def method_missing(method, *args, &block)
return
end
begin
+ conf = @bot.path 'conf.yaml'
+ fnew = conf + '.new'
debug "Writing new conf.yaml ..."
- File.open("#{@bot.botclass}/conf.yaml.new", "w") do |file|
+ File.open(fnew, "w") do |file|
savehash = {}
@config.each { |key, val|
savehash[key.to_s] = val
file.puts savehash.to_yaml
end
debug "Officializing conf.yaml ..."
- File.rename("#{@bot.botclass}/conf.yaml.new",
- "#{@bot.botclass}/conf.yaml")
+ File.rename(fnew, conf)
@changed = false
rescue => e
error "failed to write configuration file conf.yaml! #{$!}"
end
def run()
+ $stdout.sync = true
puts _("First time rbot configuration wizard")
puts "===================================="
puts _("This is the first time you have run rbot with a config directory of: #{@bot.botclass}")