Skip to content

Instantly share code, notes, and snippets.

@lsegal
Created November 25, 2009 23:37
Show Gist options
  • Save lsegal/243096 to your computer and use it in GitHub Desktop.
Save lsegal/243096 to your computer and use it in GitHub Desktop.
module ODB
def self.new(store = nil)
Database.new(store || HashStore.new)
end
class Database
attr_accessor :store
class << self
attr_accessor :current
end
def initialize(store)
self.store = store
self.store.db = self
self.class.current = self unless self.class.current
end
def transaction(&block)
Transaction.new(self, &block)
end
end
class DataStore
attr_reader :name
def initialize(name)
@name = name
end
def read(key)
raise NotImplementedError
end
def write(key, value)
raise NotImplementedError
end
def [](key) read(key) end
def []=(key, value) write(key, value) end
end
class Transaction
attr_accessor :objects
def self.transactions
Thread.current['__odb_transactions'] ||= []
end
def self.current
transactions.last
end
def initialize(db = ODB.current, &block)
@objects = []
@db = db
transaction(&block) if block_given?
end
def transaction(&block)
self.class.transactions << self
yield
commit
self.class.transactions.pop
end
def commit
while objects.size > 0
object = objects.pop
@db.store[object.object_id] = object
end
end
end
class FileStore < DataStore
def read(key)
Marshal.load(IO.read(resource(key)))
end
def write(key, value)
File.open(resource(key), "wb") do |file|
file.write(Marshal.dump(value))
end
end
protected
def resource(key)
File.join(@name, key)
end
end
class HashStore < DataStore
attr_accessor :db
def initialize
super(nil)
@store = {}
end
def read(key)
@store[key].__deserialize__(db)
end
def write(key, value)
@store[key] = value.__serialize__
end
end
end
class Object
def __serialize__
obj = {:class => self.class, :ivars => {}}
instance_variables.each do |ivar|
subobj = instance_variable_get(ivar)
ODB::Transaction.current.objects << subobj unless Fixnum === subobj
obj[:ivars][ivar[1..-1]] = Fixnum === subobj ? subobj.__serialize__ : subobj.object_id
end
obj
end
end
class String
def __serialize__
super().update(:value => self)
end
end
class Array
def __serialize__
obj = super
obj[:items] = []
each do |item|
ODB::Transaction.current.objects << item
obj[:items].push = item.object_id
end
obj
end
end
class Fixnum
def __serialize__
{:class => Fixnum, :value => self}
end
end
class Float
def __serialize__
super().update(:value => self)
end
end
class Hash
def __deserialize__(db = ODB::Database.current)
object = self[:value] ? self[:value] : self[:class].allocate
self[:ivars].each do |ivar, value|
object.instance_variable_set("@#{ivar}", Hash === value ? value[:value] : db.store[value])
end
if self[:class] == Array
self[:list].each do |item|
object.push(db.store[item])
end
end
object
end
end
class NilClass
def __deserialize__(db = nil)
nil
end
end
require 'odb'
Post = Struct.new(:title, :author, :comment)
class Comment
def initialize
@name = 1
@value = 2.5
end
end
db = ODB.new
db.transaction do
post = Post.new.tap {|p| p.title = "x"; p.author = "Joe"; p.comment = Comment.new }
db.store[:foo] = post
end
p db.store[:foo]
# #<Post:0x100123170 @comment=#<Comment:0x100123030 @name=1, @value=2.5>, @author="Joe", @title="x">
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment