perobs 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +113 -0
- data/Rakefile +22 -0
- data/lib/perobs/Array.rb +173 -0
- data/lib/perobs/BlockDB.rb +242 -0
- data/lib/perobs/Cache.rb +201 -0
- data/lib/perobs/DataBase.rb +115 -0
- data/lib/perobs/FileSystemDB.rb +171 -0
- data/lib/perobs/Hash.rb +175 -0
- data/lib/perobs/HashedBlocksDB.rb +153 -0
- data/lib/perobs/Object.rb +189 -0
- data/lib/perobs/ObjectBase.rb +159 -0
- data/lib/perobs/Store.rb +290 -0
- data/lib/perobs/version.rb +4 -0
- data/lib/perobs.rb +29 -0
- data/perobs.gemspec +23 -0
- data/spec/Array_spec.rb +94 -0
- data/spec/FileSystemDB_spec.rb +107 -0
- data/spec/Hash_spec.rb +96 -0
- data/spec/Object_spec.rb +108 -0
- data/spec/Store_spec.rb +412 -0
- data/spec/perobs_spec.rb +155 -0
- data/tasks/changelog.rake +169 -0
- data/tasks/gem.rake +50 -0
- data/tasks/rdoc.rake +14 -0
- data/tasks/test.rake +7 -0
- metadata +121 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# = HashedBlocksDB.rb -- Persistent Ruby Object Store
|
4
|
+
#
|
5
|
+
# Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
|
6
|
+
#
|
7
|
+
# MIT License
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
|
28
|
+
require 'time'
|
29
|
+
require 'json'
|
30
|
+
require 'json/add/core'
|
31
|
+
require 'json/add/struct'
|
32
|
+
require 'yaml'
|
33
|
+
require 'fileutils'
|
34
|
+
|
35
|
+
require 'perobs/DataBase'
|
36
|
+
require 'perobs/BlockDB'
|
37
|
+
|
38
|
+
module PEROBS
|
39
|
+
|
40
|
+
# This class provides a filesytem based database store for objects.
|
41
|
+
class HashedBlocksDB < DataBase
|
42
|
+
|
43
|
+
@@Extensions = {
|
44
|
+
:marshal => '.mshl',
|
45
|
+
:json => '.json',
|
46
|
+
:yaml => '.yml'
|
47
|
+
}
|
48
|
+
|
49
|
+
# Create a new FileSystemDB object. This will create a DB with the given
|
50
|
+
# name. A database will live in a directory of that name.
|
51
|
+
# @param db_name [String] name of the DB directory
|
52
|
+
# @param options [Hash] options to customize the behavior. Currently only
|
53
|
+
# the following options are supported:
|
54
|
+
# :serializer : Can be :marshal, :json, :yaml
|
55
|
+
# :dir_nibbles : The number of nibbles to use for directory names.
|
56
|
+
# Meaningful values are 1, 2, and 3. The larger the
|
57
|
+
# number the more back-end files are used. Each
|
58
|
+
# nibble provides 16 times more directories.
|
59
|
+
# :block_size : The size of the blocks inside the storage files in
|
60
|
+
# bytes. This should roughly correspond to the size
|
61
|
+
# of the smallest serialized objects you want to
|
62
|
+
# store in quantities. It also should be an fraction
|
63
|
+
# of 4096, the native storage system block size.
|
64
|
+
def initialize(db_name, options = {})
|
65
|
+
super(options[:serializer] || :json)
|
66
|
+
@db_dir = db_name
|
67
|
+
@dir_nibbles = options[:dir_nibbles] || 2
|
68
|
+
@block_size = options[:block_size] || 256
|
69
|
+
|
70
|
+
# Create the database directory if it doesn't exist yet.
|
71
|
+
ensure_dir_exists(@db_dir)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return true if the object with given ID exists
|
75
|
+
# @param id [Fixnum or Bignum]
|
76
|
+
def include?(id)
|
77
|
+
!BlockDB.new(directory(id), @block_size).find(id).nil?
|
78
|
+
end
|
79
|
+
|
80
|
+
# Store the given object into the cluster files.
|
81
|
+
# @param obj [Hash] Object as defined by PEROBS::ObjectBase
|
82
|
+
def put_object(obj, id)
|
83
|
+
BlockDB.new(directory(id), @block_size).write_object(id, serialize(obj))
|
84
|
+
end
|
85
|
+
|
86
|
+
# Load the given object from the filesystem.
|
87
|
+
# @param id [Fixnum or Bignum] object ID
|
88
|
+
# @return [Hash] Object as defined by PEROBS::ObjectBase
|
89
|
+
def get_object(id)
|
90
|
+
deserialize(BlockDB.new(directory(id), @block_size).read_object(id))
|
91
|
+
end
|
92
|
+
|
93
|
+
# This method must be called to initiate the marking process.
|
94
|
+
def clear_marks
|
95
|
+
Dir.glob(File.join(@db_dir, '*')) do |dir|
|
96
|
+
BlockDB.new(dir, @block_size).clear_marks
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Permanently delete all objects that have not been marked. Those are
|
101
|
+
# orphaned and are no longer referenced by any actively used object.
|
102
|
+
def delete_unmarked_objects
|
103
|
+
Dir.glob(File.join(@db_dir, '*')) do |dir|
|
104
|
+
BlockDB.new(dir, @block_size).delete_unmarked_entries
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Mark an object.
|
109
|
+
# @param id [Fixnum or Bignum] ID of the object to mark
|
110
|
+
def mark(id)
|
111
|
+
BlockDB.new(directory(id), @block_size).mark(id)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Check if the object is marked.
|
115
|
+
# @param id [Fixnum or Bignum] ID of the object to check
|
116
|
+
def is_marked?(id)
|
117
|
+
BlockDB.new(directory(id), @block_size).is_marked?(id)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Check if the stored object is syntactically correct.
|
121
|
+
# @param id [Fixnum/Bignum] Object ID
|
122
|
+
# @param repair [TrueClass/FalseClass] True if an repair attempt should be
|
123
|
+
# made.
|
124
|
+
# @return [TrueClass/FalseClass] True if the object is OK, otherwise
|
125
|
+
# false.
|
126
|
+
def check(id, repair)
|
127
|
+
begin
|
128
|
+
get_object(id)
|
129
|
+
rescue => e
|
130
|
+
$stderr.puts "Cannot read object with ID #{id}: #{e.message}"
|
131
|
+
return false
|
132
|
+
end
|
133
|
+
|
134
|
+
true
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
# Determine the file name to store the object. The object ID determines
|
140
|
+
# the directory and file name inside the store.
|
141
|
+
# @param id [Fixnum or Bignum] ID of the object
|
142
|
+
def directory(id)
|
143
|
+
hex_id = "%016X" % id
|
144
|
+
dir = hex_id[0..(@dir_nibbles - 1)]
|
145
|
+
ensure_dir_exists(dir_name = File.join(@db_dir, dir))
|
146
|
+
|
147
|
+
dir_name
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# = Object.rb -- Persistent Ruby Object Store
|
4
|
+
#
|
5
|
+
# Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
|
6
|
+
#
|
7
|
+
# MIT License
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
|
28
|
+
require 'time'
|
29
|
+
|
30
|
+
require 'perobs/ObjectBase'
|
31
|
+
|
32
|
+
module PEROBS
|
33
|
+
|
34
|
+
# The PEROBS::Object class is the base class for user-defined objects to be
|
35
|
+
# stored in the Store. It provides all the plumbing to define the class
|
36
|
+
# attributes and to transparently load and store the instances of the class
|
37
|
+
# in the database. You can use instance variables like normal instance
|
38
|
+
# variables unless they refer to other PEROBS objects. In these cases you
|
39
|
+
# must use the accessor methods for these instance variables. You must use
|
40
|
+
# accessor methods for any read and write operation to instance variables
|
41
|
+
# that hold or should hold PEROBS objects.
|
42
|
+
class Object < ObjectBase
|
43
|
+
|
44
|
+
# Modify the Metaclass of PEROBS::Object to add the attribute method and
|
45
|
+
# instance variables to store the default values of the attributes.
|
46
|
+
class << self
|
47
|
+
|
48
|
+
attr_reader :attributes
|
49
|
+
|
50
|
+
# This method can be used to define instance variable for
|
51
|
+
# PEROBS::Object derived classes.
|
52
|
+
# @param attributes [Symbol] Name of the instance variable
|
53
|
+
def po_attr(*attributes)
|
54
|
+
attributes.each do |attr_name|
|
55
|
+
unless attr_name.is_a?(Symbol)
|
56
|
+
raise ArgumentError, "name must be a symbol but is a " +
|
57
|
+
"#{attr_name.class}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Create the attribute reader method with name of attr_name.
|
61
|
+
define_method(attr_name.to_s) do
|
62
|
+
_get(attr_name)
|
63
|
+
end
|
64
|
+
# Create the attribute writer method with name of attr_name.
|
65
|
+
define_method(attr_name.to_s + '=') do |val|
|
66
|
+
_set(attr_name, val)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Store a list of the attribute names
|
70
|
+
@attributes ||= []
|
71
|
+
@attributes << attr_name unless @attributes.include?(attr_name)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
attr_reader :attributes
|
78
|
+
|
79
|
+
# Create a new PEROBS::Object object.
|
80
|
+
def initialize(store)
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
# Initialize the specified attribute _attr_ with the value _val_ unless
|
85
|
+
# the attribute has been initialized already. Use this method in the class
|
86
|
+
# constructor to avoid overwriting values that have been set when the
|
87
|
+
# object was reconstructed from the store.
|
88
|
+
# @param attr [Symbol] Name of the attribute
|
89
|
+
# @param val [Any] Value to be set
|
90
|
+
# @return [true|false] True if the value was initialized, otherwise false.
|
91
|
+
def init_attr(attr, val)
|
92
|
+
if self.class.attributes.include?(attr)
|
93
|
+
_set(attr, val)
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
|
97
|
+
false
|
98
|
+
end
|
99
|
+
|
100
|
+
# Return a list of all object IDs that the attributes of this instance are
|
101
|
+
# referencing.
|
102
|
+
# @return [Array of Fixnum or Bignum] IDs of referenced objects
|
103
|
+
def _referenced_object_ids
|
104
|
+
ids = []
|
105
|
+
self.class.attributes.each do |attr|
|
106
|
+
value = instance_variable_get(('@' + attr.to_s).to_sym)
|
107
|
+
ids << value.id if value && value.is_a?(POReference)
|
108
|
+
end
|
109
|
+
ids
|
110
|
+
end
|
111
|
+
|
112
|
+
# This method should only be used during store repair operations. It will
|
113
|
+
# delete all referenced to the given object ID.
|
114
|
+
# @param id [Fixnum/Bignum] targeted object ID
|
115
|
+
def _delete_reference_to_id(id)
|
116
|
+
self.class.attributes.each do |attr|
|
117
|
+
ivar = ('@' + attr.to_s).to_sym
|
118
|
+
value = instance_variable_get(ivar)
|
119
|
+
if value && value.is_a?(POReference) && value.id == id
|
120
|
+
instance_variable_set(ivar, nil)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Restore the persistent data from a single data structure.
|
126
|
+
# This is a library internal method. Do not use outside of this library.
|
127
|
+
# @param data [Hash] attribute values hashed by their name
|
128
|
+
# @private
|
129
|
+
def _deserialize(data)
|
130
|
+
# Initialize all attributes with the provided values.
|
131
|
+
data.each do |attr_name, value|
|
132
|
+
instance_variable_set(('@' + attr_name).to_sym, value)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
# Return a single data structure that holds all persistent data for this
|
139
|
+
# class.
|
140
|
+
def _serialize
|
141
|
+
attributes = {}
|
142
|
+
self.class.attributes.each do |attr|
|
143
|
+
ivar = ('@' + attr.to_s).to_sym
|
144
|
+
if (value = instance_variable_get(ivar)).is_a?(ObjectBase)
|
145
|
+
raise ArgumentError, "The instance variable #{ivar} contains a " +
|
146
|
+
"reference to a PEROBS::ObjectBase object! " +
|
147
|
+
"This is not allowed. You must use the " +
|
148
|
+
"accessor method to assign a reference to " +
|
149
|
+
"another PEROBS object."
|
150
|
+
end
|
151
|
+
attributes[attr.to_s] = value
|
152
|
+
end
|
153
|
+
attributes
|
154
|
+
end
|
155
|
+
|
156
|
+
def _set(attr, val)
|
157
|
+
ivar = ('@' + attr.to_s).to_sym
|
158
|
+
if val.is_a?(ObjectBase)
|
159
|
+
# References to other PEROBS::Objects must be handled somewhat
|
160
|
+
# special.
|
161
|
+
if @store != val.store
|
162
|
+
raise ArgumentError, 'The referenced object is not part of this store'
|
163
|
+
end
|
164
|
+
# To release the object from the Ruby object list later, we store the
|
165
|
+
# PEROBS::Store ID of the referenced object instead of the actual
|
166
|
+
# reference.
|
167
|
+
instance_variable_set(ivar, POReference.new(val._id))
|
168
|
+
else
|
169
|
+
instance_variable_set(ivar, val)
|
170
|
+
end
|
171
|
+
# Let the store know that we have a modified object.
|
172
|
+
@store.cache.cache_write(self)
|
173
|
+
|
174
|
+
val
|
175
|
+
end
|
176
|
+
|
177
|
+
def _get(attr)
|
178
|
+
value = instance_variable_get(('@' + attr.to_s).to_sym)
|
179
|
+
if value.is_a?(POReference)
|
180
|
+
@store.object_by_id(value.id)
|
181
|
+
else
|
182
|
+
value
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# = ObjectBase.rb -- Persistent Ruby Object Store
|
4
|
+
#
|
5
|
+
# Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
|
6
|
+
#
|
7
|
+
# MIT License
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
|
28
|
+
module PEROBS
|
29
|
+
|
30
|
+
# This class is used to replace a direct reference to another Ruby object by
|
31
|
+
# the Store ID. This makes object disposable by the Ruby garbage collector
|
32
|
+
# since it's no longer referenced once it has been evicted from the
|
33
|
+
# PEROBS::Store cache.
|
34
|
+
class POReference < Struct.new(:id)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Base class for all persistent objects. It provides the functionality
|
38
|
+
# common to all classes of persistent objects.
|
39
|
+
class ObjectBase
|
40
|
+
|
41
|
+
attr_reader :_id, :store
|
42
|
+
|
43
|
+
# Create a new PEROBS::ObjectBase object.
|
44
|
+
def initialize(store)
|
45
|
+
@store = store
|
46
|
+
@_id = @store.db.new_id
|
47
|
+
@_stash_map = nil
|
48
|
+
|
49
|
+
# Let the store know that we have a modified object.
|
50
|
+
@store.cache.cache_write(self)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Two objects are considered equal if their object IDs are the same.
|
54
|
+
def ==(obj)
|
55
|
+
obj && @_id == obj._id
|
56
|
+
end
|
57
|
+
|
58
|
+
# Write the object into the backing store database.
|
59
|
+
def _sync
|
60
|
+
# Reset the stash map to ensure that it's reset before the next
|
61
|
+
# transaction is being started.
|
62
|
+
@_stash_map = nil
|
63
|
+
|
64
|
+
db_obj = {
|
65
|
+
'class' => self.class.to_s,
|
66
|
+
'data' => _serialize
|
67
|
+
}
|
68
|
+
@store.db.put_object(db_obj, @_id)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Read an raw object with the specified ID from the backing store and
|
72
|
+
# instantiate a new object of the specific type.
|
73
|
+
def ObjectBase.read(store, id)
|
74
|
+
# Read the object from database.
|
75
|
+
db_obj = store.db.get_object(id)
|
76
|
+
|
77
|
+
# Call the constructor of the specified class.
|
78
|
+
obj = Object.const_get(db_obj['class']).new(store)
|
79
|
+
# The object gets created with a new ID by default. We need to restore
|
80
|
+
# the old one.
|
81
|
+
obj._change_id(id)
|
82
|
+
obj._deserialize(db_obj['data'])
|
83
|
+
|
84
|
+
obj
|
85
|
+
end
|
86
|
+
|
87
|
+
# Restore the object state from the storage back-end.
|
88
|
+
# @param level [Fixnum] the transaction nesting level
|
89
|
+
def _restore(level)
|
90
|
+
# Find the most recently stored state of this object. This could be on
|
91
|
+
# any previous stash level or in the regular object DB. If the object
|
92
|
+
# was created during the transaction, there is not previous state to
|
93
|
+
# restore to.
|
94
|
+
id = nil
|
95
|
+
if @_stash_map
|
96
|
+
(level - 1).downto(0) do |lvl|
|
97
|
+
if @_stash_map[lvl]
|
98
|
+
id = @_stash_map[lvl]
|
99
|
+
break
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
unless id
|
104
|
+
if @store.db.include?(@_id)
|
105
|
+
id = @_id
|
106
|
+
end
|
107
|
+
end
|
108
|
+
if id
|
109
|
+
db_obj = store.db.get_object(id)
|
110
|
+
_deserialize(db_obj['data'])
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Save the object state for this transaction level to the storage
|
115
|
+
# back-end. The object gets a new ID that is stored in @_stash_map to map
|
116
|
+
# the stash ID back to the original data.
|
117
|
+
def _stash(level)
|
118
|
+
db_obj = {
|
119
|
+
'class' => self.class.to_s,
|
120
|
+
'data' => _serialize
|
121
|
+
}
|
122
|
+
@_stash_map = [] unless @_stash_map
|
123
|
+
# Get a new ID to store this version of the object.
|
124
|
+
@_stash_map[level] = stash_id = @store.db.new_id
|
125
|
+
@store.db.put_object(db_obj, stash_id)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Library internal method. Do not use outside of this library.
|
129
|
+
# @private
|
130
|
+
def _change_id(id)
|
131
|
+
# Unregister the object with the old ID from the write cache to prevent
|
132
|
+
# cache corruption. The objects are index by ID in the cache.
|
133
|
+
store.cache.unwrite(self)
|
134
|
+
@_id = id
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def _dereferenced(v)
|
140
|
+
v.is_a?(POReference) ? @store.object_by_id(v.id) : v
|
141
|
+
end
|
142
|
+
|
143
|
+
def _referenced(obj)
|
144
|
+
if obj.is_a?(ObjectBase)
|
145
|
+
# The obj is a reference to another persistent object. Store the ID
|
146
|
+
# of that object in a POReference object.
|
147
|
+
if @store != obj.store
|
148
|
+
raise ArgumentError, 'The referenced object is not part of this store'
|
149
|
+
end
|
150
|
+
POReference.new(obj._id)
|
151
|
+
else
|
152
|
+
obj
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|