lib/perobs/ObjectBase.rb in perobs-2.0.1 vs lib/perobs/ObjectBase.rb in perobs-2.1.0
- old
+ new
@@ -1,10 +1,10 @@
# encoding: UTF-8
#
# = ObjectBase.rb -- Persistent Ruby Object Store
#
-# Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
+# Copyright (c) 2015, 2016 by Chris Schlaeger <chris@taskjuggler.org>
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -32,11 +32,11 @@
# This class is used to replace a direct reference to another Ruby object by
# the Store ID. This makes object disposable by the Ruby garbage collector
# since it's no longer referenced once it has been evicted from the
# PEROBS::Store cache. The POXReference objects function as a transparent
# proxy for the objects they are referencing.
- class POXReference < BasicObject
+ class POXReference < BasicObject
attr_reader :store, :id
def initialize(store, id)
super()
@@ -45,16 +45,17 @@
end
# Proxy all calls to unknown methods to the referenced object.
def method_missing(method_sym, *args, &block)
unless (obj = _referenced_object)
- raise ::RuntimeError, "Internal consistency error. No object with " +
- "ID #{@id} found in the store"
+ ::Kernel.raise ::RuntimeError,
+ "Internal consistency error. No object with ID #{@id} found in " +
+ 'the store.'
end
if obj.respond_to?(:is_poxreference?)
- raise ::RuntimeError,
- "POXReference that references a POXReference found"
+ ::Kernel.raise ::RuntimeError,
+ "POXReference that references a POXReference found."
end
obj.send(method_sym, *args, &block)
end
# Proxy all calls to unknown methods to the referenced object. Calling
@@ -84,10 +85,22 @@
# @param obj object to compare this object with.
def ==(obj)
_referenced_object == obj
end
+ # BasicObject provides a equal?() method that prevents method_missing from
+ # being called. So we have to pass the call manually to the referenced
+ # object.
+ # @param obj object to compare this object with.
+ def equal?(obj)
+ if obj.respond_to?(:is_poxreference?)
+ _referenced_object.equal?(obj._referenced_object)
+ else
+ _referenced_object.equal?(obj)
+ end
+ end
+
# Shortcut to access the _id() method of the referenced object.
def _id
@id
end
@@ -100,29 +113,41 @@
# Base class for all persistent objects. It provides the functionality
# common to all classes of persistent objects.
class ObjectBase
- attr_reader :_id, :store
+ attr_reader :_id, :store, :myself
# New PEROBS objects must always be created by calling # Store.new().
# PEROBS users should never call this method or equivalents of derived
# methods directly.
def initialize(store)
@store = store
unless @store.object_creation_in_progress
- raise ::RuntimeError,
+ ::Kernel.raise ::RuntimeError,
"All PEROBS objects must exclusively be created by calling " +
"Store.new(). Never call the object constructor directly."
end
@_id = @store.db.new_id
+ ObjectSpace.define_finalizer(self, ObjectBase._finalize(@store, @_id))
@_stash_map = nil
+ # Allocate a proxy object for this object. User code should only operate
+ # on this proxy, never on self.
+ @myself = POXReference.new(@store, @_id)
# Let the store know that we have a modified object.
@store.cache.cache_write(self)
end
+ # This method generates the destructor for the objects of this class. It
+ # is done this way to prevent the Proc object hanging on to a reference to
+ # self which would prevent the object from being collected. This internal
+ # method is not intended for users to call.
+ def ObjectBase._finalize(store, id)
+ proc { store._collect(id) }
+ end
+
public
# This method can be overloaded by derived classes to do some massaging on
# the data after it has been restored from the database. This could either
# be some sanity check or code to migrate the object from one version to
@@ -155,13 +180,10 @@
# Read the object from database.
db_obj = store.db.get_object(id)
klass = store.class_map.id_to_class(db_obj['class_id'])
# Call the constructor of the specified class.
- obj = store.construct_po(Object.const_get(klass))
- # The object gets created with a new ID by default. We need to restore
- # the old one.
- obj._change_id(id)
+ obj = store._construct_po(Object.const_get(klass), id)
obj._deserialize(db_obj['data'])
obj.post_restore
obj
end