lib/perobs/ObjectBase.rb in perobs-1.1.0 vs lib/perobs/ObjectBase.rb in perobs-2.0.0
- old
+ new
@@ -30,39 +30,111 @@
module PEROBS
# 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.
- class POReference < Struct.new(:id)
+ # PEROBS::Store cache. The POXReference objects function as a transparent
+ # proxy for the objects they are referencing.
+ class POXReference < BasicObject
- # Textual dump for debugging purposes
- # @return [String]
- def inspect
- "@#{id}"
+ attr_reader :store, :id
+
+ def initialize(store, id)
+ super()
+ @store = store
+ @id = id
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"
+ end
+ if obj.respond_to?(:is_poxreference?)
+ 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
+ # respond_to?(:is_poxreference?) is the only reliable way to find out if
+ # the object is a POXReference or not as pretty much every method call is
+ # proxied to the referenced object.
+ def respond_to?(method_sym, include_private = false)
+ (method_sym == :is_poxreference?) ||
+ _referenced_object.respond_to?(method_sym, include_private)
+ end
+
+ # Just for completeness. We don't want to be caught lying.
+ def is_poxreference?
+ true
+ end
+
+ # @return [ObjectBase] Return the referenced object. This method should
+ # not be used outside of the PEROBS library. Leaked references can cause
+ # data corruption.
+ def _referenced_object
+ @store.object_by_id(@id)
+ end
+
+ # BasicObject provides a ==() 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 ==(obj)
+ _referenced_object == obj
+ end
+
+ # Shortcut to access the _id() method of the referenced object.
+ def _id
+ @id
+ end
+
end
+ # This class is used to serialize the POXReference objects. It only holds
+ # the ID of the referenced Object.
+ class POReference < Struct.new(:id)
+ end
+
# Base class for all persistent objects. It provides the functionality
# common to all classes of persistent objects.
class ObjectBase
attr_reader :_id, :store
- # Create a new PEROBS::ObjectBase object.
+ # 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,
+ "All PEROBS objects must exclusively be created by calling " +
+ "Store.new(). Never call the object constructor directly."
+ end
@_id = @store.db.new_id
@_stash_map = nil
# Let the store know that we have a modified object.
@store.cache.cache_write(self)
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
+ # another.
+ def post_restore
+ end
+
# Two objects are considered equal if their object IDs are the same.
def ==(obj)
+ return false unless obj.is_a?(ObjectBase)
obj && @_id == obj._id
end
# Write the object into the backing store database.
def _sync
@@ -83,15 +155,16 @@
# 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 = Object.const_get(klass).new(store)
+ 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._deserialize(db_obj['data'])
+ obj.post_restore
obj
end
# Restore the object state from the storage back-end.
@@ -138,30 +211,11 @@
# Library internal method. Do not use outside of this library.
# @private
def _change_id(id)
# Unregister the object with the old ID from the write cache to prevent
# cache corruption. The objects are index by ID in the cache.
- store.cache.unwrite(self)
+ @store.cache.unwrite(self)
@_id = id
- end
-
- private
-
- def _dereferenced(v)
- v.is_a?(POReference) ? @store.object_by_id(v.id) : v
- end
-
- def _referenced(obj)
- if obj.is_a?(ObjectBase)
- # The obj is a reference to another persistent object. Store the ID
- # of that object in a POReference object.
- if @store != obj.store
- raise ArgumentError, 'The referenced object is not part of this store'
- end
- POReference.new(obj._id)
- else
- obj
- end
end
end
end