]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/config.rb
fix: restart logger thread after fork
[user/henk/code/ruby/rbot.git] / lib / rbot / config.rb
index 7ed019fe8dc48ce89c19d15099578a43309cfe77..ef63a2feb06c36f1372e75d412d199814b633543 100644 (file)
@@ -24,6 +24,7 @@ module Config
     attr_reader :desc
     attr_reader :key
     attr_reader :wizard
+    attr_reader :store_default
     attr_reader :requires_restart
     attr_reader :requires_rescan
     attr_reader :order
@@ -40,7 +41,9 @@ module Config
       @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
@@ -50,6 +53,7 @@ module Config
       @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('.','::')}"
@@ -63,18 +67,20 @@ module Config
     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
@@ -96,16 +102,18 @@ module Config
       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
@@ -157,6 +165,18 @@ module Config
   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
@@ -164,8 +184,13 @@ module Config
       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
@@ -205,6 +230,7 @@ module Config
     attr_reader :bot
     attr_reader :items
     attr_reader :config
+    attr_reader :overrides
     attr_accessor :changed
 
     def initialize
@@ -214,6 +240,21 @@ module Config
     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_
@@ -223,9 +264,10 @@ module Config
       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
           }
@@ -234,6 +276,14 @@ module Config
           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
@@ -268,9 +318,13 @@ module 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)
@@ -284,8 +338,10 @@ module Config
         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
@@ -293,8 +349,7 @@ module Config
           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! #{$!}"
@@ -323,6 +378,7 @@ module Config
     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}")