# PeristedArray Code: module GxG module Database # class DetachedArray # private # def element_destroy(indexer=nil) # Review : develop on-server element destroy command path ?? end # Note: property_read handled by load_property_links def element_write(indexer=nil) # Review : not needed on webclient version ?? end # def load_element_links # Review : not needed on webclient version ?? end # def refresh_ordinals() new_links = [] @element_links.each_with_index do |the_key, ordinal| @element_links[(ordinal)][:record][:ordinal] = ordinal new_links << @element_links[(ordinal)] end @element_links = new_links end # public # def self.create() result = ::GxG::Database::DetachedArray.new result.uuid = ::GxG::uuid_generate.to_sym result.title = "Untitled #{new_object.uuid.to_s}" result end # def self.create_constrained(the_format=nil) result = ::GxG::Database::DetachedArray::create() format_record = nil if ::GxG::valid_uuid?(the_format) format_record = ::GxG::DB[:roles][:formats].format_load({:uuid => the_format}) else format_record = ::GxG::DB[:roles][:formats].format_load({:ufs => the_format}) end if format_record # unless ::GxG::DB_SAFETY.synchronize { GxG::DB[:formats][(format_record[:uuid].to_s.to_sym)] } ::GxG::DB_SAFETY.synchronize { GxG::DB[:formats][(format_record[:uuid].to_s.to_sym)] = format_record } end # result.constraint = format_record[:uuid].to_s.to_sym end result end # def assimilate_detached(detached_object=nil) result = false if detached_object.is_a?(::GxG::Database::DetachedArray) # Review : ?? the ability to ingest a detached_object's data while retaining self's identity data. worth it ?? object_db = [{:destination => self, :source => detached_object}] while object_db.size > 0 do pair = object_db.shift if pair pair[:source].search do |item, selector, container| if item.is_any?(::GxG::Database::DetachedArray, ::GxG::Database::DetachedHash) if item.is_a?(::GxG::Database::DetachedHash) unless pair[:destination][(selector)].is_a?(::GxG::Database::PersistedHash) pair[:destination][(selector)] = @db_address[:database].try_persist({}, @credential, {:with_uuid => item.uuid.to_s, :with_title => item.title.to_s, :with_version => item.version, :with_format => item.format}) end object_db << [{:destination => pair[:destination][(selector)], :source => item}] else if item.is_a?(::GxG::Database::DetachedArray) unless pair[:destination][(selector)].is_a?(::GxG::Database::PersistedArray) pair[:destination][(selector)] = @db_address[:database].try_persist([], @credential, {:with_uuid => item.uuid.to_s, :with_title => item.title.to_s, :with_version => item.version, :with_constraint => item.constraint}) end object_db << [{:destination => pair[:destination][(selector)], :source => item}] end end # else pair[:destination][(selector)] = item end end end end # result = true end result end # def detach() ::GxG::Database::detached_import(self.export()) end # def emit_structures(preference=false) @as_structures = preference preference end # def uuid() @uuid.clone end # def uuid=(the_uuid=nil) if GxG::valid_uuid?(the_uuid) @uuid = the_uuid.to_s.to_sym end end # def title() @title.clone end # def title=(the_title=nil) if the_title @title = the_title.to_s[0..255] self.increment_version end end # def version() @version.clone end # def version=(the_version=nil) if the_version.is_a?(::Numeric) @version = the_version.to_s("F").to_d end end # def element_version(index=nil) result = 0.0 if index.is_a?(::Numeric) # if @element_links[(index)] result = @element_links[(index)][:record][:version] else result = 0.0 end # end result end # def set_element_version(element_index, the_version=nil) result = false if @element_links[(element_index)] if the_version.is_a?(::Numeric) @element_links[(element_index)][: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 index #{element_index.inspect} on Object #{@uuid.inspect}") end else log_warning("Attempted to set version with an invalid index #{element_index.inspect} on Object #{@uuid.inspect}") end result end # def constraint() @constraint.clone end # def constraint=(the_format=nil) if GxG::valid_uuid?(the_format) @constraint = the_format.to_s.to_sym end end # def parent() @parent end # def parent=(object=nil) if object.is_any?([::GxG::Database::DetachedHash, ::GxG::Database::DetachedArray]) # Review : parent can only be set once -- is this best?? unless @parent @parent = object end end end # Permission Management: def get_permissions() # Review : not needed on webclient version ?? end # def set_permissions(credential=nil, the_permission=nil) # Review : not needed on webclient version ?? end # def reservation() # Review : not needed on webclient version ?? end # def write_permission?() true end # def write_reserved?() true end # def release_reservation() true end # def get_reservation() true end # def wait_for_reservation(timeout=nil) true end # def get_delegate() # Review : not needed on webclient version ?? end # def set_delegate(the_delegate=nil) # Review : not needed on webclient version ?? end # def increment_version() if self.alive? if self.write_reserved?() @version = (((@version + 0.0001) * 10000.0).to_i.to_f / 10000.0) end end end # def save_version() # Review : not needed on webclient version ?? end # def clear_constraint() if self.alive? if @constraint @constraint = nil end end true end # def constraint() @constraint.clone end # def ufs() if @format record = GxG::DB[:formats][(@format.to_s.to_sym)] if record record[:ufs].to_s.to_sym else "" end else "" end end # def refresh_links() # Review : not needed on webclient ?? end # def initialize() # Review : ! @as_structures = false @active = true @uuid = nil @title = nil @version = 0.0 @constraint = nil @parent = nil @data = [] @element_links = [] self end # def inspect() # FORNOW: make re-entrant (yes, I know!) Fortunately, circular links are impossible with DetachedArray. # TODO: make interative instead of re-entrant. result = "[" @element_links.each_index do |element_index| result = result + @element_links[(element_index)][:content].inspect # if @element_links[(element_index)].is_a?(::Hash) # result = result + @element_links[(element_index)][:content].inspect # else # result = result + "(Not Loaded)" # end unless element_index == (@element_links.size - 1) result = result + ", " end end result = result + "]" result end # def alive?() @active end # def db_address() nil end # def save() # Review : create a save-to-server command path here. end # def deactivate() ::GxG::DB_SAFETY.synchronize { ::GxG::DB[:cache].delete(@uuid) } @active = false true end # def destroy() result = false if self.alive? result = self.deactivate end result end # def size() @element_links.size end # def structure_attached?() # Review : not needed on webclient ?? false end def structure_detach() # Review : not needed on webclient ?? true end # def []=(indexer=nil, value=nil) # Only works with Integer indexes. unless self.alive?() raise Exception, "Attempted to alter a defunct structure" end result = nil if indexer.is_a?(::Integer) if value.is_a?(::OpenStruct) value = value.table end # Review : rewrite ???? if value.is_any?(::GxG::Database::valid_field_classes()) || value.is_any?(::GxG::Database::DetachedHash, ::GxG::Database::DetachedArray, ::Hash, ::Array) unless self.write_reserved?() self.get_reservation() end if self.write_reserved?() # Further screen value provided: # ### Check provided DetachedHashes and DetachedArrays # ### Check provided Hashes and Arrays if value.is_any?(::Hash, ::Array) value.search do |the_value, the_selector, the_container| unless the_value.is_any?(::GxG::Database::valid_field_classes()) || the_value.is_any?(::GxG::Database::DetachedHash, ::GxG::Database::DetachedArray, ::Hash, ::Array) raise Exception, "The structure you are attaching has an unpersistable item within it (not supported)." end end end # ### Property Exists? if @element_links[(indexer)] # set property to value operation = :set_value else if @constraint 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, :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 => ::Time.now, :time_offset => 0.0, :time_prior => 0.0, :time_after => 0.0, :length => 0, :element_text => "", :element_binary => nil, :element_array => nil, :element_hash => nil } } # if value.is_any?(::GxG::Database::DetachedHash, ::GxG::Database::DetachedArray) # ### Assimilate DetachedHash or DetachedArray 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::DetachedHash) new_value[:record][:element] = "element_hash" new_value[:record][:element_hash] = value else new_value[:record][:element] = "element_array" new_value[:record][:element_array] = value end else # ### Persist Hashes and Arrays if value.is_any?(::Hash, ::Array) new_value[:content] = ::GxG::Database::iterative_detached_persist(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?(::Hash) new_value[:record][:element] = "element_hash" new_value[:record][:element_hash] = new_value[:content] else new_value[:record][:element] = "element_array" new_value[:record][:element_array] = new_value[:content] end else # ### Persist Base Element Values new_value[:record][:element] = ::GxG::Database::element_table_for_instance(value).to_s case new_value[:record][:element] when "element_boolean" case value.class when ::NilClass new_value[:record][:element_boolean] = -1 new_value[:content] = nil when ::FalseClass new_value[:record][:element_boolean] = 0 new_value[:content] = false when ::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" new_value[:record][:element_text] = value new_value[:record][:length] = value.size 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] = @element_links[(indexer)][:record][:version] new_value[:record][:element_binary_uuid] = @element_links[(indexer)][: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?(@element_links[(indexer)][:content].class) || ([true, false, nil].include?(new_value[:content]) && [true, false, nil].include?(@element_links[(indexer)][: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] = @element_links[(indexer)][:linkid] if new_value[:content].is_any?(::GxG::Database::DetachedHash, ::GxG::Database::DetachedArray) new_value[:record][:version] = new_value[:content].version() else new_value[:record][:version] = (((@element_links[(indexer)][:record][:version].to_f + 0.0001) * 10000.0).to_i.to_f / 10000.0) end new_value[:record][:ordinal] = @element_links[(indexer)][:record][:ordinal] new_value[:loaded] = true new_value[:state] = @element_links[(indexer)][:state] # if new_value[:content].is_a?(::String) @element_links[(indexer)] = new_value end # else # Other class value being substituted. if new_value[:content].is_any?(::GxG::Database::DetachedHash, ::GxG::Database::DetachedArray) new_value[:record][:version] = new_value[:content].version() else new_value[:record][:version] = (((@element_links[(indexer)][:record][:version].to_f + 0.0001) * 10000.0).to_i.to_f / 10000.0) end new_value[:record][:ordinal] = @element_links[(indexer)][:record][:ordinal] new_value[:loaded] = true # @element_links[(indexer)] = new_value end # when :add_value # Note: set in-memory value and save. if new_value[:content].is_any?(::GxG::Database::DetachedHash, ::GxG::Database::DetachedArray) new_value[:record][:version] = new_value[:content].version() else new_value[:record][:version] = 0.0 end new_value[:record][:ordinal] = @element_links.size new_value[:loaded] = true # @element_links[(indexer)] = new_value end # ### Handle coordination between persisted objects: if @element_links[(indexer)][:content].is_any?(::GxG::Database::DetachedHash, ::GxG::Database::DetachedArray) @element_links[(indexer)][:content].parent = (self) end # self.increment_version() result = @element_links[(indexer)][: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 index in the form of an Integer." end result end # # def [](indexer=nil) result = nil # if exists # if is not loaded, load element (if String or ByteArray) if self.alive?() if indexer.is_a?(::Integer) if @element_links[(indexer)] result = @element_links[(indexer)][:content] if @as_structures == true if result.is_a?(::GxG::Database::PersistedHash) result = ::OpenStruct.new(result) end end end else raise ArgumentError, "You must specify with an Integer, not a #{indexer.class.inspect}" end else raise Exception, "Attempted to access a defunct structure" end result end # def delete_at(indexer=nil) result = nil # if exists # if is not loaded, load element if self.alive?() if indexer.is_a?(::Integer) if self.write_reserved?() # if @element_links[(indexer)] # This will load the unloaded prior to separation. result = self[(indexer)] if @as_structures == true if result.is_a?(::GxG::Database::PersistedHash) result = ::OpenStruct.new(result) end end the_link = @element_links.delete_at(indexer) # Review : element-unlink on server side ?? if the_link[:content].is_any?(::GxG::Database::DetachedHash, ::GxG::Database::DetachedArray) the_link[:content].save end self.increment_version() end # else raise Exception, "You do not have sufficient privileges to make this change" end else raise ArgumentError, "You must specify with a Symbol, not a #{key.class.inspect}" end else raise Exception, "Attempted to access a defunct structure" end result end # def <<(*args) if args.size > 0 args.each do |item| self[(@element_links.size)] = item end end self end # def push(*args) if args.size > 0 args.each do |item| self << item end end self end # def pop() self.delete_at((@element_links.size - 1)) end # def shift() self.delete_at(0) end # def insert(the_index=nil, the_object=nil) # resovle the_index : the_index < 0 : the_index = (size - the_index) # if the_index == 0 : @element_links.unshift(nil-record); self[0] = the_object # if the_index > size-1 : push(the_object) # else: @element_links.insert(the_index, nil-record); self[(the_index)] = the_object # vette the_object --> persistable? if ::GxG::Database::persistable?(the_object) new_value = { :linkid => nil, :content => nil, :loaded => false, :state => 0, :record => { :parent_uuid => @uuid.to_s, :ordinal => 0, :version => ::BigDecimal.new("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 => "" } } the_index = the_index.to_i if the_index < 0 the_index = (self.size - the_index) end if the_index == 0 new_value[:record][:ordinal] = the_index @element_links.unshift(new_value) refresh_ordinals self[0] = the_object else if the_index > (self.size - 1) self.push(the_object) else new_value[:record][:ordinal] = the_index @element_links.insert(the_index, new_value) refresh_ordinals self[(the_index)] = the_object end end end self end # ???? def insert - ::GxG::Database::persistable?(the_object) def unshift(the_object=nil) self.insert(0,the_object) end # def swap(first_index=nil, last_index=nil) result = false if first_index && last_index if (0..(self.size - 1)).include?(first_index.to_i) && (0..(self.size - 1)).include?(last_index.to_i) if first_index.to_i != last_index.to_i record = @element_links[(first_index)] @element_links[(first_index)] = @element_links[(last_index)] @element_links[(last_index)] = record result = true end end end result end # def include?(the_value) result = false if the_value.is_a?(::OpenStruct) the_value = the_value.table end @element_links.each do |element| if element[:content] == the_value result = true break end end result end # def find_index(the_value) result = nil if the_value.is_a?(::OpenStruct) the_value = the_value.table end @element_links.each_with_index do |element, indexer| if element[:content] == the_value result = indexer break end end result end # 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 unconstrained? sync issues?? result = {:type => :element_array, :uuid => GxG::uuid_generate.to_s.to_sym, :title => @title.clone, :version => @version.clone, :content => []} else result = {:type => :element_array, :uuid => @uuid.clone, :title => @title.clone, :version => @version.clone, :constraint => @constraint.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::DetachedHash) 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::DetachedArray) 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::valid_field_classes()) data_type = GxG::Database::element_table_for_instance(the_value) case data_type when :element_bigdecimal data = the_value.to_s when :element_datetime data = the_value.iso8601.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::DetachedHash, ::GxG::Database::DetachedArray) link_db << the_child end end end else result = nil end result end # def export_package(options={:exclude_file_segments=>false}) result = {:formats => {}, :records => []} object_record = self.export(options) object_record.search do |item,selector,container| if selector == :format || selector == :constraint if item.to_s.size > 0 format_uuid = item unless result[:formats][(format_uuid.to_s.to_sym)].is_a?(::Hash) format_sample = ::GxG::DB[:roles][:formats].format_load({:uuid => format_uuid.to_s.to_sym}) format_sample[:content] = format_sample[:content].gxg_export() result[:formats][(format_uuid.to_s.to_sym)] = format_sample end end end end result[:records] << object_record result end # def first() if @element_links.size > 0 self[0] else nil end end # def last() if @element_links.size > 0 self[(@element_links.size - 1)] else nil end end # def each(&block) if block.respond_to?(:call) load_element_links if @element_links.size > 0 @element_links.each_index do |index| block.call(self[(index)]) end end self else self.to_enum(:each) end end # def each_index(&block) if block.respond_to?(:call) load_element_links if @element_links.size > 0 @element_links.each_index do |index| block.call(index) end end self else self.to_enum(:each_index) end end # def each_with_index(offset=0,&block) if block.respond_to?(:call) load_element_links if @element_links.size > 0 @element_links.to_enum(:each).with_index(offset).each do |entry, index| block.call(self[(index)], index) end end self else self.to_enum(:each_with_index,offset) end end # 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::DetachedHash) 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::DetachedArray) 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 # 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 # def process!(options={:include_inactive => true}, &block) self.iterative(options, &block) self end # def search(options={:include_inactive => true}, &block) results = [] if block.respond_to?(:call) results = self.iterative(options, &block) end results end # def paths_to(the_object=nil,base_path="") # new idea here: if the_object.is_a?(::OpenStruct) the_object = the_object.table end 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::DetachedHash, ::GxG::Database::DetachedArray) 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 safe_key = selector.to_s # xxx # if selector.is_a?(Symbol) # safe_key = (":" + selector.to_s) # else # safe_key = selector.to_s # end # xxx 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::DetachedHash, ::GxG::Database::DetachedArray) 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 # def get_at_path(the_path="/") # /^(?:[0-9])*[0-9](?:[0-9])*$/ = nil if an alpha present there, else 0 only numeric # Attribution : http://stackoverflow.com/questions/1240674/regex-match-a-string-containing-numbers-and-letters-but-not-a-string-of-just-nu # # if ":" detected do: (str.gsub("%2f","/").to_sym) as key else (str.gsub("%2f","/")) 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","/") element = element.to_sym 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 # 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","/") selector = selector.to_sym end end if selector container[(selector)] = the_value result = container[(selector)] else # ignore double slashes? '//' # break end # end end result end # 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::DetachedHash) export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {}} else if the_value.is_a?(::GxG::Database::DetachedArray) 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::DetachedHash, ::GxG::Database::DetachedArray) link_db << the_child end end end # end result end # end # class PersistedArray # private # def element_destroy(indexer=nil) # Destroy database property record, but leave in-memory property intact - for now. (preserves key-ordinal position) result = false if indexer if @element_links[(indexer)] element = @element_links[(indexer)] # ### Delete sub-object linked to. the_table = nil the_uuid = nil if element[:record][:element_text_uuid].size > 0 the_table = :element_text the_uuid = property[:record][:element_text_uuid] end if element[:record][:element_binary_uuid].size > 0 the_table = :element_binary the_uuid = element[:record][:element_binary_uuid] end if element[:record][:element_array_uuid].size > 0 the_table = :element_array the_uuid = element[:record][:element_array_uuid] end if element[:record][:element_hash_uuid].size > 0 the_table = :element_hash the_uuid = element[:record][:element_hash_uuid] end case the_table when :element_text the_temp_record = @db_address[:database].connector()[:element_text].select(:dbid).where({:uuid => the_uuid}).first if the_temp_record @db_address[:database].element_destroy(@credential, :element_text, the_temp_record[:dbid]) end when :element_binary the_temp_record = @db_address[:database].connector()[:element_binary].select(:dbid).where({:uuid => the_uuid}).first if the_temp_record @db_address[:database].element_destroy(@credential, :element_binary, the_temp_record[:dbid]) end when :element_array, :element_hash @db_address[:database].destroy_by_uuid(@credential, the_uuid) end # ### Delete property record. @db_address[:database].connector()[:array_elements].filter({:dbid => element[:linkid]}).delete result = true end end result end # Note: property_read handled by load_property_links def element_write(indexer=nil) result = false if indexer if @element_links[(indexer)] element = @element_links[(indexer)] # Save out text and binary pages if needed. if element[:content].is_a?(::String) if element[:content].size > 256 unless element[:record][:element_text_uuid].size > 0 # create text header the_uuid = ::GxG::uuid_generate while @db_address[:database].connector()[:element_text].filter({:uuid => the_uuid.to_s}).count > 0 do the_uuid = ::GxG::uuid_generate end element[:record][:length] = element[:content].size the_dbid = @db_address[:database].connector()[:element_text].insert({:uuid => the_uuid.to_s, :version => element[:record][:version], :length => element[:record][:length]}) # element[:record][:element_text_uuid] = the_uuid end end else if element[:content].is_a?(::GxG::ByteArray) unless element[:record][:element_binary_uuid].size > 0 # create binary header the_uuid = ::GxG::uuid_generate while @db_address[:database].connector()[:element_binary].filter({:uuid => the_uuid.to_s}).count > 0 do the_uuid = ::GxG::uuid_generate end element[:record][:length] = element[:content].size # format field?? the_dbid = @db_address[:database].connector()[:element_binary].insert({:uuid => the_uuid.to_s, :version => element[:record][:version], :length => element[:record][:length]}) # element[:record][:element_binary_uuid] = the_uuid end end end # Store text pages if element[:record][:element_text_uuid].size > 0 header_record = @db_address[:database].connector()[:element_text].filter({:uuid => element[:record][:element_text_uuid].to_s}).first if header_record old_segments = [] @db_address[:database].connector()[:text_page].filter({:parent_uuid => header_record[:uuid].to_s}).order(:ordinal).each do |entry| old_segments << entry[:dbid] end if element[:content].size == 0 # delete all text_pages if count > 0 if old_segments.size > 0 @db_address[:database].connector()[:text_page].filter({:parent_uuid => (header_record[:uuid])}).order(:ordinal).delete end else new_segments = ::GxG::apportioned_ranges(element[:content].size, 4096) if old_segments.size > new_segments.size the_count = old_segments.size else the_count = new_segments.size end manifest = [] (0..(the_count - 1)).each do |the_ordinal| if new_segments[(the_ordinal)] and old_segments[(the_ordinal)] #overwrite with new segment range manifest << {:operation => :overwrite, :dbid => (old_segments[(the_ordinal)]), :ordinal => (the_ordinal), :portion => (new_segments[(the_ordinal)])} else if new_segments[(the_ordinal)] # create new segment at this ordinal manifest << {:operation => :create, :ordinal => (the_ordinal), :portion => (new_segments[(the_ordinal)])} else # delete old segment manifest << {:operation => :delete, :dbid => (old_segments[(the_ordinal)])} end end end # manifest.each do |action| case action[:operation] when :overwrite @db_address[:database].element_update(:text_page,{:dbid => action[:dbid]},{:parent_uuid => header_record[:uuid], :ordinal => action[:ordinal], :length => element[:content][(action[:portion])].size, :content => element[:content][(action[:portion])]}) # @db_address[:database].connector()[:text_page].filter({:dbid => (action[:dbid])}).update({:ordinal => (action[:ordinal]), :length => (@content[(action[:portion])].size), :content => (@content[(action[:portion])])}) when :create new_dbid = @db_address[:database].element_create(:text_page,{:parent_uuid => header_record[:uuid], :ordinal => action[:ordinal], :length => element[:content][(action[:portion])].size, :content => element[:content][(action[:portion])]}) # @db_address[:database].connector()[:text_page].insert({:parent_uuid => (record[:uuid]), :ordinal => (action[:ordinal]), :length => (@content[(action[:portion])].size), :content => (@content[(action[:portion])])}) when :delete @db_address[:database].element_destroy(@credential,:text_page,action[:dbid]) # @db_address[:database].connector()[:text_page].filter({:dbid => (action[:dbid])}).delete end end # end end else if element[:record][:element_binary_uuid].size > 0 header_record = @db_address[:database].connector()[:element_binary].filter({:uuid => element[:record][:element_binary_uuid].to_s}).first if header_record old_segments = [] @db_address[:database].connector()[:binary_page].filter({:parent_uuid => header_record[:uuid].to_s}).order(:ordinal).each do |entry| old_segments << entry[:dbid] end if element[:content].size == 0 # delete all text_pages if count > 0 if old_segments.size > 0 @db_address[:database].connector()[:binary_page].filter({:parent_uuid => (header_record[:uuid])}).order(:ordinal).delete end else new_segments = ::GxG::apportioned_ranges(element[:content].size, 65536) if old_segments.size > new_segments.size the_count = old_segments.size else the_count = new_segments.size end manifest = [] (0..(the_count - 1)).each do |the_ordinal| if new_segments[(the_ordinal)] and old_segments[(the_ordinal)] #overwrite with new segment range manifest << {:operation => :overwrite, :dbid => (old_segments[(the_ordinal)]), :ordinal => (the_ordinal), :portion => (new_segments[(the_ordinal)])} else if new_segments[(the_ordinal)] # create new segment at this ordinal manifest << {:operation => :create, :ordinal => (the_ordinal), :portion => (new_segments[(the_ordinal)])} else # delete old segment manifest << {:operation => :delete, :dbid => (old_segments[(the_ordinal)])} end end end # manifest.each do |action| case action[:operation] when :overwrite @db_address[:database].element_update(:binary_page,{:dbid => action[:dbid]},{:parent_uuid => header_record[:uuid], :ordinal => action[:ordinal], :length => element[:content][(action[:portion])].size, :content => element[:content][(action[:portion])]}) # @db_address[:database].connector()[:text_page].filter({:dbid => (action[:dbid])}).update({:ordinal => (action[:ordinal]), :length => (@content[(action[:portion])].size), :content => (@content[(action[:portion])])}) when :create new_dbid = @db_address[:database].element_create(:binary_page,{:parent_uuid => header_record[:uuid], :ordinal => action[:ordinal], :length => element[:content][(action[:portion])].size, :content => element[:content][(action[:portion])]}) # @db_address[:database].connector()[:text_page].insert({:parent_uuid => (record[:uuid]), :ordinal => (action[:ordinal]), :length => (@content[(action[:portion])].size), :content => (@content[(action[:portion])])}) when :delete @db_address[:database].element_destroy(@credential,:binary_page,action[:dbid]) # @db_address[:database].connector()[:text_page].filter({:dbid => (action[:dbid])}).delete end end # end end end end # ### Create new property record if missing: otherwise just update it. if element[:linkid] == nil || element[:linkid] == 0 element[:linkid] = @db_address[:database].connector()[:array_elements].insert(element[:record]) else @db_address[:database].connector()[:array_elements].filter({:dbid => element[:linkid]}).update(element[:record]) end # element[:state] = element[:content].hash result = true end end result end # def load_element_links # result = true begin if @db_address if @db_address[:database].is_a?(::GxG::Database::Database) if @db_address[:database].open?() new_links = [] the_records = @db_address[:database].connector()[:array_elements].filter({:parent_uuid => @uuid.to_s}).order(:ordinal) the_records.each_with_index do |entry, indexer| # (element indexer) => {:linkid => 0, :content => nil, :loaded => false, :state => 0, :record => {}} new_links[(indexer)] = {:linkid => entry.delete(:dbid), :content => nil, :loaded => false, :state => 0, :record => entry} # Load indicated :content case new_links[(indexer)][:record][:element] when "element_boolean" case new_links[(indexer)][:record][:element_boolean] when -1 new_links[(indexer)][:content] = nil new_links[(indexer)][:loaded] = true when 0 new_links[(indexer)][:content] = false new_links[(indexer)][:loaded] = true when 1 new_links[(indexer)][:content] = true new_links[(indexer)][:loaded] = true end when "element_integer" new_links[(indexer)][:content] = new_links[(indexer)][:record][:element_integer] new_links[(indexer)][:loaded] = true when "element_float" new_links[(indexer)][:content] = new_links[(indexer)][:record][:element_float] new_links[(indexer)][:loaded] = true when "element_bigdecimal" new_links[(indexer)][:content] = ::BigDecimal.new(new_links[(indexer)][:record][:element_bigdecimal].to_s) new_links[(indexer)][:loaded] = true when "element_datetime" new_links[(indexer)][:content] = new_links[(indexer)][:record][:element_datetime] if new_links[(indexer)][:content].is_a?(::Time) new_links[(indexer)][:content] = new_links[(indexer)][:content].to_datetime() end new_links[(indexer)][:loaded] = true when "element_text" if new_links[(indexer)][:record][:element_text_uuid].size > 0 # Load full text content from pages new_links[(indexer)][:content] = "" @db_address[:database].connector()[:text_page].filter({:parent_uuid => new_links[(indexer)][:record][:element_text_uuid].to_s}).order(:ordinal).each do |text_page| new_links[(indexer)][:content] << text_page[:content] end else new_links[(indexer)][:content] = new_links[(indexer)][:record][:element_text] end new_links[(indexer)][:loaded] = true when "element_binary" # Load binary data new_links[(indexer)][:content] = ::GxG::ByteArray.new new_links[(indexer)][:loaded] = false # Review : support on-read segment loading and element unloading. (thinking of large binary files here) when "element_array" # Load PersistedArray new_links[(indexer)][:content] = @db_address[:database].retrieve_by_uuid(new_links[(indexer)][:record][:element_array_uuid].to_s.to_sym, @credential, (@reservation || @delegate)) if new_links[(indexer)][:content].is_a?(::GxG::Database::PersistedArray) new_links[(indexer)][:loaded] = true new_links[(indexer)][:state] = new_links[(indexer)][:content].hash new_links[(indexer)][:content].set_parent(self) else new_links[(indexer)][:loaded] = false end when "element_hash" # Load PersistedHash new_links[(indexer)][:content] = @db_address[:database].retrieve_by_uuid(new_links[(indexer)][:record][:element_hash_uuid].to_s.to_sym, @credential, (@reservation || @delegate)) if new_links[(indexer)][:content].is_a?(::GxG::Database::PersistedHash) new_links[(indexer)][:loaded] = true new_links[(indexer)][:state] = new_links[(indexer)][:content].hash new_links[(indexer)][:content].set_parent(self) else new_links[(indexer)][:loaded] = false end end end @element_links = new_links else raise Exception, "Database not available" end else raise Exception, "Invalid Database object" end else # Deactivated result = false end rescue Exception => the_error result = false log_error({:error => the_error}) end result end # def refresh_ordinals() if self.alive? if @db_address[:database].open? @element_links.each_with_index do |the_key, ordinal| @element_links[(ordinal)][:record][:ordinal] = ordinal @db_address[:database].connector()[:array_elements].filter({:dbid => @element_links[(ordinal)][:linkid]}).update({:ordinal => (ordinal)}) end end end end # public # def emit_structures(preference=false) @as_structures = preference preference end # Supplimental Supports def element_byte_map(class_filter = ::GxG::ByteArray) result = [] manifest = [] @element_links.each_with_index do |record, index| element_type = nil if class_filter if record[:content].is_a?(class_filter) element_type = @db_address[:database].element_table_for_instance(record[:content]).to_s end else element_type = @db_address[:database].element_table_for_instance(record[:content]).to_s end if element_type manifest << {:index => index, :type => element_type} end end manifest.each do |reference| record = @db_address[:database].connector()[:array_elements].filter({:parent_uuid => @uuid.to_s, :element => reference[:type], :ordinal => reference[:index]}).first if record result << {:index => record[:ordinal], :size => record[:length]} end end result end # Permission Management: def get_permissions() @permission.clone end # 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 |entry| @db_address[:database].assign_element_permission(entry[:table], entry[: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 # def reservation() if self.alive? if @reservation.is_any?(::String, ::Symbol) @reservation.clone end end end # def write_permission?() @permission[:write] end # def write_reserved?() if @reservation or @delegate true else false end end # 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 @element_links.each_index do |ordinal| if @element_links[(ordinal)][:content].is_any?([::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray]) @element_links[(ordinal)][:content].release_reservation end end end true end # 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 @element_links.each_index do |ordinal| if @element_links[(ordinal)][:content].is_any?([::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray]) clean_up_list << @element_links[(ordinal)][:content] unless @element_links[(ordinal)][: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 keep old code for now # 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 # @element_links.each_index do |ordinal| # if @element_links[(ordinal)][:content].is_any?([::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray]) # @element_links[(ordinal)][:content].set_delegate((@reservation || @delegate)) # end # end # result = true # end # # # end # end # end # xxx end old code result end # 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 # def get_delegate() if self.alive? if @delegate.is_any?(::String, ::Symbol) @delegate.clone end end end # def set_delegate(the_delegate=nil) if self.alive? if the_delegate.is_any?([::String, ::Symbol]) @delegate = the_delegate.to_sym end end end # def uuid() @uuid.clone end # def version(input=nil) @version.clone end # def version=(the_version=nil) if the_version.is_a?(::Numeric) @version = ::BigDecimal.new(the_version.to_s) end end # def element_version(index=nil) result = 0.0 if index.is_a?(::Numeric) if @element_links[(index)] result = @element_links[(index)][:record][:version] end # end result end # def set_element_version(element_index, the_version=nil) result = false if @element_links[(element_index)] if the_version.is_a?(::Numeric) @element_links[(element_index)][:record][:version] = the_version.to_s("F").to_d result = true else log_warning("Attempted to set version to an invalid version value #{the_version.inspect} for index #{element_index.inspect} on Object #{@uuid.inspect}") end else log_warning("Attempted to set version with an invalid index #{element_index.inspect} on Object #{@uuid.inspect}") end result end # def increment_version() if self.alive? if @db_address[:database].open? if self.write_reserved?() @version += 0.0001 end end end end # def save_version() @db_address[:database].connector()[:element_array].filter({:dbid => @db_address[:dbid]}).update({:version => @version}) end # def set_version(the_version=0.0) if self.write_reserved?() @version = the_version.to_s("F").to_d self.save_version end end # def title() @title.clone end # 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..256] @db_address[:database].connector()[:element_array].filter({:dbid => @db_address[:dbid]}).update({:title => @title}) self.increment_version end end end end end alias :title= :set_title # def set_constraint(format_uuid=nil) if self.alive? unless @constraint if ::GxG::valid_uuid?(format_uuid) @constraint = format_uuid.to_sym @db_address[:database].connector()[:element_array].filter({:dbid => (@db_address[:dbid])}).update({:constraint => (@constraint.to_s)}) end end end end alias :constraint= :set_constraint # def clear_constraint() if self.alive? if @constraint @constraint = nil @db_address[:database].connector()[:element_array].filter({:dbid => (@db_address[:dbid])}).update({:constraint => ""}) end end true end # def constraint() @constraint.clone end # def ufs() if @constraint record = @db_address[:database].format_load({:uuid => @constraint.to_s}) if record record[:ufs] else "" end else "" end end # def parent() @parent end # 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 # def refresh_links() if self.alive?() unless @db_address[:database].element_in_use?(@db_address[:table], @db_address[:dbid]) @parent = nil end end end # def initialize(settings = {}, options={}) # mount element: {:database => , :uuid => , :credential => , :delegate => nil/, :parent => nil/} # create element: {:database => , :credential => , :delegate => nil/, :parent => nil/} # 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 @as_structures = false @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 @constraint = settings[:constraint] unless @constraint if options[:with_constraint] @constraint = options[:with_constraint] end end if settings[:database].is_a?(::GxG::Database::Database) if settings[:database].open?() 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 if options[:with_title] the_title = options[:with_title] else the_title = ("Untitled PersistedArray " << (settings[:database].connector()[:element_array].count.to_s)) end end unless ::GxG::valid_uuid?(the_uuid) # without specifying a uuid, the implied intent is to create a new PersistedArray if ::GxG::valid_uuid?(options[:with_uuid]) the_uuid = options[:with_uuid].to_s else the_uuid = ::GxG::uuid_generate() while ((settings[:database].connector()[:element_array].filter({:uuid => the_uuid}).count > 0) or (settings[:database].connector()[:element_array].filter({:uuid => the_uuid}).count > 0)) the_uuid = ::GxG::uuid_generate() end end # create blank array record new_dbid = settings[:database].connector()[:element_array].insert({:uuid => (the_uuid), :title => (the_title), :constraint => (@constraint.to_s), :version => (options[:with_version] || BigDecimal.new("0.0"))}) # @db_address = {:database => (settings[:database]), :table => :element_array, :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_array, 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_array, 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_array, new_dbid, @credential, @permission) end # mount according to uuid if the_uuid # mount record = settings[:database].connector()[:element_array].filter({:uuid => (the_uuid)}).first if record @uuid = the_uuid.to_sym @title = record[:title].to_s @version = record[:version] @db_address = {:database => (settings[:database]), :table => :element_array, :dbid => (record[:dbid])} @element_links = [] if record[:constraint].to_s.size > 0 @constraint = record[:constraint].to_sym else @constraint = nil end 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_element_links # else raise Exception, "Unable to mount the PersistedArray record" end else raise Exception, "Unable to mount the PersistedArray 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 # def inspect() # FORNOW: make re-entrant (yes, I know!) Fortunately, circular links are impossible with PersistedArray. # 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 result = "[" @element_links.each_index do |element_index| if @element_links[(element_index)].is_a?(::Hash) result << @element_links[(element_index)][:content].inspect else result << "(Not Loaded)" end unless element_index == (@element_links.size - 1) result << ", " end end result << "]" else result = "(Too Damned Big)" end # end result end # def alive?() if @db_address if @db_address[:database].element_exists?(@db_address[:table],@db_address[:dbid]) true else false end else false end end # def db_address() @db_address end # def deactivate() if self.alive?() @element_links.each_index do |index| element = @element_links[(index)][: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.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 @element_links = [] @db_address = nil end true end # def save() result = false if self.alive? begin if @db_address[:database].open?() @element_links.each_index do |index| # Note: only save property data and header if: a) data is still loaded and b) state has changed. if @element_links[(index)][:loaded] == true if @element_links[(index)][:state] != @element_links[(index)][:content].hash @element_links[(index)][:record][:version] = (@element_links[(index)][:record][:version].to_s("F").to_d + 0.0001) element_write(index) 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 # 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].element_in_use?(@db_address[:table], @db_address[:dbid]) raise Exception, "Element is still linked to a structure. Use .delete().destroy or .delete_at().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 # def size() @element_links.size end # def structure_attached?() @db_address[:database].structure_attached?(@uuid.to_s) end def structure_detach() @db_address[:database].structure_detach(@uuid.to_s) end # def []=(indexer=nil, value=nil) # Only works with Integer indexes. 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 value.is_a?(::OpenStruct) value = value.table end if indexer.is_a?(::Integer) # 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" value.structure_detach 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 @element_links[(indexer)] # 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, :ordinal => 0, :version => ::BigDecimal.new("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" case value.class when ::NilClass new_value[:record][:element_boolean] = -1 new_value[:content] = nil when ::FalseClass new_value[:record][:element_boolean] = 0 new_value[:content] = false when ::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] = @element_links[(indexer)][:record][:version] new_value[:record][:element_text_uuid] = @element_links[(indexer)][: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] = @element_links[(indexer)][:record][:version] new_value[:record][:element_binary_uuid] = @element_links[(indexer)][: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?(@element_links[(indexer)][:content].class) || ([true, false, nil].include?(new_value[:content]) && [true, false, nil].include?(@element_links[(indexer)][: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] = @element_links[(indexer)][: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] = (@element_links[(indexer)][:record][:version] + 0.0001) end new_value[:record][:ordinal] = @element_links[(indexer)][:record][:ordinal] new_value[:loaded] = true new_value[:state] = @element_links[(indexer)][:state] # ### Did string grow or shrink crossing the threshold?? if new_value[:content].is_a?(::String) if (@element_links[(indexer)][:content].size > 256 && new_value[:content].size <= 256) # shrank - delete text object and pages as well as property record (slow) element_destroy(indexer) @element_links[(indexer)] = new_value element_write(indexer) else if (@element_links[(indexer)][:content].size <= 256 && new_value[:content].size > 256) # grew - create text object and pages (a little less slow) @element_links[(indexer)] = new_value element_write(indexer) else # N/A @element_links[(indexer)] = new_value end end else # N/A @element_links[(indexer)] = 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] = (@element_links[(indexer)][:record][:version] + 0.0001) end new_value[:record][:ordinal] = @element_links[(indexer)][: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 @element_links[(indexer)][:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray, ::GxG::ByteArray, ::String) if @element_links[(indexer)][:content].is_a?(::String) if (@element_links[(indexer)][:content].size > 256 && new_value[:content].size <= 256) || (@element_links[(indexer)][:content].size <= 256 && new_value[:content].size > 256) element_destroy(indexer) end else element_destroy(indexer) end end # @element_links[(indexer)] = new_value element_write(indexer) 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] = ::BigDecimal.new("0.0") end new_value[:record][:ordinal] = @element_links.size new_value[:loaded] = true # @element_links[(indexer)] = new_value # Review : is this wise to do this here?? element_write(indexer) end # ### Handle coordination between persisted objects: if @element_links[(indexer)][:content].is_any?(::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray) # ### Extend Reservation to persisted objects ( Review : does this slow it down too much?? ) @element_links[(indexer)][:content].release_reservation @db_address[:database].reservation_add_element((@reservation || @delegate), (@element_links[(indexer)][:content].db_address()[:table]), @element_links[(indexer)][:content].db_address()[:dbid]) @element_links[(indexer)][:content].set_delegate((@reservation || @delegate)) # ### Extend Permissions to persisted object @db_address[:database].extend_element_permissions(@db_address[:table],@db_address[:dbid],@element_links[(indexer)][:content].db_address()[:table],@element_links[(indexer)][:content].db_address()[:dbid]) @db_address[:database].enforce_permission_policy({:action => :extend, :credential => @credential, :source => self, :destination => @element_links[(indexer)][:content]}) # @element_links[(indexer)][:content].set_parent(self) end # self.increment_version() result = @element_links[(indexer)][: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 index in the form of an Integer." end result end # def unload(indexer=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 indexer.is_a?(::Integer) if @element_links[(indexer)] if @element_links[(indexer)][:content].is_any?(::String, ::GxG::ByteArray) if @element_links[(indexer)][:loaded] == true # ### Unload Base Element contents from memory, leaving reference harness intact and active. @element_links[(indexer)][:content].clear @element_links[(indexer)][:loaded] = false end end result = true end else raise ArgumentError, "You must specify with an Integer, not a #{indexer.class.inspect}" end else raise Exception, "Database not available" end else raise Exception, "Attempted to access a defunct structure" end result end # def [](indexer=nil) result = nil # if exists # if is not loaded, load element (if String or ByteArray) if self.alive?() if @db_address[:database].open?() if indexer.is_a?(::Integer) if @element_links[(indexer)] if @element_links[(indexer)][:content].is_any?(::String, ::GxG::ByteArray) unless @element_links[(indexer)][:loaded] == true # ### Load Base Element contents from the database. if @element_links[(indexer)][:content].is_a?(::String) if @element_links[(indexer)][:record][:element_text_uuid].to_s.size > 0 @db_address[:database].connector()[:text_page].filter({:parent_uuid => @element_links[(indexer)][:record][:element_text_uuid].to_s}).order(:ordinal).each do |text_page| @element_links[(indexer)][:content] << text_page[:content] end else @element_links[(indexer)][:content] = @element_links[(indexer)][:record][:element_text].to_s end else @db_address[:database].connector()[:binary_page].filter({:parent_uuid => @element_links[(indexer)][:record][:element_binary_uuid].to_s}).order(:ordinal).each do |binary_page| @element_links[(indexer)][:content] << binary_page[:content] end end @element_links[(indexer)][:loaded] = true end end result = @element_links[(indexer)][:content] if @as_structures == true if result.is_a?(::GxG::Database::PersistedHash) result = ::OpenStruct.new(result) end end end else raise ArgumentError, "You must specify with an Integer, not a #{indexer.class.inspect}" end else raise Exception, "Database not available" end else raise Exception, "Attempted to access a defunct structure" end result end # def delete_at(indexer=nil) result = nil # if exists # if is not loaded, load element if self.alive?() if @db_address[:database].open?() if indexer.is_a?(::Integer) if self.write_reserved?() property_key = key.to_s.to_sym # if @element_links[(indexer)] # This will load the unloaded prior to separation. result = self[(indexer)] if @as_structures == true if result.is_a?(::GxG::Database::PersistedHash) result = ::OpenStruct.new(result) end end the_link = @element_links.delete_at(indexer) 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()[:array_elements].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 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 # def <<(*args) if args.size > 0 args.each do |item| self[(@element_links.size)] = item end end self end # def push(*args) if args.size > 0 args.each do |item| self << item end end self end # def pop() self.delete_at((@element_links.size - 1)) end # def shift() self.delete_at(0) end # def insert(the_index=nil, the_object=nil) # resovle the_index : the_index < 0 : the_index = (size - the_index) # if the_index == 0 : @element_links.unshift(nil-record); self[0] = the_object # if the_index > size-1 : push(the_object) # else: @element_links.insert(the_index, nil-record); self[(the_index)] = the_object # vette the_object --> persistable? if @db_address if @db_address[:database].persistable?(the_object) new_value = { :linkid => nil, :content => nil, :loaded => false, :state => 0, :record => { :parent_uuid => @uuid.to_s, :ordinal => 0, :version => ::BigDecimal.new("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 => "" } } the_index = the_index.to_i if the_index < 0 the_index = (self.size - the_index) end if the_index == 0 new_value[:record][:ordinal] = the_index @element_links.unshift(new_value) element_write(the_index) refresh_ordinals self[0] = the_object else if the_index > (self.size - 1) self.push(the_object) else new_value[:record][:ordinal] = the_index @element_links.insert(the_index, new_value) element_write(the_index) refresh_ordinals self[(the_index)] = the_object end end else log_warning("Ignoring attempt to insert a non-persistable object.") end end self end # def unshift(the_object=nil) self.insert(0,the_object) end # def swap(first_index=nil, last_index=nil) result = false if first_index && last_index if (0..(self.size - 1)).include?(first_index.to_i) && (0..(self.size - 1)).include?(last_index.to_i) if first_index.to_i != last_index.to_i record = @element_links[(first_index)] @element_links[(first_index)] = @element_links[(last_index)] @element_links[(last_index)] = record refresh_ordinals result = true end end end result end # def include?(the_value) result = false if the_value.is_a?(::OpenStruct) the_value = the_value.table end @element_links.each do |element| if element[:content] == the_value result = true break end end result end # def find_index(the_value) result = nil if the_value.is_a?(::OpenStruct) the_value = the_value.table end @element_links.each_with_index do |element, indexer| if element[:content] == the_value result = indexer break end end result end # 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 unconstrained? sync issues?? result = {:type => :element_array, :uuid => GxG::uuid_generate.to_s.to_sym, :title => @title.clone, :version => @version.to_s("F"), :content => []} else result = {:type => :element_array, :uuid => @uuid.clone, :title => @title.clone, :version => @version.to_s("F"), :constraint => @constraint.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 => ::BigDecimal.new(the_value.version().to_s).to_s("F"), :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 => ::BigDecimal.new(the_value.version().to_s).to_s("F"), :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 => ::BigDecimal.new(the_value.version().to_s).to_s("F"), :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 => ::BigDecimal.new(the_value.version().to_s).to_s("F"), :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 => (::BigDecimal.new(the_container.version(the_selector).to_s) || ::BigDecimal.new("0.0")).to_s("F"), :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 # def export_package(options={:exclude_file_segments=>false}) self.save result = {:formats => {}, :records => []} object_record = self.export(options) object_record.search do |item,selector,container| if selector == :format || selector == :constraint if item.to_s.size > 0 format_uuid = item unless result[:formats][(format_uuid.to_s.to_sym)].is_a?(::Hash) format_sample = @db_address[:database].format_load({:uuid => format_uuid.to_s.to_sym}) format_sample[:content] = format_sample[:content].gxg_export() result[:formats][(format_uuid.to_s.to_sym)] = format_sample end end end end result[:records] << object_record result end # def first() if @element_links.size > 0 self[0] else nil end end # def last() if @element_links.size > 0 self[(@element_links.size - 1)] else nil end end # def each(&block) if block.respond_to?(:call) load_element_links if @element_links.size > 0 @element_links.each_index do |index| block.call(self[(index)]) end end self else self.to_enum(:each) end end # def each_index(&block) if block.respond_to?(:call) load_element_links if @element_links.size > 0 @element_links.each_index do |index| block.call(index) end end self else self.to_enum(:each_index) end end # def each_with_index(offset=0,&block) if block.respond_to?(:call) load_element_links if @element_links.size > 0 @element_links.to_enum(:each).with_index(offset).each do |entry, index| block.call(self[(index)], index) end end self else self.to_enum(:each_with_index,offset) end end # 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 # 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 alias :process :iterative # def process!(options={:include_inactive => true}, &block) self.iterative(options, &block) self end # def search(options={:include_inactive => true}, &block) results = [] if block.respond_to?(:call) results = self.iterative(options, &block) end results end # 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 safe_key = selector.to_s # xxx # if selector.is_a?(Symbol) # safe_key = (":" + selector.to_s) # else # safe_key = selector.to_s # end # xxx 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 # def get_at_path(the_path="/") # /^(?:[0-9])*[0-9](?:[0-9])*$/ = nil if an alpha present there, else 0 only numeric # Attribution : http://stackoverflow.com/questions/1240674/regex-match-a-string-containing-numbers-and-letters-but-not-a-string-of-just-nu # # if ":" detected do: (str.gsub("%2f","/").to_sym) as key else (str.gsub("%2f","/")) 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","/") element = element.to_sym 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 # 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","/") selector = selector.to_sym end end if selector container[(selector)] = the_value result = container[(selector)] else # ignore double slashes? '//' # break end # end end result end # 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 # end end end