Class: Rod::AbstractDatabase
- Inherits:
-
Object
- Object
- Rod::AbstractDatabase
- Includes:
- Utils, Singleton
- Defined in:
- lib/rod/abstract_database.rb
Overview
This class implements the database abstraction, i.e. it is a mediator between some model (a set of classes) and the generated C code, implementing the data storage functionality.
Direct Known Subclasses
Constant Summary collapse
- @@rod_development_mode =
This flag indicates, if Database and Model works in development mode, i.e. the dynamically loaded library has a unique, different id each time the rod library is used.
false
Instance Attribute Summary collapse
-
#metadata ⇒ Object
readonly
The meta-data of the DataBase.
-
#path ⇒ Object
readonly
The path which the database instance is located on.
Class Method Summary collapse
-
.development_mode ⇒ Object
Reader of the
rod_development_mode
flag. -
.development_mode=(value) ⇒ Object
Writer of the
rod_development_mode
flag.
Instance Method Summary collapse
-
#add_class(klass) ⇒ Object
Adds the
klass
to the set of classes linked with this database. -
#allocate_join_elements(size) ⇒ Object
Allocates space for join elements.
-
#allocate_polymorphic_join_elements(size) ⇒ Object
Allocates space for polymorphic join elements.
-
#clear_cache ⇒ Object
Clears the cache of the database.
-
#close_database(purge_classes = false, skip_indices = false) ⇒ Object
Closes the database.
-
#count(klass) ⇒ Object
Returns the number of objects for given
klass
. -
#create_database(path) ⇒ Object
Creates the database at specified
path
, which allows for Rod::Model#store calls to be performed. -
#fast_intersection_size(first_offset, first_length, second_offset, second_length) ⇒ Object
Computes fast intersection for sorted join elements.
-
#initialize ⇒ AbstractDatabase
constructor
Initializes the classes linked with this database and the handler.
-
#join_index(offset, index) ⇒ Object
Returns join index with
index
andoffset
. -
#legacy_class?(klass) ⇒ Boolean
Returns true if the
klass
is a legacy class, i.e. -
#migrate_database(path) ⇒ Object
Migrates the database, which is located at
path
. -
#open_database(path, options = {:readonly => true}) ⇒ Object
Opens the database at
path
withoptions
. -
#opened? ⇒ Boolean
Returns whether the database is opened.
-
#polymorphic_join_class(offset, index) ⇒ Object
Returns polymorphic join class id with
index
andoffset
. -
#polymorphic_join_index(offset, index) ⇒ Object
Returns polymorphic join index with
index
andoffset
. -
#print_layout ⇒ Object
Prints the layout of the pages in memory and other internal data of the model.
-
#print_system_error ⇒ Object
Prints the last error of system call.
-
#read_string(length, offset) ⇒ Object
Returns the string of given
length
starting at givenoffset
. -
#readonly_data? ⇒ Boolean
The DB open mode.
-
#referenced_objects ⇒ Object
“Stack” of objects which are referenced by other objects during store, but are not yet stored.
-
#remove_class(klass) ⇒ Object
Remove the
klass
from the set of classes linked with this database. -
#set_count(klass, value) ⇒ Object
Sets the number of objects for given
klass
. -
#set_join_element_id(offset, index, object_id) ⇒ Object
Sets the
object_id
of the join element withoffset
andindex
. -
#set_page_count(klass, value) ⇒ Object
Sets the number of pages allocated for given
klass
. -
#set_polymorphic_join_element_id(offset, index, object_id, class_id) ⇒ Object
Sets the
object_id
andclass_id
of the polymorphic join element withoffset
andindex
. -
#set_string(value) ⇒ Object
Stores the string in the DB.
-
#special_class?(klass) ⇒ Boolean
Returns true if the class is one of speciall classes (JoinElement, PolymorphicJoinElement, StringElement).
-
#store(klass, object) ⇒ Object
Store the object in the database.
Methods included from Utils
#remove_file, #remove_files, #remove_files_but, #report_progress
Constructor Details
#initialize ⇒ AbstractDatabase
Initializes the classes linked with this database and the handler.
30 31 32 33 |
# File 'lib/rod/abstract_database.rb', line 30 def initialize @classes ||= self.special_classes @handler = nil end |
Instance Attribute Details
#metadata ⇒ Object (readonly)
The meta-data of the DataBase.
19 20 21 |
# File 'lib/rod/abstract_database.rb', line 19 def @metadata end |
#path ⇒ Object (readonly)
The path which the database instance is located on.
22 23 24 |
# File 'lib/rod/abstract_database.rb', line 22 def path @path end |
Class Method Details
.development_mode ⇒ Object
Reader of the rod_development_mode
flag.
41 42 43 |
# File 'lib/rod/abstract_database.rb', line 41 def self.development_mode @@rod_development_mode end |
.development_mode=(value) ⇒ Object
Writer of the rod_development_mode
flag.
36 37 38 |
# File 'lib/rod/abstract_database.rb', line 36 def self.development_mode=(value) @@rod_development_mode = value end |
Instance Method Details
#add_class(klass) ⇒ Object
Adds the klass
to the set of classes linked with this database.
287 288 289 |
# File 'lib/rod/abstract_database.rb', line 287 def add_class(klass) @classes << klass unless @classes.include?(klass) end |
#allocate_join_elements(size) ⇒ Object
Allocates space for join elements.
339 340 341 342 |
# File 'lib/rod/abstract_database.rb', line 339 def allocate_join_elements(size) raise DatabaseError.new("Readonly database.") if readonly_data? _allocate_join_elements(size,@handler) end |
#allocate_polymorphic_join_elements(size) ⇒ Object
Allocates space for polymorphic join elements.
333 334 335 336 |
# File 'lib/rod/abstract_database.rb', line 333 def allocate_polymorphic_join_elements(size) raise DatabaseError.new("Readonly database.") if readonly_data? _allocate_polymorphic_join_elements(size,@handler) end |
#clear_cache ⇒ Object
Clears the cache of the database.
259 260 261 |
# File 'lib/rod/abstract_database.rb', line 259 def clear_cache classes.each{|c| c.cache.clear } end |
#close_database(purge_classes = false, skip_indices = false) ⇒ Object
Closes the database.
If the purge_classes
flag is set to true, the information about the classes linked with this database is removed. This is important for testing, when classes with same names have different definitions.
If the skip_indeces
flat is set to true, the indices are not written.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/rod/abstract_database.rb', line 233 def close_database(purge_classes=false,skip_indices=false) raise DatabaseError.new("Database not opened.") unless opened? unless readonly_data? unless referenced_objects.select{|k, v| not v.empty?}.size == 0 raise DatabaseError.new("Not all associations have been stored: #{referenced_objects.size} objects") end unless skip_indices self.classes.each do |klass| klass.indexed_properties.each do |property| property.index.save end end end end _close(@handler) @handler = nil # clear cached data self.clear_cache if purge_classes @classes = self.special_classes end end |
#count(klass) ⇒ Object
Returns the number of objects for given klass
.
362 363 364 |
# File 'lib/rod/abstract_database.rb', line 362 def count(klass) send("_#{klass.struct_name}_count",@handler) end |
#create_database(path) ⇒ Object
Creates the database at specified path
, which allows for Rod::Model#store calls to be performed.
The database is created for all classes, which have this database configured via Rod::Model#database_class call (this configuration is by default inherited in subclasses, so it have to be called only in the root class of given model).
WARNING: all files in the DB directory are removed during DB creation!
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/rod/abstract_database.rb', line 68 def create_database(path) if block_given? create_database(path) begin yield ensure close_database end else raise DatabaseError.new("Database already opened.") if opened? @readonly = false @path = canonicalize_path(path) if File.exist?(@path) remove_file("#{@path}database.yml") else FileUtils.mkdir_p(@path) end self.classes.each do |klass| klass.send(:build_structure) remove_file(klass.path_for_data(@path)) klass.indexed_properties.each do |property| property.index.destroy end next if special_class?(klass) remove_files_but(klass.inline_library) end remove_files(self.inline_library) generate_c_code(@path, classes) remove_files_but(self.inline_library) @metadata = {} @metadata["Rod"] = {} @metadata["Rod"][:created_at] = Time.now @handler = _init_handler(@path) _create(@handler) end end |
#fast_intersection_size(first_offset, first_length, second_offset, second_length) ⇒ Object
Computes fast intersection for sorted join elements.
345 346 347 348 |
# File 'lib/rod/abstract_database.rb', line 345 def fast_intersection_size(first_offset,first_length,second_offset,second_length) _fast_intersection_size(first_offset,first_length,second_offset, second_length,@handler) end |
#join_index(offset, index) ⇒ Object
Returns join index with index
and offset
.
300 301 302 |
# File 'lib/rod/abstract_database.rb', line 300 def join_index(offset, index) _join_element_index(offset, index, @handler) end |
#legacy_class?(klass) ⇒ Boolean
Returns true if the klass
is a legacy class, i.e. a class generated during migration used to access the legacy data.
282 283 284 |
# File 'lib/rod/abstract_database.rb', line 282 def legacy_class?(klass) klass.name =~ LEGACY_RE end |
#migrate_database(path) ⇒ Object
Migrates the database, which is located at path
. The old version of the DB is placed at path
/backup.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/rod/abstract_database.rb', line 171 def migrate_database(path) raise DatabaseError.new("Database already opened.") if opened? @readonly = false @path = canonicalize_path(path) @metadata = create_legacy_classes FileUtils.mkdir_p(@path + BACKUP_PREFIX) # Copy special classes data. special_classes.each do |klass| file = klass.path_for_data(@path) puts "Copying #{file} to #{@path + BACKUP_PREFIX}" if $ROD_DEBUG FileUtils.cp(file,@path + BACKUP_PREFIX) end Dir.glob(@path + "*").each do |file| # Don't move the directory itself and speciall classes data. unless file.to_s == @path + BACKUP_PREFIX[0..-2] || special_classes.map{|c| c.path_for_data(@path)}.include?(file.to_s) puts "Moving #{file} to #{@path + BACKUP_PREFIX}" if $ROD_DEBUG FileUtils.mv(file,@path + BACKUP_PREFIX) end end remove_files(self.inline_library) self.classes.each do |klass| klass.send(:build_structure) end generate_c_code(@path, self.classes) @handler = _init_handler(@path) self.classes.each do |klass| next unless special_class?(klass) or legacy_class?(klass) = @metadata[klass.name] set_count(klass,[:count]) file_size = File.new(klass.path_for_data(@path)).size unless file_size % _page_size == 0 raise DatabaseError.new("Size of data file of #{klass} is invalid: #{file_size}") end set_page_count(klass,file_size / _page_size) next unless legacy_class?(klass) new_class = klass.name.sub(LEGACY_RE,"").constantize set_count(new_class,[:count]) pages = ([:count] * new_class.struct_size / _page_size.to_f).ceil set_page_count(new_class,pages) end _open(@handler) self.classes.each do |klass| next unless legacy_class?(klass) klass.migrate @classes.delete(klass) end path_with_date = @path + BACKUP_PREFIX[0..-2] + "_" + Time.new.strftime("%Y_%m_%d_%H_%M_%S") + "/" puts "Moving #{@path + BACKUP_PREFIX} to #{path_with_date}" if $ROD_DEBUG FileUtils.mv(@path + BACKUP_PREFIX,path_with_date) close_database end |
#open_database(path, options = {:readonly => true}) ⇒ Object
Opens the database at path
with options
. This allows for Rod::Model.count, Rod::Model.each, and similar calls. Options:
-
:readonly
- no modifiaction (append of models and has many association) is allowed (defaults totrue
) -
:generate
- value could be true or a module. If present, generates the classes from the database metadata. If module given, the classes are generated withing the module.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/rod/abstract_database.rb', line 120 def open_database(path,={:readonly => true}) raise DatabaseError.new("Database already opened.") if opened? = () @readonly = [:readonly] @path = canonicalize_path(path) @metadata = if [:generate] module_instance = ([:generate] == true ? Object : [:generate]) generate_classes(module_instance) end self.classes.each do |klass| klass.send(:build_structure) next if special_class?(klass) if [:generate] && module_instance != Object remove_files_but(klass.inline_library) end end generate_c_code(@path, self.classes) @handler = _init_handler(@path) = @metadata.dup .delete("Rod") self.classes.each do |klass| = .delete(klass.name) if .nil? # new class next end unless klass.compatible?() || [:generate] || [:migrate] raise IncompatibleVersion. new("Incompatible definition of '#{klass.name}' class.\n" + "Database and runtime versions are different:\n " + klass.difference(). map{|e1,e2| "DB: #{e1} vs. RT: #{e2}"}.join("\n ")) end set_count(klass,[:count]) file_size = File.new(klass.path_for_data(@path)).size unless file_size % _page_size == 0 raise DatabaseError.new("Size of data file of #{klass} is invalid: #{file_size}") end set_page_count(klass,file_size / _page_size) end if .size > 0 @handler = nil raise DatabaseError.new("The following classes are missing in runtime:\n - " + .keys.join("\n - ")) end _open(@handler) end |
#opened? ⇒ Boolean
Returns whether the database is opened.
50 51 52 |
# File 'lib/rod/abstract_database.rb', line 50 def opened? not @handler.nil? end |
#polymorphic_join_class(offset, index) ⇒ Object
Returns polymorphic join class id with index
and offset
. This is the class_id (name_hash) of the object referenced via a polymorphic has many association for one instance.
314 315 316 |
# File 'lib/rod/abstract_database.rb', line 314 def polymorphic_join_class(offset, index) _polymorphic_join_element_class(offset, index, @handler) end |
#polymorphic_join_index(offset, index) ⇒ Object
Returns polymorphic join index with index
and offset
. This is the rod_id of the object referenced via a polymorphic has many association for one instance.
307 308 309 |
# File 'lib/rod/abstract_database.rb', line 307 def polymorphic_join_index(offset, index) _polymorphic_join_element_index(offset, index, @handler) end |
#print_layout ⇒ Object
Prints the layout of the pages in memory and other internal data of the model.
387 388 389 390 |
# File 'lib/rod/abstract_database.rb', line 387 def print_layout raise DatabaseError.new("Database not opened.") unless opened? _print_layout(@handler) end |
#print_system_error ⇒ Object
Prints the last error of system call.
393 394 395 |
# File 'lib/rod/abstract_database.rb', line 393 def print_system_error _print_system_error end |
#read_string(length, offset) ⇒ Object
Returns the string of given length
starting at given offset
.
351 352 353 |
# File 'lib/rod/abstract_database.rb', line 351 def read_string(length, offset) value = _read_string(length, offset, @handler) end |
#readonly_data? ⇒ Boolean
The DB open mode.
55 56 57 |
# File 'lib/rod/abstract_database.rb', line 55 def readonly_data? @readonly end |
#referenced_objects ⇒ Object
“Stack” of objects which are referenced by other objects during store, but are not yet stored.
269 270 271 |
# File 'lib/rod/abstract_database.rb', line 269 def referenced_objects @referenced_objects ||= {} end |
#remove_class(klass) ⇒ Object
Remove the klass
from the set of classes linked with this database.
292 293 294 295 296 297 |
# File 'lib/rod/abstract_database.rb', line 292 def remove_class(klass) unless @classes.include?(klass) raise DatabaseError.new("Class #{klass} is not linked with #{self}!") end @classes.delete(klass) end |
#set_count(klass, value) ⇒ Object
Sets the number of objects for given klass
.
367 368 369 |
# File 'lib/rod/abstract_database.rb', line 367 def set_count(klass,value) send("_#{klass.struct_name}_count=",@handler,value) end |
#set_join_element_id(offset, index, object_id) ⇒ Object
Sets the object_id
of the join element with offset
and index
.
319 320 321 322 |
# File 'lib/rod/abstract_database.rb', line 319 def set_join_element_id(offset,index,object_id) raise DatabaseError.new("Readonly database.") if readonly_data? _set_join_element_offset(offset, index, object_id, @handler) end |
#set_page_count(klass, value) ⇒ Object
Sets the number of pages allocated for given klass
.
372 373 374 |
# File 'lib/rod/abstract_database.rb', line 372 def set_page_count(klass,value) send("_#{klass.struct_name}_page_count=",@handler,value) end |
#set_polymorphic_join_element_id(offset, index, object_id, class_id) ⇒ Object
Sets the object_id
and class_id
of the polymorphic join element with offset
and index
.
326 327 328 329 330 |
# File 'lib/rod/abstract_database.rb', line 326 def set_polymorphic_join_element_id(offset,index,object_id,class_id) raise DatabaseError.new("Readonly database.") if readonly_data? _set_polymorphic_join_element_offset(offset, index, object_id, class_id, @handler) end |
#set_string(value) ⇒ Object
Stores the string in the DB.
356 357 358 359 |
# File 'lib/rod/abstract_database.rb', line 356 def set_string(value) raise DatabaseError.new("Readonly database.") if readonly_data? _set_string(value,@handler) end |
#special_class?(klass) ⇒ Boolean
Returns true if the class is one of speciall classes (JoinElement, PolymorphicJoinElement, StringElement).
275 276 277 |
# File 'lib/rod/abstract_database.rb', line 275 def special_class?(klass) self.special_classes.include?(klass) end |
#store(klass, object) ⇒ Object
Store the object in the database.
377 378 379 380 381 382 |
# File 'lib/rod/abstract_database.rb', line 377 def store(klass,object) raise DatabaseError.new("Readonly database.") if readonly_data? if object.new? send("_store_" + klass.struct_name,object,@handler) end end |