class GxG::Database::PersistedHash
Public Class Methods
new(settings = {})
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 735 def initialize(settings = {}) # mount element: {:database => <database>, :uuid => <uuid>, :credential => <uuid>, :delegate => nil/<uuid>, :parent => nil/<an-object>} # create element: {:database => <database>, :credential => <uuid>, :delegate => nil/<uuid>, :parent => nil/<an-object>} # if settings.is_a?(::Hash) unless ::GxG::valid_uuid?(settings[:credential]) raise ArgumentError, "You must supply a valid credential UUID (37 character limit) as a String or Symbol" end @quick = settings[:quick] @credential = settings[:credential].to_sym @reservation = nil @delegate = settings[:delegate] if settings[:parent].is_any?([::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray]) @parent = settings[:parent] else @parent = nil end if settings[:database].is_a?(::GxG::Database::Database) if settings[:database].open?() # note - rewrite. if create then as subset, eventually will always mount if all checks out. the_uuid = settings[:uuid] if the_uuid.is_a?(::Symbol) the_uuid = the_uuid.to_s end # TODO: validate uuid pattern if settings[:title].is_a?(::String) the_title = settings[:title] else the_title = ("Untitled PersistedHash " << (settings[:database].connector()[:element_hash].count.to_s)) end @format = settings[:format] unless the_uuid # without specifying a uuid, the implied intent is to create a new PersistedHash the_uuid = ::GxG::uuid_generate() while ((settings[:database].connector()[:element_hash].filter({:uuid => the_uuid}).count > 0) or (settings[:database].connector()[:element_array].filter({:uuid => the_uuid}).count > 0)) the_uuid = ::GxG::uuid_generate() end # creat blank hash record new_dbid = settings[:database].connector()[:element_hash].insert({:uuid => (the_uuid), :title => (the_title), :format => (@format.to_s)}) # @db_address = {:database => (settings[:database]), :table => :element_hash, :dbid => (new_dbid)} if @parent @permission = @parent.get_permissions() # settings[:database].enforce_permission_policy({:action => :extend, :credential => @credential, :source => settings[:parent], :destination => self}) # settings[:database].extend_element_permissions(settings[:parent].db_address()[:table], settings[:parent].db_address()[:dbid], :element_hash, new_dbid) else # create raw permissions for this element @permission = {:execute => false, :rename => true, :move => true, :destroy => true, :create => true, :write => true, :read => true} # settings[:database].assign_element_permission(:element_hash, new_dbid, @credential, {:execute => false, :rename => true, :move => true, :destroy => true, :create => true, :write => true, :read => true}) # settings[:database].enforce_permission_policy({:action => :extend, :credential => @credential, :source => self, :destination => self}) end settings[:database].assign_element_permission(:element_hash, new_dbid, @credential, @permission) end # mount according to uuid if the_uuid # mount record = settings[:database].connector()[:element_hash].filter({:uuid => (the_uuid)}).first if record @uuid = the_uuid.to_sym @title = record[:title].to_s @version = record[:version].to_f @db_address = {:database => (settings[:database]), :table => :element_hash, :dbid => (record[:dbid])} if record[:format].to_s.size > 0 @format = record[:format].to_sym end @property_links = {} # unless @permission @permission = @db_address[:database].effective_element_permission(@db_address[:table], @db_address[:dbid], @credential) end # @read_reservation = @db_address[:database].reserve_element_locks(@credential, (@db_address[:database].element_manifest(@db_address[:table], @db_address[:dbid])), :read) # load_property_links # else raise Exception, "Unable to mount the PersistedHash record" end else raise Exception, "Unable to mount the PersistedHash record" end # else raise Exception, "Database not available for storage" end # else raise Exception, "Invalid Database object" end # else raise ArgumentError, "You must supply a hash of details" end # end
Public Instance Methods
[](key=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1311 def [](key=nil) result = nil # if key exists # if key is not loaded, load element (if String or ByteArray) if self.alive?() if @db_address[:database].open?() if key.is_a?(::Symbol) if key.to_s.size > 256 log_warn({:warning => "Attempted oversized key usage (limited to 256 characters), truncated #{key.inspect} to #{key.to_s[(0..255)].to_sym.inspect}"}) key = key.to_s[(0..255)].to_sym end property_key = key.to_s.to_sym if @property_links[(property_key)] if @property_links[(property_key)][:content].is_any?(::String, ::GxG::ByteArray) unless @property_links[(property_key)][:loaded] == true # ### Load Base Element contents from the database. if @property_links[(property_key)][:content].is_a?(::String) if @property_links[(property_key)][:record][:element_text_uuid].to_s.size > 0 @db_address[:database].connector()[:text_page].filter({:parent_uuid => @property_links[(property_key)][:record][:element_text_uuid].to_s}).order(:ordinal).each do |text_page| @property_links[(property_key)][:content] << text_page[:content] end else @property_links[(property_key)][:content] = @property_links[(property_key)][:record][:element_text].to_s end else @db_address[:database].connector()[:binary_page].filter({:parent_uuid => @property_links[(property_key)][:record][:element_binary_uuid].to_s}).order(:ordinal).each do |binary_page| @property_links[(property_key)][:content] << binary_page[:content] end end @property_links[(property_key)][:loaded] = true end end result = @property_links[(property_key)][:content] end else raise ArgumentError, "You must specify with a Symbol, not a #{key.class.inspect}" end else raise Exception, "Database not available" end else raise Exception, "Attempted to access a defunct structure" end result end
[]=(key=nil, value=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 980 def []=(key=nil, value=nil) # Only works with Symbols. unless self.alive?() raise Exception, "Attempted to alter a defunct structure" end unless @db_address[:database].open?() raise Exception, "Database not available" end result = nil if key.is_a?(::Symbol) if key.to_s.size > 256 log_warn({:warning => "Attempted oversized key usage (limited to 256 characters), truncated #{key.inspect} to #{key.to_s[(0..255)].to_sym.inspect}"}) key = key.to_s[(0..255)].to_sym end property_key = key.to_s.to_sym # Review : rewrite ???? if value.is_any?(::GxG::Database::Database::valid_field_classes()) || value.is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray, ::Hash, ::Array) unless self.write_reserved?() self.get_reservation() end if self.write_reserved?() # Further screen value provided: # ### Check provided PersistedHashes and PersistedArrays if value.is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) unless value.db_address() raise Exception, "The structure you are attaching is Deactivated (not supported)." end if value.structure_attached?() # Review : experimental - auto-detach from prior structural relationship and reassign here. # raise Exception, "Cross linked structures not supported" unless value.write_reserved?() value.get_reservation() end if value.write_reserved?() value.structure_detach else raise Exception, "Unable to secure write-reservation for this structural change." end end value.search do |the_value, the_selector, the_container| if the_value.is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) unless the_value.db_address() raise Exception, "The structure you are attaching has a Deactivated item within it (not supported)." end end end end # ### Check provided Hashes and Arrays if value.is_any?(::Hash, ::Array) value.search do |the_value, the_selector, the_container| if the_value.is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) unless the_value.db_address() raise Exception, "The structure you are attaching has a Deactivated item within it (not supported)." end if @db_address[:database].structure_attached?(the_value.uuid.to_s) && the_container.is_any?(::Hash, ::Array) raise Exception, "The structure you are attaching has a cross-linked structure within it (cross linked structures not supported)." end end unless the_value.is_any?(::GxG::Database::Database::valid_field_classes()) || the_value.is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray, ::Hash, ::Array) raise Exception, "The structure you are attaching has an unpersistable item within it (not supported)." end end end # ### Property Exists? if @property_links[(key.to_s.to_sym)] # set property to value operation = :set_value else if @format raise Exception, "Formatted - the structure cannot be altered" else # add property : value pair operation = :add_value end end # ### Prepare new value new_value = { :linkid => nil, :content => nil, :loaded => false, :state => 0, :record => { :parent_uuid => @uuid.to_s, :property => key.to_s, :ordinal => 0, :version => 0.0, :element => "element_boolean", :element_boolean => -1, :element_integer => 0, :element_float => 0.0, :element_bigdecimal => ::BigDecimal.new("0.0"), :element_datetime => ::DateTime.now, :time_offset => 0.0, :time_prior => 0.0, :time_after => 0.0, :length => 0, :element_text => "", :element_text_uuid => "", :element_binary_uuid => "", :element_array_uuid => "", :element_hash_uuid => "" } } # if value.is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) # ### Assimilate PersistedHash or PersistedArray new_value[:content] = value new_value[:loaded] = true new_value[:state] = new_value[:content].hash # Note: version is sync'd here, but don't rely upon property version with linked structures, but use structure's version method. new_value[:record][:version] = new_value[:content].version() if value.is_a?(::GxG::Database::PersistedHash) new_value[:record][:element] = "element_hash" new_value[:record][:element_hash_uuid] = value.uuid.to_s else new_value[:record][:element] = "element_array" new_value[:record][:element_array_uuid] = value.uuid.to_s end else # ### Persist Hashes and Arrays if value.is_any?(::Hash, ::Array) new_value[:content] = @db_address[:database].iterative_persist(value,@credential) new_value[:loaded] = true new_value[:state] = new_value[:content].hash # Note: version is sync'd here, but don't rely upon property version with linked structures, but use structure's version method. new_value[:record][:version] = new_value[:content].version() if value.is_a?(::Hash) new_value[:record][:element] = "element_hash" new_value[:record][:element_hash_uuid] = new_value[:content].uuid.to_s else new_value[:record][:element] = "element_array" new_value[:record][:element_array_uuid] = new_value[:content].uuid.to_s end else # ### Persist Base Element Values new_value[:record][:element] = ::GxG::Database::Database::element_table_for_instance(value).to_s case new_value[:record][:element] when "element_boolean" if value.class == ::NilClass new_value[:record][:element_boolean] = -1 new_value[:content] = nil end if value.class == ::FalseClass new_value[:record][:element_boolean] = 0 new_value[:content] = false end if value.class == ::TrueClass new_value[:record][:element_boolean] = 1 new_value[:content] = true end when "element_integer" new_value[:record][:element_integer] = value new_value[:content] = value when "element_float" new_value[:record][:element_float] = value new_value[:content] = value when "element_bigdecimal" new_value[:record][:element_bigdecimal] = ::BigDecimal.new(value.to_s) new_value[:content] = new_value[:record][:element_bigdecimal] when "element_datetime" new_value[:record][:element_datetime] = value new_value[:content] = value when "element_text" if value.size > 256 # Note: be sure to keep text version & length sync'd with linked text element record if operation == :set_value new_value[:record][:version] = @property_links[(property_key)][:record][:version] new_value[:record][:element_text_uuid] = @property_links[(property_key)][:record][:element_text_uuid] end new_value[:record][:length] = value.size else new_value[:record][:element_text] = value new_value[:record][:length] = value.size end new_value[:content] = value when "element_binary" # Note: be sure to keep version & length sync'd with linked binary element record if operation == :set_value new_value[:record][:version] = @property_links[(property_key)][:record][:version] new_value[:record][:element_binary_uuid] = @property_links[(property_key)][:record][:element_binary_uuid] end new_value[:record][:length] = value.size new_value[:content] = value else raise Exception, "Unable to map an element type for value: #{value.inspect}" end end end # ### Commit Changes case operation when :set_value # Note: Set In-memory value only, don't save unless you have to. if new_value[:content].is_a?(@property_links[(property_key)][:content].class) || ([true, false, nil].include?(new_value[:content]) && [true, false, nil].include?(@property_links[(property_key)][:content])) # Replace value directly, but don't save yet unless you have to (:state refers to the last 'loaded-in-from-db' state of the data). new_value[:linkid] = @property_links[(property_key)][:linkid] if new_value[:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) new_value[:record][:version] = new_value[:content].version() else new_value[:record][:version] = (((@property_links[(property_key)][:record][:version].to_f + 0.0001) * 10000.0).to_i.to_f / 10000.0) end new_value[:record][:ordinal] = @property_links[(property_key)][:record][:ordinal] new_value[:loaded] = true new_value[:state] = @property_links[(property_key)][:state] # ### Did string grow or shrink crossing the threshold?? if new_value[:content].is_a?(::String) if (@property_links[(property_key)][:content].size > 256 && new_value[:content].size <= 256) # shrank - delete text object and pages as well as property record (slow) property_destroy(property_key) @property_links[(property_key)] = new_value property_write(property_key) else if (@property_links[(property_key)][:content].size <= 256 && new_value[:content].size > 256) # grew - create text object and pages (a little less slow) @property_links[(property_key)] = new_value property_write(property_key) else # N/A @property_links[(property_key)] = new_value end end else # N/A @property_links[(property_key)] = new_value end if new_value[:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) # Note: this ensures attachment to this object property_write(property_key) end # else # Other class value being substituted. if new_value[:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) new_value[:record][:version] = new_value[:content].version() else new_value[:record][:version] = (((@property_links[(property_key)][:record][:version].to_f + 0.0001) * 10000.0).to_i.to_f / 10000.0) end new_value[:record][:ordinal] = @property_links[(property_key)][:record][:ordinal] new_value[:loaded] = true # Destroy old property record on db, and associated linked objects. # Review : this is slow - look into optimizations here. if @property_links[(property_key)][:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray, ::GxG::ByteArray, ::String) if @property_links[(property_key)][:content].is_a?(::String) if (@property_links[(property_key)][:content].size > 256 && new_value[:content].to_s.size <= 256) || (@property_links[(property_key)][:content].size <= 256 && new_value[:content].to_s.size > 256) property_destroy(property_key) end else property_destroy(property_key) end end # @property_links[(property_key)] = new_value property_write(property_key) end # when :add_value # Note: set in-memory value and save. if new_value[:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) new_value[:record][:version] = new_value[:content].version() else new_value[:record][:version] = 0.0 end new_value[:record][:ordinal] = @property_links.keys.size new_value[:loaded] = true # @property_links[(property_key)] = new_value # Review : is this wise to do this here?? property_write(property_key) # self.refresh_ordinals ?? end # ### Handle coordination between persisted objects: if @property_links[(property_key)][:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) # ### Extend Reservation to persisted objects ( Review : does this slow it down too much?? ) @property_links[(property_key)][:content].release_reservation @db_address[:database].reservation_add_element((@reservation || @delegate), (@property_links[(property_key)][:content].db_address()[:table]), @property_links[(property_key)][:content].db_address()[:dbid]) @property_links[(property_key)][:content].set_delegate((@reservation || @delegate)) # ### Extend Permissions to persisted object @db_address[:database].extend_element_permissions(@db_address[:table],@db_address[:dbid],@property_links[(property_key)][:content].db_address()[:table],@property_links[(property_key)][:content].db_address()[:dbid]) @db_address[:database].enforce_permission_policy({:action => :extend, :credential => @credential, :source => self, :destination => @property_links[(property_key)][:content]}) # @property_links[(property_key)][:content].set_parent(self) end # self.increment_version() result = @property_links[(property_key)][:content] # else raise Exception, "You do not have sufficient privileges to make this change. (write-reservation)" end else raise Exception, "The value is not persistable." end else raise Exception, "You must provide a property key in the form of a Symbol." end result end
alive?()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 861 def alive?() if @db_address true else false end end
db_address()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 869 def db_address() @db_address end
deactivate()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 873 def deactivate() if self.alive?() @property_links.keys.each do |key| element = @property_links[(key)][:content] if element.is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) containers = [(element)] element.search({:include_inactive => false}) do |the_value, the_selector, the_container| if the_value.alive? && the_value.respond_to?(:deactivate) the_value.deactivate end if the_container.alive? unless containers.include?(the_container) containers.unshift(the_container) end end nil end containers.each do |the_container| the_container.deactivate end end end if self.write_reserved?() self.release_reservation() end @property_links = {} @db_address = nil end true end
delete(key=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1361 def delete(key=nil) result = nil # if key exists # if key is not loaded, load element if self.alive?() if @db_address[:database].open?() if key.is_a?(::Symbol) if key.to_s.size > 256 log_warn({:warning => "Attempted oversized key usage (limited to 256 characters), truncated #{key.inspect} to #{key.to_s[(0..255)].to_sym.inspect}"}) key = key.to_s[(0..255)].to_sym end if @format raise Exception, "Formatted - the structure cannot be altered" else if self.write_reserved?() property_key = key.to_s.to_sym # if @property_links[(property_key)] # This will load the unloaded prior to separation. result = self[(property_key)] the_link = @property_links.delete(property_key) if the_link[:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) the_link[:content].save the_link[:content].release_reservation() @db_address[:database].reservation_remove_element((@reservation || @delegate), the_link[:content].db_address()[:table], the_link[:content].db_address()[:dbid]) else # Eliminate orphaned text and binary objects in the database. They will exist only in memory. if the_link[:content].is_any?(::String, ::GxG::ByteArray) if the_link[:content].is_a?(::String) if the_link[:content].size > 256 # eliminate text object and pages. the_temp_record = @db_address[:database].connector()[:element_text].select(:dbid).where({:uuid => the_link[:record][:element_text_uuid]}).first if the_temp_record @db_address[:database].element_destroy(@credential, :element_text, the_temp_record[:dbid]) end end end if the_link[:content].is_a?(::GxG::ByteArray) # eliminate binary object and pages. the_temp_record = @db_address[:database].connector()[:element_binary].select(:dbid).where({:uuid => the_link[:record][:element_binary_uuid]}).first if the_temp_record @db_address[:database].element_destroy(@credential, :element_binary, the_temp_record[:dbid]) end end end end @db_address[:database].connector()[:hash_properties].filter({:dbid => the_link[:linkid]}).delete refresh_ordinals self.increment_version() end # else raise Exception, "You do not have sufficient privileges to make this change" end end else raise ArgumentError, "You must specify with a Symbol, not a #{key.class.inspect}" end else raise Exception, "Database not available" end else raise Exception, "Attempted to access a defunct structure" end result end
destroy()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 931 def destroy() result = false if self.alive? begin unless self.write_reserved?() raise Exception, "You do not have a write-lock for this element" end unless @permission[:destroy] raise Exception, "You do not have permission to destory this element" end if @db_address[:database].open?() if @db_address[:database].structure_attached?(@uuid) raise Exception, "Element is still linked to a structure. Use <PersistedHash>.delete(<key>).destroy or <PersistedArray>.delete_at(<index>).destroy" else if self.write_reserved?() address = @db_address self.deactivate address[:database].element_destroy(@credential, address[:table],address[:dbid]) # Review : consider either emptying the db trash now, or adding a flag to manifest records to prevent future loading. result = true else raise Exception, "You do not have sufficient privileges to make this change" end end else raise Exception, "Database not available" end rescue Exception => the_error log_error({:error => the_error}) end end result end
each_pair(&block)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1577 def each_pair(&block) collection = {} @property_links.keys.each do |key| collection[(key)] = (self[(key)]) end if block.respond_to?(:call) collection.to_enum(:each_pair).each do |key,value| block.call(key,value) end else collection.to_enum(:each_pair) end end
element_version(key=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 375 def element_version(key=nil) result = 0.0 if key.is_a?(::Symbol) if key.to_s.size > 256 log_warn({:warning => "Attempted oversized key usage (limited to 256 characters), truncated #{key.inspect} to #{key.to_s[(0..255)].to_sym.inspect}"}) key = key.to_s[(0..255)].to_sym end # if @property_links[(key.to_s.to_sym)] result = @property_links[(key.to_s.to_sym)][:record][:version] else result = 0.0 end # end result end
export(options={:exclude_file_segments=>false})
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1492 def export(options={:exclude_file_segments=>false}) if self.alive? exclude_file_segments = (options[:exclude_file_segments] || false) if options[:clone] == true # Review : why are cloned objects unformatted? sync issues?? result = {:type => :element_hash, :uuid => GxG::uuid_generate.to_s.to_sym, :title => @title.clone, :version => @version.clone, :content => {}} else result = {:type => :element_hash, :uuid => @uuid.clone, :title => @title.clone, :version => @version.clone, :format => @format.clone, :content => {}} end export_db = [{:parent => nil, :parent_selector => nil, :object => self, :record => result}] children_of = Proc.new do |the_parent=nil| list = [] export_db.each do |node| if node[:parent].object_id == the_parent.object_id list << node end end list end # Build up export_db: self.search do |the_value, the_selector, the_container| if the_value.is_a?(::GxG::Database::PersistedHash) if options[:clone] == true the_uuid = GxG::uuid_generate.to_s.to_sym else the_uuid = the_value.uuid().clone end if (exclude_file_segments == true) && (the_selector == :file_segments || the_selector == :segments || the_selector == :portions) export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => {}, :record => {:type => :element_hash, :uuid => the_uuid, :title => the_value.title().clone, :version => the_value.version().clone, :format => the_value.format().clone, :content => {}}} else export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => :element_hash, :uuid => the_uuid, :title => the_value.title().clone, :version => the_value.version().clone, :format => the_value.format().clone, :content => {}}} end end if the_value.is_a?(::GxG::Database::PersistedArray) if options[:clone] == true the_uuid = GxG::uuid_generate.to_s.to_sym else the_uuid = the_value.uuid().clone end if (exclude_file_segments == true) && (the_selector == :file_segments || the_selector == :segments || the_selector == :portions) export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => [], :record => {:type => :element_array, :uuid => the_uuid, :title => the_value.title().clone, :version => the_value.version().clone, :constraint => the_value.constraint().clone, :content => []}} else export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => :element_array, :uuid => the_uuid, :title => the_value.title().clone, :version => the_value.version().clone, :constraint => the_value.constraint().clone, :content => []}} end end if the_value.is_any?(::GxG::Database::Database::valid_field_classes()) data_type = GxG::Database::Database::element_table_for_instance(the_value) case data_type when :element_bigdecimal data = the_value.to_s when :element_datetime data = the_value.to_s when :element_binary data = the_value.to_s.encode64 when :element_text data = the_value.to_s else data = the_value end export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => data_type, :version => (the_container.version(the_selector) || 0.0), :content => data}} end end # Collect children export content: link_db = [(export_db[0])] while link_db.size > 0 do entry = link_db.shift children_of.call(entry[:object]).each do |the_child| entry[:record][:content][(the_child[:parent_selector])] = the_child[:record] if the_child[:object].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) link_db << the_child end end end else result = nil end # result end
format()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 408 def format() @format.clone end
format=(the_format=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 412 def format=(the_format=nil) if GxG::valid_uuid?(the_format) @format = the_format.to_s.to_sym end end
get_at_path(the_path="/")
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1750 def get_at_path(the_path="/") result = nil if the_path == "/" result = self else object_stack = [(self)] path_stack = the_path.split("/") path_stack.to_enum.each do |path_element| element = nil if path_element.size > 0 if (path_element =~ /^(?:[0-9])*[0-9](?:[0-9])*$/) == 0 element = path_element.to_i else element = path_element element.gsub!("%2f","/") if element[0] == ":" element = element[(1..-1)].to_sym end end end if element result = object_stack.first[(element)] if result.is_a?(NilClass) break else object_stack.unshift(result) end else # ignore double slashes? '//' # break end end end result end
get_delegate()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 608 def get_delegate() if self.alive? if @delegate.is_any?(::String, ::Symbol) @delegate.clone end end end
get_permissions()
click to toggle source
Review: move to Database class as generic toolbox method at some point:
# File lib/gxg/gxg_database_persistedhash.rb, line 432 def get_permissions() @permission.clone end
get_reservation()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 535 def get_reservation() result = false # Review : Experimental design change - deprecate @delegate use, require entire structure (and sub-structures) to get solid @reservation (all of them) or this fails. # Note : keep your structures small as this opens the door for stack-depth exceptions if it is nested too deep. (say 256 or less layers deep??) if @reservation result = true else if self.alive? if @permission[:write] clean_up_list = [] # Review : efficiency - write locks only apply to PersistedHashes and PersistedArrays so do we need a full manifest?? the_reservation = @db_address[:database].reserve_element_locks(@credential, (@db_address[:database].element_manifest(@db_address[:table], @db_address[:dbid])), :write) if the_reservation # @property_links.keys.each do |the_key| if @property_links[(the_key)][:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) clean_up_list << @property_links[(the_key)][:content] unless @property_links[(the_key)][:content].get_reservation() @db_address[:database].release_element_locks(the_reservation) the_reservation = nil break end end end # end if the_reservation @reservation = the_reservation result = true else clean_up_list.each do |the_object| the_object.release_reservation end end end end end # xxx # if (@reservation || @delegate) # result = true # else # if self.alive? # if @permission[:write] # @reservation = @db_address[:database].reserve_element_locks(@credential, (@db_address[:database].element_manifest(@db_address[:table], @db_address[:dbid])), :write) # if @reservation # @property_links.keys.each do |the_key| # if @property_links[(the_key)][:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) # @property_links[(the_key)][:content].set_delegate((@reservation || @delegate)) # end # end # result = true # end # # # end # end # end # xxx result end
import_update(the_record=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1470 def import_update(the_record=nil) if self.alive? if the_record.is_a?(::Hash) if @uuid == the_record[:uuid].to_s.to_sym if @reservation had_reservation = true self.release_reservation else had_reservation = false end @db_address[:database].synchronize_records([{:operation => :merge, :data => the_record}],@credential) @property_links = {} load_property_links if had_reservation self.get_reservation end end end end self end
include?(the_key)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1357 def include?(the_key) @property_links.include?(the_key) end
increment_version()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 696 def increment_version() if self.alive? if @db_address[:database].open? if self.write_reserved?() @version = (((@version + 0.0001) * 10000.0).to_i.to_f / 10000.0) end end end end
inspect()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 830 def inspect() # FORNOW: make re-entrant (yes, I know!) Fortunately, circular links are impossible with PersistedHash. # TODO: make interative instead of re-entrant. result = "(Not Loaded)" if @db_address if @db_address[:database].element_byte_size(@db_address[:table], @db_address[:dbid]) <= 10485760 # smaller than 10MB inspection_db = [] last_key = @property_links.keys.last result = "{" @property_links.keys.each do |element_key| if @property_links[(element_key)][:loaded] == true result << (":#{element_key.to_s} => " << @property_links[(element_key)][:content].inspect) else result << (":#{element_key.to_s} => (Not Loaded)") end unless last_key == element_key result << ", " end end result << "}" else result = "(Too Damned Big)" end # # # end result end
iterative(options={:include_inactive => true}, &block)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1591 def iterative(options={:include_inactive => true}, &block) result = [] visit = Proc.new do |the_node=nil, accumulator=[]| node_stack = [] if the_node node_stack << ({:parent => nil, :parent_selector => nil, :object => (the_node)}) while (node_stack.size > 0) do a_node = node_stack.shift # if a_node[:object].is_a?(::GxG::Database::PersistedHash) if a_node[:object].alive? a_node[:object].each_pair do |the_key, the_value| node_stack << ({:parent => a_node[:object], :parent_selector => the_key, :object => the_value}) end else if options[:include_inactive] accumulator << a_node end end end if a_node[:object].is_a?(::GxG::Database::PersistedArray) if a_node[:object].alive? a_node[:object].each_with_index do |the_value, the_index| node_stack << ({:parent => a_node[:object], :parent_selector => the_index, :object => the_value}) end else if options[:include_inactive] accumulator << a_node end end end # Review : is this for a Field?? if a_node.alive? || options[:include_inactive] accumulator << a_node end end end accumulator end # children_of = Proc.new do |the_db=[], the_parent=nil| list = [] the_db.each do |node| if node[:parent].object_id == the_parent.object_id list << node end end list end # begin database = visit.call(self,[]) link_db = children_of.call(database, self) if block.respond_to?(:call) while (link_db.size > 0) do entry = link_db.shift unless entry[:object].object_id == self.object_id # calls with parameters: the_value, the_key/the_index (the_selector), the_container raw_result = block.call(entry[:object], entry[:parent_selector], entry[:parent]) if raw_result result << raw_result end end if entry[:object].object_id != nil.object_id children = children_of.call(database, entry[:object]) children.each do |child| link_db << child end end end end # rescue Exception => the_error log_error({:error => the_error, :parameters => {}}) end # result end
keys()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 976 def keys() @property_links.keys end
parent()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 418 def parent() @parent end
parent=(object=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 422 def parent=(object=nil) if object.is_any?([::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray]) # Review : parent can only be set once -- is this best?? unless @parent @parent = object end end end
paths_to(the_object=nil,base_path="")
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1683 def paths_to(the_object=nil,base_path="") # new idea here: search_results = [] unless base_path[0] == "/" base_path = ("/" << base_path) end if base_path.size > 1 path_stack = base_path.split("/")[1..-1].reverse else path_stack = [] end origin = self.get_at_path(base_path) container_stack = [{:selector => nil, :container => origin}] find_container = Proc.new do |the_container| result = nil container_stack.each_with_index do |entry, index| if entry[:container] == the_container result = entry break end end result end last_container = origin found = false # tester = {:a=>1, :b=>2, :c=>[0, 5], :testing=>{:d=>4.0, :e=>0.9, :f => nil}} if origin.is_any?(::Hash, ::Array, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) origin.process! do |the_value, selector, container| if last_container.object_id != container.object_id container_record = find_container.call(container) if container_record path_stack = container_record[:prefix].split("/").reverse if path_stack.size == 0 path_stack << "" end end last_container = container end if selector.is_a?(Symbol) safe_key = (":" + selector.to_s) else safe_key = selector.to_s end safe_key.gsub!("/","%2f") path_stack.unshift(safe_key) # compare the_value found = false if (the_value == the_object) found = true end if found search_results << ("/" << path_stack.reverse.join("/")) end # if the_value.is_any?(::Array, ::Hash, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) container_stack.unshift({:selector => selector, :container => the_value, :prefix => (path_stack.reverse.join("/"))}) end path_stack.shift # nil end else search_results << ("/" << path_stack.join("/")) end search_results end
process!(options={:include_inactive => true}, &block)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1670 def process!(options={:include_inactive => true}, &block) self.iterative(options, &block) self end
refresh_links()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 688 def refresh_links() if self.alive?() unless @db_address[:database].element_in_use?(@db_address[:table], @db_address[:dbid]) @parent = nil end end end
release_reservation()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 516 def release_reservation() if self.alive?() if @reservation @db_address[:database].release_element_locks(@reservation) @reservation = nil end if @delegate @delegate = nil end # release write-locks of all sub-items @property_links.keys.each do |the_key| if @property_links[(the_key)][:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) @property_links[(the_key)][:content].release_reservation end end end true end
reservation()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 496 def reservation() if self.alive? if @reservation.is_any?(::String, ::Symbol) @reservation.clone end end end
save()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 904 def save() result = false if self.alive? begin if @db_address[:database].open?() @property_links.keys.each do |the_key| # Note: only save property data and header if: a) data is still loaded and b) state has changed. if @property_links[(the_key)][:loaded] == true if @property_links[(the_key)][:state] != @property_links[(the_key)][:content].hash @property_links[(the_key)][:record][:version] = (((@property_links[(the_key)][:record][:version].to_f + 0.0001) * 10000.0).to_i.to_f / 10000.0) property_write(the_key) end end # end else raise Exception, "Database not available" end self.save_version result = true rescue Exception => the_error log_error({:error => the_error}) end end result end
save_version()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 706 def save_version() @db_address[:database].connector()[:element_hash].filter({:dbid => @db_address[:dbid]}).update({:version => @version}) end
search(options={:include_inactive => true}, &block)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1675 def search(options={:include_inactive => true}, &block) results = [] if block.respond_to?(:call) results = self.iterative(options, &block) end results end
set_at_path(the_path="/",the_value=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1786 def set_at_path(the_path="/",the_value=nil) result = nil if the_path != "/" container = self.get_at_path(::File::dirname(the_path)) if container raw_selector = ::File::basename(the_path) selector = nil if raw_selector.size > 0 if (raw_selector =~ /^(?:[0-9])*[0-9](?:[0-9])*$/) == 0 selector = raw_selector.to_i else selector = raw_selector selector.gsub!("%2f","/") if selector[0] == ":" selector = selector[(1..-1)].to_sym end end end if selector container[(selector)] = the_value result = container[(selector)] else # ignore double slashes? '//' # break end # end end result end
set_delegate(the_delegate=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 616 def set_delegate(the_delegate=nil) if self.alive? if the_delegate.is_any?([::String, ::Symbol]) @delegate = the_delegate.to_sym end end end
set_element_version(element_key, the_version=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 393 def set_element_version(element_key, the_version=nil) result = false if @property_links[(element_key)] if the_version.is_a?(::Numeric) @property_links[(element_key)][:record][:version] = (((the_version.to_f) * 10000.0).to_i.to_f / 10000.0) result = true else log_warning("Attempted to set version to an invalid version value #{the_version.inspect} for #{element_key.inspect} on Object #{@uuid.inspect}") end else log_warning("Attempted to set version with an invalid key #{element_key.inspect} on Object #{@uuid.inspect}") end result end
set_format(format_uuid=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 624 def set_format(format_uuid=nil) if self.alive? unless @format if ::GxG::valid_uuid?(format_uuid) @format = format_uuid.to_sym @db_address[:database].connector()[:element_hash].filter({:dbid => (@db_address[:dbid])}).update({:format => (@format.to_s)}) end end end end
set_parent(object=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 678 def set_parent(object=nil) if self.alive? if object.is_any?([::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray]) unless @parent @parent = object end end end end
set_permissions(credential=nil, the_permission=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 436 def set_permissions(credential=nil, the_permission=nil) if self.alive? if credential.is_a?(::Hash) && the_permission.is_a?(::Hash) if credential[:user] if credential[:user] == "public" || credential[:user] == "Public" credential = "00000000-0000-4000-0000-000000000000" else credential = (@db_address[:database].user_fetch({:user_id => credential[:user].to_s}) || {})[:uuid] end if credential credential = credential.to_s.to_sym end else if credential[:group] credential = @db_address[:database].group_credential(credential) else credential = nil end end else if credential.is_a?(::Hash) && the_permission == nil the_permission = credential credential = @credential else unless credential credential = @credential end end end if the_permission.is_a?(::Hash) && ::GxG::valid_uuid?(credential) manifest = [] structures = [{:table => @db_address[:table], :dbid => @db_address[:dbid]}] while structures.size > 0 do entry = structures[0] @db_address[:database].element_manifest(entry[:table],entry[:dbid]).each do |record| if [:element_hash, :element_array].include?(record[:table]) unless structures.include?(record) structures << record end else # Review - only set/get/check permissions on persisted arrays/hashes # unless manifest.include?(record) # manifest << record # end end end manifest << structures.shift end manifest.each do |record| @db_address[:database].assign_element_permission(record[:table], record[:dbid], credential, the_permission) end # true else log_error({:error => Exception.new("You MUST provide a valid permissions Hash."), :parameters => {:credential => credential, :permission => the_permission}}) false end end end
set_title(the_title=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 721 def set_title(the_title=nil) if self.alive? if @db_address[:database].open? if self.write_reserved?() if the_title @title = the_title.to_s[0..255] @db_address[:database].connector()[:element_hash].filter({:dbid => @db_address[:dbid]}).update({:title => @title}) self.increment_version end end end end end
set_version(the_version=0.0)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 710 def set_version(the_version=0.0) if self.write_reserved?() @version = (((the_version.to_f) * 10000.0).to_i.to_f / 10000.0) self.save_version end end
size()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 965 def size() @property_links.size end
structure_attached?()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 969 def structure_attached?() @db_address[:database].structure_attached?(@uuid.to_s) end
structure_detach()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 972 def structure_detach() @db_address[:database].structure_detach(@uuid.to_s) end
sync_export(options={})
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1572 def sync_export(options={}) self.save @db_address[:database].sync_export(@credential, [(@uuid)], options) end
title()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 354 def title() @title.clone end
title=(the_title=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 358 def title=(the_title=nil) if the_title @title = the_title.to_s[0..255] self.increment_version end end
ufs()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 639 def ufs() if @format record = @db_address[:database].format_load({:uuid => @format.to_s}) if record record[:ufs] else "" end else "" end end
unload(key=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1277 def unload(key=nil) result = false # if key exists # if key is loaded, unload element (if String or ByteArray) if self.alive?() if @db_address[:database].open?() if key.is_a?(::Symbol) if key.to_s.size > 256 log_warn({:warning => "Attempted oversized key usage (limited to 256 characters), truncated #{key.inspect} to #{key.to_s[(0..255)].to_sym.inspect}"}) key = key.to_s[(0..255)].to_sym end property_key = key.to_s.to_sym if @property_links[(property_key)] if @property_links[(property_key)][:content].is_any?(::String, ::GxG::ByteArray) if @property_links[(property_key)][:loaded] == true # ### Unload Base Element contents from memory, leaving reference harness intact and active. @property_links[(property_key)][:content].clear @property_links[(property_key)][:loaded] = false end end result = true end else raise ArgumentError, "You must specify with a Symbol, not a #{key.class.inspect}" end else raise Exception, "Database not available" end else raise Exception, "Attempted to access a defunct structure" end result end
unpersist()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 1428 def unpersist() result = {} if self.alive? # export_db = [{:parent => nil, :parent_selector => nil, :object => self, :record => result}] children_of = Proc.new do |the_parent=nil| list = [] export_db.each do |node| if node[:parent].object_id == the_parent.object_id list << node end end list end # Build up export_db: self.search do |the_value, the_selector, the_container| if the_value.is_a?(::GxG::Database::PersistedHash) export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {}} else if the_value.is_a?(::GxG::Database::PersistedArray) export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => []} else export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => the_value} end end end # Collect children export content: link_db =[(export_db[0])] while link_db.size > 0 do entry = link_db.shift children_of.call(entry[:object]).each do |the_child| entry[:record][(the_child[:parent_selector])] = the_child[:record] if the_child[:object].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) link_db << the_child end end end # end result end
uuid()
click to toggle source
Review : favor the following more Ruby-like accessors over the set_xxx style methods, eliminate older duplicate methods.
# File lib/gxg/gxg_database_persistedhash.rb, line 344 def uuid() @uuid.clone end
uuid=(the_uuid=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 348 def uuid=(the_uuid=nil) if GxG::valid_uuid?(the_uuid) @uuid = the_uuid.to_s.to_sym end end
version()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 365 def version() @version.clone end
version=(the_version=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 369 def version=(the_version=nil) if the_version.is_a?(::Numeric) @version = (((the_version.to_f) * 10000.0).to_i.to_f / 10000.0) end end
wait_for_reservation(timeout=nil)
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 595 def wait_for_reservation(timeout=nil) result = true the_timeout = Time.now.to_f + (timeout || 30.0).to_f until self.get_reservation() == true do sleep 1.0 if Time.now.to_f >= the_timeout result = false break end end result end
write_permission?()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 504 def write_permission?() @permission[:write] end
write_reserved?()
click to toggle source
# File lib/gxg/gxg_database_persistedhash.rb, line 508 def write_reserved?() if @reservation or @delegate true else false end end