X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Fregistry.rb;h=a3c55da83953e72c8eb5646f107aae22e61ab9b5;hb=a4ff366eea4c88083be8a3d30cc6395f17b55fe2;hp=cd78dcbf8537199aa2b6837a4a8d9658af27a273;hpb=2a96c9198c1f6e13407d0999083f6ce5e0bc06fa;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/registry.rb b/lib/rbot/registry.rb index cd78dcbf..a3c55da8 100644 --- a/lib/rbot/registry.rb +++ b/lib/rbot/registry.rb @@ -1,41 +1,14 @@ -# Copyright (C) 2002 Tom Gilbert. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies of the Software and its documentation and acknowledgment shall be -# given in the documentation and software packages that this Software was -# used. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - require 'rbot/dbhash' module Irc - # this is the backend of the RegistryAccessor class, which ties it to a - # DBHash object called plugin_registry(.db). All methods are delegated to - # the DBHash. + # 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 def initialize(bot) @bot = bot upgrade_data - @db = DBTree.new @bot, "plugin_registry" - end - - # delegation hack - def method_missing(method, *args, &block) - @db.send(method, *args, &block) + upgrade_data2 end # check for older versions of rbot with data formats that require updating @@ -43,24 +16,64 @@ module Irc # 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, "set_pagesize" => 1024, - "set_cachesize" => [0, 32 * 1024, 0] - new = BDB::CIBtree.open "#{@bot.botclass}/plugin_registry.db", nil, - BDB::CREATE | BDB::EXCL | BDB::TRUNCATE, - 0600, "set_pagesize" => 1024, - "set_cachesize" => [0, 32 * 1024, 0] + log "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) old.each {|k,v| new[k] = v } old.close new.close - File.delete("#{@bot.botclass}/registry.db") + File.rename("#{@bot.botclass}/registry.db", "#{@bot.botclass}/registry.db.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)# | BDB::TXN_NOSYNC) + dbs = Hash.new + log "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) + 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) + 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") + dbs.each {|k,v| + 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 # reference them with hash keys. This is because the default store/restore @@ -75,7 +88,7 @@ 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 @@ -107,19 +120,31 @@ module Irc class BotRegistryAccessor # plugins don't call this - a BotRegistryAccessor is created for them and # is accessible via @registry. - def initialize(bot, prefix) + def initialize(bot, name) @bot = bot - @registry = @bot.registry - @orig_prefix = prefix - @prefix = prefix + "/" + @name = name.downcase + dirs = File.dirname("#{@bot.botclass}/registry/#{@name}").split("/") + dirs.length.times { |i| + dir = dirs[0,i+1].join("/")+"/" + unless File.exist?(dir) + debug "creating subregistry directory #{dir}" + Dir.mkdir(dir) + end + } + @registry = DBTree.new bot, "registry/#{@name}" @default = nil - # debug "initializing registry accessor with prefix #{@prefix}" + # debug "initializing registry accessor with name #{@name}" end - # use this to chop up your namespace into bits, so you can keep and - # reference separate object stores under the same registry - def sub_registry(prefix) - return BotRegistryAccessor.new(@bot, @orig_prefix + "+" + prefix) + def flush + # debug "fushing registry #{@registry}" + @registry.flush + @registry.sync + end + + def close + # debug "closing registry #{@registry}" + @registry.close end # convert value to string form for storing in the registry @@ -141,13 +166,28 @@ module Irc # val # end def restore(val) - Marshal.restore(val) + begin + Marshal.restore(val) + rescue Exception => e + warning "failed to restore marshal data for #{val.inspect}, falling back to default" + debug e.inspect + debug e.backtrace.join("\n") + if @default != nil + begin + return Marshal.restore(@default) + rescue + return nil + end + else + return nil + end + end end # lookup a key in the registry def [](key) - if @registry.has_key?(@prefix + key) - return restore(@registry[@prefix + key]) + if @registry.has_key?(key) + return restore(@registry[key]) elsif @default != nil return restore(@default) else @@ -157,7 +197,7 @@ module Irc # set a key in the registry def []=(key,value) - @registry[@prefix + key] = store(value) + @registry[key] = store(value) end # set the default value for registry lookups, if the key sought is not @@ -169,42 +209,36 @@ module Irc # just like Hash#each def each(&block) @registry.each {|key,value| - if key.gsub!(/^#{Regexp.escape(@prefix)}/, "") - block.call(key, restore(value)) - end + block.call(key, restore(value)) } end - + # just like Hash#each_key def each_key(&block) @registry.each {|key, value| - if key.gsub!(/^#{Regexp.escape(@prefix)}/, "") - block.call(key) - end + block.call(key) } end - + # just like Hash#each_value def each_value(&block) @registry.each {|key, value| - if key =~ /^#{Regexp.escape(@prefix)}/ - block.call(restore(value)) - end + block.call(restore(value)) } end # just like Hash#has_key? def has_key?(key) - return @registry.has_key?(@prefix + key) + return @registry.has_key?(key) end alias include? has_key? alias member? has_key? # just like Hash#has_both? def has_both?(key, value) - return @registry.has_both?(@prefix + key, store(value)) + return @registry.has_both?(key, store(value)) end - + # just like Hash#has_value? def has_value?(value) return @registry.has_value?(store(value)) @@ -213,58 +247,44 @@ module Irc # just like Hash#index? def index(value) ind = @registry.index(store(value)) - if ind && ind.gsub!(/^#{Regexp.escape(@prefix)}/, "") + if ind return ind else return nil end end - + # delete a key from the registry def delete(key) - return @registry.delete(@prefix + key) + return @registry.delete(key) end # returns a list of your keys def keys - return @registry.keys.collect {|key| - if key.gsub!(/^#{Regexp.escape(@prefix)}/, "") - key - else - nil - end - }.compact + return @registry.keys end # Return an array of all associations [key, value] in your namespace def to_a ret = Array.new @registry.each {|key, value| - if key.gsub!(/^#{Regexp.escape(@prefix)}/, "") - ret << [key, restore(value)] - end + ret << [key, restore(value)] } return ret end - + # Return an hash of all associations {key => value} in your namespace def to_hash ret = Hash.new @registry.each {|key, value| - if key.gsub!(/^#{Regexp.escape(@prefix)}/, "") - ret[key] = restore(value) - end + ret[key] = restore(value) } return ret end # empties the registry (restricted to your namespace) def clear - @registry.each_key {|key| - if key =~ /^#{Regexp.escape(@prefix)}/ - @registry.delete(key) - end - } + @registry.clear end alias truncate clear @@ -277,16 +297,16 @@ module Irc return ret end + def sub_registry(prefix) + return BotRegistryAccessor.new(@bot, @name + "/" + prefix.to_s) + end + # returns the number of keys in your registry namespace def length self.keys.length end alias size length - def flush - @registry.flush - end - end end