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.
@@ -0,0 +1,201 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # = Cache.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 'perobs/Store'
29
+
30
+ module PEROBS
31
+
32
+ # The Cache provides two functions for the PEROBS Store. It keeps some
33
+ # amount of objects in memory to substantially reduce read access latencies.
34
+ # It # also stores a list of objects that haven't been synced to the
35
+ # permanent store yet to accelerate object writes.
36
+ class Cache
37
+
38
+ # Create a new Cache object.
39
+ # @param bits [Fixnum] Number of bits for the cache index. This parameter
40
+ # heavilty affects the performance and memory consumption of the
41
+ # cache.
42
+ def initialize(bits = 16)
43
+ @bits = bits
44
+ # This mask is used to access the _bits_ least significant bits of the
45
+ # object ID.
46
+ @mask = 2 ** bits - 1
47
+ # Initialize the read and write cache
48
+ reset
49
+ end
50
+
51
+ # Add an PEROBS::Object to the read cache.
52
+ # @param obj [PEROBS::ObjectBase]
53
+ def cache_read(obj)
54
+ @reads[index(obj)] = obj
55
+ end
56
+
57
+ # Add a PEROBS::Object to the write cache.
58
+ # @param obj [PEROBS::ObjectBase]
59
+ def cache_write(obj)
60
+ if @transaction_stack.empty?
61
+ idx = index(obj)
62
+ if (old_obj = @writes[idx]) && old_obj._id != obj._id
63
+ # There is another old object using this cache slot. Before we can
64
+ # re-use the slot, we need to sync it to the permanent storage.
65
+ old_obj._sync
66
+ end
67
+ @writes[idx] = obj
68
+ else
69
+ # When a transaction is active, we don't have a write cache. The read
70
+ # cache is used to speed up access to recently used objects.
71
+ cache_read(obj)
72
+ # Push the reference of the modified object into the write buffer for
73
+ # this transaction level.
74
+ unless @transaction_stack.last.include?(obj)
75
+ @transaction_stack.last << obj
76
+ end
77
+ end
78
+ end
79
+
80
+ # Remove an object from the write cache. This will prevent a modified
81
+ # object from being written to the back-end store.
82
+ def unwrite(obj)
83
+ if @transaction_stack.empty?
84
+ idx = index(obj)
85
+ if (old_obj = @writes[idx]).nil? || old_obj._id != obj._id
86
+ raise RuntimeError, "Object to unwrite is not in cache"
87
+ end
88
+ @writes[idx] = nil
89
+ else
90
+ unless @transaction_stack.last.include?(obj)
91
+ raise RuntimeError, 'unwrite failed'
92
+ end
93
+ @transaction_stack.last.delete(obj)
94
+ end
95
+ end
96
+
97
+ # Return the PEROBS::Object with the specified ID or nil if not found.
98
+ # @param id [Fixnum or Bignum] ID of the cached PEROBS::ObjectBase
99
+ def object_by_id(id)
100
+ idx = id & @mask
101
+ # The index is just a hash. We still need to check if the object IDs are
102
+ # actually the same before we can return the object.
103
+ if (obj = @writes[idx]) && obj._id == id
104
+ # The object was in the write cache.
105
+ return obj
106
+ elsif (obj = @reads[idx]) && obj._id == id
107
+ # The object was in the read cache.
108
+ return obj
109
+ end
110
+
111
+ nil
112
+ end
113
+
114
+ # Flush all pending writes to the persistant storage back-end.
115
+ def flush
116
+ @writes.each { |w| w._sync if w }
117
+ @writes = ::Array.new(2 ** @bits)
118
+ end
119
+
120
+ # Returns true if the Cache is currently handling a transaction, false
121
+ # otherwise.
122
+ # @return [true/false]
123
+ def in_transaction?
124
+ !@transaction_stack.empty?
125
+ end
126
+
127
+ # Tell the cache to start a new transaction. If no other transaction is
128
+ # active, the write cached is flushed before the transaction is started.
129
+ def begin_transaction
130
+ if @transaction_stack.empty?
131
+ # This is the top-level transaction. Flush the write buffer to save
132
+ # the current state of all objects.
133
+ flush
134
+ else
135
+ @transaction_stack.last.each do |o|
136
+ o._stash(@transaction_stack.length - 1)
137
+ end
138
+ end
139
+ # Push a transaction buffer onto the transaction stack. This buffer will
140
+ # hold a reference to all objects modified during this transaction.
141
+ @transaction_stack.push(::Array.new)
142
+ end
143
+
144
+ # Tell the cache to end the currently active transaction. All write
145
+ # operations of the current transaction will be synced to the storage
146
+ # back-end.
147
+ def end_transaction
148
+ case @transaction_stack.length
149
+ when 0
150
+ raise RuntimeError, 'No ongoing transaction to end'
151
+ when 1
152
+ # All transactions completed successfully. Write all modified objects
153
+ # into the backend storage.
154
+ @transaction_stack.pop.each { |o| o._sync }
155
+ else
156
+ # A nested transaction completed successfully. We add the list of
157
+ # modified objects to the list of the enclosing transaction.
158
+ transactions = @transaction_stack.pop
159
+ # Merge the two lists
160
+ @transaction_stack.push(@transaction_stack.pop + transactions)
161
+ # Ensure that each object is only included once in the list.
162
+ @transaction_stack.last.uniq!
163
+ end
164
+ end
165
+
166
+ # Tell the cache to abort the currently active transaction. All modified
167
+ # objects will be restored from the storage back-end to their state before
168
+ # the transaction started.
169
+ def abort_transaction
170
+ if @transaction_stack.empty?
171
+ raise RuntimeError, 'No ongoing transaction to abort'
172
+ end
173
+ @transaction_stack.pop.each { |o| o._restore(@transaction_stack.length) }
174
+ end
175
+
176
+ # Clear all cached entries. You must call flush before calling this
177
+ # method. Otherwise unwritten objects will be lost.
178
+ def reset
179
+ # The read and write caches are Arrays. We use the _bits_ least
180
+ # significant bits of the PEROBS::ObjectBase ID to select the index in
181
+ # the read or write cache Arrays.
182
+ @reads = ::Array.new(2 ** @bits)
183
+ @writes = ::Array.new(2 ** @bits)
184
+ @transaction_stack = []
185
+ end
186
+
187
+ # Don't include the cache buffers in output of other objects that
188
+ # reference Cache.
189
+ def inspect
190
+ end
191
+
192
+ private
193
+
194
+ def index(obj)
195
+ obj._id & @mask
196
+ end
197
+
198
+ end
199
+
200
+ end
201
+
@@ -0,0 +1,115 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # = DataBase.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/ObjectBase'
36
+
37
+ module PEROBS
38
+
39
+ # Base class for all storage back-ends.
40
+ class DataBase
41
+
42
+ def initialize(serializer = :json)
43
+ @serializer = serializer
44
+ end
45
+
46
+ # Serialize the given object using the object serializer.
47
+ # @param obj [ObjectBase] Object to serialize
48
+ # @return [String] Serialized version
49
+ def serialize(obj)
50
+ begin
51
+ case @serializer
52
+ when :marshal
53
+ Marshal.dump(obj)
54
+ when :json
55
+ obj.to_json
56
+ when :yaml
57
+ YAML.dump(obj)
58
+ end
59
+ rescue => e
60
+ raise RuntimeError,
61
+ "Cannot serialize object as #{@serializer}: #{e.message}"
62
+ end
63
+ end
64
+
65
+ # De-serialize the given String into a Ruby object.
66
+ # @param raw [String]
67
+ # @return [Hash] Deserialized version
68
+ def deserialize(raw)
69
+ begin
70
+ case @serializer
71
+ when :marshal
72
+ Marshal.load(raw)
73
+ when :json
74
+ JSON.parse(raw, :create_additions => true)
75
+ when :yaml
76
+ YAML.load(raw)
77
+ end
78
+ rescue => e
79
+ raise RuntimeError,
80
+ "Cannot de-serialize object with #{@serializer} parser: " +
81
+ e.message
82
+ end
83
+ end
84
+
85
+ # Generate a new unique ID. It uses random numbers between 0 and 2**64 -
86
+ # 1. Deriving classes can overwrite this method. They must implement a
87
+ # include? method.
88
+ # @return [Fixnum or Bignum]
89
+ def new_id
90
+ begin
91
+ # Generate a random number. It's recommended to not store more than
92
+ # 2**62 objects in the same store.
93
+ id = rand(2**64)
94
+ # Ensure that we don't have already another object with this ID.
95
+ end while include?(id)
96
+
97
+ id
98
+ end
99
+
100
+ private
101
+
102
+ # Ensure that we have a directory to store the DB items.
103
+ def ensure_dir_exists(dir)
104
+ unless Dir.exists?(dir)
105
+ begin
106
+ Dir.mkdir(dir)
107
+ rescue IOError => e
108
+ raise IOError, "Cannote create DB directory '#{dir}': #{e.message}"
109
+ end
110
+ end
111
+ end
112
+
113
+ end
114
+
115
+ end
@@ -0,0 +1,171 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # = FileSystemDB.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/ObjectBase'
37
+
38
+ module PEROBS
39
+
40
+ # This class provides a filesytem based database store for objects.
41
+ class FileSystemDB < 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 option is supported:
54
+ # :serializer : Can be :marshal, :json, :yaml
55
+ def initialize(db_name, options = {})
56
+ super(options[:serializer] || :json)
57
+ @db_dir = db_name
58
+
59
+ # Create the database directory if it doesn't exist yet.
60
+ ensure_dir_exists(@db_dir)
61
+ end
62
+
63
+ # Return true if the object with given ID exists
64
+ # @param id [Fixnum or Bignum]
65
+ def include?(id)
66
+ File.exists?(object_file_name(id))
67
+ end
68
+
69
+ # Store the given object into the filesystem.
70
+ # @param obj [Hash] Object as defined by PEROBS::ObjectBase
71
+ def put_object(obj, id)
72
+ File.write(object_file_name(id), serialize(obj))
73
+ end
74
+
75
+ # Load the given object from the filesystem.
76
+ # @param id [Fixnum or Bignum] object ID
77
+ # @return [Hash] Object as defined by PEROBS::ObjectBase
78
+ def get_object(id)
79
+ begin
80
+ raw = File.read(file_name = object_file_name(id))
81
+ rescue => e
82
+ raise RuntimeError, "Error in #{file_name}: #{e.message}"
83
+ end
84
+ deserialize(raw)
85
+ end
86
+
87
+ # This method must be called to initiate the marking process.
88
+ def clear_marks
89
+ @mark_start = Time.now
90
+ # The filesystem stores access times with second granularity. We need to
91
+ # wait 1 sec. to ensure that all marks are noticeable.
92
+ sleep(1)
93
+ end
94
+
95
+ # Permanently delete all objects that have not been marked. Those are
96
+ # orphaned and are no longer referenced by any actively used object.
97
+ def delete_unmarked_objects
98
+ Dir.glob(File.join(@db_dir, '*')) do |dir|
99
+ next unless Dir.exists?(dir)
100
+
101
+ Dir.glob(File.join(dir, '*')) do |file|
102
+ if File.atime(file) <= @mark_start
103
+ File.delete(file)
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ # Mark an object.
110
+ # @param id [Fixnum or Bignum] ID of the object to mark
111
+ def mark(id)
112
+ FileUtils.touch(object_file_name(id))
113
+ end
114
+
115
+ # Check if the object is marked.
116
+ # @param id [Fixnum or Bignum] ID of the object to check
117
+ def is_marked?(id)
118
+ File.atime(object_file_name(id)) > @mark_start
119
+ end
120
+
121
+ # Check if the stored object is syntactically correct.
122
+ # @param id [Fixnum/Bignum] Object ID
123
+ # @param repair [TrueClass/FalseClass] True if an repair attempt should be
124
+ # made.
125
+ # @return [TrueClass/FalseClass] True if the object is OK, otherwise
126
+ # false.
127
+ def check(id, repair)
128
+ file_name = object_file_name(id)
129
+ unless File.exists?(file_name)
130
+ $stderr.puts "Object file for ID #{id} does not exist"
131
+ return false
132
+ end
133
+
134
+ begin
135
+ get_object(id)
136
+ rescue => e
137
+ $stderr.puts "Cannot read object file #{file_name}: #{e.message}"
138
+ return false
139
+ end
140
+
141
+ true
142
+ end
143
+
144
+ private
145
+
146
+ # Ensure that we have a directory to store the DB items.
147
+ def ensure_dir_exists(dir)
148
+ unless Dir.exists?(dir)
149
+ begin
150
+ Dir.mkdir(dir)
151
+ rescue IOError => e
152
+ raise IOError, "Cannote create DB directory '#{dir}': #{e.message}"
153
+ end
154
+ end
155
+ end
156
+
157
+ # Determine the file name to store the object. The object ID determines
158
+ # the directory and file name inside the store.
159
+ # @param id [Fixnum or Bignum] ID of the object
160
+ def object_file_name(id)
161
+ hex_id = "%016X" % id
162
+ dir = hex_id[0..1]
163
+ ensure_dir_exists(File.join(@db_dir, dir))
164
+
165
+ File.join(@db_dir, dir, hex_id + @@Extensions[@serializer])
166
+ end
167
+
168
+ end
169
+
170
+ end
171
+
@@ -0,0 +1,175 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # = Hash.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 'perobs/ObjectBase'
29
+
30
+ module PEROBS
31
+
32
+ # A Hash that is transparently persisted in the back-end storage. It is very
33
+ # similar to the Ruby built-in Hash class but has some additional
34
+ # limitations. The hash key must always be a String.
35
+ class Hash < ObjectBase
36
+
37
+ # Create a new PersistentHash object.
38
+ # @param store [Store] The Store this hash is stored in
39
+ # @param default [Any] The default value that is returned when no value is
40
+ # stored for a specific key.
41
+ def initialize(store, default = nil)
42
+ super(store)
43
+ @default = nil
44
+ @data = {}
45
+ end
46
+
47
+ # Retrieves the value object corresponding to the
48
+ # key object. If not found, returns the default value.
49
+ def [](key)
50
+ #unless key.is_a?(String)
51
+ # raise ArgumentError, 'The Hash key must be of type String'
52
+ #end
53
+ _dereferenced(@data.include?(key) ? @data[key] : @default)
54
+ end
55
+
56
+ # Associates the value given by value with the key given by key.
57
+ # @param key [String] The key
58
+ # @param value [Any] The value to store
59
+ def []=(key, value)
60
+ #unless key.is_a?(String)
61
+ # raise ArgumentError, 'The Hash key must be of type String'
62
+ #end
63
+ @data[key] = _referenced(value)
64
+ @store.cache.cache_write(self)
65
+
66
+ value
67
+ end
68
+
69
+ # Equivalent to Hash::clear
70
+ def clear
71
+ @store.cache.cache_write(self)
72
+ @data.clear
73
+ end
74
+
75
+ # Equivalent to Hash::delete
76
+ def delete(key)
77
+ @store.cache.cache_write(self)
78
+ @data.delete(key)
79
+ end
80
+
81
+ # Equivalent to Hash::delete_if
82
+ def delete_if
83
+ @store.cache.cache_write(self)
84
+ @data.delete_if do |k, v|
85
+ yield(k, _dereferenced(v))
86
+ end
87
+ end
88
+
89
+ # Equivalent to Hash::each
90
+ def each
91
+ @data.each do |k, v|
92
+ yield(k, _dereferenced(v))
93
+ end
94
+ end
95
+
96
+ # Equivalent to Hash::each_key
97
+ def each_key
98
+ @data.each_key { |k| yield(k) }
99
+ end
100
+
101
+ # Equivalent to Hash::each_value
102
+ def each_value
103
+ @data.each_value do |v|
104
+ yield(_dereferenced(v))
105
+ end
106
+ end
107
+
108
+ # Equivalent to Hash::empty?
109
+ def emtpy?
110
+ @data.empty?
111
+ end
112
+
113
+ # Equivalent to Hash::has_key?
114
+ def has_key?(key)
115
+ @data.has_key?(key)
116
+ end
117
+ alias include? has_key?
118
+ alias key? has_key?
119
+ alias member? has_key?
120
+
121
+ # Equivalent to Hash::keys
122
+ def keys
123
+ @data.keys
124
+ end
125
+
126
+ # Equivalent to Hash::length
127
+ def length
128
+ @data.length
129
+ end
130
+ alias size length
131
+
132
+ # Equivalent to Hash::map
133
+ def map
134
+ @data.map do |k, v|
135
+ yield(k, _dereferenced(v))
136
+ end
137
+ end
138
+
139
+ # Equivalent to Hash::values
140
+ def values
141
+ @data.values.map { |v| _dereferenced(v) }
142
+ end
143
+
144
+ # Return a list of all object IDs of all persistend objects that this Hash
145
+ # is referencing.
146
+ # @return [Array of Fixnum or Bignum] IDs of referenced objects
147
+ def _referenced_object_ids
148
+ @data.each_value.select { |v| v && v.is_a?(POReference) }.map { |o| o.id }
149
+ end
150
+
151
+ # This method should only be used during store repair operations. It will
152
+ # delete all referenced to the given object ID.
153
+ # @param id [Fixnum/Bignum] targeted object ID
154
+ def _delete_reference_to_id(id)
155
+ @data.delete_if { |k, v| v && v.is_a?(POReference) && v.id == id }
156
+ end
157
+
158
+ # Restore the persistent data from a single data structure.
159
+ # This is a library internal method. Do not use outside of this library.
160
+ # @param data [Hash] the actual Hash object
161
+ # @private
162
+ def _deserialize(data)
163
+ @data = data
164
+ end
165
+
166
+ private
167
+
168
+ def _serialize
169
+ @data
170
+ end
171
+
172
+ end
173
+
174
+ end
175
+