require "volatiledb/version" require 'digest/sha1' # # Main module. See DB class. # module Volatile # # The main DB class. This is the public API for the library. # class DB # # Initializes storage to the given path. # def initialize(path) @raw ||= Volatile::Raw.new(path) @db ||= {} end # # Define an action for a given key. The action should return a String. # A timeout in seconds may optionally be defined. Default is 3 seconds. # def put(key, timeout=3, &action) populate_db key, timeout, action raw_put key key end # # Read the data for the given key from storage. If the underlying # storage has disappeared, it will be re-initialized by calling # the action defined for that key again. After the storage has # been re-initialized, its value will be read and returned. # # Returns nil for a key which has not had any action defined for it yet. # def get(key) data = raw_get key if data.nil? sync key else data end end # # Works the same as get, but will fire the action defined for the given key # after the timeout defined for that key has elapsed. # # Returns nil for a key which has not had any action defined for it yet. # def fetch(key) checking key do |k| item = @db[k] delta = now - item[:last_access] delta > item[:timeout] ? sync(k) : get(k) end end # # Works the same as fetch, but ignores timeout and calls the defined action # every time. Should be used sparingly as it re-initializes storage on every # call. # # Returns nil for a key which has not had any action defined for it yet. # def pull(key) sync key end private def populate_db(key, timeout, action) @db[key] = { :action => action, :timeout => timeout, :last_access => now } end def sync(key) touch key save key end def touch(key) checking(key) {|k| @db[k][:last_access] = now } end def now Time.now.to_i end def save(key) raw_put key raw_get key end def raw_put(key) checking(key) {|k| @raw.put(k, @db[k][:action].call) } end def raw_get(key) checking(key) {|k| @raw.get k } end def checking(key) if @db.key?(key) yield(key) else nil end end end # # Implementation detail. Not intended for direct instantiation. # class Raw # # Implementation detail. # def initialize(path) @path = path @db ||= {} end # # Implementation detail. # def put(key, data) handle = handle_from key File.open(path(handle), "w") {|f| f << data} @db[key] = handle key end # # Implementation detail. # def get(key) handle = @db[key] f = path handle if File.file?(f) File.read f else nil end end private def handle_from(key) timestamp = Time.now.to_i guid = Digest::SHA1.hexdigest("#{key}#{timestamp}") "#{guid}.volatiledb" end def path(handle) File.join(@path, handle) end end end