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