X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Fregistry.rb;h=4d52bff22e35d501764f73c8ef19fff0e4d75728;hb=7f9a979b04fbacda7343b1bcbda1ce83bfb31402;hp=580bfb5b272081a656027da46f0b1cd3d81f3278;hpb=8483e916ca3f1bc5450dc8c6966786b6958c2465;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/registry.rb b/lib/rbot/registry.rb index 580bfb5b..4d52bff2 100644 --- a/lib/rbot/registry.rb +++ b/lib/rbot/registry.rb @@ -1,10 +1,11 @@ require 'rbot/dbhash' module Irc +class Bot - # this class is now used purely for upgrading from prior versions of rbot + # This class is now used purely for upgrading from prior versions of rbot # the new registry is split into multiple DBHash objects, one per plugin - class BotRegistry + class Registry def initialize(bot) @bot = bot upgrade_data @@ -15,53 +16,63 @@ module Irc # NB this function is called _early_ in init(), pretty much all you have to # work with is @bot.botclass. def upgrade_data - if File.exist?("#{@bot.botclass}/registry.db") - puts "upgrading old-style (rbot 0.9.5 or earlier) plugin registry to new format" - old = BDB::Hash.open("#{@bot.botclass}/registry.db", nil, - "r+", 0600) - new = BDB::CIBtree.open("#{@bot.botclass}/plugin_registry.db", nil, - BDB::CREATE | BDB::EXCL, - 0600) + oldreg = @bot.path 'registry.db' + newreg = @bot.path 'plugin_registry.db' + if File.exist?(oldreg) + log _("upgrading old-style (rbot 0.9.5 or earlier) plugin registry to new format") + old = BDB::Hash.open(oldreg, nil, "r+", 0600) + new = BDB::CIBtree.open(newreg, nil, BDB::CREATE | BDB::EXCL, 0600) old.each {|k,v| new[k] = v } old.close new.close - File.delete("#{@bot.botclass}/registry.db") + File.rename(oldreg, oldreg + ".old") end end - + def upgrade_data2 - if File.exist?("#{@bot.botclass}/plugin_registry.db") - Dir.mkdir("#{@bot.botclass}/registry") unless File.exist?("#{@bot.botclass}/registry") - env = BDB::Env.open("#{@bot.botclass}", BDB::INIT_TRANSACTION | BDB::CREATE | BDB::RECOVER) + oldreg = @bot.path 'plugin_registry.db' + newdir = @bot.path 'registry' + if File.exist?(oldreg) + Dir.mkdir(newdir) unless File.exist?(newdir) + env = BDB::Env.open(@bot.botclass, BDB::INIT_TRANSACTION | BDB::CREATE | BDB::RECOVER)# | BDB::TXN_NOSYNC) dbs = Hash.new - puts "upgrading previous (rbot 0.9.9 or earlier) plugin registry to new split format" - old = BDB::CIBtree.open("#{@bot.botclass}/plugin_registry.db", nil, - "r+", 0600, "env" => env) + log _("upgrading previous (rbot 0.9.9 or earlier) plugin registry to new split format") + old = BDB::CIBtree.open(oldreg, nil, "r+", 0600, "env" => env) old.each {|k,v| prefix,key = k.split("/", 2) prefix.downcase! + # subregistries were split with a +, now they are in separate folders + if prefix.gsub!(/\+/, "/") + # Ok, this code needs to be put in the db opening routines + dirs = File.dirname("#{@bot.botclass}/registry/#{prefix}.db").split("/") + dirs.length.times { |i| + dir = dirs[0,i+1].join("/")+"/" + unless File.exist?(dir) + log _("creating subregistry directory #{dir}") + Dir.mkdir(dir) + end + } + end unless dbs.has_key?(prefix) - puts "creating db #{@bot.botclass}/registry/#{prefix}.db" + log _("creating db #{@bot.botclass}/registry/#{prefix}.db") dbs[prefix] = BDB::CIBtree.open("#{@bot.botclass}/registry/#{prefix}.db", nil, BDB::CREATE | BDB::EXCL, 0600, "env" => env) - end dbs[prefix][key] = v } old.close - File.rename("#{@bot.botclass}/plugin_registry.db", "#{@bot.botclass}/plugin_registry.db.old") + File.rename(oldreg, oldreg + ".old") dbs.each {|k,v| - puts "closing db #{k}" + log _("closing db #{k}") v.close } env.close end end - end - + # This class provides persistent storage for plugins via a hash interface. # The default mode is an object store, so you can store ruby objects and @@ -77,19 +88,19 @@ module Irc # blah = @registry[:blah] # The registry can of course be used to store simple strings, fixnums, etc as # well, and should be useful to store or cache plugin data or dynamic plugin - # configuration. + # configuration. # # WARNING: # in object store mode, don't make the mistake of treating it like a live # object, e.g. (using the example above) # @registry[:blah][:foo] = "flump" - # will NOT modify the object in the registry - remember that BotRegistry#[] + # will NOT modify the object in the registry - remember that Registry#[] # returns a Marshal.restore'd object, the object you just modified in place # will disappear. You would need to: # blah = @registry[:blah] # blah[:foo] = "flump" # @registry[:blah] = blah - + # # If you don't need to store objects, and strictly want a persistant hash of # strings, you can override the store/restore methods to suit your needs, for # example (in your plugin): @@ -106,24 +117,46 @@ module Irc # Your plugins section of the registry is private, it has its own namespace # (derived from the plugin's class name, so change it and lose your data). # Calls to registry.each etc, will only iterate over your namespace. - class BotRegistryAccessor - # plugins don't call this - a BotRegistryAccessor is created for them and + class Accessor + + attr_accessor :recovery + + # plugins don't call this - a Registry::Accessor is created for them and # is accessible via @registry. def initialize(bot, name) @bot = bot @name = name.downcase - @registry = DBTree.new bot, "registry/#{@name}" + @filename = @bot.path 'registry', @name + dirs = File.dirname(@filename).split("/") + dirs.length.times { |i| + dir = dirs[0,i+1].join("/")+"/" + unless File.exist?(dir) + debug "creating subregistry directory #{dir}" + Dir.mkdir(dir) + end + } + @filename << ".db" + @registry = nil @default = nil + @recovery = nil # debug "initializing registry accessor with name #{@name}" end + def registry + @registry ||= DBTree.new @bot, "registry/#{@name}" + end + def flush - @registry.flush - @registry.sync + # debug "fushing registry #{registry}" + return if !@registry + registry.flush + registry.sync end def close - @registry.close + # debug "closing registry #{registry}" + return if !@registry + registry.close end # convert value to string form for storing in the registry @@ -147,113 +180,128 @@ module Irc def restore(val) begin Marshal.restore(val) - rescue Exception - $stderr.puts "failed to restore marshal data, falling back to default" - if @default != nil + rescue Exception => e + error _("failed to restore marshal data for #{val.inspect}, attempting recovery or fallback to default") + debug e + if defined? @recovery and @recovery begin - return Marshal.restore(@default) - rescue - return nil + return @recovery.call(val) + rescue Exception => ee + error _("marshal recovery failed, trying default") + debug ee end - else - return nil end + return default end end # lookup a key in the registry def [](key) - if @registry.has_key?(key) - return restore(@registry[key]) - elsif @default != nil - return restore(@default) + if File.exist?(@filename) && registry.has_key?(key) + return restore(registry[key]) else - return nil + return default end end # set a key in the registry def []=(key,value) - @registry[key] = store(value) + registry[key] = store(value) end # set the default value for registry lookups, if the key sought is not # found, the default will be returned. The default default (har) is nil. def set_default (default) - @default = store(default) + @default = default + end + + def default + @default && (@default.dup rescue @default) end # just like Hash#each - def each(&block) - @registry.each {|key,value| + def each(set=nil, bulk=0, &block) + return nil unless File.exist?(@filename) + registry.each(set, bulk) {|key,value| block.call(key, restore(value)) } end - + # just like Hash#each_key - def each_key(&block) - @registry.each {|key, value| + def each_key(set=nil, bulk=0, &block) + return nil unless File.exist?(@filename) + registry.each_key(set, bulk) {|key| block.call(key) } end - + # just like Hash#each_value - def each_value(&block) - @registry.each {|key, value| + def each_value(set=nil, bulk=0, &block) + return nil unless File.exist?(@filename) + registry.each_value(set, bulk) { |value| block.call(restore(value)) } end # just like Hash#has_key? def has_key?(key) - return @registry.has_key?(key) + return false unless File.exist?(@filename) + return registry.has_key?(key) end alias include? has_key? alias member? has_key? + alias key? has_key? # just like Hash#has_both? def has_both?(key, value) - return @registry.has_both?(key, store(value)) + return false unless File.exist?(@filename) + return registry.has_both?(key, store(value)) end - + # just like Hash#has_value? def has_value?(value) - return @registry.has_value?(store(value)) + return false unless File.exist?(@filename) + return registry.has_value?(store(value)) end # just like Hash#index? def index(value) - ind = @registry.index(store(value)) + return nil unless File.exist?(@filename) + ind = registry.index(store(value)) if ind return ind else return nil end end - + # delete a key from the registry def delete(key) - return @registry.delete(key) + return default unless File.exist?(@filename) + return registry.delete(key) end # returns a list of your keys def keys - return @registry.keys + return [] unless File.exist?(@filename) + return registry.keys end # Return an array of all associations [key, value] in your namespace def to_a + return [] unless File.exist?(@filename) ret = Array.new - @registry.each {|key, value| + registry.each {|key, value| ret << [key, restore(value)] } return ret end - + # Return an hash of all associations {key => value} in your namespace def to_hash + return {} unless File.exist?(@filename) ret = Hash.new - @registry.each {|key, value| + registry.each {|key, value| ret[key] = restore(value) } return ret @@ -261,12 +309,14 @@ module Irc # empties the registry (restricted to your namespace) def clear - @registry.clear + return true unless File.exist?(@filename) + registry.clear end alias truncate clear # returns an array of the values in your namespace of the registry def values + return [] unless File.exist?(@filename) ret = Array.new self.each {|k,v| ret << restore(v) @@ -275,7 +325,7 @@ module Irc end def sub_registry(prefix) - return BotRegistryAccessor.new(@bot, @name + "/" + prefix) + return Accessor.new(@bot, @name + "/" + prefix.to_s) end # returns the number of keys in your registry namespace @@ -286,4 +336,6 @@ module Irc end + end +end end