gxg-web-client/gxg/web/gxg_augments.rb
2021-11-16 08:58:51 -08:00

1270 lines
No EOL
36 KiB
Ruby

# Alteration to Opal's Buffer::Array
class Buffer
class Array
def native_object()
`#@native`
end
end
end
# Place holders for Hash & Array modifications:
module GxG
module Database
class Field
#
end
class PersistedArray
#
end
class PersistedHash
#
end
end
end
# Logger (minimal) support:
class Logger
DEBUG = 0
INFO = 1
WARN = 2
ERROR = 3
FATAL = 4
UNKNOWN = 5
TRACE = 5
#
def initialize(loglevel=3)
@level = loglevel
end
#
def level()
@level
end
#
def level=(loglevel=3)
@level = loglevel
end
#
def add(severity = nil, message = nil, progname = nil, origin = nil, &block)
#
unless [::Logger::DEBUG, ::Logger::INFO, ::Logger::WARN, ::Logger::ERROR, ::Logger::FATAL, ::Logger::UNKNOWN].include?(severity)
severity = ::Logger::UNKNOWN
end
if progname
unless message
message = progname
progname = nil
end
end
begin
unless message
if block.respond_to?(:call)
message = block.call()
end
end
levels = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL", "ANY"]
puts "#{levels[(severity)].to_s}, [#{Time.now.strftime('%Y-%m-%dT%H:%M:%S%z')}##{$$.inspect}] #{Time.now.to_i.to_s} -- #{progname.inspect}: #{message.to_s}\n"
#
true
rescue Exception => the_error
log_error({:error => the_error, :parameters => {:severity => severity, :message => message, :progname => progname, :block => block}})
false
end
end
#
def debug(progname = nil, &block)
add(DEBUG, nil, progname, &block)
end
#
def info(progname = nil, &block)
add(INFO, nil, progname, &block)
end
#
def warn(progname = nil, &block)
add(WARN, nil, progname, &block)
end
alias :warning :warn
#
def error(progname = nil, &block)
add(ERROR, nil, progname, &block)
end
#
def fatal(progname = nil, &block)
add(FATAL, nil, progname, &block)
end
#
def unknown(progname = nil, &block)
add(UNKNOWN, nil, progname, &block)
end
alias :trace :unknown
#
end
#
class Time
def to_d()
BigDecimal("%#{::Float::DIG}f" % self.to_f)
end
end
#
class DateTime < Date
# Review : totally revamp and rethink this.
# Notes, see: https://ruby-doc.org/stdlib-2.5.0/libdoc/date/rdoc/DateTime.html#method-c-parse
def intialize(*args)
@data = Time.parse(*args)
super(*args)
end
#
def to_time()
::Time.parse(self.to_s)
end
#
def to_d()
BigDecimal(self.to_time.to_d)
end
#
def self.now()
DateTime.parse(Time.now.strftime('%Y-%m-%dT%H:%M:%S%z'))
end
def to_s()
self.strftime('%Y-%m-%dT%H:%M:%S%z')
end
def to_json()
self.strftime('%Y-%m-%dT%H:%M:%S%z')
end
def strftime(fmt='%FT%T%:z')
super(fmt)
end
def self._strptime(str, fmt='%FT%T%z')
super(str, fmt)
end
def iso8601_timediv(n) # :nodoc:
n = n.to_i
strftime('T%T' +
if n < 1
''
else
'.%0*d' % [n, (sec_fraction * 10**n).round]
end +
'%:z')
end
private :iso8601_timediv
def iso8601(n=0)
super() + iso8601_timediv(n)
end
def rfc3339(n=0) iso8601(n) end
def xmlschema(n=0) iso8601(n) end # :nodoc:
def jisx0301(n=0)
super() + iso8601_timediv(n)
end
end
# ######## Hassle-free Cloning support
#class NilClass
# public
# def initialize_clone
# nil
# end
# alias :initialize_dup :initialize_clone
# alias :dup :initialize_clone
# def clone()
# initialize_clone
# end
#end
##
#class FalseClass
# public
# def initialize_clone
# false
# end
# alias :initialize_dup :initialize_clone
# alias :dup :initialize_clone
# def clone()
# initialize_clone
# end
#end
##
#class TrueClass
# public
# def initialize_clone
# true
# end
# alias :initialize_dup :initialize_clone
# alias :dup :initialize_clone
# def clone()
# initialize_clone
# end
#end
##
class Integer
# public
# def initialize_clone
# (self | self).to_i
# end
# alias :initialize_dup :initialize_clone
# alias :dup :initialize_clone
# def clone()
# initialize_clone
# end
def to_d()
BigDecimal("%#{::Float::DIG}f" % self.to_f)
end
end
##
class Float
public
def to_d()
BigDecimal("%#{::Float::DIG}f" % self)
end
# def initialize_clone
# (self + 0.0)
# end
# alias :initialize_dup :initialize_clone
# alias :dup :initialize_clone
# def clone()
# initialize_clone
# end
end
#
#class Symbol
# def initialize_clone
# (self.to_s.dup.to_sym)
# end
# alias :initialize_dup :initialize_clone
# alias :dup :initialize_clone
# def clone()
# initialize_clone
# end
#end
#
class String
#
def cast_to_ascii()
`new window.TextDecoder("ascii").decode(new window.TextEncoder().encode(#{self.clone}.toString()))`.to_s
end
#
def valid_date?()
# Match for Date pattern
if (/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/.match(self))
true
else
false
end
end
#
def valid_datetime?()
# Match for ISO 8601 pattern
if (/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9][+-][0-9][0-9]:[0-9][0-9]/.match(self))
true
else
false
end
end
#
def valid_datetime_nolocale?()
# Match for not-so-much ISO 8601-ish pattern
if (/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]/.match(self))
true
else
false
end
end
#
def valid_path?()
if (/.*(?:\\|\/)(.+)$/.match(self) || (self.split(" ").size == 1 || self.split("/").size > 1 || self == "/"))
true
else
false
end
end
#
def valid_jid?()
if /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/.match(self)
true
else
false
end
end
#
def camel_case?()
# self.match(/([A-Z][a-z]+[A-Z][a-zA-Z]+)/)
if self.match(/[A-Z]([A-Z0-9]*[a-z][a-z0-9]*[A-Z]|[a-z0-9]*[A-Z][A-Z0-9]*[a-z])[A-Za-z0-9]*/)
true
else
false
end
end
#
def split_camelcase()
if self.camel_case?()
self.split(/(?=[A-Z])/)
else
self
end
end
#
# def write(*args)
# # no-op
# end
# #
# def mime_type()
# result = nil
# raw = ::MimeMagic.by_magic(::StringIO.new(self.cast_to_ascii(self.clone)))
# if raw
# result = {:type => (raw.type), :mediatype => (raw.mediatype), :subtype => (raw.subtype)}
# end
# result
# end
#
def json_1?()
self.slice(0,1) == '{'
end
#
def json_2?()
(self.json_1?() && (self.match('"apiVersion"\s?:\s?"2.0"') || false))
end
#
def json?()
self.json_1?() || self.json_2?()
end
#
def from_json(symbolize_names = true)
if self.json?
the_object = ::JSON::parse(self,{:symbolize_names => symbolize_names})
if the_object.is_any?(::Hash, ::Array)
the_object.process! do |value, selector, container|
if value.is_a?(::String)
if (value.valid_datetime? || value.valid_datetime_nolocale?)
container[(selector)] = ::DateTime::parse(value)
else
item = value.numeric_values()
if item.is_a?(::Hash)
if item[:integer]
item = item[:integer]
container[(selector)] = item
else
if item[:float]
item = item[:float]
container[(selector)] = item
end
end
end
end
#
if value[0..6] == "binary:" && value[7..-1].base64?
container[(selector)] = GxG::ByteArray.new(value[7..-1].decode64)
end
end
nil
end
#
end
else
self
end
end
# Base64 Stuff:
def base64?()
# RFC 4648
# SOMEDAY: use regex based detection, current is open to some bugs.
# See: http://www.perlmonks.org/?node_id=775820
# See: http://mattfaus.com/blog/2007/02/14/base64-regular-expression/
# See: http://stackoverflow.com/questions/475074/regex-to-parse-or-validate-base64-data
# Regex Testing: http://www.myregexp.com/signedJar.html
# For now:
begin
# Weird: the word 'page' and 'form' are considered a base64 strings. (time to monkey patch a fix)
if ! ["page","form", "left"].include?(self)
# ^(?:[A-Za-z0-9+/]{4})+(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
# failing: ^(?:[A-Za-z0-9+\/]{4}\n?)*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$
# ^@(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
# /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}[AEIMQUYcgkosw048]=|[A-Za-z0-9+\/][AQgw]==)?\z/
# (?:[A-Za-z0-9+\/]{4}){2,}(?:[A-Za-z0-9+\/]{2}[AEIMQUYcgkosw048]=|[A-Za-z0-9+\/][AQgw]==)
# if (self.size > 0 && /^(?:[A-Za-z0-9+\/]{4})+(?:[A-Za-z0-9+\/]{2}[AEIMQUYcgkosw048]=|[A-Za-z0-9+\/][AQgw]==)?\z/.match(self))
if (self.size > 0)
begin
Base64::strict_decode64(self)
true
rescue Exception => the_error
false
end
else
false
end
else
false
end
rescue Exception => the_error
false
end
end
#
def encode64()
# RFC 4648
if self.base64?()
self
else
::Base64::strict_encode64(self)
end
end
#
def decode64()
# RFC 4648
if self.base64?()
::Base64::strict_decode64(self)
else
self
end
end
#
def encrypt(withkey="")
#
if withkey.to_s.size > 0
keybytes = ::GxG::ByteArray.new(withkey.to_s)
container = ::GxG::ByteArray.new(self.to_s)
#
container.each_index do |index|
the_value = container[(index)]
#
keybytes.each do |the_byte|
[127,63,31,15,7,3,1,0].each do |the_bit|
if the_byte > the_bit
the_value += 1
if the_value > 255
the_value = 0
end
else
the_value -= 1
if the_value < 0
the_value = 255
end
end
end
end
#
container[(index)] = the_value
end
#
result = container.to_s
result
else
self.dup
end
end
#
def decrypt(withkey="")
#
if withkey.to_s.size > 0
keybytes = ::GxG::ByteArray.new(withkey.to_s)
container = ::GxG::ByteArray.new(self.to_s)
#
container.each_index do |index|
the_value = container[(index)]
#
keybytes.each do |the_byte|
[127,63,31,15,7,3,1,0].each do |the_bit|
if the_byte > the_bit
the_value -= 1
if the_value < 0
the_value = 255
end
else
the_value += 1
if the_value > 255
the_value = 0
end
end
end
end
#
container[(index)] = the_value
end
#
result = container.to_s
result
else
self.dup
end
end
#
def to_d()
BigDecimal(self)
end
end
#
class Hash
#
def self.process(the_hash={},&block)
new_hash = {}
if block.respond_to?(:call)
if the_hash.is_any?(::Array, ::Hash, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
new_hash = the_hash.process!(&block)
else
raise ArgumentError, "you must pass a Hash or an Array, or a ByteArray"
end
end
new_hash
end
#
def self.search(the_hash={},&block)
new_hash = {}
if block.respond_to?(:call)
if the_hash.is_any?(::Array, ::Hash, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
new_hash = the_hash.search(&block)
else
raise ArgumentError, "you must pass a Hash or an Array, or a ByteArray"
end
end
new_hash
end
#
def iterative(&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_any?(::Hash, ::Struct, ::GxG::Database::PersistedHash)
a_node[:object].each_pair do |the_key, the_value|
node_stack << ({:parent => a_node[:object], :parent_selector => the_key, :object => the_value})
end
end
if a_node[:object].is_any?(::Array, ::GxG::Database::PersistedArray)
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
end
#
accumulator << a_node
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(&block)
result = self.clone
result.iterative(&block)
result
end
#
def process!(&block)
self.iterative(&block)
self
end
#
def search(&block)
results = []
if block.respond_to?(:call)
results = self.iterative(&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, :prefix => "/"}]
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 = safe_key.gsub("/","%2f")
path_stack.unshift(safe_key)
# compare the_value
found = false
if the_value.is_a?(::GxG::Database::Field)
if (the_value.content == the_object)
found = true
end
else
if (the_value == the_object)
found = true
end
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="/")
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 = 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
#
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 = 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
#
#
def symbolize_keys()
self.process! do |value, selector, container|
if container.is_a?(::Hash)
unless selector.is_a?(::Symbol)
container[(selector.to_sym)] = container.delete(selector)
end
end
nil
end
self
end
#
def gxg_export()
result = {:type => "Hash", :content => {}}
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
export_record = Proc.new do |the_value|
if the_value.is_any?(Integer, Float, String)
if the_value.is_a?(Numeric)
if the_value.to_s.include?(".")
{:type => "Float", :content => the_value}
else
{:type => "Integer", :content => the_value}
end
else
{:type => (the_value.class.to_s), :content => the_value}
end
else
{:type => (the_value.class.to_s), :content => the_value.to_s}
end
end
# Build up export_db:
self.search do |the_value, the_selector, the_container|
if the_value.is_a?(::Hash)
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => "Hash", :content => {}}}
else
if the_value.is_a?(::Array)
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => "Array", :content => []}}
else
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => export_record.call(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][:content][(the_child[:parent_selector])] = the_child[:record]
if the_child[:object].is_any?(Hash, Array)
link_db << the_child
end
end
end
#
result
end
#
def self.gxg_import(the_exported_record=nil)
result = nil
if the_exported_record.is_a?(Hash)
if the_exported_record[:type] == "Array"
result = ::Array::gxg_import(the_exported_record)
else
import_value = Proc.new do |type,value|
if value.is_a?(String)
begin
the_class = eval(type)
if the_class.respond_to?(:parse)
value = the_class.parse(value)
else
if the_class.respond_to?(:new)
value = the_class.new(value)
else
if the_class.respond_to?(:try_convert)
value = the_class.try_convert(value)
end
end
end
rescue Exception => the_error
end
end
value
end
if the_exported_record[:type] == "Hash"
result = {}
import_db = [{:parent => nil, :parent_selector => nil, :object => result, :record => the_exported_record}]
while import_db.size > 0 do
entry = import_db.shift
if entry[:record][:content].is_a?(Hash)
entry[:record][:content].each_pair do |selector, value|
if value[:type] == "Hash"
entry[:object][(selector)] = {}
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
if value[:type] == "Array"
entry[:object][(selector)] = []
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
entry[:object][(selector)] = import_value.call(value[:type], value[:content])
end
end
end
else
if entry[:record][:content].is_a?(Array)
entry[:record][:content].each_with_index do |value, selector|
if value[:type] == "Hash"
entry[:object][(selector)] = {}
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
if value[:type] == "Array"
entry[:object][(selector)] = []
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
entry[:object][(selector)] = import_value.call(value[:type], value[:content])
end
end
end
end
end
end
else
result = import_value.call(the_exported_record[:type], the_exported_record[:content])
end
end
end
result
end
#
end
#
class Array
#
def self.process(the_array=[],&block)
new_array = []
if block.respond_to?(:call)
if the_array.is_any?(::Array, ::Hash, ::GxG::ByteArray, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
new_array = the_array.process(&block)
else
raise ArgumentError, "you must pass a Array, a Hash, or a ByteArray"
end
end
new_array
end
#
def self.search(the_array=[],&block)
new_array = []
if block.respond_to?(:call)
if the_array.is_any?(::Array, ::Hash, ::GxG::ByteArray, ::Struct, ::GxG::Database::PersistedHash, ::GxG::Database::PersistedArray)
new_array = the_array.search(&block)
else
raise ArgumentError, "you must pass a Array, a Hash, or a ByteArray"
end
end
new_array
end
#
def iterative(&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_any?(::Hash, ::Struct, ::GxG::Database::PersistedHash)
a_node[:object].each_pair do |the_key, the_value|
node_stack << ({:parent => a_node[:object], :parent_selector => the_key, :object => the_value})
end
end
if a_node[:object].is_any?(::Array, ::GxG::Database::PersistedArray)
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
end
#
accumulator << a_node
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(&block)
result = self.clone
result.iterative(&block)
result
end
#
def process!(&block)
self.iterative(&block)
self
end
#
def search(&block)
results = []
if block.respond_to?(:call)
results = self.iterative(&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
if selector.is_a?(Symbol)
safe_key = (":" + selector.to_s)
else
safe_key = selector.to_s
end
safe_key = safe_key.gsub("/","%2f")
path_stack.unshift(safe_key)
# compare the_value
found = false
if the_value.is_a?(::GxG::Database::Field)
if (the_value.content == the_object)
found = true
end
else
if (the_value == the_object)
found = true
end
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 = 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
#
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 = 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
#
def gxg_export()
result = {:type => "Array", :content => []}
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
export_record = Proc.new do |the_value|
if the_value.is_any?(Integer, Float, String)
if the_value.is_a?(Numeric)
if the_value.to_s.include?(".")
{:type => "Float", :content => the_value}
else
{:type => "Integer", :content => the_value}
end
else
{:type => (the_value.class.to_s), :content => the_value}
end
else
{:type => (the_value.class.to_s), :content => the_value.to_s}
end
end
# Build up export_db:
self.search do |the_value, the_selector, the_container|
if the_value.is_a?(::Hash)
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => "Hash", :content => {}}}
else
if the_value.is_a?(::Array)
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => {:type => "Array", :content => []}}
else
export_db << {:parent => the_container, :parent_selector => the_selector.clone, :object => the_value, :record => export_record.call(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][:content][(the_child[:parent_selector])] = the_child[:record]
if the_child[:object].is_any?(Hash, Array)
link_db << the_child
end
end
end
#
result
end
#
def self.gxg_import(the_exported_record=nil)
result = nil
if the_exported_record.is_a?(Hash)
if the_exported_record[:type] == "Hash"
result = ::Hash::gxg_import(the_exported_record)
else
import_value = Proc.new do |type,value|
if value.is_a?(String)
begin
the_class = eval(type)
if the_class.respond_to?(:parse)
value = the_class.parse(value)
else
if the_class.respond_to?(:new)
value = the_class.new(value)
else
if the_class.respond_to?(:try_convert)
value = the_class.try_convert(value)
end
end
end
rescue Exception => the_error
end
end
value
end
if the_exported_record[:type] == "Array"
result = []
import_db = [{:parent => nil, :parent_selector => nil, :object => result, :record => the_exported_record}]
while import_db.size > 0 do
entry = import_db.shift
if entry[:record][:content].is_a?(Hash)
entry[:record][:content].each_pair do |selector, value|
if value[:type] == "Hash"
entry[:object][(selector)] = {}
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
if value[:type] == "Array"
entry[:object][(selector)] = []
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
entry[:object][(selector)] = import_value.call(value[:type], value[:content])
end
end
end
else
if entry[:record][:content].is_a?(Array)
entry[:record][:content].each_with_index do |value, selector|
if value[:type] == "Hash"
entry[:object][(selector)] = {}
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
if value[:type] == "Array"
entry[:object][(selector)] = []
import_db << {:parent => entry[:object], :parent_selector => selector, :object => entry[:object][(selector)], :record => value}
else
entry[:object][(selector)] = import_value.call(value[:type], value[:content])
end
end
end
end
end
end
else
result = import_value.call(the_exported_record[:type], the_exported_record[:content])
end
end
end
result
end
end
#