perobs 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|