summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMatthias H <apoc@sixserv.org>2014-02-19 17:45:17 +0100
committerMatthias H <apoc@sixserv.org>2014-02-19 17:45:17 +0100
commit8b980642ce55d04aea4ed282154b4cd9fb1574b4 (patch)
tree6a2d06c002ad56d91babc5840ded22632e5edc76 /lib
parent7178fe47dfa606092e564dcdae48ea9586ac4a98 (diff)
[registry] registry folder with suffix, added daybreak engine
Diffstat (limited to 'lib')
-rw-r--r--lib/rbot/ircbot.rb12
-rw-r--r--lib/rbot/registry/daybreak.rb264
-rw-r--r--lib/rbot/registry/dbm.rb2
3 files changed, 269 insertions, 9 deletions
diff --git a/lib/rbot/ircbot.rb b/lib/rbot/ircbot.rb
index 53235edb..0a8b1aa2 100644
--- a/lib/rbot/ircbot.rb
+++ b/lib/rbot/ircbot.rb
@@ -430,9 +430,9 @@ class Bot
Config.register Config::StringValue.new('core.db',
:default => "dbm",
:wizard => true, :default => "dbm",
- :validate => Proc.new { |v| ["dbm"].include? v },
+ :validate => Proc.new { |v| ["dbm", "daybreak"].include? v },
:requires_restart => true,
- :desc => "DB adaptor to use for storing the plugin data/registries. Options: dbm (included in ruby)")
+ :desc => "DB adaptor to use for storing the plugin data/registries. Options: dbm (included in ruby), daybreak")
@argv = params[:argv]
@run_dir = params[:run_dir] || Dir.pwd
@@ -469,12 +469,6 @@ class Bot
repopulate_botclass_directory
- registry_dir = File.join(@botclass, 'registry')
- Dir.mkdir(registry_dir) unless File.exist?(registry_dir)
- unless FileTest.directory? registry_dir
- error "registry storage location #{registry_dir} is not a directory"
- exit 2
- end
save_dir = File.join(@botclass, 'safe_save')
Dir.mkdir(save_dir) unless File.exist?(save_dir)
unless FileTest.directory? save_dir
@@ -505,6 +499,8 @@ class Bot
case @config["core.db"]
when "dbm"
require 'rbot/registry/dbm'
+ when "daybreak"
+ require 'rbot/registry/daybreak'
else
raise _("Unknown DB adaptor: %s") % @config["core.db"]
end
diff --git a/lib/rbot/registry/daybreak.rb b/lib/rbot/registry/daybreak.rb
new file mode 100644
index 00000000..7ec5bd29
--- /dev/null
+++ b/lib/rbot/registry/daybreak.rb
@@ -0,0 +1,264 @@
+#-- vim:sw=2:et
+#++
+#
+# Daybreak is a simple and very fast key value store for ruby.
+# http://propublica.github.io/daybreak/
+#
+# :title: DB interface
+
+require 'daybreak'
+
+module Irc
+class Bot
+class Registry
+
+ # 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
+ # methods of the plugins' RegistryAccessor are calls to Marshal.dump and
+ # Marshal.restore,
+ # for example:
+ # blah = Hash.new
+ # blah[:foo] = "fum"
+ # @registry[:blah] = blah
+ # then, even after the bot is shut down and disconnected, on the next run you
+ # can access the blah object as it was, with:
+ # 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.
+ #
+ # 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 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):
+ # def initialize
+ # class << @registry
+ # def store(val)
+ # val
+ # end
+ # def restore(val)
+ # val
+ # end
+ # end
+ # end
+ # 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 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
+ @filename = @bot.path 'registry_daybreak', @name+'.db'
+ 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
+ }
+ @registry = nil
+ @default = nil
+ @recovery = nil
+ # debug "initializing registry accessor with name #{@name}"
+ end
+
+ def registry
+ @registry ||= Daybreak::DB.new(@filename)
+ end
+
+ def flush
+ return unless @registry
+ @registry.flush
+ end
+
+ def close
+ return unless @registry
+ @registry.close
+ @registry = nil
+ end
+
+ # convert value to string form for storing in the registry
+ # defaults to Marshal.dump(val) but you can override this in your module's
+ # registry object to use any method you like.
+ # For example, if you always just handle strings use:
+ # def store(val)
+ # val
+ # end
+ def store(val)
+ Marshal.dump(val)
+ end
+
+ # restores object from string form, restore(store(val)) must return val.
+ # If you override store, you should override restore to reverse the
+ # action.
+ # For example, if you always just handle strings use:
+ # def restore(val)
+ # val
+ # end
+ def restore(val)
+ begin
+ Marshal.restore(val)
+ 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 @recovery.call(val)
+ rescue Exception => ee
+ error _("marshal recovery failed, trying default")
+ debug ee
+ end
+ end
+ return default
+ end
+ end
+
+ # lookup a key in the registry
+ def [](key)
+ if registry.has_key?(key.to_s)
+ return restore(registry[key.to_s])
+ else
+ return default
+ end
+ end
+
+ # set a key in the registry
+ def []=(key,value)
+ registry[key.to_s] = 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 = default
+ end
+
+ def default
+ @default && (@default.dup rescue @default)
+ end
+
+ # like Hash#each
+ def each(&block)
+ registry.each_key do |key|
+ block.call(key, self[key])
+ end
+ end
+
+ alias each_pair each
+
+ # like Hash#each_key
+ def each_key(&block)
+ registry.each_key do |key|
+ block.call(key)
+ end
+ end
+
+ # like Hash#each_value
+ def each_value(&block)
+ registry.each_key do |key|
+ block.call(self[key])
+ end
+ end
+
+ # just like Hash#has_key?
+ def has_key?(key)
+ return registry.has_key?(key.to_s)
+ end
+
+ alias include? has_key?
+ alias member? has_key?
+ alias key? has_key?
+
+ # just like Hash#has_both?
+ def has_both?(key, value)
+ registry.has_key?(key.to_s) and registry.has_value?(store(value))
+ end
+
+ # just like Hash#has_value?
+ def has_value?(value)
+ return registry.has_value?(store(value))
+ end
+
+ # just like Hash#index?
+ def index(value)
+ self.each do |k,v|
+ return k if v == value
+ end
+ return nil
+ end
+
+ # delete a key from the registry
+ def delete(key)
+ return registry.delete(key.to_s)
+ end
+
+ # returns a list of your keys
+ def keys
+ 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|
+ 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|
+ ret[key] = restore(value)
+ }
+ return ret
+ end
+
+ # empties the registry (restricted to your namespace)
+ def clear
+ registry.clear
+ end
+ alias truncate clear
+
+ # returns an array of the values in your namespace of the registry
+ def values
+ ret = Array.new
+ self.each {|k,v|
+ ret << restore(v)
+ }
+ return ret
+ end
+
+ def sub_registry(prefix)
+ return Accessor.new(@bot, @name + "/" + prefix.to_s)
+ end
+
+ # returns the number of keys in your registry namespace
+ def length
+ registry.length
+ end
+ alias size length
+ end
+
+end # Registry
+end # Bot
+end # Irc
+
diff --git a/lib/rbot/registry/dbm.rb b/lib/rbot/registry/dbm.rb
index 7c309c7f..a0676539 100644
--- a/lib/rbot/registry/dbm.rb
+++ b/lib/rbot/registry/dbm.rb
@@ -68,7 +68,7 @@ class Registry
def initialize(bot, name)
@bot = bot
@name = name.downcase
- @filename = @bot.path 'registry', @name
+ @filename = @bot.path 'registry_dbm', @name
dirs = File.dirname(@filename).split("/")
dirs.length.times { |i|
dir = dirs[0,i+1].join("/")+"/"