4 # The DBM class of the ruby std-lib provides wrappers for Unix-style
5 # dbm or Database Manager libraries. The exact library used depends
6 # on how ruby was compiled. Its any of the following: ndbm, bdb,
8 # DBM API Documentation:
9 # http://ruby-doc.org/stdlib-2.1.0/libdoc/dbm/rdoc/DBM.html
11 # :title: DB interface
19 # This class provides persistent storage for plugins via a hash interface.
20 # The default mode is an object store, so you can store ruby objects and
21 # reference them with hash keys. This is because the default store/restore
22 # methods of the plugins' RegistryAccessor are calls to Marshal.dump and
27 # @registry[:blah] = blah
28 # then, even after the bot is shut down and disconnected, on the next run you
29 # can access the blah object as it was, with:
30 # blah = @registry[:blah]
31 # The registry can of course be used to store simple strings, fixnums, etc as
32 # well, and should be useful to store or cache plugin data or dynamic plugin
36 # in object store mode, don't make the mistake of treating it like a live
37 # object, e.g. (using the example above)
38 # @registry[:blah][:foo] = "flump"
39 # will NOT modify the object in the registry - remember that Registry#[]
40 # returns a Marshal.restore'd object, the object you just modified in place
41 # will disappear. You would need to:
42 # blah = @registry[:blah]
43 # blah[:foo] = "flump"
44 # @registry[:blah] = blah
46 # If you don't need to store objects, and strictly want a persistant hash of
47 # strings, you can override the store/restore methods to suit your needs, for
48 # example (in your plugin):
59 # Your plugins section of the registry is private, it has its own namespace
60 # (derived from the plugin's class name, so change it and lose your data).
61 # Calls to registry.each etc, will only iterate over your namespace.
64 attr_accessor :recovery
66 # plugins don't call this - a Registry::Accessor is created for them and
67 # is accessible via @registry.
68 def initialize(bot, name)
71 @filename = @bot.path 'registry_dbm', @name
72 dirs = File.dirname(@filename).split("/")
73 dirs.length.times { |i|
74 dir = dirs[0,i+1].join("/")+"/"
75 unless File.exist?(dir)
76 debug "creating subregistry directory #{dir}"
83 # debug "initializing registry accessor with name #{@name}"
87 @registry ||= DBM.open(@filename, 0666, DBM::WRCREAT)
92 # ruby dbm has no flush, so we close/reopen :(
103 # convert value to string form for storing in the registry
104 # defaults to Marshal.dump(val) but you can override this in your module's
105 # registry object to use any method you like.
106 # For example, if you always just handle strings use:
114 # restores object from string form, restore(store(val)) must return val.
115 # If you override store, you should override restore to reverse the
117 # For example, if you always just handle strings use:
124 rescue Exception => e
125 error _("failed to restore marshal data for #{val.inspect}, attempting recovery or fallback to default")
127 if defined? @recovery and @recovery
129 return @recovery.call(val)
130 rescue Exception => ee
131 error _("marshal recovery failed, trying default")
139 # lookup a key in the registry
141 if registry.has_key?(key.to_s)
142 return restore(registry[key.to_s])
148 # set a key in the registry
150 registry[key.to_s] = store(value)
153 # set the default value for registry lookups, if the key sought is not
154 # found, the default will be returned. The default default (har) is nil.
155 def set_default (default)
160 @default && (@default.dup rescue @default)
165 registry.each_key do |key|
166 block.call(key, self[key])
174 registry.each_key do |key|
179 # like Hash#each_value
180 def each_value(&block)
181 registry.each_key do |key|
182 block.call(self[key])
186 # just like Hash#has_key?
188 return registry.has_key?(key.to_s)
191 alias include? has_key?
192 alias member? has_key?
195 # just like Hash#has_both?
196 def has_both?(key, value)
197 registry.has_key?(key.to_s) and registry.has_value?(store(value))
200 # just like Hash#has_value?
201 def has_value?(value)
202 return registry.has_value?(store(value))
205 # just like Hash#index?
208 return k if v == value
213 # delete a key from the registry
215 return registry.delete(key.to_s)
218 # returns a list of your keys
223 # Return an array of all associations [key, value] in your namespace
226 registry.each {|key, value|
227 ret << [key, restore(value)]
232 # Return an hash of all associations {key => value} in your namespace
235 registry.each {|key, value|
236 ret[key] = restore(value)
241 # empties the registry (restricted to your namespace)
247 # returns an array of the values in your namespace of the registry
256 def sub_registry(prefix)
257 return Accessor.new(@bot, @name + "/" + prefix.to_s)
260 # returns the number of keys in your registry namespace